diff options
-rw-r--r-- | lowpan/java/android/net/lowpan/ILowpanInterface.aidl | 51 | ||||
-rw-r--r-- | lowpan/java/android/net/lowpan/ILowpanInterfaceListener.aidl | 8 | ||||
-rw-r--r-- | lowpan/java/android/net/lowpan/ILowpanManager.aidl | 1 | ||||
-rw-r--r-- | lowpan/java/android/net/lowpan/LowpanException.java | 156 | ||||
-rw-r--r-- | lowpan/java/android/net/lowpan/LowpanIdentity.java | 4 | ||||
-rw-r--r-- | lowpan/java/android/net/lowpan/LowpanInterface.java | 292 | ||||
-rw-r--r-- | lowpan/java/android/net/lowpan/LowpanManager.java | 289 | ||||
-rw-r--r-- | lowpan/java/android/net/lowpan/LowpanProperties.java | 11 | ||||
-rw-r--r-- | lowpan/java/android/net/lowpan/LowpanScanner.java | 24 |
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()); } } |