• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
onEnabledChanged(boolean value)121         public void onEnabledChanged(boolean value) {}
122 
123         @Override
onUpChanged(boolean value)124         public void onUpChanged(boolean value) {}
125 
126         @Override
onConnectedChanged(boolean value)127         public void onConnectedChanged(boolean value) {}
128 
129         @Override
onStateChanged(@onNull String state)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
onProvisioningSuccess(LinkProperties newLp)137         public void onProvisioningSuccess(LinkProperties newLp) {
138             LowpanInterfaceTracker.this.sendMessage(CMD_PROVISIONING_SUCCESS, newLp);
139         }
140 
141         @Override
onProvisioningFailure(LinkProperties newLp)142         public void onProvisioningFailure(LinkProperties newLp) {
143             LowpanInterfaceTracker.this.sendMessage(CMD_PROVISIONING_FAILURE, newLp);
144         }
145 
146         @Override
onLinkPropertiesChange(LinkProperties newLp)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
enter()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
processMessage(Message message)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
exit()239         public void exit() {
240             mLowpanInterface.unregisterCallback(mLocalLowpanCallback);
241         }
242     }
243 
244     class NormalState extends State {
245         @Override
enter()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
processMessage(Message message)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
exit()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
enter()326         public void enter() {
327             shutdownNetworkAgent();
328             mNetworkInfo.setIsAvailable(true);
329 
330             mIpManager.stop();
331         }
332 
333         @Override
processMessage(Message message)334         public boolean processMessage(Message message) {
335             return NOT_HANDLED;
336         }
337 
338         @Override
exit()339         public void exit() {}
340     }
341 
342     class CommissioningState extends State {
343         @Override
enter()344         public void enter() {}
345 
346         @Override
processMessage(Message message)347         public boolean processMessage(Message message) {
348             return NOT_HANDLED;
349         }
350 
351         @Override
exit()352         public void exit() {}
353     }
354 
355     class AttachingState extends State {
356         @Override
enter()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
processMessage(Message message)365         public boolean processMessage(Message message) {
366             return NOT_HANDLED;
367         }
368 
369         @Override
exit()370         public void exit() {}
371     }
372 
373     class AttachedState extends State {
374         @Override
enter()375         public void enter() {
376             bringUpNetworkAgent();
377             mNetworkInfo.setIsAvailable(true);
378         }
379 
380         @Override
processMessage(Message message)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
exit()396         public void exit() {
397             mNetworkInfo.setIsAvailable(false);
398         }
399     }
400 
401     class ObtainingIpState extends State {
402         @Override
enter()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
processMessage(Message message)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
exit()488         public void exit() {}
489     }
490 
491     class ConnectedState extends State {
492         @Override
enter()493         public void enter() {
494             mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, mHwAddr);
495             mNetworkAgent.sendNetworkInfo(mNetworkInfo);
496             mNetworkAgent.sendNetworkScore(NETWORK_SCORE);
497         }
498 
499         @Override
processMessage(Message message)500         public boolean processMessage(Message message) {
501             return NOT_HANDLED;
502         }
503 
504         @Override
exit()505         public void exit() {
506             if (mNetworkAgent != null) {
507                 mNetworkAgent.sendNetworkScore(0);
508             }
509         }
510     }
511 
512     class FaultState extends State {
513         @Override
enter()514         public void enter() {}
515 
516         @Override
processMessage(Message message)517         public boolean processMessage(Message message) {
518             return NOT_HANDLED;
519         }
520 
521         @Override
exit()522         public void exit() {}
523     }
524 
LowpanInterfaceTracker(Context context, ILowpanInterface ifaceService, Looper looper)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 
shutdownNetworkAgent()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 
bringUpNetworkAgent()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 
register()613     public void register() {
614         if (DBG) {
615             Log.i(TAG, "register()");
616         }
617         start();
618     }
619 
unregister()620     public void unregister() {
621         if (DBG) {
622             Log.i(TAG, "unregister()");
623         }
624         quit();
625     }
626 }
627