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