Home | History | Annotate | Download | only in lowpan
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.lowpan;
     18 
     19 import android.annotation.NonNull;
     20 import android.content.Context;
     21 import android.net.ConnectivityManager;
     22 import android.net.IpPrefix;
     23 import android.net.LinkAddress;
     24 import android.net.LinkProperties;
     25 import android.net.NetworkAgent;
     26 import android.net.NetworkCapabilities;
     27 import android.net.NetworkFactory;
     28 import android.net.NetworkInfo;
     29 import android.net.NetworkInfo.DetailedState;
     30 import android.net.ip.IpManager;
     31 import android.net.ip.IpManager.InitialConfiguration;
     32 import android.net.ip.IpManager.ProvisioningConfiguration;
     33 import android.net.lowpan.ILowpanInterface;
     34 import android.net.lowpan.LowpanException;
     35 import android.net.lowpan.LowpanInterface;
     36 import android.net.lowpan.LowpanRuntimeException;
     37 import android.os.Looper;
     38 import android.os.Message;
     39 import android.os.RemoteException;
     40 import android.os.ServiceSpecificException;
     41 import android.util.Log;
     42 import com.android.internal.util.HexDump;
     43 import com.android.internal.util.Protocol;
     44 import com.android.internal.util.State;
     45 import com.android.internal.util.StateMachine;
     46 
     47 /** Tracks connectivity of a LoWPAN interface. */
     48 class LowpanInterfaceTracker extends StateMachine {
     49 
     50     // Misc Constants
     51 
     52     /** Network type string for NetworkInfo */
     53     private static final String NETWORK_TYPE = "LoWPAN";
     54 
     55     /** Tag used for logging */
     56     private static final String TAG = "LowpanInterfaceTracker";
     57 
     58     /**
     59      * Maximum network score for LoWPAN networks.
     60      *
     61      * <p>TODO: Research if 30 is an appropriate value.
     62      */
     63     private static final int NETWORK_SCORE = 30;
     64 
     65     /** Internal debugging flag. */
     66     private static final boolean DBG = true;
     67 
     68     /** Number of state machine log records. */
     69     public static final short NUM_LOG_RECS_NORMAL = 100;
     70 
     71     // Message Code Enumeration Constants
     72 
     73     /** The base for LoWPAN message codes */
     74     static final int BASE = Protocol.BASE_LOWPAN;
     75 
     76     static final int CMD_START_NETWORK = BASE + 3;
     77     static final int CMD_STOP_NETWORK = BASE + 4;
     78     static final int CMD_STATE_CHANGE = BASE + 5;
     79     static final int CMD_LINK_PROPERTIES_CHANGE = BASE + 6;
     80     static final int CMD_UNWANTED = BASE + 7;
     81     static final int CMD_PROVISIONING_SUCCESS = BASE + 8;
     82     static final int CMD_PROVISIONING_FAILURE = BASE + 9;
     83 
     84     // Services and interfaces
     85 
     86     ILowpanInterface mILowpanInterface;
     87     private LowpanInterface mLowpanInterface;
     88     private NetworkAgent mNetworkAgent;
     89     private NetworkFactory mNetworkFactory;
     90     private IpManager mIpManager;
     91     private final IpManager.Callback mIpManagerCallback = new IpManagerCallback();
     92 
     93     // Instance Variables
     94 
     95     private String mInterfaceName;
     96     private String mHwAddr;
     97     private Context mContext;
     98     private NetworkInfo mNetworkInfo;
     99     private LinkProperties mLinkProperties;
    100     private final NetworkCapabilities mNetworkCapabilities = new NetworkCapabilities();
    101     private String mState = "";
    102 
    103     // State machine state instances
    104 
    105     final DefaultState mDefaultState = new DefaultState();
    106     final NormalState mNormalState = new NormalState();
    107     final OfflineState mOfflineState = new OfflineState();
    108     final CommissioningState mCommissioningState = new CommissioningState();
    109     final AttachingState mAttachingState = new AttachingState();
    110     final AttachedState mAttachedState = new AttachedState();
    111     final ObtainingIpState mObtainingIpState = new ObtainingIpState();
    112     final FaultState mFaultState = new FaultState();
    113     final ConnectedState mConnectedState = new ConnectedState();
    114 
    115     private LocalLowpanCallback mLocalLowpanCallback = new LocalLowpanCallback();
    116 
    117     // Misc Private Classes
    118 
    119     private class LocalLowpanCallback extends LowpanInterface.Callback {
    120         @Override
    121         public void onEnabledChanged(boolean value) {}
    122 
    123         @Override
    124         public void onUpChanged(boolean value) {}
    125 
    126         @Override
    127         public void onConnectedChanged(boolean value) {}
    128 
    129         @Override
    130         public void onStateChanged(@NonNull String state) {
    131             LowpanInterfaceTracker.this.sendMessage(CMD_STATE_CHANGE, state);
    132         }
    133     }
    134 
    135     class IpManagerCallback extends IpManager.Callback {
    136         @Override
    137         public void onProvisioningSuccess(LinkProperties newLp) {
    138             LowpanInterfaceTracker.this.sendMessage(CMD_PROVISIONING_SUCCESS, newLp);
    139         }
    140 
    141         @Override
    142         public void onProvisioningFailure(LinkProperties newLp) {
    143             LowpanInterfaceTracker.this.sendMessage(CMD_PROVISIONING_FAILURE, newLp);
    144         }
    145 
    146         @Override
    147         public void onLinkPropertiesChange(LinkProperties newLp) {
    148             LowpanInterfaceTracker.this.sendMessage(CMD_LINK_PROPERTIES_CHANGE, newLp);
    149         }
    150     }
    151 
    152     // State Definitions
    153 
    154     class DefaultState extends State {
    155         @Override
    156         public void enter() {
    157             if (DBG) {
    158                 Log.i(TAG, "DefaultState.enter()");
    159             }
    160 
    161             mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_NONE, 0, NETWORK_TYPE, "");
    162             mNetworkInfo.setIsAvailable(true);
    163 
    164             mLowpanInterface.registerCallback(mLocalLowpanCallback);
    165 
    166             mState = "";
    167 
    168             sendMessage(CMD_STATE_CHANGE, mLowpanInterface.getState());
    169         }
    170 
    171         @Override
    172         public boolean processMessage(Message message) {
    173             boolean retValue = NOT_HANDLED;
    174 
    175             switch (message.what) {
    176                 case CMD_START_NETWORK:
    177                     if (DBG) {
    178                         Log.i(TAG, "CMD_START_NETWORK");
    179                     }
    180                     try {
    181                         mLowpanInterface.setEnabled(true);
    182                     } catch (LowpanException | LowpanRuntimeException x) {
    183                         Log.e(TAG, "Exception while enabling: " + x);
    184                         transitionTo(mFaultState);
    185                         return HANDLED;
    186                     }
    187                     break;
    188 
    189                 case CMD_STOP_NETWORK:
    190                     if (DBG) {
    191                         Log.i(TAG, "CMD_STOP_NETWORK");
    192                     }
    193                     try {
    194                         mLowpanInterface.setEnabled(false);
    195                     } catch (LowpanException | LowpanRuntimeException x) {
    196                         Log.e(TAG, "Exception while disabling: " + x);
    197                         transitionTo(mFaultState);
    198                         return HANDLED;
    199                     }
    200                     break;
    201 
    202                 case CMD_STATE_CHANGE:
    203                     if (!mState.equals(message.obj)) {
    204                         if (DBG) {
    205                             Log.i(
    206                                     TAG,
    207                                     "LowpanInterface changed state from \""
    208                                             + mState
    209                                             + "\" to \""
    210                                             + message.obj
    211                                             + "\".");
    212                         }
    213                         mState = (String) message.obj;
    214                         switch (mState) {
    215                             case LowpanInterface.STATE_OFFLINE:
    216                                 transitionTo(mOfflineState);
    217                                 break;
    218                             case LowpanInterface.STATE_COMMISSIONING:
    219                                 transitionTo(mCommissioningState);
    220                                 break;
    221                             case LowpanInterface.STATE_ATTACHING:
    222                                 transitionTo(mAttachingState);
    223                                 break;
    224                             case LowpanInterface.STATE_ATTACHED:
    225                                 transitionTo(mObtainingIpState);
    226                                 break;
    227                             case LowpanInterface.STATE_FAULT:
    228                                 transitionTo(mFaultState);
    229                                 break;
    230                         }
    231                     }
    232                     retValue = HANDLED;
    233                     break;
    234             }
    235             return retValue;
    236         }
    237 
    238         @Override
    239         public void exit() {
    240             mLowpanInterface.unregisterCallback(mLocalLowpanCallback);
    241         }
    242     }
    243 
    244     class NormalState extends State {
    245         @Override
    246         public void enter() {
    247             if (DBG) {
    248                 Log.i(TAG, "NormalState.enter()");
    249             }
    250 
    251             mIpManager = new IpManager(mContext, mInterfaceName, mIpManagerCallback);
    252 
    253             if (mHwAddr == null) {
    254                 byte[] hwAddr = null;
    255                 try {
    256                     hwAddr = mLowpanInterface.getService().getMacAddress();
    257 
    258                 } catch (RemoteException | ServiceSpecificException x) {
    259                     // Don't let misbehavior of the interface service
    260                     // crash the system service.
    261                     Log.e(TAG, "Call to getMacAddress() failed: " + x);
    262                     transitionTo(mFaultState);
    263                 }
    264 
    265                 if (hwAddr != null) {
    266                     mHwAddr = HexDump.toHexString(hwAddr);
    267                 }
    268             }
    269 
    270             mNetworkFactory.register();
    271         }
    272 
    273         @Override
    274         public boolean processMessage(Message message) {
    275             switch (message.what) {
    276                 case CMD_UNWANTED:
    277                     if (mNetworkAgent == message.obj) {
    278                         if (DBG) {
    279                             Log.i(TAG, "UNWANTED.");
    280                         }
    281 
    282                         try {
    283                             mLowpanInterface.setEnabled(false);
    284                         } catch (LowpanException | LowpanRuntimeException x) {
    285                             Log.e(TAG, "Exception while disabling: " + x);
    286                             transitionTo(mFaultState);
    287                             return HANDLED;
    288                         }
    289 
    290                         shutdownNetworkAgent();
    291                     }
    292                     break;
    293 
    294                 case CMD_LINK_PROPERTIES_CHANGE:
    295                     mLinkProperties = (LinkProperties) message.obj;
    296                     if (DBG) {
    297                         Log.i(TAG, "Got LinkProperties: " + mLinkProperties);
    298                     }
    299                     if (mNetworkAgent != null) {
    300                         mNetworkAgent.sendLinkProperties(mLinkProperties);
    301                     }
    302                     break;
    303 
    304                 case CMD_PROVISIONING_FAILURE:
    305                     Log.i(TAG, "Provisioning Failure: " + message.obj);
    306                     break;
    307             }
    308 
    309             return NOT_HANDLED;
    310         }
    311 
    312         @Override
    313         public void exit() {
    314             shutdownNetworkAgent();
    315             mNetworkFactory.unregister();
    316 
    317             if (mIpManager != null) {
    318                 mIpManager.shutdown();
    319             }
    320             mIpManager = null;
    321         }
    322     }
    323 
    324     class OfflineState extends State {
    325         @Override
    326         public void enter() {
    327             shutdownNetworkAgent();
    328             mNetworkInfo.setIsAvailable(true);
    329 
    330             mIpManager.stop();
    331         }
    332 
    333         @Override
    334         public boolean processMessage(Message message) {
    335             return NOT_HANDLED;
    336         }
    337 
    338         @Override
    339         public void exit() {}
    340     }
    341 
    342     class CommissioningState extends State {
    343         @Override
    344         public void enter() {}
    345 
    346         @Override
    347         public boolean processMessage(Message message) {
    348             return NOT_HANDLED;
    349         }
    350 
    351         @Override
    352         public void exit() {}
    353     }
    354 
    355     class AttachingState extends State {
    356         @Override
    357         public void enter() {
    358             mNetworkInfo.setDetailedState(DetailedState.CONNECTING, null, mHwAddr);
    359             mNetworkInfo.setIsAvailable(true);
    360             bringUpNetworkAgent();
    361             mNetworkAgent.sendNetworkInfo(mNetworkInfo);
    362         }
    363 
    364         @Override
    365         public boolean processMessage(Message message) {
    366             return NOT_HANDLED;
    367         }
    368 
    369         @Override
    370         public void exit() {}
    371     }
    372 
    373     class AttachedState extends State {
    374         @Override
    375         public void enter() {
    376             bringUpNetworkAgent();
    377             mNetworkInfo.setIsAvailable(true);
    378         }
    379 
    380         @Override
    381         public boolean processMessage(Message message) {
    382             switch (message.what) {
    383                 case CMD_STATE_CHANGE:
    384                     if (!mState.equals(message.obj)
    385                             && !LowpanInterface.STATE_ATTACHED.equals(message.obj)) {
    386                         return NOT_HANDLED;
    387                     }
    388                     return HANDLED;
    389 
    390                 default:
    391                     return NOT_HANDLED;
    392             }
    393         }
    394 
    395         @Override
    396         public void exit() {
    397             mNetworkInfo.setIsAvailable(false);
    398         }
    399     }
    400 
    401     class ObtainingIpState extends State {
    402         @Override
    403         public void enter() {
    404             InitialConfiguration initialConfiguration = new InitialConfiguration();
    405 
    406             try {
    407                 for (LinkAddress address : mLowpanInterface.getLinkAddresses()) {
    408                     if (DBG) {
    409                         Log.i(TAG, "Adding link address: " + address);
    410                     }
    411 
    412                     initialConfiguration.ipAddresses.add(address);
    413 
    414                     IpPrefix prefix = new IpPrefix(address.getAddress(), address.getPrefixLength());
    415 
    416                     initialConfiguration.directlyConnectedRoutes.add(prefix);
    417                 }
    418 
    419                 for (IpPrefix prefix : mLowpanInterface.getLinkNetworks()) {
    420                     if (DBG) {
    421                         Log.i(TAG, "Adding directly connected route: " + prefix);
    422                     }
    423 
    424                     initialConfiguration.directlyConnectedRoutes.add(prefix);
    425                 }
    426 
    427             } catch (LowpanException | LowpanRuntimeException x) {
    428                 Log.e(TAG, "Exception while populating InitialConfiguration: " + x);
    429                 transitionTo(mFaultState);
    430                 return;
    431 
    432             } catch (RuntimeException x) {
    433                 if (x.getCause() instanceof RemoteException) {
    434                     // Don't let misbehavior of an interface service
    435                     // crash the system service.
    436                     Log.e(TAG, "RuntimeException while populating InitialConfiguration: " + x);
    437                     transitionTo(mFaultState);
    438 
    439                 } else {
    440                     // This exception wasn't remote in origin, so we rethrow.
    441                     throw x;
    442                 }
    443             }
    444 
    445             if (!initialConfiguration.isValid()) {
    446                 Log.e(TAG, "Invalid initial configuration: " + initialConfiguration);
    447                 transitionTo(mFaultState);
    448                 return;
    449             }
    450 
    451             if (DBG) {
    452                 Log.d(TAG, "Using Initial configuration: " + initialConfiguration);
    453             }
    454 
    455             final ProvisioningConfiguration.Builder builder =
    456                     mIpManager.buildProvisioningConfiguration();
    457 
    458             builder.withInitialConfiguration(initialConfiguration).withProvisioningTimeoutMs(0);
    459 
    460             // LoWPAN networks generally don't have internet connectivity,
    461             // so the reachability monitor would almost always fail.
    462             builder.withoutIpReachabilityMonitor();
    463 
    464             // We currently only support IPv6 on LoWPAN networks, although
    465             // theoretically we could make this determination by examining
    466             // the InitialConfiguration for any IPv4 addresses.
    467             builder.withoutIPv4();
    468 
    469             mIpManager.startProvisioning(builder.build());
    470 
    471             mNetworkInfo.setDetailedState(DetailedState.OBTAINING_IPADDR, null, mHwAddr);
    472             mNetworkAgent.sendNetworkInfo(mNetworkInfo);
    473         }
    474 
    475         @Override
    476         public boolean processMessage(Message message) {
    477 
    478             switch (message.what) {
    479                 case CMD_PROVISIONING_SUCCESS:
    480                     Log.i(TAG, "Provisioning Success: " + message.obj);
    481                     transitionTo(mConnectedState);
    482                     return HANDLED;
    483             }
    484             return NOT_HANDLED;
    485         }
    486 
    487         @Override
    488         public void exit() {}
    489     }
    490 
    491     class ConnectedState extends State {
    492         @Override
    493         public void enter() {
    494             mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, mHwAddr);
    495             mNetworkAgent.sendNetworkInfo(mNetworkInfo);
    496             mNetworkAgent.sendNetworkScore(NETWORK_SCORE);
    497         }
    498 
    499         @Override
    500         public boolean processMessage(Message message) {
    501             return NOT_HANDLED;
    502         }
    503 
    504         @Override
    505         public void exit() {
    506             if (mNetworkAgent != null) {
    507                 mNetworkAgent.sendNetworkScore(0);
    508             }
    509         }
    510     }
    511 
    512     class FaultState extends State {
    513         @Override
    514         public void enter() {}
    515 
    516         @Override
    517         public boolean processMessage(Message message) {
    518             return NOT_HANDLED;
    519         }
    520 
    521         @Override
    522         public void exit() {}
    523     }
    524 
    525     public LowpanInterfaceTracker(Context context, ILowpanInterface ifaceService, Looper looper) {
    526         super(TAG, looper);
    527 
    528         if (DBG) {
    529             Log.i(TAG, "LowpanInterfaceTracker() begin");
    530         }
    531 
    532         setDbg(DBG);
    533         setLogRecSize(NUM_LOG_RECS_NORMAL);
    534         setLogOnlyTransitions(false);
    535 
    536         mILowpanInterface = ifaceService;
    537         mLowpanInterface = new LowpanInterface(context, ifaceService, looper);
    538         mContext = context;
    539 
    540         mInterfaceName = mLowpanInterface.getName();
    541 
    542         mLinkProperties = new LinkProperties();
    543         mLinkProperties.setInterfaceName(mInterfaceName);
    544 
    545         // Initialize capabilities
    546         mNetworkCapabilities.addTransportType(NetworkCapabilities.TRANSPORT_LOWPAN);
    547         mNetworkCapabilities.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED);
    548         mNetworkCapabilities.setLinkUpstreamBandwidthKbps(100);
    549         mNetworkCapabilities.setLinkDownstreamBandwidthKbps(100);
    550 
    551         // CHECKSTYLE:OFF IndentationCheck
    552         addState(mDefaultState);
    553         addState(mFaultState, mDefaultState);
    554         addState(mNormalState, mDefaultState);
    555         addState(mOfflineState, mNormalState);
    556         addState(mCommissioningState, mNormalState);
    557         addState(mAttachingState, mNormalState);
    558         addState(mAttachedState, mNormalState);
    559         addState(mObtainingIpState, mAttachedState);
    560         addState(mConnectedState, mAttachedState);
    561         // CHECKSTYLE:ON IndentationCheck
    562 
    563         setInitialState(mDefaultState);
    564 
    565         mNetworkFactory =
    566                 new NetworkFactory(looper, context, NETWORK_TYPE, mNetworkCapabilities) {
    567                     @Override
    568                     protected void startNetwork() {
    569                         LowpanInterfaceTracker.this.sendMessage(CMD_START_NETWORK);
    570                     }
    571 
    572                     @Override
    573                     protected void stopNetwork() {
    574                         LowpanInterfaceTracker.this.sendMessage(CMD_STOP_NETWORK);
    575                     }
    576                 };
    577 
    578         if (DBG) {
    579             Log.i(TAG, "LowpanInterfaceTracker() end");
    580         }
    581     }
    582 
    583     private void shutdownNetworkAgent() {
    584         mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, mHwAddr);
    585         mNetworkInfo.setIsAvailable(false);
    586 
    587         if (mNetworkAgent != null) {
    588             mNetworkAgent.sendNetworkScore(0);
    589             mNetworkAgent.sendNetworkInfo(mNetworkInfo);
    590         }
    591 
    592         mNetworkAgent = null;
    593     }
    594 
    595     private void bringUpNetworkAgent() {
    596         if (mNetworkAgent == null) {
    597             mNetworkAgent =
    598                     new NetworkAgent(
    599                             mNetworkFactory.getLooper(),
    600                             mContext,
    601                             NETWORK_TYPE,
    602                             mNetworkInfo,
    603                             mNetworkCapabilities,
    604                             mLinkProperties,
    605                             NETWORK_SCORE) {
    606                         public void unwanted() {
    607                             LowpanInterfaceTracker.this.sendMessage(CMD_UNWANTED, this);
    608                         };
    609                     };
    610         }
    611     }
    612 
    613     public void register() {
    614         if (DBG) {
    615             Log.i(TAG, "register()");
    616         }
    617         start();
    618     }
    619 
    620     public void unregister() {
    621         if (DBG) {
    622             Log.i(TAG, "unregister()");
    623         }
    624         quit();
    625     }
    626 }
    627