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/java/com/android/server/lowpan/LowpanInterfaceTracker.java | |
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/java/com/android/server/lowpan/LowpanInterfaceTracker.java')
-rw-r--r-- | service/java/com/android/server/lowpan/LowpanInterfaceTracker.java | 591 |
1 files changed, 591 insertions, 0 deletions
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); + } +} |