1 /*
2  * Copyright (C) 2014 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.services.telephony;
18 
19 import android.annotation.NonNull;
20 import android.content.ActivityNotFoundException;
21 import android.content.BroadcastReceiver;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.net.Uri;
27 import android.os.Bundle;
28 import android.os.Handler;
29 import android.os.HandlerThread;
30 import android.os.Looper;
31 import android.telecom.Conference;
32 import android.telecom.Connection;
33 import android.telecom.ConnectionRequest;
34 import android.telecom.ConnectionService;
35 import android.telecom.DisconnectCause;
36 import android.telecom.PhoneAccount;
37 import android.telecom.PhoneAccountHandle;
38 import android.telecom.TelecomManager;
39 import android.telecom.VideoProfile;
40 import android.telephony.CarrierConfigManager;
41 import android.telephony.PhoneNumberUtils;
42 import android.telephony.RadioAccessFamily;
43 import android.telephony.ServiceState;
44 import android.telephony.SubscriptionManager;
45 import android.telephony.TelephonyManager;
46 import android.telephony.emergency.EmergencyNumber;
47 import android.text.TextUtils;
48 import android.util.Pair;
49 
50 import com.android.internal.annotations.VisibleForTesting;
51 import com.android.internal.telephony.Call;
52 import com.android.internal.telephony.CallStateException;
53 import com.android.internal.telephony.GsmCdmaPhone;
54 import com.android.internal.telephony.IccCard;
55 import com.android.internal.telephony.IccCardConstants;
56 import com.android.internal.telephony.Phone;
57 import com.android.internal.telephony.PhoneConstants;
58 import com.android.internal.telephony.PhoneFactory;
59 import com.android.internal.telephony.PhoneSwitcher;
60 import com.android.internal.telephony.RIL;
61 import com.android.internal.telephony.SubscriptionController;
62 import com.android.internal.telephony.imsphone.ImsExternalCallTracker;
63 import com.android.internal.telephony.imsphone.ImsPhone;
64 import com.android.internal.telephony.imsphone.ImsPhoneConnection;
65 import com.android.phone.MMIDialogActivity;
66 import com.android.phone.PhoneUtils;
67 import com.android.phone.R;
68 
69 import java.lang.ref.WeakReference;
70 import java.util.ArrayList;
71 import java.util.Arrays;
72 import java.util.Collection;
73 import java.util.Collections;
74 import java.util.HashMap;
75 import java.util.LinkedList;
76 import java.util.List;
77 import java.util.Map;
78 import java.util.Queue;
79 import java.util.concurrent.CompletableFuture;
80 import java.util.concurrent.TimeUnit;
81 import java.util.regex.Pattern;
82 
83 import javax.annotation.Nullable;
84 
85 /**
86  * Service for making GSM and CDMA connections.
87  */
88 public class TelephonyConnectionService extends ConnectionService {
89 
90     // Timeout before we continue with the emergency call without waiting for DDS switch response
91     // from the modem.
92     private static final int DEFAULT_DATA_SWITCH_TIMEOUT_MS = 1000;
93 
94     // If configured, reject attempts to dial numbers matching this pattern.
95     private static final Pattern CDMA_ACTIVATION_CODE_REGEX_PATTERN =
96             Pattern.compile("\\*228[0-9]{0,2}");
97 
98     private final TelephonyConnectionServiceProxy mTelephonyConnectionServiceProxy =
99             new TelephonyConnectionServiceProxy() {
100         @Override
101         public Collection<Connection> getAllConnections() {
102             return TelephonyConnectionService.this.getAllConnections();
103         }
104         @Override
105         public void addConference(TelephonyConference mTelephonyConference) {
106             TelephonyConnectionService.this.addTelephonyConference(mTelephonyConference);
107         }
108         @Override
109         public void addConference(ImsConference mImsConference) {
110             TelephonyConnectionService.this.addTelephonyConference(mImsConference);
111         }
112         @Override
113         public void addExistingConnection(PhoneAccountHandle phoneAccountHandle,
114                                           Connection connection) {
115             TelephonyConnectionService.this
116                     .addExistingConnection(phoneAccountHandle, connection);
117         }
118         @Override
119         public void addExistingConnection(PhoneAccountHandle phoneAccountHandle,
120                 Connection connection, Conference conference) {
121             TelephonyConnectionService.this
122                     .addExistingConnection(phoneAccountHandle, connection, conference);
123         }
124         @Override
125         public void addConnectionToConferenceController(TelephonyConnection connection) {
126             TelephonyConnectionService.this.addConnectionToConferenceController(connection);
127         }
128     };
129 
130     private final BroadcastReceiver mTtyBroadcastReceiver = new BroadcastReceiver() {
131         @Override
132         public void onReceive(Context context, Intent intent) {
133             String action = intent.getAction();
134             Log.v(this, "onReceive, action: %s", action);
135             if (action.equals(TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED)) {
136                 int newPreferredTtyMode = intent.getIntExtra(
137                         TelecomManager.EXTRA_TTY_PREFERRED_MODE, TelecomManager.TTY_MODE_OFF);
138 
139                 boolean isTtyNowEnabled = newPreferredTtyMode != TelecomManager.TTY_MODE_OFF;
140                 if (isTtyNowEnabled != mIsTtyEnabled) {
141                     handleTtyModeChange(isTtyNowEnabled);
142                 }
143             }
144         }
145     };
146 
147     private final TelephonyConferenceController mTelephonyConferenceController =
148             new TelephonyConferenceController(mTelephonyConnectionServiceProxy);
149     private final CdmaConferenceController mCdmaConferenceController =
150             new CdmaConferenceController(this);
151     private ImsConferenceController mImsConferenceController;
152 
153     private ComponentName mExpectedComponentName = null;
154     private RadioOnHelper mRadioOnHelper;
155     private EmergencyTonePlayer mEmergencyTonePlayer;
156     private HoldTracker mHoldTracker;
157     private boolean mIsTtyEnabled;
158 
159     // Contains one TelephonyConnection that has placed a call and a memory of which Phones it has
160     // already tried to connect with. There should be only one TelephonyConnection trying to place a
161     // call at one time. We also only access this cache from a TelephonyConnection that wishes to
162     // redial, so we use a WeakReference that will become stale once the TelephonyConnection is
163     // destroyed.
164     @VisibleForTesting
165     public Pair<WeakReference<TelephonyConnection>, Queue<Phone>> mEmergencyRetryCache;
166     private Handler mDdsSwitchHandler;
167     private HandlerThread mHandlerThread;
168     private DeviceState mDeviceState = new DeviceState();
169 
170     /**
171      * Keeps track of the status of a SIM slot.
172      */
173     private static class SlotStatus {
174         public int slotId;
175         // RAT capabilities
176         public int capabilities;
177         // By default, we will assume that the slots are not locked.
178         public boolean isLocked = false;
179         // Is the emergency number associated with the slot
180         public boolean hasDialedEmergencyNumber = false;
181         //SimState
182         public int simState;
183 
SlotStatus(int slotId, int capabilities)184         public SlotStatus(int slotId, int capabilities) {
185             this.slotId = slotId;
186             this.capabilities = capabilities;
187         }
188     }
189 
190     /**
191      * SubscriptionManager dependencies for testing.
192      */
193     @VisibleForTesting
194     public interface SubscriptionManagerProxy {
getDefaultVoicePhoneId()195         int getDefaultVoicePhoneId();
getSimStateForSlotIdx(int slotId)196         int getSimStateForSlotIdx(int slotId);
getPhoneId(int subId)197         int getPhoneId(int subId);
198     }
199 
200     private SubscriptionManagerProxy mSubscriptionManagerProxy = new SubscriptionManagerProxy() {
201         @Override
202         public int getDefaultVoicePhoneId() {
203             return SubscriptionManager.getDefaultVoicePhoneId();
204         }
205 
206         @Override
207         public int getSimStateForSlotIdx(int slotId) {
208             return SubscriptionManager.getSimStateForSlotIndex(slotId);
209         }
210 
211         @Override
212         public int getPhoneId(int subId) {
213             return SubscriptionManager.getPhoneId(subId);
214         }
215     };
216 
217     /**
218      * TelephonyManager dependencies for testing.
219      */
220     @VisibleForTesting
221     public interface TelephonyManagerProxy {
getPhoneCount()222         int getPhoneCount();
hasIccCard(int slotId)223         boolean hasIccCard(int slotId);
isCurrentEmergencyNumber(String number)224         boolean isCurrentEmergencyNumber(String number);
getCurrentEmergencyNumberList()225         Map<Integer, List<EmergencyNumber>> getCurrentEmergencyNumberList();
226     }
227 
228     private TelephonyManagerProxy mTelephonyManagerProxy;
229 
230     private class TelephonyManagerProxyImpl implements TelephonyManagerProxy {
231         private final TelephonyManager mTelephonyManager;
232 
233 
TelephonyManagerProxyImpl(Context context)234         TelephonyManagerProxyImpl(Context context) {
235             mTelephonyManager = new TelephonyManager(context);
236         }
237 
238         @Override
getPhoneCount()239         public int getPhoneCount() {
240             return mTelephonyManager.getPhoneCount();
241         }
242 
243         @Override
hasIccCard(int slotId)244         public boolean hasIccCard(int slotId) {
245             return mTelephonyManager.hasIccCard(slotId);
246         }
247 
248         @Override
isCurrentEmergencyNumber(String number)249         public boolean isCurrentEmergencyNumber(String number) {
250             try {
251                 return mTelephonyManager.isEmergencyNumber(number);
252             } catch (IllegalStateException ise) {
253                 return false;
254             }
255         }
256 
257         @Override
getCurrentEmergencyNumberList()258         public Map<Integer, List<EmergencyNumber>> getCurrentEmergencyNumberList() {
259             try {
260                 return mTelephonyManager.getEmergencyNumberList();
261             } catch (IllegalStateException ise) {
262                 return new HashMap<>();
263             }
264         }
265     }
266 
267     /**
268      * PhoneFactory Dependencies for testing.
269      */
270     @VisibleForTesting
271     public interface PhoneFactoryProxy {
getPhone(int index)272         Phone getPhone(int index);
getDefaultPhone()273         Phone getDefaultPhone();
getPhones()274         Phone[] getPhones();
275     }
276 
277     private PhoneFactoryProxy mPhoneFactoryProxy = new PhoneFactoryProxy() {
278         @Override
279         public Phone getPhone(int index) {
280             return PhoneFactory.getPhone(index);
281         }
282 
283         @Override
284         public Phone getDefaultPhone() {
285             return PhoneFactory.getDefaultPhone();
286         }
287 
288         @Override
289         public Phone[] getPhones() {
290             return PhoneFactory.getPhones();
291         }
292     };
293 
294     /**
295      * PhoneUtils dependencies for testing.
296      */
297     @VisibleForTesting
298     public interface PhoneUtilsProxy {
getSubIdForPhoneAccountHandle(PhoneAccountHandle accountHandle)299         int getSubIdForPhoneAccountHandle(PhoneAccountHandle accountHandle);
makePstnPhoneAccountHandle(Phone phone)300         PhoneAccountHandle makePstnPhoneAccountHandle(Phone phone);
makePstnPhoneAccountHandleWithPrefix(Phone phone, String prefix, boolean isEmergency)301         PhoneAccountHandle makePstnPhoneAccountHandleWithPrefix(Phone phone, String prefix,
302                 boolean isEmergency);
303     }
304 
305     private PhoneUtilsProxy mPhoneUtilsProxy = new PhoneUtilsProxy() {
306         @Override
307         public int getSubIdForPhoneAccountHandle(PhoneAccountHandle accountHandle) {
308             return PhoneUtils.getSubIdForPhoneAccountHandle(accountHandle);
309         }
310 
311         @Override
312         public PhoneAccountHandle makePstnPhoneAccountHandle(Phone phone) {
313             return PhoneUtils.makePstnPhoneAccountHandle(phone);
314         }
315 
316         @Override
317         public PhoneAccountHandle makePstnPhoneAccountHandleWithPrefix(Phone phone, String prefix,
318                 boolean isEmergency) {
319             return PhoneUtils.makePstnPhoneAccountHandleWithPrefix(phone, prefix, isEmergency);
320         }
321     };
322 
323     /**
324      * PhoneNumberUtils dependencies for testing.
325      */
326     @VisibleForTesting
327     public interface PhoneNumberUtilsProxy {
convertToEmergencyNumber(Context context, String number)328         String convertToEmergencyNumber(Context context, String number);
329     }
330 
331     private PhoneNumberUtilsProxy mPhoneNumberUtilsProxy = new PhoneNumberUtilsProxy() {
332         @Override
333         public String convertToEmergencyNumber(Context context, String number) {
334             return PhoneNumberUtils.convertToEmergencyNumber(context, number);
335         }
336     };
337 
338     /**
339      * PhoneSwitcher dependencies for testing.
340      */
341     @VisibleForTesting
342     public interface PhoneSwitcherProxy {
getPhoneSwitcher()343         PhoneSwitcher getPhoneSwitcher();
344     }
345 
346     private PhoneSwitcherProxy mPhoneSwitcherProxy = new PhoneSwitcherProxy() {
347         @Override
348         public PhoneSwitcher getPhoneSwitcher() {
349             return PhoneSwitcher.getInstance();
350         }
351     };
352 
353     /**
354      * Factory for Handler creation in order to remove flakiness during t esting.
355      */
356     @VisibleForTesting
357     public interface HandlerFactory {
createHandlerThread(String name)358         HandlerThread createHandlerThread(String name);
createHandler(Looper looper)359         Handler createHandler(Looper looper);
360     }
361 
362     private HandlerFactory mHandlerFactory = new HandlerFactory() {
363         @Override
364         public HandlerThread createHandlerThread(String name) {
365             return new HandlerThread(name);
366         }
367 
368         @Override
369         public Handler createHandler(Looper looper) {
370             return new Handler(looper);
371         }
372     };
373 
374     /**
375      * DisconnectCause depends on PhoneGlobals in order to get a system context. Mock out
376      * dependency for testing.
377      */
378     @VisibleForTesting
379     public interface DisconnectCauseFactory {
toTelecomDisconnectCause(int telephonyDisconnectCause, String reason)380         DisconnectCause toTelecomDisconnectCause(int telephonyDisconnectCause, String reason);
toTelecomDisconnectCause(int telephonyDisconnectCause, String reason, int phoneId)381         DisconnectCause toTelecomDisconnectCause(int telephonyDisconnectCause,
382                 String reason, int phoneId);
383     }
384 
385     private DisconnectCauseFactory mDisconnectCauseFactory = new DisconnectCauseFactory() {
386         @Override
387         public DisconnectCause toTelecomDisconnectCause(int telephonyDisconnectCause,
388                 String reason) {
389             return DisconnectCauseUtil.toTelecomDisconnectCause(telephonyDisconnectCause, reason);
390         }
391 
392         @Override
393         public DisconnectCause toTelecomDisconnectCause(int telephonyDisconnectCause, String reason,
394                 int phoneId) {
395             return DisconnectCauseUtil.toTelecomDisconnectCause(telephonyDisconnectCause, reason,
396                     phoneId);
397         }
398     };
399 
400     /**
401      * Overrides SubscriptionManager dependencies for testing.
402      */
403     @VisibleForTesting
setSubscriptionManagerProxy(SubscriptionManagerProxy proxy)404     public void setSubscriptionManagerProxy(SubscriptionManagerProxy proxy) {
405         mSubscriptionManagerProxy = proxy;
406     }
407 
408     /**
409      * Overrides TelephonyManager dependencies for testing.
410      */
411     @VisibleForTesting
setTelephonyManagerProxy(TelephonyManagerProxy proxy)412     public void setTelephonyManagerProxy(TelephonyManagerProxy proxy) {
413         mTelephonyManagerProxy = proxy;
414     }
415 
416     /**
417      * Overrides PhoneFactory dependencies for testing.
418      */
419     @VisibleForTesting
setPhoneFactoryProxy(PhoneFactoryProxy proxy)420     public void setPhoneFactoryProxy(PhoneFactoryProxy proxy) {
421         mPhoneFactoryProxy = proxy;
422     }
423 
424     /**
425      * Overrides configuration and settings dependencies for testing.
426      */
427     @VisibleForTesting
setDeviceState(DeviceState state)428     public void setDeviceState(DeviceState state) {
429         mDeviceState = state;
430     }
431 
432     /**
433      * Overrides radioOnHelper for testing.
434      */
435     @VisibleForTesting
setRadioOnHelper(RadioOnHelper radioOnHelper)436     public void setRadioOnHelper(RadioOnHelper radioOnHelper) {
437         mRadioOnHelper = radioOnHelper;
438     }
439 
440     /**
441      * Overrides PhoneSwitcher dependencies for testing.
442      */
443     @VisibleForTesting
setPhoneSwitcherProxy(PhoneSwitcherProxy proxy)444     public void setPhoneSwitcherProxy(PhoneSwitcherProxy proxy) {
445         mPhoneSwitcherProxy = proxy;
446     }
447 
448     /**
449      * Overrides PhoneNumberUtils dependencies for testing.
450      */
451     @VisibleForTesting
setPhoneNumberUtilsProxy(PhoneNumberUtilsProxy proxy)452     public void setPhoneNumberUtilsProxy(PhoneNumberUtilsProxy proxy) {
453         mPhoneNumberUtilsProxy = proxy;
454     }
455 
456     /**
457      * Overrides PhoneUtils dependencies for testing.
458      */
459     @VisibleForTesting
setPhoneUtilsProxy(PhoneUtilsProxy proxy)460     public void setPhoneUtilsProxy(PhoneUtilsProxy proxy) {
461         mPhoneUtilsProxy = proxy;
462     }
463 
464     /**
465      * Override Handler creation factory for testing.
466      */
467     @VisibleForTesting
setHandlerFactory(HandlerFactory handlerFactory)468     public void setHandlerFactory(HandlerFactory handlerFactory) {
469         mHandlerFactory = handlerFactory;
470     }
471 
472     /**
473      * Override DisconnectCause creation for testing.
474      */
475     @VisibleForTesting
setDisconnectCauseFactory(DisconnectCauseFactory factory)476     public void setDisconnectCauseFactory(DisconnectCauseFactory factory) {
477         mDisconnectCauseFactory = factory;
478     }
479 
480     /**
481      * A listener to actionable events specific to the TelephonyConnection.
482      */
483     private final TelephonyConnection.TelephonyConnectionListener mTelephonyConnectionListener =
484             new TelephonyConnection.TelephonyConnectionListener() {
485         @Override
486         public void onOriginalConnectionConfigured(TelephonyConnection c) {
487             addConnectionToConferenceController(c);
488         }
489 
490         @Override
491         public void onOriginalConnectionRetry(TelephonyConnection c, boolean isPermanentFailure) {
492             retryOutgoingOriginalConnection(c, isPermanentFailure);
493         }
494     };
495 
496     private final TelephonyConferenceBase.TelephonyConferenceListener mTelephonyConferenceListener =
497             new TelephonyConferenceBase.TelephonyConferenceListener() {
498         @Override
499         public void onConferenceMembershipChanged(Connection connection) {
500             mHoldTracker.updateHoldCapability(connection.getPhoneAccountHandle());
501         }
502     };
503 
504     @Override
onCreate()505     public void onCreate() {
506         super.onCreate();
507         mImsConferenceController = new ImsConferenceController(
508                 TelecomAccountRegistry.getInstance(this),
509                 mTelephonyConnectionServiceProxy,
510                 // FeatureFlagProxy; used to determine if standalone call emulation is enabled.
511                 // TODO: Move to carrier config
512                 () -> true);
513         setTelephonyManagerProxy(new TelephonyManagerProxyImpl(getApplicationContext()));
514         mExpectedComponentName = new ComponentName(this, this.getClass());
515         mEmergencyTonePlayer = new EmergencyTonePlayer(this);
516         TelecomAccountRegistry.getInstance(this).setTelephonyConnectionService(this);
517         mHoldTracker = new HoldTracker();
518         mIsTtyEnabled = mDeviceState.isTtyModeEnabled(this);
519 
520         IntentFilter intentFilter = new IntentFilter(
521                 TelecomManager.ACTION_TTY_PREFERRED_MODE_CHANGED);
522         registerReceiver(mTtyBroadcastReceiver, intentFilter);
523         mHandlerThread = mHandlerFactory.createHandlerThread("DdsSwitchHandlerThread");
524         mHandlerThread.start();
525         Looper looper = mHandlerThread.getLooper();
526         mDdsSwitchHandler = mHandlerFactory.createHandler(looper);
527     }
528 
529     @Override
onUnbind(Intent intent)530     public boolean onUnbind(Intent intent) {
531         unregisterReceiver(mTtyBroadcastReceiver);
532         mHandlerThread.quitSafely();
533         return super.onUnbind(intent);
534     }
535 
placeOutgoingConference(ConnectionRequest request, Connection resultConnection, Phone phone)536     private Conference placeOutgoingConference(ConnectionRequest request,
537             Connection resultConnection, Phone phone) {
538         if (resultConnection instanceof TelephonyConnection) {
539             return placeOutgoingConference((TelephonyConnection) resultConnection, phone, request);
540         }
541         return null;
542     }
543 
placeOutgoingConference(TelephonyConnection conferenceHostConnection, Phone phone, ConnectionRequest request)544     private Conference placeOutgoingConference(TelephonyConnection conferenceHostConnection,
545             Phone phone, ConnectionRequest request) {
546         updatePhoneAccount(conferenceHostConnection, phone);
547         com.android.internal.telephony.Connection originalConnection = null;
548         try {
549             originalConnection = phone.startConference(
550                     getParticipantsToDial(request.getParticipants()),
551                     new ImsPhone.ImsDialArgs.Builder()
552                     .setVideoState(request.getVideoState())
553                     .setRttTextStream(conferenceHostConnection.getRttTextStream())
554                     .build());
555         } catch (CallStateException e) {
556             Log.e(this, e, "placeOutgoingConference, phone.startConference exception: " + e);
557             handleCallStateException(e, conferenceHostConnection, phone);
558             return null;
559         }
560 
561         if (originalConnection == null) {
562             Log.d(this, "placeOutgoingConference, phone.startConference returned null");
563             conferenceHostConnection.setDisconnected(DisconnectCauseUtil.toTelecomDisconnectCause(
564                     android.telephony.DisconnectCause.OUTGOING_FAILURE,
565                     "conferenceHostConnection is null",
566                     phone.getPhoneId()));
567             conferenceHostConnection.clearOriginalConnection();
568             conferenceHostConnection.destroy();
569         } else {
570             conferenceHostConnection.setOriginalConnection(originalConnection);
571         }
572 
573         return prepareConference(conferenceHostConnection, request.getAccountHandle());
574     }
575 
prepareConference(Connection conn, PhoneAccountHandle phoneAccountHandle)576     Conference prepareConference(Connection conn, PhoneAccountHandle phoneAccountHandle) {
577         if (!(conn instanceof TelephonyConnection)) {
578             Log.w(this, "prepareConference returning NULL conference");
579             return null;
580         }
581 
582         TelephonyConnection connection = (TelephonyConnection)conn;
583 
584         ImsConference conference = new ImsConference(TelecomAccountRegistry.getInstance(this),
585                 mTelephonyConnectionServiceProxy, connection,
586                 phoneAccountHandle, () -> true,
587                 ImsConferenceController.getCarrierConfig(connection.getPhone()));
588         mImsConferenceController.addConference(conference);
589         conference.setVideoState(connection,
590                 connection.getVideoState());
591         conference.setVideoProvider(connection,
592                 connection.getVideoProvider());
593         conference.setStatusHints(connection.getStatusHints());
594         conference.setAddress(connection.getAddress(),
595                 connection.getAddressPresentation());
596         conference.setCallerDisplayName(connection.getCallerDisplayName(),
597                 connection.getCallerDisplayNamePresentation());
598         conference.setParticipants(connection.getParticipants());
599         return conference;
600     }
601 
602     @Override
onCreateIncomingConference( @ullable PhoneAccountHandle connectionManagerPhoneAccount, @NonNull final ConnectionRequest request)603     public @Nullable Conference onCreateIncomingConference(
604             @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
605             @NonNull final ConnectionRequest request) {
606         Log.i(this, "onCreateIncomingConference, request: " + request);
607         Connection connection = onCreateIncomingConnection(connectionManagerPhoneAccount, request);
608         Log.d(this, "onCreateIncomingConference, connection: %s", connection);
609         if (connection == null) {
610             Log.i(this, "onCreateIncomingConference, implementation returned null connection.");
611             return Conference.createFailedConference(
612                     new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONNECTION"),
613                     request.getAccountHandle());
614         }
615 
616         final Phone phone = getPhoneForAccount(request.getAccountHandle(),
617                 false /* isEmergencyCall*/, null /* not an emergency call */);
618         if (phone == null) {
619             Log.d(this, "onCreateIncomingConference, phone is null");
620             return Conference.createFailedConference(
621                     DisconnectCauseUtil.toTelecomDisconnectCause(
622                             android.telephony.DisconnectCause.OUT_OF_SERVICE,
623                             "Phone is null"),
624                     request.getAccountHandle());
625         }
626 
627         return prepareConference(connection, request.getAccountHandle());
628     }
629 
630     @Override
onCreateOutgoingConference( @ullable PhoneAccountHandle connectionManagerPhoneAccount, @NonNull final ConnectionRequest request)631     public @Nullable Conference onCreateOutgoingConference(
632             @Nullable PhoneAccountHandle connectionManagerPhoneAccount,
633             @NonNull final ConnectionRequest request) {
634         Log.i(this, "onCreateOutgoingConference, request: " + request);
635         Connection connection = onCreateOutgoingConnection(connectionManagerPhoneAccount, request);
636         Log.d(this, "onCreateOutgoingConference, connection: %s", connection);
637         if (connection == null) {
638             Log.i(this, "onCreateOutgoingConference, implementation returned null connection.");
639             return Conference.createFailedConference(
640                     new DisconnectCause(DisconnectCause.ERROR, "IMPL_RETURNED_NULL_CONNECTION"),
641                     request.getAccountHandle());
642         }
643 
644         final Phone phone = getPhoneForAccount(request.getAccountHandle(),
645                 false /* isEmergencyCall*/, null /* not an emergency call */);
646         if (phone == null) {
647             Log.d(this, "onCreateOutgoingConference, phone is null");
648             return Conference.createFailedConference(
649                     DisconnectCauseUtil.toTelecomDisconnectCause(
650                             android.telephony.DisconnectCause.OUT_OF_SERVICE,
651                             "Phone is null"),
652                     request.getAccountHandle());
653         }
654 
655         return placeOutgoingConference(request, connection, phone);
656     }
657 
getParticipantsToDial(List<Uri> participants)658     private String[] getParticipantsToDial(List<Uri> participants) {
659         String[] participantsToDial = new String[participants.size()];
660         int i = 0;
661         for (Uri participant : participants) {
662            participantsToDial[i] = participant.getSchemeSpecificPart();
663            i++;
664         }
665         return participantsToDial;
666     }
667 
668     @Override
onCreateOutgoingConnection( PhoneAccountHandle connectionManagerPhoneAccount, final ConnectionRequest request)669     public Connection onCreateOutgoingConnection(
670             PhoneAccountHandle connectionManagerPhoneAccount,
671             final ConnectionRequest request) {
672         Log.i(this, "onCreateOutgoingConnection, request: " + request);
673 
674         Uri handle = request.getAddress();
675         boolean isAdhocConference = request.isAdhocConferenceCall();
676 
677         if (!isAdhocConference && handle == null) {
678             Log.d(this, "onCreateOutgoingConnection, handle is null");
679             return Connection.createFailedConnection(
680                     mDisconnectCauseFactory.toTelecomDisconnectCause(
681                             android.telephony.DisconnectCause.NO_PHONE_NUMBER_SUPPLIED,
682                             "No phone number supplied"));
683         }
684 
685         String scheme = handle.getScheme();
686         String number;
687         if (PhoneAccount.SCHEME_VOICEMAIL.equals(scheme)) {
688             // TODO: We don't check for SecurityException here (requires
689             // CALL_PRIVILEGED permission).
690             final Phone phone = getPhoneForAccount(request.getAccountHandle(),
691                     false /* isEmergencyCall */, null /* not an emergency call */);
692             if (phone == null) {
693                 Log.d(this, "onCreateOutgoingConnection, phone is null");
694                 return Connection.createFailedConnection(
695                         mDisconnectCauseFactory.toTelecomDisconnectCause(
696                                 android.telephony.DisconnectCause.OUT_OF_SERVICE,
697                                 "Phone is null"));
698             }
699             number = phone.getVoiceMailNumber();
700             if (TextUtils.isEmpty(number)) {
701                 Log.d(this, "onCreateOutgoingConnection, no voicemail number set.");
702                 return Connection.createFailedConnection(
703                         mDisconnectCauseFactory.toTelecomDisconnectCause(
704                                 android.telephony.DisconnectCause.VOICEMAIL_NUMBER_MISSING,
705                                 "Voicemail scheme provided but no voicemail number set.",
706                                 phone.getPhoneId()));
707             }
708 
709             // Convert voicemail: to tel:
710             handle = Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null);
711         } else {
712             if (!PhoneAccount.SCHEME_TEL.equals(scheme)) {
713                 Log.d(this, "onCreateOutgoingConnection, Handle %s is not type tel", scheme);
714                 return Connection.createFailedConnection(
715                         mDisconnectCauseFactory.toTelecomDisconnectCause(
716                                 android.telephony.DisconnectCause.INVALID_NUMBER,
717                                 "Handle scheme is not type tel"));
718             }
719 
720             number = handle.getSchemeSpecificPart();
721             if (TextUtils.isEmpty(number)) {
722                 Log.d(this, "onCreateOutgoingConnection, unable to parse number");
723                 return Connection.createFailedConnection(
724                         mDisconnectCauseFactory.toTelecomDisconnectCause(
725                                 android.telephony.DisconnectCause.INVALID_NUMBER,
726                                 "Unable to parse number"));
727             }
728 
729             final Phone phone = getPhoneForAccount(request.getAccountHandle(),
730                     false /* isEmergencyCall*/, null /* not an emergency call */);
731             if (phone != null && CDMA_ACTIVATION_CODE_REGEX_PATTERN.matcher(number).matches()) {
732                 // Obtain the configuration for the outgoing phone's SIM. If the outgoing number
733                 // matches the *228 regex pattern, fail the call. This number is used for OTASP, and
734                 // when dialed could lock LTE SIMs to 3G if not prohibited..
735                 boolean disableActivation = false;
736                 CarrierConfigManager cfgManager = (CarrierConfigManager)
737                         phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
738                 if (cfgManager != null) {
739                     disableActivation = cfgManager.getConfigForSubId(phone.getSubId())
740                             .getBoolean(CarrierConfigManager.KEY_DISABLE_CDMA_ACTIVATION_CODE_BOOL);
741                 }
742 
743                 if (disableActivation) {
744                     return Connection.createFailedConnection(
745                             mDisconnectCauseFactory.toTelecomDisconnectCause(
746                                     android.telephony.DisconnectCause
747                                             .CDMA_ALREADY_ACTIVATED,
748                                     "Tried to dial *228",
749                                     phone.getPhoneId()));
750                 }
751             }
752         }
753 
754         final boolean isEmergencyNumber = mTelephonyManagerProxy.isCurrentEmergencyNumber(number);
755         // Find out if this is a test emergency number
756         final boolean isTestEmergencyNumber = isEmergencyNumberTestNumber(number);
757 
758         // Convert into emergency number if necessary
759         // This is required in some regions (e.g. Taiwan).
760         if (isEmergencyNumber) {
761             final Phone phone = getPhoneForAccount(request.getAccountHandle(), false,
762                     handle.getSchemeSpecificPart());
763             // We only do the conversion if the phone is not in service. The un-converted
764             // emergency numbers will go to the correct destination when the phone is in-service,
765             // so they will only need the special emergency call setup when the phone is out of
766             // service.
767             if (phone == null || phone.getServiceState().getState()
768                     != ServiceState.STATE_IN_SERVICE) {
769                 String convertedNumber = mPhoneNumberUtilsProxy.convertToEmergencyNumber(this,
770                         number);
771                 if (!TextUtils.equals(convertedNumber, number)) {
772                     Log.i(this, "onCreateOutgoingConnection, converted to emergency number");
773                     number = convertedNumber;
774                     handle = Uri.fromParts(PhoneAccount.SCHEME_TEL, number, null);
775                 }
776             }
777         }
778         final String numberToDial = number;
779 
780         final boolean isAirplaneModeOn = mDeviceState.isAirplaneModeOn(this);
781 
782         boolean needToTurnOnRadio = (isEmergencyNumber && (!isRadioOn() || isAirplaneModeOn))
783                 || isRadioPowerDownOnBluetooth();
784 
785         // Get the right phone object from the account data passed in.
786         final Phone phone = getPhoneForAccount(request.getAccountHandle(), isEmergencyNumber,
787                 /* Note: when not an emergency, handle can be null for unknown callers */
788                 handle == null ? null : handle.getSchemeSpecificPart());
789 
790         if (needToTurnOnRadio) {
791             final Uri resultHandle = handle;
792             final int originalPhoneType = phone.getPhoneType();
793             final Connection resultConnection = getTelephonyConnection(request, numberToDial,
794                     isEmergencyNumber, resultHandle, phone);
795             if (mRadioOnHelper == null) {
796                 mRadioOnHelper = new RadioOnHelper(this);
797             }
798             mRadioOnHelper.triggerRadioOnAndListen(new RadioOnStateListener.Callback() {
799                 @Override
800                 public void onComplete(RadioOnStateListener listener, boolean isRadioReady) {
801                     handleOnComplete(isRadioReady, isEmergencyNumber, resultConnection, request,
802                             numberToDial, resultHandle, originalPhoneType, phone);
803                 }
804 
805                 @Override
806                 public boolean isOkToCall(Phone phone, int serviceState) {
807                     // HAL 1.4 introduced a new variant of dial for emergency calls, which includes
808                     // an isTesting parameter. For HAL 1.4+, do not wait for IN_SERVICE, this will
809                     // be handled at the RIL/vendor level by emergencyDial(...).
810                     boolean waitForInServiceToDialEmergency = isTestEmergencyNumber
811                             && phone.getHalVersion().less(RIL.RADIO_HAL_VERSION_1_4);
812                     if (isEmergencyNumber && !waitForInServiceToDialEmergency) {
813                         // We currently only look to make sure that the radio is on before dialing.
814                         // We should be able to make emergency calls at any time after the radio has
815                         // been powered on and isn't in the UNAVAILABLE state, even if it is
816                         // reporting the OUT_OF_SERVICE state.
817                         return (phone.getState() == PhoneConstants.State.OFFHOOK)
818                             || phone.getServiceStateTracker().isRadioOn();
819                     } else {
820                         // Wait until we are in service and ready to make calls. This can happen
821                         // when we power down the radio on bluetooth to save power on watches or if
822                         // it is a test emergency number and we have to wait for the device to move
823                         // IN_SERVICE before the call can take place over normal routing.
824                         return (phone.getState() == PhoneConstants.State.OFFHOOK)
825                                 // Do not wait for voice in service on opportunistic SIMs.
826                                 || SubscriptionController.getInstance().isOpportunistic(
827                                         phone.getSubId())
828                                 || serviceState == ServiceState.STATE_IN_SERVICE;
829                     }
830                 }
831             }, isEmergencyNumber && !isTestEmergencyNumber, phone);
832             // Return the still unconnected GsmConnection and wait for the Radios to boot before
833             // connecting it to the underlying Phone.
834             return resultConnection;
835         } else {
836             if (!canAddCall() && !isEmergencyNumber) {
837                 Log.d(this, "onCreateOutgoingConnection, cannot add call .");
838                 return Connection.createFailedConnection(
839                         new DisconnectCause(DisconnectCause.ERROR,
840                                 getApplicationContext().getText(
841                                         R.string.incall_error_cannot_add_call),
842                                 getApplicationContext().getText(
843                                         R.string.incall_error_cannot_add_call),
844                                 "Add call restricted due to ongoing video call"));
845             }
846 
847             if (!isEmergencyNumber) {
848                 final Connection resultConnection = getTelephonyConnection(request, numberToDial,
849                         false, handle, phone);
850                 if (isAdhocConference) {
851                     if (resultConnection instanceof TelephonyConnection) {
852                         TelephonyConnection conn = (TelephonyConnection)resultConnection;
853                         conn.setParticipants(request.getParticipants());
854                     }
855                     return resultConnection;
856                 } else {
857                     return placeOutgoingConnection(request, resultConnection, phone);
858                 }
859             } else {
860                 final Connection resultConnection = getTelephonyConnection(request, numberToDial,
861                         true, handle, phone);
862                 mDdsSwitchHandler.post(new Runnable() {
863                     @Override
864                     public void run() {
865                         boolean result = delayDialForDdsSwitch(phone);
866                         Log.i(this,
867                                 "onCreateOutgoingConn - delayDialForDdsSwitch result = " + result);
868                         placeOutgoingConnection(request, resultConnection, phone);
869                     }
870                 });
871                 return resultConnection;
872             }
873         }
874     }
875 
placeOutgoingConnection(ConnectionRequest request, Connection resultConnection, Phone phone)876     private Connection placeOutgoingConnection(ConnectionRequest request,
877             Connection resultConnection, Phone phone) {
878         // If there was a failure, the resulting connection will not be a TelephonyConnection,
879         // so don't place the call!
880         if (resultConnection instanceof TelephonyConnection) {
881             if (request.getExtras() != null && request.getExtras().getBoolean(
882                     TelecomManager.EXTRA_USE_ASSISTED_DIALING, false)) {
883                 ((TelephonyConnection) resultConnection).setIsUsingAssistedDialing(true);
884             }
885             placeOutgoingConnection((TelephonyConnection) resultConnection, phone, request);
886         }
887         return resultConnection;
888     }
889 
isEmergencyNumberTestNumber(String number)890     private boolean isEmergencyNumberTestNumber(String number) {
891         number = PhoneNumberUtils.stripSeparators(number);
892         Map<Integer, List<EmergencyNumber>> list =
893                 mTelephonyManagerProxy.getCurrentEmergencyNumberList();
894         // Do not worry about which subscription the test emergency call is on yet, only detect that
895         // it is an emergency.
896         for (Integer sub : list.keySet()) {
897             for (EmergencyNumber eNumber : list.get(sub)) {
898                 if (number.equals(eNumber.getNumber())
899                         && eNumber.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST)) {
900                     Log.i(this, "isEmergencyNumberTestNumber: " + number + " has been detected as "
901                             + "a test emergency number.,");
902                     return true;
903                 }
904             }
905         }
906         return false;
907     }
908 
909     /**
910      * Whether the cellular radio is power off because the device is on Bluetooth.
911      */
isRadioPowerDownOnBluetooth()912     private boolean isRadioPowerDownOnBluetooth() {
913         final boolean allowed = mDeviceState.isRadioPowerDownAllowedOnBluetooth(this);
914         final int cellOn = mDeviceState.getCellOnStatus(this);
915         return (allowed && cellOn == PhoneConstants.CELL_ON_FLAG && !isRadioOn());
916     }
917 
918     /**
919      * Handle the onComplete callback of RadioOnStateListener.
920      */
handleOnComplete(boolean isRadioReady, boolean isEmergencyNumber, Connection originalConnection, ConnectionRequest request, String numberToDial, Uri handle, int originalPhoneType, Phone phone)921     private void handleOnComplete(boolean isRadioReady, boolean isEmergencyNumber,
922             Connection originalConnection, ConnectionRequest request, String numberToDial,
923             Uri handle, int originalPhoneType, Phone phone) {
924         // Make sure the Call has not already been canceled by the user.
925         if (originalConnection.getState() == Connection.STATE_DISCONNECTED) {
926             Log.i(this, "Call disconnected before the outgoing call was placed. Skipping call "
927                     + "placement.");
928             if (isEmergencyNumber) {
929                 // If call is already canceled by the user, notify modem to exit emergency call
930                 // mode by sending radio on with forEmergencyCall=false.
931                 for (Phone curPhone : mPhoneFactoryProxy.getPhones()) {
932                     curPhone.setRadioPower(true, false, false, true);
933                 }
934             }
935             return;
936         }
937         if (isRadioReady) {
938             if (!isEmergencyNumber) {
939                 adjustAndPlaceOutgoingConnection(phone, originalConnection, request, numberToDial,
940                         handle, originalPhoneType, false);
941             } else {
942                 mDdsSwitchHandler.post(new Runnable() {
943                     @Override
944                     public void run() {
945                         boolean result = delayDialForDdsSwitch(phone);
946                         Log.i(this, "handleOnComplete - delayDialForDdsSwitch result = " + result);
947                         adjustAndPlaceOutgoingConnection(phone, originalConnection, request,
948                                 numberToDial, handle, originalPhoneType, true);
949                     }
950                 });
951             }
952 
953         } else {
954             Log.w(this, "onCreateOutgoingConnection, failed to turn on radio");
955             closeOrDestroyConnection(originalConnection,
956                     mDisconnectCauseFactory.toTelecomDisconnectCause(
957                             android.telephony.DisconnectCause.POWER_OFF,
958                             "Failed to turn on radio."));
959         }
960     }
961 
adjustAndPlaceOutgoingConnection(Phone phone, Connection connectionToEvaluate, ConnectionRequest request, String numberToDial, Uri handle, int originalPhoneType, boolean isEmergencyNumber)962     private void adjustAndPlaceOutgoingConnection(Phone phone, Connection connectionToEvaluate,
963             ConnectionRequest request, String numberToDial, Uri handle, int originalPhoneType,
964             boolean isEmergencyNumber) {
965         // If the PhoneType of the Phone being used is different than the Default Phone, then we
966         // need to create a new Connection using that PhoneType and replace it in Telecom.
967         if (phone.getPhoneType() != originalPhoneType) {
968             Connection repConnection = getTelephonyConnection(request, numberToDial,
969                     isEmergencyNumber, handle, phone);
970             // If there was a failure, the resulting connection will not be a TelephonyConnection,
971             // so don't place the call, just return!
972             if (repConnection instanceof TelephonyConnection) {
973                 placeOutgoingConnection((TelephonyConnection) repConnection, phone, request);
974             }
975             // Notify Telecom of the new Connection type.
976             // TODO: Switch out the underlying connection instead of creating a new
977             // one and causing UI Jank.
978             boolean noActiveSimCard = SubscriptionController.getInstance()
979                     .getActiveSubInfoCount(phone.getContext().getOpPackageName(),
980                             phone.getContext().getAttributionTag()) == 0;
981             // If there's no active sim card and the device is in emergency mode, use E account.
982             addExistingConnection(mPhoneUtilsProxy.makePstnPhoneAccountHandleWithPrefix(
983                     phone, "", isEmergencyNumber && noActiveSimCard), repConnection);
984             // Remove the old connection from Telecom after.
985             closeOrDestroyConnection(connectionToEvaluate,
986                     mDisconnectCauseFactory.toTelecomDisconnectCause(
987                             android.telephony.DisconnectCause.OUTGOING_CANCELED,
988                             "Reconnecting outgoing Emergency Call.",
989                             phone.getPhoneId()));
990         } else {
991             placeOutgoingConnection((TelephonyConnection) connectionToEvaluate, phone, request);
992         }
993     }
994 
995     /**
996      * @return {@code true} if any other call is disabling the ability to add calls, {@code false}
997      *      otherwise.
998      */
canAddCall()999     private boolean canAddCall() {
1000         Collection<Connection> connections = getAllConnections();
1001         for (Connection connection : connections) {
1002             if (connection.getExtras() != null &&
1003                     connection.getExtras().getBoolean(Connection.EXTRA_DISABLE_ADD_CALL, false)) {
1004                 return false;
1005             }
1006         }
1007         return true;
1008     }
1009 
getTelephonyConnection(final ConnectionRequest request, final String number, boolean isEmergencyNumber, final Uri handle, Phone phone)1010     private Connection getTelephonyConnection(final ConnectionRequest request, final String number,
1011             boolean isEmergencyNumber, final Uri handle, Phone phone) {
1012 
1013         if (phone == null) {
1014             final Context context = getApplicationContext();
1015             if (mDeviceState.shouldCheckSimStateBeforeOutgoingCall(this)) {
1016                 // Check SIM card state before the outgoing call.
1017                 // Start the SIM unlock activity if PIN_REQUIRED.
1018                 final Phone defaultPhone = mPhoneFactoryProxy.getDefaultPhone();
1019                 final IccCard icc = defaultPhone.getIccCard();
1020                 IccCardConstants.State simState = IccCardConstants.State.UNKNOWN;
1021                 if (icc != null) {
1022                     simState = icc.getState();
1023                 }
1024                 if (simState == IccCardConstants.State.PIN_REQUIRED) {
1025                     final String simUnlockUiPackage = context.getResources().getString(
1026                             R.string.config_simUnlockUiPackage);
1027                     final String simUnlockUiClass = context.getResources().getString(
1028                             R.string.config_simUnlockUiClass);
1029                     if (simUnlockUiPackage != null && simUnlockUiClass != null) {
1030                         Intent simUnlockIntent = new Intent().setComponent(new ComponentName(
1031                                 simUnlockUiPackage, simUnlockUiClass));
1032                         simUnlockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1033                         try {
1034                             context.startActivity(simUnlockIntent);
1035                         } catch (ActivityNotFoundException exception) {
1036                             Log.e(this, exception, "Unable to find SIM unlock UI activity.");
1037                         }
1038                     }
1039                     return Connection.createFailedConnection(
1040                             mDisconnectCauseFactory.toTelecomDisconnectCause(
1041                                     android.telephony.DisconnectCause.OUT_OF_SERVICE,
1042                                     "SIM_STATE_PIN_REQUIRED"));
1043                 }
1044             }
1045 
1046             Log.d(this, "onCreateOutgoingConnection, phone is null");
1047             return Connection.createFailedConnection(
1048                     mDisconnectCauseFactory.toTelecomDisconnectCause(
1049                             android.telephony.DisconnectCause.OUT_OF_SERVICE, "Phone is null"));
1050         }
1051 
1052         // Check both voice & data RAT to enable normal CS call,
1053         // when voice RAT is OOS but Data RAT is present.
1054         int state = phone.getServiceState().getState();
1055         if (state == ServiceState.STATE_OUT_OF_SERVICE) {
1056             int dataNetType = phone.getServiceState().getDataNetworkType();
1057             if (dataNetType == TelephonyManager.NETWORK_TYPE_LTE ||
1058                     dataNetType == TelephonyManager.NETWORK_TYPE_LTE_CA) {
1059                 state = phone.getServiceState().getDataRegistrationState();
1060             }
1061         }
1062 
1063         // If we're dialing a non-emergency number and the phone is in ECM mode, reject the call if
1064         // carrier configuration specifies that we cannot make non-emergency calls in ECM mode.
1065         if (!isEmergencyNumber && phone.isInEcm()) {
1066             boolean allowNonEmergencyCalls = true;
1067             CarrierConfigManager cfgManager = (CarrierConfigManager)
1068                     phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
1069             if (cfgManager != null) {
1070                 allowNonEmergencyCalls = cfgManager.getConfigForSubId(phone.getSubId())
1071                         .getBoolean(CarrierConfigManager.KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL);
1072             }
1073 
1074             if (!allowNonEmergencyCalls) {
1075                 return Connection.createFailedConnection(
1076                         mDisconnectCauseFactory.toTelecomDisconnectCause(
1077                                 android.telephony.DisconnectCause.CDMA_NOT_EMERGENCY,
1078                                 "Cannot make non-emergency call in ECM mode.",
1079                                 phone.getPhoneId()));
1080             }
1081         }
1082 
1083         if (!isEmergencyNumber) {
1084             switch (state) {
1085                 case ServiceState.STATE_IN_SERVICE:
1086                 case ServiceState.STATE_EMERGENCY_ONLY:
1087                     break;
1088                 case ServiceState.STATE_OUT_OF_SERVICE:
1089                     if (phone.isUtEnabled() && number.endsWith("#")) {
1090                         Log.d(this, "onCreateOutgoingConnection dial for UT");
1091                         break;
1092                     } else {
1093                         return Connection.createFailedConnection(
1094                                 mDisconnectCauseFactory.toTelecomDisconnectCause(
1095                                         android.telephony.DisconnectCause.OUT_OF_SERVICE,
1096                                         "ServiceState.STATE_OUT_OF_SERVICE",
1097                                         phone.getPhoneId()));
1098                     }
1099                 case ServiceState.STATE_POWER_OFF:
1100                     // Don't disconnect if radio is power off because the device is on Bluetooth.
1101                     if (isRadioPowerDownOnBluetooth()) {
1102                         break;
1103                     }
1104                     return Connection.createFailedConnection(
1105                             mDisconnectCauseFactory.toTelecomDisconnectCause(
1106                                     android.telephony.DisconnectCause.POWER_OFF,
1107                                     "ServiceState.STATE_POWER_OFF",
1108                                     phone.getPhoneId()));
1109                 default:
1110                     Log.d(this, "onCreateOutgoingConnection, unknown service state: %d", state);
1111                     return Connection.createFailedConnection(
1112                             mDisconnectCauseFactory.toTelecomDisconnectCause(
1113                                     android.telephony.DisconnectCause.OUTGOING_FAILURE,
1114                                     "Unknown service state " + state,
1115                                     phone.getPhoneId()));
1116             }
1117         }
1118 
1119         final boolean isTtyModeEnabled = mDeviceState.isTtyModeEnabled(this);
1120         if (VideoProfile.isVideo(request.getVideoState()) && isTtyModeEnabled
1121                 && !isEmergencyNumber) {
1122             return Connection.createFailedConnection(mDisconnectCauseFactory.toTelecomDisconnectCause(
1123                     android.telephony.DisconnectCause.VIDEO_CALL_NOT_ALLOWED_WHILE_TTY_ENABLED,
1124                     null, phone.getPhoneId()));
1125         }
1126 
1127         // Check for additional limits on CDMA phones.
1128         final Connection failedConnection = checkAdditionalOutgoingCallLimits(phone);
1129         if (failedConnection != null) {
1130             return failedConnection;
1131         }
1132 
1133         // Check roaming status to see if we should block custom call forwarding codes
1134         if (blockCallForwardingNumberWhileRoaming(phone, number)) {
1135             return Connection.createFailedConnection(
1136                     mDisconnectCauseFactory.toTelecomDisconnectCause(
1137                             android.telephony.DisconnectCause.DIALED_CALL_FORWARDING_WHILE_ROAMING,
1138                             "Call forwarding while roaming",
1139                             phone.getPhoneId()));
1140         }
1141 
1142 
1143         final TelephonyConnection connection =
1144                 createConnectionFor(phone, null, true /* isOutgoing */, request.getAccountHandle(),
1145                         request.getTelecomCallId(), request.isAdhocConferenceCall());
1146         if (connection == null) {
1147             return Connection.createFailedConnection(
1148                     mDisconnectCauseFactory.toTelecomDisconnectCause(
1149                             android.telephony.DisconnectCause.OUTGOING_FAILURE,
1150                             "Invalid phone type",
1151                             phone.getPhoneId()));
1152         }
1153         connection.setAddress(handle, PhoneConstants.PRESENTATION_ALLOWED);
1154         connection.setTelephonyConnectionInitializing();
1155         connection.setTelephonyVideoState(request.getVideoState());
1156         connection.setRttTextStream(request.getRttTextStream());
1157         connection.setTtyEnabled(isTtyModeEnabled);
1158         connection.setIsAdhocConferenceCall(request.isAdhocConferenceCall());
1159         connection.setParticipants(request.getParticipants());
1160         return connection;
1161     }
1162 
1163     @Override
onCreateIncomingConnection( PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)1164     public Connection onCreateIncomingConnection(
1165             PhoneAccountHandle connectionManagerPhoneAccount,
1166             ConnectionRequest request) {
1167         Log.i(this, "onCreateIncomingConnection, request: " + request);
1168         // If there is an incoming emergency CDMA Call (while the phone is in ECBM w/ No SIM),
1169         // make sure the PhoneAccount lookup retrieves the default Emergency Phone.
1170         PhoneAccountHandle accountHandle = request.getAccountHandle();
1171         boolean isEmergency = false;
1172         if (accountHandle != null && PhoneUtils.EMERGENCY_ACCOUNT_HANDLE_ID.equals(
1173                 accountHandle.getId())) {
1174             Log.i(this, "Emergency PhoneAccountHandle is being used for incoming call... " +
1175                     "Treat as an Emergency Call.");
1176             isEmergency = true;
1177         }
1178         Phone phone = getPhoneForAccount(accountHandle, isEmergency,
1179                 /* Note: when not an emergency, handle can be null for unknown callers */
1180                 request.getAddress() == null ? null : request.getAddress().getSchemeSpecificPart());
1181         if (phone == null) {
1182             return Connection.createFailedConnection(
1183                     mDisconnectCauseFactory.toTelecomDisconnectCause(
1184                             android.telephony.DisconnectCause.ERROR_UNSPECIFIED,
1185                             "Phone is null"));
1186         }
1187 
1188         Call call = phone.getRingingCall();
1189         if (!call.getState().isRinging()) {
1190             Log.i(this, "onCreateIncomingConnection, no ringing call");
1191             Connection connection = Connection.createFailedConnection(
1192                     mDisconnectCauseFactory.toTelecomDisconnectCause(
1193                             android.telephony.DisconnectCause.INCOMING_MISSED,
1194                             "Found no ringing call",
1195                             phone.getPhoneId()));
1196             Bundle extras = request.getExtras();
1197 
1198             long time = extras.getLong(TelecomManager.EXTRA_CALL_CREATED_EPOCH_TIME_MILLIS);
1199             if (time != 0) {
1200                 Log.i(this, "onCreateIncomingConnection. Set connect time info.");
1201                 connection.setConnectTimeMillis(time);
1202             }
1203 
1204             Uri address = extras.getParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS);
1205             if (address != null) {
1206                 Log.i(this, "onCreateIncomingConnection. Set caller id info.");
1207                 connection.setAddress(address, TelecomManager.PRESENTATION_ALLOWED);
1208             }
1209 
1210             return connection;
1211         }
1212 
1213         com.android.internal.telephony.Connection originalConnection =
1214                 call.getState() == Call.State.WAITING ?
1215                     call.getLatestConnection() : call.getEarliestConnection();
1216         if (isOriginalConnectionKnown(originalConnection)) {
1217             Log.i(this, "onCreateIncomingConnection, original connection already registered");
1218             return Connection.createCanceledConnection();
1219         }
1220 
1221         TelephonyConnection connection =
1222                 createConnectionFor(phone, originalConnection, false /* isOutgoing */,
1223                         request.getAccountHandle(), request.getTelecomCallId(),
1224                         request.isAdhocConferenceCall());
1225         handleIncomingRtt(request, originalConnection);
1226         if (connection == null) {
1227             return Connection.createCanceledConnection();
1228         } else {
1229             connection.setTtyEnabled(mDeviceState.isTtyModeEnabled(getApplicationContext()));
1230             return connection;
1231         }
1232     }
1233 
handleIncomingRtt(ConnectionRequest request, com.android.internal.telephony.Connection originalConnection)1234     private void handleIncomingRtt(ConnectionRequest request,
1235             com.android.internal.telephony.Connection originalConnection) {
1236         if (originalConnection == null
1237                 || originalConnection.getPhoneType() != PhoneConstants.PHONE_TYPE_IMS) {
1238             if (request.isRequestingRtt()) {
1239                 Log.w(this, "Requesting RTT on non-IMS call, ignoring");
1240             }
1241             return;
1242         }
1243 
1244         ImsPhoneConnection imsOriginalConnection = (ImsPhoneConnection) originalConnection;
1245         if (!request.isRequestingRtt()) {
1246             if (imsOriginalConnection.isRttEnabledForCall()) {
1247                 Log.w(this, "Incoming call requested RTT but we did not get a RttTextStream");
1248             }
1249             return;
1250         }
1251 
1252         Log.i(this, "Setting RTT stream on ImsPhoneConnection in case we need it later");
1253         imsOriginalConnection.setCurrentRttTextStream(request.getRttTextStream());
1254 
1255         if (!imsOriginalConnection.isRttEnabledForCall()) {
1256             if (request.isRequestingRtt()) {
1257                 Log.w(this, "Incoming call processed as RTT but did not come in as one. Ignoring");
1258             }
1259             return;
1260         }
1261 
1262         Log.i(this, "Setting the call to be answered with RTT on.");
1263         imsOriginalConnection.getImsCall().setAnswerWithRtt();
1264     }
1265 
1266     /**
1267      * Called by the {@link ConnectionService} when a newly created {@link Connection} has been
1268      * added to the {@link ConnectionService} and sent to Telecom.  Here it is safe to send
1269      * connection events.
1270      *
1271      * @param connection the {@link Connection}.
1272      */
1273     @Override
onCreateConnectionComplete(Connection connection)1274     public void onCreateConnectionComplete(Connection connection) {
1275         if (connection instanceof TelephonyConnection) {
1276             TelephonyConnection telephonyConnection = (TelephonyConnection) connection;
1277             maybeSendInternationalCallEvent(telephonyConnection);
1278         }
1279     }
1280 
1281     @Override
onCreateIncomingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)1282     public void onCreateIncomingConnectionFailed(PhoneAccountHandle connectionManagerPhoneAccount,
1283             ConnectionRequest request) {
1284         Log.i(this, "onCreateIncomingConnectionFailed, request: " + request);
1285         // If there is an incoming emergency CDMA Call (while the phone is in ECBM w/ No SIM),
1286         // make sure the PhoneAccount lookup retrieves the default Emergency Phone.
1287         PhoneAccountHandle accountHandle = request.getAccountHandle();
1288         boolean isEmergency = false;
1289         if (accountHandle != null && PhoneUtils.EMERGENCY_ACCOUNT_HANDLE_ID.equals(
1290                 accountHandle.getId())) {
1291             Log.w(this, "onCreateIncomingConnectionFailed:Emergency call failed... ");
1292             isEmergency = true;
1293         }
1294         Phone phone = getPhoneForAccount(accountHandle, isEmergency,
1295                 /* Note: when not an emergency, handle can be null for unknown callers */
1296                 request.getAddress() == null ? null : request.getAddress().getSchemeSpecificPart());
1297         if (phone == null) {
1298             Log.w(this, "onCreateIncomingConnectionFailed: can not find corresponding phone.");
1299             return;
1300         }
1301 
1302         Call call = phone.getRingingCall();
1303         if (!call.getState().isRinging()) {
1304             Log.w(this, "onCreateIncomingConnectionFailed, no ringing call found for failed call");
1305             return;
1306         }
1307 
1308         com.android.internal.telephony.Connection originalConnection =
1309                 call.getState() == Call.State.WAITING
1310                         ? call.getLatestConnection() : call.getEarliestConnection();
1311         TelephonyConnection knownConnection =
1312                 getConnectionForOriginalConnection(originalConnection);
1313         if (knownConnection != null) {
1314             Log.w(this, "onCreateIncomingConnectionFailed, original connection already registered."
1315                     + " Hanging it up.");
1316             knownConnection.onAbort();
1317             return;
1318         }
1319 
1320         TelephonyConnection connection =
1321                 createConnectionFor(phone, originalConnection, false /* isOutgoing */,
1322                         request.getAccountHandle(), request.getTelecomCallId());
1323         if (connection == null) {
1324             Log.w(this, "onCreateIncomingConnectionFailed, TelephonyConnection created as null, "
1325                     + "ignoring.");
1326             return;
1327         }
1328 
1329         // We have to do all of this work because in some cases, hanging up the call maps to
1330         // different underlying signaling (CDMA), which is already encapsulated in
1331         // TelephonyConnection.
1332         connection.onReject();
1333         connection.close();
1334     }
1335 
1336     /**
1337      * Called by the {@link ConnectionService} when a newly created {@link Conference} has been
1338      * added to the {@link ConnectionService} and sent to Telecom.  Here it is safe to send
1339      * connection events.
1340      *
1341      * @param conference the {@link Conference}.
1342      */
1343     @Override
onCreateConferenceComplete(Conference conference)1344     public void onCreateConferenceComplete(Conference conference) {
1345         if (conference instanceof ImsConference) {
1346             ImsConference imsConference = (ImsConference)conference;
1347             TelephonyConnection telephonyConnection =
1348                     (TelephonyConnection)(imsConference.getConferenceHost());
1349             maybeSendInternationalCallEvent(telephonyConnection);
1350         }
1351     }
1352 
onCreateIncomingConferenceFailed(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)1353     public void onCreateIncomingConferenceFailed(PhoneAccountHandle connectionManagerPhoneAccount,
1354             ConnectionRequest request) {
1355         Log.i(this, "onCreateIncomingConferenceFailed, request: " + request);
1356         onCreateIncomingConnectionFailed(connectionManagerPhoneAccount, request);
1357     }
1358 
1359     @Override
triggerConferenceRecalculate()1360     public void triggerConferenceRecalculate() {
1361         if (mTelephonyConferenceController.shouldRecalculate()) {
1362             mTelephonyConferenceController.recalculate();
1363         }
1364     }
1365 
1366     @Override
onCreateUnknownConnection(PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request)1367     public Connection onCreateUnknownConnection(PhoneAccountHandle connectionManagerPhoneAccount,
1368             ConnectionRequest request) {
1369         Log.i(this, "onCreateUnknownConnection, request: " + request);
1370         // Use the registered emergency Phone if the PhoneAccountHandle is set to Telephony's
1371         // Emergency PhoneAccount
1372         PhoneAccountHandle accountHandle = request.getAccountHandle();
1373         boolean isEmergency = false;
1374         if (accountHandle != null && PhoneUtils.EMERGENCY_ACCOUNT_HANDLE_ID.equals(
1375                 accountHandle.getId())) {
1376             Log.i(this, "Emergency PhoneAccountHandle is being used for unknown call... " +
1377                     "Treat as an Emergency Call.");
1378             isEmergency = true;
1379         }
1380         Phone phone = getPhoneForAccount(accountHandle, isEmergency,
1381                 /* Note: when not an emergency, handle can be null for unknown callers */
1382                 request.getAddress() == null ? null : request.getAddress().getSchemeSpecificPart());
1383         if (phone == null) {
1384             return Connection.createFailedConnection(
1385                     mDisconnectCauseFactory.toTelecomDisconnectCause(
1386                             android.telephony.DisconnectCause.ERROR_UNSPECIFIED,
1387                             "Phone is null"));
1388         }
1389         Bundle extras = request.getExtras();
1390 
1391         final List<com.android.internal.telephony.Connection> allConnections = new ArrayList<>();
1392 
1393         // Handle the case where an unknown connection has an IMS external call ID specified; we can
1394         // skip the rest of the guesswork and just grad that unknown call now.
1395         if (phone.getImsPhone() != null && extras != null &&
1396                 extras.containsKey(ImsExternalCallTracker.EXTRA_IMS_EXTERNAL_CALL_ID)) {
1397 
1398             ImsPhone imsPhone = (ImsPhone) phone.getImsPhone();
1399             ImsExternalCallTracker externalCallTracker = imsPhone.getExternalCallTracker();
1400             int externalCallId = extras.getInt(ImsExternalCallTracker.EXTRA_IMS_EXTERNAL_CALL_ID,
1401                     -1);
1402 
1403             if (externalCallTracker != null) {
1404                 com.android.internal.telephony.Connection connection =
1405                         externalCallTracker.getConnectionById(externalCallId);
1406 
1407                 if (connection != null) {
1408                     allConnections.add(connection);
1409                 }
1410             }
1411         }
1412 
1413         if (allConnections.isEmpty()) {
1414             final Call ringingCall = phone.getRingingCall();
1415             if (ringingCall.hasConnections()) {
1416                 allConnections.addAll(ringingCall.getConnections());
1417             }
1418             final Call foregroundCall = phone.getForegroundCall();
1419             if ((foregroundCall.getState() != Call.State.DISCONNECTED)
1420                     && (foregroundCall.hasConnections())) {
1421                 allConnections.addAll(foregroundCall.getConnections());
1422             }
1423             if (phone.getImsPhone() != null) {
1424                 final Call imsFgCall = phone.getImsPhone().getForegroundCall();
1425                 if ((imsFgCall.getState() != Call.State.DISCONNECTED) && imsFgCall
1426                         .hasConnections()) {
1427                     allConnections.addAll(imsFgCall.getConnections());
1428                 }
1429             }
1430             final Call backgroundCall = phone.getBackgroundCall();
1431             if (backgroundCall.hasConnections()) {
1432                 allConnections.addAll(phone.getBackgroundCall().getConnections());
1433             }
1434         }
1435 
1436         com.android.internal.telephony.Connection unknownConnection = null;
1437         for (com.android.internal.telephony.Connection telephonyConnection : allConnections) {
1438             if (!isOriginalConnectionKnown(telephonyConnection)) {
1439                 unknownConnection = telephonyConnection;
1440                 Log.d(this, "onCreateUnknownConnection: conn = " + unknownConnection);
1441                 break;
1442             }
1443         }
1444 
1445         if (unknownConnection == null) {
1446             Log.i(this, "onCreateUnknownConnection, did not find previously unknown connection.");
1447             return Connection.createCanceledConnection();
1448         }
1449 
1450         // We should rely on the originalConnection to get the video state.  The request coming
1451         // from Telecom does not know the video state of the unknown call.
1452         int videoState = unknownConnection != null ? unknownConnection.getVideoState() :
1453                 VideoProfile.STATE_AUDIO_ONLY;
1454 
1455         TelephonyConnection connection =
1456                 createConnectionFor(phone, unknownConnection,
1457                         !unknownConnection.isIncoming() /* isOutgoing */,
1458                         request.getAccountHandle(), request.getTelecomCallId()
1459                 );
1460 
1461         if (connection == null) {
1462             return Connection.createCanceledConnection();
1463         } else {
1464             connection.updateState();
1465             return connection;
1466         }
1467     }
1468 
1469     /**
1470      * Conferences two connections.
1471      *
1472      * Note: The {@link android.telecom.RemoteConnection#setConferenceableConnections(List)} API has
1473      * a limitation in that it can only specify conferenceables which are instances of
1474      * {@link android.telecom.RemoteConnection}.  In the case of an {@link ImsConference}, the
1475      * regular {@link Connection#setConferenceables(List)} API properly handles being able to merge
1476      * a {@link Conference} and a {@link Connection}.  As a result when, merging a
1477      * {@link android.telecom.RemoteConnection} into a {@link android.telecom.RemoteConference}
1478      * require merging a {@link ConferenceParticipantConnection} which is a child of the
1479      * {@link Conference} with a {@link TelephonyConnection}.  The
1480      * {@link ConferenceParticipantConnection} class does not have the capability to initiate a
1481      * conference merge, so we need to call
1482      * {@link TelephonyConnection#performConference(Connection)} on either {@code connection1} or
1483      * {@code connection2}, one of which is an instance of {@link TelephonyConnection}.
1484      *
1485      * @param connection1 A connection to merge into a conference call.
1486      * @param connection2 A connection to merge into a conference call.
1487      */
1488     @Override
onConference(Connection connection1, Connection connection2)1489     public void onConference(Connection connection1, Connection connection2) {
1490         if (connection1 instanceof TelephonyConnection) {
1491             ((TelephonyConnection) connection1).performConference(connection2);
1492         } else if (connection2 instanceof TelephonyConnection) {
1493             ((TelephonyConnection) connection2).performConference(connection1);
1494         } else {
1495             Log.w(this, "onConference - cannot merge connections " +
1496                     "Connection1: %s, Connection2: %2", connection1, connection2);
1497         }
1498     }
1499 
1500     @Override
onConnectionAdded(Connection connection)1501     public void onConnectionAdded(Connection connection) {
1502         if (connection instanceof Holdable && !isExternalConnection(connection)) {
1503             mHoldTracker.addHoldable(
1504                     connection.getPhoneAccountHandle(), (Holdable) connection);
1505         }
1506     }
1507 
1508     @Override
onConnectionRemoved(Connection connection)1509     public void onConnectionRemoved(Connection connection) {
1510         if (connection instanceof Holdable && !isExternalConnection(connection)) {
1511             mHoldTracker.removeHoldable(connection.getPhoneAccountHandle(), (Holdable) connection);
1512         }
1513     }
1514 
1515     @Override
onConferenceAdded(Conference conference)1516     public void onConferenceAdded(Conference conference) {
1517         if (conference instanceof Holdable) {
1518             mHoldTracker.addHoldable(conference.getPhoneAccountHandle(), (Holdable) conference);
1519         }
1520     }
1521 
1522     @Override
onConferenceRemoved(Conference conference)1523     public void onConferenceRemoved(Conference conference) {
1524         if (conference instanceof Holdable) {
1525             mHoldTracker.removeHoldable(conference.getPhoneAccountHandle(), (Holdable) conference);
1526         }
1527     }
1528 
isExternalConnection(Connection connection)1529     private boolean isExternalConnection(Connection connection) {
1530         return (connection.getConnectionProperties() & Connection.PROPERTY_IS_EXTERNAL_CALL)
1531                 == Connection.PROPERTY_IS_EXTERNAL_CALL;
1532     }
1533 
blockCallForwardingNumberWhileRoaming(Phone phone, String number)1534     private boolean blockCallForwardingNumberWhileRoaming(Phone phone, String number) {
1535         if (phone == null || TextUtils.isEmpty(number) || !phone.getServiceState().getRoaming()) {
1536             return false;
1537         }
1538         String[] blockPrefixes = null;
1539         CarrierConfigManager cfgManager = (CarrierConfigManager)
1540                 phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
1541         if (cfgManager != null) {
1542             blockPrefixes = cfgManager.getConfigForSubId(phone.getSubId()).getStringArray(
1543                     CarrierConfigManager.KEY_CALL_FORWARDING_BLOCKS_WHILE_ROAMING_STRING_ARRAY);
1544         }
1545 
1546         if (blockPrefixes != null) {
1547             for (String prefix : blockPrefixes) {
1548                 if (number.startsWith(prefix)) {
1549                     return true;
1550                 }
1551             }
1552         }
1553         return false;
1554     }
1555 
isRadioOn()1556     private boolean isRadioOn() {
1557         boolean result = false;
1558         for (Phone phone : mPhoneFactoryProxy.getPhones()) {
1559             result |= phone.isRadioOn();
1560         }
1561         return result;
1562     }
1563 
makeCachedConnectionPhonePair( TelephonyConnection c)1564     private Pair<WeakReference<TelephonyConnection>, Queue<Phone>> makeCachedConnectionPhonePair(
1565             TelephonyConnection c) {
1566         Queue<Phone> phones = new LinkedList<>(Arrays.asList(mPhoneFactoryProxy.getPhones()));
1567         return new Pair<>(new WeakReference<>(c), phones);
1568     }
1569 
1570     // Update the mEmergencyRetryCache by removing the Phone used to call the last failed emergency
1571     // number and then moving it to the back of the queue if it is not a permanent failure cause
1572     // from the modem.
updateCachedConnectionPhonePair(TelephonyConnection c, boolean isPermanentFailure)1573     private void updateCachedConnectionPhonePair(TelephonyConnection c,
1574             boolean isPermanentFailure) {
1575         // No cache exists, create a new one.
1576         if (mEmergencyRetryCache == null) {
1577             Log.i(this, "updateCachedConnectionPhonePair, cache is null. Generating new cache");
1578             mEmergencyRetryCache = makeCachedConnectionPhonePair(c);
1579         // Cache is stale, create a new one with the new TelephonyConnection.
1580         } else if (mEmergencyRetryCache.first.get() != c) {
1581             Log.i(this, "updateCachedConnectionPhonePair, cache is stale. Regenerating.");
1582             mEmergencyRetryCache = makeCachedConnectionPhonePair(c);
1583         }
1584 
1585         Queue<Phone> cachedPhones = mEmergencyRetryCache.second;
1586         // Need to refer default phone considering ImsPhone because
1587         // cachedPhones is a list that contains default phones.
1588         Phone phoneUsed = c.getPhone().getDefaultPhone();
1589         if (phoneUsed == null) {
1590             return;
1591         }
1592         // Remove phone used from the list, but for temporary fail cause, it will be added
1593         // back to list further in this method. However in case of permanent failure, the
1594         // phone shouldn't be reused, hence it will not be added back again.
1595         cachedPhones.remove(phoneUsed);
1596         Log.i(this, "updateCachedConnectionPhonePair, isPermanentFailure:" + isPermanentFailure);
1597         if (!isPermanentFailure) {
1598             // In case of temporary failure, add the phone back, this will result adding it
1599             // to tail of list mEmergencyRetryCache.second, giving other phone more
1600             // priority and that is what we want.
1601             cachedPhones.offer(phoneUsed);
1602         }
1603     }
1604 
1605     /**
1606      * Updates a cache containing all of the slots that are available for redial at any point.
1607      *
1608      * - If a Connection returns with the disconnect cause EMERGENCY_TEMP_FAILURE, keep that phone
1609      * in the cache, but move it to the lowest priority in the list. Then, place the emergency call
1610      * on the next phone in the list.
1611      * - If a Connection returns with the disconnect cause EMERGENCY_PERM_FAILURE, remove that phone
1612      * from the cache and pull another phone from the cache to place the emergency call.
1613      *
1614      * This will continue until there are no more slots to dial on.
1615      */
1616     @VisibleForTesting
retryOutgoingOriginalConnection(TelephonyConnection c, boolean isPermanentFailure)1617     public void retryOutgoingOriginalConnection(TelephonyConnection c, boolean isPermanentFailure) {
1618         int phoneId = (c.getPhone() == null) ? -1 : c.getPhone().getPhoneId();
1619         updateCachedConnectionPhonePair(c, isPermanentFailure);
1620         // Pull next phone to use from the cache or null if it is empty
1621         Phone newPhoneToUse = (mEmergencyRetryCache.second != null)
1622                 ? mEmergencyRetryCache.second.peek() : null;
1623         if (newPhoneToUse != null) {
1624             int videoState = c.getVideoState();
1625             Bundle connExtras = c.getExtras();
1626             Log.i(this, "retryOutgoingOriginalConnection, redialing on Phone Id: " + newPhoneToUse);
1627             c.clearOriginalConnection();
1628             if (phoneId != newPhoneToUse.getPhoneId()) updatePhoneAccount(c, newPhoneToUse);
1629             placeOutgoingConnection(c, newPhoneToUse, videoState, connExtras);
1630         } else {
1631             // We have run out of Phones to use. Disconnect the call and destroy the connection.
1632             Log.i(this, "retryOutgoingOriginalConnection, no more Phones to use. Disconnecting.");
1633             closeOrDestroyConnection(c, new DisconnectCause(DisconnectCause.ERROR));
1634         }
1635     }
1636 
updatePhoneAccount(TelephonyConnection connection, Phone phone)1637     private void updatePhoneAccount(TelephonyConnection connection, Phone phone) {
1638         PhoneAccountHandle pHandle = mPhoneUtilsProxy.makePstnPhoneAccountHandle(phone);
1639         // For ECall handling on MSIM, until the request reaches here (i.e PhoneApp), we don't know
1640         // on which phone account ECall can be placed. After deciding, we should notify Telecom of
1641         // the change so that the proper PhoneAccount can be displayed.
1642         Log.i(this, "updatePhoneAccount setPhoneAccountHandle, account = " + pHandle);
1643         connection.setPhoneAccountHandle(pHandle);
1644     }
1645 
placeOutgoingConnection( TelephonyConnection connection, Phone phone, ConnectionRequest request)1646     private void placeOutgoingConnection(
1647             TelephonyConnection connection, Phone phone, ConnectionRequest request) {
1648         placeOutgoingConnection(connection, phone, request.getVideoState(), request.getExtras());
1649     }
1650 
placeOutgoingConnection( TelephonyConnection connection, Phone phone, int videoState, Bundle extras)1651     private void placeOutgoingConnection(
1652             TelephonyConnection connection, Phone phone, int videoState, Bundle extras) {
1653 
1654         String number = (connection.getAddress() != null)
1655                 ? connection.getAddress().getSchemeSpecificPart()
1656                 : "";
1657 
1658         com.android.internal.telephony.Connection originalConnection = null;
1659         try {
1660             if (phone != null) {
1661                 EmergencyNumber emergencyNumber =
1662                         phone.getEmergencyNumberTracker().getEmergencyNumber(number);
1663                 if (emergencyNumber != null) {
1664                     phone.notifyOutgoingEmergencyCall(emergencyNumber);
1665                     if (!getAllConnections().isEmpty()) {
1666                         if (!shouldHoldForEmergencyCall(phone)) {
1667                             // If we do not support holding ongoing calls for an outgoing
1668                             // emergency call, disconnect the ongoing calls.
1669                             for (Connection c : getAllConnections()) {
1670                                 if (!c.equals(connection)
1671                                         && c.getState() != Connection.STATE_DISCONNECTED
1672                                         && c instanceof TelephonyConnection) {
1673                                     ((TelephonyConnection) c).hangup(
1674                                             android.telephony.DisconnectCause
1675                                                     .OUTGOING_EMERGENCY_CALL_PLACED);
1676                                 }
1677                             }
1678                             for (Conference c : getAllConferences()) {
1679                                 if (c.getState() != Connection.STATE_DISCONNECTED
1680                                         && c instanceof Conference) {
1681                                     ((Conference) c).onDisconnect();
1682                                 }
1683                             }
1684                         } else if (!isVideoCallHoldAllowed(phone)) {
1685                             // If we do not support holding ongoing video call for an outgoing
1686                             // emergency call, disconnect the ongoing video call.
1687                             for (Connection c : getAllConnections()) {
1688                                 if (!c.equals(connection)
1689                                         && c.getState() == Connection.STATE_ACTIVE
1690                                         && VideoProfile.isVideo(c.getVideoState())
1691                                         && c instanceof TelephonyConnection) {
1692                                     ((TelephonyConnection) c).hangup(
1693                                             android.telephony.DisconnectCause
1694                                                     .OUTGOING_EMERGENCY_CALL_PLACED);
1695                                     break;
1696                                 }
1697                             }
1698                         }
1699                     }
1700                 }
1701                 originalConnection = phone.dial(number, new ImsPhone.ImsDialArgs.Builder()
1702                         .setVideoState(videoState)
1703                         .setIntentExtras(extras)
1704                         .setRttTextStream(connection.getRttTextStream())
1705                         .build());
1706             }
1707         } catch (CallStateException e) {
1708             Log.e(this, e, "placeOutgoingConnection, phone.dial exception: " + e);
1709             handleCallStateException(e, connection, phone);
1710             return;
1711         }
1712 
1713         if (originalConnection == null) {
1714             int telephonyDisconnectCause = android.telephony.DisconnectCause.OUTGOING_FAILURE;
1715             // On GSM phones, null connection means that we dialed an MMI code
1716             if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_GSM) {
1717                 Log.d(this, "dialed MMI code");
1718                 int subId = phone.getSubId();
1719                 Log.d(this, "subId: "+subId);
1720                 telephonyDisconnectCause = android.telephony.DisconnectCause.DIALED_MMI;
1721                 final Intent intent = new Intent(this, MMIDialogActivity.class);
1722                 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
1723                         Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
1724                 if (SubscriptionManager.isValidSubscriptionId(subId)) {
1725                     SubscriptionManager.putSubscriptionIdExtra(intent, subId);
1726                 }
1727                 startActivity(intent);
1728             }
1729             Log.d(this, "placeOutgoingConnection, phone.dial returned null");
1730             connection.setTelephonyConnectionDisconnected(
1731                     mDisconnectCauseFactory.toTelecomDisconnectCause(telephonyDisconnectCause,
1732                             "Connection is null", phone.getPhoneId()));
1733             connection.close();
1734         } else {
1735             connection.setOriginalConnection(originalConnection);
1736         }
1737     }
1738 
isVideoCallHoldAllowed(Phone phone)1739     private boolean isVideoCallHoldAllowed(Phone phone) {
1740          CarrierConfigManager cfgManager = (CarrierConfigManager)
1741                 phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
1742         if (cfgManager == null) {
1743             // For some reason CarrierConfigManager is unavailable, return default
1744             Log.w(this, "isVideoCallHoldAllowed: couldn't get CarrierConfigManager");
1745             return true;
1746         }
1747         return cfgManager.getConfigForSubId(phone.getSubId()).getBoolean(
1748                 CarrierConfigManager.KEY_ALLOW_HOLD_VIDEO_CALL_BOOL, true);
1749     }
1750 
shouldHoldForEmergencyCall(Phone phone)1751     private boolean shouldHoldForEmergencyCall(Phone phone) {
1752         CarrierConfigManager cfgManager = (CarrierConfigManager)
1753                 phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
1754         if (cfgManager == null) {
1755             // For some reason CarrierConfigManager is unavailable, return default
1756             Log.w(this, "shouldHoldForEmergencyCall: couldn't get CarrierConfigManager");
1757             return true;
1758         }
1759         return cfgManager.getConfigForSubId(phone.getSubId()).getBoolean(
1760                 CarrierConfigManager.KEY_ALLOW_HOLD_CALL_DURING_EMERGENCY_BOOL, true);
1761     }
1762 
handleCallStateException(CallStateException e, TelephonyConnection connection, Phone phone)1763     private void handleCallStateException(CallStateException e, TelephonyConnection connection,
1764             Phone phone) {
1765         int cause = android.telephony.DisconnectCause.OUTGOING_FAILURE;
1766         switch (e.getError()) {
1767             case CallStateException.ERROR_OUT_OF_SERVICE:
1768                 cause = android.telephony.DisconnectCause.OUT_OF_SERVICE;
1769                 break;
1770             case CallStateException.ERROR_POWER_OFF:
1771                  cause = android.telephony.DisconnectCause.POWER_OFF;
1772                  break;
1773             case CallStateException.ERROR_ALREADY_DIALING:
1774                  cause = android.telephony.DisconnectCause.ALREADY_DIALING;
1775                  break;
1776             case CallStateException.ERROR_CALL_RINGING:
1777                  cause = android.telephony.DisconnectCause.CANT_CALL_WHILE_RINGING;
1778                  break;
1779             case CallStateException.ERROR_CALLING_DISABLED:
1780                  cause = android.telephony.DisconnectCause.CALLING_DISABLED;
1781                  break;
1782             case CallStateException.ERROR_TOO_MANY_CALLS:
1783                  cause = android.telephony.DisconnectCause.TOO_MANY_ONGOING_CALLS;
1784                  break;
1785             case CallStateException.ERROR_OTASP_PROVISIONING_IN_PROCESS:
1786                  cause = android.telephony.DisconnectCause.OTASP_PROVISIONING_IN_PROCESS;
1787                  break;
1788         }
1789         connection.setTelephonyConnectionDisconnected(
1790                 DisconnectCauseUtil.toTelecomDisconnectCause(cause, e.getMessage(),
1791                         phone.getPhoneId()));
1792         connection.close();
1793     }
1794 
createConnectionFor( Phone phone, com.android.internal.telephony.Connection originalConnection, boolean isOutgoing, PhoneAccountHandle phoneAccountHandle, String telecomCallId)1795     private TelephonyConnection createConnectionFor(
1796             Phone phone,
1797             com.android.internal.telephony.Connection originalConnection,
1798             boolean isOutgoing,
1799             PhoneAccountHandle phoneAccountHandle,
1800             String telecomCallId) {
1801             return createConnectionFor(phone, originalConnection, isOutgoing, phoneAccountHandle,
1802                     telecomCallId, false);
1803     }
1804 
createConnectionFor( Phone phone, com.android.internal.telephony.Connection originalConnection, boolean isOutgoing, PhoneAccountHandle phoneAccountHandle, String telecomCallId, boolean isAdhocConference)1805     private TelephonyConnection createConnectionFor(
1806             Phone phone,
1807             com.android.internal.telephony.Connection originalConnection,
1808             boolean isOutgoing,
1809             PhoneAccountHandle phoneAccountHandle,
1810             String telecomCallId,
1811             boolean isAdhocConference) {
1812         TelephonyConnection returnConnection = null;
1813         int phoneType = phone.getPhoneType();
1814         int callDirection = isOutgoing ? android.telecom.Call.Details.DIRECTION_OUTGOING
1815                 : android.telecom.Call.Details.DIRECTION_INCOMING;
1816         if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {
1817             returnConnection = new GsmConnection(originalConnection, telecomCallId, callDirection);
1818         } else if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) {
1819             boolean allowsMute = allowsMute(phone);
1820             returnConnection = new CdmaConnection(originalConnection, mEmergencyTonePlayer,
1821                     allowsMute, callDirection, telecomCallId);
1822         }
1823         if (returnConnection != null) {
1824             if (!isAdhocConference) {
1825                 // Listen to Telephony specific callbacks from the connection
1826                 returnConnection.addTelephonyConnectionListener(mTelephonyConnectionListener);
1827             }
1828             returnConnection.setVideoPauseSupported(
1829                     TelecomAccountRegistry.getInstance(this).isVideoPauseSupported(
1830                             phoneAccountHandle));
1831             returnConnection.setManageImsConferenceCallSupported(
1832                     TelecomAccountRegistry.getInstance(this).isManageImsConferenceCallSupported(
1833                             phoneAccountHandle));
1834             returnConnection.setShowPreciseFailedCause(
1835                     TelecomAccountRegistry.getInstance(this).isShowPreciseFailedCause(
1836                             phoneAccountHandle));
1837             returnConnection.setTelephonyConnectionService(this);
1838         }
1839         return returnConnection;
1840     }
1841 
isOriginalConnectionKnown( com.android.internal.telephony.Connection originalConnection)1842     private boolean isOriginalConnectionKnown(
1843             com.android.internal.telephony.Connection originalConnection) {
1844         return (getConnectionForOriginalConnection(originalConnection) != null);
1845     }
1846 
getConnectionForOriginalConnection( com.android.internal.telephony.Connection originalConnection)1847     private TelephonyConnection getConnectionForOriginalConnection(
1848             com.android.internal.telephony.Connection originalConnection) {
1849         for (Connection connection : getAllConnections()) {
1850             if (connection instanceof TelephonyConnection) {
1851                 TelephonyConnection telephonyConnection = (TelephonyConnection) connection;
1852                 if (telephonyConnection.getOriginalConnection() == originalConnection) {
1853                     return telephonyConnection;
1854                 }
1855             }
1856         }
1857         return null;
1858     }
1859 
1860     /**
1861      * Determines which {@link Phone} will be used to place the call.
1862      * @param accountHandle The {@link PhoneAccountHandle} which was sent from Telecom to place the
1863      *      call on.
1864      * @param isEmergency {@code true} if this is an emergency call, {@code false} otherwise.
1865      * @param emergencyNumberAddress When {@code isEmergency} is {@code true}, will be the phone
1866      *      of the emergency call.  Otherwise, this can be {@code null}  .
1867      * @return
1868      */
getPhoneForAccount(PhoneAccountHandle accountHandle, boolean isEmergency, @Nullable String emergencyNumberAddress)1869     private Phone getPhoneForAccount(PhoneAccountHandle accountHandle, boolean isEmergency,
1870                                      @Nullable String emergencyNumberAddress) {
1871         Phone chosenPhone = null;
1872         int subId = mPhoneUtilsProxy.getSubIdForPhoneAccountHandle(accountHandle);
1873         if (subId != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
1874             int phoneId = mSubscriptionManagerProxy.getPhoneId(subId);
1875             chosenPhone = mPhoneFactoryProxy.getPhone(phoneId);
1876         }
1877         // If this is an emergency call and the phone we originally planned to make this call
1878         // with is not in service or was invalid, try to find one that is in service, using the
1879         // default as a last chance backup.
1880         if (isEmergency && (chosenPhone == null || !isAvailableForEmergencyCalls(chosenPhone))) {
1881             Log.d(this, "getPhoneForAccount: phone for phone acct handle %s is out of service "
1882                     + "or invalid for emergency call.", accountHandle);
1883             chosenPhone = getPhoneForEmergencyCall(emergencyNumberAddress);
1884             Log.d(this, "getPhoneForAccount: using subId: " +
1885                     (chosenPhone == null ? "null" : chosenPhone.getSubId()));
1886         }
1887         return chosenPhone;
1888     }
1889 
1890     /**
1891      * If needed, block until the the default data is is switched for outgoing emergency call, or
1892      * timeout expires.
1893      */
delayDialForDdsSwitch(Phone phone)1894     private boolean delayDialForDdsSwitch(Phone phone) {
1895         if (phone == null) {
1896             return true;
1897         }
1898         try {
1899             return possiblyOverrideDefaultDataForEmergencyCall(phone).get(
1900                     DEFAULT_DATA_SWITCH_TIMEOUT_MS, TimeUnit.MILLISECONDS);
1901         } catch (Exception e) {
1902             Log.w(this, "delayDialForDdsSwitch - exception= "
1903                     + e.getMessage());
1904             return false;
1905         }
1906     }
1907 
1908     /**
1909      * If needed, block until Default Data subscription is switched for outgoing emergency call.
1910      *
1911      * In some cases, we need to try to switch the Default Data subscription before placing the
1912      * emergency call on DSDS devices. This includes the following situation:
1913      * - The modem does not support processing GNSS SUPL requests on the non-default data
1914      * subscription. For some carriers that do not provide a control plane fallback mechanism, the
1915      * SUPL request will be dropped and we will not be able to get the user's location for the
1916      * emergency call. In this case, we need to swap default data temporarily.
1917      * @param phone Evaluates whether or not the default data should be moved to the phone
1918      *              specified. Should not be null.
1919      */
possiblyOverrideDefaultDataForEmergencyCall( @onNull Phone phone)1920     private CompletableFuture<Boolean> possiblyOverrideDefaultDataForEmergencyCall(
1921             @NonNull Phone phone) {
1922         int phoneCount = mTelephonyManagerProxy.getPhoneCount();
1923         // Do not override DDS if this is a single SIM device.
1924         if (phoneCount <= PhoneConstants.MAX_PHONE_COUNT_SINGLE_SIM) {
1925             return CompletableFuture.completedFuture(Boolean.TRUE);
1926         }
1927 
1928         // Do not switch Default data if this device supports emergency SUPL on non-DDS.
1929         final boolean gnssSuplRequiresDefaultData =
1930                 mDeviceState.isSuplDdsSwitchRequiredForEmergencyCall(this);
1931         if (!gnssSuplRequiresDefaultData) {
1932             Log.d(this, "possiblyOverrideDefaultDataForEmergencyCall: not switching DDS, does not "
1933                     + "require DDS switch.");
1934             return CompletableFuture.completedFuture(Boolean.TRUE);
1935         }
1936 
1937         CarrierConfigManager cfgManager = (CarrierConfigManager)
1938                 phone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
1939         if (cfgManager == null) {
1940             // For some reason CarrierConfigManager is unavailable. Do not block emergency call.
1941             Log.w(this, "possiblyOverrideDefaultDataForEmergencyCall: couldn't get"
1942                     + "CarrierConfigManager");
1943             return CompletableFuture.completedFuture(Boolean.TRUE);
1944         }
1945 
1946         // Only override default data if we are IN_SERVICE already.
1947         if (!isAvailableForEmergencyCalls(phone)) {
1948             Log.d(this, "possiblyOverrideDefaultDataForEmergencyCall: not switching DDS");
1949             return CompletableFuture.completedFuture(Boolean.TRUE);
1950         }
1951 
1952         // Only override default data if we are not roaming, we do not want to switch onto a network
1953         // that only supports data plane only (if we do not know).
1954         boolean isRoaming = phone.getServiceState().getVoiceRoaming();
1955         // In some roaming conditions, we know the roaming network doesn't support control plane
1956         // fallback even though the home operator does. For these operators we will need to do a DDS
1957         // switch anyway to make sure the SUPL request doesn't fail.
1958         boolean roamingNetworkSupportsControlPlaneFallback = true;
1959         String[] dataPlaneRoamPlmns = cfgManager.getConfigForSubId(phone.getSubId()).getStringArray(
1960                 CarrierConfigManager.Gps.KEY_ES_SUPL_DATA_PLANE_ONLY_ROAMING_PLMN_STRING_ARRAY);
1961         if (dataPlaneRoamPlmns != null && Arrays.asList(dataPlaneRoamPlmns).contains(
1962                 phone.getServiceState().getOperatorNumeric())) {
1963             roamingNetworkSupportsControlPlaneFallback = false;
1964         }
1965         if (isRoaming && roamingNetworkSupportsControlPlaneFallback) {
1966             Log.d(this, "possiblyOverrideDefaultDataForEmergencyCall: roaming network is assumed "
1967                     + "to support CP fallback, not switching DDS.");
1968             return CompletableFuture.completedFuture(Boolean.TRUE);
1969         }
1970         // Do not try to swap default data if we support CS fallback or it is assumed that the
1971         // roaming network supports control plane fallback, we do not want to introduce
1972         // a lag in emergency call setup time if possible.
1973         final boolean supportsCpFallback = cfgManager.getConfigForSubId(phone.getSubId())
1974                 .getInt(CarrierConfigManager.Gps.KEY_ES_SUPL_CONTROL_PLANE_SUPPORT_INT,
1975                         CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_CP_ONLY)
1976                 != CarrierConfigManager.Gps.SUPL_EMERGENCY_MODE_TYPE_DP_ONLY;
1977         if (supportsCpFallback && roamingNetworkSupportsControlPlaneFallback) {
1978             Log.d(this, "possiblyOverrideDefaultDataForEmergencyCall: not switching DDS, carrier "
1979                     + "supports CP fallback.");
1980             return CompletableFuture.completedFuture(Boolean.TRUE);
1981         }
1982 
1983         // Get extension time, may be 0 for some carriers that support ECBM as well. Use
1984         // CarrierConfig default if format fails.
1985         int extensionTime = 0;
1986         try {
1987             extensionTime = Integer.parseInt(cfgManager.getConfigForSubId(phone.getSubId())
1988                     .getString(CarrierConfigManager.Gps.KEY_ES_EXTENSION_SEC_STRING, "0"));
1989         } catch (NumberFormatException e) {
1990             // Just use default.
1991         }
1992         CompletableFuture<Boolean> modemResultFuture = new CompletableFuture<>();
1993         try {
1994             Log.d(this, "possiblyOverrideDefaultDataForEmergencyCall: overriding DDS for "
1995                     + extensionTime + "seconds");
1996             mPhoneSwitcherProxy.getPhoneSwitcher().overrideDefaultDataForEmergency(
1997                     phone.getPhoneId(), extensionTime, modemResultFuture);
1998             // Catch all exceptions, we want to continue with emergency call if possible.
1999         } catch (Exception e) {
2000             Log.w(this, "possiblyOverrideDefaultDataForEmergencyCall: exception = "
2001                     + e.getMessage());
2002             modemResultFuture = CompletableFuture.completedFuture(Boolean.FALSE);
2003         }
2004         return modemResultFuture;
2005     }
2006 
2007     /**
2008      * Get the Phone to use for an emergency call of the given emergency number address:
2009      *  a) If there are multiple Phones with the Subscriptions that support the emergency number
2010      *     address, and one of them is the default voice Phone, consider the default voice phone
2011      *     if 1.4 HAL is supported, or if it is available for emergency call.
2012      *  b) If there are multiple Phones with the Subscriptions that support the emergency number
2013      *     address, and none of them is the default voice Phone, use one of these Phones if 1.4 HAL
2014      *     is supported, or if it is available for emergency call.
2015      *  c) If there is no Phone that supports the emergency call for the address, use the defined
2016      *     Priority list to select the Phone via {@link #getFirstPhoneForEmergencyCall}.
2017      */
getPhoneForEmergencyCall(String emergencyNumberAddress)2018     public Phone getPhoneForEmergencyCall(String emergencyNumberAddress) {
2019         // Find the list of available Phones for the given emergency number address
2020         List<Phone> potentialEmergencyPhones = new ArrayList<>();
2021         int defaultVoicePhoneId = mSubscriptionManagerProxy.getDefaultVoicePhoneId();
2022         for (Phone phone : mPhoneFactoryProxy.getPhones()) {
2023             if (phone.getEmergencyNumberTracker() != null) {
2024                 if (phone.getEmergencyNumberTracker().isEmergencyNumber(
2025                         emergencyNumberAddress, true)) {
2026                     if (phone.getHalVersion().greaterOrEqual(RIL.RADIO_HAL_VERSION_1_4)
2027                             || isAvailableForEmergencyCalls(phone)) {
2028                         // a)
2029                         if (phone.getPhoneId() == defaultVoicePhoneId) {
2030                             Log.i(this, "getPhoneForEmergencyCall, Phone Id that supports"
2031                                     + " emergency number: " + phone.getPhoneId());
2032                             return phone;
2033                         }
2034                         potentialEmergencyPhones.add(phone);
2035                     }
2036                 }
2037             }
2038         }
2039         // b)
2040         if (potentialEmergencyPhones.size() > 0) {
2041             Log.i(this, "getPhoneForEmergencyCall, Phone Id that supports emergency number:"
2042                     + potentialEmergencyPhones.get(0).getPhoneId());
2043             return getFirstPhoneForEmergencyCall(potentialEmergencyPhones);
2044         }
2045         // c)
2046         return getFirstPhoneForEmergencyCall();
2047     }
2048 
2049     @VisibleForTesting
getFirstPhoneForEmergencyCall()2050     public Phone getFirstPhoneForEmergencyCall() {
2051         return getFirstPhoneForEmergencyCall(null);
2052     }
2053 
2054     /**
2055      * Retrieves the most sensible Phone to use for an emergency call using the following Priority
2056      *  list (for multi-SIM devices):
2057      *  1) The User's SIM preference for Voice calling
2058      *  2) The First Phone that is currently IN_SERVICE or is available for emergency calling
2059      *  3) Prioritize phones that have the dialed emergency number as part of their emergency
2060      *     number list
2061      *  4) If there is a PUK locked SIM, compare the SIMs that are not PUK locked. If all the SIMs
2062      *     are locked, skip to condition 5).
2063      *  5) The Phone with more Capabilities.
2064      *  6) The First Phone that has a SIM card in it (Starting from Slot 0...N)
2065      *  7) The Default Phone (Currently set as Slot 0)
2066      */
2067     @VisibleForTesting
getFirstPhoneForEmergencyCall(List<Phone> phonesWithEmergencyNumber)2068     public Phone getFirstPhoneForEmergencyCall(List<Phone> phonesWithEmergencyNumber) {
2069         // 1)
2070         int phoneId = mSubscriptionManagerProxy.getDefaultVoicePhoneId();
2071         if (phoneId != SubscriptionManager.INVALID_PHONE_INDEX) {
2072             Phone defaultPhone = mPhoneFactoryProxy.getPhone(phoneId);
2073             if (defaultPhone != null && isAvailableForEmergencyCalls(defaultPhone)) {
2074                 if (phonesWithEmergencyNumber == null
2075                         || phonesWithEmergencyNumber.contains(defaultPhone)) {
2076                     return defaultPhone;
2077                 }
2078             }
2079         }
2080 
2081         Phone firstPhoneWithSim = null;
2082         int phoneCount = mTelephonyManagerProxy.getPhoneCount();
2083         List<SlotStatus> phoneSlotStatus = new ArrayList<>(phoneCount);
2084         for (int i = 0; i < phoneCount; i++) {
2085             Phone phone = mPhoneFactoryProxy.getPhone(i);
2086             if (phone == null) {
2087                 continue;
2088             }
2089             // 2)
2090             if (isAvailableForEmergencyCalls(phone)) {
2091                 if (phonesWithEmergencyNumber == null
2092                         || phonesWithEmergencyNumber.contains(phone)) {
2093                     // the slot has the radio on & state is in service.
2094                     Log.i(this,
2095                             "getFirstPhoneForEmergencyCall, radio on & in service, Phone Id:" + i);
2096                     return phone;
2097                 }
2098             }
2099             // 5)
2100             // Store the RAF Capabilities for sorting later.
2101             int radioAccessFamily = phone.getRadioAccessFamily();
2102             SlotStatus status = new SlotStatus(i, radioAccessFamily);
2103             phoneSlotStatus.add(status);
2104             Log.i(this, "getFirstPhoneForEmergencyCall, RAF:" +
2105                     Integer.toHexString(radioAccessFamily) + " saved for Phone Id:" + i);
2106             // 4)
2107             // Report Slot's PIN/PUK lock status for sorting later.
2108             int simState = mSubscriptionManagerProxy.getSimStateForSlotIdx(i);
2109             // Record SimState.
2110             status.simState = simState;
2111             if (simState == TelephonyManager.SIM_STATE_PIN_REQUIRED ||
2112                     simState == TelephonyManager.SIM_STATE_PUK_REQUIRED) {
2113                 status.isLocked = true;
2114             }
2115             // 3) Store if the Phone has the corresponding emergency number
2116             if (phonesWithEmergencyNumber != null) {
2117                 for (Phone phoneWithEmergencyNumber : phonesWithEmergencyNumber) {
2118                     if (phoneWithEmergencyNumber != null
2119                             && phoneWithEmergencyNumber.getPhoneId() == i) {
2120                         status.hasDialedEmergencyNumber = true;
2121                     }
2122                 }
2123             }
2124             // 6)
2125             if (firstPhoneWithSim == null && mTelephonyManagerProxy.hasIccCard(i)) {
2126                 // The slot has a SIM card inserted, but is not in service, so keep track of this
2127                 // Phone. Do not return because we want to make sure that none of the other Phones
2128                 // are in service (because that is always faster).
2129                 firstPhoneWithSim = phone;
2130                 Log.i(this, "getFirstPhoneForEmergencyCall, SIM card inserted, Phone Id:" +
2131                         firstPhoneWithSim.getPhoneId());
2132             }
2133         }
2134         // 7)
2135         if (firstPhoneWithSim == null && phoneSlotStatus.isEmpty()) {
2136             if (phonesWithEmergencyNumber == null || phonesWithEmergencyNumber.isEmpty()) {
2137                 // No Phones available, get the default
2138                 Log.i(this, "getFirstPhoneForEmergencyCall, return default phone");
2139                 return  mPhoneFactoryProxy.getDefaultPhone();
2140             }
2141             return phonesWithEmergencyNumber.get(0);
2142         } else {
2143             // 5)
2144             final int defaultPhoneId = mPhoneFactoryProxy.getDefaultPhone().getPhoneId();
2145             final Phone firstOccupiedSlot = firstPhoneWithSim;
2146             if (!phoneSlotStatus.isEmpty()) {
2147                 // Only sort if there are enough elements to do so.
2148                 if (phoneSlotStatus.size() > 1) {
2149                     Collections.sort(phoneSlotStatus, (o1, o2) -> {
2150                         if (!o1.hasDialedEmergencyNumber && o2.hasDialedEmergencyNumber) {
2151                             return -1;
2152                         }
2153                         if (o1.hasDialedEmergencyNumber && !o2.hasDialedEmergencyNumber) {
2154                             return 1;
2155                         }
2156                         // Sort by non-absent SIM.
2157                         if (o1.simState == TelephonyManager.SIM_STATE_ABSENT
2158                                 && o2.simState != TelephonyManager.SIM_STATE_ABSENT) {
2159                             return -1;
2160                         }
2161                         if (o2.simState == TelephonyManager.SIM_STATE_ABSENT
2162                                 && o1.simState != TelephonyManager.SIM_STATE_ABSENT) {
2163                             return 1;
2164                         }
2165                         // First start by seeing if either of the phone slots are locked. If they
2166                         // are, then sort by non-locked SIM first. If they are both locked, sort
2167                         // by capability instead.
2168                         if (o1.isLocked && !o2.isLocked) {
2169                             return -1;
2170                         }
2171                         if (o2.isLocked && !o1.isLocked) {
2172                             return 1;
2173                         }
2174                         // sort by number of RadioAccessFamily Capabilities.
2175                         int compare = RadioAccessFamily.compare(o1.capabilities, o2.capabilities);
2176                         if (compare == 0) {
2177                             if (firstOccupiedSlot != null) {
2178                                 // If the RAF capability is the same, choose based on whether or
2179                                 // not any of the slots are occupied with a SIM card (if both
2180                                 // are, always choose the first).
2181                                 if (o1.slotId == firstOccupiedSlot.getPhoneId()) {
2182                                     return 1;
2183                                 } else if (o2.slotId == firstOccupiedSlot.getPhoneId()) {
2184                                     return -1;
2185                                 }
2186                             } else {
2187                                 // No slots have SIMs detected in them, so weight the default
2188                                 // Phone Id greater than the others.
2189                                 if (o1.slotId == defaultPhoneId) {
2190                                     return 1;
2191                                 } else if (o2.slotId == defaultPhoneId) {
2192                                     return -1;
2193                                 }
2194                             }
2195                         }
2196                         return compare;
2197                     });
2198                 }
2199                 int mostCapablePhoneId = phoneSlotStatus.get(phoneSlotStatus.size() - 1).slotId;
2200                 Log.i(this, "getFirstPhoneForEmergencyCall, Using Phone Id: " + mostCapablePhoneId +
2201                         "with highest capability");
2202                 return mPhoneFactoryProxy.getPhone(mostCapablePhoneId);
2203             } else {
2204                 // 6)
2205                 return firstPhoneWithSim;
2206             }
2207         }
2208     }
2209 
2210     /**
2211      * Returns true if the state of the Phone is IN_SERVICE or available for emergency calling only.
2212      */
isAvailableForEmergencyCalls(Phone phone)2213     private boolean isAvailableForEmergencyCalls(Phone phone) {
2214         return ServiceState.STATE_IN_SERVICE == phone.getServiceState().getState() ||
2215                 phone.getServiceState().isEmergencyOnly();
2216     }
2217 
2218     /**
2219      * Determines if the connection should allow mute.
2220      *
2221      * @param phone The current phone.
2222      * @return {@code True} if the connection should allow mute.
2223      */
allowsMute(Phone phone)2224     private boolean allowsMute(Phone phone) {
2225         // For CDMA phones, check if we are in Emergency Callback Mode (ECM).  Mute is disallowed
2226         // in ECM mode.
2227         if (phone.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
2228             if (phone.isInEcm()) {
2229                 return false;
2230             }
2231         }
2232 
2233         return true;
2234     }
2235 
getTelephonyConnectionListener()2236     TelephonyConnection.TelephonyConnectionListener getTelephonyConnectionListener() {
2237         return mTelephonyConnectionListener;
2238     }
2239 
2240     /**
2241      * When a {@link TelephonyConnection} has its underlying original connection configured,
2242      * we need to add it to the correct conference controller.
2243      *
2244      * @param connection The connection to be added to the controller
2245      */
addConnectionToConferenceController(TelephonyConnection connection)2246     public void addConnectionToConferenceController(TelephonyConnection connection) {
2247         // TODO: Need to revisit what happens when the original connection for the
2248         // TelephonyConnection changes.  If going from CDMA --> GSM (for example), the
2249         // instance of TelephonyConnection will still be a CdmaConnection, not a GsmConnection.
2250         // The CDMA conference controller makes the assumption that it will only have CDMA
2251         // connections in it, while the other conference controllers aren't as restrictive.  Really,
2252         // when we go between CDMA and GSM we should replace the TelephonyConnection.
2253         if (connection.isImsConnection()) {
2254             Log.d(this, "Adding IMS connection to conference controller: " + connection);
2255             mImsConferenceController.add(connection);
2256             mTelephonyConferenceController.remove(connection);
2257             if (connection instanceof CdmaConnection) {
2258                 mCdmaConferenceController.remove((CdmaConnection) connection);
2259             }
2260         } else {
2261             int phoneType = connection.getCall().getPhone().getPhoneType();
2262             if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {
2263                 Log.d(this, "Adding GSM connection to conference controller: " + connection);
2264                 mTelephonyConferenceController.add(connection);
2265                 if (connection instanceof CdmaConnection) {
2266                     mCdmaConferenceController.remove((CdmaConnection) connection);
2267                 }
2268             } else if (phoneType == TelephonyManager.PHONE_TYPE_CDMA &&
2269                     connection instanceof CdmaConnection) {
2270                 Log.d(this, "Adding CDMA connection to conference controller: " + connection);
2271                 mCdmaConferenceController.add((CdmaConnection) connection);
2272                 mTelephonyConferenceController.remove(connection);
2273             }
2274             Log.d(this, "Removing connection from IMS conference controller: " + connection);
2275             mImsConferenceController.remove(connection);
2276         }
2277     }
2278 
2279     /**
2280      * Create a new CDMA connection. CDMA connections have additional limitations when creating
2281      * additional calls which are handled in this method.  Specifically, CDMA has a "FLASH" command
2282      * that can be used for three purposes: merging a call, swapping unmerged calls, and adding
2283      * a new outgoing call. The function of the flash command depends on the context of the current
2284      * set of calls. This method will prevent an outgoing call from being made if it is not within
2285      * the right circumstances to support adding a call.
2286      */
checkAdditionalOutgoingCallLimits(Phone phone)2287     private Connection checkAdditionalOutgoingCallLimits(Phone phone) {
2288         if (phone.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) {
2289             // Check to see if any CDMA conference calls exist, and if they do, check them for
2290             // limitations.
2291             for (Conference conference : getAllConferences()) {
2292                 if (conference instanceof CdmaConference) {
2293                     CdmaConference cdmaConf = (CdmaConference) conference;
2294 
2295                     // If the CDMA conference has not been merged, add-call will not work, so fail
2296                     // this request to add a call.
2297                     if ((cdmaConf.getConnectionCapabilities()
2298                             & Connection.CAPABILITY_MERGE_CONFERENCE) != 0) {
2299                         return Connection.createFailedConnection(new DisconnectCause(
2300                                     DisconnectCause.RESTRICTED,
2301                                     null,
2302                                     getResources().getString(R.string.callFailed_cdma_call_limit),
2303                                     "merge-capable call exists, prevent flash command."));
2304                     }
2305                 }
2306             }
2307         }
2308 
2309         return null; // null means nothing went wrong, and call should continue.
2310     }
2311 
2312     /**
2313      * For outgoing dialed calls, potentially send a ConnectionEvent if the user is on WFC and is
2314      * dialing an international number.
2315      * @param telephonyConnection The connection.
2316      */
maybeSendInternationalCallEvent(TelephonyConnection telephonyConnection)2317     private void maybeSendInternationalCallEvent(TelephonyConnection telephonyConnection) {
2318         if (telephonyConnection == null || telephonyConnection.getPhone() == null ||
2319                 telephonyConnection.getPhone().getDefaultPhone() == null) {
2320             return;
2321         }
2322         Phone phone = telephonyConnection.getPhone().getDefaultPhone();
2323         if (phone instanceof GsmCdmaPhone) {
2324             GsmCdmaPhone gsmCdmaPhone = (GsmCdmaPhone) phone;
2325             if (telephonyConnection.isOutgoingCall() &&
2326                     gsmCdmaPhone.isNotificationOfWfcCallRequired(
2327                             telephonyConnection.getOriginalConnection().getOrigDialString())) {
2328                 // Send connection event to InCall UI to inform the user of the fact they
2329                 // are potentially placing an international call on WFC.
2330                 Log.i(this, "placeOutgoingConnection - sending international call on WFC " +
2331                         "confirmation event");
2332                 telephonyConnection.sendTelephonyConnectionEvent(
2333                         TelephonyManager.EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC, null);
2334             }
2335         }
2336     }
2337 
handleTtyModeChange(boolean isTtyEnabled)2338     private void handleTtyModeChange(boolean isTtyEnabled) {
2339         Log.i(this, "handleTtyModeChange; isTtyEnabled=%b", isTtyEnabled);
2340         mIsTtyEnabled = isTtyEnabled;
2341         for (Connection connection : getAllConnections()) {
2342             if (connection instanceof TelephonyConnection) {
2343                 TelephonyConnection telephonyConnection = (TelephonyConnection) connection;
2344                 telephonyConnection.setTtyEnabled(isTtyEnabled);
2345             }
2346         }
2347     }
2348 
closeOrDestroyConnection(Connection connection, DisconnectCause cause)2349     private void closeOrDestroyConnection(Connection connection, DisconnectCause cause) {
2350         if (connection instanceof TelephonyConnection) {
2351             TelephonyConnection telephonyConnection = (TelephonyConnection) connection;
2352             telephonyConnection.setTelephonyConnectionDisconnected(cause);
2353             // Close destroys the connection and notifies TelephonyConnection listeners.
2354             telephonyConnection.close();
2355         } else {
2356             connection.setDisconnected(cause);
2357             connection.destroy();
2358         }
2359     }
2360 
2361     /**
2362      * Adds a {@link Conference} to the telephony ConnectionService and registers a listener for
2363      * changes to the conference.  Should be used instead of {@link #addConference(Conference)}.
2364      * @param conference The conference.
2365      */
addTelephonyConference(@onNull TelephonyConferenceBase conference)2366     public void addTelephonyConference(@NonNull TelephonyConferenceBase conference) {
2367         addConference(conference);
2368         conference.addTelephonyConferenceListener(mTelephonyConferenceListener);
2369     }
2370 }
2371