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