1 /* 2 * Copyright (C) 2021 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; 18 19 import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_FIRST_CHANGE; 20 import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_FIRST_POWER_UP; 21 import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_IMS_ONLY; 22 import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_NONE; 23 import static android.telephony.CarrierConfigManager.ImsSs.CALL_WAITING_SYNC_USER_CHANGE; 24 import static android.telephony.CarrierConfigManager.ImsSs.KEY_TERMINAL_BASED_CALL_WAITING_DEFAULT_ENABLED_BOOL; 25 import static android.telephony.CarrierConfigManager.ImsSs.KEY_TERMINAL_BASED_CALL_WAITING_SYNC_TYPE_INT; 26 import static android.telephony.CarrierConfigManager.ImsSs.KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY; 27 import static android.telephony.CarrierConfigManager.ImsSs.SUPPLEMENTARY_SERVICE_CW; 28 29 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_NONE; 30 import static com.android.internal.telephony.CommandsInterface.SERVICE_CLASS_VOICE; 31 32 import android.annotation.Nullable; 33 import android.content.Context; 34 import android.content.SharedPreferences; 35 import android.os.AsyncResult; 36 import android.os.Handler; 37 import android.os.Message; 38 import android.os.PersistableBundle; 39 import android.telephony.CarrierConfigManager; 40 import android.telephony.ServiceState; 41 import android.telephony.SubscriptionManager; 42 43 import com.android.internal.annotations.VisibleForTesting; 44 import com.android.internal.util.IndentingPrintWriter; 45 import com.android.telephony.Rlog; 46 47 import java.io.PrintWriter; 48 49 /** 50 * Controls the change of the user setting of the call waiting service 51 */ 52 public class CallWaitingController extends Handler { 53 54 public static final String LOG_TAG = "CallWaitingCtrl"; 55 private static final boolean DBG = false; /* STOPSHIP if true */ 56 57 // Terminal-based call waiting is not supported. */ 58 public static final int TERMINAL_BASED_NOT_SUPPORTED = -1; 59 // Terminal-based call waiting is supported but not activated. */ 60 public static final int TERMINAL_BASED_NOT_ACTIVATED = 0; 61 // Terminal-based call waiting is supported and activated. */ 62 public static final int TERMINAL_BASED_ACTIVATED = 1; 63 64 private static final int EVENT_SET_CALL_WAITING_DONE = 1; 65 private static final int EVENT_GET_CALL_WAITING_DONE = 2; 66 private static final int EVENT_REGISTERED_TO_NETWORK = 3; 67 68 // Class to pack mOnComplete object passed by the caller 69 private static class Cw { 70 final boolean mEnable; 71 final Message mOnComplete; 72 final boolean mImsRegistered; 73 Cw(boolean enable, boolean imsRegistered, Message onComplete)74 Cw(boolean enable, boolean imsRegistered, Message onComplete) { 75 mEnable = enable; 76 mOnComplete = onComplete; 77 mImsRegistered = imsRegistered; 78 } 79 } 80 81 @VisibleForTesting 82 public static final String PREFERENCE_TBCW = "terminal_based_call_waiting"; 83 @VisibleForTesting 84 public static final String KEY_SUB_ID = "subId"; 85 @VisibleForTesting 86 public static final String KEY_STATE = "state"; 87 @VisibleForTesting 88 public static final String KEY_CS_SYNC = "cs_sync"; 89 90 private final CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener = 91 (slotIndex, subId, carrierId, specificCarrierId) -> onCarrierConfigurationChanged( 92 slotIndex); 93 94 private boolean mSupportedByImsService = false; 95 private boolean mValidSubscription = false; 96 97 // The user's last setting of terminal-based call waiting 98 private int mCallWaitingState = TERMINAL_BASED_NOT_SUPPORTED; 99 100 private int mSyncPreference = CALL_WAITING_SYNC_NONE; 101 private int mLastSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; 102 103 private boolean mCsEnabled = false; 104 private boolean mRegisteredForNetworkAttach = false; 105 private boolean mImsRegistered = false; 106 107 private final GsmCdmaPhone mPhone; 108 private final ServiceStateTracker mSST; 109 private final Context mContext; 110 111 // Constructors CallWaitingController(GsmCdmaPhone phone)112 public CallWaitingController(GsmCdmaPhone phone) { 113 mPhone = phone; 114 mSST = phone.getServiceStateTracker(); 115 mContext = phone.getContext(); 116 } 117 initialize()118 private void initialize() { 119 CarrierConfigManager ccm = mContext.getSystemService(CarrierConfigManager.class); 120 if (ccm != null) { 121 // Callback directly handle carrier config change should be executed in handler thread 122 ccm.registerCarrierConfigChangeListener(this::post, mCarrierConfigChangeListener); 123 } else { 124 loge("CarrierConfigLoader is not available."); 125 } 126 127 int phoneId = mPhone.getPhoneId(); 128 int subId = mPhone.getSubId(); 129 SharedPreferences sp = 130 mContext.getSharedPreferences(PREFERENCE_TBCW, Context.MODE_PRIVATE); 131 mLastSubId = sp.getInt(KEY_SUB_ID + phoneId, SubscriptionManager.INVALID_SUBSCRIPTION_ID); 132 mCallWaitingState = sp.getInt(KEY_STATE + subId, TERMINAL_BASED_NOT_SUPPORTED); 133 mSyncPreference = sp.getInt(KEY_CS_SYNC + phoneId, CALL_WAITING_SYNC_NONE); 134 135 logi("initialize phoneId=" + phoneId 136 + ", lastSubId=" + mLastSubId + ", subId=" + subId 137 + ", state=" + mCallWaitingState + ", sync=" + mSyncPreference 138 + ", csEnabled=" + mCsEnabled); 139 } 140 141 /** 142 * Returns the cached user setting. 143 * 144 * Possible values are 145 * {@link #TERMINAL_BASED_NOT_SUPPORTED}, 146 * {@link #TERMINAL_BASED_NOT_ACTIVATED}, and 147 * {@link #TERMINAL_BASED_ACTIVATED}. 148 * 149 * @param forCsOnly indicates the caller expects the result for CS calls only 150 */ 151 @VisibleForTesting getTerminalBasedCallWaitingState(boolean forCsOnly)152 public synchronized int getTerminalBasedCallWaitingState(boolean forCsOnly) { 153 if (forCsOnly && (!mImsRegistered) && mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) { 154 return TERMINAL_BASED_NOT_SUPPORTED; 155 } 156 if (!mValidSubscription) return TERMINAL_BASED_NOT_SUPPORTED; 157 return mCallWaitingState; 158 } 159 160 /** 161 * Serves the user's requests to interrogate the call waiting service 162 * 163 * @return true when terminal-based call waiting is supported, otherwise false 164 */ 165 @VisibleForTesting getCallWaiting(@ullable Message onComplete)166 public synchronized boolean getCallWaiting(@Nullable Message onComplete) { 167 if (mCallWaitingState == TERMINAL_BASED_NOT_SUPPORTED) return false; 168 169 logi("getCallWaiting " + mCallWaitingState); 170 171 if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) { 172 // Interrogate CW in CS network 173 if (!mCsEnabled) { 174 // skip interrogation if CS is not available and IMS is registered 175 if (isCircuitSwitchedNetworkAvailable() || !isImsRegistered()) { 176 Cw cw = new Cw(false, isImsRegistered(), onComplete); 177 Message resp = obtainMessage(EVENT_GET_CALL_WAITING_DONE, 0, 0, cw); 178 mPhone.mCi.queryCallWaiting(SERVICE_CLASS_NONE, resp); 179 return true; 180 } 181 } 182 } 183 184 if (mSyncPreference == CALL_WAITING_SYNC_NONE 185 || mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE 186 || mSyncPreference == CALL_WAITING_SYNC_FIRST_POWER_UP 187 || isSyncImsOnly()) { 188 sendGetCallWaitingResponse(onComplete); 189 return true; 190 } else if (mSyncPreference == CALL_WAITING_SYNC_USER_CHANGE 191 || mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) { 192 Cw cw = new Cw(false, isImsRegistered(), onComplete); 193 Message resp = obtainMessage(EVENT_GET_CALL_WAITING_DONE, 0, 0, cw); 194 mPhone.mCi.queryCallWaiting(SERVICE_CLASS_NONE, resp); 195 return true; 196 } 197 198 return false; 199 } 200 201 /** 202 * Serves the user's requests to set the call waiting service 203 * 204 * @param serviceClass the target service class. Values are CommandsInterface.SERVICE_CLASS_*. 205 * @return true when terminal-based call waiting is supported, otherwise false 206 */ 207 @VisibleForTesting setCallWaiting(boolean enable, int serviceClass, @Nullable Message onComplete)208 public synchronized boolean setCallWaiting(boolean enable, 209 int serviceClass, @Nullable Message onComplete) { 210 if (mCallWaitingState == TERMINAL_BASED_NOT_SUPPORTED) return false; 211 212 if ((serviceClass & SERVICE_CLASS_VOICE) != SERVICE_CLASS_VOICE) return false; 213 214 logi("setCallWaiting enable=" + enable + ", service=" + serviceClass); 215 216 if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) { 217 // Enable CW in the CS network 218 if (!mCsEnabled && enable) { 219 if (isCircuitSwitchedNetworkAvailable() || !isImsRegistered()) { 220 Cw cw = new Cw(true, isImsRegistered(), onComplete); 221 Message resp = obtainMessage(EVENT_SET_CALL_WAITING_DONE, 0, 0, cw); 222 mPhone.mCi.setCallWaiting(true, serviceClass, resp); 223 return true; 224 } else { 225 // CS network is not available, however, IMS is registered. 226 // Enabling the service in the CS network will be delayed. 227 registerForNetworkAttached(); 228 } 229 } 230 } 231 232 if (mSyncPreference == CALL_WAITING_SYNC_NONE 233 || mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE 234 || mSyncPreference == CALL_WAITING_SYNC_FIRST_POWER_UP 235 || isSyncImsOnly()) { 236 updateState( 237 enable ? TERMINAL_BASED_ACTIVATED : TERMINAL_BASED_NOT_ACTIVATED); 238 239 sendToTarget(onComplete, null, null); 240 return true; 241 } else if (mSyncPreference == CALL_WAITING_SYNC_USER_CHANGE 242 || mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) { 243 Cw cw = new Cw(enable, isImsRegistered(), onComplete); 244 Message resp = obtainMessage(EVENT_SET_CALL_WAITING_DONE, 0, 0, cw); 245 mPhone.mCi.setCallWaiting(enable, serviceClass, resp); 246 return true; 247 } 248 249 return false; 250 } 251 252 @Override handleMessage(Message msg)253 public void handleMessage(Message msg) { 254 switch (msg.what) { 255 case EVENT_SET_CALL_WAITING_DONE: 256 onSetCallWaitingDone((AsyncResult) msg.obj); 257 break; 258 case EVENT_GET_CALL_WAITING_DONE: 259 onGetCallWaitingDone((AsyncResult) msg.obj); 260 break; 261 case EVENT_REGISTERED_TO_NETWORK: 262 onRegisteredToNetwork(); 263 break; 264 default: 265 break; 266 } 267 } 268 onSetCallWaitingDone(AsyncResult ar)269 private synchronized void onSetCallWaitingDone(AsyncResult ar) { 270 if (ar.userObj == null) { 271 // For the case, CALL_WAITING_SYNC_FIRST_POWER_UP 272 if (DBG) logd("onSetCallWaitingDone to sync on network attached"); 273 if (ar.exception == null) { 274 updateSyncState(true); 275 } else { 276 loge("onSetCallWaitingDone e=" + ar.exception); 277 } 278 return; 279 } 280 281 if (!(ar.userObj instanceof Cw)) { 282 // Unexpected state 283 if (DBG) logd("onSetCallWaitingDone unexpected result"); 284 return; 285 } 286 287 if (DBG) logd("onSetCallWaitingDone"); 288 Cw cw = (Cw) ar.userObj; 289 290 if (mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) { 291 // do not synchronize service state between CS and IMS 292 sendToTarget(cw.mOnComplete, ar.result, ar.exception); 293 return; 294 } 295 296 if (ar.exception == null) { 297 if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) { 298 // SYNC_FIRST_CHANGE implies cw.mEnable is true. 299 updateSyncState(true); 300 } 301 updateState( 302 cw.mEnable ? TERMINAL_BASED_ACTIVATED : TERMINAL_BASED_NOT_ACTIVATED); 303 } else if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) { 304 if (cw.mImsRegistered) { 305 // IMS is registered. Do not notify error. 306 // SYNC_FIRST_CHANGE implies cw.mEnable is true. 307 updateState(TERMINAL_BASED_ACTIVATED); 308 sendToTarget(cw.mOnComplete, null, null); 309 return; 310 } 311 } 312 sendToTarget(cw.mOnComplete, ar.result, ar.exception); 313 } 314 onGetCallWaitingDone(AsyncResult ar)315 private synchronized void onGetCallWaitingDone(AsyncResult ar) { 316 if (ar.userObj == null) { 317 // For the case, CALL_WAITING_SYNC_FIRST_POWER_UP 318 if (DBG) logd("onGetCallWaitingDone to sync on network attached"); 319 boolean enabled = false; 320 if (ar.exception == null) { 321 //resp[0]: 1 if enabled, 0 otherwise 322 //resp[1]: bitwise ORs of SERVICE_CLASS_* constants 323 int[] resp = (int[]) ar.result; 324 if (resp != null && resp.length > 1) { 325 enabled = (resp[0] == 1) 326 && (resp[1] & SERVICE_CLASS_VOICE) == SERVICE_CLASS_VOICE; 327 } else { 328 loge("onGetCallWaitingDone unexpected response"); 329 } 330 } else { 331 loge("onGetCallWaitingDone e=" + ar.exception); 332 } 333 if (enabled) { 334 updateSyncState(true); 335 } else { 336 logi("onGetCallWaitingDone enabling CW service in CS network"); 337 mPhone.mCi.setCallWaiting(true, SERVICE_CLASS_VOICE, 338 obtainMessage(EVENT_SET_CALL_WAITING_DONE)); 339 } 340 unregisterForNetworkAttached(); 341 return; 342 } 343 344 if (!(ar.userObj instanceof Cw)) { 345 // Unexpected state 346 if (DBG) logd("onGetCallWaitingDone unexpected result"); 347 return; 348 } 349 350 if (DBG) logd("onGetCallWaitingDone"); 351 Cw cw = (Cw) ar.userObj; 352 353 if (mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY) { 354 // do not synchronize service state between CS and IMS 355 sendToTarget(cw.mOnComplete, ar.result, ar.exception); 356 return; 357 } 358 359 if (ar.exception == null) { 360 int[] resp = (int[]) ar.result; 361 //resp[0]: 1 if enabled, 0 otherwise 362 //resp[1]: bitwise ORs of SERVICE_CLASS_ 363 if (resp == null || resp.length < 2) { 364 logi("onGetCallWaitingDone unexpected response"); 365 if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) { 366 // no exception but unexpected response, local setting is preferred. 367 sendGetCallWaitingResponse(cw.mOnComplete); 368 } else { 369 sendToTarget(cw.mOnComplete, ar.result, ar.exception); 370 } 371 return; 372 } 373 374 boolean enabled = resp[0] == 1 375 && (resp[1] & SERVICE_CLASS_VOICE) == SERVICE_CLASS_VOICE; 376 377 if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) { 378 updateSyncState(enabled); 379 380 if (!enabled && !cw.mImsRegistered) { 381 // IMS is not registered, change the local setting 382 logi("onGetCallWaitingDone CW in CS network is disabled."); 383 updateState(TERMINAL_BASED_NOT_ACTIVATED); 384 } 385 386 // return the user setting saved 387 sendGetCallWaitingResponse(cw.mOnComplete); 388 return; 389 } 390 updateState(enabled ? TERMINAL_BASED_ACTIVATED : TERMINAL_BASED_NOT_ACTIVATED); 391 } else if (mSyncPreference == CALL_WAITING_SYNC_FIRST_CHANGE) { 392 // Got an exception 393 if (cw.mImsRegistered) { 394 // queryCallWaiting failed. However, IMS is registered. Do not notify error. 395 // return the user setting saved 396 logi("onGetCallWaitingDone get an exception, but IMS is registered"); 397 sendGetCallWaitingResponse(cw.mOnComplete); 398 return; 399 } 400 } 401 sendToTarget(cw.mOnComplete, ar.result, ar.exception); 402 } 403 sendToTarget(Message onComplete, Object result, Throwable exception)404 private void sendToTarget(Message onComplete, Object result, Throwable exception) { 405 if (onComplete != null) { 406 AsyncResult.forMessage(onComplete, result, exception); 407 onComplete.sendToTarget(); 408 } 409 } 410 sendGetCallWaitingResponse(Message onComplete)411 private void sendGetCallWaitingResponse(Message onComplete) { 412 if (onComplete != null) { 413 int serviceClass = SERVICE_CLASS_NONE; 414 if (mCallWaitingState == TERMINAL_BASED_ACTIVATED) { 415 serviceClass = SERVICE_CLASS_VOICE; 416 } 417 sendToTarget(onComplete, new int[] { mCallWaitingState, serviceClass }, null); 418 } 419 } 420 onRegisteredToNetwork()421 private synchronized void onRegisteredToNetwork() { 422 if (mCsEnabled) return; 423 424 if (DBG) logd("onRegisteredToNetwork"); 425 426 mPhone.mCi.queryCallWaiting(SERVICE_CLASS_NONE, 427 obtainMessage(EVENT_GET_CALL_WAITING_DONE)); 428 } 429 onCarrierConfigurationChanged(int slotIndex)430 private synchronized void onCarrierConfigurationChanged(int slotIndex) { 431 if (slotIndex != mPhone.getPhoneId()) return; 432 433 int subId = mPhone.getSubId(); 434 if (!SubscriptionManager.isValidSubscriptionId(subId)) { 435 logi("onCarrierConfigChanged invalid subId=" + subId); 436 437 mValidSubscription = false; 438 unregisterForNetworkAttached(); 439 return; 440 } 441 442 if (!updateCarrierConfig(subId, false /* ignoreSavedState */)) { 443 return; 444 } 445 446 logi("onCarrierConfigChanged cs_enabled=" + mCsEnabled); 447 448 if (mSyncPreference == CALL_WAITING_SYNC_FIRST_POWER_UP) { 449 if (!mCsEnabled) { 450 registerForNetworkAttached(); 451 } 452 } 453 } 454 455 /** 456 * @param ignoreSavedState only used for test 457 * @return true when succeeded. 458 */ 459 @VisibleForTesting updateCarrierConfig(int subId, boolean ignoreSavedState)460 public boolean updateCarrierConfig(int subId, boolean ignoreSavedState) { 461 mValidSubscription = true; 462 463 PersistableBundle b = 464 CarrierConfigManager.getCarrierConfigSubset( 465 mContext, 466 subId, 467 KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY, 468 KEY_TERMINAL_BASED_CALL_WAITING_SYNC_TYPE_INT, 469 KEY_TERMINAL_BASED_CALL_WAITING_DEFAULT_ENABLED_BOOL); 470 if (b.isEmpty()) return false; 471 472 boolean supportsTerminalBased = false; 473 int[] services = b.getIntArray(KEY_UT_TERMINAL_BASED_SERVICES_INT_ARRAY); 474 if (services != null) { 475 for (int service : services) { 476 if (service == SUPPLEMENTARY_SERVICE_CW) { 477 supportsTerminalBased = true; 478 } 479 } 480 } 481 int syncPreference = b.getInt(KEY_TERMINAL_BASED_CALL_WAITING_SYNC_TYPE_INT, 482 CALL_WAITING_SYNC_FIRST_CHANGE); 483 boolean activated = b.getBoolean(KEY_TERMINAL_BASED_CALL_WAITING_DEFAULT_ENABLED_BOOL); 484 int defaultState = supportsTerminalBased 485 ? (activated ? TERMINAL_BASED_ACTIVATED : TERMINAL_BASED_NOT_ACTIVATED) 486 : TERMINAL_BASED_NOT_SUPPORTED; 487 int savedState = getSavedState(subId); 488 489 if (DBG) { 490 logd("updateCarrierConfig phoneId=" + mPhone.getPhoneId() 491 + ", subId=" + subId + ", support=" + supportsTerminalBased 492 + ", sync=" + syncPreference + ", default=" + defaultState 493 + ", savedState=" + savedState); 494 } 495 496 int desiredState = savedState; 497 498 if (ignoreSavedState) { 499 desiredState = defaultState; 500 } else if ((mLastSubId != subId) 501 && (syncPreference == CALL_WAITING_SYNC_FIRST_POWER_UP 502 || syncPreference == CALL_WAITING_SYNC_FIRST_CHANGE)) { 503 desiredState = defaultState; 504 } else { 505 if (defaultState == TERMINAL_BASED_NOT_SUPPORTED) { 506 desiredState = TERMINAL_BASED_NOT_SUPPORTED; 507 } else if (savedState == TERMINAL_BASED_NOT_SUPPORTED) { 508 desiredState = defaultState; 509 } 510 } 511 512 updateState(desiredState, syncPreference, ignoreSavedState); 513 return true; 514 } 515 updateState(int state)516 private void updateState(int state) { 517 updateState(state, mSyncPreference, false); 518 } 519 updateState(int state, int syncPreference, boolean ignoreSavedState)520 private void updateState(int state, int syncPreference, boolean ignoreSavedState) { 521 int subId = mPhone.getSubId(); 522 523 if (mLastSubId == subId 524 && mCallWaitingState == state 525 && mSyncPreference == syncPreference 526 && (!ignoreSavedState)) { 527 return; 528 } 529 530 int phoneId = mPhone.getPhoneId(); 531 532 logi("updateState phoneId=" + phoneId 533 + ", subId=" + subId + ", state=" + state 534 + ", sync=" + syncPreference + ", ignoreSavedState=" + ignoreSavedState); 535 536 SharedPreferences sp = 537 mContext.getSharedPreferences(PREFERENCE_TBCW, Context.MODE_PRIVATE); 538 539 SharedPreferences.Editor editor = sp.edit(); 540 editor.putInt(KEY_SUB_ID + phoneId, subId); 541 editor.putInt(KEY_STATE + subId, state); 542 editor.putInt(KEY_CS_SYNC + phoneId, syncPreference); 543 editor.apply(); 544 545 mCallWaitingState = state; 546 mLastSubId = subId; 547 mSyncPreference = syncPreference; 548 if (mLastSubId != subId) { 549 mCsEnabled = false; 550 } 551 552 mPhone.setTerminalBasedCallWaitingStatus(mCallWaitingState); 553 } 554 getSavedState(int subId)555 private int getSavedState(int subId) { 556 SharedPreferences sp = 557 mContext.getSharedPreferences(PREFERENCE_TBCW, Context.MODE_PRIVATE); 558 int state = sp.getInt(KEY_STATE + subId, TERMINAL_BASED_NOT_SUPPORTED); 559 560 logi("getSavedState subId=" + subId + ", state=" + state); 561 562 return state; 563 } 564 updateSyncState(boolean enabled)565 private void updateSyncState(boolean enabled) { 566 int phoneId = mPhone.getPhoneId(); 567 568 logi("updateSyncState phoneId=" + phoneId + ", enabled=" + enabled); 569 570 mCsEnabled = enabled; 571 } 572 573 /** 574 * @return whether the service is enabled in the CS network 575 */ 576 @VisibleForTesting getSyncState()577 public boolean getSyncState() { 578 return mCsEnabled; 579 } 580 isCircuitSwitchedNetworkAvailable()581 private boolean isCircuitSwitchedNetworkAvailable() { 582 logi("isCircuitSwitchedNetworkAvailable=" 583 + (mSST.getServiceState().getState() == ServiceState.STATE_IN_SERVICE)); 584 return mSST.getServiceState().getState() == ServiceState.STATE_IN_SERVICE; 585 } 586 isImsRegistered()587 private boolean isImsRegistered() { 588 logi("isImsRegistered " + mImsRegistered); 589 return mImsRegistered; 590 } 591 592 /** 593 * Sets the registration state of IMS service. 594 */ setImsRegistrationState(boolean registered)595 public synchronized void setImsRegistrationState(boolean registered) { 596 logi("setImsRegistrationState prev=" + mImsRegistered 597 + ", new=" + registered); 598 mImsRegistered = registered; 599 } 600 registerForNetworkAttached()601 private void registerForNetworkAttached() { 602 logi("registerForNetworkAttached"); 603 if (mRegisteredForNetworkAttach) return; 604 605 mSST.registerForNetworkAttached(this, EVENT_REGISTERED_TO_NETWORK, null); 606 mRegisteredForNetworkAttach = true; 607 } 608 unregisterForNetworkAttached()609 private void unregisterForNetworkAttached() { 610 logi("unregisterForNetworkAttached"); 611 if (!mRegisteredForNetworkAttach) return; 612 613 mSST.unregisterForNetworkAttached(this); 614 removeMessages(EVENT_REGISTERED_TO_NETWORK); 615 mRegisteredForNetworkAttach = false; 616 } 617 618 /** 619 * Sets whether the device supports the terminal-based call waiting. 620 * Only for test 621 */ 622 @VisibleForTesting setTerminalBasedCallWaitingSupported(boolean supported)623 public synchronized void setTerminalBasedCallWaitingSupported(boolean supported) { 624 if (mSupportedByImsService == supported) return; 625 626 logi("setTerminalBasedCallWaitingSupported " + supported); 627 628 mSupportedByImsService = supported; 629 630 if (supported) { 631 initialize(); 632 onCarrierConfigurationChanged(mPhone.getPhoneId()); 633 } else { 634 CarrierConfigManager ccm = mContext.getSystemService(CarrierConfigManager.class); 635 if (ccm != null && mCarrierConfigChangeListener != null) { 636 ccm.unregisterCarrierConfigChangeListener(mCarrierConfigChangeListener); 637 } 638 updateState(TERMINAL_BASED_NOT_SUPPORTED); 639 } 640 } 641 642 /** 643 * Notifies that the UE has attached to the network 644 * Only for test 645 */ 646 @VisibleForTesting notifyRegisteredToNetwork()647 public void notifyRegisteredToNetwork() { 648 sendEmptyMessage(EVENT_REGISTERED_TO_NETWORK); 649 } 650 isSyncImsOnly()651 private boolean isSyncImsOnly() { 652 return (mSyncPreference == CALL_WAITING_SYNC_IMS_ONLY && mImsRegistered); 653 } 654 655 /** 656 * Dump this instance into a readable format for dumpsys usage. 657 */ dump(PrintWriter printWriter)658 public void dump(PrintWriter printWriter) { 659 IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, " "); 660 pw.increaseIndent(); 661 pw.println("CallWaitingController:"); 662 pw.println(" mSupportedByImsService=" + mSupportedByImsService); 663 pw.println(" mValidSubscription=" + mValidSubscription); 664 pw.println(" mCallWaitingState=" + mCallWaitingState); 665 pw.println(" mSyncPreference=" + mSyncPreference); 666 pw.println(" mLastSubId=" + mLastSubId); 667 pw.println(" mCsEnabled=" + mCsEnabled); 668 pw.println(" mRegisteredForNetworkAttach=" + mRegisteredForNetworkAttach); 669 pw.println(" mImsRegistered=" + mImsRegistered); 670 pw.decreaseIndent(); 671 } 672 loge(String msg)673 private void loge(String msg) { 674 Rlog.e(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg); 675 } 676 logi(String msg)677 private void logi(String msg) { 678 Rlog.i(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg); 679 } 680 logd(String msg)681 private void logd(String msg) { 682 Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg); 683 } 684 } 685