1 /* 2 * Copyright (C) 2022 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.internal.telephony.ims; 18 19 import android.content.ComponentName; 20 import android.os.Handler; 21 import android.os.IBinder; 22 import android.os.Looper; 23 import android.os.Message; 24 import android.os.RemoteException; 25 import android.telephony.SubscriptionManager; 26 import android.telephony.ims.aidl.IImsServiceController; 27 import android.util.Log; 28 import android.util.SparseArray; 29 30 import com.android.internal.annotations.VisibleForTesting; 31 import com.android.internal.util.IState; 32 import com.android.internal.util.State; 33 import com.android.internal.util.StateMachine; 34 35 import java.util.HashMap; 36 import java.util.Map; 37 38 /** 39 * This class will abstract away all the new enablement logic and take the reset/enable/disable 40 * IMS commands as inputs. 41 * The IMS commands will call enableIms, disableIms or resetIms to match the enablement state only 42 * when it changes. 43 */ 44 public class ImsEnablementTracker { 45 private static final String LOG_TAG = "ImsEnablementTracker"; 46 private static final long REQUEST_THROTTLE_TIME_MS = 3 * 1000L; // 3 seconds 47 48 private static final int COMMAND_NONE_MSG = 0; 49 // Indicate that the enableIms command has been received. 50 @VisibleForTesting 51 public static final int COMMAND_ENABLE_MSG = 1; 52 // Indicate that the disableIms command has been received. 53 @VisibleForTesting 54 public static final int COMMAND_DISABLE_MSG = 2; 55 // Indicate that the resetIms command has been received. 56 private static final int COMMAND_RESET_MSG = 3; 57 // Indicate that the internal enable message with delay has been received. 58 private static final int COMMAND_ENABLING_DONE = 4; 59 // Indicate that the internal disable message with delay has been received. 60 private static final int COMMAND_DISABLING_DONE = 5; 61 // Indicate that the internal reset message with delay has been received. 62 @VisibleForTesting 63 public static final int COMMAND_RESETTING_DONE = 6; 64 // The ImsServiceController binder is connected. 65 private static final int COMMAND_CONNECTED_MSG = 7; 66 // The ImsServiceController binder is disconnected. 67 private static final int COMMAND_DISCONNECTED_MSG = 8; 68 // The subId is changed to INVALID_SUBSCRIPTION_ID. 69 private static final int COMMAND_INVALID_SUBID_MSG = 9; 70 // Indicate that the internal post reset message with delay has been received. 71 @VisibleForTesting 72 public static final int COMMAND_POST_RESETTING_DONE = 10; 73 74 private static final Map<Integer, String> EVENT_DESCRIPTION = new HashMap<>(); 75 static { EVENT_DESCRIPTION.put(COMMAND_NONE_MSG, "COMMAND_NONE_MSG")76 EVENT_DESCRIPTION.put(COMMAND_NONE_MSG, "COMMAND_NONE_MSG"); EVENT_DESCRIPTION.put(COMMAND_ENABLE_MSG, "COMMAND_ENABLE_MSG")77 EVENT_DESCRIPTION.put(COMMAND_ENABLE_MSG, "COMMAND_ENABLE_MSG"); EVENT_DESCRIPTION.put(COMMAND_DISABLE_MSG, "COMMAND_DISABLE_MSG")78 EVENT_DESCRIPTION.put(COMMAND_DISABLE_MSG, "COMMAND_DISABLE_MSG"); EVENT_DESCRIPTION.put(COMMAND_RESET_MSG, "COMMAND_RESET_MSG")79 EVENT_DESCRIPTION.put(COMMAND_RESET_MSG, "COMMAND_RESET_MSG"); EVENT_DESCRIPTION.put(COMMAND_ENABLING_DONE, "COMMAND_ENABLING_DONE")80 EVENT_DESCRIPTION.put(COMMAND_ENABLING_DONE, "COMMAND_ENABLING_DONE"); EVENT_DESCRIPTION.put(COMMAND_DISABLING_DONE, "COMMAND_DISABLING_DONE")81 EVENT_DESCRIPTION.put(COMMAND_DISABLING_DONE, "COMMAND_DISABLING_DONE"); EVENT_DESCRIPTION.put(COMMAND_RESETTING_DONE, "COMMAND_RESETTING_DONE")82 EVENT_DESCRIPTION.put(COMMAND_RESETTING_DONE, "COMMAND_RESETTING_DONE"); EVENT_DESCRIPTION.put(COMMAND_CONNECTED_MSG, "COMMAND_CONNECTED_MSG")83 EVENT_DESCRIPTION.put(COMMAND_CONNECTED_MSG, "COMMAND_CONNECTED_MSG"); EVENT_DESCRIPTION.put(COMMAND_DISCONNECTED_MSG, "COMMAND_DISCONNECTED_MSG")84 EVENT_DESCRIPTION.put(COMMAND_DISCONNECTED_MSG, "COMMAND_DISCONNECTED_MSG"); EVENT_DESCRIPTION.put(COMMAND_INVALID_SUBID_MSG, "COMMAND_INVALID_SUBID_MSG")85 EVENT_DESCRIPTION.put(COMMAND_INVALID_SUBID_MSG, "COMMAND_INVALID_SUBID_MSG"); 86 } 87 88 @VisibleForTesting 89 protected static final int STATE_IMS_DISCONNECTED = 0; 90 @VisibleForTesting 91 protected static final int STATE_IMS_DEFAULT = 1; 92 @VisibleForTesting 93 protected static final int STATE_IMS_ENABLED = 2; 94 @VisibleForTesting 95 protected static final int STATE_IMS_DISABLING = 3; 96 @VisibleForTesting 97 protected static final int STATE_IMS_DISABLED = 4; 98 @VisibleForTesting 99 protected static final int STATE_IMS_ENABLING = 5; 100 @VisibleForTesting 101 protected static final int STATE_IMS_RESETTING = 6; 102 103 @VisibleForTesting 104 protected static final int STATE_IMS_POSTRESETTING = 7; 105 106 protected final Object mLock = new Object(); 107 private IImsServiceController mIImsServiceController; 108 private long mLastImsOperationTimeMs = 0L; 109 private final ComponentName mComponentName; 110 private final SparseArray<ImsEnablementTrackerStateMachine> mStateMachines; 111 112 private final Looper mLooper; 113 private final int mState; 114 115 /** 116 * Provides Ims Enablement Tracker State Machine responsible for ims enable/disable/reset 117 * command interactions with Ims service controller binder. 118 * The enable/disable/reset ims commands have a time interval of at least 119 * {@link ImsEnablementTracker#REQUEST_THROTTLE_TIME_MS} second between 120 * processing each command. 121 * For example, the enableIms command is received and the binder's enableIms is called. 122 * After that, if the disableIms command is received, the binder's disableIms will be 123 * called after {@link ImsEnablementTracker#REQUEST_THROTTLE_TIME_MS} second. 124 * A time of {@link ImsEnablementTracker#REQUEST_THROTTLE_TIME_MS} will be used 125 * {@link Handler#sendMessageDelayed(Message, long)}, 126 * and the enabled, disabled and reset states are responsible for waiting for 127 * that delay message. 128 */ 129 class ImsEnablementTrackerStateMachine extends StateMachine { 130 /** 131 * The initial state of this class and waiting for an ims commands. 132 */ 133 @VisibleForTesting 134 public final Default mDefault; 135 136 /** 137 * Indicates that {@link IImsServiceController#enableIms(int, int)} has been called and 138 * waiting for an ims commands. 139 * Common transitions are to 140 * {@link #mDisabling} state when the disable command is received 141 * or {@link #mResetting} state when the reset command is received 142 * or {@link #mDisconnected} if the binder is disconnected. 143 */ 144 @VisibleForTesting 145 public final Enabled mEnabled; 146 147 /** 148 * Indicates that the state waiting for the throttle time to elapse before calling 149 * {@link IImsServiceController#disableIms(int, int)}. 150 * Common transitions are to 151 * {@link #mEnabled} when the enable command is received. 152 * or {@link #mResetting} when the reset command is received. 153 * or {@link #mDisabled} the previous binder API call has passed 154 * {@link ImsEnablementTracker#REQUEST_THROTTLE_TIME_MS} second, and if 155 * {@link IImsServiceController#disableIms(int, int)} called. 156 * or {@link #mDisabling} received a disableIms message and the previous binder API call 157 * has not passed {@link ImsEnablementTracker#REQUEST_THROTTLE_TIME_MS} second. 158 * Then send a disableIms message with delay. 159 * or {@link #mDisconnected} if the binder is disconnected. 160 */ 161 @VisibleForTesting 162 public final Disabling mDisabling; 163 164 /** 165 * Indicates that {@link IImsServiceController#disableIms(int, int)} has been called and 166 * waiting for an ims commands. 167 * Common transitions are to 168 * {@link #mEnabling} state when the enable command is received. 169 * or {@link #mDisconnected} if the binder is disconnected. 170 */ 171 @VisibleForTesting 172 public final Disabled mDisabled; 173 174 /** 175 * Indicates that the state waiting for the throttle time to elapse before calling 176 * {@link IImsServiceController#enableIms(int, int)}. 177 * Common transitions are to 178 * {@link #mEnabled} the previous binder API call has passed 179 * {@link ImsEnablementTracker#REQUEST_THROTTLE_TIME_MS} second, and 180 * {@link IImsServiceController#enableIms(int, int)} called. 181 * or {@link #mDisabled} when the disable command is received. 182 * or {@link #mEnabling} received an enableIms message and the previous binder API call 183 * has not passed {@link ImsEnablementTracker#REQUEST_THROTTLE_TIME_MS} second. 184 * Then send an enableIms message with delay. 185 * or {@link #mDisconnected} if the binder is disconnected. 186 */ 187 @VisibleForTesting 188 public final Enabling mEnabling; 189 190 /** 191 * Indicates that the state waiting for the throttle time to elapse before calling 192 * {@link IImsServiceController#resetIms(int, int)}. 193 * Common transitions are to 194 * {@link #mPostResetting} state to call either enableIms or disableIms after calling 195 * {@link IImsServiceController#resetIms(int, int)} 196 * or {@link #mDisconnected} if the binder is disconnected. 197 */ 198 @VisibleForTesting 199 public final Resetting mResetting; 200 201 /** 202 * Indicates that the state waiting after resetIms for the throttle time to elapse before 203 * calling {@link IImsServiceController#enableIms(int, int)} or 204 * {@link IImsServiceController#disableIms(int, int)}. 205 * Common transitions are to 206 * {@link #mEnabled} state when the disable command is received, 207 * {@link #mDisabled} state when the enable command is received after calling 208 * {@link IImsServiceController#enableIms(int, int)}, 209 * {@link IImsServiceController#disableIms(int, int)} 210 * or {@link #mDisconnected} if the binder is disconnected. 211 */ 212 public final PostResetting mPostResetting; 213 214 /** 215 * Indicates that {@link IImsServiceController} has not been set. 216 * Common transition is to 217 * {@link #mDefault} state when the binder is set. 218 * or {@link #mDisabling} If the disable command is received while the binder is 219 * disconnected 220 * or {@link #mEnabling} If the enable command is received while the binder is 221 * disconnected 222 */ 223 224 private final Disconnected mDisconnected; 225 private int mSlotId; 226 private int mSubId; 227 228 private final int mPhoneId; 229 230 private IState mPreviousState; 231 232 private int mLastMsg = COMMAND_NONE_MSG; 233 ImsEnablementTrackerStateMachine(String name, Looper looper, int state, int slotId)234 ImsEnablementTrackerStateMachine(String name, Looper looper, int state, int slotId) { 235 super(name, looper); 236 mPhoneId = slotId; 237 mDefault = new Default(); 238 mEnabled = new Enabled(); 239 mDisabling = new Disabling(); 240 mDisabled = new Disabled(); 241 mEnabling = new Enabling(); 242 mResetting = new Resetting(); 243 mDisconnected = new Disconnected(); 244 mPostResetting = new PostResetting(); 245 246 addState(mDefault); 247 addState(mEnabled); 248 addState(mDisabling); 249 addState(mDisabled); 250 addState(mEnabling); 251 addState(mResetting); 252 addState(mDisconnected); 253 addState(mPostResetting); 254 255 setInitialState(getState(state)); 256 mPreviousState = getState(state); 257 } 258 clearAllMessage()259 public void clearAllMessage() { 260 Log.d(LOG_TAG, "clearAllMessage"); 261 removeMessages(COMMAND_ENABLE_MSG); 262 removeMessages(COMMAND_DISABLE_MSG); 263 removeMessages(COMMAND_RESET_MSG); 264 removeMessages(COMMAND_ENABLING_DONE); 265 removeMessages(COMMAND_DISABLING_DONE); 266 removeMessages(COMMAND_RESETTING_DONE); 267 removeMessages(COMMAND_POST_RESETTING_DONE); 268 } 269 serviceBinderConnected()270 public void serviceBinderConnected() { 271 clearAllMessage(); 272 sendMessage(COMMAND_CONNECTED_MSG); 273 } 274 serviceBinderDisconnected()275 public void serviceBinderDisconnected() { 276 clearAllMessage(); 277 sendMessage(COMMAND_DISCONNECTED_MSG); 278 } 279 280 @VisibleForTesting isState(int state)281 public boolean isState(int state) { 282 State expect = null; 283 switch (state) { 284 case Default.STATE_NO: 285 expect = mDefault; 286 break; 287 case Enabled.STATE_NO: 288 expect = mEnabled; 289 break; 290 case Disabling.STATE_NO: 291 expect = mDisabling; 292 break; 293 case Disabled.STATE_NO: 294 expect = mDisabled; 295 break; 296 case Enabling.STATE_NO: 297 expect = mEnabling; 298 break; 299 case Resetting.STATE_NO: 300 expect = mResetting; 301 break; 302 case Disconnected.STATE_NO: 303 expect = mDisconnected; 304 break; 305 case PostResetting.STATE_NO: 306 expect = mPostResetting; 307 break; 308 default: 309 break; 310 } 311 return (getCurrentState() == expect) ? true : false; 312 } 313 getState(int state)314 private State getState(int state) { 315 switch (state) { 316 case ImsEnablementTracker.STATE_IMS_ENABLED: 317 return mEnabled; 318 case ImsEnablementTracker.STATE_IMS_DISABLING: 319 return mDisabling; 320 case ImsEnablementTracker.STATE_IMS_DISABLED: 321 return mDisabled; 322 case ImsEnablementTracker.STATE_IMS_ENABLING: 323 return mEnabling; 324 case ImsEnablementTracker.STATE_IMS_RESETTING: 325 return mResetting; 326 case ImsEnablementTracker.STATE_IMS_DISCONNECTED: 327 return mDisconnected; 328 case ImsEnablementTracker.STATE_IMS_POSTRESETTING: 329 return mPostResetting; 330 default: 331 return mDefault; 332 } 333 } 334 handleInvalidSubIdMessage()335 private void handleInvalidSubIdMessage() { 336 clearAllMessage(); 337 transitionState(mDefault); 338 } 339 transitionState(State state)340 private void transitionState(State state) { 341 mPreviousState = getCurrentState(); 342 transitionTo(state); 343 } 344 345 class Default extends State { 346 private static final int STATE_NO = STATE_IMS_DEFAULT; 347 348 @Override enter()349 public void enter() { 350 Log.d(LOG_TAG, "[" + mPhoneId + "]Default state:enter"); 351 mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 352 } 353 354 @Override processMessage(Message message)355 public boolean processMessage(Message message) { 356 Log.d(LOG_TAG, "[" + mPhoneId + "]Default state:processMessage. msg.what=" 357 + EVENT_DESCRIPTION.get(message.what) + ",component:" + mComponentName); 358 359 switch (message.what) { 360 // When enableIms() is called, enableIms of binder is call and the state 361 // change to the enabled state. 362 case COMMAND_ENABLE_MSG: 363 sendEnableIms(message.arg1, message.arg2); 364 transitionState(mEnabled); 365 return HANDLED; 366 // When disableIms() is called, disableIms of binder is call and the state 367 // change to the disabled state. 368 case COMMAND_DISABLE_MSG: 369 sendDisableIms(message.arg1, message.arg2); 370 transitionState(mDisabled); 371 return HANDLED; 372 // When resetIms() is called, change to the resetting state to call enableIms 373 // after calling resetIms of binder. 374 case COMMAND_RESET_MSG: 375 mSlotId = message.arg1; 376 mSubId = message.arg2; 377 transitionState(mResetting); 378 return HANDLED; 379 case COMMAND_DISCONNECTED_MSG: 380 transitionState(mDisconnected); 381 return HANDLED; 382 default: 383 return NOT_HANDLED; 384 } 385 } 386 } 387 388 class Enabled extends State { 389 private static final int STATE_NO = STATE_IMS_ENABLED; 390 391 @Override enter()392 public void enter() { 393 Log.d(LOG_TAG, "[" + mPhoneId + "]Enabled state:enter"); 394 } 395 396 @Override processMessage(Message message)397 public boolean processMessage(Message message) { 398 Log.d(LOG_TAG, "[" + mPhoneId + "]Enabled state:processMessage. msg.what=" 399 + EVENT_DESCRIPTION.get(message.what) + ",component:" + mComponentName); 400 401 switch (message.what) { 402 // the disableIms() is called. 403 case COMMAND_DISABLE_MSG: 404 mSlotId = message.arg1; 405 mSubId = message.arg2; 406 transitionState(mDisabling); 407 return HANDLED; 408 // the resetIms() is called. 409 case COMMAND_RESET_MSG: 410 mSlotId = message.arg1; 411 mSubId = message.arg2; 412 transitionState(mResetting); 413 return HANDLED; 414 case COMMAND_DISCONNECTED_MSG: 415 transitionState(mDisconnected); 416 return HANDLED; 417 case COMMAND_INVALID_SUBID_MSG: 418 handleInvalidSubIdMessage(); 419 return HANDLED; 420 default: 421 return NOT_HANDLED; 422 } 423 } 424 } 425 426 class Disabling extends State { 427 private static final int STATE_NO = STATE_IMS_DISABLING; 428 429 @Override enter()430 public void enter() { 431 Log.d(LOG_TAG, "[" + mPhoneId + "]Disabling state:enter"); 432 sendMessageDelayed(COMMAND_DISABLING_DONE, mSlotId, mSubId, 433 getRemainThrottleTime()); 434 } 435 436 @Override processMessage(Message message)437 public boolean processMessage(Message message) { 438 Log.d(LOG_TAG, "[" + mPhoneId + "]Disabling state:processMessage. msg.what=" 439 + EVENT_DESCRIPTION.get(message.what) + ",component:" + mComponentName); 440 441 switch (message.what) { 442 case COMMAND_ENABLE_MSG: 443 mSlotId = message.arg1; 444 mSubId = message.arg2; 445 clearAllMessage(); 446 if (mPreviousState == mResetting) { 447 // if we are moving from Resetting -> Disabling and receive 448 // the COMMAND_ENABLE_MSG, we need to send the enableIms command, 449 // so move to Enabling state. 450 transitionState(mEnabling); 451 } else { 452 // When moving from Enabled -> Disabling and we receive an ENABLE_MSG, 453 // we can move straight back to Enabled state because we have not sent 454 // the disableIms command to IMS yet. 455 transitionState(mEnabled); 456 } 457 return HANDLED; 458 case COMMAND_DISABLING_DONE: 459 // If the disable command is received before disableIms is processed, 460 // it will be ignored because the disable command processing is in progress. 461 removeMessages(COMMAND_DISABLE_MSG); 462 sendDisableIms(message.arg1, message.arg2); 463 transitionState(mDisabled); 464 return HANDLED; 465 case COMMAND_RESET_MSG: 466 mSlotId = message.arg1; 467 mSubId = message.arg2; 468 clearAllMessage(); 469 transitionState(mResetting); 470 return HANDLED; 471 case COMMAND_DISCONNECTED_MSG: 472 transitionState(mDisconnected); 473 return HANDLED; 474 case COMMAND_INVALID_SUBID_MSG: 475 handleInvalidSubIdMessage(); 476 return HANDLED; 477 default: 478 return NOT_HANDLED; 479 } 480 } 481 } 482 483 class Disabled extends State { 484 private static final int STATE_NO = STATE_IMS_DISABLED; 485 486 @Override enter()487 public void enter() { 488 Log.d(LOG_TAG, "[" + mPhoneId + "]Disabled state:enter"); 489 } 490 491 @Override processMessage(Message message)492 public boolean processMessage(Message message) { 493 Log.d(LOG_TAG, "[" + mPhoneId + "]Disabled state:processMessage. msg.what=" 494 + EVENT_DESCRIPTION.get(message.what) + ",component:" + mComponentName); 495 496 switch (message.what) { 497 case COMMAND_ENABLE_MSG: 498 mSlotId = message.arg1; 499 mSubId = message.arg2; 500 transitionState(mEnabling); 501 return HANDLED; 502 case COMMAND_RESET_MSG: 503 mSlotId = message.arg1; 504 mSubId = message.arg2; 505 transitionState(mResetting); 506 return HANDLED; 507 case COMMAND_DISCONNECTED_MSG: 508 transitionState(mDisconnected); 509 return HANDLED; 510 case COMMAND_INVALID_SUBID_MSG: 511 handleInvalidSubIdMessage(); 512 return HANDLED; 513 default: 514 return NOT_HANDLED; 515 } 516 } 517 } 518 519 class Enabling extends State { 520 private static final int STATE_NO = STATE_IMS_ENABLING; 521 522 @Override enter()523 public void enter() { 524 Log.d(LOG_TAG, "[" + mPhoneId + "]Enabling state:enter"); 525 sendMessageDelayed(COMMAND_ENABLING_DONE, mSlotId, mSubId, getRemainThrottleTime()); 526 } 527 528 @Override processMessage(Message message)529 public boolean processMessage(Message message) { 530 Log.d(LOG_TAG, "[" + mPhoneId + "]Enabling state:processMessage. msg.what=" 531 + EVENT_DESCRIPTION.get(message.what) + ",component:" + mComponentName); 532 533 switch (message.what) { 534 // Enabling state comes from Resetting and disableIms() is called. 535 // In this case disableIms() of binder should be called. 536 // When enabling state comes from disabled, just change state to the disabled. 537 case COMMAND_DISABLE_MSG: 538 mSlotId = message.arg1; 539 mSubId = message.arg2; 540 clearAllMessage(); 541 if (mPreviousState == mResetting) { 542 transitionState(mDisabling); 543 } else { 544 transitionState(mDisabled); 545 } 546 return HANDLED; 547 case COMMAND_RESET_MSG: 548 mSlotId = message.arg1; 549 mSubId = message.arg2; 550 transitionState(mResetting); 551 return HANDLED; 552 case COMMAND_ENABLING_DONE: 553 // If the enable command is received before enableIms is processed, 554 // it will be ignored because the enable command processing is in progress. 555 removeMessages(COMMAND_ENABLE_MSG); 556 sendEnableIms(message.arg1, message.arg2); 557 transitionState(mEnabled); 558 return HANDLED; 559 case COMMAND_DISCONNECTED_MSG: 560 transitionState(mDisconnected); 561 return HANDLED; 562 case COMMAND_INVALID_SUBID_MSG: 563 handleInvalidSubIdMessage(); 564 return HANDLED; 565 default: 566 return NOT_HANDLED; 567 } 568 } 569 } 570 571 class Resetting extends State { 572 private static final int STATE_NO = STATE_IMS_RESETTING; 573 574 @Override enter()575 public void enter() { 576 Log.d(LOG_TAG, "[" + mPhoneId + "]Resetting state:enter"); 577 sendMessageDelayed(COMMAND_RESETTING_DONE, mSlotId, mSubId, 578 getRemainThrottleTime()); 579 } 580 581 @Override processMessage(Message message)582 public boolean processMessage(Message message) { 583 Log.d(LOG_TAG, "[" + mPhoneId + "]Resetting state:processMessage. msg.what=" 584 + EVENT_DESCRIPTION.get(message.what) + ",component:" + mComponentName); 585 586 switch (message.what) { 587 case COMMAND_DISABLE_MSG: 588 mLastMsg = COMMAND_DISABLE_MSG; 589 return HANDLED; 590 case COMMAND_ENABLE_MSG: 591 mLastMsg = COMMAND_ENABLE_MSG; 592 return HANDLED; 593 case COMMAND_RESETTING_DONE: 594 mSlotId = message.arg1; 595 mSubId = message.arg2; 596 // If the reset command is received before disableIms is processed, 597 // it will be ignored because the reset command processing is in progress. 598 removeMessages(COMMAND_RESET_MSG); 599 sendResetIms(mSlotId, mSubId); 600 transitionState(mPostResetting); 601 return HANDLED; 602 case COMMAND_DISCONNECTED_MSG: 603 transitionState(mDisconnected); 604 return HANDLED; 605 case COMMAND_INVALID_SUBID_MSG: 606 handleInvalidSubIdMessage(); 607 return HANDLED; 608 default: 609 return NOT_HANDLED; 610 } 611 } 612 } 613 614 class Disconnected extends State { 615 private static final int STATE_NO = STATE_IMS_DISCONNECTED; 616 617 private int mLastMsg = COMMAND_NONE_MSG; 618 619 @Override enter()620 public void enter() { 621 Log.d(LOG_TAG, "[" + mPhoneId + "]Disconnected state:enter"); 622 clearAllMessage(); 623 } 624 625 @Override processMessage(Message message)626 public boolean processMessage(Message message) { 627 Log.d(LOG_TAG, "[" + mPhoneId + "]Disconnected state:processMessage. msg.what=" 628 + EVENT_DESCRIPTION.get(message.what) + ",component:" + mComponentName); 629 630 switch (message.what) { 631 case COMMAND_CONNECTED_MSG: 632 clearAllMessage(); 633 transitionState(mDefault); 634 if (mLastMsg != COMMAND_NONE_MSG) { 635 sendMessageDelayed(mLastMsg, mSlotId, mSubId, 0); 636 mLastMsg = COMMAND_NONE_MSG; 637 } 638 return HANDLED; 639 case COMMAND_ENABLE_MSG: 640 case COMMAND_DISABLE_MSG: 641 case COMMAND_RESET_MSG: 642 mLastMsg = message.what; 643 mSlotId = message.arg1; 644 mSubId = message.arg2; 645 return HANDLED; 646 default: 647 return NOT_HANDLED; 648 } 649 } 650 } 651 652 class PostResetting extends State { 653 private static final int STATE_NO = STATE_IMS_POSTRESETTING; 654 655 @Override enter()656 public void enter() { 657 Log.d(LOG_TAG, "[" + mPhoneId + "]PostResetting state:enter"); 658 sendMessageDelayed(COMMAND_POST_RESETTING_DONE, mSlotId, mSubId, 659 getRemainThrottleTime()); 660 } 661 662 @Override exit()663 public void exit() { 664 mLastMsg = COMMAND_NONE_MSG; 665 } 666 667 @Override processMessage(Message message)668 public boolean processMessage(Message message) { 669 Log.d(LOG_TAG, "[" + mPhoneId + "]PostResetting state:processMessage. msg.what=" 670 + EVENT_DESCRIPTION.get(message.what) + ",component:" + mComponentName); 671 672 switch (message.what) { 673 case COMMAND_POST_RESETTING_DONE: 674 mSlotId = message.arg1; 675 mSubId = message.arg2; 676 if (mLastMsg == COMMAND_DISABLE_MSG) { 677 sendDisableIms(mSlotId, mSubId); 678 transitionState(mDisabled); 679 } else { 680 // if mLastMsg is COMMAND_NONE_MSG or COMMAND_ENABLE_MSG 681 sendEnableIms(mSlotId, mSubId); 682 transitionState(mEnabled); 683 } 684 return HANDLED; 685 case COMMAND_ENABLE_MSG: 686 case COMMAND_DISABLE_MSG: 687 mLastMsg = message.what; 688 mSlotId = message.arg1; 689 mSubId = message.arg2; 690 return HANDLED; 691 case COMMAND_RESET_MSG: 692 // when resetIms() called again, skip to call 693 // IImsServiceController.resetIms(slotId, subId), but after throttle time 694 // IImsServiceController.enableIms(slotId, subId) should be called. 695 mLastMsg = COMMAND_ENABLE_MSG; 696 mSlotId = message.arg1; 697 mSubId = message.arg2; 698 return HANDLED; 699 case COMMAND_DISCONNECTED_MSG: 700 transitionState(mDisconnected); 701 return HANDLED; 702 case COMMAND_INVALID_SUBID_MSG: 703 handleInvalidSubIdMessage(); 704 return HANDLED; 705 default: 706 return NOT_HANDLED; 707 } 708 } 709 } 710 } 711 ImsEnablementTracker(Looper looper, ComponentName componentName)712 public ImsEnablementTracker(Looper looper, ComponentName componentName) { 713 mIImsServiceController = null; 714 mStateMachines = new SparseArray<>(); 715 mLooper = looper; 716 mState = ImsEnablementTracker.STATE_IMS_DISCONNECTED; 717 mComponentName = componentName; 718 } 719 720 @VisibleForTesting ImsEnablementTracker(Looper looper, IImsServiceController controller, int state, int numSlots)721 public ImsEnablementTracker(Looper looper, IImsServiceController controller, int state, 722 int numSlots) { 723 mIImsServiceController = controller; 724 mStateMachines = new SparseArray<>(); 725 mLooper = looper; 726 mState = state; 727 mComponentName = null; 728 729 setNumOfSlots(numSlots); 730 } 731 732 /** 733 * Set the number of SIM slots. 734 * @param numOfSlots the number of SIM slots. 735 */ setNumOfSlots(int numOfSlots)736 public void setNumOfSlots(int numOfSlots) { 737 int oldNumSlots = mStateMachines.size(); 738 Log.d(LOG_TAG, "set the slots: old[" + oldNumSlots + "], new[" + numOfSlots + "]," 739 + "component:" + mComponentName); 740 if (numOfSlots == oldNumSlots) { 741 return; 742 } 743 ImsEnablementTrackerStateMachine enablementStateMachine = null; 744 if (oldNumSlots < numOfSlots) { 745 for (int i = oldNumSlots; i < numOfSlots; i++) { 746 enablementStateMachine = new ImsEnablementTrackerStateMachine( 747 "ImsEnablementTracker", mLooper, mState, i); 748 enablementStateMachine.start(); 749 mStateMachines.put(i, enablementStateMachine); 750 } 751 } else if (oldNumSlots > numOfSlots) { 752 for (int i = (oldNumSlots - 1); i > (numOfSlots - 1); i--) { 753 enablementStateMachine = mStateMachines.get(i); 754 mStateMachines.remove(i); 755 enablementStateMachine.quitNow(); 756 } 757 } 758 } 759 760 @VisibleForTesting getHandler(int slotId)761 public Handler getHandler(int slotId) { 762 return mStateMachines.get(slotId).getHandler(); 763 } 764 765 /** 766 * Check that the current state and the input state are the same. 767 * @param state the input state. 768 * @return true if the current state and input state are the same or false. 769 */ 770 @VisibleForTesting isState(int slotId, int state)771 public boolean isState(int slotId, int state) { 772 return mStateMachines.get(slotId).isState(state); 773 } 774 775 /** 776 * Notify the state machine that the subId has changed to invalid. 777 * @param slotId subscription id 778 */ subIdChangedToInvalid(int slotId)779 public void subIdChangedToInvalid(int slotId) { 780 Log.d(LOG_TAG, "[" + slotId + "] subId changed to invalid, component:" + mComponentName); 781 ImsEnablementTrackerStateMachine stateMachine = mStateMachines.get(slotId); 782 if (stateMachine != null) { 783 stateMachine.sendMessage(COMMAND_INVALID_SUBID_MSG, slotId); 784 } else { 785 Log.w(LOG_TAG, "There is no state machine associated with this slotId."); 786 } 787 } 788 789 /** 790 * Notify ImsService to enable IMS for the framework. This will trigger IMS registration and 791 * trigger ImsFeature status updates. 792 * @param slotId slot id 793 * @param subId subscription id 794 */ enableIms(int slotId, int subId)795 public void enableIms(int slotId, int subId) { 796 Log.d(LOG_TAG, "[" + slotId + "][" + subId + "]enableIms, component:" + mComponentName); 797 ImsEnablementTrackerStateMachine stateMachine = mStateMachines.get(slotId); 798 if (stateMachine != null) { 799 stateMachine.sendMessage(COMMAND_ENABLE_MSG, slotId, subId); 800 } else { 801 Log.w(LOG_TAG, "There is no state machine associated with this slotId."); 802 } 803 } 804 805 /** 806 * Notify ImsService to disable IMS for the framework. This will trigger IMS de-registration and 807 * trigger ImsFeature capability status to become false. 808 * @param slotId slot id 809 * @param subId subscription id 810 */ disableIms(int slotId, int subId)811 public void disableIms(int slotId, int subId) { 812 Log.d(LOG_TAG, "[" + slotId + "][" + subId + "]disableIms, component:" + mComponentName); 813 ImsEnablementTrackerStateMachine stateMachine = mStateMachines.get(slotId); 814 if (stateMachine != null) { 815 stateMachine.sendMessage(COMMAND_DISABLE_MSG, slotId, subId); 816 } else { 817 Log.w(LOG_TAG, "There is no state machine associated with this slotId."); 818 } 819 } 820 821 /** 822 * Notify ImsService to reset IMS for the framework. This will trigger ImsService to perform 823 * de-registration and release all resource. After that, if enaleIms is called, the ImsService 824 * performs registration and appropriate initialization to bring up all ImsFeatures. 825 * @param slotId slot id 826 * @param subId subscription id 827 */ resetIms(int slotId, int subId)828 public void resetIms(int slotId, int subId) { 829 Log.d(LOG_TAG, "[" + slotId + "][" + subId + "]resetIms, component:" + mComponentName); 830 ImsEnablementTrackerStateMachine stateMachine = mStateMachines.get(slotId); 831 if (stateMachine != null) { 832 stateMachine.sendMessage(COMMAND_RESET_MSG, slotId, subId); 833 } else { 834 Log.w(LOG_TAG, "There is no state machine associated with this slotId."); 835 } 836 } 837 838 /** 839 * Sets the IImsServiceController instance. 840 */ setServiceController(IBinder serviceController)841 protected void setServiceController(IBinder serviceController) { 842 synchronized (mLock) { 843 mIImsServiceController = IImsServiceController.Stub.asInterface(serviceController); 844 Log.d(LOG_TAG, "setServiceController with Binder:" + mIImsServiceController 845 + ", component:" + mComponentName); 846 ImsEnablementTrackerStateMachine stateMachine = null; 847 for (int i = 0; i < mStateMachines.size(); i++) { 848 stateMachine = mStateMachines.get(i); 849 if (stateMachine == null) { 850 Log.w(LOG_TAG, "There is no state machine associated with" 851 + "the slotId[" + i + "]"); 852 continue; 853 } 854 if (isServiceControllerAvailable()) { 855 stateMachine.serviceBinderConnected(); 856 } else { 857 stateMachine.serviceBinderDisconnected(); 858 } 859 } 860 } 861 } 862 getLastOperationTimeMillis()863 protected long getLastOperationTimeMillis() { 864 return mLastImsOperationTimeMs; 865 } 866 867 /** 868 * Get remaining throttle time value 869 * @return remaining throttle time value 870 */ 871 @VisibleForTesting getRemainThrottleTime()872 public long getRemainThrottleTime() { 873 long remainTime = REQUEST_THROTTLE_TIME_MS - (System.currentTimeMillis() 874 - getLastOperationTimeMillis()); 875 876 if (remainTime < 0) { 877 remainTime = 0L; 878 } 879 Log.d(LOG_TAG, "getRemainThrottleTime:" + remainTime); 880 881 return remainTime; 882 } 883 884 /** 885 * Check to see if the service controller is available. 886 * @return true if available, false otherwise 887 */ isServiceControllerAvailable()888 private boolean isServiceControllerAvailable() { 889 if (mIImsServiceController != null) { 890 return true; 891 } 892 Log.d(LOG_TAG, "isServiceControllerAvailable : binder is not alive"); 893 return false; 894 } 895 sendEnableIms(int slotId, int subId)896 private void sendEnableIms(int slotId, int subId) { 897 try { 898 synchronized (mLock) { 899 if (isServiceControllerAvailable()) { 900 Log.d(LOG_TAG, "[" + slotId + "][" + subId + "]sendEnableIms," 901 + "componentName[" + mComponentName + "]"); 902 mIImsServiceController.enableIms(slotId, subId); 903 mLastImsOperationTimeMs = System.currentTimeMillis(); 904 } 905 } 906 } catch (RemoteException e) { 907 Log.w(LOG_TAG, "Couldn't enable IMS: " + e.getMessage()); 908 } 909 } 910 sendDisableIms(int slotId, int subId)911 private void sendDisableIms(int slotId, int subId) { 912 try { 913 synchronized (mLock) { 914 if (isServiceControllerAvailable()) { 915 Log.d(LOG_TAG, "[" + slotId + "][" + subId + "]sendDisableIms" 916 + " componentName[" + mComponentName + "]"); 917 mIImsServiceController.disableIms(slotId, subId); 918 mLastImsOperationTimeMs = System.currentTimeMillis(); 919 } 920 } 921 } catch (RemoteException e) { 922 Log.w(LOG_TAG, "Couldn't disable IMS: " + e.getMessage()); 923 } 924 } 925 sendResetIms(int slotId, int subId)926 private void sendResetIms(int slotId, int subId) { 927 try { 928 synchronized (mLock) { 929 if (isServiceControllerAvailable()) { 930 Log.d(LOG_TAG, "[" + slotId + "][" + subId + "]sendResetIms"); 931 mIImsServiceController.resetIms(slotId, subId); 932 mLastImsOperationTimeMs = System.currentTimeMillis(); 933 } 934 } 935 } catch (RemoteException e) { 936 Log.w(LOG_TAG, "Couldn't reset IMS: " + e.getMessage()); 937 } 938 } 939 } 940