Initial support for proposed android.net.lowpan
Change-Id: I0bf8edb5199d50d2a549a353b2785aef8134ff92
diff --git a/Android.mk b/Android.mk
index 28abb89..91b835d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -558,6 +558,15 @@
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 e39cb70..ed344b5 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 0000000..9e9164f
--- /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 0000000..f09dbe3
--- /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 0000000..647fcc1
--- /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 0000000..c99d732
--- /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 0000000..5a8d7dc
--- /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 0000000..d4846f6
--- /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 0000000..c20a6f8
--- /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 0000000..b344527
--- /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 0000000..50afe6d
--- /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 0000000..9cad00c
--- /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 0000000..dea4d78
--- /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 0000000..c680687
--- /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 0000000..8ff37f9
--- /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 0000000..2e7b560
--- /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 0000000..cd54819
--- /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 0000000..b58608d
--- /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 0000000..0d5acc2
--- /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 0000000..7f26986
--- /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 0000000..ace1f9c
--- /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 0000000..754f72e3
--- /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 0000000..342e32ee
--- /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>