summaryrefslogtreecommitdiff
path: root/service/java/com/android/server/lowpan/LowpanInterfaceTracker.java
diff options
context:
space:
mode:
authorRobert Quattlebaum <rquattle@google.com>2017-03-29 15:23:26 -0700
committerRobert Quattlebaum <rquattlebaum@nestlabs.com>2017-06-23 10:56:41 +0900
commit3c0a5570fdcc6a01e1a3a90337eebf3e4e6d19b8 (patch)
tree8079edd0458195b64d25f46ad034ddefc3283be6 /service/java/com/android/server/lowpan/LowpanInterfaceTracker.java
parent7bf3ff0542b52af0693d7c093159bcd8691a10a5 (diff)
downloadlowpan-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.java591
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);
+ }
+}