diff options
author | Robert Quattlebaum <rquattle@google.com> | 2017-03-29 15:23:26 -0700 |
---|---|---|
committer | Robert Quattlebaum <rquattlebaum@nestlabs.com> | 2017-06-23 10:56:41 +0900 |
commit | 3c0a5570fdcc6a01e1a3a90337eebf3e4e6d19b8 (patch) | |
tree | 8079edd0458195b64d25f46ad034ddefc3283be6 /service | |
parent | 7bf3ff0542b52af0693d7c093159bcd8691a10a5 (diff) | |
download | lowpan-3c0a5570fdcc6a01e1a3a90337eebf3e4e6d19b8.tar.gz |
Initial commit of Android LoWPAN Manager Service
Change-Id: I5fe40aa223c15110c753fe76a670e2fe8a01c5d0
Bug: b/33073713
Diffstat (limited to 'service')
-rw-r--r-- | service/Android.mk | 29 | ||||
-rw-r--r-- | service/java/com/android/server/lowpan/LowpanInterfaceTracker.java | 591 | ||||
-rw-r--r-- | service/java/com/android/server/lowpan/LowpanService.java | 45 | ||||
-rw-r--r-- | service/java/com/android/server/lowpan/LowpanServiceImpl.java | 275 |
4 files changed, 940 insertions, 0 deletions
diff --git a/service/Android.mk b/service/Android.mk new file mode 100644 index 0000000..ff96a6c --- /dev/null +++ b/service/Android.mk @@ -0,0 +1,29 @@ +# +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := lowpan-service +LOCAL_MODULE_TAGS := +LOCAL_REQUIRED_MODULES := services +LOCAL_JAVA_LIBRARIES := services +LOCAL_SRC_FILES += java/com/android/server/lowpan/LowpanInterfaceTracker.java +LOCAL_SRC_FILES += java/com/android/server/lowpan/LowpanService.java +LOCAL_SRC_FILES += java/com/android/server/lowpan/LowpanServiceImpl.java +include $(BUILD_JAVA_LIBRARY) + +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/service/java/com/android/server/lowpan/LowpanInterfaceTracker.java b/service/java/com/android/server/lowpan/LowpanInterfaceTracker.java new file mode 100644 index 0000000..32af169 --- /dev/null +++ b/service/java/com/android/server/lowpan/LowpanInterfaceTracker.java @@ -0,0 +1,591 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.lowpan; + +import android.annotation.NonNull; +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.LinkProperties; +import android.net.NetworkAgent; +import android.net.NetworkCapabilities; +import android.net.NetworkFactory; +import android.net.NetworkInfo; +import android.net.NetworkInfo.DetailedState; +import android.net.lowpan.ILowpanInterface; +import android.net.lowpan.LowpanException; +import android.net.lowpan.LowpanInterface; +import android.net.lowpan.LowpanProperties; +import android.os.IBinder; +import android.os.INetworkManagementService; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Log; +import com.android.internal.util.HexDump; +import com.android.internal.util.Protocol; +import com.android.internal.util.State; +import com.android.internal.util.StateMachine; +import com.android.server.net.NetlinkTracker; + +/** Tracks connectivity of a LoWPAN interface. */ +class LowpanInterfaceTracker extends StateMachine { + + //////////////////////////////////////////////////////////////////////////// + // Misc Constants + + /** Network type string for NetworkInfo */ + private static final String NETWORK_TYPE = "LoWPAN"; + + /** Tag used for logging */ + private static final String TAG = "LowpanInterfaceTracker"; + + /** + * Maximum network score for LoWPAN networks. + * + * <p>TODO: Research if 30 is an appropriate value. + */ + private static final int NETWORK_SCORE = 30; + + /** Internal debugging flag. */ + private static final boolean DBG = true; + + /** Number of state machine log records. */ + public static final short NUM_LOG_RECS_NORMAL = 100; + + //////////////////////////////////////////////////////////////////////////// + // Message Code Enumeration Constants + + /** The base for LoWPAN message codes */ + static final int BASE = Protocol.BASE_LOWPAN; + + static final int CMD_REGISTER = BASE + 1; + static final int CMD_UNREGISTER = BASE + 2; + static final int CMD_START_NETWORK = BASE + 3; + static final int CMD_STOP_NETWORK = BASE + 4; + static final int CMD_STATE_CHANGE = BASE + 5; + static final int CMD_LINK_PROPERTIES_CHANGE = BASE + 6; + static final int CMD_UNWANTED = BASE + 7; + + //////////////////////////////////////////////////////////////////////////// + // Services and interfaces + + ILowpanInterface mILowpanInterface; + private LowpanInterface mLowpanInterface; + private NetworkAgent mNetworkAgent; + private NetworkFactory mNetworkFactory; + private INetworkManagementService mNmService; + private final NetlinkTracker mNetlinkTracker; + + //////////////////////////////////////////////////////////////////////////// + // Instance Variables + + private String mInterfaceName; + private String mHwAddr; + private Context mContext; + private NetworkInfo mNetworkInfo; + private LinkProperties mLinkProperties; + private final NetworkCapabilities mNetworkCapabilities = new NetworkCapabilities(); + private String mState = ""; + + //////////////////////////////////////////////////////////////////////////// + // State machine state instances + + final DefaultState mDefaultState = new DefaultState(); + final NormalState mNormalState = new NormalState(); + final InitState mInitState = new InitState(); + final OfflineState mOfflineState = new OfflineState(); + final CommissioningState mCommissioningState = new CommissioningState(); + final AttachingState mAttachingState = new AttachingState(); + final AttachedState mAttachedState = new AttachedState(); + final FaultState mFaultState = new FaultState(); + final ConnectedState mConnectedState = new ConnectedState(); + + //////////////////////////////////////////////////////////////////////////// + + private LocalLowpanCallback mLocalLowpanCallback = new LocalLowpanCallback(); + + //////////////////////////////////////////////////////////////////////////// + // Misc Private Classes + + private class LocalLowpanCallback extends LowpanInterface.Callback { + @Override + public void onEnabledChanged(boolean value) {} + + @Override + public void onUpChanged(boolean value) {} + + @Override + public void onConnectedChanged(boolean value) {} + + @Override + public void onStateChanged(@NonNull String state) { + LowpanInterfaceTracker.this.sendMessage(CMD_STATE_CHANGE, state); + } + } + + //////////////////////////////////////////////////////////////////////////// + // State Definitions + + class InitState extends State { + @Override + public void enter() {} + + @Override + public boolean processMessage(Message message) { + switch (message.what) { + case CMD_REGISTER: + if (DBG) { + Log.i(TAG, "CMD_REGISTER"); + } + transitionTo(mDefaultState); + break; + + default: + return NOT_HANDLED; + } + return HANDLED; + } + + @Override + public void exit() {} + } + + class DefaultState extends State { + @Override + public void enter() { + if (DBG) { + Log.i(TAG, "DefaultState.enter()"); + } + + mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_NONE, 0, NETWORK_TYPE, ""); + mNetworkInfo.setIsAvailable(true); + + if (mNmService == null) { + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + mNmService = INetworkManagementService.Stub.asInterface(b); + } + + // Start tracking interface change events. + try { + mNmService.registerObserver(mNetlinkTracker); + } catch (RemoteException e) { + Log.e(TAG, "Could not register InterfaceObserver " + e); + } + + mLowpanInterface.registerCallback(mLocalLowpanCallback); + + mState = ""; + + sendMessage(CMD_STATE_CHANGE, mLowpanInterface.getState()); + } + + @Override + public boolean processMessage(Message message) { + boolean retValue = NOT_HANDLED; + + switch (message.what) { + case CMD_UNREGISTER: + transitionTo(mInitState); + retValue = HANDLED; + break; + + case CMD_START_NETWORK: + if (DBG) { + Log.i(TAG, "CMD_START_NETWORK"); + } + break; + + case CMD_STOP_NETWORK: + if (DBG) { + Log.i(TAG, "CMD_START_NETWORK"); + } + break; + + case CMD_STATE_CHANGE: + if (!mState.equals(message.obj)) { + if (DBG) { + Log.i( + TAG, + "LowpanInterface changed state from \"" + + mState + + "\" to \"" + + message.obj.toString() + + "\"."); + } + mState = (String) message.obj; + switch (mState) { + case LowpanInterface.STATE_OFFLINE: + transitionTo(mOfflineState); + break; + case LowpanInterface.STATE_COMMISSIONING: + transitionTo(mCommissioningState); + break; + case LowpanInterface.STATE_ATTACHING: + transitionTo(mAttachingState); + break; + case LowpanInterface.STATE_ATTACHED: + transitionTo(mConnectedState); + break; + case LowpanInterface.STATE_FAULT: + transitionTo(mFaultState); + break; + } + } + retValue = HANDLED; + break; + } + return retValue; + } + + @Override + public void exit() { + + try { + mNmService.unregisterObserver(mNetlinkTracker); + } catch (RemoteException x) { + Log.e(TAG, x.toString()); + } + + mLowpanInterface.unregisterCallback(mLocalLowpanCallback); + } + } + + class NormalState extends State { + @Override + public void enter() { + if (DBG) { + Log.i(TAG, "NormalState.enter()"); + } + + if (mHwAddr == null) { + byte[] hwAddr = null; + try { + hwAddr = mLowpanInterface.getProperty(LowpanProperties.KEY_MAC_ADDRESS); + } catch (LowpanException x) { + Log.e(TAG, x.toString()); + } + + if (hwAddr != null) { + mHwAddr = HexDump.toHexString(hwAddr); + } + } + + try { + mNmService.enableIpv6(mInterfaceName); + } catch (RemoteException x) { + Log.e( + TAG, + "Failed trying to enable IPv6 on " + mInterfaceName + ": " + x.toString()); + } + + mNetworkFactory.register(); + } + + @Override + public boolean processMessage(Message message) { + switch (message.what) { + case CMD_UNWANTED: + if (mNetworkAgent == message.obj) { + if (DBG) { + Log.i(TAG, "UNWANTED."); + } + // TODO: Figure out how to properly handle this. + try { + mLowpanInterface.leave(); + } catch (LowpanException x) { + Log.e(TAG, x.toString()); + } + shutdownNetworkAgent(); + } + break; + + case CMD_LINK_PROPERTIES_CHANGE: + mLinkProperties = (LinkProperties) message.obj; + if (DBG) { + Log.i(TAG, "Got LinkProperties: " + mLinkProperties); + } + if (mNetworkAgent != null) { + mNetworkAgent.sendLinkProperties(mLinkProperties); + } + break; + } + + return NOT_HANDLED; + } + + @Override + public void exit() { + shutdownNetworkAgent(); + mNetworkFactory.unregister(); + } + } + + class OfflineState extends State { + @Override + public void enter() { + shutdownNetworkAgent(); + mNetworkInfo.setIsAvailable(true); + } + + @Override + public boolean processMessage(Message message) { + return NOT_HANDLED; + } + + @Override + public void exit() {} + } + + class CommissioningState extends State { + @Override + public void enter() {} + + @Override + public boolean processMessage(Message message) { + return NOT_HANDLED; + } + + @Override + public void exit() {} + } + + class AttachingState extends State { + @Override + public void enter() { + try { + mNmService.enableIpv6(mInterfaceName); + } catch (RemoteException x) { + Log.e( + TAG, + "Failed trying to enable IPv6 on " + mInterfaceName + ": " + x.toString()); + } + + mNetworkInfo.setDetailedState(DetailedState.CONNECTING, null, mHwAddr); + mNetworkInfo.setIsAvailable(true); + bringUpNetworkAgent(); + mNetworkAgent.sendNetworkInfo(mNetworkInfo); + } + + @Override + public boolean processMessage(Message message) { + return NOT_HANDLED; + } + + @Override + public void exit() {} + } + + class AttachedState extends State { + @Override + public void enter() { + mNetworkInfo.setIsAvailable(true); + } + + @Override + public boolean processMessage(Message message) { + switch (message.what) { + case CMD_STATE_CHANGE: + if (!mState.equals(message.obj)) { + if (!LowpanInterface.STATE_ATTACHED.equals(message.obj)) { + return NOT_HANDLED; + } + } + break; + + default: + return NOT_HANDLED; + } + return HANDLED; + } + + @Override + public void exit() { + mNetworkInfo.setIsAvailable(false); + } + } + + class ConnectedState extends State { + @Override + public void enter() { + mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, mHwAddr); + + synchronized (mNetlinkTracker) { + mLinkProperties = mNetlinkTracker.getLinkProperties(); + } + + bringUpNetworkAgent(); + + mNetworkAgent.sendLinkProperties(mLinkProperties); + mNetworkAgent.sendNetworkInfo(mNetworkInfo); + mNetworkAgent.sendNetworkScore(NETWORK_SCORE); + } + + @Override + public boolean processMessage(Message message) { + return NOT_HANDLED; + } + + @Override + public void exit() { + if (mNetworkAgent != null) { + mNetworkAgent.sendNetworkScore(0); + } + } + } + + class FaultState extends State { + @Override + public void enter() {} + + @Override + public boolean processMessage(Message message) { + return NOT_HANDLED; + } + + @Override + public void exit() {} + } + + //////////////////////////////////////////////////////////////////////////// + + public LowpanInterfaceTracker( + ILowpanInterface lowpanInterface, Context context, Looper looper) { + super(TAG, looper); + + if (DBG) { + Log.i(TAG, "LowpanInterfaceTracker() begin"); + } + + setDbg(DBG); + setLogRecSize(NUM_LOG_RECS_NORMAL); + setLogOnlyTransitions(false); + + mILowpanInterface = lowpanInterface; + mLowpanInterface = LowpanInterface.from(mILowpanInterface); + mContext = context; + + mInterfaceName = mLowpanInterface.getName(); + + mLinkProperties = new LinkProperties(); + mLinkProperties.setInterfaceName(mInterfaceName); + + mNmService = + INetworkManagementService.Stub.asInterface( + ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)); + + // Initialize capabilities + mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_LOWPAN); + mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED); + mNetworkCapabilities.setLinkUpstreamBandwidthKbps(100); + mNetworkCapabilities.setLinkDownstreamBandwidthKbps(100); + + // Things don't seem to work properly without this. TODO: Investigate. + mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET); + + // CHECKSTYLE:OFF IndentationCheck + addState(mInitState); + addState(mDefaultState); + addState(mFaultState, mDefaultState); + addState(mNormalState, mDefaultState); + addState(mOfflineState, mNormalState); + addState(mCommissioningState, mNormalState); + addState(mAttachingState, mNormalState); + addState(mAttachedState, mNormalState); + addState(mConnectedState, mAttachedState); + // CHECKSTYLE:ON IndentationCheck + + setInitialState(mInitState); + + mNetworkFactory = + new NetworkFactory(looper, context, NETWORK_TYPE, mNetworkCapabilities) { + @Override + protected void startNetwork() { + LowpanInterfaceTracker.this.sendMessage(CMD_START_NETWORK); + } + + @Override + protected void stopNetwork() { + LowpanInterfaceTracker.this.sendMessage(CMD_STOP_NETWORK); + } + }; + + mNetlinkTracker = + new NetlinkTracker( + mInterfaceName, + new NetlinkTracker.Callback() { + @Override + public void update() { + synchronized (mNetlinkTracker) { + sendMessage( + CMD_LINK_PROPERTIES_CHANGE, + mNetlinkTracker.getLinkProperties()); + } + } + }); + + start(); + + if (DBG) { + Log.i(TAG, "LowpanInterfaceTracker() end"); + } + } + + private void shutdownNetworkAgent() { + mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr); + mNetworkInfo.setIsAvailable(false); + + if (mNetworkAgent != null) { + mNetworkAgent.sendNetworkScore(0); + mNetworkAgent.sendNetworkInfo(mNetworkInfo); + } + + mNetworkAgent = null; + } + + private void bringUpNetworkAgent() { + if (mNetworkAgent == null) { + mNetworkAgent = + new NetworkAgent( + mNetworkFactory.getLooper(), + mContext, + NETWORK_TYPE, + mNetworkInfo, + mNetworkCapabilities, + mLinkProperties, + NETWORK_SCORE) { + public void unwanted() { + LowpanInterfaceTracker.this.sendMessage(CMD_UNWANTED, this); + }; + }; + } + } + + public void register() { + if (DBG) { + Log.i(TAG, "register()"); + } + sendMessage(CMD_REGISTER); + } + + public void unregister() { + if (DBG) { + Log.i(TAG, "unregister()"); + } + sendMessage(CMD_UNREGISTER); + } +} diff --git a/service/java/com/android/server/lowpan/LowpanService.java b/service/java/com/android/server/lowpan/LowpanService.java new file mode 100644 index 0000000..910dff0 --- /dev/null +++ b/service/java/com/android/server/lowpan/LowpanService.java @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.lowpan; + +import android.content.Context; +import android.net.lowpan.ILowpanManager; +import android.util.Log; +import com.android.server.SystemService; + +public final class LowpanService extends SystemService { + private static final String TAG = LowpanService.class.getSimpleName(); + private final LowpanServiceImpl mImpl; + + public LowpanService(Context context) { + super(context); + mImpl = new LowpanServiceImpl(context); + } + + @Override + public void onStart() { + Log.i(TAG, "Registering " + ILowpanManager.LOWPAN_SERVICE_NAME); + publishBinderService(ILowpanManager.LOWPAN_SERVICE_NAME, mImpl); + } + + @Override + public void onBootPhase(int phase) { + if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { + mImpl.checkAndStartLowpan(); + } + } +} diff --git a/service/java/com/android/server/lowpan/LowpanServiceImpl.java b/service/java/com/android/server/lowpan/LowpanServiceImpl.java new file mode 100644 index 0000000..be3eef6 --- /dev/null +++ b/service/java/com/android/server/lowpan/LowpanServiceImpl.java @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.lowpan; + +import android.content.Context; +import android.net.lowpan.ILowpanInterface; +import android.net.lowpan.ILowpanManager; +import android.net.lowpan.ILowpanManagerListener; +import android.os.Binder; +import android.os.HandlerThread; +import android.os.IBinder; +import android.os.Looper; +import android.os.RemoteException; +import android.util.Log; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; + + +/** + * LowpanService handles remote LoWPAN operation requests by implementing the ILowpanManager + * interface. + * + * @hide + */ +public class LowpanServiceImpl extends ILowpanManager.Stub { + private static final String TAG = LowpanServiceImpl.class.getSimpleName(); + private final Set<ILowpanManagerListener> mListenerSet = new HashSet<>(); + private final Map<String, LowpanInterfaceTracker> mInterfaceMap = new HashMap<>(); + private final Context mContext; + private final HandlerThread mHandlerThread = new HandlerThread("LowpanServiceThread"); + private final AtomicBoolean mStarted = new AtomicBoolean(false); + + public LowpanServiceImpl(Context context) { + mContext = context; + } + + public Looper getLooper() { + Looper looper = mHandlerThread.getLooper(); + if (looper == null) { + mHandlerThread.start(); + looper = mHandlerThread.getLooper(); + } + + return looper; + } + + public void checkAndStartLowpan() { + synchronized (mInterfaceMap) { + if (mStarted.compareAndSet(false, true)) { + for (Map.Entry<String, LowpanInterfaceTracker> entry : mInterfaceMap.entrySet()) { + entry.getValue().register(); + } + } + } + + // TODO: Bring up any daemons(like wpantund)? + } + + private void enforceAccessPermission() { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.ACCESS_LOWPAN_STATE, "LowpanService"); + } + + private void enforceManagePermission() { + // TODO: Change to android.Manifest.permission.MANAGE_lowpanInterfaceS + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.CHANGE_LOWPAN_STATE, "LowpanService"); + } + + public ILowpanInterface getInterface(String name) { + ILowpanInterface iface = null; + + enforceAccessPermission(); + + synchronized (mInterfaceMap) { + LowpanInterfaceTracker tracker = mInterfaceMap.get(name); + if (tracker != null) { + iface = tracker.mILowpanInterface; + } + } + + return iface; + } + + public String[] getInterfaceList() { + enforceAccessPermission(); + synchronized (mInterfaceMap) { + return mInterfaceMap.keySet().toArray(new String[mInterfaceMap.size()]); + } + } + + private void onInterfaceRemoved(ILowpanInterface lowpanInterface, String name) { + Log.i(TAG, "Removed LoWPAN interface `" + name + "` (" + lowpanInterface.toString() + ")"); + synchronized (mListenerSet) { + for (ILowpanManagerListener listener : mListenerSet) { + try { + listener.onInterfaceRemoved(lowpanInterface); + } catch (RemoteException x) { + // Just skip. + Log.e(TAG, "Exception caught: " + x); + } + } + } + } + + private void onInterfaceAdded(ILowpanInterface lowpanInterface, String name) { + Log.i(TAG, "Added LoWPAN interface `" + name + "` (" + lowpanInterface.toString() + ")"); + synchronized (mListenerSet) { + for (ILowpanManagerListener listener : mListenerSet) { + try { + listener.onInterfaceAdded(lowpanInterface); + } catch (RemoteException x) { + // Just skip. + Log.e(TAG, "Exception caught: " + x); + } + } + } + } + + public void addInterface(ILowpanInterface lowpanInterface) { + enforceManagePermission(); + + final String name; + + try { + // We allow blocking calls to get the name of the interface. + Binder.allowBlocking(lowpanInterface.asBinder()); + + name = lowpanInterface.getName(); + lowpanInterface + .asBinder() + .linkToDeath( + new IBinder.DeathRecipient() { + @Override + public void binderDied() { + Log.w( + TAG, + "LoWPAN interface `" + + name + + "` (" + + lowpanInterface.toString() + + ") died."); + removeInterface(lowpanInterface); + } + }, + 0); + + } catch (RemoteException x) { + Log.e(TAG, "Exception caught: " + x); + return; + } + + final LowpanInterfaceTracker previous; + final LowpanInterfaceTracker agent; + + synchronized (mInterfaceMap) { + previous = mInterfaceMap.get(name); + + agent = new LowpanInterfaceTracker(lowpanInterface, mContext, getLooper()); + + mInterfaceMap.put(name, agent); + } + + if (previous != null) { + previous.unregister(); + onInterfaceRemoved(previous.mILowpanInterface, name); + } + + if (mStarted.get()) { + agent.register(); + } + + onInterfaceAdded(lowpanInterface, name); + } + + private void removeInterfaceByName(String name) { + final ILowpanInterface lowpanInterface; + + enforceManagePermission(); + + if (name == null) { + return; + } + + final LowpanInterfaceTracker agent; + + synchronized (mInterfaceMap) { + agent = mInterfaceMap.get(name); + + if (agent == null) { + return; + } + + lowpanInterface = agent.mILowpanInterface; + + if (mStarted.get()) { + agent.unregister(); + } + + mInterfaceMap.remove(name); + } + + onInterfaceRemoved(lowpanInterface, name); + } + + public void removeInterface(ILowpanInterface lowpanInterface) { + String name = null; + + try { + name = lowpanInterface.getName(); + } catch (RemoteException x) { + // Directly fetching the name failed, so fall back to + // a reverse lookup. + synchronized (mInterfaceMap) { + for (Map.Entry<String, LowpanInterfaceTracker> entry : mInterfaceMap.entrySet()) { + if (entry.getValue().mILowpanInterface == lowpanInterface) { + name = entry.getKey(); + break; + } + } + } + } + + removeInterfaceByName(name); + } + + public void addListener(ILowpanManagerListener listener) { + enforceAccessPermission(); + synchronized (mListenerSet) { + if (!mListenerSet.contains(listener)) { + try { + listener.asBinder() + .linkToDeath( + new IBinder.DeathRecipient() { + @Override + public void binderDied() { + synchronized (mListenerSet) { + mListenerSet.remove(listener); + } + } + }, + 0); + mListenerSet.add(listener); + } catch (RemoteException x) { + Log.e(TAG, "Exception caught: " + x); + } + } + } + } + + public void removeListener(ILowpanManagerListener listener) { + enforceAccessPermission(); + synchronized (mListenerSet) { + mListenerSet.remove(listener); + // TODO: Shouldn't we be unlinking from the death notification? + } + } +} |