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.emergency; 18 19 import static android.telecom.Connection.STATE_ACTIVE; 20 import static android.telecom.Connection.STATE_DISCONNECTED; 21 import static android.telephony.CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL; 22 import static android.telephony.CarrierConfigManager.KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL; 23 import static android.telephony.TelephonyManager.EMERGENCY_CALLBACK_MODE_CALL; 24 import static android.telephony.TelephonyManager.EMERGENCY_CALLBACK_MODE_SMS; 25 import static android.telephony.TelephonyManager.STOP_REASON_EMERGENCY_SMS_SENT; 26 import static android.telephony.TelephonyManager.STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED; 27 import static android.telephony.TelephonyManager.STOP_REASON_TIMER_EXPIRED; 28 import static android.telephony.TelephonyManager.STOP_REASON_UNKNOWN; 29 30 import static com.android.internal.telephony.TelephonyIntents.ACTION_EMERGENCY_CALL_STATE_CHANGED; 31 import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_CALLBACK; 32 import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_NONE; 33 import static com.android.internal.telephony.emergency.EmergencyConstants.MODE_EMERGENCY_WWAN; 34 35 import android.annotation.IntDef; 36 import android.annotation.NonNull; 37 import android.content.BroadcastReceiver; 38 import android.content.Context; 39 import android.content.Intent; 40 import android.content.IntentFilter; 41 import android.content.SharedPreferences; 42 import android.os.AsyncResult; 43 import android.os.Handler; 44 import android.os.Looper; 45 import android.os.Message; 46 import android.os.PersistableBundle; 47 import android.os.PowerManager; 48 import android.os.SystemClock; 49 import android.os.UserHandle; 50 import android.preference.PreferenceManager; 51 import android.provider.Settings; 52 import android.sysprop.TelephonyProperties; 53 import android.telephony.AccessNetworkConstants; 54 import android.telephony.Annotation.DisconnectCauses; 55 import android.telephony.CarrierConfigManager; 56 import android.telephony.DisconnectCause; 57 import android.telephony.EmergencyRegistrationResult; 58 import android.telephony.NetworkRegistrationInfo; 59 import android.telephony.ServiceState; 60 import android.telephony.SubscriptionManager; 61 import android.telephony.TelephonyManager; 62 import android.util.ArraySet; 63 64 import com.android.internal.annotations.VisibleForTesting; 65 import com.android.internal.telephony.Call; 66 import com.android.internal.telephony.CallStateException; 67 import com.android.internal.telephony.Connection; 68 import com.android.internal.telephony.GsmCdmaPhone; 69 import com.android.internal.telephony.Phone; 70 import com.android.internal.telephony.PhoneConstants; 71 import com.android.internal.telephony.PhoneFactory; 72 import com.android.internal.telephony.TelephonyIntents; 73 import com.android.internal.telephony.data.PhoneSwitcher; 74 import com.android.internal.telephony.flags.FeatureFlags; 75 import com.android.internal.telephony.imsphone.ImsPhoneConnection; 76 import com.android.internal.telephony.satellite.SatelliteController; 77 import com.android.internal.telephony.subscription.SubscriptionInfoInternal; 78 import com.android.internal.telephony.subscription.SubscriptionManagerService; 79 import com.android.telephony.Rlog; 80 81 import java.lang.annotation.Retention; 82 import java.lang.annotation.RetentionPolicy; 83 import java.util.Arrays; 84 import java.util.Objects; 85 import java.util.Set; 86 import java.util.concurrent.CompletableFuture; 87 import java.util.function.Consumer; 88 89 /** 90 * Tracks the emergency call state and notifies listeners of changes to the emergency mode. 91 */ 92 public class EmergencyStateTracker { 93 94 private static final String TAG = "EmergencyStateTracker"; 95 96 private static class OnDisconnectListener extends Connection.ListenerBase { 97 private final CompletableFuture<Boolean> mFuture; 98 OnDisconnectListener(CompletableFuture<Boolean> future)99 OnDisconnectListener(CompletableFuture<Boolean> future) { 100 mFuture = future; 101 } 102 103 @Override onDisconnect(int cause)104 public void onDisconnect(int cause) { 105 mFuture.complete(true); 106 } 107 }; 108 109 /** 110 * Timeout before we continue with the emergency call without waiting for DDS switch response 111 * from the modem. 112 */ 113 private static final int DEFAULT_DATA_SWITCH_TIMEOUT_MS = 1 * 1000; 114 /** Default value for if Emergency Callback Mode is supported. */ 115 private static final boolean DEFAULT_EMERGENCY_CALLBACK_MODE_SUPPORTED = true; 116 /** Default Emergency Callback Mode exit timeout value. */ 117 private static final long DEFAULT_ECM_EXIT_TIMEOUT_MS = 300000; 118 119 private static final int DEFAULT_TRANSPORT_CHANGE_TIMEOUT_MS = 1 * 1000; 120 121 // Timeout to wait for the termination of incoming call before continue with the emergency call. 122 private static final int DEFAULT_REJECT_INCOMING_CALL_TIMEOUT_MS = 3 * 1000; // 3 seconds. 123 124 /** The emergency types used when setting the emergency mode on modem. */ 125 @Retention(RetentionPolicy.SOURCE) 126 @IntDef(prefix = "EMERGENCY_TYPE_", 127 value = { 128 EMERGENCY_TYPE_CALL, 129 EMERGENCY_TYPE_SMS}) 130 public @interface EmergencyType {} 131 132 /** Indicates the emergency type is call. */ 133 public static final int EMERGENCY_TYPE_CALL = 1; 134 /** Indicates the emergency type is SMS. */ 135 public static final int EMERGENCY_TYPE_SMS = 2; 136 137 private static final String KEY_NO_SIM_ECBM_SUPPORT = "no_sim_ecbm_support"; 138 139 private static EmergencyStateTracker INSTANCE = null; 140 141 private final Context mContext; 142 private final CarrierConfigManager mConfigManager; 143 private final Handler mHandler; 144 private final boolean mIsSuplDdsSwitchRequiredForEmergencyCall; 145 private final int mWaitForInServiceTimeoutMs; 146 private final boolean mTurnOffOemEnabledSatelliteDuringEmergencyCall; 147 private final boolean mTurnOffNonEmergencyNbIotNtnSatelliteForEmergencyCall; 148 private final PowerManager.WakeLock mWakeLock; 149 private RadioOnHelper mRadioOnHelper; 150 @EmergencyConstants.EmergencyMode 151 private int mEmergencyMode = MODE_EMERGENCY_NONE; 152 private boolean mWasEmergencyModeSetOnModem; 153 private EmergencyRegistrationResult mLastEmergencyRegistrationResult; 154 private boolean mIsEmergencyModeInProgress; 155 private boolean mIsEmergencyCallStartedDuringEmergencySms; 156 private boolean mIsWaitingForRadioOff; 157 158 /** For emergency calls */ 159 private final long mEcmExitTimeoutMs; 160 // A runnable which is used to automatically exit from Ecm after a period of time. 161 private final Runnable mExitEcmRunnable = () -> exitEmergencyCallbackMode( 162 STOP_REASON_TIMER_EXPIRED); 163 // Tracks emergency calls by callId that have reached {@link Call.State#ACTIVE}. 164 private final Set<android.telecom.Connection> mActiveEmergencyCalls = new ArraySet<>(); 165 private Phone mPhone; 166 // Tracks ongoing emergency connection to handle a second emergency call 167 private android.telecom.Connection mOngoingConnection; 168 // Domain of the active emergency call. Assuming here that there will only be one domain active. 169 private int mEmergencyCallDomain = NetworkRegistrationInfo.DOMAIN_UNKNOWN; 170 // Phone type of the active emergency call. Assuming that there will only be one phone active. 171 private int mEmergencyCallPhoneType = PhoneConstants.PHONE_TYPE_NONE; 172 private CompletableFuture<Integer> mCallEmergencyModeFuture; 173 private boolean mIsInEmergencyCall; 174 private boolean mIsInEcm; 175 private boolean mIsTestEmergencyNumber; 176 private Runnable mOnEcmExitCompleteRunnable; 177 private int mOngoingCallProperties; 178 private boolean mSentEmergencyCallState; 179 private android.telecom.Connection mNormalRoutingEmergencyConnection; 180 181 /** For emergency SMS */ 182 private final Set<String> mOngoingEmergencySmsIds = new ArraySet<>(); 183 private Phone mSmsPhone; 184 private CompletableFuture<Integer> mSmsEmergencyModeFuture; 185 private boolean mIsTestEmergencyNumberForSms; 186 // For tracking the emergency SMS callback mode. 187 private boolean mIsInScbm; 188 private boolean mIsEmergencySmsStartedDuringScbm; 189 190 private CompletableFuture<Boolean> mEmergencyTransportChangedFuture; 191 private final Object mRegistrantidentifier = new Object(); 192 193 private final android.util.ArrayMap<Integer, Boolean> mNoSimEcbmSupported = 194 new android.util.ArrayMap<>(); 195 private final android.util.ArrayMap<Integer, Boolean> mBroadcastEmergencyCallStateChanges = 196 new android.util.ArrayMap<>(); 197 private final CarrierConfigManager.CarrierConfigChangeListener mCarrierConfigChangeListener = 198 (slotIndex, subId, carrierId, specificCarrierId) -> onCarrierConfigurationChanged( 199 slotIndex, subId); 200 /** Feature flags */ 201 private final FeatureFlags mFeatureFlags; 202 203 /** 204 * Listens for Emergency Callback Mode state change intents 205 */ 206 private final BroadcastReceiver mEcmExitReceiver = new BroadcastReceiver() { 207 @Override 208 public void onReceive(Context context, Intent intent) { 209 if (intent.getAction().equals( 210 TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED)) { 211 212 boolean isInEcm = intent.getBooleanExtra( 213 TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, false); 214 Rlog.d(TAG, "Received ACTION_EMERGENCY_CALLBACK_MODE_CHANGED isInEcm = " + isInEcm); 215 216 // If we exit ECM mode, notify all connections. 217 if (!isInEcm) { 218 exitEmergencyCallbackMode(); 219 } 220 } 221 } 222 }; 223 224 /** PhoneFactory Dependencies for testing. */ 225 @VisibleForTesting 226 public interface PhoneFactoryProxy { getPhones()227 Phone[] getPhones(); 228 } 229 230 private PhoneFactoryProxy mPhoneFactoryProxy = PhoneFactory::getPhones; 231 232 /** PhoneSwitcher dependencies for testing. */ 233 @VisibleForTesting 234 public interface PhoneSwitcherProxy { 235 getPhoneSwitcher()236 PhoneSwitcher getPhoneSwitcher(); 237 } 238 239 private PhoneSwitcherProxy mPhoneSwitcherProxy = PhoneSwitcher::getInstance; 240 241 /** 242 * TelephonyManager dependencies for testing. 243 */ 244 @VisibleForTesting 245 public interface TelephonyManagerProxy { getPhoneCount()246 int getPhoneCount(); getSimState(int slotIndex)247 int getSimState(int slotIndex); 248 } 249 250 private final TelephonyManagerProxy mTelephonyManagerProxy; 251 252 private static class TelephonyManagerProxyImpl implements TelephonyManagerProxy { 253 private final TelephonyManager mTelephonyManager; 254 TelephonyManagerProxyImpl(Context context)255 TelephonyManagerProxyImpl(Context context) { 256 mTelephonyManager = new TelephonyManager(context); 257 } 258 259 @Override getPhoneCount()260 public int getPhoneCount() { 261 return mTelephonyManager.getActiveModemCount(); 262 } 263 264 @Override getSimState(int slotIndex)265 public int getSimState(int slotIndex) { 266 return mTelephonyManager.getSimState(slotIndex); 267 } 268 } 269 270 /** 271 * Return the handler for testing. 272 */ 273 @VisibleForTesting getHandler()274 public Handler getHandler() { 275 return mHandler; 276 } 277 278 @VisibleForTesting 279 public static final int MSG_SET_EMERGENCY_MODE_DONE = 1; 280 @VisibleForTesting 281 public static final int MSG_EXIT_EMERGENCY_MODE_DONE = 2; 282 @VisibleForTesting 283 public static final int MSG_SET_EMERGENCY_CALLBACK_MODE_DONE = 3; 284 /** A message which is used to automatically exit from SCBM after a period of time. */ 285 private static final int MSG_EXIT_SCBM = 4; 286 @VisibleForTesting 287 public static final int MSG_NEW_RINGING_CONNECTION = 5; 288 @VisibleForTesting 289 public static final int MSG_VOICE_REG_STATE_CHANGED = 6; 290 291 private class MyHandler extends Handler { 292 MyHandler(Looper looper)293 MyHandler(Looper looper) { 294 super(looper); 295 } 296 297 @Override handleMessage(Message msg)298 public void handleMessage(Message msg) { 299 switch (msg.what) { 300 case MSG_SET_EMERGENCY_MODE_DONE: { 301 AsyncResult ar = (AsyncResult) msg.obj; 302 Integer emergencyType = (Integer) ar.userObj; 303 Rlog.v(TAG, "MSG_SET_EMERGENCY_MODE_DONE for " 304 + emergencyTypeToString(emergencyType)); 305 if (ar.exception == null) { 306 mLastEmergencyRegistrationResult = (EmergencyRegistrationResult) ar.result; 307 } else { 308 mLastEmergencyRegistrationResult = null; 309 Rlog.w(TAG, 310 "LastEmergencyRegistrationResult not set. AsyncResult.exception: " 311 + ar.exception); 312 } 313 setEmergencyModeInProgress(false); 314 315 // Transport changed from WLAN to WWAN or CALLBACK to WWAN 316 maybeNotifyTransportChangeCompleted(emergencyType, false); 317 318 if (emergencyType == EMERGENCY_TYPE_CALL) { 319 // If the emergency registration result(mLastEmergencyRegistrationResult) is 320 // null, it means that the emergency mode is not set properly on the modem. 321 // Therefore, based on the emergency registration result and current 322 // subscription status, the current phone is not available for an emergency 323 // call, so we check if an emergency call is possible through cross stack. 324 if (mFeatureFlags.performCrossStackRedialCheckForEmergencyCall() 325 && mLastEmergencyRegistrationResult == null 326 && mPhone != null 327 && !SubscriptionManager.isValidSubscriptionId(mPhone.getSubId()) 328 && needToSwitchPhone(mPhone)) { 329 Rlog.i(TAG, "setEmergencyMode failed: need to switch stacks."); 330 mEmergencyMode = MODE_EMERGENCY_NONE; 331 completeEmergencyMode(emergencyType, 332 DisconnectCause.EMERGENCY_PERM_FAILURE); 333 } else { 334 setIsInEmergencyCall(true); 335 completeEmergencyMode(emergencyType); 336 } 337 338 // Case 1) When the emergency call is setting the emergency mode and 339 // the emergency SMS is being sent, completes the SMS future also. 340 // Case 2) When the emergency SMS is setting the emergency mode and 341 // the emergency call is being started, the SMS request is cancelled and 342 // the call request will be handled. 343 if (mSmsPhone != null) { 344 completeEmergencyMode(EMERGENCY_TYPE_SMS); 345 } 346 } else if (emergencyType == EMERGENCY_TYPE_SMS) { 347 if (mPhone != null && mSmsPhone != null) { 348 if (mIsEmergencyCallStartedDuringEmergencySms) { 349 if (!isSamePhone(mPhone, mSmsPhone) || !isInScbm()) { 350 // Clear call phone temporarily to exit the emergency mode 351 // if the emergency call is started. 352 Phone phone = mPhone; 353 mPhone = null; 354 exitEmergencyMode(mSmsPhone, emergencyType); 355 // Restore call phone for further use. 356 mPhone = phone; 357 if (!isSamePhone(mPhone, mSmsPhone)) { 358 completeEmergencyMode(emergencyType, 359 DisconnectCause.OUTGOING_EMERGENCY_CALL_PLACED); 360 exitEmergencySmsCallbackMode( 361 STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED); 362 } 363 } else { 364 completeEmergencyMode(emergencyType); 365 mIsEmergencyCallStartedDuringEmergencySms = false; 366 exitEmergencySmsCallbackMode( 367 STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED); 368 turnOnRadioAndSwitchDds(mPhone, EMERGENCY_TYPE_CALL, 369 mIsTestEmergencyNumber); 370 } 371 } else { 372 completeEmergencyMode(emergencyType); 373 } 374 } else { 375 completeEmergencyMode(emergencyType); 376 377 if (mIsEmergencyCallStartedDuringEmergencySms) { 378 mIsEmergencyCallStartedDuringEmergencySms = false; 379 exitEmergencySmsCallbackMode( 380 STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED); 381 turnOnRadioAndSwitchDds(mPhone, EMERGENCY_TYPE_CALL, 382 mIsTestEmergencyNumber); 383 } 384 } 385 } 386 break; 387 } 388 case MSG_EXIT_EMERGENCY_MODE_DONE: { 389 AsyncResult ar = (AsyncResult) msg.obj; 390 Integer emergencyType = (Integer) ar.userObj; 391 Rlog.v(TAG, "MSG_EXIT_EMERGENCY_MODE_DONE for " 392 + emergencyTypeToString(emergencyType)); 393 setEmergencyModeInProgress(false); 394 395 if (emergencyType == EMERGENCY_TYPE_CALL) { 396 setIsInEmergencyCall(false); 397 if (mOnEcmExitCompleteRunnable != null) { 398 mOnEcmExitCompleteRunnable.run(); 399 mOnEcmExitCompleteRunnable = null; 400 } 401 if (mPhone != null && mEmergencyMode == MODE_EMERGENCY_WWAN) { 402 // In cross sim redialing. 403 setEmergencyModeInProgress(true); 404 mWasEmergencyModeSetOnModem = true; 405 mPhone.setEmergencyMode(MODE_EMERGENCY_WWAN, 406 mHandler.obtainMessage(MSG_SET_EMERGENCY_MODE_DONE, 407 Integer.valueOf(EMERGENCY_TYPE_CALL))); 408 } 409 } else if (emergencyType == EMERGENCY_TYPE_SMS) { 410 if (mIsEmergencyCallStartedDuringEmergencySms) { 411 mIsEmergencyCallStartedDuringEmergencySms = false; 412 turnOnRadioAndSwitchDds(mPhone, EMERGENCY_TYPE_CALL, 413 mIsTestEmergencyNumber); 414 } else if (mPhone != null && mEmergencyMode == MODE_EMERGENCY_WWAN) { 415 // Starting emergency call while exiting emergency mode 416 setEmergencyModeInProgress(true); 417 mWasEmergencyModeSetOnModem = true; 418 mPhone.setEmergencyMode(MODE_EMERGENCY_WWAN, 419 mHandler.obtainMessage(MSG_SET_EMERGENCY_MODE_DONE, 420 Integer.valueOf(EMERGENCY_TYPE_CALL))); 421 } else if (mIsEmergencySmsStartedDuringScbm) { 422 mIsEmergencySmsStartedDuringScbm = false; 423 setEmergencyMode(mSmsPhone, emergencyType, 424 MODE_EMERGENCY_WWAN, MSG_SET_EMERGENCY_MODE_DONE); 425 } 426 } 427 break; 428 } 429 case MSG_SET_EMERGENCY_CALLBACK_MODE_DONE: { 430 AsyncResult ar = (AsyncResult) msg.obj; 431 Integer emergencyType = (Integer) ar.userObj; 432 Rlog.v(TAG, "MSG_SET_EMERGENCY_CALLBACK_MODE_DONE for " 433 + emergencyTypeToString(emergencyType)); 434 setEmergencyModeInProgress(false); 435 // When the emergency callback mode is in progress and the emergency SMS is 436 // started, it needs to be completed here for the emergency SMS. 437 if (emergencyType == EMERGENCY_TYPE_CALL) { 438 if (mSmsPhone != null) { 439 completeEmergencyMode(EMERGENCY_TYPE_SMS); 440 } 441 } else if (emergencyType == EMERGENCY_TYPE_SMS) { 442 // When the emergency SMS callback mode is in progress on other phone and 443 // the emergency call was started, needs to exit the emergency mode first. 444 if (mIsEmergencyCallStartedDuringEmergencySms) { 445 final Phone smsPhone = mSmsPhone; 446 exitEmergencySmsCallbackMode( 447 STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED); 448 449 if (mPhone != null && smsPhone != null 450 && !isSamePhone(mPhone, smsPhone)) { 451 Phone phone = mPhone; 452 mPhone = null; 453 exitEmergencyMode(smsPhone, emergencyType); 454 // Restore call phone for further use. 455 mPhone = phone; 456 } else { 457 mIsEmergencyCallStartedDuringEmergencySms = false; 458 turnOnRadioAndSwitchDds(mPhone, EMERGENCY_TYPE_CALL, 459 mIsTestEmergencyNumber); 460 } 461 } 462 } 463 break; 464 } 465 case MSG_EXIT_SCBM: { 466 exitEmergencySmsCallbackModeAndEmergencyMode(STOP_REASON_TIMER_EXPIRED); 467 break; 468 } 469 case MSG_NEW_RINGING_CONNECTION: { 470 handleNewRingingConnection(msg); 471 break; 472 } 473 case MSG_VOICE_REG_STATE_CHANGED: { 474 if (mIsWaitingForRadioOff && isPowerOff()) { 475 unregisterForVoiceRegStateOrRatChanged(); 476 if (mPhone != null) { 477 turnOnRadioAndSwitchDds(mPhone, EMERGENCY_TYPE_CALL, 478 mIsTestEmergencyNumber); 479 } 480 } 481 break; 482 } 483 default: 484 break; 485 } 486 } 487 } 488 489 /** 490 * Creates the EmergencyStateTracker singleton instance. 491 * 492 * @param context The context of the application. 493 * @param isSuplDdsSwitchRequiredForEmergencyCall Whether gnss supl requires default data for 494 * emergency call. 495 * @param turnOffOemEnabledSatelliteDuringEmergencyCall Specifying whether OEM enabled satellite 496 * should be turned off during emergency 497 * call. 498 * @param turnOffNonEmergencyNbIotNtnSatelliteForEmergencyCall Specifying whether non-emergency 499 * NB-IOT NTN satellite should be 500 * turned off for emergency call. 501 * @param featureFlags The telephony feature flags. 502 */ make(Context context, boolean isSuplDdsSwitchRequiredForEmergencyCall, int waitForInServiceTimeout, boolean turnOffOemEnabledSatelliteDuringEmergencyCall, boolean turnOffNonEmergencyNbIotNtnSatelliteForEmergencyCall, @NonNull FeatureFlags featureFlags)503 public static void make(Context context, boolean isSuplDdsSwitchRequiredForEmergencyCall, 504 int waitForInServiceTimeout, boolean turnOffOemEnabledSatelliteDuringEmergencyCall, 505 boolean turnOffNonEmergencyNbIotNtnSatelliteForEmergencyCall, 506 @NonNull FeatureFlags featureFlags) { 507 if (INSTANCE == null) { 508 INSTANCE = new EmergencyStateTracker(context, Looper.myLooper(), 509 isSuplDdsSwitchRequiredForEmergencyCall, waitForInServiceTimeout, 510 turnOffOemEnabledSatelliteDuringEmergencyCall, 511 turnOffNonEmergencyNbIotNtnSatelliteForEmergencyCall, 512 featureFlags); 513 } 514 } 515 516 /** 517 * Returns the singleton instance of EmergencyStateTracker. 518 * 519 * @return {@link EmergencyStateTracker} instance. 520 */ getInstance()521 public static EmergencyStateTracker getInstance() { 522 if (INSTANCE == null) { 523 throw new IllegalStateException("EmergencyStateTracker is not ready!"); 524 } 525 return INSTANCE; 526 } 527 528 /** 529 * Initializes EmergencyStateTracker. 530 */ EmergencyStateTracker(Context context, Looper looper, boolean isSuplDdsSwitchRequiredForEmergencyCall, int waitForInServiceTimeout, boolean turnOffOemEnabledSatelliteDuringEmergencyCall, boolean turnOffNonEmergencyNbIotNtnSatelliteForEmergencyCall, @NonNull FeatureFlags featureFlags)531 private EmergencyStateTracker(Context context, Looper looper, 532 boolean isSuplDdsSwitchRequiredForEmergencyCall, int waitForInServiceTimeout, 533 boolean turnOffOemEnabledSatelliteDuringEmergencyCall, 534 boolean turnOffNonEmergencyNbIotNtnSatelliteForEmergencyCall, 535 @NonNull FeatureFlags featureFlags) { 536 mEcmExitTimeoutMs = DEFAULT_ECM_EXIT_TIMEOUT_MS; 537 mContext = context; 538 mHandler = new MyHandler(looper); 539 mIsSuplDdsSwitchRequiredForEmergencyCall = isSuplDdsSwitchRequiredForEmergencyCall; 540 mWaitForInServiceTimeoutMs = waitForInServiceTimeout; 541 mTurnOffOemEnabledSatelliteDuringEmergencyCall = 542 turnOffOemEnabledSatelliteDuringEmergencyCall; 543 mTurnOffNonEmergencyNbIotNtnSatelliteForEmergencyCall = 544 turnOffNonEmergencyNbIotNtnSatelliteForEmergencyCall; 545 mFeatureFlags = featureFlags; 546 PowerManager pm = context.getSystemService(PowerManager.class); 547 mWakeLock = (pm != null) ? pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 548 "telephony:" + TAG) : null; 549 mConfigManager = context.getSystemService(CarrierConfigManager.class); 550 if (mConfigManager != null) { 551 // Carrier config changed callback should be executed in handler thread 552 mConfigManager.registerCarrierConfigChangeListener(mHandler::post, 553 mCarrierConfigChangeListener); 554 } else { 555 Rlog.e(TAG, "CarrierConfigLoader is not available."); 556 } 557 558 // Register receiver for ECM exit. 559 IntentFilter filter = new IntentFilter(); 560 filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); 561 context.registerReceiver(mEcmExitReceiver, filter, null, mHandler); 562 mTelephonyManagerProxy = new TelephonyManagerProxyImpl(context); 563 564 registerForNewRingingConnection(); 565 566 // To recover the abnormal state after crash of com.android.phone process 567 maybeResetEmergencyCallStateChangedIntent(); 568 } 569 570 /** 571 * Initializes EmergencyStateTracker with injections for testing. 572 * 573 * @param context The context of the application. 574 * @param looper The {@link Looper} of the application. 575 * @param isSuplDdsSwitchRequiredForEmergencyCall Whether gnss supl requires default data for 576 * emergency call. 577 * @param waitForInServiceTimeout The timeout duration how long does it wait for 578 * modem to get in-service state when emergency 579 * call is dialed in airplane mode before 580 * starting the emergency call. 581 * @param turnOffOemEnabledSatelliteDuringEmergencyCall Specifying whether OEM enabled satellite 582 * should be turned off during emergency 583 * call. 584 * @param turnOffNonEmergencyNbIotNtnSatelliteForEmergencyCall Specifying whether non-emergency 585 * NB-IOT NTN satellite should be 586 * turned off for emergency call. 587 * @param phoneFactoryProxy The {@link PhoneFactoryProxy} to be injected. 588 * @param phoneSwitcherProxy The {@link PhoneSwitcherProxy} to be injected. 589 * @param telephonyManagerProxy The {@link TelephonyManagerProxy} to be 590 * injected. 591 * @param radioOnHelper The {@link RadioOnHelper} to be injected. 592 * @param featureFlags The {@link FeatureFlags} to be injected. 593 */ 594 @VisibleForTesting EmergencyStateTracker(Context context, Looper looper, boolean isSuplDdsSwitchRequiredForEmergencyCall, int waitForInServiceTimeout, boolean turnOffOemEnabledSatelliteDuringEmergencyCall, boolean turnOffNonEmergencyNbIotNtnSatelliteForEmergencyCall, PhoneFactoryProxy phoneFactoryProxy, PhoneSwitcherProxy phoneSwitcherProxy, TelephonyManagerProxy telephonyManagerProxy, RadioOnHelper radioOnHelper, long ecmExitTimeoutMs, FeatureFlags featureFlags)595 public EmergencyStateTracker(Context context, Looper looper, 596 boolean isSuplDdsSwitchRequiredForEmergencyCall, int waitForInServiceTimeout, 597 boolean turnOffOemEnabledSatelliteDuringEmergencyCall, 598 boolean turnOffNonEmergencyNbIotNtnSatelliteForEmergencyCall, 599 PhoneFactoryProxy phoneFactoryProxy, PhoneSwitcherProxy phoneSwitcherProxy, 600 TelephonyManagerProxy telephonyManagerProxy, RadioOnHelper radioOnHelper, 601 long ecmExitTimeoutMs, FeatureFlags featureFlags) { 602 mContext = context; 603 mHandler = new MyHandler(looper); 604 mIsSuplDdsSwitchRequiredForEmergencyCall = isSuplDdsSwitchRequiredForEmergencyCall; 605 mWaitForInServiceTimeoutMs = waitForInServiceTimeout; 606 mTurnOffOemEnabledSatelliteDuringEmergencyCall = 607 turnOffOemEnabledSatelliteDuringEmergencyCall; 608 mTurnOffNonEmergencyNbIotNtnSatelliteForEmergencyCall = 609 turnOffNonEmergencyNbIotNtnSatelliteForEmergencyCall; 610 mPhoneFactoryProxy = phoneFactoryProxy; 611 mPhoneSwitcherProxy = phoneSwitcherProxy; 612 mTelephonyManagerProxy = telephonyManagerProxy; 613 mRadioOnHelper = radioOnHelper; 614 mEcmExitTimeoutMs = ecmExitTimeoutMs; 615 mFeatureFlags = featureFlags; 616 mWakeLock = null; // Don't declare a wakelock in tests 617 mConfigManager = context.getSystemService(CarrierConfigManager.class); 618 mConfigManager.registerCarrierConfigChangeListener(mHandler::post, 619 mCarrierConfigChangeListener); 620 IntentFilter filter = new IntentFilter(); 621 filter.addAction(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); 622 context.registerReceiver(mEcmExitReceiver, filter, null, mHandler); 623 registerForNewRingingConnection(); 624 } 625 626 /** 627 * Starts the process of an emergency call. 628 * 629 * <p> 630 * Handles turning on radio and switching DDS. 631 * 632 * @param phone the {@code Phone} on which to process the emergency call. 633 * @param c the {@code Connection} on which to process the emergency call. 634 * @param isTestEmergencyNumber whether this is a test emergency number. 635 * @return a {@code CompletableFuture} that results in {@code DisconnectCause.NOT_DISCONNECTED} 636 * if emergency call successfully started. 637 */ startEmergencyCall(@onNull Phone phone, @NonNull android.telecom.Connection c, boolean isTestEmergencyNumber)638 public CompletableFuture<Integer> startEmergencyCall(@NonNull Phone phone, 639 @NonNull android.telecom.Connection c, boolean isTestEmergencyNumber) { 640 Rlog.i(TAG, "startEmergencyCall: phoneId=" + phone.getPhoneId() 641 + ", callId=" + c.getTelecomCallId()); 642 643 if (needToSwitchPhone(phone)) { 644 Rlog.e(TAG, "startEmergencyCall failed. need to switch stacks."); 645 return CompletableFuture.completedFuture(DisconnectCause.EMERGENCY_PERM_FAILURE); 646 } 647 648 if (mPhone != null) { 649 // Create new future to return as to not interfere with any uncompleted futures. 650 // Case1) When 2nd emergency call is initiated during an active call on the same phone. 651 // Case2) While the device is in ECBM, an emergency call is initiated on the same phone. 652 if (isSamePhone(mPhone, phone) && (!mActiveEmergencyCalls.isEmpty() || isInEcm())) { 653 exitEmergencySmsCallbackMode(STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED); 654 mOngoingConnection = c; 655 mIsTestEmergencyNumber = isTestEmergencyNumber; 656 if (isInEcm()) { 657 // Remove pending exit ECM runnable. 658 mHandler.removeCallbacks(mExitEcmRunnable); 659 releaseWakeLock(); 660 ((GsmCdmaPhone) mPhone).notifyEcbmTimerReset(Boolean.TRUE); 661 662 if (mFeatureFlags.emergencyCallbackModeNotification()) { 663 mPhone.stopEmergencyCallbackMode(EMERGENCY_CALLBACK_MODE_CALL, 664 STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED); 665 } 666 667 mOngoingCallProperties = 0; 668 mCallEmergencyModeFuture = new CompletableFuture<>(); 669 setEmergencyMode(mPhone, EMERGENCY_TYPE_CALL, MODE_EMERGENCY_WWAN, 670 MSG_SET_EMERGENCY_MODE_DONE); 671 return mCallEmergencyModeFuture; 672 } 673 // Ensure that domain selector requests scan. 674 mLastEmergencyRegistrationResult = new EmergencyRegistrationResult( 675 AccessNetworkConstants.AccessNetworkType.UNKNOWN, 676 NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN, 677 NetworkRegistrationInfo.DOMAIN_UNKNOWN, false, false, 0, 0, "", "", ""); 678 return CompletableFuture.completedFuture(DisconnectCause.NOT_DISCONNECTED); 679 } 680 681 Rlog.e(TAG, "startEmergencyCall failed. Existing emergency call in progress."); 682 return CompletableFuture.completedFuture(DisconnectCause.ERROR_UNSPECIFIED); 683 } 684 685 mOngoingCallProperties = 0; 686 mCallEmergencyModeFuture = new CompletableFuture<>(); 687 688 if (mSmsPhone != null) { 689 mIsEmergencyCallStartedDuringEmergencySms = true; 690 // Case1) While exiting the emergency mode on the other phone, 691 // the emergency mode for this call will be restarted after the exit complete. 692 // Case2) While entering the emergency mode on the other phone, 693 // exit the emergency mode when receiving the result of setting the emergency mode and 694 // the emergency mode for this call will be restarted after the exit complete. 695 if (isInEmergencyMode() && !isEmergencyModeInProgress()) { 696 if (!isSamePhone(mSmsPhone, phone)) { 697 exitEmergencyMode(mSmsPhone, EMERGENCY_TYPE_SMS); 698 } else { 699 // If the device is already in the emergency mode on the same phone, 700 // the general emergency call procedure can be immediately performed. 701 // And, if the emergency PDN is already connected, then we need to keep 702 // this PDN active while initating the emergency call. 703 mIsEmergencyCallStartedDuringEmergencySms = false; 704 } 705 706 exitEmergencySmsCallbackMode(STOP_REASON_OUTGOING_EMERGENCY_CALL_INITIATED); 707 } 708 709 if (mIsEmergencyCallStartedDuringEmergencySms) { 710 mPhone = phone; 711 mOngoingConnection = c; 712 mIsTestEmergencyNumber = isTestEmergencyNumber; 713 sendEmergencyCallStateChange(mPhone, true); 714 maybeRejectIncomingCall(null); 715 return mCallEmergencyModeFuture; 716 } 717 } 718 719 mPhone = phone; 720 mOngoingConnection = c; 721 mIsTestEmergencyNumber = isTestEmergencyNumber; 722 sendEmergencyCallStateChange(mPhone, true); 723 final android.telecom.Connection expectedConnection = mOngoingConnection; 724 maybeRejectIncomingCall(result -> { 725 Rlog.i(TAG, "maybeRejectIncomingCall : result = " + result); 726 if (!Objects.equals(mOngoingConnection, expectedConnection)) { 727 Rlog.i(TAG, "maybeRejectIncomingCall " 728 + expectedConnection.getTelecomCallId() + " canceled."); 729 return; 730 } 731 turnOnRadioAndSwitchDds(mPhone, EMERGENCY_TYPE_CALL, mIsTestEmergencyNumber); 732 }); 733 return mCallEmergencyModeFuture; 734 } 735 736 /** 737 * Ends emergency call. 738 * 739 * <p> 740 * Enter ECM only once all active emergency calls have ended. If a call never reached 741 * {@link Call.State#ACTIVE}, then no need to enter ECM. 742 * 743 * @param c the emergency call disconnected. 744 */ endCall(@onNull android.telecom.Connection c)745 public void endCall(@NonNull android.telecom.Connection c) { 746 boolean wasActive = mActiveEmergencyCalls.remove(c); 747 748 if (Objects.equals(mOngoingConnection, c)) { 749 mOngoingConnection = null; 750 mOngoingCallProperties = 0; 751 sendEmergencyCallStateChange(mPhone, false); 752 unregisterForVoiceRegStateOrRatChanged(); 753 } 754 755 if (wasActive && mActiveEmergencyCalls.isEmpty() 756 && isEmergencyCallbackModeSupported(mPhone, true)) { 757 enterEmergencyCallbackMode(); 758 759 if (mOngoingConnection == null) { 760 mIsEmergencyCallStartedDuringEmergencySms = false; 761 mCallEmergencyModeFuture = null; 762 } 763 } else if (mOngoingConnection == null) { 764 if (isInEcm()) { 765 mIsEmergencyCallStartedDuringEmergencySms = false; 766 mCallEmergencyModeFuture = null; 767 768 if (mActiveEmergencyCalls.isEmpty()) { 769 // If the emergency call was initiated during the emergency callback mode, 770 // the emergency callback mode should be restored when the emergency call is 771 // ended. 772 if (isEmergencyCallbackModeSupported(mPhone, true)) { 773 enterEmergencyCallbackMode(); 774 } else { 775 exitEmergencyCallbackMode(); 776 clearEmergencyCallInfo(); 777 } 778 } 779 } else { 780 if (isInScbm()) { 781 setIsInEmergencyCall(false); 782 setEmergencyCallbackMode(mSmsPhone, EMERGENCY_TYPE_SMS); 783 } else { 784 exitEmergencyMode(mPhone, EMERGENCY_TYPE_CALL); 785 } 786 clearEmergencyCallInfo(); 787 } 788 } 789 790 // Release any blocked thread immediately 791 maybeNotifyTransportChangeCompleted(EMERGENCY_TYPE_CALL, true); 792 } 793 clearEmergencyCallInfo()794 private void clearEmergencyCallInfo() { 795 mEmergencyCallDomain = NetworkRegistrationInfo.DOMAIN_UNKNOWN; 796 mEmergencyCallPhoneType = PhoneConstants.PHONE_TYPE_NONE; 797 mIsTestEmergencyNumber = false; 798 mIsEmergencyCallStartedDuringEmergencySms = false; 799 mCallEmergencyModeFuture = null; 800 mOngoingConnection = null; 801 mOngoingCallProperties = 0; 802 mPhone = null; 803 } 804 switchDdsAndSetEmergencyMode(Phone phone, @EmergencyType int emergencyType)805 private void switchDdsAndSetEmergencyMode(Phone phone, @EmergencyType int emergencyType) { 806 switchDdsDelayed(phone, result -> { 807 Rlog.i(TAG, "switchDdsDelayed: result = " + result); 808 if (!result) { 809 // DDS Switch timed out/failed, but continue with call as it may still succeed. 810 Rlog.e(TAG, "DDS Switch failed."); 811 } 812 // Once radio is on and DDS switched, must call setEmergencyMode() before selecting 813 // emergency domain. EmergencyRegistrationResult is required to determine domain and 814 // this is the only API that can receive it before starting domain selection. 815 // Once domain selection is finished, the actual emergency mode will be set when 816 // onEmergencyTransportChanged() is called. 817 if (mEmergencyMode != MODE_EMERGENCY_WWAN) { 818 setEmergencyMode(phone, emergencyType, MODE_EMERGENCY_WWAN, 819 MSG_SET_EMERGENCY_MODE_DONE); 820 } else { 821 // Ensure that domain selector requests the network scan. 822 mLastEmergencyRegistrationResult = new EmergencyRegistrationResult( 823 AccessNetworkConstants.AccessNetworkType.UNKNOWN, 824 NetworkRegistrationInfo.REGISTRATION_STATE_UNKNOWN, 825 NetworkRegistrationInfo.DOMAIN_UNKNOWN, false, false, 0, 0, "", "", ""); 826 if (emergencyType == EMERGENCY_TYPE_CALL) { 827 setIsInEmergencyCall(true); 828 } 829 completeEmergencyMode(emergencyType); 830 } 831 }); 832 } 833 834 /** 835 * Triggers modem to set new emergency mode. 836 * 837 * @param phone the {@code Phone} to set the emergency mode on modem. 838 * @param emergencyType the emergency type to identify an emergency call or SMS. 839 * @param mode the new emergency mode. 840 * @param msg the message to be sent once mode has been set. 841 */ setEmergencyMode(Phone phone, @EmergencyType int emergencyType, @EmergencyConstants.EmergencyMode int mode, int msg)842 private void setEmergencyMode(Phone phone, @EmergencyType int emergencyType, 843 @EmergencyConstants.EmergencyMode int mode, int msg) { 844 Rlog.i(TAG, "setEmergencyMode from " + mEmergencyMode + " to " + mode + " for " 845 + emergencyTypeToString(emergencyType)); 846 847 if (mEmergencyMode == mode) { 848 // Initial transport selection of DomainSelector 849 maybeNotifyTransportChangeCompleted(emergencyType, false); 850 return; 851 } 852 853 if (emergencyType == EMERGENCY_TYPE_CALL 854 && mode == MODE_EMERGENCY_WWAN 855 && isEmergencyModeInProgress() && !isInEmergencyMode()) { 856 // In cross sim redialing or ending emergency SMS, exitEmergencyMode is not completed. 857 mEmergencyMode = mode; 858 Rlog.i(TAG, "setEmergencyMode wait for the completion of exitEmergencyMode"); 859 return; 860 } 861 862 mEmergencyMode = mode; 863 setEmergencyModeInProgress(true); 864 865 Message m = mHandler.obtainMessage(msg, Integer.valueOf(emergencyType)); 866 if (mIsTestEmergencyNumberForSms && emergencyType == EMERGENCY_TYPE_SMS) { 867 Rlog.d(TAG, "TestEmergencyNumber for " + emergencyTypeToString(emergencyType) 868 + ": Skipping setting emergency mode on modem."); 869 // Send back a response for the command, but with null information 870 AsyncResult.forMessage(m, null, null); 871 // Ensure that we do not accidentally block indefinitely when trying to validate test 872 // emergency numbers 873 m.sendToTarget(); 874 return; 875 } 876 877 mWasEmergencyModeSetOnModem = true; 878 phone.setEmergencyMode(mode, m); 879 } 880 881 /** 882 * Sets the emergency callback mode on modem. 883 * 884 * @param phone the {@code Phone} to set the emergency mode on modem. 885 * @param emergencyType the emergency type to identify an emergency call or SMS. 886 */ setEmergencyCallbackMode(Phone phone, @EmergencyType int emergencyType)887 private void setEmergencyCallbackMode(Phone phone, @EmergencyType int emergencyType) { 888 boolean needToSetCallbackMode = false; 889 890 if (emergencyType == EMERGENCY_TYPE_CALL) { 891 needToSetCallbackMode = true; 892 } else if (emergencyType == EMERGENCY_TYPE_SMS) { 893 // Ensure that no emergency call is in progress. 894 if (mActiveEmergencyCalls.isEmpty() && mOngoingConnection == null 895 && mOngoingEmergencySmsIds.isEmpty()) { 896 needToSetCallbackMode = true; 897 } 898 } 899 900 if (needToSetCallbackMode) { 901 // Set emergency mode on modem. 902 setEmergencyMode(phone, emergencyType, MODE_EMERGENCY_CALLBACK, 903 MSG_SET_EMERGENCY_CALLBACK_MODE_DONE); 904 } 905 } 906 completeEmergencyMode(@mergencyType int emergencyType)907 private void completeEmergencyMode(@EmergencyType int emergencyType) { 908 completeEmergencyMode(emergencyType, DisconnectCause.NOT_DISCONNECTED); 909 } 910 completeEmergencyMode(@mergencyType int emergencyType, @DisconnectCauses int result)911 private void completeEmergencyMode(@EmergencyType int emergencyType, 912 @DisconnectCauses int result) { 913 CompletableFuture<Integer> emergencyModeFuture = null; 914 915 if (emergencyType == EMERGENCY_TYPE_CALL) { 916 emergencyModeFuture = mCallEmergencyModeFuture; 917 918 if (result != DisconnectCause.NOT_DISCONNECTED) { 919 clearEmergencyCallInfo(); 920 } 921 } else if (emergencyType == EMERGENCY_TYPE_SMS) { 922 emergencyModeFuture = mSmsEmergencyModeFuture; 923 924 if (result != DisconnectCause.NOT_DISCONNECTED) { 925 clearEmergencySmsInfo(); 926 } 927 } 928 929 if (emergencyModeFuture != null && !emergencyModeFuture.isDone()) { 930 emergencyModeFuture.complete(result); 931 } 932 } 933 934 /** 935 * Checks if the device is currently in the emergency mode or not. 936 */ 937 @VisibleForTesting isInEmergencyMode()938 public boolean isInEmergencyMode() { 939 return mEmergencyMode != MODE_EMERGENCY_NONE; 940 } 941 942 /** 943 * Sets the flag to inidicate whether setting the emergency mode on modem is in progress or not. 944 */ setEmergencyModeInProgress(boolean isEmergencyModeInProgress)945 private void setEmergencyModeInProgress(boolean isEmergencyModeInProgress) { 946 mIsEmergencyModeInProgress = isEmergencyModeInProgress; 947 } 948 949 /** 950 * Checks whether setting the emergency mode on modem is in progress or not. 951 */ isEmergencyModeInProgress()952 private boolean isEmergencyModeInProgress() { 953 return mIsEmergencyModeInProgress; 954 } 955 956 /** 957 * Notifies external app listeners of emergency mode changes. 958 * 959 * @param isInEmergencyCall a flag to indicate whether there is an active emergency call. 960 */ setIsInEmergencyCall(boolean isInEmergencyCall)961 private void setIsInEmergencyCall(boolean isInEmergencyCall) { 962 mIsInEmergencyCall = isInEmergencyCall; 963 } 964 965 /** 966 * Checks if there is an ongoing emergency call. 967 * 968 * @return true if in emergency call 969 */ isInEmergencyCall()970 public boolean isInEmergencyCall() { 971 return mIsInEmergencyCall; 972 } 973 974 /** 975 * Triggers modem to exit emergency mode. 976 * 977 * @param phone the {@code Phone} to exit the emergency mode. 978 * @param emergencyType the emergency type to identify an emergency call or SMS. 979 */ exitEmergencyMode(Phone phone, @EmergencyType int emergencyType)980 private void exitEmergencyMode(Phone phone, @EmergencyType int emergencyType) { 981 Rlog.i(TAG, "exitEmergencyMode for " + emergencyTypeToString(emergencyType)); 982 983 if (emergencyType == EMERGENCY_TYPE_CALL) { 984 if (mSmsPhone != null && isSamePhone(phone, mSmsPhone)) { 985 // Waits for exiting the emergency mode until the emergency SMS is ended. 986 Rlog.i(TAG, "exitEmergencyMode: waits for emergency SMS end."); 987 setIsInEmergencyCall(false); 988 return; 989 } 990 } else if (emergencyType == EMERGENCY_TYPE_SMS) { 991 if (mPhone != null && isSamePhone(phone, mPhone)) { 992 // Waits for exiting the emergency mode until the emergency call is ended. 993 Rlog.i(TAG, "exitEmergencyMode: waits for emergency call end."); 994 return; 995 } 996 } 997 998 if (mEmergencyMode == MODE_EMERGENCY_NONE) { 999 return; 1000 } 1001 mEmergencyMode = MODE_EMERGENCY_NONE; 1002 setEmergencyModeInProgress(true); 1003 1004 Message m = mHandler.obtainMessage( 1005 MSG_EXIT_EMERGENCY_MODE_DONE, Integer.valueOf(emergencyType)); 1006 if (!mWasEmergencyModeSetOnModem) { 1007 Rlog.d(TAG, "Emergency mode was not set on modem: Skipping exiting emergency mode."); 1008 // Send back a response for the command, but with null information 1009 AsyncResult.forMessage(m, null, null); 1010 // Ensure that we do not accidentally block indefinitely when trying to validate 1011 // the exit condition. 1012 m.sendToTarget(); 1013 return; 1014 } 1015 1016 mWasEmergencyModeSetOnModem = false; 1017 phone.exitEmergencyMode(m); 1018 } 1019 1020 /** Returns last {@link EmergencyRegistrationResult} as set by {@code setEmergencyMode()}. */ getEmergencyRegistrationResult()1021 public EmergencyRegistrationResult getEmergencyRegistrationResult() { 1022 return mLastEmergencyRegistrationResult; 1023 } 1024 waitForTransportChangeCompleted(CompletableFuture<Boolean> future)1025 private void waitForTransportChangeCompleted(CompletableFuture<Boolean> future) { 1026 if (future != null) { 1027 synchronized (future) { 1028 if ((mEmergencyMode == MODE_EMERGENCY_NONE) 1029 || mHandler.getLooper().isCurrentThread()) { 1030 // Do not block the Handler's thread 1031 return; 1032 } 1033 long now = SystemClock.elapsedRealtime(); 1034 long deadline = now + DEFAULT_TRANSPORT_CHANGE_TIMEOUT_MS; 1035 // Guard with while loop to handle spurious wakeups 1036 while (!future.isDone() && now < deadline) { 1037 try { 1038 future.wait(deadline - now); 1039 } catch (Exception e) { 1040 Rlog.e(TAG, "waitForTransportChangeCompleted wait e=" + e); 1041 } 1042 now = SystemClock.elapsedRealtime(); 1043 } 1044 } 1045 } 1046 } 1047 maybeNotifyTransportChangeCompleted(@mergencyType int emergencyType, boolean enforced)1048 private void maybeNotifyTransportChangeCompleted(@EmergencyType int emergencyType, 1049 boolean enforced) { 1050 if (emergencyType != EMERGENCY_TYPE_CALL) { 1051 // It's not for the emergency call 1052 return; 1053 } 1054 CompletableFuture<Boolean> future = mEmergencyTransportChangedFuture; 1055 if (future != null) { 1056 synchronized (future) { 1057 if (!future.isDone() 1058 && ((!isEmergencyModeInProgress() && mEmergencyMode == MODE_EMERGENCY_WWAN) 1059 || enforced)) { 1060 future.complete(Boolean.TRUE); 1061 future.notifyAll(); 1062 } 1063 } 1064 } 1065 } 1066 1067 /** 1068 * Handles emergency transport change by setting new emergency mode. 1069 * 1070 * @param emergencyType the emergency type to identify an emergency call or SMS 1071 * @param mode the new emergency mode 1072 */ onEmergencyTransportChangedAndWait(@mergencyType int emergencyType, @EmergencyConstants.EmergencyMode int mode)1073 public void onEmergencyTransportChangedAndWait(@EmergencyType int emergencyType, 1074 @EmergencyConstants.EmergencyMode int mode) { 1075 // Wait for the completion of setting MODE_EMERGENCY_WWAN only for emergency calls 1076 if (emergencyType == EMERGENCY_TYPE_CALL && mode == MODE_EMERGENCY_WWAN) { 1077 CompletableFuture<Boolean> future = new CompletableFuture<>(); 1078 synchronized (future) { 1079 mEmergencyTransportChangedFuture = future; 1080 onEmergencyTransportChanged(emergencyType, mode); 1081 waitForTransportChangeCompleted(future); 1082 } 1083 return; 1084 } 1085 onEmergencyTransportChanged(emergencyType, mode); 1086 } 1087 1088 /** 1089 * Handles emergency transport change by setting new emergency mode. 1090 * 1091 * @param emergencyType the emergency type to identify an emergency call or SMS 1092 * @param mode the new emergency mode 1093 */ onEmergencyTransportChanged(@mergencyType int emergencyType, @EmergencyConstants.EmergencyMode int mode)1094 public void onEmergencyTransportChanged(@EmergencyType int emergencyType, 1095 @EmergencyConstants.EmergencyMode int mode) { 1096 if (mHandler.getLooper().isCurrentThread()) { 1097 Phone phone = null; 1098 if (emergencyType == EMERGENCY_TYPE_CALL) { 1099 phone = mPhone; 1100 } else if (emergencyType == EMERGENCY_TYPE_SMS) { 1101 phone = mSmsPhone; 1102 } 1103 1104 if (phone != null) { 1105 setEmergencyMode(phone, emergencyType, mode, MSG_SET_EMERGENCY_MODE_DONE); 1106 } 1107 } else { 1108 mHandler.post(() -> { 1109 onEmergencyTransportChanged(emergencyType, mode); 1110 }); 1111 } 1112 } 1113 1114 /** 1115 * Notify the tracker that the emergency call domain has been updated. 1116 * @param phoneType The new PHONE_TYPE_* of the call. 1117 * @param c The connection of the call 1118 */ onEmergencyCallDomainUpdated(int phoneType, android.telecom.Connection c)1119 public void onEmergencyCallDomainUpdated(int phoneType, android.telecom.Connection c) { 1120 Rlog.d(TAG, "domain update for callId: " + c.getTelecomCallId()); 1121 int domain = -1; 1122 switch(phoneType) { 1123 case (PhoneConstants.PHONE_TYPE_CDMA_LTE): 1124 //fallthrough 1125 case (PhoneConstants.PHONE_TYPE_GSM): 1126 //fallthrough 1127 case (PhoneConstants.PHONE_TYPE_CDMA): { 1128 domain = NetworkRegistrationInfo.DOMAIN_CS; 1129 break; 1130 } 1131 case (PhoneConstants.PHONE_TYPE_IMS): { 1132 domain = NetworkRegistrationInfo.DOMAIN_PS; 1133 break; 1134 } 1135 default: { 1136 Rlog.w(TAG, "domain updated: Unexpected phoneType:" + phoneType); 1137 } 1138 } 1139 1140 if (mEmergencyCallPhoneType != phoneType) { 1141 Rlog.i(TAG, "phoneType updated: from " + mEmergencyCallPhoneType + " to " + phoneType); 1142 mEmergencyCallPhoneType = phoneType; 1143 } 1144 1145 if (mEmergencyCallDomain != domain) { 1146 Rlog.i(TAG, "domain updated: from " + mEmergencyCallDomain + " to " + domain); 1147 mEmergencyCallDomain = domain; 1148 } 1149 } 1150 1151 /** 1152 * Handles emergency call state change. 1153 * 1154 * @param state the new call state 1155 * @param c the call whose state has changed 1156 */ onEmergencyCallStateChanged(Call.State state, android.telecom.Connection c)1157 public void onEmergencyCallStateChanged(Call.State state, android.telecom.Connection c) { 1158 if (state == Call.State.ACTIVE) { 1159 mActiveEmergencyCalls.add(c); 1160 if (Objects.equals(mOngoingConnection, c)) { 1161 Rlog.i(TAG, "call connected " + c.getTelecomCallId()); 1162 if (mPhone != null 1163 && isVoWiFi(mOngoingCallProperties) 1164 && mEmergencyMode == EmergencyConstants.MODE_EMERGENCY_WLAN) { 1165 // Recover normal service in cellular when VoWiFi is connected 1166 mPhone.cancelEmergencyNetworkScan(true, null); 1167 } 1168 } 1169 } 1170 } 1171 1172 /** 1173 * Handles the change of emergency call properties. 1174 * 1175 * @param properties the new call properties. 1176 * @param c the call whose state has changed. 1177 */ onEmergencyCallPropertiesChanged(int properties, android.telecom.Connection c)1178 public void onEmergencyCallPropertiesChanged(int properties, android.telecom.Connection c) { 1179 if (Objects.equals(mOngoingConnection, c)) { 1180 mOngoingCallProperties = properties; 1181 } 1182 } 1183 1184 /** 1185 * Handles the radio power off request. 1186 */ onCellularRadioPowerOffRequested()1187 public void onCellularRadioPowerOffRequested() { 1188 exitEmergencySmsCallbackModeAndEmergencyMode(STOP_REASON_UNKNOWN); 1189 exitEmergencyCallbackMode(); 1190 } 1191 isVoWiFi(int properties)1192 private static boolean isVoWiFi(int properties) { 1193 return (properties & android.telecom.Connection.PROPERTY_WIFI) > 0 1194 || (properties & android.telecom.Connection.PROPERTY_CROSS_SIM) > 0; 1195 } 1196 1197 /** 1198 * Returns {@code true} if device and carrier support emergency callback mode. If 1199 * {@code forEcbm} is {@code true}, it also checks RAT used when the emergency call ended. 1200 * 1201 * @param phone The {@link Phone} instance to be checked. 1202 * @param forEcbm {@code true} if it's for the ECBM. {@code false} if it's for the SCBM. 1203 */ 1204 @VisibleForTesting isEmergencyCallbackModeSupported(Phone phone, boolean forEcbm)1205 public boolean isEmergencyCallbackModeSupported(Phone phone, boolean forEcbm) { 1206 // TODO(b/399787802): Remove the forEcbm parameter and related logic when the CDMA-related 1207 // APIs are deprecated. Replace this logic with a check that utilizes the domain parameter 1208 // to determine ECBM and SCBM support. 1209 if (forEcbm) { 1210 if (mFeatureFlags.disableEcbmBasedOnRat()) { 1211 if ((mEmergencyCallPhoneType == PhoneConstants.PHONE_TYPE_GSM) 1212 || (mEmergencyCallPhoneType == PhoneConstants.PHONE_TYPE_NONE)) { 1213 Rlog.d(TAG, "ecbmUnavailableRat"); 1214 return false; 1215 } 1216 } 1217 } 1218 1219 if (phone == null) { 1220 return false; 1221 } 1222 int subId = phone.getSubId(); 1223 int phoneId = phone.getPhoneId(); 1224 if (!isSimReady(phoneId, subId)) { 1225 // If there is no SIM, refer to the saved last carrier configuration with valid 1226 // subscription. 1227 Boolean savedConfig = mNoSimEcbmSupported.get(Integer.valueOf(phoneId)); 1228 if (savedConfig == null) { 1229 // Exceptional case such as with poor boot performance. 1230 // Usually, the first carrier config change will update the cache. 1231 // But with poor boot performance, the carrier config change 1232 // can be delayed for a long time. 1233 SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(mContext); 1234 savedConfig = Boolean.valueOf( 1235 sp.getBoolean(KEY_NO_SIM_ECBM_SUPPORT + phoneId, false)); 1236 Rlog.i(TAG, "ECBM value not cached, load from preference"); 1237 mNoSimEcbmSupported.put(Integer.valueOf(phoneId), savedConfig); 1238 } 1239 Rlog.i(TAG, "isEmergencyCallbackModeSupported savedConfig=" + savedConfig); 1240 return savedConfig; 1241 } else { 1242 return getConfig(subId, 1243 CarrierConfigManager.ImsEmergency.KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL, 1244 DEFAULT_EMERGENCY_CALLBACK_MODE_SUPPORTED); 1245 } 1246 } 1247 1248 /** 1249 * Trigger entry into emergency callback mode. 1250 */ enterEmergencyCallbackMode()1251 private void enterEmergencyCallbackMode() { 1252 Rlog.d(TAG, "enter ECBM"); 1253 setIsInEmergencyCall(false); 1254 // Check if not in ECM already. 1255 if (!isInEcm()) { 1256 setIsInEcm(true); 1257 if (!mPhone.getUnitTestMode()) { 1258 TelephonyProperties.in_ecm_mode(true); 1259 } 1260 1261 // Notify listeners of the entrance to ECM. 1262 sendEmergencyCallbackModeChange(); 1263 if (isInImsEcm()) { 1264 // emergency call registrants are not notified of new emergency call until entering 1265 // ECBM (see ImsPhone#handleEnterEmergencyCallbackMode) 1266 ((GsmCdmaPhone) mPhone).notifyEmergencyCallRegistrants(true); 1267 } 1268 } else { 1269 // Inform to reset the ECBM timer. 1270 ((GsmCdmaPhone) mPhone).notifyEcbmTimerReset(Boolean.FALSE); 1271 } 1272 1273 setEmergencyCallbackMode(mPhone, EMERGENCY_TYPE_CALL); 1274 1275 long delayInMillis = TelephonyProperties.ecm_exit_timer() 1276 .orElse(mEcmExitTimeoutMs); 1277 if (mFeatureFlags.emergencyCallbackModeNotification()) { 1278 mPhone.startEmergencyCallbackMode(EMERGENCY_CALLBACK_MODE_CALL, delayInMillis); 1279 } 1280 1281 // Post this runnable so we will automatically exit if no one invokes 1282 // exitEmergencyCallbackMode() directly. 1283 mHandler.postDelayed(mExitEcmRunnable, delayInMillis); 1284 1285 // We don't want to go to sleep while in ECM. 1286 if (mWakeLock != null) mWakeLock.acquire(delayInMillis); 1287 } 1288 1289 /** 1290 * Exits the emergency callback mode. 1291 * 1292 * <p>This method exits the emergency callback mode with an unknown stop reason. It removes 1293 * any pending exit requests and notifies relevant listeners about the change in status. 1294 */ exitEmergencyCallbackMode()1295 public void exitEmergencyCallbackMode() { 1296 exitEmergencyCallbackMode(STOP_REASON_UNKNOWN); 1297 } 1298 1299 /** 1300 * Exits the emergency callback mode. 1301 * 1302 * <p>This method exits the emergency callback mode with the specified stop reason. It removes 1303 * any pending exit requests and notifies relevant listeners about the change in status, 1304 * providing the reason for exiting. 1305 * 1306 * @param reason The reason for exiting. See 1307 * {@link TelephonyManager.EmergencyCallbackModeStopReason} for possible values. 1308 */ exitEmergencyCallbackMode( @elephonyManager.EmergencyCallbackModeStopReason int reason)1309 public void exitEmergencyCallbackMode( 1310 @TelephonyManager.EmergencyCallbackModeStopReason int reason) { 1311 Rlog.d(TAG, "exit ECBM"); 1312 // Remove pending exit ECM runnable, if any. 1313 mHandler.removeCallbacks(mExitEcmRunnable); 1314 1315 if (isInEcm()) { 1316 setIsInEcm(false); 1317 if (!mPhone.getUnitTestMode()) { 1318 TelephonyProperties.in_ecm_mode(false); 1319 } 1320 1321 // Release wakeLock. 1322 releaseWakeLock(); 1323 1324 GsmCdmaPhone gsmCdmaPhone = (GsmCdmaPhone) mPhone; 1325 // Send intents that ECM has changed. 1326 sendEmergencyCallbackModeChange(); 1327 gsmCdmaPhone.notifyEmergencyCallRegistrants(false); 1328 1329 if (mFeatureFlags.emergencyCallbackModeNotification()) { 1330 gsmCdmaPhone.stopEmergencyCallbackMode(EMERGENCY_CALLBACK_MODE_CALL, reason); 1331 } 1332 1333 // Exit emergency mode on modem. 1334 exitEmergencyMode(gsmCdmaPhone, EMERGENCY_TYPE_CALL); 1335 } 1336 1337 // If an emergency call is in progress, even if this method is called for any reason, 1338 // we should not initialize the Phone object so that the application can normally end 1339 // the emergency call. 1340 if (mOngoingConnection == null) { 1341 mEmergencyCallDomain = NetworkRegistrationInfo.DOMAIN_UNKNOWN; 1342 mEmergencyCallPhoneType = PhoneConstants.PHONE_TYPE_NONE; 1343 mIsTestEmergencyNumber = false; 1344 mPhone = null; 1345 } 1346 } 1347 releaseWakeLock()1348 private void releaseWakeLock() { 1349 // Release wakeLock. 1350 if (mWakeLock != null && mWakeLock.isHeld()) { 1351 try { 1352 mWakeLock.release(); 1353 } catch (Exception e) { 1354 // Ignore the exception if the system has already released this WakeLock. 1355 Rlog.d(TAG, "WakeLock already released: " + e.toString()); 1356 } 1357 } 1358 } 1359 1360 /** 1361 * Exits the emergency callback mode and triggers {@link Runnable} after exit response is 1362 * received. 1363 * 1364 * <p>This method exits the emergency callback mode with the specified stop reason and then 1365 * executes the provided {@link Runnable}. 1366 * 1367 * @param onComplete The {@link Runnable} to execute after exiting emergency callback mode. 1368 * @param reason The reason for exiting. See 1369 * {@link TelephonyManager.EmergencyCallbackModeStopReason} for possible values. 1370 */ exitEmergencyCallbackMode(Runnable onComplete, @TelephonyManager.EmergencyCallbackModeStopReason int reason)1371 public void exitEmergencyCallbackMode(Runnable onComplete, 1372 @TelephonyManager.EmergencyCallbackModeStopReason int reason) { 1373 mOnEcmExitCompleteRunnable = onComplete; 1374 exitEmergencyCallbackMode(reason); 1375 } 1376 1377 /** 1378 * Sends intents that emergency callback mode changed. 1379 */ sendEmergencyCallbackModeChange()1380 private void sendEmergencyCallbackModeChange() { 1381 Rlog.d(TAG, "sendEmergencyCallbackModeChange: isInEcm=" + isInEcm()); 1382 1383 Intent intent = new Intent(TelephonyIntents.ACTION_EMERGENCY_CALLBACK_MODE_CHANGED); 1384 intent.putExtra(TelephonyManager.EXTRA_PHONE_IN_ECM_STATE, isInEcm()); 1385 SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId()); 1386 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 1387 } 1388 1389 /** 1390 * Returns {@code true} if currently in emergency callback mode. 1391 * 1392 * <p> 1393 * This is a period where the phone should be using as little power as possible and be ready to 1394 * receive an incoming call from the emergency operator. 1395 */ isInEcm()1396 public boolean isInEcm() { 1397 return mIsInEcm; 1398 } 1399 1400 /** 1401 * Sets the emergency callback mode state. 1402 * 1403 * @param isInEcm {@code true} if currently in emergency callback mode, {@code false} otherwise. 1404 */ setIsInEcm(boolean isInEcm)1405 private void setIsInEcm(boolean isInEcm) { 1406 mIsInEcm = isInEcm; 1407 } 1408 1409 /** 1410 * Returns {@code true} if currently in emergency callback mode over PS 1411 */ isInImsEcm()1412 public boolean isInImsEcm() { 1413 return mEmergencyCallDomain == NetworkRegistrationInfo.DOMAIN_PS && isInEcm(); 1414 } 1415 1416 /** 1417 * Returns {@code true} if currently in emergency callback mode over CS 1418 */ isInCdmaEcm()1419 public boolean isInCdmaEcm() { 1420 // Phone can be null in the case where we are not actively tracking an emergency call. 1421 if (mPhone == null) return false; 1422 // Ensure that this method doesn't return true when we are attached to GSM. 1423 return mPhone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA 1424 && mEmergencyCallDomain == NetworkRegistrationInfo.DOMAIN_CS && isInEcm(); 1425 } 1426 1427 /** 1428 * Returns {@code true} if currently in emergency callback mode with the given {@link Phone}. 1429 * 1430 * @param phone the {@link Phone} for the emergency call. 1431 */ isInEcm(Phone phone)1432 public boolean isInEcm(Phone phone) { 1433 return isInEcm() && isSamePhone(mPhone, phone); 1434 } 1435 sendEmergencyCallStateChange(Phone phone, boolean isAlive)1436 private void sendEmergencyCallStateChange(Phone phone, boolean isAlive) { 1437 if ((isAlive && !mSentEmergencyCallState && getBroadcastEmergencyCallStateChanges(phone)) 1438 || (!isAlive && mSentEmergencyCallState)) { 1439 mSentEmergencyCallState = isAlive; 1440 Rlog.i(TAG, "sendEmergencyCallStateChange: " + isAlive); 1441 Intent intent = new Intent(ACTION_EMERGENCY_CALL_STATE_CHANGED); 1442 intent.putExtra(TelephonyManager.EXTRA_PHONE_IN_EMERGENCY_CALL, isAlive); 1443 SubscriptionManager.putPhoneIdAndSubIdExtra(intent, phone.getPhoneId()); 1444 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 1445 } 1446 } 1447 1448 /** 1449 * Starts the process of an emergency SMS. 1450 * 1451 * @param phone the {@code Phone} on which to process the emergency SMS. 1452 * @param smsId the SMS id on which to process the emergency SMS. 1453 * @param isTestEmergencyNumber whether this is a test emergency number. 1454 * @return A {@code CompletableFuture} that results in {@code DisconnectCause.NOT_DISCONNECTED} 1455 * if the emergency SMS is successfully started. 1456 */ startEmergencySms(@onNull Phone phone, @NonNull String smsId, boolean isTestEmergencyNumber)1457 public CompletableFuture<Integer> startEmergencySms(@NonNull Phone phone, @NonNull String smsId, 1458 boolean isTestEmergencyNumber) { 1459 Rlog.i(TAG, "startEmergencySms: phoneId=" + phone.getPhoneId() + ", smsId=" + smsId 1460 + ", scbm=" + isInScbm()); 1461 1462 // When an emergency call is in progress, it checks whether an emergency call is already in 1463 // progress on the different phone. 1464 if (mPhone != null && !isSamePhone(mPhone, phone)) { 1465 Rlog.e(TAG, "Emergency call is in progress on the other slot."); 1466 return CompletableFuture.completedFuture(DisconnectCause.ERROR_UNSPECIFIED); 1467 } 1468 1469 boolean exitScbmInOtherPhone = false; 1470 boolean smsStartedInScbm = isInScbm(); 1471 1472 // When an emergency SMS is in progress, it checks whether an emergency SMS is already 1473 // in progress on the different phone. 1474 if (mSmsPhone != null && !isSamePhone(mSmsPhone, phone)) { 1475 if (smsStartedInScbm) { 1476 // When other phone is in the emergency SMS callback mode, we need to stop the 1477 // emergency SMS callback mode first. 1478 exitScbmInOtherPhone = true; 1479 mIsEmergencySmsStartedDuringScbm = true; 1480 exitEmergencySmsCallbackModeAndEmergencyMode(STOP_REASON_EMERGENCY_SMS_SENT); 1481 } else { 1482 Rlog.e(TAG, "Emergency SMS is in progress on the other slot."); 1483 return CompletableFuture.completedFuture(DisconnectCause.ERROR_UNSPECIFIED); 1484 } 1485 } 1486 1487 // When the previous emergency SMS is not completed yet and the device is not in SCBM, 1488 // this new request will not be allowed. 1489 if (mSmsPhone != null && isInEmergencyMode() && isEmergencyModeInProgress() 1490 && !smsStartedInScbm) { 1491 Rlog.e(TAG, "Existing emergency SMS is in progress and not in SCBM."); 1492 return CompletableFuture.completedFuture(DisconnectCause.ERROR_UNSPECIFIED); 1493 } 1494 1495 mSmsPhone = phone; 1496 mIsTestEmergencyNumberForSms = isTestEmergencyNumber; 1497 mOngoingEmergencySmsIds.add(smsId); 1498 1499 if (smsStartedInScbm) { 1500 // When the device is in SCBM and emergency SMS is being sent, 1501 // completes the future immediately. 1502 if (!exitScbmInOtherPhone) { 1503 // The emergency SMS is allowed and returns the success result. 1504 return CompletableFuture.completedFuture(DisconnectCause.NOT_DISCONNECTED); 1505 } 1506 1507 mSmsEmergencyModeFuture = new CompletableFuture<>(); 1508 } else { 1509 // When the emergency mode is already set by the previous emergency call or SMS, 1510 // completes the future immediately. 1511 if (isInEmergencyMode() && !isEmergencyModeInProgress()) { 1512 // The emergency SMS is allowed and returns the success result. 1513 return CompletableFuture.completedFuture(DisconnectCause.NOT_DISCONNECTED); 1514 } 1515 1516 mSmsEmergencyModeFuture = new CompletableFuture<>(); 1517 1518 if (!isInEmergencyMode()) { 1519 setEmergencyMode(mSmsPhone, EMERGENCY_TYPE_SMS, MODE_EMERGENCY_WWAN, 1520 MSG_SET_EMERGENCY_MODE_DONE); 1521 } 1522 } 1523 1524 return mSmsEmergencyModeFuture; 1525 } 1526 1527 /** 1528 * Ends an emergency SMS. 1529 * This should be called once an emergency SMS is sent. 1530 * 1531 * @param smsId the SMS id on which to end the emergency SMS. 1532 * @param success the flag specifying whether an emergency SMS is successfully sent or not. 1533 * {@code true} if SMS is successfully sent, {@code false} otherwise. 1534 * @param domain the domain that MO SMS was sent. 1535 * @param isLastSmsPart the flag specifying whether this result is for the last SMS part or not. 1536 */ endSms(@onNull String smsId, boolean success, @NetworkRegistrationInfo.Domain int domain, boolean isLastSmsPart)1537 public void endSms(@NonNull String smsId, boolean success, 1538 @NetworkRegistrationInfo.Domain int domain, boolean isLastSmsPart) { 1539 if (success && !isLastSmsPart) { 1540 // Waits until all SMS parts are sent successfully. 1541 // Ensures that all SMS parts are sent while in the emergency mode. 1542 Rlog.i(TAG, "endSms: wait for additional SMS parts to be sent."); 1543 } else { 1544 mOngoingEmergencySmsIds.remove(smsId); 1545 } 1546 1547 // If the outgoing emergency SMSs are empty, we can try to exit the emergency mode. 1548 if (mOngoingEmergencySmsIds.isEmpty()) { 1549 mSmsEmergencyModeFuture = null; 1550 mIsEmergencySmsStartedDuringScbm = false; 1551 1552 if (isInEcm()) { 1553 // When the emergency mode is not in MODE_EMERGENCY_CALLBACK, 1554 // it needs to notify the emergency callback mode to modem. 1555 if (mActiveEmergencyCalls.isEmpty() && mOngoingConnection == null) { 1556 setEmergencyCallbackMode(mPhone, EMERGENCY_TYPE_CALL); 1557 } 1558 } 1559 1560 // If SCBM supports, SCBM will be entered here regardless of ECBM state. 1561 if (success && domain == NetworkRegistrationInfo.DOMAIN_PS 1562 && (isInScbm() || isEmergencyCallbackModeSupported(mSmsPhone, false))) { 1563 enterEmergencySmsCallbackMode(); 1564 } else if (isInScbm()) { 1565 // Sets the emergency mode to CALLBACK without re-initiating SCBM timer. 1566 setEmergencyCallbackMode(mSmsPhone, EMERGENCY_TYPE_SMS); 1567 } else { 1568 if (mSmsPhone != null) { 1569 exitEmergencyMode(mSmsPhone, EMERGENCY_TYPE_SMS); 1570 } 1571 clearEmergencySmsInfo(); 1572 } 1573 } 1574 } 1575 1576 /** 1577 * Called when emergency SMS is received from the network. 1578 */ onEmergencySmsReceived()1579 public void onEmergencySmsReceived() { 1580 if (isInScbm()) { 1581 Rlog.d(TAG, "Emergency SMS received, re-initiate SCBM timer"); 1582 // Reinitiate the SCBM timer when receiving emergency SMS while in SCBM. 1583 enterEmergencySmsCallbackMode(); 1584 } 1585 } 1586 clearEmergencySmsInfo()1587 private void clearEmergencySmsInfo() { 1588 mOngoingEmergencySmsIds.clear(); 1589 mIsEmergencySmsStartedDuringScbm = false; 1590 mIsTestEmergencyNumberForSms = false; 1591 mSmsEmergencyModeFuture = null; 1592 mSmsPhone = null; 1593 } 1594 1595 /** 1596 * Returns {@code true} if currently in emergency SMS callback mode. 1597 */ isInScbm()1598 public boolean isInScbm() { 1599 return mIsInScbm; 1600 } 1601 1602 /** 1603 * Sets the emergency SMS callback mode state. 1604 * 1605 * @param isInScbm {@code true} if currently in emergency SMS callback mode, 1606 * {@code false} otherwise. 1607 */ setIsInScbm(boolean isInScbm)1608 private void setIsInScbm(boolean isInScbm) { 1609 mIsInScbm = isInScbm; 1610 } 1611 1612 /** 1613 * Enters the emergency SMS callback mode. 1614 */ enterEmergencySmsCallbackMode()1615 private void enterEmergencySmsCallbackMode() { 1616 Rlog.d(TAG, "enter SCBM while " + (isInScbm() ? "in" : "not in") + " SCBM"); 1617 1618 boolean shouldRestartEcm = isInScbm(); 1619 1620 // Remove pending message if present. 1621 mHandler.removeMessages(MSG_EXIT_SCBM); 1622 1623 if (!isInScbm()) { 1624 setIsInScbm(true); 1625 } 1626 1627 setEmergencyCallbackMode(mSmsPhone, EMERGENCY_TYPE_SMS); 1628 1629 // At the moment, the default SCBM timer value will be used with the same value 1630 // that is configured for emergency callback mode. 1631 int delayInMillis = Long.valueOf(mEcmExitTimeoutMs).intValue(); 1632 int subId = mSmsPhone.getSubId(); 1633 if (SubscriptionManager.isValidSubscriptionId(subId)) { 1634 delayInMillis = getConfig(subId, 1635 CarrierConfigManager.KEY_EMERGENCY_SMS_MODE_TIMER_MS_INT, 1636 delayInMillis); 1637 if (delayInMillis == 0) { 1638 delayInMillis = Long.valueOf(mEcmExitTimeoutMs).intValue(); 1639 } 1640 } 1641 // Post the message so we will automatically exit if no one invokes 1642 // exitEmergencySmsCallbackModeAndEmergencyMode() directly. 1643 mHandler.sendEmptyMessageDelayed(MSG_EXIT_SCBM, delayInMillis); 1644 1645 if (mFeatureFlags.emergencyCallbackModeNotification()) { 1646 if (shouldRestartEcm) { 1647 mSmsPhone.restartEmergencyCallbackMode(EMERGENCY_CALLBACK_MODE_SMS, delayInMillis); 1648 } else { 1649 mSmsPhone.startEmergencyCallbackMode(EMERGENCY_CALLBACK_MODE_SMS, delayInMillis); 1650 } 1651 } 1652 } 1653 1654 /** 1655 * Exits emergency SMS callback mode and emergency mode if the device is in SCBM and 1656 * the emergency mode is in CALLBACK. 1657 * 1658 * @param reason The reason for exiting. See 1659 * {@link TelephonyManager.EmergencyCallbackModeStopReason} for possible values. 1660 */ exitEmergencySmsCallbackModeAndEmergencyMode( @elephonyManager.EmergencyCallbackModeStopReason int reason)1661 private void exitEmergencySmsCallbackModeAndEmergencyMode( 1662 @TelephonyManager.EmergencyCallbackModeStopReason int reason) { 1663 Rlog.d(TAG, "exit SCBM and emergency mode"); 1664 final Phone smsPhone = mSmsPhone; 1665 boolean wasInScbm = isInScbm(); 1666 exitEmergencySmsCallbackMode(reason); 1667 1668 // The emergency mode needs to be checked to ensure that there is no ongoing emergency SMS. 1669 if (wasInScbm && mOngoingEmergencySmsIds.isEmpty()) { 1670 // Exit emergency mode on modem. 1671 exitEmergencyMode(smsPhone, EMERGENCY_TYPE_SMS); 1672 } 1673 } 1674 1675 /** 1676 * Exits emergency SMS callback mode. 1677 * 1678 * @param reason The reason for exiting. See 1679 * {@link TelephonyManager.EmergencyCallbackModeStopReason} for possible values. 1680 */ exitEmergencySmsCallbackMode( @elephonyManager.EmergencyCallbackModeStopReason int reason)1681 private void exitEmergencySmsCallbackMode( 1682 @TelephonyManager.EmergencyCallbackModeStopReason int reason) { 1683 // Remove pending message if present. 1684 mHandler.removeMessages(MSG_EXIT_SCBM); 1685 1686 if (isInScbm()) { 1687 Rlog.i(TAG, "exit SCBM"); 1688 1689 if (mFeatureFlags.emergencyCallbackModeNotification()) { 1690 mSmsPhone.stopEmergencyCallbackMode(EMERGENCY_CALLBACK_MODE_SMS, reason); 1691 } 1692 1693 setIsInScbm(false); 1694 } 1695 1696 if (mOngoingEmergencySmsIds.isEmpty()) { 1697 mIsTestEmergencyNumberForSms = false; 1698 mSmsPhone = null; 1699 } 1700 } 1701 1702 /** 1703 * Returns {@code true} if any phones from PhoneFactory have radio on. 1704 */ isRadioOn()1705 private boolean isRadioOn() { 1706 boolean result = false; 1707 for (Phone phone : mPhoneFactoryProxy.getPhones()) { 1708 result |= phone.isRadioOn(); 1709 } 1710 return result; 1711 } 1712 1713 /** 1714 * Returns {@code true} if service states of all phones from PhoneFactory are radio off. 1715 */ isPowerOff()1716 private boolean isPowerOff() { 1717 for (Phone phone : mPhoneFactoryProxy.getPhones()) { 1718 ServiceState ss = phone.getServiceStateTracker().getServiceState(); 1719 if (ss.getState() != ServiceState.STATE_POWER_OFF) return false; 1720 } 1721 return true; 1722 } 1723 registerForVoiceRegStateOrRatChanged()1724 private void registerForVoiceRegStateOrRatChanged() { 1725 if (mIsWaitingForRadioOff) return; 1726 for (Phone phone : mPhoneFactoryProxy.getPhones()) { 1727 phone.getServiceStateTracker().registerForVoiceRegStateOrRatChanged(mHandler, 1728 MSG_VOICE_REG_STATE_CHANGED, null); 1729 } 1730 mIsWaitingForRadioOff = true; 1731 } 1732 unregisterForVoiceRegStateOrRatChanged()1733 private void unregisterForVoiceRegStateOrRatChanged() { 1734 if (!mIsWaitingForRadioOff) return; 1735 for (Phone phone : mPhoneFactoryProxy.getPhones()) { 1736 phone.getServiceStateTracker().unregisterForVoiceRegStateOrRatChanged(mHandler); 1737 } 1738 mIsWaitingForRadioOff = false; 1739 } 1740 1741 /** 1742 * Returns {@code true} if airplane mode is on. 1743 */ isAirplaneModeOn(Context context)1744 private boolean isAirplaneModeOn(Context context) { 1745 return Settings.Global.getInt(context.getContentResolver(), 1746 Settings.Global.AIRPLANE_MODE_ON, 0) > 0; 1747 } 1748 1749 /** 1750 * Ensures that the radio is switched on and that DDS is switched for emergency call/SMS. 1751 * 1752 * <p> 1753 * Once radio is on and DDS switched, must call setEmergencyMode() before completing the future 1754 * and selecting emergency domain. EmergencyRegistrationResult is required to determine domain 1755 * and setEmergencyMode() is the only API that can receive it before starting domain selection. 1756 * Once domain selection is finished, the actual emergency mode will be set when 1757 * onEmergencyTransportChanged() is called. 1758 * 1759 * @param phone the {@code Phone} for the emergency call/SMS. 1760 * @param emergencyType the emergency type to identify an emergency call or SMS. 1761 * @param isTestEmergencyNumber a flag to inidicate whether the emergency call/SMS uses the test 1762 * emergency number. 1763 */ turnOnRadioAndSwitchDds(Phone phone, @EmergencyType int emergencyType, boolean isTestEmergencyNumber)1764 private void turnOnRadioAndSwitchDds(Phone phone, @EmergencyType int emergencyType, 1765 boolean isTestEmergencyNumber) { 1766 final boolean isAirplaneModeOn = isAirplaneModeOn(mContext); 1767 boolean needToTurnOnRadio = !isRadioOn() || isAirplaneModeOn; 1768 boolean needToTurnOffSatellite = shouldExitSatelliteMode(); 1769 1770 if (isAirplaneModeOn && !isPowerOff() 1771 && !phone.getServiceStateTracker().getDesiredPowerState()) { 1772 // power off is delayed to disconnect data connections 1773 Rlog.i(TAG, "turnOnRadioAndSwitchDds: wait for the delayed power off"); 1774 registerForVoiceRegStateOrRatChanged(); 1775 return; 1776 } 1777 1778 if (needToTurnOnRadio || needToTurnOffSatellite) { 1779 Rlog.i(TAG, "turnOnRadioAndSwitchDds: phoneId=" + phone.getPhoneId() + " for " 1780 + emergencyTypeToString(emergencyType)); 1781 if (mRadioOnHelper == null) { 1782 mRadioOnHelper = new RadioOnHelper(mContext); 1783 } 1784 1785 final Phone phoneForEmergency = phone; 1786 final android.telecom.Connection expectedConnection = mOngoingConnection; 1787 final int waitForInServiceTimeout = needToTurnOnRadio ? mWaitForInServiceTimeoutMs : 0; 1788 Rlog.i(TAG, "turnOnRadioAndSwitchDds: timeout=" + waitForInServiceTimeout); 1789 mRadioOnHelper.triggerRadioOnAndListen(new RadioOnStateListener.Callback() { 1790 @Override 1791 public void onComplete(RadioOnStateListener listener, boolean isRadioReady) { 1792 // Make sure the Call has not already been canceled by the user. 1793 if (expectedConnection.getState() == STATE_DISCONNECTED) { 1794 Rlog.i(TAG, "Call disconnected before the outgoing call was placed." 1795 + "Skipping call placement."); 1796 // If call is already canceled by the user, notify modem to exit emergency 1797 // call mode by sending radio on with forEmergencyCall=false. 1798 for (Phone phone : mPhoneFactoryProxy.getPhones()) { 1799 phone.setRadioPower(true, false, false, true); 1800 } 1801 return; 1802 } 1803 if (!isRadioReady) { 1804 if (shouldExitSatelliteMode()) { 1805 // Could not turn satellite off 1806 Rlog.e(TAG, "Failed to turn off satellite modem."); 1807 completeEmergencyMode(emergencyType, DisconnectCause.SATELLITE_ENABLED); 1808 } else { 1809 // Could not turn radio on 1810 Rlog.e(TAG, "Failed to turn on radio."); 1811 completeEmergencyMode(emergencyType, DisconnectCause.POWER_OFF); 1812 } 1813 } else { 1814 if (!Objects.equals(mOngoingConnection, expectedConnection)) { 1815 Rlog.i(TAG, "onComplete " 1816 + expectedConnection.getTelecomCallId() + " canceled."); 1817 return; 1818 } 1819 switchDdsAndSetEmergencyMode(phone, emergencyType); 1820 } 1821 } 1822 1823 @Override 1824 public boolean isOkToCall(Phone phone, int serviceState, boolean imsVoiceCapable) { 1825 if (!Objects.equals(mOngoingConnection, expectedConnection)) { 1826 Rlog.i(TAG, "isOkToCall " 1827 + expectedConnection.getTelecomCallId() + " canceled."); 1828 return true; 1829 } 1830 // Wait for normal service state or timeout if required. 1831 if (phone == phoneForEmergency 1832 && waitForInServiceTimeout > 0 1833 && !isNetworkRegistered(phone)) { 1834 return false; 1835 } 1836 return phone.getServiceStateTracker().isRadioOn() 1837 && !shouldExitSatelliteMode(); 1838 } 1839 1840 @Override 1841 public boolean onTimeout(Phone phone, int serviceState, boolean imsVoiceCapable) { 1842 if (!Objects.equals(mOngoingConnection, expectedConnection)) { 1843 Rlog.i(TAG, "onTimeout " 1844 + expectedConnection.getTelecomCallId() + " canceled."); 1845 return true; 1846 } 1847 // onTimeout shall be called only with the Phone for emergency 1848 return phone.getServiceStateTracker().isRadioOn() 1849 && !shouldExitSatelliteMode(); 1850 } 1851 }, !isTestEmergencyNumber, phone, isTestEmergencyNumber, waitForInServiceTimeout); 1852 } else { 1853 switchDdsAndSetEmergencyMode(phone, emergencyType); 1854 } 1855 } 1856 1857 /** 1858 * If needed, block until the default data is switched for outgoing emergency call, or 1859 * timeout expires. 1860 * 1861 * @param phone The Phone to switch the DDS on. 1862 * @param completeConsumer The consumer to call once the default data subscription has been 1863 * switched, provides {@code true} result if the switch happened 1864 * successfully or {@code false} if the operation timed out/failed. 1865 */ 1866 @VisibleForTesting switchDdsDelayed(Phone phone, Consumer<Boolean> completeConsumer)1867 public void switchDdsDelayed(Phone phone, Consumer<Boolean> completeConsumer) { 1868 if (phone == null) { 1869 // Do not block indefinitely. 1870 completeConsumer.accept(false); 1871 } 1872 try { 1873 // Waiting for PhoneSwitcher to complete the operation. 1874 CompletableFuture<Boolean> future = possiblyOverrideDefaultDataForEmergencyCall(phone); 1875 // In the case that there is an issue or bug in PhoneSwitcher logic, do not wait 1876 // indefinitely for the future to complete. Instead, set a timeout that will complete 1877 // the future as to not block the outgoing call indefinitely. 1878 CompletableFuture<Boolean> timeout = new CompletableFuture<>(); 1879 mHandler.postDelayed(() -> timeout.complete(false), DEFAULT_DATA_SWITCH_TIMEOUT_MS); 1880 // Also ensure that the Consumer is completed on the main thread. 1881 CompletableFuture<Void> unused = future.acceptEitherAsync(timeout, completeConsumer, 1882 mHandler::post); 1883 } catch (Exception e) { 1884 Rlog.w(TAG, "switchDdsDelayed - exception= " + e.getMessage()); 1885 } 1886 } 1887 1888 /** 1889 * If needed, block until Default Data subscription is switched for outgoing emergency call. 1890 * 1891 * <p> 1892 * In some cases, we need to try to switch the Default Data subscription before placing the 1893 * emergency call on DSDS devices. This includes the following situation: - The modem does not 1894 * support processing GNSS SUPL requests on the non-default data subscription. For some carriers 1895 * that do not provide a control plane fallback mechanism, the SUPL request will be dropped and 1896 * we will not be able to get the user's location for the emergency call. In this case, we need 1897 * to swap default data temporarily. 1898 * 1899 * @param phone Evaluates whether or not the default data should be moved to the phone 1900 * specified. Should not be null. 1901 */ possiblyOverrideDefaultDataForEmergencyCall( @onNull Phone phone)1902 private CompletableFuture<Boolean> possiblyOverrideDefaultDataForEmergencyCall( 1903 @NonNull Phone phone) { 1904 int phoneCount = mTelephonyManagerProxy.getPhoneCount(); 1905 // Do not override DDS if this is a single SIM device. 1906 if (phoneCount <= PhoneConstants.MAX_PHONE_COUNT_SINGLE_SIM) { 1907 return CompletableFuture.completedFuture(Boolean.TRUE); 1908 } 1909 1910 // Do not switch Default data if this device supports emergency SUPL on non-DDS. 1911 if (!mIsSuplDdsSwitchRequiredForEmergencyCall) { 1912 Rlog.d(TAG, "possiblyOverrideDefaultDataForEmergencyCall: not switching DDS, does not " 1913 + "require DDS switch."); 1914 return CompletableFuture.completedFuture(Boolean.TRUE); 1915 } 1916 1917 // Only override default data if we are IN_SERVICE already. 1918 if (!isAvailableForEmergencyCalls(phone)) { 1919 Rlog.d(TAG, "possiblyOverrideDefaultDataForEmergencyCall: not switching DDS"); 1920 return CompletableFuture.completedFuture(Boolean.TRUE); 1921 } 1922 1923 // Only override default data if we are not roaming, we do not want to switch onto a network 1924 // that only supports data plane only (if we do not know). 1925 boolean isRoaming = phone.getServiceState().getVoiceRoaming(); 1926 // In some roaming conditions, we know the roaming network doesn't support control plane 1927 // fallback even though the home operator does. For these operators we will need to do a DDS 1928 // switch anyway to make sure the SUPL request doesn't fail. 1929 boolean roamingNetworkSupportsControlPlaneFallback = true; 1930 String[] dataPlaneRoamPlmns = getConfig(phone.getSubId(), 1931 CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY); 1932 if (dataPlaneRoamPlmns != null && Arrays.asList(dataPlaneRoamPlmns) 1933 .contains(phone.getServiceState().getOperatorNumeric())) { 1934 roamingNetworkSupportsControlPlaneFallback = false; 1935 } 1936 if (isRoaming && roamingNetworkSupportsControlPlaneFallback) { 1937 Rlog.d(TAG, "possiblyOverrideDefaultDataForEmergencyCall: roaming network is assumed " 1938 + "to support CP fallback, not switching DDS."); 1939 return CompletableFuture.completedFuture(Boolean.TRUE); 1940 } 1941 // Do not try to swap default data if we support CS fallback or it is assumed that the 1942 // roaming network supports control plane fallback, we do not want to introduce a lag in 1943 // emergency call setup time if possible. 1944 final boolean supportsCpFallback = getConfig(phone.getSubId(), 1945 CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT, 1946 CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_ONLY) 1947 != CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY; 1948 if (supportsCpFallback && roamingNetworkSupportsControlPlaneFallback) { 1949 Rlog.d(TAG, "possiblyOverrideDefaultDataForEmergencyCall: not switching DDS, carrier " 1950 + "supports CP fallback."); 1951 return CompletableFuture.completedFuture(Boolean.TRUE); 1952 } 1953 1954 // Get extension time, may be 0 for some carriers that support ECBM as well. Use 1955 // CarrierConfig default if format fails. 1956 int extensionTime = 0; 1957 try { 1958 extensionTime = Integer.parseInt(getConfig(phone.getSubId(), 1959 CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0")); 1960 } catch (NumberFormatException e) { 1961 // Just use default. 1962 } 1963 CompletableFuture<Boolean> modemResultFuture = new CompletableFuture<>(); 1964 try { 1965 Rlog.d(TAG, "possiblyOverrideDefaultDataForEmergencyCall: overriding DDS for " 1966 + extensionTime + "seconds"); 1967 mPhoneSwitcherProxy.getPhoneSwitcher().overrideDefaultDataForEmergency( 1968 phone.getPhoneId(), extensionTime, modemResultFuture); 1969 // Catch all exceptions, we want to continue with emergency call if possible. 1970 } catch (Exception e) { 1971 Rlog.w(TAG, 1972 "possiblyOverrideDefaultDataForEmergencyCall: exception = " + e.getMessage()); 1973 modemResultFuture = CompletableFuture.completedFuture(Boolean.FALSE); 1974 } 1975 return modemResultFuture; 1976 } 1977 1978 // Helper functions for easy CarrierConfigManager access getConfig(int subId, String key, String defVal)1979 private String getConfig(int subId, String key, String defVal) { 1980 return getConfigBundle(subId, key).getString(key, defVal); 1981 } getConfig(int subId, String key, int defVal)1982 private int getConfig(int subId, String key, int defVal) { 1983 return getConfigBundle(subId, key).getInt(key, defVal); 1984 } getConfig(int subId, String key)1985 private String[] getConfig(int subId, String key) { 1986 return getConfigBundle(subId, key).getStringArray(key); 1987 } getConfig(int subId, String key, boolean defVal)1988 private boolean getConfig(int subId, String key, boolean defVal) { 1989 return getConfigBundle(subId, key).getBoolean(key, defVal); 1990 } getConfigBundle(int subId, String... keys)1991 private PersistableBundle getConfigBundle(int subId, String... keys) { 1992 if (mConfigManager == null) return new PersistableBundle(); 1993 return mConfigManager.getConfigForSubId(subId, keys); 1994 } 1995 1996 /** 1997 * Returns true if the state of the Phone is IN_SERVICE or available for emergency calling only. 1998 */ isAvailableForEmergencyCalls(Phone phone)1999 private boolean isAvailableForEmergencyCalls(Phone phone) { 2000 return ServiceState.STATE_IN_SERVICE == phone.getServiceState().getState() 2001 || phone.getServiceState().isEmergencyOnly(); 2002 } 2003 isNetworkRegistered(Phone phone)2004 private static boolean isNetworkRegistered(Phone phone) { 2005 ServiceState ss = phone.getServiceStateTracker().getServiceState(); 2006 if (ss != null) { 2007 NetworkRegistrationInfo nri = ss.getNetworkRegistrationInfo( 2008 NetworkRegistrationInfo.DOMAIN_PS, 2009 AccessNetworkConstants.TRANSPORT_TYPE_WWAN); 2010 if (nri != null && nri.isNetworkRegistered()) { 2011 // PS is IN_SERVICE state. 2012 return true; 2013 } 2014 nri = ss.getNetworkRegistrationInfo( 2015 NetworkRegistrationInfo.DOMAIN_CS, 2016 AccessNetworkConstants.TRANSPORT_TYPE_WWAN); 2017 if (nri != null && nri.isNetworkRegistered()) { 2018 // CS is IN_SERVICE state. 2019 return true; 2020 } 2021 } 2022 return false; 2023 } 2024 2025 /** 2026 * Checks whether both {@code Phone}s are same or not. 2027 */ isSamePhone(Phone p1, Phone p2)2028 private static boolean isSamePhone(Phone p1, Phone p2) { 2029 return p1 != null && p2 != null && (p1.getPhoneId() == p2.getPhoneId()); 2030 } 2031 emergencyTypeToString(@mergencyType int emergencyType)2032 private static String emergencyTypeToString(@EmergencyType int emergencyType) { 2033 switch (emergencyType) { 2034 case EMERGENCY_TYPE_CALL: return "CALL"; 2035 case EMERGENCY_TYPE_SMS: return "SMS"; 2036 default: return "UNKNOWN"; 2037 } 2038 } 2039 onCarrierConfigurationChanged(int slotIndex, int subId)2040 private void onCarrierConfigurationChanged(int slotIndex, int subId) { 2041 Rlog.i(TAG, "onCarrierConfigChanged slotIndex=" + slotIndex + ", subId=" + subId); 2042 2043 if (slotIndex < 0) { 2044 return; 2045 } 2046 2047 SharedPreferences sp = null; 2048 Boolean savedConfig = mNoSimEcbmSupported.get(Integer.valueOf(slotIndex)); 2049 if (savedConfig == null) { 2050 sp = PreferenceManager.getDefaultSharedPreferences(mContext); 2051 savedConfig = Boolean.valueOf( 2052 sp.getBoolean(KEY_NO_SIM_ECBM_SUPPORT + slotIndex, false)); 2053 mNoSimEcbmSupported.put(Integer.valueOf(slotIndex), savedConfig); 2054 Rlog.i(TAG, "onCarrierConfigChanged load from preference slotIndex=" + slotIndex 2055 + ", ecbmSupported=" + savedConfig); 2056 } 2057 2058 if (!isSimReady(slotIndex, subId)) { 2059 Rlog.i(TAG, "onCarrierConfigChanged SIM not ready"); 2060 return; 2061 } 2062 2063 PersistableBundle b = getConfigBundle(subId, 2064 KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL, 2065 KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL); 2066 if (b.isEmpty()) { 2067 Rlog.e(TAG, "onCarrierConfigChanged empty result"); 2068 return; 2069 } 2070 2071 if (!CarrierConfigManager.isConfigForIdentifiedCarrier(b)) { 2072 Rlog.i(TAG, "onCarrierConfigChanged not carrier specific configuration"); 2073 return; 2074 } 2075 2076 // KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL 2077 boolean broadcast = b.getBoolean(KEY_BROADCAST_EMERGENCY_CALL_STATE_CHANGES_BOOL); 2078 mBroadcastEmergencyCallStateChanges.put( 2079 Integer.valueOf(slotIndex), Boolean.valueOf(broadcast)); 2080 2081 Rlog.i(TAG, "onCarrierConfigChanged slotIndex=" + slotIndex 2082 + ", broadcastEmergencyCallStateChanges=" + broadcast); 2083 2084 // KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL 2085 boolean carrierConfig = b.getBoolean(KEY_EMERGENCY_CALLBACK_MODE_SUPPORTED_BOOL); 2086 if (carrierConfig == savedConfig) { 2087 return; 2088 } 2089 2090 mNoSimEcbmSupported.put(Integer.valueOf(slotIndex), Boolean.valueOf(carrierConfig)); 2091 2092 if (sp == null) { 2093 sp = PreferenceManager.getDefaultSharedPreferences(mContext); 2094 } 2095 SharedPreferences.Editor editor = sp.edit(); 2096 editor.putBoolean(KEY_NO_SIM_ECBM_SUPPORT + slotIndex, carrierConfig); 2097 editor.apply(); 2098 2099 Rlog.i(TAG, "onCarrierConfigChanged preference updated slotIndex=" + slotIndex 2100 + ", ecbmSupported=" + carrierConfig); 2101 } 2102 isSimReady(int slotIndex, int subId)2103 private boolean isSimReady(int slotIndex, int subId) { 2104 return SubscriptionManager.isValidSubscriptionId(subId) 2105 && mTelephonyManagerProxy.getSimState(slotIndex) 2106 == TelephonyManager.SIM_STATE_READY; 2107 } 2108 getBroadcastEmergencyCallStateChanges(Phone phone)2109 private boolean getBroadcastEmergencyCallStateChanges(Phone phone) { 2110 Boolean broadcast = mBroadcastEmergencyCallStateChanges.get( 2111 Integer.valueOf(phone.getPhoneId())); 2112 return (broadcast == null) ? false : broadcast; 2113 } 2114 2115 /** 2116 * Resets the emergency call state if it's in alive state. 2117 */ 2118 @VisibleForTesting maybeResetEmergencyCallStateChangedIntent()2119 public void maybeResetEmergencyCallStateChangedIntent() { 2120 Intent intent = mContext.registerReceiver(null, 2121 new IntentFilter(ACTION_EMERGENCY_CALL_STATE_CHANGED), Context.RECEIVER_NOT_EXPORTED); 2122 if (intent != null 2123 && ACTION_EMERGENCY_CALL_STATE_CHANGED.equals(intent.getAction())) { 2124 boolean isAlive = intent.getBooleanExtra( 2125 TelephonyManager.EXTRA_PHONE_IN_EMERGENCY_CALL, false); 2126 Rlog.i(TAG, "maybeResetEmergencyCallStateChangedIntent isAlive=" + isAlive); 2127 if (isAlive) { 2128 intent = new Intent(ACTION_EMERGENCY_CALL_STATE_CHANGED); 2129 intent.putExtra(TelephonyManager.EXTRA_PHONE_IN_EMERGENCY_CALL, false); 2130 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); 2131 } 2132 } 2133 } 2134 getRingingCall(Phone phone)2135 private Call getRingingCall(Phone phone) { 2136 if (phone == null) return null; 2137 Call ringingCall = phone.getRingingCall(); 2138 if (ringingCall != null 2139 && ringingCall.getState() != Call.State.IDLE 2140 && ringingCall.getState() != Call.State.DISCONNECTED) { 2141 return ringingCall; 2142 } 2143 // Check the ImsPhoneCall in DISCONNECTING state. 2144 Phone imsPhone = phone.getImsPhone(); 2145 if (imsPhone != null) { 2146 ringingCall = imsPhone.getRingingCall(); 2147 } 2148 if (imsPhone != null && ringingCall != null 2149 && ringingCall.getState() != Call.State.IDLE 2150 && ringingCall.getState() != Call.State.DISCONNECTED) { 2151 return ringingCall; 2152 } 2153 return null; 2154 } 2155 2156 /** 2157 * Ensures that there is no incoming call. 2158 * 2159 * @param completeConsumer The consumer to call once rejecting incoming call completes, 2160 * provides {@code true} result if operation completes successfully 2161 * or {@code false} if the operation timed out/failed. 2162 */ maybeRejectIncomingCall(Consumer<Boolean> completeConsumer)2163 private void maybeRejectIncomingCall(Consumer<Boolean> completeConsumer) { 2164 Phone[] phones = mPhoneFactoryProxy.getPhones(); 2165 if (phones == null) { 2166 if (completeConsumer != null) { 2167 completeConsumer.accept(true); 2168 } 2169 return; 2170 } 2171 2172 Call ringingCall = null; 2173 for (Phone phone : phones) { 2174 ringingCall = getRingingCall(phone); 2175 if (ringingCall != null) { 2176 Rlog.i(TAG, "maybeRejectIncomingCall found a ringing call"); 2177 break; 2178 } 2179 } 2180 2181 if (ringingCall == null) { 2182 if (completeConsumer != null) { 2183 completeConsumer.accept(true); 2184 } 2185 return; 2186 } 2187 2188 try { 2189 ringingCall.hangup(); 2190 if (completeConsumer == null) return; 2191 2192 CompletableFuture<Boolean> future = new CompletableFuture<>(); 2193 com.android.internal.telephony.Connection cn = ringingCall.getLatestConnection(); 2194 cn.addListener(new OnDisconnectListener(future)); 2195 // A timeout that will complete the future to not block the outgoing call indefinitely. 2196 CompletableFuture<Boolean> timeout = new CompletableFuture<>(); 2197 mHandler.postDelayed( 2198 () -> timeout.complete(false), DEFAULT_REJECT_INCOMING_CALL_TIMEOUT_MS); 2199 // Ensure that the Consumer is completed on the main thread. 2200 CompletableFuture<Void> unused = future.acceptEitherAsync(timeout, completeConsumer, 2201 mHandler::post).exceptionally((ex) -> { 2202 Rlog.w(TAG, "maybeRejectIncomingCall - exceptionally= " + ex); 2203 return null; 2204 }); 2205 } catch (Exception e) { 2206 Rlog.w(TAG, "maybeRejectIncomingCall - exception= " + e.getMessage()); 2207 if (completeConsumer != null) { 2208 completeConsumer.accept(false); 2209 } 2210 } 2211 } 2212 registerForNewRingingConnection()2213 private void registerForNewRingingConnection() { 2214 Phone[] phones = mPhoneFactoryProxy.getPhones(); 2215 if (phones == null) { 2216 // unit testing 2217 return; 2218 } 2219 for (Phone phone : phones) { 2220 phone.registerForNewRingingConnection(mHandler, MSG_NEW_RINGING_CONNECTION, 2221 mRegistrantidentifier); 2222 } 2223 } 2224 2225 /** 2226 * Hangup the new ringing call if there is an ongoing emergency call not connected. 2227 */ handleNewRingingConnection(Message msg)2228 private void handleNewRingingConnection(Message msg) { 2229 Connection c = (Connection) ((AsyncResult) msg.obj).result; 2230 if (c == null) return; 2231 if ((mNormalRoutingEmergencyConnection == null 2232 || mNormalRoutingEmergencyConnection.getState() == STATE_ACTIVE 2233 || mNormalRoutingEmergencyConnection.getState() == STATE_DISCONNECTED) 2234 && (mOngoingConnection == null 2235 || mOngoingConnection.getState() == STATE_ACTIVE 2236 || mOngoingConnection.getState() == STATE_DISCONNECTED)) { 2237 return; 2238 } 2239 if ((c.getPhoneType() == PhoneConstants.PHONE_TYPE_IMS) 2240 && ((ImsPhoneConnection) c).isIncomingCallAutoRejected()) { 2241 Rlog.i(TAG, "handleNewRingingConnection auto rejected call"); 2242 } else { 2243 try { 2244 Rlog.i(TAG, "handleNewRingingConnection silently drop incoming call"); 2245 c.getCall().hangup(); 2246 } catch (CallStateException e) { 2247 Rlog.w(TAG, "handleNewRingingConnection", e); 2248 } 2249 } 2250 } 2251 2252 /** 2253 * Indicates the start of a normal routing emergency call. 2254 * 2255 * <p> 2256 * Handles turning on radio and switching DDS. 2257 * 2258 * @param phone the {@code Phone} on which to process the emergency call. 2259 * @param c the {@code Connection} on which to process the emergency call. 2260 * @param completeConsumer The consumer to call once rejecting incoming call completes, 2261 * provides {@code true} result if operation completes successfully 2262 * or {@code false} if the operation timed out/failed. 2263 */ startNormalRoutingEmergencyCall(@onNull Phone phone, @NonNull android.telecom.Connection c, @NonNull Consumer<Boolean> completeConsumer)2264 public void startNormalRoutingEmergencyCall(@NonNull Phone phone, 2265 @NonNull android.telecom.Connection c, @NonNull Consumer<Boolean> completeConsumer) { 2266 Rlog.i(TAG, "startNormalRoutingEmergencyCall: phoneId=" + phone.getPhoneId() 2267 + ", callId=" + c.getTelecomCallId()); 2268 2269 mNormalRoutingEmergencyConnection = c; 2270 maybeRejectIncomingCall(completeConsumer); 2271 } 2272 2273 /** 2274 * Indicates the termination of a normal routing emergency call. 2275 * 2276 * @param c the normal routing emergency call disconnected. 2277 */ endNormalRoutingEmergencyCall(@onNull android.telecom.Connection c)2278 public void endNormalRoutingEmergencyCall(@NonNull android.telecom.Connection c) { 2279 if (c != mNormalRoutingEmergencyConnection) return; 2280 Rlog.i(TAG, "endNormalRoutingEmergencyCall: callId=" + c.getTelecomCallId()); 2281 mNormalRoutingEmergencyConnection = null; 2282 } 2283 2284 /** 2285 * Handles the normal routing emergency call state change. 2286 * 2287 * @param c the call whose state has changed 2288 * @param state the new call state 2289 */ onNormalRoutingEmergencyCallStateChanged(android.telecom.Connection c, @android.telecom.Connection.ConnectionState int state)2290 public void onNormalRoutingEmergencyCallStateChanged(android.telecom.Connection c, 2291 @android.telecom.Connection.ConnectionState int state) { 2292 if (c != mNormalRoutingEmergencyConnection) return; 2293 2294 // If the call is connected, we don't need to monitor incoming call any more. 2295 if (state == android.telecom.Connection.STATE_ACTIVE 2296 || state == android.telecom.Connection.STATE_DISCONNECTED) { 2297 endNormalRoutingEmergencyCall(c); 2298 } 2299 } 2300 2301 /** 2302 * Determines whether switching stacks is needed or not. 2303 * 2304 * @param phone the {@code Phone} on which to process the emergency call. 2305 * @return true if switching stacks is needed. 2306 */ 2307 @VisibleForTesting needToSwitchPhone(Phone phone)2308 public boolean needToSwitchPhone(Phone phone) { 2309 int subId = phone.getSubId(); 2310 int phoneId = phone.getPhoneId(); 2311 2312 if (isSimReady(phoneId, subId)) return false; 2313 2314 boolean switchPhone = false; 2315 if (!SubscriptionManager.isValidSubscriptionId(subId)) { 2316 Rlog.i(TAG, "needToSwitchPhone SIM absent"); 2317 if (phoneId != 0 || isThereOtherPhone(phoneId, true)) { 2318 // Prefer default Phone or other Phone with a SIM regardless of lock state. 2319 switchPhone = true; 2320 } 2321 } else { 2322 Rlog.i(TAG, "needToSwitchPhone SIM not ready"); 2323 if ((phoneId == 0 && isThereOtherPhone(phoneId, false)) 2324 || (phoneId != 0 && isThereOtherPhone(phoneId, true))) { 2325 // If there is another one with a SIM ready, switch Phones. 2326 // Otherwise, prefer default Phone if both SIMs are locked. 2327 switchPhone = true; 2328 } 2329 } 2330 Rlog.i(TAG, "needToSwitchPhone " + switchPhone); 2331 return switchPhone; 2332 } 2333 isThereOtherPhone(int skipPhoneId, boolean ignoreLockState)2334 private boolean isThereOtherPhone(int skipPhoneId, boolean ignoreLockState) { 2335 for (Phone phone : mPhoneFactoryProxy.getPhones()) { 2336 int phoneId = phone.getPhoneId(); 2337 if (phoneId == skipPhoneId) { 2338 continue; 2339 } 2340 2341 int subId = phone.getSubId(); 2342 if (!SubscriptionManager.isValidSubscriptionId(subId)) { 2343 Rlog.i(TAG, "isThereOtherPhone phoneId=" + phoneId + ", subId=" + subId); 2344 continue; 2345 } 2346 int simState = mTelephonyManagerProxy.getSimState(phoneId); 2347 if ((simState == TelephonyManager.SIM_STATE_READY) || (ignoreLockState 2348 && simState != TelephonyManager.SIM_STATE_ABSENT 2349 && simState != TelephonyManager.SIM_STATE_NOT_READY)) { 2350 Rlog.i(TAG, "isThereOtherPhone found, ignoreLockState=" + ignoreLockState 2351 + ", phoneId=" + phoneId + ", simState=" + simState); 2352 return true; 2353 } 2354 Rlog.i(TAG, "isThereOtherPhone phoneId=" + phoneId + ", simState=" + simState); 2355 } 2356 2357 return false; 2358 } 2359 2360 /** 2361 * Checks whether the satellite mode should be turned off to proceed with an emergency call 2362 * when satellite mode is enabled or an NTN(Non Terrestrial Network) session is in progress. 2363 * 2364 * @return {@code true} if satellite mode should be exited before an emergency call is being 2365 * processed, {@code false} otherwise. 2366 */ 2367 @VisibleForTesting shouldExitSatelliteMode()2368 public boolean shouldExitSatelliteMode() { 2369 final SatelliteController satelliteController = SatelliteController.getInstance(); 2370 2371 if (!satelliteController.isSatelliteEnabledOrBeingEnabled()) { 2372 return false; 2373 } 2374 2375 if (!mTurnOffNonEmergencyNbIotNtnSatelliteForEmergencyCall) { 2376 // Carrier 2377 return false; 2378 } 2379 2380 if (satelliteController.isDemoModeEnabled()) { 2381 // If user makes emergency call in demo mode, end the satellite session 2382 return true; 2383 } else if (mFeatureFlags.carrierRoamingNbIotNtn() 2384 && !satelliteController.getRequestIsEmergency()) { 2385 // If satellite is not for emergency, end the satellite session 2386 return true; 2387 } else { // satellite is for emergency 2388 if (mFeatureFlags.carrierRoamingNbIotNtn()) { 2389 int subId = satelliteController.getSelectedSatelliteSubId(); 2390 SubscriptionInfoInternal info = SubscriptionManagerService.getInstance() 2391 .getSubscriptionInfoInternal(subId); 2392 if (info == null) { 2393 Rlog.e(TAG, "satellite is/being enabled, but satellite sub " 2394 + subId + " is null"); 2395 return false; 2396 } 2397 2398 if (info.getOnlyNonTerrestrialNetwork() == 1) { 2399 // OEM 2400 return mTurnOffOemEnabledSatelliteDuringEmergencyCall; 2401 } else { 2402 // Carrier 2403 return satelliteController.shouldTurnOffCarrierSatelliteForEmergencyCall(); 2404 } 2405 } else { 2406 return mTurnOffOemEnabledSatelliteDuringEmergencyCall; 2407 } 2408 } 2409 } 2410 } 2411