summaryrefslogtreecommitdiff
path: root/lowpan/java
diff options
context:
space:
mode:
authorRobert Quattlebaum <rquattle@google.com>2017-06-27 15:01:05 -0700
committerRobert Quattlebaum <rquattle@google.com>2017-07-10 11:07:08 -0700
commitdfd6889a842b61eb0b02d9630dc7f9785a5da7ec (patch)
tree04e95a92d6bb88f191eec5845d86272af5f2bbe4 /lowpan/java
parentff09314508b3d24d34ebb8658735f9a19ed8005c (diff)
Updates and fixes to android.net.lowpan
This commit is an incremental improvement to the LoWPAN support API. Bug: b/33073713 Test: Successfully ran unit tests from Ib3750be5052bf1a90bf871756e9121b047d3871f Change-Id: I7ca6374b6a7135411eadf631bd7d84d7dc008203
Diffstat (limited to 'lowpan/java')
-rw-r--r--lowpan/java/android/net/lowpan/ILowpanInterface.aidl51
-rw-r--r--lowpan/java/android/net/lowpan/ILowpanInterfaceListener.aidl8
-rw-r--r--lowpan/java/android/net/lowpan/ILowpanManager.aidl1
-rw-r--r--lowpan/java/android/net/lowpan/LowpanException.java156
-rw-r--r--lowpan/java/android/net/lowpan/LowpanIdentity.java4
-rw-r--r--lowpan/java/android/net/lowpan/LowpanInterface.java292
-rw-r--r--lowpan/java/android/net/lowpan/LowpanManager.java289
-rw-r--r--lowpan/java/android/net/lowpan/LowpanProperties.java11
-rw-r--r--lowpan/java/android/net/lowpan/LowpanScanner.java24
9 files changed, 477 insertions, 359 deletions
diff --git a/lowpan/java/android/net/lowpan/ILowpanInterface.aidl b/lowpan/java/android/net/lowpan/ILowpanInterface.aidl
index 647fcc1eef3d..329e9fa1d743 100644
--- a/lowpan/java/android/net/lowpan/ILowpanInterface.aidl
+++ b/lowpan/java/android/net/lowpan/ILowpanInterface.aidl
@@ -37,41 +37,79 @@ interface ILowpanInterface {
//////////////////////////////////////////////////////////////////////////
// Property Key Constants
+ /** Type: Boolean */
const String KEY_INTERFACE_ENABLED = "android.net.lowpan.property.INTERFACE_ENABLED";
+
+ /** Type: Boolean */
const String KEY_INTERFACE_UP = "android.net.lowpan.property.INTERFACE_UP";
+
+ /** Type: Boolean */
const String KEY_INTERFACE_COMMISSIONED = "android.net.lowpan.property.INTERFACE_COMMISSIONED";
+
+ /** Type: Boolean */
const String KEY_INTERFACE_CONNECTED = "android.net.lowpan.property.INTERFACE_CONNECTED";
+
+ /** Type: String */
const String KEY_INTERFACE_STATE = "android.net.lowpan.property.INTERFACE_STATE";
+ /** Type: String */
const String KEY_NETWORK_NAME = "android.net.lowpan.property.NETWORK_NAME";
+
+ /** Type: Integer */
const String KEY_NETWORK_TYPE = "android.net.lowpan.property.NETWORK_TYPE";
+
+ /** Type: Integer */
const String KEY_NETWORK_PANID = "android.net.lowpan.property.NETWORK_PANID";
+
+ /** Type: byte[] */
const String KEY_NETWORK_XPANID = "android.net.lowpan.property.NETWORK_XPANID";
+
+ /** Type: String */
const String KEY_NETWORK_ROLE = "android.net.lowpan.property.NETWORK_ROLE";
+
+ /** Type: byte[] */
const String KEY_NETWORK_MASTER_KEY = "android.net.lowpan.property.NETWORK_MASTER_KEY";
+
+ /** Type: Integer */
const String KEY_NETWORK_MASTER_KEY_INDEX
= "android.net.lowpan.property.NETWORK_MASTER_KEY_INDEX";
+ /** Type: int[] */
const String KEY_SUPPORTED_CHANNELS = "android.net.lowpan.property.SUPPORTED_CHANNELS";
+
+ /** Type: Integer */
const String KEY_CHANNEL = "android.net.lowpan.property.CHANNEL";
+
+ /** Type: int[] */
const String KEY_CHANNEL_MASK = "android.net.lowpan.property.CHANNEL_MASK";
+
+ /** Type: Integer */
const String KEY_MAX_TX_POWER = "android.net.lowpan.property.MAX_TX_POWER";
+
+ /** Type: Integer */
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";
+ /** Type: Integer */
+ const String KEY_LQI = "android.net.lowpan.property.LQI";
+ /** Type: byte[] */
const String KEY_BEACON_ADDRESS = "android.net.lowpan.property.BEACON_ORIGIN_ADDRESS";
+
+ /** Type: Boolean */
const String KEY_BEACON_CAN_ASSIST = "android.net.lowpan.property.BEACON_CAN_ASSIST";
+ /** Type: String */
const String DRIVER_VERSION = "android.net.lowpan.property.DRIVER_VERSION";
+
+ /** Type: String */
const String NCP_VERSION = "android.net.lowpan.property.NCP_VERSION";
- /** @hide */
+ /** Type: byte[]
+ * @hide */
const String KEY_EXTENDED_ADDRESS = "android.net.lowpan.property.EXTENDED_ADDRESS";
- /** @hide */
+ /** Type: byte[]
+ * @hide */
const String KEY_MAC_ADDRESS = "android.net.lowpan.property.MAC_ADDRESS";
//////////////////////////////////////////////////////////////////////////
@@ -144,6 +182,9 @@ interface ILowpanInterface {
void startEnergyScan(in Map properties, ILowpanEnergyScanCallback listener);
oneway void stopEnergyScan();
+ String[] copyLinkAddresses();
+ IpPrefix[] copyLinkNetworks();
+
void addOnMeshPrefix(in IpPrefix prefix, int flags);
oneway void removeOnMeshPrefix(in IpPrefix prefix);
diff --git a/lowpan/java/android/net/lowpan/ILowpanInterfaceListener.aidl b/lowpan/java/android/net/lowpan/ILowpanInterfaceListener.aidl
index c99d732d4eba..0ae01a1873b5 100644
--- a/lowpan/java/android/net/lowpan/ILowpanInterfaceListener.aidl
+++ b/lowpan/java/android/net/lowpan/ILowpanInterfaceListener.aidl
@@ -16,7 +16,15 @@
package android.net.lowpan;
+import android.net.IpPrefix;
+
/** {@hide} */
interface ILowpanInterfaceListener {
oneway void onPropertiesChanged(in Map properties);
+
+ oneway void onLinkNetworkAdded(in IpPrefix prefix);
+ oneway void onLinkNetworkRemoved(in IpPrefix prefix);
+
+ oneway void onLinkAddressAdded(in String address);
+ oneway void onLinkAddressRemoved(in String address);
}
diff --git a/lowpan/java/android/net/lowpan/ILowpanManager.aidl b/lowpan/java/android/net/lowpan/ILowpanManager.aidl
index 5a8d7dce7c6f..326aa65e0fef 100644
--- a/lowpan/java/android/net/lowpan/ILowpanManager.aidl
+++ b/lowpan/java/android/net/lowpan/ILowpanManager.aidl
@@ -21,6 +21,7 @@ import android.net.lowpan.ILowpanManagerListener;
/** {@hide} */
interface ILowpanManager {
+ /* Keep this in sync with Context.LOWPAN_SERVICE */
const String LOWPAN_SERVICE_NAME = "lowpan";
ILowpanInterface getInterface(@utf8InCpp String name);
diff --git a/lowpan/java/android/net/lowpan/LowpanException.java b/lowpan/java/android/net/lowpan/LowpanException.java
index b43b2fe087e1..5a1f72993fb5 100644
--- a/lowpan/java/android/net/lowpan/LowpanException.java
+++ b/lowpan/java/android/net/lowpan/LowpanException.java
@@ -16,8 +16,6 @@
package android.net.lowpan;
-import android.os.DeadObjectException;
-import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.util.AndroidException;
@@ -49,92 +47,76 @@ public class LowpanException extends AndroidException {
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;
+ public static LowpanException rethrowAsLowpanException(ServiceSpecificException e)
+ throws LowpanException {
+ 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);
}
private final int mReason;
diff --git a/lowpan/java/android/net/lowpan/LowpanIdentity.java b/lowpan/java/android/net/lowpan/LowpanIdentity.java
index 9be45ef14b98..2d36f7fa9789 100644
--- a/lowpan/java/android/net/lowpan/LowpanIdentity.java
+++ b/lowpan/java/android/net/lowpan/LowpanIdentity.java
@@ -53,7 +53,7 @@ public class LowpanIdentity {
}
public Builder setXpanid(byte x[]) {
- identity.mXpanid = x.clone();
+ identity.mXpanid = (x != null ? x.clone() : null);
return this;
}
@@ -115,7 +115,7 @@ public class LowpanIdentity {
}
public byte[] getXpanid() {
- return mXpanid.clone();
+ return mXpanid != null ? mXpanid.clone() : null;
}
public int getPanid() {
diff --git a/lowpan/java/android/net/lowpan/LowpanInterface.java b/lowpan/java/android/net/lowpan/LowpanInterface.java
index 2bb4ecd380f2..55bf3994574d 100644
--- a/lowpan/java/android/net/lowpan/LowpanInterface.java
+++ b/lowpan/java/android/net/lowpan/LowpanInterface.java
@@ -18,9 +18,12 @@ package android.net.lowpan;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.content.Context;
import android.net.IpPrefix;
+import android.net.LinkAddress;
+import android.os.DeadObjectException;
import android.os.Handler;
-import android.os.IBinder;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.util.Log;
@@ -188,60 +191,35 @@ public class LowpanInterface {
public void onPropertiesChanged(@NonNull Map properties) {}
}
- private ILowpanInterface mBinder;
+ private final ILowpanInterface mBinder;
+ private final Looper mLooper;
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.
+ * Create a new LowpanInterface instance. Applications will almost always want to use {@link
+ * LowpanManager#getInterface LowpanManager.getInterface()} instead of this.
*
+ * @param context the application context
+ * @param service the Binder interface
+ * @param looper the Binder 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());
- }
+ public LowpanInterface(Context context, ILowpanInterface service, Looper looper) {
+ /* We aren't currently using the context, but if we need
+ * it later on we can easily add it to the class.
+ */
- /** {@hide} */
- public static final LowpanInterface getInterfaceFromBinder(IBinder binder) {
- return from(binder);
+ mBinder = service;
+ mLooper = looper;
}
/**
- * Returns the IBinder object associated with this interface.
+ * Returns the ILowpanInterface object associated with this interface.
*
* @hide
*/
- public IBinder getBinder() {
- return mBinder.asBinder();
- }
-
- private static void throwAsPublicException(Throwable t) throws LowpanException {
- LowpanException.throwAsPublicException(t);
+ public ILowpanInterface getService() {
+ return mBinder;
}
// Private Property Helpers
@@ -251,11 +229,10 @@ public class LowpanInterface {
mBinder.setProperties(properties);
} catch (RemoteException x) {
- // Catch and ignore all binder exceptions
- Log.e(TAG, x.toString());
+ throw x.rethrowAsRuntimeException();
} catch (ServiceSpecificException x) {
- throwAsPublicException(x);
+ throw LowpanException.rethrowAsLowpanException(x);
}
}
@@ -263,13 +240,13 @@ public class LowpanInterface {
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());
+ throw x.rethrowAsRuntimeException();
+
} catch (ServiceSpecificException x) {
- throwAsPublicException(x);
+ throw LowpanException.rethrowAsLowpanException(x);
}
- return new HashMap();
}
/** @hide */
@@ -294,13 +271,13 @@ public class LowpanInterface {
<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());
+ throw x.rethrowAsRuntimeException();
+
} catch (ServiceSpecificException x) {
- throwAsPublicException(x);
+ throw LowpanException.rethrowAsLowpanException(x);
}
- return null;
}
int getPropertyAsInt(LowpanProperty<Integer> key) throws LowpanException {
@@ -332,10 +309,12 @@ public class LowpanInterface {
Map<String, Object> parameters = new HashMap();
provision.addToMap(parameters);
mBinder.form(parameters);
+
} catch (RemoteException x) {
- throwAsPublicException(x);
+ throw x.rethrowAsRuntimeException();
+
} catch (ServiceSpecificException x) {
- throwAsPublicException(x);
+ throw LowpanException.rethrowAsLowpanException(x);
}
}
@@ -352,10 +331,12 @@ public class LowpanInterface {
Map<String, Object> parameters = new HashMap();
provision.addToMap(parameters);
mBinder.join(parameters);
+
} catch (RemoteException x) {
- throwAsPublicException(x);
+ throw x.rethrowAsRuntimeException();
+
} catch (ServiceSpecificException x) {
- throwAsPublicException(x);
+ throw LowpanException.rethrowAsLowpanException(x);
}
}
@@ -386,10 +367,12 @@ public class LowpanInterface {
public void leave() throws LowpanException {
try {
mBinder.leave();
+
} catch (RemoteException x) {
- throwAsPublicException(x);
+ throw x.rethrowAsRuntimeException();
+
} catch (ServiceSpecificException x) {
- throwAsPublicException(x);
+ throw LowpanException.rethrowAsLowpanException(x);
}
}
@@ -415,34 +398,26 @@ public class LowpanInterface {
public void reset() throws LowpanException {
try {
mBinder.reset();
+
} catch (RemoteException x) {
- throwAsPublicException(x);
+ throw x.rethrowAsRuntimeException();
+
} catch (ServiceSpecificException x) {
- throwAsPublicException(x);
+ throw LowpanException.rethrowAsLowpanException(x);
}
}
// Public Getters and Setters
- /**
- * Returns the name of this network interface.
- *
- * <p>Will return empty string if this interface is no longer viable.
- */
+ /** Returns the name of this network interface. */
@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());
+ throw x.rethrowAsRuntimeException();
}
- return "";
}
/**
@@ -640,58 +615,77 @@ public class LowpanInterface {
public void registerCallback(@NonNull Callback cb, @Nullable Handler handler) {
ILowpanInterfaceListener.Stub listenerBinder =
new ILowpanInterfaceListener.Stub() {
- public void onPropertiesChanged(Map properties) {
+ private Handler mHandler;
+
+ {
+ if (handler != null) {
+ mHandler = handler;
+ } else if (mLooper != null) {
+ mHandler = new Handler(mLooper);
+ } else {
+ mHandler = new Handler();
+ }
+ }
+
+ @Override public void onPropertiesChanged(Map 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;
- }
+ () -> {
+ 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);
}
+ cb.onPropertiesChanged(properties);
};
- if (handler != null) {
- handler.post(runnable);
- } else {
- runnable.run();
- }
+ mHandler.post(runnable);
+ }
+
+ @Override public void onLinkNetworkAdded(IpPrefix prefix) {
+ // Support for this event isn't yet implemented.
+ }
+
+ @Override public void onLinkNetworkRemoved(IpPrefix prefix) {
+ // Support for this event isn't yet implemented.
+ }
+
+ @Override public void onLinkAddressAdded(String address) {
+ // Support for this event isn't yet implemented.
+ }
+
+ @Override public void onLinkAddressRemoved(String address) {
+ // Support for this event isn't yet implemented.
}
};
try {
mBinder.addListener(listenerBinder);
} catch (RemoteException x) {
- // Log and ignore. If this happens, this interface
- // is likely dead anyway.
- Log.e(TAG, x.toString());
+ throw x.rethrowAsRuntimeException();
}
+
synchronized (mListenerMap) {
mListenerMap.put(System.identityHashCode(cb), listenerBinder);
}
@@ -728,9 +722,11 @@ public class LowpanInterface {
try {
mBinder.removeListener(listenerBinder);
+ } catch (DeadObjectException x) {
+ // We ignore a dead object exception because that
+ // pretty clearly means our callback isn't registered.
} catch (RemoteException x) {
- // Catch and ignore all binder exceptions
- Log.e(TAG, x.toString());
+ throw x.rethrowAsRuntimeException();
}
}
}
@@ -752,6 +748,46 @@ public class LowpanInterface {
// Route Management
/**
+ * Makes a copy of the internal list of LinkAddresses.
+ *
+ * @hide
+ */
+ public LinkAddress[] copyLinkAddresses() throws LowpanException {
+ try {
+ String[] linkAddressStrings = mBinder.copyLinkAddresses();
+ LinkAddress[] ret = new LinkAddress[linkAddressStrings.length];
+ int i = 0;
+ for (String str : linkAddressStrings) {
+ ret[i++] = new LinkAddress(str);
+ }
+ return ret;
+
+ } catch (RemoteException x) {
+ throw x.rethrowAsRuntimeException();
+
+ } catch (ServiceSpecificException x) {
+ throw LowpanException.rethrowAsLowpanException(x);
+ }
+ }
+
+ /**
+ * Makes a copy of the internal list of networks reachable on via this link.
+ *
+ * @hide
+ */
+ public IpPrefix[] copyLinkNetworks() throws LowpanException {
+ try {
+ return mBinder.copyLinkNetworks();
+
+ } catch (RemoteException x) {
+ throw x.rethrowAsRuntimeException();
+
+ } catch (ServiceSpecificException x) {
+ throw LowpanException.rethrowAsLowpanException(x);
+ }
+ }
+
+ /**
* Advertise the given IP prefix as an on-mesh prefix.
*
* @hide
@@ -759,10 +795,12 @@ public class LowpanInterface {
public void addOnMeshPrefix(IpPrefix prefix, int flags) throws LowpanException {
try {
mBinder.addOnMeshPrefix(prefix, flags);
+
} catch (RemoteException x) {
- throwAsPublicException(x);
+ throw x.rethrowAsRuntimeException();
+
} catch (ServiceSpecificException x) {
- throwAsPublicException(x);
+ throw LowpanException.rethrowAsLowpanException(x);
}
}
@@ -775,9 +813,10 @@ public class LowpanInterface {
public void removeOnMeshPrefix(IpPrefix prefix) {
try {
mBinder.removeOnMeshPrefix(prefix);
+
} catch (RemoteException x) {
- // Catch and ignore all binder exceptions
- Log.e(TAG, x.toString());
+ throw x.rethrowAsRuntimeException();
+
} catch (ServiceSpecificException x) {
// Catch and ignore all service exceptions
Log.e(TAG, x.toString());
@@ -793,10 +832,12 @@ public class LowpanInterface {
public void addExternalRoute(IpPrefix prefix, int flags) throws LowpanException {
try {
mBinder.addExternalRoute(prefix, flags);
+
} catch (RemoteException x) {
- throwAsPublicException(x);
+ throw x.rethrowAsRuntimeException();
+
} catch (ServiceSpecificException x) {
- throwAsPublicException(x);
+ throw LowpanException.rethrowAsLowpanException(x);
}
}
@@ -808,9 +849,10 @@ public class LowpanInterface {
public void removeExternalRoute(IpPrefix prefix) {
try {
mBinder.removeExternalRoute(prefix);
+
} catch (RemoteException x) {
- // Catch and ignore all binder exceptions
- Log.e(TAG, x.toString());
+ throw x.rethrowAsRuntimeException();
+
} 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
index ecdda49f718a..2d974ee79e40 100644
--- a/lowpan/java/android/net/lowpan/LowpanManager.java
+++ b/lowpan/java/android/net/lowpan/LowpanManager.java
@@ -19,14 +19,15 @@ 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.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.util.AndroidException;
-import android.util.Log;
+import java.lang.ref.WeakReference;
import java.util.HashMap;
+import java.util.Map;
+import java.util.WeakHashMap;
/**
* Manager object for looking up LoWPAN interfaces.
@@ -45,72 +46,119 @@ public class LowpanManager {
public void onInterfaceRemoved(LowpanInterface lowpanInterface) {}
}
- private Context mContext;
- private ILowpanManager mManager;
- private HashMap<Integer, ILowpanManagerListener> mListenerMap = new HashMap<>();
+ private final Map<Integer, ILowpanManagerListener> mListenerMap = new HashMap<>();
+ private final Map<String, LowpanInterface> mInterfaceCache = new HashMap<>();
- private static LowpanManager sSingletonInstance;
+ /* This is a WeakHashMap because we don't want to hold onto
+ * a strong reference to ILowpanInterface, so that it can be
+ * garbage collected if it isn't being used anymore. Since
+ * the value class holds onto this specific ILowpanInterface,
+ * we also need to have a weak reference to the value.
+ * This design pattern allows us to skip removal of items
+ * from this Map without leaking memory.
+ */
+ private final Map<ILowpanInterface, WeakReference<LowpanInterface>> mBinderCache =
+ new WeakHashMap<>();
+
+ private final ILowpanManager mService;
+ private final Context mContext;
+ private final Looper mLooper;
// 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) {
+ return (LowpanManager) context.getSystemService(Context.LOWPAN_SERVICE);
}
- public static LowpanManager from(Context context) {
- // TODO: Actually get this from the context!
+ /** @hide */
+ public static LowpanManager getManager() {
+ IBinder binder = ServiceManager.getService(Context.LOWPAN_SERVICE);
- if (sSingletonInstance == null) {
- sSingletonInstance = new LowpanManager();
+ if (binder != null) {
+ ILowpanManager service = ILowpanManager.Stub.asInterface(binder);
+ return new LowpanManager(service);
}
- return sSingletonInstance;
+
+ return null;
}
// Constructors
- /**
- * Private LowpanManager constructor. Since we are a singleton, we do not allow external
- * construction.
- */
- private LowpanManager() {}
-
- // Private Methods
+ LowpanManager(ILowpanManager service) {
+ mService = service;
+ mContext = null;
+ mLooper = null;
+ }
/**
- * Returns a reference to the ILowpanManager interface, provided by the LoWPAN Manager Service.
+ * Create a new LowpanManager instance. Applications will almost always want to use {@link
+ * android.content.Context#getSystemService Context.getSystemService()} to retrieve the standard
+ * {@link android.content.Context#LOWPAN_SERVICE Context.LOWPAN_SERVICE}.
+ *
+ * @param context the application context
+ * @param service the Binder interface
+ * @param looper the default Looper to run callbacks on
+ * @hide - hide this because it takes in a parameter of type ILowpanManager, which is a system
+ * private class.
*/
- @Nullable
- private synchronized ILowpanManager getILowpanManager() {
- // Use a local reference of this object for thread safety.
- ILowpanManager manager = mManager;
+ public LowpanManager(Context context, ILowpanManager service, Looper looper) {
+ mContext = context;
+ mService = service;
+ mLooper = looper;
+ }
- if (manager == null) {
- IBinder serviceBinder =
- new ServiceManager().getService(ILowpanManager.LOWPAN_SERVICE_NAME);
+ /** @hide */
+ @Nullable
+ public LowpanInterface getInterface(@NonNull ILowpanInterface ifaceService) {
+ LowpanInterface iface = null;
- manager = ILowpanManager.Stub.asInterface(serviceBinder);
+ try {
+ synchronized (mBinderCache) {
+ if (mBinderCache.containsKey(ifaceService)) {
+ iface = mBinderCache.get(ifaceService).get();
+ }
- mManager = manager;
+ if (iface == null) {
+ String ifaceName = ifaceService.getName();
- // Add any listeners
- synchronized (mListenerMap) {
- for (ILowpanManagerListener listener : mListenerMap.values()) {
- try {
- manager.addListener(listener);
+ iface = new LowpanInterface(mContext, ifaceService, mLooper);
- } catch (RemoteException x) {
- // Consider any failure here as implying the manager is defunct
- mManager = null;
- manager = null;
+ synchronized (mInterfaceCache) {
+ mInterfaceCache.put(iface.getName(), iface);
}
+
+ mBinderCache.put(ifaceService, new WeakReference(iface));
+
+ /* Make sure we remove the object from the
+ * interface cache if the associated service
+ * dies.
+ */
+ ifaceService
+ .asBinder()
+ .linkToDeath(
+ new IBinder.DeathRecipient() {
+ @Override
+ public void binderDied() {
+ synchronized (mInterfaceCache) {
+ LowpanInterface iface =
+ mInterfaceCache.get(ifaceName);
+
+ if ((iface != null)
+ && (iface.getService() == ifaceService)) {
+ mInterfaceCache.remove(ifaceName);
+ }
+ }
+ }
+ },
+ 0);
}
}
+ } catch (RemoteException x) {
+ throw x.rethrowAsRuntimeException();
}
- return manager;
- }
- // Public Methods
+ return iface;
+ }
/**
* Returns a reference to the requested LowpanInterface object. If the given interface doesn't
@@ -118,27 +166,32 @@ public class LowpanManager {
*/
@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());
+ LowpanInterface iface = null;
+
+ try {
+ /* This synchronized block covers both branches of the enclosed
+ * if() statement in order to avoid a race condition. Two threads
+ * calling getInterface() with the same name would race to create
+ * the associated LowpanInterface object, creating two of them.
+ * Having the whole block be synchronized avoids that race.
+ */
+ synchronized (mInterfaceCache) {
+ if (mInterfaceCache.containsKey(name)) {
+ iface = mInterfaceCache.get(name);
+
+ } else {
+ ILowpanInterface ifaceService = mService.getInterface(name);
+
+ if (ifaceService != null) {
+ iface = getInterface(ifaceService);
+ }
}
- break;
- } catch (RemoteException x) {
- // In all of the cases when we get this exception, we reconnect and try again
- mManager = null;
- manager = getILowpanManager();
}
+ } catch (RemoteException x) {
+ throw x.rethrowFromSystemServer();
}
- return ret;
+ return iface;
}
/**
@@ -160,23 +213,11 @@ public class LowpanManager {
*/
@NonNull
public String[] getInterfaceList() {
- 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 {
- return manager.getInterfaceList();
- } catch (RemoteException x) {
- // In all of the cases when we get this exception, we reconnect and try again
- mManager = null;
- manager = getILowpanManager();
- }
+ try {
+ return mService.getInterfaceList();
+ } catch (RemoteException x) {
+ throw x.rethrowFromSystemServer();
}
-
- // Return empty list if we have no service.
- return new String[0];
}
/**
@@ -189,55 +230,52 @@ public class LowpanManager {
throws LowpanException {
ILowpanManagerListener.Stub listenerBinder =
new ILowpanManagerListener.Stub() {
- public void onInterfaceAdded(ILowpanInterface lowpanInterface) {
- Runnable runnable =
- new Runnable() {
- @Override
- public void run() {
- cb.onInterfaceAdded(
- LowpanInterface.getInterfaceFromBinder(
- lowpanInterface.asBinder()));
- }
- };
+ private Handler mHandler;
+ {
if (handler != null) {
- handler.post(runnable);
+ mHandler = handler;
+ } else if (mLooper != null) {
+ mHandler = new Handler(mLooper);
} else {
- runnable.run();
+ mHandler = new Handler();
}
}
- public void onInterfaceRemoved(ILowpanInterface lowpanInterface) {
+ @Override
+ public void onInterfaceAdded(ILowpanInterface ifaceService) {
Runnable runnable =
- new Runnable() {
- @Override
- public void run() {
- cb.onInterfaceRemoved(
- LowpanInterface.getInterfaceFromBinder(
- lowpanInterface.asBinder()));
+ () -> {
+ LowpanInterface iface = getInterface(ifaceService);
+
+ if (iface != null) {
+ cb.onInterfaceAdded(iface);
}
};
- if (handler != null) {
- handler.post(runnable);
- } else {
- runnable.run();
- }
+ mHandler.post(runnable);
+ }
+
+ @Override
+ public void onInterfaceRemoved(ILowpanInterface ifaceService) {
+ Runnable runnable =
+ () -> {
+ LowpanInterface iface = getInterface(ifaceService);
+
+ if (iface != null) {
+ cb.onInterfaceRemoved(iface);
+ }
+ };
+
+ mHandler.post(runnable);
}
};
- 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);
- }
+ try {
+ mService.addListener(listenerBinder);
+ } catch (RemoteException x) {
+ throw x.rethrowFromSystemServer();
}
+
synchronized (mListenerMap) {
mListenerMap.put(Integer.valueOf(System.identityHashCode(cb)), listenerBinder);
}
@@ -253,20 +291,23 @@ public class LowpanManager {
*
* @hide
*/
- public void unregisterCallback(@NonNull Callback cb) throws AndroidException {
+ public void unregisterCallback(@NonNull Callback cb) {
Integer hashCode = Integer.valueOf(System.identityHashCode(cb));
- ILowpanManagerListener listenerBinder = mListenerMap.get(hashCode);
+ ILowpanManagerListener listenerBinder = null;
+
+ synchronized (mListenerMap) {
+ listenerBinder = mListenerMap.get(hashCode);
+ mListenerMap.remove(hashCode);
+ }
+
if (listenerBinder != null) {
- synchronized (mListenerMap) {
- mListenerMap.remove(hashCode);
- }
- if (getILowpanManager() != null) {
- try {
- mManager.removeListener(listenerBinder);
- } catch (DeadObjectException x) {
- mManager = null;
- }
+ try {
+ mService.removeListener(listenerBinder);
+ } catch (RemoteException x) {
+ throw x.rethrowFromSystemServer();
}
+ } else {
+ throw new RuntimeException("Attempt to unregister an unknown callback");
}
}
}
diff --git a/lowpan/java/android/net/lowpan/LowpanProperties.java b/lowpan/java/android/net/lowpan/LowpanProperties.java
index 0d5acc2d0c21..f835260b842e 100644
--- a/lowpan/java/android/net/lowpan/LowpanProperties.java
+++ b/lowpan/java/android/net/lowpan/LowpanProperties.java
@@ -16,9 +16,8 @@
package android.net.lowpan;
+import android.net.IpPrefix;
import android.net.LinkAddress;
-import android.net.RouteInfo;
-import java.util.List;
/** {@hide} */
public final class LowpanProperties {
@@ -77,14 +76,6 @@ public final class LowpanProperties {
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(
diff --git a/lowpan/java/android/net/lowpan/LowpanScanner.java b/lowpan/java/android/net/lowpan/LowpanScanner.java
index e0df55d9af3f..b0557ee5c19b 100644
--- a/lowpan/java/android/net/lowpan/LowpanScanner.java
+++ b/lowpan/java/android/net/lowpan/LowpanScanner.java
@@ -226,8 +226,12 @@ public class LowpanScanner {
try {
mBinder.startNetScan(map, binderListener);
- } catch (ServiceSpecificException | RemoteException x) {
- LowpanException.throwAsPublicException(x);
+
+ } catch (RemoteException x) {
+ throw x.rethrowAsRuntimeException();
+
+ } catch (ServiceSpecificException x) {
+ throw LowpanException.rethrowAsLowpanException(x);
}
}
@@ -239,8 +243,11 @@ public class LowpanScanner {
public void stopNetScan() {
try {
mBinder.stopNetScan();
+
} catch (RemoteException x) {
- // Catch and ignore all binder exceptions
+ throw x.rethrowAsRuntimeException();
+
+ } catch (ServiceSpecificException x) {
Log.e(TAG, x.toString());
}
}
@@ -303,10 +310,12 @@ public class LowpanScanner {
try {
mBinder.startEnergyScan(map, binderListener);
+
} catch (RemoteException x) {
- LowpanException.throwAsPublicException(x);
+ throw x.rethrowAsRuntimeException();
+
} catch (ServiceSpecificException x) {
- LowpanException.throwAsPublicException(x);
+ throw LowpanException.rethrowAsLowpanException(x);
}
}
@@ -318,8 +327,11 @@ public class LowpanScanner {
public void stopEnergyScan() {
try {
mBinder.stopEnergyScan();
+
} catch (RemoteException x) {
- // Catch and ignore all binder exceptions
+ throw x.rethrowAsRuntimeException();
+
+ } catch (ServiceSpecificException x) {
Log.e(TAG, x.toString());
}
}