Merge "Updates and fixes to android.net.lowpan"

This commit is contained in:
TreeHugger Robot
2017-07-10 20:20:18 +00:00
committed by Android (Google) Code Review
9 changed files with 477 additions and 359 deletions

View File

@@ -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";
/** 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 @@ 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);

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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);
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;

View File

@@ -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() {

View File

@@ -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<>();
/**
* 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 @@ 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();
}
}
}
@@ -751,6 +747,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.
*
@@ -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());

View File

@@ -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
/**
* 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 @@ public class LowpanManager {
*/
@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 @@ 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");
}
}
}

View File

@@ -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(

View File

@@ -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());
}
}