• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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