Merge "Updates and fixes to android.net.lowpan"
diff --git a/lowpan/java/android/net/lowpan/ILowpanInterface.aidl b/lowpan/java/android/net/lowpan/ILowpanInterface.aidl
index 647fcc1..329e9fa 100644
--- a/lowpan/java/android/net/lowpan/ILowpanInterface.aidl
+++ b/lowpan/java/android/net/lowpan/ILowpanInterface.aidl
@@ -37,41 +37,79 @@
//////////////////////////////////////////////////////////////////////////
// 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";
+
+ /** Type: Integer */
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: 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 @@
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 c99d732..0ae01a1 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 5a8d7dc..326aa65 100644
--- a/lowpan/java/android/net/lowpan/ILowpanManager.aidl
+++ b/lowpan/java/android/net/lowpan/ILowpanManager.aidl
@@ -21,6 +21,7 @@
/** {@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 b43b2fe..5a1f729 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 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);
+ 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_PERMISSION_DENIED:
+ throw new SecurityException(e.getMessage(), e);
- case ILowpanInterface.ERROR_DISABLED:
- reason = LowpanException.LOWPAN_DISABLED;
- break;
+ case ILowpanInterface.ERROR_DISABLED:
+ reason = LowpanException.LOWPAN_DISABLED;
+ break;
- case ILowpanInterface.ERROR_WRONG_STATE:
- reason = LowpanException.LOWPAN_WRONG_STATE;
- break;
+ case ILowpanInterface.ERROR_WRONG_STATE:
+ reason = LowpanException.LOWPAN_WRONG_STATE;
+ break;
- case ILowpanInterface.ERROR_BUSY:
- reason = LowpanException.LOWPAN_BUSY;
- break;
+ case ILowpanInterface.ERROR_BUSY:
+ reason = LowpanException.LOWPAN_BUSY;
+ break;
- case ILowpanInterface.ERROR_ALREADY:
- reason = LowpanException.LOWPAN_ALREADY;
- break;
+ case ILowpanInterface.ERROR_ALREADY:
+ reason = LowpanException.LOWPAN_ALREADY;
+ break;
- case ILowpanInterface.ERROR_CANCELED:
- reason = LowpanException.LOWPAN_CANCELED;
- break;
+ case ILowpanInterface.ERROR_CANCELED:
+ reason = LowpanException.LOWPAN_CANCELED;
+ break;
- case ILowpanInterface.ERROR_CREDENTIAL_NEEDED:
- reason = LowpanException.LOWPAN_CREDENTIAL_NEEDED;
- 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_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_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_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_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_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_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;
+ 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 9be45ef..2d36f7f 100644
--- a/lowpan/java/android/net/lowpan/LowpanIdentity.java
+++ b/lowpan/java/android/net/lowpan/LowpanIdentity.java
@@ -53,7 +53,7 @@
}
public Builder setXpanid(byte x[]) {
- identity.mXpanid = x.clone();
+ identity.mXpanid = (x != null ? x.clone() : null);
return this;
}
@@ -115,7 +115,7 @@
}
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 2bb4ecd..55bf399 100644
--- a/lowpan/java/android/net/lowpan/LowpanInterface.java
+++ b/lowpan/java/android/net/lowpan/LowpanInterface.java
@@ -18,9 +18,12 @@
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 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<>();
+ /**
+ * 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
+ */
+ 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.
+ */
- private LowpanInterface(IBinder binder) {
- mBinder = ILowpanInterface.Stub.asInterface(binder);
+ mBinder = service;
+ mLooper = looper;
}
/**
- * Get the LowpanInterface object associated with this IBinder. Returns null if this IBinder
- * does not implement the appropriate interface.
+ * Returns the ILowpanInterface object associated with this 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);
+ public ILowpanInterface getService() {
+ return mBinder;
}
// Private Property Helpers
@@ -251,11 +229,10 @@
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 @@
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 @@
<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 @@
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 @@
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 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 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 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 @@
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 @@
// 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 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 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 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 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 ecdda49..2d974ee 100644
--- a/lowpan/java/android/net/lowpan/LowpanManager.java
+++ b/lowpan/java/android/net/lowpan/LowpanManager.java
@@ -19,14 +19,15 @@
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 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
-
- /**
- * Returns a reference to the ILowpanManager interface, provided by the LoWPAN Manager Service.
- */
- @Nullable
- private synchronized ILowpanManager getILowpanManager() {
- // Use a local reference of this object for thread safety.
- ILowpanManager manager = mManager;
-
- if (manager == null) {
- IBinder serviceBinder =
- new ServiceManager().getService(ILowpanManager.LOWPAN_SERVICE_NAME);
-
- manager = ILowpanManager.Stub.asInterface(serviceBinder);
-
- mManager = manager;
-
- // Add any listeners
- synchronized (mListenerMap) {
- for (ILowpanManagerListener listener : mListenerMap.values()) {
- try {
- manager.addListener(listener);
-
- } catch (RemoteException x) {
- // Consider any failure here as implying the manager is defunct
- mManager = null;
- manager = null;
- }
- }
- }
- }
- return manager;
+ LowpanManager(ILowpanManager service) {
+ mService = service;
+ mContext = null;
+ mLooper = null;
}
- // Public Methods
+ /**
+ * 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.
+ */
+ public LowpanManager(Context context, ILowpanManager service, Looper looper) {
+ mContext = context;
+ mService = service;
+ mLooper = looper;
+ }
+
+ /** @hide */
+ @Nullable
+ public LowpanInterface getInterface(@NonNull ILowpanInterface ifaceService) {
+ LowpanInterface iface = null;
+
+ try {
+ synchronized (mBinderCache) {
+ if (mBinderCache.containsKey(ifaceService)) {
+ iface = mBinderCache.get(ifaceService).get();
+ }
+
+ if (iface == null) {
+ String ifaceName = ifaceService.getName();
+
+ iface = new LowpanInterface(mContext, ifaceService, mLooper);
+
+ 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 iface;
+ }
/**
* Returns a reference to the requested LowpanInterface object. If the given interface doesn't
@@ -118,27 +166,32 @@
*/
@Nullable
public LowpanInterface getInterface(@NonNull String name) {
- LowpanInterface ret = null;
- ILowpanManager manager = getILowpanManager();
+ LowpanInterface iface = null;
- // 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());
+ 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 @@
*/
@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 @@
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 @@
*
* @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);
+ try {
+ mService.removeListener(listenerBinder);
+ } catch (RemoteException x) {
+ throw x.rethrowFromSystemServer();
}
- if (getILowpanManager() != null) {
- try {
- mManager.removeListener(listenerBinder);
- } catch (DeadObjectException x) {
- mManager = null;
- }
- }
+ } 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 0d5acc2..f835260 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 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 e0df55d9..b0557ee 100644
--- a/lowpan/java/android/net/lowpan/LowpanScanner.java
+++ b/lowpan/java/android/net/lowpan/LowpanScanner.java
@@ -226,8 +226,12 @@
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 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 @@
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 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());
}
}