diff options
author | Robert Quattlebaum <rquattle@google.com> | 2017-02-08 12:13:19 -0800 |
---|---|---|
committer | Robert Quattlebaum <rquattle@google.com> | 2017-06-05 17:47:16 +0000 |
commit | 4e0c2195dd999859f4e79cec1884326fb52a5916 (patch) | |
tree | 3666b0241b636d5842ec84ae88a68d41cb9e6278 | |
parent | 4bfb604d043058b0ce95234319fd872124c5ec11 (diff) |
Initial support for proposed android.net.lowpan
Change-Id: I0bf8edb5199d50d2a549a353b2785aef8134ff92
23 files changed, 2985 insertions, 0 deletions
diff --git a/Android.mk b/Android.mk index 28abb8969079..91b835dbd17d 100644 --- a/Android.mk +++ b/Android.mk @@ -558,6 +558,15 @@ LOCAL_SRC_FILES += \ LOCAL_AIDL_INCLUDES += system/update_engine/binder_bindings +LOCAL_AIDL_INCLUDES += frameworks/base/lowpan/java +LOCAL_SRC_FILES += \ + lowpan/java/android/net/lowpan/ILowpanEnergyScanCallback.aidl \ + lowpan/java/android/net/lowpan/ILowpanNetScanCallback.aidl \ + lowpan/java/android/net/lowpan/ILowpanInterfaceListener.aidl \ + lowpan/java/android/net/lowpan/ILowpanInterface.aidl \ + lowpan/java/android/net/lowpan/ILowpanManagerListener.aidl \ + lowpan/java/android/net/lowpan/ILowpanManager.aidl + # FRAMEWORKS_BASE_JAVA_SRC_DIRS comes from build/core/pathmap.mk LOCAL_AIDL_INCLUDES += \ $(FRAMEWORKS_BASE_JAVA_SRC_DIRS) \ diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index e39cb7054241..ed344b563223 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1322,6 +1322,27 @@ <permission android:name="android.permission.NETWORK_SETTINGS" android:protectionLevel="signature" /> + <!-- #SystemApi @hide Allows applications to access information about LoWPAN interfaces. + <p>Not for use by third-party applications. --> + <permission android:name="android.permission.ACCESS_LOWPAN_STATE" + android:protectionLevel="signature|privileged" /> + + <!-- #SystemApi @hide Allows applications to change LoWPAN connectivity state. + <p>Not for use by third-party applications. --> + <permission android:name="android.permission.CHANGE_LOWPAN_STATE" + android:protectionLevel="signature|privileged" /> + + <!-- #SystemApi @hide Allows applications to read LoWPAN credential. + <p>Not for use by third-party applications. --> + <permission android:name="android.permission.READ_LOWPAN_CREDENTIAL" + android:protectionLevel="signature|privileged" /> + + <!-- #SystemApi @hide Allows a service to register or unregister + new LoWPAN interfaces. + <p>Not for use by third-party applications. --> + <permission android:name="android.permission.MANAGE_LOWPAN_INTERFACES" + android:protectionLevel="signature|privileged" /> + <!-- ======================================= --> <!-- Permissions for short range, peripheral networks --> <!-- ======================================= --> diff --git a/lowpan/Android.mk b/lowpan/Android.mk new file mode 100644 index 000000000000..9e9164f51513 --- /dev/null +++ b/lowpan/Android.mk @@ -0,0 +1,31 @@ +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH := $(call my-dir) + +ifneq (,$(findstring lowpan/java,$(FRAMEWORKS_BASE_SUBDIRS))) +include $(CLEAR_VARS) +LOCAL_MODULE := libandroid_net_lowpan +LOCAL_MODULE_TAGS := optional +LOCAL_SHARED_LIBRARIES += libbase +LOCAL_SHARED_LIBRARIES += libbinder +LOCAL_SHARED_LIBRARIES += libutils +LOCAL_AIDL_INCLUDES += frameworks/native/aidl/binder +LOCAL_AIDL_INCLUDES += frameworks/base/lowpan/java +LOCAL_AIDL_INCLUDES += frameworks/base/core/java +LOCAL_SRC_FILES += $(call all-Iaidl-files-under, java/android/net/lowpan) +include $(BUILD_SHARED_LIBRARY) +endif diff --git a/lowpan/java/android/net/lowpan/ILowpanEnergyScanCallback.aidl b/lowpan/java/android/net/lowpan/ILowpanEnergyScanCallback.aidl new file mode 100644 index 000000000000..f09dbe3d077e --- /dev/null +++ b/lowpan/java/android/net/lowpan/ILowpanEnergyScanCallback.aidl @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.lowpan; + +/** {@hide} */ +interface ILowpanEnergyScanCallback { + oneway void onEnergyScanResult(int channel, int rssi); + oneway void onEnergyScanFinished(); +} diff --git a/lowpan/java/android/net/lowpan/ILowpanInterface.aidl b/lowpan/java/android/net/lowpan/ILowpanInterface.aidl new file mode 100644 index 000000000000..647fcc1eef3d --- /dev/null +++ b/lowpan/java/android/net/lowpan/ILowpanInterface.aidl @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.lowpan; + +import android.net.lowpan.ILowpanInterfaceListener; +import android.net.lowpan.ILowpanNetScanCallback; +import android.net.lowpan.ILowpanEnergyScanCallback; +import android.os.PersistableBundle; +import android.net.IpPrefix; + +/** {@hide} */ +interface ILowpanInterface { + + ////////////////////////////////////////////////////////////////////////// + // Permission String Constants + + /* These are here for the sake of C++ interface implementations. */ + + const String PERM_ACCESS_LOWPAN_STATE = "android.permission.ACCESS_LOWPAN_STATE"; + const String PERM_CHANGE_LOWPAN_STATE = "android.permission.CHANGE_LOWPAN_STATE"; + const String PERM_READ_LOWPAN_CREDENTIAL = "android.permission.READ_LOWPAN_CREDENTIAL"; + + ////////////////////////////////////////////////////////////////////////// + // Property Key Constants + + const String KEY_INTERFACE_ENABLED = "android.net.lowpan.property.INTERFACE_ENABLED"; + const String KEY_INTERFACE_UP = "android.net.lowpan.property.INTERFACE_UP"; + const String KEY_INTERFACE_COMMISSIONED = "android.net.lowpan.property.INTERFACE_COMMISSIONED"; + const String KEY_INTERFACE_CONNECTED = "android.net.lowpan.property.INTERFACE_CONNECTED"; + const String KEY_INTERFACE_STATE = "android.net.lowpan.property.INTERFACE_STATE"; + + const String KEY_NETWORK_NAME = "android.net.lowpan.property.NETWORK_NAME"; + const String KEY_NETWORK_TYPE = "android.net.lowpan.property.NETWORK_TYPE"; + const String KEY_NETWORK_PANID = "android.net.lowpan.property.NETWORK_PANID"; + const String KEY_NETWORK_XPANID = "android.net.lowpan.property.NETWORK_XPANID"; + const String KEY_NETWORK_ROLE = "android.net.lowpan.property.NETWORK_ROLE"; + const String KEY_NETWORK_MASTER_KEY = "android.net.lowpan.property.NETWORK_MASTER_KEY"; + const String KEY_NETWORK_MASTER_KEY_INDEX + = "android.net.lowpan.property.NETWORK_MASTER_KEY_INDEX"; + + const String KEY_SUPPORTED_CHANNELS = "android.net.lowpan.property.SUPPORTED_CHANNELS"; + const String KEY_CHANNEL = "android.net.lowpan.property.CHANNEL"; + const String KEY_CHANNEL_MASK = "android.net.lowpan.property.CHANNEL_MASK"; + const String KEY_MAX_TX_POWER = "android.net.lowpan.property.MAX_TX_POWER"; + const String KEY_RSSI = "android.net.lowpan.property.RSSI"; + const String KEY_LQI = "android.net.lowpan.property.LQI"; + + const String KEY_LINK_ADDRESS_ARRAY = "android.net.lowpan.property.LINK_ADDRESS_ARRAY"; + const String KEY_ROUTE_INFO_ARRAY = "android.net.lowpan.property.ROUTE_INFO_ARRAY"; + + const String KEY_BEACON_ADDRESS = "android.net.lowpan.property.BEACON_ORIGIN_ADDRESS"; + const String KEY_BEACON_CAN_ASSIST = "android.net.lowpan.property.BEACON_CAN_ASSIST"; + + const String DRIVER_VERSION = "android.net.lowpan.property.DRIVER_VERSION"; + const String NCP_VERSION = "android.net.lowpan.property.NCP_VERSION"; + + /** @hide */ + const String KEY_EXTENDED_ADDRESS = "android.net.lowpan.property.EXTENDED_ADDRESS"; + + /** @hide */ + const String KEY_MAC_ADDRESS = "android.net.lowpan.property.MAC_ADDRESS"; + + ////////////////////////////////////////////////////////////////////////// + // Interface States + + const String STATE_OFFLINE = "offline"; + const String STATE_COMMISSIONING = "commissioning"; + const String STATE_ATTACHING = "attaching"; + const String STATE_ATTACHED = "attached"; + const String STATE_FAULT = "fault"; + + ////////////////////////////////////////////////////////////////////////// + // Device Roles + + const String ROLE_END_DEVICE = "end-device"; + const String ROLE_ROUTER = "router"; + const String ROLE_SLEEPY_END_DEVICE = "sleepy-end-device"; + const String ROLE_SLEEPY_ROUTER = "sleepy-router"; + const String ROLE_UNKNOWN = "unknown"; + + ////////////////////////////////////////////////////////////////////////// + // Service-Specific Error Code Constants + + const int ERROR_UNSPECIFIED = 1; + const int ERROR_INVALID_ARGUMENT = 2; + const int ERROR_DISABLED = 3; + const int ERROR_WRONG_STATE = 4; + const int ERROR_INVALID_TYPE = 5; + const int ERROR_INVALID_VALUE = 6; + const int ERROR_TIMEOUT = 7; + const int ERROR_IO_FAILURE = 8; + const int ERROR_BUSY = 9; + const int ERROR_ALREADY = 10; + const int ERROR_CANCELED = 11; + const int ERROR_CREDENTIAL_NEEDED = 12; + const int ERROR_FEATURE_NOT_SUPPORTED = 14; + const int ERROR_PROPERTY_NOT_FOUND = 16; + const int ERROR_JOIN_FAILED_UNKNOWN = 18; + const int ERROR_JOIN_FAILED_AT_SCAN = 19; + const int ERROR_JOIN_FAILED_AT_AUTH = 20; + const int ERROR_FORM_FAILED_AT_SCAN = 21; + const int ERROR_NCP_PROBLEM = 27; + const int ERROR_PERMISSION_DENIED = 28; + + ////////////////////////////////////////////////////////////////////////// + // Methods + + @utf8InCpp String getName(); + + void join(in Map parameters); + void form(in Map parameters); + void leave(); + void reset(); + + void beginLowPower(); + void pollForData(); + + oneway void onHostWake(); + + @utf8InCpp String[] getPropertyKeys(); + Map getProperties(in @utf8InCpp String[] keys); + void setProperties(in Map properties); + + void addListener(ILowpanInterfaceListener listener); + oneway void removeListener(ILowpanInterfaceListener listener); + + void startNetScan(in Map properties, ILowpanNetScanCallback listener); + oneway void stopNetScan(); + + void startEnergyScan(in Map properties, ILowpanEnergyScanCallback listener); + oneway void stopEnergyScan(); + + void addOnMeshPrefix(in IpPrefix prefix, int flags); + oneway void removeOnMeshPrefix(in IpPrefix prefix); + + void addExternalRoute(in IpPrefix prefix, int flags); + oneway void removeExternalRoute(in IpPrefix prefix); + + @utf8InCpp String getPropertyAsString(@utf8InCpp String key); +} diff --git a/lowpan/java/android/net/lowpan/ILowpanInterfaceListener.aidl b/lowpan/java/android/net/lowpan/ILowpanInterfaceListener.aidl new file mode 100644 index 000000000000..c99d732d4eba --- /dev/null +++ b/lowpan/java/android/net/lowpan/ILowpanInterfaceListener.aidl @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.lowpan; + +/** {@hide} */ +interface ILowpanInterfaceListener { + oneway void onPropertiesChanged(in Map properties); +} diff --git a/lowpan/java/android/net/lowpan/ILowpanManager.aidl b/lowpan/java/android/net/lowpan/ILowpanManager.aidl new file mode 100644 index 000000000000..5a8d7dce7c6f --- /dev/null +++ b/lowpan/java/android/net/lowpan/ILowpanManager.aidl @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.lowpan; +import android.net.lowpan.ILowpanInterface; +import android.net.lowpan.ILowpanManagerListener; + +/** {@hide} */ +interface ILowpanManager { + + const String LOWPAN_SERVICE_NAME = "lowpan"; + + ILowpanInterface getInterface(@utf8InCpp String name); + + @utf8InCpp String[] getInterfaceList(); + + void addListener(ILowpanManagerListener listener); + void removeListener(ILowpanManagerListener listener); + + void addInterface(ILowpanInterface lowpan_interface); + void removeInterface(ILowpanInterface lowpan_interface); +} diff --git a/lowpan/java/android/net/lowpan/ILowpanManagerListener.aidl b/lowpan/java/android/net/lowpan/ILowpanManagerListener.aidl new file mode 100644 index 000000000000..d4846f6740b9 --- /dev/null +++ b/lowpan/java/android/net/lowpan/ILowpanManagerListener.aidl @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.lowpan; + +import android.net.lowpan.ILowpanInterface; + +/** {@hide} */ +interface ILowpanManagerListener { + oneway void onInterfaceAdded(ILowpanInterface lowpanInterface); + oneway void onInterfaceRemoved(ILowpanInterface lowpanInterface); +} diff --git a/lowpan/java/android/net/lowpan/ILowpanNetScanCallback.aidl b/lowpan/java/android/net/lowpan/ILowpanNetScanCallback.aidl new file mode 100644 index 000000000000..c20a6f8110c8 --- /dev/null +++ b/lowpan/java/android/net/lowpan/ILowpanNetScanCallback.aidl @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.lowpan; + +/** {@hide} */ +interface ILowpanNetScanCallback { + oneway void onNetScanBeacon(in Map parameters); + oneway void onNetScanFinished(); +} diff --git a/lowpan/java/android/net/lowpan/LowpanBeaconInfo.java b/lowpan/java/android/net/lowpan/LowpanBeaconInfo.java new file mode 100644 index 000000000000..b344527e4be5 --- /dev/null +++ b/lowpan/java/android/net/lowpan/LowpanBeaconInfo.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.lowpan; + +import com.android.internal.util.HexDump; +import java.util.Collection; +import java.util.Map; +import java.util.TreeSet; + +/** + * Describes a LoWPAN Beacon + * + * @hide + */ +//@SystemApi +public class LowpanBeaconInfo extends LowpanIdentity { + + private int mRssi = UNKNOWN; + private int mLqi = UNKNOWN; + private byte[] mBeaconAddress = null; + private final TreeSet<Integer> mFlags = new TreeSet<>(); + + public static final int FLAG_CAN_ASSIST = 1; + + static class Builder extends LowpanIdentity.Builder { + private final LowpanBeaconInfo identity = new LowpanBeaconInfo(); + + public Builder setRssi(int x) { + identity.mRssi = x; + return this; + } + + public Builder setLqi(int x) { + identity.mLqi = x; + return this; + } + + public Builder setBeaconAddress(byte x[]) { + identity.mBeaconAddress = x.clone(); + return this; + } + + public Builder setFlag(int x) { + identity.mFlags.add(x); + return this; + } + + public Builder setFlags(Collection<Integer> x) { + identity.mFlags.addAll(x); + return this; + } + + /** @hide */ + Builder updateFromMap(Map map) { + if (map.containsKey(LowpanProperties.KEY_RSSI.getName())) { + setRssi(LowpanProperties.KEY_RSSI.getFromMap(map)); + } + if (map.containsKey(LowpanProperties.KEY_LQI.getName())) { + setLqi(LowpanProperties.KEY_LQI.getFromMap(map)); + } + if (map.containsKey(LowpanProperties.KEY_BEACON_ADDRESS.getName())) { + setBeaconAddress(LowpanProperties.KEY_BEACON_ADDRESS.getFromMap(map)); + } + identity.mFlags.clear(); + if (map.containsKey(LowpanProperties.KEY_BEACON_CAN_ASSIST.getName()) + && LowpanProperties.KEY_BEACON_CAN_ASSIST.getFromMap(map).booleanValue()) { + setFlag(FLAG_CAN_ASSIST); + } + super.updateFromMap(map); + return this; + } + + public LowpanBeaconInfo build() { + return identity; + } + } + + private LowpanBeaconInfo() {} + + public int getRssi() { + return mRssi; + } + + public int getLqi() { + return mLqi; + } + + public byte[] getBeaconAddress() { + return mBeaconAddress.clone(); + } + + public Collection<Integer> getFlags() { + return mFlags.clone(); + } + + public boolean isFlagSet(int flag) { + return mFlags.contains(flag); + } + + @Override + void addToMap(Map<String, Object> parameters) { + super.addToMap(parameters); + } + + @Override + public String toString() { + StringBuffer sb = new StringBuffer(); + + sb.append(super.toString()); + + if (mRssi != UNKNOWN) { + sb.append(", RSSI: ").append(mRssi); + } + + if (mLqi != UNKNOWN) { + sb.append(", LQI: ").append(mLqi); + } + + if (mBeaconAddress != null) { + sb.append(", BeaconAddress: ").append(HexDump.toHexString(mBeaconAddress)); + } + + for (Integer flag : mFlags) { + switch (flag.intValue()) { + case FLAG_CAN_ASSIST: + sb.append(", CAN_ASSIST"); + break; + default: + sb.append(", FLAG_").append(Integer.toHexString(flag)); + break; + } + } + + return sb.toString(); + } +} diff --git a/lowpan/java/android/net/lowpan/LowpanChannelInfo.java b/lowpan/java/android/net/lowpan/LowpanChannelInfo.java new file mode 100644 index 000000000000..50afe6d3a4c0 --- /dev/null +++ b/lowpan/java/android/net/lowpan/LowpanChannelInfo.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.lowpan; + + +/** Provides detailed information about a given channel. */ +//@SystemApi +public class LowpanChannelInfo { + + public static final int UNKNOWN_POWER = Integer.MAX_VALUE; + + ////////////////////////////////////////////////////////////////////////// + // Instance Variables + + private String mName = null; + private int mIndex = 0; + private boolean mIsMaskedByRegulatoryDomain = false; + private float mSpectrumCenterFrequency = 0.0f; + private float mSpectrumBandwidth = 0.0f; + private int mMaxTransmitPower = UNKNOWN_POWER; + + ////////////////////////////////////////////////////////////////////////// + // Public Getters and Setters + + public String getName() { + return mName; + } + + public int getIndex() { + return mIndex; + } + + public int getMaxTransmitPower() { + return mMaxTransmitPower; + } + + public boolean isMaskedByRegulatoryDomain() { + return mIsMaskedByRegulatoryDomain; + } + + public float getSpectrumCenterFrequency() { + return mSpectrumCenterFrequency; + } + + public float getSpectrumBandwidth() { + return mSpectrumBandwidth; + } + + @Override + public String toString() { + StringBuffer sb = new StringBuffer(); + + sb.append("Channel ").append(mIndex); + + if (mName != null) { + sb.append(" (").append(mName).append(")"); + } + + if (mSpectrumCenterFrequency > 0.0f) { + sb.append(", SpectrumCenterFrequency: ").append(mSpectrumCenterFrequency).append("Hz"); + } + + if (mSpectrumBandwidth > 0.0f) { + sb.append(", SpectrumBandwidth: ").append(mSpectrumBandwidth).append("Hz"); + } + + if (mMaxTransmitPower != UNKNOWN_POWER) { + sb.append(", MaxTransmitPower: ").append(mMaxTransmitPower); + } + + return sb.toString(); + } +} diff --git a/lowpan/java/android/net/lowpan/LowpanCommissioningSession.java b/lowpan/java/android/net/lowpan/LowpanCommissioningSession.java new file mode 100644 index 000000000000..9cad00c3415a --- /dev/null +++ b/lowpan/java/android/net/lowpan/LowpanCommissioningSession.java @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.lowpan; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.Network; +import android.os.Handler; +import java.net.InetSocketAddress; + +/** + * Commissioning Session. + * + * <p>This class enables a device to learn the credential needed to join a network using a technique + * called "in-band commissioning". + * + * @hide + */ +//@SystemApi +public abstract class LowpanCommissioningSession { + public LowpanCommissioningSession() {} + + /** + * Callback base class for {@link LowpanCommissioningSession} + * + * @hide + */ + //@SystemApi + public class Callback { + public void onReceiveFromCommissioner(@NonNull byte[] packet) {}; + + public void onClosed() {}; + } + + /** TODO: doc */ + @NonNull + public abstract LowpanBeaconInfo getBeaconInfo(); + + /** TODO: doc */ + public abstract void sendToCommissioner(@NonNull byte[] packet); + + /** TODO: doc */ + public abstract void setCallback(@Nullable Callback cb, @Nullable Handler handler); + + /** TODO: doc */ + public abstract void close(); + + /** + * This method is largely for Nest Weave, as an alternative to {@link #sendToCommissioner()} + * and @{link Callback#onReceiveFromCommissioner()}. + * + * <p>When used with the Network instance obtained from getNetwork(), the caller can use the + * given InetSocketAddress to communicate with the commissioner using a UDP (or, under certain + * circumstances, TCP) socket. + */ + public abstract @Nullable InetSocketAddress getInetSocketAddress(); + + public abstract @Nullable Network getNetwork(); +} diff --git a/lowpan/java/android/net/lowpan/LowpanCredential.java b/lowpan/java/android/net/lowpan/LowpanCredential.java new file mode 100644 index 000000000000..dea4d7888884 --- /dev/null +++ b/lowpan/java/android/net/lowpan/LowpanCredential.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.lowpan; + + +import java.util.Map; + +/** + * Describes a credential for a LoWPAN network. + * + * @hide + */ +//@SystemApi +public class LowpanCredential { + + public static final int UNSPECIFIED_KEY_INDEX = 0; + + private byte[] mMasterKey = null; + private int mMasterKeyIndex = UNSPECIFIED_KEY_INDEX; + + LowpanCredential() {} + + private LowpanCredential(byte[] masterKey, int keyIndex) { + setMasterKey(masterKey, keyIndex); + } + + private LowpanCredential(byte[] masterKey) { + setMasterKey(masterKey); + } + + public static LowpanCredential createMasterKey(byte[] masterKey) { + return new LowpanCredential(masterKey); + } + + public static LowpanCredential createMasterKey(byte[] masterKey, int keyIndex) { + return new LowpanCredential(masterKey, keyIndex); + } + + public void setMasterKey(byte[] masterKey) { + if (masterKey != null) { + masterKey = masterKey.clone(); + } + mMasterKey = masterKey; + } + + public void setMasterKeyIndex(int keyIndex) { + mMasterKeyIndex = keyIndex; + } + + public void setMasterKey(byte[] masterKey, int keyIndex) { + setMasterKey(masterKey); + setMasterKeyIndex(keyIndex); + } + + public byte[] getMasterKey() { + if (mMasterKey != null) { + return mMasterKey.clone(); + } + return null; + } + + public int getMasterKeyIndex() { + return mMasterKeyIndex; + } + + public boolean isMasterKey() { + return mMasterKey != null; + } + + void addToMap(Map<String, Object> parameters) throws LowpanException { + if (isMasterKey()) { + LowpanProperties.KEY_NETWORK_MASTER_KEY.putInMap(parameters, getMasterKey()); + LowpanProperties.KEY_NETWORK_MASTER_KEY_INDEX.putInMap( + parameters, getMasterKeyIndex()); + } else { + throw new LowpanException("Unsupported Network Credential"); + } + } +} diff --git a/lowpan/java/android/net/lowpan/LowpanEnergyScanResult.java b/lowpan/java/android/net/lowpan/LowpanEnergyScanResult.java new file mode 100644 index 000000000000..c680687d0e09 --- /dev/null +++ b/lowpan/java/android/net/lowpan/LowpanEnergyScanResult.java @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.lowpan; + + +/** + * Describes the result from one channel of an energy scan. + * + * @hide + */ +//@SystemApi +public class LowpanEnergyScanResult { + public static final int UNKNOWN = Integer.MAX_VALUE; + + private int mChannel = UNKNOWN; + private int mMaxRssi = UNKNOWN; + + public LowpanEnergyScanResult() {} + + public int getChannel() { + return mChannel; + } + + public int getMaxRssi() { + return mMaxRssi; + } + + public void setChannel(int x) { + mChannel = x; + } + + public void setMaxRssi(int x) { + mMaxRssi = x; + } + + @Override + public String toString() { + return "LowpanEnergyScanResult(channel: " + mChannel + ", maxRssi:" + mMaxRssi + ")"; + } +} diff --git a/lowpan/java/android/net/lowpan/LowpanException.java b/lowpan/java/android/net/lowpan/LowpanException.java new file mode 100644 index 000000000000..8ff37f926899 --- /dev/null +++ b/lowpan/java/android/net/lowpan/LowpanException.java @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.lowpan; + +import android.os.DeadObjectException; +import android.os.RemoteException; +import android.os.ServiceSpecificException; +import android.util.AndroidException; + +/** + * <code>LowpanException</code> is thrown if an action to a LoWPAN interface could not be performed + * or a LoWPAN interface property could not be fetched or changed. + * + * @see LowpanInterface + * @hide + */ +//@SystemApi +public class LowpanException extends AndroidException { + // Make the eclipse warning about serializable exceptions go away + private static final long serialVersionUID = 0x31863cbe562b0e11l; // randomly generated + + public static final int LOWPAN_ERROR = 1; + public static final int LOWPAN_CREDENTIAL_NEEDED = 2; + public static final int LOWPAN_DEAD = 3; + public static final int LOWPAN_DISABLED = 4; + public static final int LOWPAN_WRONG_STATE = 5; + public static final int LOWPAN_BUSY = 7; + public static final int LOWPAN_NCP_PROBLEM = 8; + public static final int LOWPAN_ALREADY = 9; + public static final int LOWPAN_CANCELED = 10; + public static final int LOWPAN_FEATURE_NOT_SUPPORTED = 12; + public static final int LOWPAN_PROPERTY_NOT_FOUND = 13; + public static final int LOWPAN_JOIN_FAILED_UNKNOWN = 14; + public static final int LOWPAN_JOIN_FAILED_AT_SCAN = 15; + public static final int LOWPAN_JOIN_FAILED_AT_AUTH = 16; + public static final int LOWPAN_FORM_FAILED_AT_SCAN = 17; + + /** + * Convert ServiceSpecificExceptions and Binder RemoteExceptions from LoWPAN binder interfaces + * into the correct public exceptions. + * + * @hide + */ + public static void throwAsPublicException(Throwable t) throws LowpanException { + if (t instanceof ServiceSpecificException) { + ServiceSpecificException e = (ServiceSpecificException) t; + int reason; + switch (e.errorCode) { + case ILowpanInterface.ERROR_INVALID_ARGUMENT: + case ILowpanInterface.ERROR_INVALID_TYPE: + case ILowpanInterface.ERROR_INVALID_VALUE: + throw new IllegalArgumentException(e.getMessage(), e); + + case ILowpanInterface.ERROR_PERMISSION_DENIED: + throw new SecurityException(e.getMessage(), e); + + case ILowpanInterface.ERROR_DISABLED: + reason = LowpanException.LOWPAN_DISABLED; + break; + + case ILowpanInterface.ERROR_WRONG_STATE: + reason = LowpanException.LOWPAN_WRONG_STATE; + break; + + case ILowpanInterface.ERROR_BUSY: + reason = LowpanException.LOWPAN_BUSY; + break; + + case ILowpanInterface.ERROR_ALREADY: + reason = LowpanException.LOWPAN_ALREADY; + break; + + case ILowpanInterface.ERROR_CANCELED: + reason = LowpanException.LOWPAN_CANCELED; + break; + + case ILowpanInterface.ERROR_CREDENTIAL_NEEDED: + reason = LowpanException.LOWPAN_CREDENTIAL_NEEDED; + break; + + case ILowpanInterface.ERROR_FEATURE_NOT_SUPPORTED: + reason = LowpanException.LOWPAN_FEATURE_NOT_SUPPORTED; + break; + + case ILowpanInterface.ERROR_PROPERTY_NOT_FOUND: + reason = LowpanException.LOWPAN_PROPERTY_NOT_FOUND; + break; + + case ILowpanInterface.ERROR_JOIN_FAILED_UNKNOWN: + reason = LowpanException.LOWPAN_JOIN_FAILED_UNKNOWN; + break; + + case ILowpanInterface.ERROR_JOIN_FAILED_AT_SCAN: + reason = LowpanException.LOWPAN_JOIN_FAILED_AT_SCAN; + break; + + case ILowpanInterface.ERROR_JOIN_FAILED_AT_AUTH: + reason = LowpanException.LOWPAN_JOIN_FAILED_AT_AUTH; + break; + + case ILowpanInterface.ERROR_FORM_FAILED_AT_SCAN: + reason = LowpanException.LOWPAN_FORM_FAILED_AT_SCAN; + break; + + case ILowpanInterface.ERROR_TIMEOUT: + case ILowpanInterface.ERROR_NCP_PROBLEM: + reason = LowpanException.LOWPAN_NCP_PROBLEM; + break; + case ILowpanInterface.ERROR_UNSPECIFIED: + default: + reason = LOWPAN_ERROR; + break; + } + throw new LowpanException(reason, e.getMessage(), e); + } else if (t instanceof DeadObjectException) { + throw new LowpanException(LOWPAN_DEAD, t); + } else if (t instanceof RemoteException) { + throw new UnsupportedOperationException( + "An unknown RemoteException was thrown" + " which should never happen.", t); + } else if (t instanceof RuntimeException) { + RuntimeException e = (RuntimeException) t; + throw e; + } + } + + private final int mReason; + + public final int getReason() { + return mReason; + } + + public LowpanException(int problem) { + super(getDefaultMessage(problem)); + mReason = problem; + } + + public LowpanException(String message) { + super(getCombinedMessage(LOWPAN_ERROR, message)); + mReason = LOWPAN_ERROR; + } + + public LowpanException(int problem, String message, Throwable cause) { + super(getCombinedMessage(problem, message), cause); + mReason = problem; + } + + public LowpanException(int problem, Throwable cause) { + super(getDefaultMessage(problem), cause); + mReason = problem; + } + + /** @hide */ + public static String getDefaultMessage(int problem) { + String problemString; + + // TODO: Does this need localization? + + switch (problem) { + case LOWPAN_DEAD: + problemString = "LoWPAN interface is no longer alive"; + break; + case LOWPAN_DISABLED: + problemString = "LoWPAN interface is disabled"; + break; + case LOWPAN_WRONG_STATE: + problemString = "LoWPAN interface in wrong state to perfom requested action"; + break; + case LOWPAN_BUSY: + problemString = + "LoWPAN interface was unable to perform the requestion action because it was busy"; + break; + case LOWPAN_NCP_PROBLEM: + problemString = + "The Network Co-Processor associated with this interface has experienced a problem"; + break; + case LOWPAN_ALREADY: + problemString = "The LoWPAN interface is already in the given state"; + break; + case LOWPAN_CANCELED: + problemString = "This operation was canceled"; + break; + case LOWPAN_CREDENTIAL_NEEDED: + problemString = "Additional credentials are required to complete this operation"; + break; + case LOWPAN_FEATURE_NOT_SUPPORTED: + problemString = + "A dependent feature required to perform the given action is not currently supported"; + break; + case LOWPAN_PROPERTY_NOT_FOUND: + problemString = "The given property was not found"; + break; + case LOWPAN_JOIN_FAILED_UNKNOWN: + problemString = "The join operation failed for an unspecified reason"; + break; + case LOWPAN_JOIN_FAILED_AT_SCAN: + problemString = + "The join operation failed because it could not communicate with any peers"; + break; + case LOWPAN_JOIN_FAILED_AT_AUTH: + problemString = + "The join operation failed because the credentials were not accepted by any peers"; + break; + case LOWPAN_FORM_FAILED_AT_SCAN: + problemString = "Network form failed"; + break; + case LOWPAN_ERROR: + default: + problemString = "The requested LoWPAN operation failed"; + break; + } + + return problemString; + } + + private static String getCombinedMessage(int problem, String message) { + String problemString = getProblemString(problem); + return String.format("%s (%d): %s", problemString, problem, message); + } + + private static String getProblemString(int problem) { + String problemString; + + switch (problem) { + case LOWPAN_ERROR: + problemString = "LOWPAN_ERROR"; + break; + case LOWPAN_DEAD: + problemString = "LOWPAN_DEAD"; + break; + case LOWPAN_DISABLED: + problemString = "LOWPAN_DISABLED"; + break; + case LOWPAN_WRONG_STATE: + problemString = "LOWPAN_WRONG_STATE"; + break; + case LOWPAN_BUSY: + problemString = "LOWPAN_BUSY"; + break; + case LOWPAN_NCP_PROBLEM: + problemString = "LOWPAN_NCP_PROBLEM"; + break; + case LOWPAN_ALREADY: + problemString = "LOWPAN_ALREADY"; + break; + case LOWPAN_CANCELED: + problemString = "LOWPAN_CANCELED"; + break; + case LOWPAN_CREDENTIAL_NEEDED: + problemString = "LOWPAN_CREDENTIAL_NEEDED"; + break; + case LOWPAN_FEATURE_NOT_SUPPORTED: + problemString = "LOWPAN_FEATURE_NOT_SUPPORTED"; + break; + case LOWPAN_PROPERTY_NOT_FOUND: + problemString = "LOWPAN_PROPERTY_NOT_FOUND"; + break; + case LOWPAN_JOIN_FAILED_UNKNOWN: + problemString = "LOWPAN_JOIN_FAILED_UNKNOWN"; + break; + case LOWPAN_JOIN_FAILED_AT_SCAN: + problemString = "LOWPAN_JOIN_FAILED_AT_SCAN"; + break; + case LOWPAN_JOIN_FAILED_AT_AUTH: + problemString = "LOWPAN_JOIN_FAILED_AT_AUTH"; + break; + case LOWPAN_FORM_FAILED_AT_SCAN: + problemString = "LOWPAN_FORM_FAILED_AT_SCAN"; + break; + default: + problemString = "LOWPAN_ERROR_CODE_" + problem; + break; + } + + return problemString; + } +} diff --git a/lowpan/java/android/net/lowpan/LowpanIdentity.java b/lowpan/java/android/net/lowpan/LowpanIdentity.java new file mode 100644 index 000000000000..2e7b560fda5e --- /dev/null +++ b/lowpan/java/android/net/lowpan/LowpanIdentity.java @@ -0,0 +1,179 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.lowpan; + +import com.android.internal.util.HexDump; +import java.util.Map; + +/** + * Describes an instance of a LoWPAN network. + * + * @hide + */ +//@SystemApi +public class LowpanIdentity { + + ////////////////////////////////////////////////////////////////////////// + // Constants + + /** @hide */ + public static final int TYPE_ZIGBEE = 1; + + /** @hide */ + public static final int TYPE_ZIGBEE_IP = 2; + + /** @hide */ + public static final int TYPE_THREAD = 3; + + public static final int UNKNOWN = Integer.MAX_VALUE; + + ////////////////////////////////////////////////////////////////////////// + // Builder + + /** @hide */ + //@SystemApi + public static class Builder { + private final LowpanIdentity identity = new LowpanIdentity(); + + public Builder setName(String x) { + identity.mName = x; + return this; + } + + public Builder setXpanid(byte x[]) { + identity.mXpanid = x.clone(); + return this; + } + + public Builder setPanid(int x) { + identity.mPanid = x; + return this; + } + + /** @hide */ + public Builder setType(int x) { + identity.mType = x; + return this; + } + + public Builder setChannel(int x) { + identity.mChannel = x; + return this; + } + + /** @hide */ + Builder updateFromMap(Map map) { + if (map.containsKey(ILowpanInterface.KEY_NETWORK_NAME)) { + setName(LowpanProperties.KEY_NETWORK_NAME.getFromMap(map)); + } + if (map.containsKey(ILowpanInterface.KEY_NETWORK_PANID)) { + setPanid(LowpanProperties.KEY_NETWORK_PANID.getFromMap(map)); + } + if (map.containsKey(ILowpanInterface.KEY_NETWORK_XPANID)) { + setXpanid(LowpanProperties.KEY_NETWORK_XPANID.getFromMap(map)); + } + if (map.containsKey(ILowpanInterface.KEY_CHANNEL)) { + setChannel(LowpanProperties.KEY_CHANNEL.getFromMap(map)); + } + if (map.containsKey(ILowpanInterface.KEY_NETWORK_TYPE)) { + setType(LowpanProperties.KEY_NETWORK_TYPE.getFromMap(map)); + } + return this; + } + + public LowpanIdentity build() { + return identity; + } + } + + LowpanIdentity() {} + + ////////////////////////////////////////////////////////////////////////// + // Instance Variables + + private String mName = null; + private byte[] mXpanid = null; + private int mType = UNKNOWN; + private int mPanid = UNKNOWN; + private int mChannel = UNKNOWN; + + ////////////////////////////////////////////////////////////////////////// + // Public Getters and Setters + + public String getName() { + return mName; + } + + public byte[] getXpanid() { + return mXpanid.clone(); + } + + public int getPanid() { + return mPanid; + } + + /** @hide */ + public int getType() { + return mType; + } + + public int getChannel() { + return mChannel; + } + + static void addToMap(Map<String, Object> parameters, LowpanIdentity networkInfo) { + if (networkInfo.getName() != null) { + LowpanProperties.KEY_NETWORK_NAME.putInMap(parameters, networkInfo.getName()); + } + if (networkInfo.getPanid() != LowpanIdentity.UNKNOWN) { + LowpanProperties.KEY_NETWORK_PANID.putInMap( + parameters, networkInfo.getPanid()); + } + if (networkInfo.getChannel() != LowpanIdentity.UNKNOWN) { + LowpanProperties.KEY_CHANNEL.putInMap( + parameters, networkInfo.getChannel()); + } + if (networkInfo.getXpanid() != null) { + LowpanProperties.KEY_NETWORK_XPANID.putInMap(parameters, networkInfo.getXpanid()); + } + } + + void addToMap(Map<String, Object> parameters) { + addToMap(parameters, this); + } + + @Override + public String toString() { + StringBuffer sb = new StringBuffer(); + + sb.append("Name: ").append(mName == null ? "<none>" : mName); + + if (mXpanid != null) { + sb.append(", XPANID: ").append(HexDump.toHexString(mXpanid)); + } + + if (mPanid != UNKNOWN) { + sb.append(", PANID: ").append(String.format("0x%04X", mPanid)); + } + + if (mChannel != UNKNOWN) { + sb.append(", Channel: ").append(mChannel); + } + + return sb.toString(); + } +} diff --git a/lowpan/java/android/net/lowpan/LowpanInterface.java b/lowpan/java/android/net/lowpan/LowpanInterface.java new file mode 100644 index 000000000000..cd548190fa17 --- /dev/null +++ b/lowpan/java/android/net/lowpan/LowpanInterface.java @@ -0,0 +1,824 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.lowpan; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.net.IpPrefix; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceSpecificException; +import android.util.Log; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * Class for managing a specific Low-power Wireless Personal Area Network (LoWPAN) interface. + * + * @hide + */ +//@SystemApi +public class LowpanInterface { + private static final String TAG = LowpanInterface.class.getSimpleName(); + + /** Detached role. The interface is not currently attached to a network. */ + public static final String ROLE_DETACHED = "detached"; + + /** End-device role. End devices do not route traffic for other nodes. */ + public static final String ROLE_END_DEVICE = "end-device"; + + /** Router role. Routers help route traffic around the mesh network. */ + public static final String ROLE_ROUTER = "router"; + + /** + * Sleepy End-Device role. + * + * <p>End devices with this role are nominally asleep, waking up periodically to check in with + * their parent to see if there are packets destined for them. Such devices are capable of + * extraordinarilly low power consumption, but packet latency can be on the order of dozens of + * seconds(depending on how the node is configured). + */ + public static final String ROLE_SLEEPY_END_DEVICE = "sleepy-end-device"; + + /** + * Sleepy-router role. + * + * <p>Routers with this role are nominally asleep, waking up periodically to check in with other + * routers and their children. + */ + public static final String ROLE_SLEEPY_ROUTER = "sleepy-router"; + + /** TODO: doc */ + public static final String ROLE_LEADER = "leader"; + + /** TODO: doc */ + public static final String ROLE_COORDINATOR = "coordinator"; + + /** + * Offline state. + * + * <p>This is the initial state of the LoWPAN interface when the underlying driver starts. In + * this state the NCP is idle and not connected to any network. + * + * <p>This state can be explicitly entered by calling {@link #reset()}, {@link #leave()}, or + * <code>setUp(false)</code>, with the later two only working if we were not previously in the + * {@link #STATE_FAULT} state. + * + * @see #getState() + * @see #STATE_FAULT + */ + public static final String STATE_OFFLINE = "offline"; + + /** + * Commissioning state. + * + * <p>The interface enters this state after a call to {@link #startCommissioningSession()}. This + * state may only be entered directly from the {@link #STATE_OFFLINE} state. + * + * @see #startCommissioningSession() + * @see #getState() + * @hide + */ + public static final String STATE_COMMISSIONING = "commissioning"; + + /** + * Attaching state. + * + * <p>The interface enters this state when it starts the process of trying to find other nodes + * so that it can attach to any pre-existing network fragment, or when it is in the process of + * calculating the optimal values for unspecified parameters when forming a new network. + * + * <p>The interface may stay in this state for a prolonged period of time (or may spontaneously + * enter this state from {@link #STATE_ATTACHED}) if the underlying network technology is + * heirarchical (like ZigBeeIP) or if the device role is that of an "end-device" ({@link + * #ROLE_END_DEVICE} or {@link #ROLE_SLEEPY_END_DEVICE}). This is because such roles cannot + * create their own network fragments. + * + * @see #STATE_ATTACHED + * @see #getState() + */ + public static final String STATE_ATTACHING = "attaching"; + + /** + * Attached state. + * + * <p>The interface enters this state from {@link #STATE_ATTACHING} once it is actively + * participating on a network fragment. + * + * @see #STATE_ATTACHING + * @see #getState() + */ + public static final String STATE_ATTACHED = "attached"; + + /** + * Fault state. + * + * <p>The interface will enter this state when the driver has detected some sort of problem from + * which it was not immediately able to recover. + * + * <p>This state can be entered spontaneously from any other state. Calling {@link #reset} will + * cause the device to return to the {@link #STATE_OFFLINE} state. + * + * @see #getState + * @see #STATE_OFFLINE + */ + public static final String STATE_FAULT = "fault"; + + /** + * Network type for Thread 1.x networks. + * + * @see android.net.lowpan.LowpanIdentity#getType + * @see #getLowpanIdentity + * @hide + */ + public static final String NETWORK_TYPE_THREAD = "org.threadgroup.thread.v1"; + + /** + * Network type for ZigBeeIP 1.x networks. + * + * @see android.net.lowpan.LowpanIdentity#getType + * @see #getLowpanIdentity + * @hide + */ + public static final String NETWORK_TYPE_ZIGBEE_IP = "org.zigbee.zigbeeip.v1"; + + private static final String NETWORK_PROPERTY_KEYS[] = { + LowpanProperties.KEY_NETWORK_NAME.getName(), + LowpanProperties.KEY_NETWORK_PANID.getName(), + LowpanProperties.KEY_NETWORK_XPANID.getName(), + LowpanProperties.KEY_CHANNEL.getName() + }; + + /** + * Callback base class for LowpanInterface + * + * @hide + */ + //@SystemApi + public abstract static class Callback { + public void onConnectedChanged(boolean value) {} + + public void onEnabledChanged(boolean value) {} + + public void onUpChanged(boolean value) {} + + public void onRoleChanged(@NonNull String value) {} + + public void onStateChanged(@NonNull String state) {} + + public void onLowpanIdentityChanged(@NonNull LowpanIdentity value) {} + + /** @hide */ + public void onPropertiesChanged(@NonNull Map properties) {} + } + + private ILowpanInterface mBinder; + private final HashMap<Integer, ILowpanInterfaceListener> mListenerMap = new HashMap<>(); + + /** Map between IBinder identity hashes and LowpanInstance objects. */ + private static final HashMap<Integer, LowpanInterface> sInstanceMap = new HashMap<>(); + + private LowpanInterface(IBinder binder) { + mBinder = ILowpanInterface.Stub.asInterface(binder); + } + + /** + * Get the LowpanInterface object associated with this IBinder. Returns null if this IBinder + * does not implement the appropriate interface. + * + * @hide + */ + @NonNull + public static final LowpanInterface from(IBinder binder) { + Integer hashCode = Integer.valueOf(System.identityHashCode(binder)); + LowpanInterface instance; + + synchronized (sInstanceMap) { + instance = sInstanceMap.get(hashCode); + + if (instance == null) { + instance = new LowpanInterface(binder); + sInstanceMap.put(hashCode, instance); + } + } + + return instance; + } + + /** {@hide} */ + public static final LowpanInterface from(ILowpanInterface iface) { + return from(iface.asBinder()); + } + + /** {@hide} */ + public static final LowpanInterface getInterfaceFromBinder(IBinder binder) { + return from(binder); + } + + /** + * Returns the IBinder object associated with this interface. + * + * @hide + */ + public IBinder getBinder() { + return mBinder.asBinder(); + } + + private static void throwAsPublicException(Throwable t) throws LowpanException { + LowpanException.throwAsPublicException(t); + } + + ////////////////////////////////////////////////////////////////////////// + // Private Property Helpers + + void setProperties(Map properties) throws LowpanException { + try { + mBinder.setProperties(properties); + + } catch (RemoteException x) { + // Catch and ignore all binder exceptions + Log.e(TAG, x.toString()); + + } catch (ServiceSpecificException x) { + throwAsPublicException(x); + } + } + + @NonNull + Map<String, Object> getProperties(String keys[]) throws LowpanException { + try { + return mBinder.getProperties(keys); + } catch (RemoteException x) { + // Catch and ignore all binder exceptions + Log.e(TAG, x.toString()); + } catch (ServiceSpecificException x) { + throwAsPublicException(x); + } + return new HashMap(); + } + + /** @hide */ + public <T> void setProperty(LowpanProperty<T> key, T value) throws LowpanException { + HashMap<String, T> prop = new HashMap<>(); + prop.put(key.getName(), value); + setProperties(prop); + } + + /** @hide */ + @Nullable + public <T> T getProperty(LowpanProperty<T> key) throws LowpanException { + Map<String, Object> map = getProperties(new String[] {key.getName()}); + if (map != null && !map.isEmpty()) { + // We know there is only one value. + return (T) map.values().iterator().next(); + } + return null; + } + + @Nullable + <T> String getPropertyAsString(LowpanProperty<T> key) throws LowpanException { + try { + return mBinder.getPropertyAsString(key.getName()); + } catch (RemoteException x) { + // Catch and ignore all binder exceptions + Log.e(TAG, x.toString()); + } catch (ServiceSpecificException x) { + throwAsPublicException(x); + } + return null; + } + + int getPropertyAsInt(LowpanProperty<Integer> key) throws LowpanException { + Integer value = getProperty(key); + return (value != null) ? value : 0; + } + + boolean getPropertyAsBoolean(LowpanProperty<Boolean> key) throws LowpanException { + Boolean value = getProperty(key); + return (value != null) ? value : 0; + } + + ////////////////////////////////////////////////////////////////////////// + // Public Actions + + /** + * Form a new network with the given network information optional credential. Unspecified fields + * in the network information will be filled in with reasonable values. If the network + * credential is unspecified, one will be generated automatically. + * + * <p>This method will block until either the network was successfully formed or an error + * prevents the network form being formed. + * + * <p>Upon success, the interface will be up and attached to the newly formed network. + * + * @see #join(LowpanProvision) + */ + public void form(@NonNull LowpanProvision provision) throws LowpanException { + try { + Map<String, Object> parameters = new HashMap(); + provision.addToMap(parameters); + mBinder.form(parameters); + } catch (RemoteException x) { + throwAsPublicException(x); + } catch (ServiceSpecificException x) { + throwAsPublicException(x); + } + } + + /** + * Attempts to join a new network with the given network information. This method will block + * until either the network was successfully joined or an error prevented the network from being + * formed. Upon success, the interface will be up and attached to the newly joined network. + * + * <p>Note that “joining” is distinct from “attaching”: Joining requires at least one other peer + * device to be present in order for the operation to complete successfully. + */ + public void join(@NonNull LowpanProvision provision) throws LowpanException { + try { + Map<String, Object> parameters = new HashMap(); + provision.addToMap(parameters); + mBinder.join(parameters); + } catch (RemoteException x) { + throwAsPublicException(x); + } catch (ServiceSpecificException x) { + throwAsPublicException(x); + } + } + + /** + * Attaches to the network described by identity and credential. This is similar to {@link + * #join}, except that (assuming the identity and credential are valid) it will always succeed + * and provision the interface, even if there are no peers nearby. + * + * <p>This method will block execution until the operation has completed. + */ + public void attach(@NonNull LowpanProvision provision) throws LowpanException { + if (ROLE_DETACHED.equals(getRole())) { + Map<String, Object> parameters = new HashMap(); + provision.addToMap(parameters); + setProperties(parameters); + setUp(true); + } else { + throw new LowpanException(LowpanException.LOWPAN_ALREADY); + } + } + + /** + * Bring down the network interface and forget all non-volatile details about the current + * network. + * + * <p>This method will block execution until the operation has completed. + */ + public void leave() throws LowpanException { + try { + mBinder.leave(); + } catch (RemoteException x) { + throwAsPublicException(x); + } catch (ServiceSpecificException x) { + throwAsPublicException(x); + } + } + + /** + * Start a new commissioning session. Will fail if the interface is attached to a network or if + * the interface is disabled. + */ + public @NonNull LowpanCommissioningSession startCommissioningSession( + @NonNull LowpanBeaconInfo beaconInfo) throws LowpanException { + + /* TODO: Implement startCommissioningSession */ + throw new LowpanException(LowpanException.LOWPAN_FEATURE_NOT_SUPPORTED); + } + + /** + * Reset this network interface as if it has been power cycled. Will bring the network interface + * down if it was previously up. Will not erase any non-volatile settings. + * + * <p>This method will block execution until the operation has completed. + * + * @hide + */ + public void reset() throws LowpanException { + try { + mBinder.reset(); + } catch (RemoteException x) { + throwAsPublicException(x); + } catch (ServiceSpecificException x) { + throwAsPublicException(x); + } + } + + ////////////////////////////////////////////////////////////////////////// + // Public Getters and Setters + + /** + * Returns the name of this network interface. + * + * <p>Will return empty string if this interface is no longer viable. + */ + @NonNull + public String getName() { + try { + return mBinder.getName(); + } catch (RemoteException x) { + // Catch and ignore all binder exceptions + // when fetching the name. + Log.e(TAG, x.toString()); + } catch (ServiceSpecificException x) { + // Catch and ignore all service-specific exceptions + // when fetching the name. + Log.e(TAG, x.toString()); + } + return ""; + } + + /** + * Indicates if the interface is enabled or disabled. + * + * @see #setEnabled + * @see android.net.lowpan.LowpanException#LOWPAN_DISABLED + */ + public boolean isEnabled() { + try { + return getPropertyAsBoolean(LowpanProperties.KEY_INTERFACE_ENABLED); + } catch (LowpanException x) { + return false; + } + } + + /** + * Enables or disables the LoWPAN interface. When disabled, the interface is put into a low-power + * state and all commands that require the NCP to be queried will fail with {@link + * android.net.lowpan.LowpanException#LOWPAN_DISABLED}. + * + * @see #isEnabled + * @see android.net.lowpan.LowpanException#LOWPAN_DISABLED + * @hide + */ + public void setEnabled(boolean enabled) throws LowpanException { + setProperty(LowpanProperties.KEY_INTERFACE_ENABLED, enabled); + } + + /** + * Indicates if the network interface is up or down. + * + * @hide + */ + public boolean isUp() { + try { + return getPropertyAsBoolean(LowpanProperties.KEY_INTERFACE_UP); + } catch (LowpanException x) { + return false; + } + } + + /** + * Bring up or shut down the network interface. + * + * <p>This method brings up or shuts down the network interface, attaching or (gracefully) + * detaching from the currently configured LoWPAN network as appropriate. + * + * @hide + */ + public void setUp(boolean interfaceUp) throws LowpanException { + setProperty(LowpanProperties.KEY_INTERFACE_UP, interfaceUp); + } + + /** + * Indicates if there is at least one peer in range. + * + * @return <code>true</code> if we have at least one other peer in range, <code>false</code> + * otherwise. + */ + public boolean isConnected() { + try { + return getPropertyAsBoolean(LowpanProperties.KEY_INTERFACE_CONNECTED); + } catch (LowpanException x) { + return false; + } + } + + /** + * Indicates if this interface is currently commissioned onto an existing network. If the + * interface is commissioned, the interface may be brought up using setUp(). + */ + public boolean isCommissioned() { + try { + return getPropertyAsBoolean(LowpanProperties.KEY_INTERFACE_COMMISSIONED); + } catch (LowpanException x) { + return false; + } + } + + /** + * Get interface state + * + * <h3>State Diagram</h3> + * + * <img src="LowpanInterface-1.png" /> + * + * @return The current state of the interface. + * @see #STATE_OFFLINE + * @see #STATE_COMMISSIONING + * @see #STATE_ATTACHING + * @see #STATE_ATTACHED + * @see #STATE_FAULT + */ + public String getState() { + try { + return getProperty(LowpanProperties.KEY_INTERFACE_STATE); + } catch (LowpanException x) { + Log.e(TAG, x.toString()); + return STATE_FAULT; + } + } + + /** TODO: doc */ + public LowpanIdentity getLowpanIdentity() { + LowpanIdentity.Builder builder = new LowpanIdentity.Builder(); + try { + builder.updateFromMap(getProperties(NETWORK_PROPERTY_KEYS)); + } catch (LowpanException x) { + // We ignore all LoWPAN-specitic exceptions here. + } + + return builder.build(); + } + + /** + * TODO: doc + * + * @hide + */ + public void setLowpanIdentity(LowpanIdentity network) throws LowpanException { + Map<String, Object> map = new HashMap(); + LowpanIdentity.addToMap(map, network); + setProperties(map); + } + + /** TODO: doc */ + @NonNull + public String getRole() { + String role = null; + + try { + role = getProperty(LowpanProperties.KEY_NETWORK_ROLE); + } catch (LowpanException x) { + // We ignore all LoWPAN-specitic exceptions here. + Log.e(TAG, x.toString()); + } + + if (role == null) { + role = ROLE_DETACHED; + } + + return role; + } + + /** TODO: doc */ + @Nullable + public LowpanCredential getLowpanCredential() { + LowpanCredential credential = null; + + try { + Integer keyIndex = getProperty(LowpanProperties.KEY_NETWORK_MASTER_KEY_INDEX); + + if (keyIndex == null) { + credential = + LowpanCredential.createMasterKey( + getProperty(LowpanProperties.KEY_NETWORK_MASTER_KEY)); + } else { + credential = + LowpanCredential.createMasterKey( + getProperty(LowpanProperties.KEY_NETWORK_MASTER_KEY), + keyIndex.intValue()); + } + } catch (LowpanException x) { + // We ignore all LoWPAN-specitic exceptions here. + Log.e(TAG, x.toString()); + } + + return credential; + } + + /** + * TODO: doc + * + * @hide + */ + public void setLowpanCredential(LowpanCredential networkCredential) throws LowpanException { + Map<String, Object> map = new HashMap(); + networkCredential.addToMap(map); + setProperties(map); + } + + ////////////////////////////////////////////////////////////////////////// + // Listener Support + + /** + * Registers a subclass of {@link LowpanInterface.Callback} to receive events. + * + * @param cb Subclass of {@link LowpanInterface.Callback} which will receive events. + * @param handler If not <code>null</code>, events will be dispatched via the given handler + * object. If <code>null</code>, the thread upon which events will be dispatched is + * unspecified. + * @see #registerCallback(Callback) + * @see #unregisterCallback(Callback) + */ + public void registerCallback(@NonNull Callback cb, @Nullable Handler handler) { + ILowpanInterfaceListener.Stub listenerBinder = + new ILowpanInterfaceListener.Stub() { + public void onPropertiesChanged(Map<String, Object> properties) { + Runnable runnable = + new Runnable() { + @Override + public void run() { + for (String key : (Set<String>) properties.keySet()) { + Object value = properties.get(key); + switch (key) { + case ILowpanInterface.KEY_INTERFACE_ENABLED: + cb.onEnabledChanged( + ((Boolean) value).booleanValue()); + break; + case ILowpanInterface.KEY_INTERFACE_UP: + cb.onUpChanged( + ((Boolean) value).booleanValue()); + break; + case ILowpanInterface.KEY_INTERFACE_CONNECTED: + cb.onConnectedChanged( + ((Boolean) value).booleanValue()); + break; + case ILowpanInterface.KEY_INTERFACE_STATE: + cb.onStateChanged((String) value); + break; + case ILowpanInterface.KEY_NETWORK_NAME: + case ILowpanInterface.KEY_NETWORK_PANID: + case ILowpanInterface.KEY_NETWORK_XPANID: + case ILowpanInterface.KEY_CHANNEL: + cb.onLowpanIdentityChanged(getLowpanIdentity()); + break; + case ILowpanInterface.KEY_NETWORK_ROLE: + cb.onRoleChanged(value.toString()); + break; + } + } + cb.onPropertiesChanged(properties); + } + }; + + if (handler != null) { + handler.post(runnable); + } else { + runnable.run(); + } + } + }; + try { + mBinder.addListener(listenerBinder); + } catch (RemoteException x) { + // Log and ignore. If this happens, this interface + // is likely dead anyway. + Log.e(TAG, x.toString()); + } + synchronized (mListenerMap) { + mListenerMap.put(System.identityHashCode(cb), listenerBinder); + } + } + + /** + * Registers a subclass of {@link LowpanInterface.Callback} to receive events. + * + * <p>The thread upon which events will be dispatched is unspecified. + * + * @param cb Subclass of {@link LowpanInterface.Callback} which will receive events. + * @see #registerCallback(Callback, Handler) + * @see #unregisterCallback(Callback) + */ + public void registerCallback(Callback cb) { + registerCallback(cb, null); + } + + /** + * Unregisters a previously registered callback class. + * + * @param cb Subclass of {@link LowpanInterface.Callback} which was previously registered to + * receive events. + * @see #registerCallback(Callback, Handler) + * @see #registerCallback(Callback) + */ + public void unregisterCallback(Callback cb) { + int hashCode = System.identityHashCode(cb); + ILowpanInterfaceListener listenerBinder = mListenerMap.get(hashCode); + + if (listenerBinder != null) { + synchronized (mListenerMap) { + mListenerMap.remove(hashCode); + } + try { + mBinder.removeListener(listenerBinder); + } catch (RemoteException x) { + // Catch and ignore all binder exceptions + Log.e(TAG, x.toString()); + } + } + } + + ////////////////////////////////////////////////////////////////////////// + // Active and Passive Scanning + + /** + * Creates a new {@link android.net.lowpan.LowpanScanner} object for this interface. + * + * <p>This method allocates a new unique object for each call. + * + * @see android.net.lowpan.LowpanScanner + */ + public @NonNull LowpanScanner createScanner() { + return new LowpanScanner(mBinder); + } + + ////////////////////////////////////////////////////////////////////////// + // Route Management + + /** + * Advertise the given IP prefix as an on-mesh prefix. + * + * @hide + */ + public void addOnMeshPrefix(IpPrefix prefix, int flags) throws LowpanException { + try { + mBinder.addOnMeshPrefix(prefix, flags); + } catch (RemoteException x) { + throwAsPublicException(x); + } catch (ServiceSpecificException x) { + throwAsPublicException(x); + } + } + + /** + * Remove an IP prefix previously advertised by this device from the list of advertised on-mesh + * prefixes. + * + * @hide + */ + public void removeOnMeshPrefix(IpPrefix prefix) { + try { + mBinder.removeOnMeshPrefix(prefix); + } catch (RemoteException x) { + // Catch and ignore all binder exceptions + Log.e(TAG, x.toString()); + } catch (ServiceSpecificException x) { + // Catch and ignore all service exceptions + Log.e(TAG, x.toString()); + } + } + + /** + * Advertise this device to other devices on the mesh network as having a specific route to the + * given network. This device will then receive forwarded traffic for that network. + * + * @hide + */ + public void addExternalRoute(IpPrefix prefix, int flags) throws LowpanException { + try { + mBinder.addExternalRoute(prefix, flags); + } catch (RemoteException x) { + throwAsPublicException(x); + } catch (ServiceSpecificException x) { + throwAsPublicException(x); + } + } + + /** + * Revoke a previously advertised specific route to the given network. + * + * @hide + */ + public void removeExternalRoute(IpPrefix prefix) { + try { + mBinder.removeExternalRoute(prefix); + } catch (RemoteException x) { + // Catch and ignore all binder exceptions + Log.e(TAG, x.toString()); + } catch (ServiceSpecificException x) { + // Catch and ignore all service exceptions + Log.e(TAG, x.toString()); + } + } +} diff --git a/lowpan/java/android/net/lowpan/LowpanManager.java b/lowpan/java/android/net/lowpan/LowpanManager.java new file mode 100644 index 000000000000..b58608da7c21 --- /dev/null +++ b/lowpan/java/android/net/lowpan/LowpanManager.java @@ -0,0 +1,283 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.lowpan; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.content.Context; +import android.os.DeadObjectException; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.AndroidException; +import android.util.Log; +import java.util.HashMap; + +/** + * Manager object for looking up LoWPAN interfaces. + * + * @hide + */ +//@SystemApi +public class LowpanManager { + private static final String TAG = LowpanManager.class.getSimpleName(); + + ////////////////////////////////////////////////////////////////////////// + // Public Classes + + /** @hide */ + //@SystemApi + public abstract static class Callback { + public void onInterfaceAdded(LowpanInterface lowpan_interface) {} + + public void onInterfaceRemoved(LowpanInterface lowpan_interface) {} + } + + ////////////////////////////////////////////////////////////////////////// + // Instance Variables + + private ILowpanManager mManager; + private HashMap<Integer, ILowpanManagerListener> mListenerMap = new HashMap<>(); + + ////////////////////////////////////////////////////////////////////////// + + private static LowpanManager sSingletonInstance; + + ////////////////////////////////////////////////////////////////////////// + // Static Methods + + /** Returns a reference to the LowpanManager object, allocating it if necessary. */ + public static LowpanManager getManager() { + return from(null); + } + + public static LowpanManager from(Context context) { + // TODO: Actually get this from the context! + + if (sSingletonInstance == null) { + sSingletonInstance = new LowpanManager(); + } + return sSingletonInstance; + } + + ////////////////////////////////////////////////////////////////////////// + // Constructors + + /** + * Private LowpanManager constructor. Since we are a singleton, we do not allow external + * construction. + */ + private LowpanManager() {} + + ////////////////////////////////////////////////////////////////////////// + // Private Methods + + /** + * Returns a reference to the ILowpanManager interface, provided by the LoWPAN Manager Service. + */ + @Nullable + private ILowpanManager getILowpanManager() { + ILowpanManager manager = mManager; + if (manager == null) { + IBinder serviceBinder = + new ServiceManager().getService(ILowpanManager.LOWPAN_SERVICE_NAME); + mManager = manager = ILowpanManager.Stub.asInterface(serviceBinder); + + // Add any listeners + synchronized (mListenerMap) { + for (Integer hashObj : mListenerMap.keySet()) { + try { + manager.addListener(mListenerMap.get(hashObj)); + } catch (RemoteException x) { + // Consider any failure here as implying the manager is defunct + mManager = manager = null; + } + } + } + } + return manager; + } + + ////////////////////////////////////////////////////////////////////////// + // Public Methods + + /** + * Returns a reference to the requested LowpanInterface object. If the given interface doesn't + * exist, or it is not a LoWPAN interface, returns null. + */ + @Nullable + public LowpanInterface getInterface(@NonNull String name) { + LowpanInterface ret = null; + ILowpanManager manager = getILowpanManager(); + + // Maximum number of tries is two. We should only try + // more than once if our manager has died or there + // was some sort of AIDL buffer full event. + for (int i = 0; i < 2 && manager != null; i++) { + try { + ILowpanInterface iface = manager.getInterface(name); + if (iface != null) { + ret = LowpanInterface.getInterfaceFromBinder(iface.asBinder()); + } + break; + } catch (RemoteException x) { + // In all of the cases when we get this exception, we reconnect and try again + mManager = null; + manager = getILowpanManager(); + } + } + return ret; + } + + /** + * Returns a reference to the first registered LowpanInterface object. If there are no LoWPAN + * interfaces registered, returns null. + */ + @Nullable + public LowpanInterface getInterface() { + String[] ifaceList = getInterfaceList(); + if (ifaceList != null && ifaceList.length > 0) { + return getInterface(ifaceList[0]); + } + return null; + } + + /** + * Returns a string array containing the names of LoWPAN interfaces. This list may contain fewer + * interfaces if the calling process does not have permissions to see individual interfaces. + */ + @NonNull + public String[] getInterfaceList() { + ILowpanManager manager = getILowpanManager(); + + if (manager != null) { + try { + return manager.getInterfaceList(); + + } catch (RemoteException x) { + // In all of the cases when we get this exception, we reconnect and try again + mManager = null; + try { + manager = getILowpanManager(); + if (manager != null) { + return manager.getInterfaceList(); + } + } catch (RemoteException ex) { + // Something weird is going on, so we log it + // and fall back thru to returning an empty array. + Log.e(TAG, ex.toString()); + mManager = null; + } + } + } + + // Return empty list if we have no service. + return new String[0]; + } + + /** + * Registers a callback object to receive notifications when LoWPAN interfaces are added or + * removed. + * + * @hide + */ + public void registerCallback(@NonNull Callback cb, @Nullable Handler handler) + throws LowpanException { + ILowpanManagerListener.Stub listenerBinder = + new ILowpanManagerListener.Stub() { + public void onInterfaceAdded(ILowpanInterface lowpan_interface) { + Runnable runnable = + new Runnable() { + @Override + public void run() { + cb.onInterfaceAdded( + LowpanInterface.getInterfaceFromBinder( + lowpan_interface.asBinder())); + } + }; + + if (handler != null) { + handler.post(runnable); + } else { + runnable.run(); + } + } + + public void onInterfaceRemoved(ILowpanInterface lowpan_interface) { + Runnable runnable = + new Runnable() { + @Override + public void run() { + cb.onInterfaceRemoved( + LowpanInterface.getInterfaceFromBinder( + lowpan_interface.asBinder())); + } + }; + + if (handler != null) { + handler.post(runnable); + } else { + runnable.run(); + } + } + }; + ILowpanManager manager = getILowpanManager(); + if (manager != null) { + try { + manager.addListener(listenerBinder); + } catch (DeadObjectException x) { + mManager = null; + // Tickle the ILowpanManager instance, which might + // get us added back. + getILowpanManager(); + } catch (Throwable x) { + LowpanException.throwAsPublicException(x); + } + } + synchronized (mListenerMap) { + mListenerMap.put(Integer.valueOf(System.identityHashCode(cb)), listenerBinder); + } + } + + /** @hide */ + public void registerCallback(@NonNull Callback cb) throws LowpanException { + registerCallback(cb, null); + } + + /** + * Unregisters a previously registered {@link LowpanManager.Callback} object. + * + * @hide + */ + public void unregisterCallback(@NonNull Callback cb) throws AndroidException { + Integer hashCode = Integer.valueOf(System.identityHashCode(cb)); + ILowpanManagerListener listenerBinder = mListenerMap.get(hashCode); + if (listenerBinder != null) { + synchronized (mListenerMap) { + mListenerMap.remove(hashCode); + } + if (getILowpanManager() != null) { + try { + mManager.removeListener(listenerBinder); + } catch (DeadObjectException x) { + mManager = null; + } + } + } + } +} diff --git a/lowpan/java/android/net/lowpan/LowpanProperties.java b/lowpan/java/android/net/lowpan/LowpanProperties.java new file mode 100644 index 000000000000..0d5acc2d0c21 --- /dev/null +++ b/lowpan/java/android/net/lowpan/LowpanProperties.java @@ -0,0 +1,125 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.lowpan; + +import android.net.LinkAddress; +import android.net.RouteInfo; +import java.util.List; + +/** {@hide} */ +public final class LowpanProperties { + + public static final LowpanProperty<Boolean> KEY_INTERFACE_ENABLED = + new LowpanStandardProperty( + "android.net.lowpan.property.INTERFACE_ENABLED", Boolean.class); + public static final LowpanProperty<Boolean> KEY_INTERFACE_COMMISSIONED = + new LowpanStandardProperty( + "android.net.lowpan.property.INTERFACE_COMMISSIONED", Boolean.class); + public static final LowpanProperty<Boolean> KEY_INTERFACE_CONNECTED = + new LowpanStandardProperty( + "android.net.lowpan.property.INTERFACE_CONNECTED", Boolean.class); + public static final LowpanProperty<Boolean> KEY_INTERFACE_UP = + new LowpanStandardProperty("android.net.lowpan.property.INTERFACE_UP", Boolean.class); + public static final LowpanProperty<String> KEY_INTERFACE_STATE = + new LowpanStandardProperty("android.net.lowpan.property.INTERFACE_STATE", String.class); + + public static final LowpanProperty<String> KEY_NETWORK_NAME = + new LowpanStandardProperty("android.net.lowpan.property.NETWORK_NAME", Boolean.class); + public static final LowpanProperty<Integer> KEY_NETWORK_PANID = + new LowpanStandardProperty("android.net.lowpan.property.NETWORK_PANID", Integer.class); + public static final LowpanProperty<byte[]> KEY_NETWORK_XPANID = + new LowpanStandardProperty("android.net.lowpan.property.NETWORK_XPANID", byte[].class); + public static final LowpanProperty<byte[]> KEY_NETWORK_MASTER_KEY = + new LowpanStandardProperty( + "android.net.lowpan.property.NETWORK_MASTER_KEY", byte[].class); + public static final LowpanProperty<Integer> KEY_NETWORK_MASTER_KEY_INDEX = + new LowpanStandardProperty( + "android.net.lowpan.property.NETWORK_MASTER_KEY_INDEX", Integer.class); + public static final LowpanProperty<Integer> KEY_NETWORK_TYPE = + new LowpanStandardProperty("android.net.lowpan.property.NETWORK_TYPE", Integer.class); + public static final LowpanProperty<String> KEY_NETWORK_ROLE = + new LowpanStandardProperty("android.net.lowpan.property.NETWORK_ROLE", String.class); + + public static final LowpanProperty<Integer> KEY_CHANNEL = + new LowpanStandardProperty("android.net.lowpan.property.CHANNEL", Integer.class); + public static final LowpanProperty<int[]> KEY_CHANNEL_MASK = + new LowpanStandardProperty("android.net.lowpan.property.CHANNEL_MASK", int[].class); + public static final LowpanProperty<Integer> KEY_MAX_TX_POWER = + new LowpanStandardProperty("android.net.lowpan.property.MAX_TX_POWER", Integer.class); + public static final LowpanProperty<Integer> KEY_RSSI = + new LowpanStandardProperty("android.net.lowpan.property.RSSI", Integer.class); + + public static final LowpanProperty<Integer> KEY_LQI = + new LowpanStandardProperty("android.net.lowpan.property.LQI", Integer.class); + public static final LowpanProperty<byte[]> KEY_BEACON_ADDRESS = + new LowpanStandardProperty("android.net.lowpan.property.BEACON_ADDRESS", byte[].class); + public static final LowpanProperty<Boolean> KEY_BEACON_CAN_ASSIST = + new LowpanStandardProperty( + "android.net.lowpan.property.BEACON_CAN_ASSIST", Boolean.class); + + public static final LowpanProperty<String> KEY_DRIVER_VERSION = + new LowpanStandardProperty("android.net.lowpan.property.DRIVER_VERSION", String.class); + + public static final LowpanProperty<String> KEY_NCP_VERSION = + new LowpanStandardProperty("android.net.lowpan.property.NCP_VERSION", String.class); + + public static final LowpanProperty<List<LinkAddress>> KEY_LINK_ADDRESS_ARRAY = + new LowpanStandardProperty( + "android.net.lowpan.property.LINK_ADDRESS_ARRAY", LinkAddress[].class); + + public static final LowpanProperty<List<RouteInfo>> KEY_ROUTE_INFO_ARRAY = + new LowpanStandardProperty( + "android.net.lowpan.property.ROUTE_INFO_ARRAY", RouteInfo[].class); + + /** @hide */ + public static final LowpanProperty<byte[]> KEY_EXTENDED_ADDRESS = + new LowpanStandardProperty( + "android.net.lowpan.property.EXTENDED_ADDRESS", byte[].class); + + /** @hide */ + public static final LowpanProperty<byte[]> KEY_MAC_ADDRESS = + new LowpanStandardProperty("android.net.lowpan.property.MAC_ADDRESS", byte[].class); + + /** @hide */ + private LowpanProperties() {} + + /** @hide */ + static final class LowpanStandardProperty<T> extends LowpanProperty<T> { + private final String mName; + private final Class<T> mType; + + LowpanStandardProperty(String name, Class<T> type) { + mName = name; + mType = type; + } + + @Override + public String getName() { + return mName; + } + + @Override + public Class<T> getType() { + return mType; + } + + @Override + public String toString() { + return getName(); + } + } +} diff --git a/lowpan/java/android/net/lowpan/LowpanProperty.java b/lowpan/java/android/net/lowpan/LowpanProperty.java new file mode 100644 index 000000000000..7f26986e9da0 --- /dev/null +++ b/lowpan/java/android/net/lowpan/LowpanProperty.java @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.lowpan; + +import java.util.Map; + +/** {@hide} */ +public abstract class LowpanProperty<T> { + public abstract String getName(); + + public abstract Class<T> getType(); + + public void putInMap(Map map, T value) { + map.put(getName(), value); + } + + public T getFromMap(Map map) { + return (T) map.get(getName()); + } +} diff --git a/lowpan/java/android/net/lowpan/LowpanProvision.java b/lowpan/java/android/net/lowpan/LowpanProvision.java new file mode 100644 index 000000000000..ace1f9c05c4d --- /dev/null +++ b/lowpan/java/android/net/lowpan/LowpanProvision.java @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.lowpan; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import java.util.Map; + +/** + * Describes the information needed to describe a network + * + * @hide + */ +//@SystemApi +public class LowpanProvision { + + ////////////////////////////////////////////////////////////////////////// + // Builder + + /** @hide */ + //@SystemApi + public static class Builder { + private final LowpanProvision provision = new LowpanProvision(); + + public Builder setLowpanIdentity(@NonNull LowpanIdentity identity) { + provision.mIdentity = identity; + return this; + } + + public Builder setLowpanCredential(@NonNull LowpanCredential credential) { + provision.mCredential = credential; + return this; + } + + public LowpanProvision build() { + return provision; + } + } + + private LowpanProvision() {} + + ////////////////////////////////////////////////////////////////////////// + // Instance Variables + + private LowpanIdentity mIdentity = new LowpanIdentity(); + private LowpanCredential mCredential = null; + + ////////////////////////////////////////////////////////////////////////// + // Public Getters and Setters + + @NonNull + public LowpanIdentity getLowpanIdentity() { + return mIdentity; + } + + @Nullable + public LowpanCredential getLowpanCredential() { + return mCredential; + } + + ////////////////////////////////////////////////////////////////////////// + // LoWPAN-Internal Methods + + static void addToMap(Map<String, Object> parameters, LowpanProvision provision) + throws LowpanException { + provision.mIdentity.addToMap(parameters); + if (provision.mCredential != null) { + provision.mCredential.addToMap(parameters); + } + } + + void addToMap(Map<String, Object> parameters) throws LowpanException { + addToMap(parameters, this); + } + + @Override + public String toString() { + StringBuffer sb = new StringBuffer(); + + sb.append("LowpanProvision { identity => ").append(mIdentity.toString()); + + if (mCredential != null) { + sb.append(", credential: ").append(mCredential.toString()); + } + + sb.append("}"); + + return sb.toString(); + } +}; diff --git a/lowpan/java/android/net/lowpan/LowpanScanner.java b/lowpan/java/android/net/lowpan/LowpanScanner.java new file mode 100644 index 000000000000..754f72e3cb84 --- /dev/null +++ b/lowpan/java/android/net/lowpan/LowpanScanner.java @@ -0,0 +1,317 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.lowpan; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.Handler; +import android.os.RemoteException; +import android.os.ServiceSpecificException; +import android.util.Log; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * LoWPAN Scanner + * + * <p>This class allows performing network (active) scans and energy (passive) scans. + * + * @see LowpanInterface + * @hide + */ +//@SystemApi +public class LowpanScanner { + private static final String TAG = LowpanInterface.class.getSimpleName(); + + ////////////////////////////////////////////////////////////////////////// + // Public Classes + + /** + * Callback base class for LowpanScanner + * + * @hide + */ + //@SystemApi + public abstract static class Callback { + public void onNetScanBeacon(LowpanBeaconInfo beacon) {} + + public void onEnergyScanResult(LowpanEnergyScanResult result) {} + + public void onScanFinished() {} + } + + ////////////////////////////////////////////////////////////////////////// + // Instance Variables + + private ILowpanInterface mBinder; + private Callback mCallback = null; + private Handler mHandler = null; + private List<Integer> mChannelMask = null; + private int mTxPower = Integer.MAX_VALUE; + + ////////////////////////////////////////////////////////////////////////// + // Constructors/Accessors and Exception Glue + + LowpanScanner(@NonNull ILowpanInterface binder) { + mBinder = binder; + } + + /** Sets an instance of {@link LowpanScanner.Callback} to receive events. */ + public void setCallback(@Nullable Callback cb, @Nullable Handler handler) { + mCallback = cb; + mHandler = handler; + } + + /** Sets an instance of {@link LowpanScanner.Callback} to receive events. */ + public void setCallback(@Nullable Callback cb) { + setCallback(cb, null); + } + + /** + * Sets the channel mask to use when scanning. + * + * @param mask The channel mask to use when scanning. If <code>null</code>, any previously set + * channel mask will be cleared and all channels not masked by the current regulatory zone + * will be scanned. + */ + public void setChannelMask(@Nullable Collection<Integer> mask) { + if (mask == null) { + mChannelMask = null; + } else { + if (mChannelMask == null) { + mChannelMask = new ArrayList<>(); + } else { + mChannelMask.clear(); + } + mChannelMask.addAll(mask); + } + } + + /** + * Gets the current channel mask. + * + * @return the current channel mask, or <code>null</code> if no channel mask is currently set. + */ + public @Nullable Collection<Integer> getChannelMask() { + return mChannelMask.clone(); + } + + /** + * Adds a channel to the channel mask used for scanning. + * + * <p>If a channel mask was previously <code>null</code>, a new one is created containing only + * this channel. May be called multiple times to add additional channels ot the channel mask. + * + * @see #setChannelMask + * @see #getChannelMask + * @see #getTxPower + */ + public void addChannel(int channel) { + if (mChannelMask == null) { + mChannelMask = new ArrayList<>(); + } + mChannelMask.add(Integer.valueOf(channel)); + } + + /** + * Sets the maximum transmit power to be used for active scanning. + * + * <p>The actual transmit power used is the lesser of this value and the currently configured + * maximum transmit power for the interface. + * + * @see #getTxPower + */ + public void setTxPower(int txPower) { + mTxPower = txPower; + } + + /** + * Gets the maximum transmit power used for active scanning. + * + * @see #setTxPower + */ + public int getTxPower() { + return mTxPower; + } + + private Map<String, Object> createScanOptionMap() { + Map<String, Object> map = new HashMap(); + + if (mChannelMask != null) { + LowpanProperties.KEY_CHANNEL_MASK.putInMap( + map, mChannelMask.stream().mapToInt(i -> i).toArray()); + } + + if (mTxPower != Integer.MAX_VALUE) { + LowpanProperties.KEY_MAX_TX_POWER.putInMap(map, Integer.valueOf(mTxPower)); + } + + return map; + } + + /** + * Start a network scan. + * + * <p>This method will return once the scan has started. + * + * @see #stopNetScan + */ + public void startNetScan() throws LowpanException { + Map<String, Object> map = createScanOptionMap(); + + ILowpanNetScanCallback binderListener = + new ILowpanNetScanCallback.Stub() { + public void onNetScanBeacon(Map parameters) { + Callback callback = mCallback; + Handler handler = mHandler; + + if (callback == null) { + return; + } + + Runnable runnable = () -> callback.onNetScanBeacon( + new LowpanBeaconInfo.Builder() + .updateFromMap(parameters) + .build()); + + if (handler != null) { + handler.post(runnable); + } else { + runnable.run(); + } + } + + public void onNetScanFinished() { + Callback callback = mCallback; + Handler handler = mHandler; + + if (callback == null) { + return; + } + + Runnable runnable = () -> callback.onScanFinished(); + + if (handler != null) { + handler.post(runnable); + } else { + runnable.run(); + } + } + }; + + try { + mBinder.startNetScan(map, binderListener); + } catch (ServiceSpecificException|RemoteException x) { + LowpanException.throwAsPublicException(x); + } + } + + /** + * Stop a network scan currently in progress. + * + * @see #startNetScan + */ + public void stopNetScan() { + try { + mBinder.stopNetScan(); + } catch (RemoteException x) { + // Catch and ignore all binder exceptions + Log.e(TAG, x.toString()); + } + } + + /** + * Start an energy scan. + * + * <p>This method will return once the scan has started. + * + * @see #stopEnergyScan + */ + public void startEnergyScan() throws LowpanException { + Map<String, Object> map = createScanOptionMap(); + + ILowpanEnergyScanCallback binderListener = + new ILowpanEnergyScanCallback.Stub() { + public void onEnergyScanResult(int channel, int rssi) { + Callback callback = mCallback; + Handler handler = mHandler; + + if (callback == null) { + return; + } + + Runnable runnable = () -> { + if (callback != null) { + LowpanEnergyScanResult result = + new LowpanEnergyScanResult(); + result.setChannel(channel); + result.setMaxRssi(rssi); + callback.onEnergyScanResult(result); + } + }; + + if (handler != null) { + handler.post(runnable); + } else { + runnable.run(); + } + } + + public void onEnergyScanFinished() { + Callback callback = mCallback; + Handler handler = mHandler; + + if (callback == null) { + return; + } + + Runnable runnable = () -> callback.onScanFinished(); + + if (handler != null) { + handler.post(runnable); + } else { + runnable.run(); + } + } + }; + + try { + mBinder.startEnergyScan(map, binderListener); + } catch (RemoteException x) { + LowpanException.throwAsPublicException(x); + } catch (ServiceSpecificException x) { + LowpanException.throwAsPublicException(x); + } + } + + /** + * Stop an energy scan currently in progress. + * + * @see #startEnergyScan + */ + public void stopEnergyScan() { + try { + mBinder.stopEnergyScan(); + } catch (RemoteException x) { + // Catch and ignore all binder exceptions + Log.e(TAG, x.toString()); + } + } +} diff --git a/lowpan/java/android/net/lowpan/package.html b/lowpan/java/android/net/lowpan/package.html new file mode 100644 index 000000000000..342e32eefc1e --- /dev/null +++ b/lowpan/java/android/net/lowpan/package.html @@ -0,0 +1,29 @@ +<HTML> +<BODY> +<p>@SystemApi</p> +<!-- @hide --> +<p>Provides classes to manage Low-power Wireless Personal Area Network (LoWPAN) functionality on the device. +Examples of such network technologies include <a href="http://threadgroup.org/">Thread</a> and +<a href="http://www.zigbee.org/zigbee-for-developers/network-specifications/zigbeeip/">ZigBee IP</a>.</p> +<p>The LoWPAN APIs provide a means by which applications can communicate +with the lower-level wireless stack that provides LoWPAN network access.</p> + +<p>Some APIs may require the following user permissions:</p> +<ul> + <li>{@link android.Manifest.permission#ACCESS_LOWPAN_STATE}</li> + <li>{@link android.Manifest.permission#CHANGE_LOWPAN_STATE}</li> + <li>TBD</li> +</ul> + +<p class="note"><strong>Note:</strong> Not all Android-powered devices provide LoWPAN functionality. +If your application uses these APIs, declare so with a <a +href="{@docRoot}guide/topics/manifest/uses-feature-element.html">{@code <uses-feature>}</a> +element in the manifest file:</p> +<pre> +<manifest ...> + <uses-feature android:name="android.hardware.lowpan" /> + ... +</manifest> +</pre> +</BODY> +</HTML> |