• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.content.BroadcastReceiver;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.IntentFilter;
24 import android.content.pm.PackageManager;
25 import android.content.res.Resources;
26 import android.database.ContentObserver;
27 import android.graphics.Bitmap;
28 import android.graphics.Canvas;
29 import android.graphics.PorterDuff;
30 import android.graphics.drawable.Drawable;
31 import android.graphics.drawable.Icon;
32 import android.net.Uri;
33 import android.os.Bundle;
34 import android.os.Handler;
35 import android.os.Looper;
36 import android.os.PersistableBundle;
37 import android.os.UserHandle;
38 import android.os.UserManager;
39 import android.provider.Settings;
40 import android.telecom.PhoneAccount;
41 import android.telecom.PhoneAccountHandle;
42 import android.telecom.TelecomManager;
43 import android.telephony.CarrierConfigManager;
44 import android.telephony.PhoneStateListener;
45 import android.telephony.ServiceState;
46 import android.telephony.SubscriptionInfo;
47 import android.telephony.SubscriptionManager;
48 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
49 import android.telephony.TelephonyManager;
50 import android.telephony.ims.ImsException;
51 import android.telephony.ims.ImsMmTelManager;
52 import android.telephony.ims.feature.MmTelFeature;
53 import android.telephony.ims.stub.ImsRegistrationImplBase;
54 import android.text.TextUtils;
55 
56 import com.android.ims.ImsManager;
57 import com.android.internal.telephony.Phone;
58 import com.android.internal.telephony.PhoneFactory;
59 import com.android.internal.telephony.SubscriptionController;
60 import com.android.phone.PhoneGlobals;
61 import com.android.phone.PhoneUtils;
62 import com.android.phone.R;
63 
64 import java.util.Arrays;
65 import java.util.LinkedList;
66 import java.util.List;
67 import java.util.Optional;
68 
69 /**
70  * Owns all data we have registered with Telecom including handling dynamic addition and
71  * removal of SIMs and SIP accounts.
72  */
73 public class TelecomAccountRegistry {
74     private static final boolean DBG = false; /* STOP SHIP if true */
75 
76     // This icon is the one that is used when the Slot ID that we have for a particular SIM
77     // is not supported, i.e. SubscriptionManager.INVALID_SLOT_ID or the 5th SIM in a phone.
78     private final static int DEFAULT_SIM_ICON =  R.drawable.ic_multi_sim;
79     private final static String GROUP_PREFIX = "group_";
80 
81     final class AccountEntry implements PstnPhoneCapabilitiesNotifier.Listener {
82         private final Phone mPhone;
83         private PhoneAccount mAccount;
84         private final PstnIncomingCallNotifier mIncomingCallNotifier;
85         private final PstnPhoneCapabilitiesNotifier mPhoneCapabilitiesNotifier;
86         private boolean mIsEmergency;
87         private boolean mIsRttCapable;
88         private boolean mIsEmergencyPreferred;
89         private MmTelFeature.MmTelCapabilities mMmTelCapabilities;
90         private ImsMmTelManager.CapabilityCallback mMmtelCapabilityCallback;
91         private ImsMmTelManager mMmTelManager;
92         private final boolean mIsDummy;
93         private boolean mIsVideoCapable;
94         private boolean mIsVideoPresenceSupported;
95         private boolean mIsVideoPauseSupported;
96         private boolean mIsMergeCallSupported;
97         private boolean mIsMergeImsCallSupported;
98         private boolean mIsVideoConferencingSupported;
99         private boolean mIsMergeOfWifiCallsAllowedWhenVoWifiOff;
100         private boolean mIsManageImsConferenceCallSupported;
101         private boolean mIsUsingSimCallManager;
102         private boolean mIsShowPreciseFailedCause;
103 
AccountEntry(Phone phone, boolean isEmergency, boolean isDummy)104         AccountEntry(Phone phone, boolean isEmergency, boolean isDummy) {
105             mPhone = phone;
106             mIsEmergency = isEmergency;
107             mIsDummy = isDummy;
108             mAccount = registerPstnPhoneAccount(isEmergency, isDummy);
109             Log.i(this, "Registered phoneAccount: %s with handle: %s",
110                     mAccount, mAccount.getAccountHandle());
111             mIncomingCallNotifier = new PstnIncomingCallNotifier((Phone) mPhone);
112             mPhoneCapabilitiesNotifier = new PstnPhoneCapabilitiesNotifier((Phone) mPhone,
113                     this);
114 
115             if (mIsDummy || isEmergency) {
116                 // For dummy and emergency entries, there is no sub ID that can be assigned, so do
117                 // not register for capabilities callbacks.
118                 return;
119             }
120 
121             try {
122                 if (mPhone.getContext().getPackageManager().hasSystemFeature(
123                         PackageManager.FEATURE_TELEPHONY_IMS)) {
124                     mMmTelManager = ImsMmTelManager.createForSubscriptionId(getSubId());
125                 }
126             } catch (IllegalArgumentException e) {
127                 Log.i(this, "Not registering MmTel capabilities listener because the subid '"
128                         + getSubId() + "' is invalid: " + e.getMessage());
129                 return;
130             }
131 
132             mMmtelCapabilityCallback = new ImsMmTelManager.CapabilityCallback() {
133                 @Override
134                 public void onCapabilitiesStatusChanged(
135                         MmTelFeature.MmTelCapabilities capabilities) {
136                     mMmTelCapabilities = capabilities;
137                     updateRttCapability();
138                 }
139             };
140 
141             registerMmTelCapabilityCallback();
142         }
143 
teardown()144         void teardown() {
145             mIncomingCallNotifier.teardown();
146             mPhoneCapabilitiesNotifier.teardown();
147             if (mMmTelManager != null && mMmtelCapabilityCallback != null) {
148                 mMmTelManager.unregisterMmTelCapabilityCallback(mMmtelCapabilityCallback);
149             }
150         }
151 
registerMmTelCapabilityCallback()152         private void registerMmTelCapabilityCallback() {
153             if (mMmTelManager == null || mMmtelCapabilityCallback == null) {
154                 // The subscription id associated with this account is invalid or not associated
155                 // with a subscription. Do not register in this case.
156                 return;
157             }
158 
159             try {
160                 mMmTelManager.registerMmTelCapabilityCallback(mContext.getMainExecutor(),
161                         mMmtelCapabilityCallback);
162             } catch (ImsException e) {
163                 Log.w(this, "registerMmTelCapabilityCallback: registration failed, no ImsService"
164                         + " available. Exception: " + e.getMessage());
165                 return;
166             } catch (IllegalArgumentException e) {
167                 Log.w(this, "registerMmTelCapabilityCallback: registration failed, invalid"
168                         + " subscription, Exception" + e.getMessage());
169                 return;
170             }
171         }
172 
173         /**
174          * Trigger re-registration of this account.
175          */
reRegisterPstnPhoneAccount()176         public void reRegisterPstnPhoneAccount() {
177             PhoneAccount newAccount = buildPstnPhoneAccount(mIsEmergency, mIsDummy);
178             if (!newAccount.equals(mAccount)) {
179                 Log.i(this, "reRegisterPstnPhoneAccount: subId: " + getSubId()
180                         + " - re-register due to account change.");
181                 mTelecomManager.registerPhoneAccount(newAccount);
182                 mAccount = newAccount;
183             } else {
184                 Log.i(this, "reRegisterPstnPhoneAccount: subId: " + getSubId() + " - no change");
185             }
186         }
187 
registerPstnPhoneAccount(boolean isEmergency, boolean isDummyAccount)188         private PhoneAccount registerPstnPhoneAccount(boolean isEmergency, boolean isDummyAccount) {
189             PhoneAccount account = buildPstnPhoneAccount(mIsEmergency, mIsDummy);
190             // Register with Telecom and put into the account entry.
191             mTelecomManager.registerPhoneAccount(account);
192             return account;
193         }
194 
195         /**
196          * Registers the specified account with Telecom as a PhoneAccountHandle.
197          */
buildPstnPhoneAccount(boolean isEmergency, boolean isDummyAccount)198         private PhoneAccount buildPstnPhoneAccount(boolean isEmergency, boolean isDummyAccount) {
199             String dummyPrefix = isDummyAccount ? "Dummy " : "";
200 
201             // Build the Phone account handle.
202             PhoneAccountHandle phoneAccountHandle =
203                     PhoneUtils.makePstnPhoneAccountHandleWithPrefix(
204                             mPhone, dummyPrefix, isEmergency);
205 
206             // Populate the phone account data.
207             int subId = mPhone.getSubId();
208             String subscriberId = mPhone.getSubscriberId();
209             int color = PhoneAccount.NO_HIGHLIGHT_COLOR;
210             int slotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
211             String line1Number = mTelephonyManager.getLine1Number(subId);
212             if (line1Number == null) {
213                 line1Number = "";
214             }
215             String subNumber = mPhone.getLine1Number();
216             if (subNumber == null) {
217                 subNumber = "";
218             }
219 
220             String label;
221             String description;
222             Icon icon = null;
223 
224             // We can only get the real slotId from the SubInfoRecord, we can't calculate the
225             // slotId from the subId or the phoneId in all instances.
226             SubscriptionInfo record =
227                     mSubscriptionManager.getActiveSubscriptionInfo(subId);
228 
229             if (isEmergency) {
230                 label = mContext.getResources().getString(R.string.sim_label_emergency_calls);
231                 description =
232                         mContext.getResources().getString(R.string.sim_description_emergency_calls);
233             } else if (mTelephonyManager.getPhoneCount() == 1) {
234                 // For single-SIM devices, we show the label and description as whatever the name of
235                 // the network is.
236                 description = label = mTelephonyManager.getNetworkOperatorName();
237             } else {
238                 CharSequence subDisplayName = null;
239 
240                 if (record != null) {
241                     subDisplayName = record.getDisplayName();
242                     slotId = record.getSimSlotIndex();
243                     color = record.getIconTint();
244                     icon = Icon.createWithBitmap(record.createIconBitmap(mContext));
245                 }
246 
247                 String slotIdString;
248                 if (SubscriptionManager.isValidSlotIndex(slotId)) {
249                     slotIdString = Integer.toString(slotId);
250                 } else {
251                     slotIdString = mContext.getResources().getString(R.string.unknown);
252                 }
253 
254                 if (TextUtils.isEmpty(subDisplayName)) {
255                     // Either the sub record is not there or it has an empty display name.
256                     Log.w(this, "Could not get a display name for subid: %d", subId);
257                     subDisplayName = mContext.getResources().getString(
258                             R.string.sim_description_default, slotIdString);
259                 }
260 
261                 // The label is user-visible so let's use the display name that the user may
262                 // have set in Settings->Sim cards.
263                 label = dummyPrefix + subDisplayName;
264                 description = dummyPrefix + mContext.getResources().getString(
265                                 R.string.sim_description_default, slotIdString);
266             }
267 
268             // By default all SIM phone accounts can place emergency calls.
269             int capabilities = PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION |
270                     PhoneAccount.CAPABILITY_CALL_PROVIDER |
271                     PhoneAccount.CAPABILITY_MULTI_USER;
272 
273             if (mContext.getResources().getBoolean(R.bool.config_pstnCanPlaceEmergencyCalls)) {
274                 capabilities |= PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS;
275             }
276 
277             mIsEmergencyPreferred = isEmergencyPreferredAccount(subId, mActiveDataSubscriptionId);
278             if (mIsEmergencyPreferred) {
279                 capabilities |= PhoneAccount.CAPABILITY_EMERGENCY_PREFERRED;
280             }
281 
282             if (isRttCurrentlySupported()) {
283                 capabilities |= PhoneAccount.CAPABILITY_RTT;
284                 mIsRttCapable = true;
285             } else {
286                 mIsRttCapable = false;
287             }
288 
289             mIsVideoCapable = mPhone.isVideoEnabled() && !mIsRttCapable;
290             boolean isVideoEnabledByPlatform = ImsManager.getInstance(mPhone.getContext(),
291                     mPhone.getPhoneId()).isVtEnabledByPlatform();
292 
293             if (!mIsPrimaryUser) {
294                 Log.i(this, "Disabling video calling for secondary user.");
295                 mIsVideoCapable = false;
296                 isVideoEnabledByPlatform = false;
297             }
298 
299             if (mIsVideoCapable) {
300                 capabilities |= PhoneAccount.CAPABILITY_VIDEO_CALLING;
301             }
302 
303             if (isVideoEnabledByPlatform) {
304                 capabilities |= PhoneAccount.CAPABILITY_SUPPORTS_VIDEO_CALLING;
305             }
306 
307             mIsVideoPresenceSupported = isCarrierVideoPresenceSupported();
308             if (mIsVideoCapable && mIsVideoPresenceSupported) {
309                 capabilities |= PhoneAccount.CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE;
310             }
311 
312             if (mIsVideoCapable && isCarrierEmergencyVideoCallsAllowed()) {
313                 capabilities |= PhoneAccount.CAPABILITY_EMERGENCY_VIDEO_CALLING;
314             }
315 
316             mIsVideoPauseSupported = isCarrierVideoPauseSupported();
317             Bundle extras = new Bundle();
318             if (isCarrierInstantLetteringSupported()) {
319                 capabilities |= PhoneAccount.CAPABILITY_CALL_SUBJECT;
320                 extras.putAll(getPhoneAccountExtras());
321             }
322 
323             final boolean isHandoverFromSupported = mContext.getResources().getBoolean(
324                     R.bool.config_support_handover_from);
325             if (isHandoverFromSupported && !isEmergency) {
326                 // Only set the extra is handover is supported and this isn't the emergency-only
327                 // acct.
328                 extras.putBoolean(PhoneAccount.EXTRA_SUPPORTS_HANDOVER_FROM,
329                         isHandoverFromSupported);
330             }
331 
332             final boolean isTelephonyAudioDeviceSupported = mContext.getResources().getBoolean(
333                     R.bool.config_support_telephony_audio_device);
334             if (isTelephonyAudioDeviceSupported && !isEmergency
335                     && isCarrierUseCallRecordingTone()) {
336                 extras.putBoolean(PhoneAccount.EXTRA_PLAY_CALL_RECORDING_TONE, true);
337             }
338 
339             extras.putBoolean(PhoneAccount.EXTRA_SUPPORTS_VIDEO_CALLING_FALLBACK,
340                     mContext.getResources()
341                             .getBoolean(R.bool.config_support_video_calling_fallback));
342 
343             if (slotId != SubscriptionManager.INVALID_SIM_SLOT_INDEX) {
344                 extras.putString(PhoneAccount.EXTRA_SORT_ORDER,
345                     String.valueOf(slotId));
346             }
347 
348             mIsMergeCallSupported = isCarrierMergeCallSupported();
349             mIsMergeImsCallSupported = isCarrierMergeImsCallSupported();
350             mIsVideoConferencingSupported = isCarrierVideoConferencingSupported();
351             mIsMergeOfWifiCallsAllowedWhenVoWifiOff =
352                     isCarrierMergeOfWifiCallsAllowedWhenVoWifiOff();
353             mIsManageImsConferenceCallSupported = isCarrierManageImsConferenceCallSupported();
354             mIsUsingSimCallManager = isCarrierUsingSimCallManager();
355             mIsShowPreciseFailedCause = isCarrierShowPreciseFailedCause();
356 
357             if (isEmergency && mContext.getResources().getBoolean(
358                     R.bool.config_emergency_account_emergency_calls_only)) {
359                 capabilities |= PhoneAccount.CAPABILITY_EMERGENCY_CALLS_ONLY;
360             }
361 
362             if (icon == null) {
363                 // TODO: Switch to using Icon.createWithResource() once that supports tinting.
364                 Resources res = mContext.getResources();
365                 Drawable drawable = res.getDrawable(DEFAULT_SIM_ICON, null);
366                 drawable.setTint(res.getColor(R.color.default_sim_icon_tint_color, null));
367                 drawable.setTintMode(PorterDuff.Mode.SRC_ATOP);
368 
369                 int width = drawable.getIntrinsicWidth();
370                 int height = drawable.getIntrinsicHeight();
371                 Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
372                 Canvas canvas = new Canvas(bitmap);
373                 drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
374                 drawable.draw(canvas);
375 
376                 icon = Icon.createWithBitmap(bitmap);
377             }
378 
379             // Check to see if the newly registered account should replace the old account.
380             String groupId = "";
381             String[] mergedImsis = mTelephonyManager.getMergedSubscriberIds();
382             boolean isMergedSim = false;
383             if (mergedImsis != null && subscriberId != null && !isEmergency) {
384                 for (String imsi : mergedImsis) {
385                     if (imsi.equals(subscriberId)) {
386                         isMergedSim = true;
387                         break;
388                     }
389                 }
390             }
391             if(isMergedSim) {
392                 groupId = GROUP_PREFIX + line1Number;
393                 Log.i(this, "Adding Merged Account with group: " + Log.pii(groupId));
394             }
395 
396             PhoneAccount account = PhoneAccount.builder(phoneAccountHandle, label)
397                     .setAddress(Uri.fromParts(PhoneAccount.SCHEME_TEL, line1Number, null))
398                     .setSubscriptionAddress(
399                             Uri.fromParts(PhoneAccount.SCHEME_TEL, subNumber, null))
400                     .setCapabilities(capabilities)
401                     .setIcon(icon)
402                     .setHighlightColor(color)
403                     .setShortDescription(description)
404                     .setSupportedUriSchemes(Arrays.asList(
405                             PhoneAccount.SCHEME_TEL, PhoneAccount.SCHEME_VOICEMAIL))
406                     .setExtras(extras)
407                     .setGroupId(groupId)
408                     .build();
409 
410             return account;
411         }
412 
getPhoneAccountHandle()413         public PhoneAccountHandle getPhoneAccountHandle() {
414             return mAccount != null ? mAccount.getAccountHandle() : null;
415         }
416 
getSubId()417         public int getSubId() {
418             return mPhone.getSubId();
419         }
420 
421         /**
422          * In some cases, we need to try sending the emergency call over this PhoneAccount due to
423          * restrictions and limitations in MSIM configured devices. This includes the following:
424          * 1) The device does not support GNSS SUPL requests on the non-DDS subscription due to
425          *   modem limitations. If the device does not support SUPL on non-DDS, we need to try the
426          *   emergency call on the DDS subscription first to allow for SUPL to be completed.
427          *
428          * @return true if Telecom should prefer this PhoneAccount, false if there is no preference
429          * needed.
430          */
isEmergencyPreferredAccount(int subId, int activeDataSubId)431         private boolean isEmergencyPreferredAccount(int subId, int activeDataSubId) {
432             Log.d(this, "isEmergencyPreferredAccount: subId=" + subId + ", activeData="
433                     + activeDataSubId);
434             final boolean gnssSuplRequiresDefaultData = mContext.getResources().getBoolean(
435                     R.bool.config_gnss_supl_requires_default_data_for_emergency);
436             if (!gnssSuplRequiresDefaultData) {
437                 Log.d(this, "isEmergencyPreferredAccount: Device does not require preference.");
438                 // No preference is necessary.
439                 return false;
440             }
441 
442             SubscriptionController controller = SubscriptionController.getInstance();
443             if (controller == null) {
444                 Log.d(this, "isEmergencyPreferredAccount: SubscriptionController not available.");
445                 return false;
446             }
447             // Only set an emergency preference on devices with multiple active subscriptions
448             // (include opportunistic subscriptions) in this check.
449             // API says never null, but this can return null in testing.
450             int[] activeSubIds = controller.getActiveSubIdList(false);
451             if (activeSubIds == null || activeSubIds.length <= 1) {
452                 Log.d(this, "isEmergencyPreferredAccount: one or less active subscriptions.");
453                 return false;
454             }
455             // Check to see if this PhoneAccount is associated with the default Data subscription.
456             if (!SubscriptionManager.isValidSubscriptionId(subId)) {
457                 Log.d(this, "isEmergencyPreferredAccount: provided subId " + subId + "is not "
458                         + "valid.");
459                 return false;
460             }
461             int userDefaultData = controller.getDefaultDataSubId();
462             boolean isActiveDataValid = SubscriptionManager.isValidSubscriptionId(activeDataSubId);
463             boolean isActiveDataOpportunistic = isActiveDataValid
464                     && controller.isOpportunistic(activeDataSubId);
465             // compare the activeDataSubId to the subId specified only if it is valid and not an
466             // opportunistic subscription (only supports data). If not, use the current default
467             // defined by the user.
468             Log.d(this, "isEmergencyPreferredAccount: userDefaultData=" + userDefaultData
469                     + ", isActiveDataOppurtunistic=" + isActiveDataOpportunistic);
470             return subId == ((isActiveDataValid && !isActiveDataOpportunistic) ? activeDataSubId :
471                     userDefaultData);
472         }
473 
474         /**
475          * Determines from carrier configuration whether pausing of IMS video calls is supported.
476          *
477          * @return {@code true} if pausing IMS video calls is supported.
478          */
isCarrierVideoPauseSupported()479         private boolean isCarrierVideoPauseSupported() {
480             // Check if IMS video pause is supported.
481             PersistableBundle b =
482                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
483             return b != null &&
484                     b.getBoolean(CarrierConfigManager.KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL);
485         }
486 
487         /**
488          * Determines from carrier configuration whether RCS presence indication for video calls is
489          * supported.
490          *
491          * @return {@code true} if RCS presence indication for video calls is supported.
492          */
isCarrierVideoPresenceSupported()493         private boolean isCarrierVideoPresenceSupported() {
494             PersistableBundle b =
495                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
496             return b != null &&
497                     b.getBoolean(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL);
498         }
499 
500         /**
501          * Determines from carrier config whether instant lettering is supported.
502          *
503          * @return {@code true} if instant lettering is supported, {@code false} otherwise.
504          */
isCarrierInstantLetteringSupported()505         private boolean isCarrierInstantLetteringSupported() {
506             PersistableBundle b =
507                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
508             return b != null &&
509                     b.getBoolean(CarrierConfigManager.KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL);
510         }
511 
512         /**
513          * Determines from carrier config whether merging calls is supported.
514          *
515          * @return {@code true} if merging calls is supported, {@code false} otherwise.
516          */
isCarrierMergeCallSupported()517         private boolean isCarrierMergeCallSupported() {
518             PersistableBundle b =
519                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
520             return b != null &&
521                     b.getBoolean(CarrierConfigManager.KEY_SUPPORT_CONFERENCE_CALL_BOOL);
522         }
523 
524         /**
525          * Determines from carrier config whether merging IMS calls is supported.
526          *
527          * @return {@code true} if merging IMS calls is supported, {@code false} otherwise.
528          */
isCarrierMergeImsCallSupported()529         private boolean isCarrierMergeImsCallSupported() {
530             PersistableBundle b =
531                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
532             return b.getBoolean(CarrierConfigManager.KEY_SUPPORT_IMS_CONFERENCE_CALL_BOOL);
533         }
534 
535         /**
536          * Determines from carrier config whether emergency video calls are supported.
537          *
538          * @return {@code true} if emergency video calls are allowed, {@code false} otherwise.
539          */
isCarrierEmergencyVideoCallsAllowed()540         private boolean isCarrierEmergencyVideoCallsAllowed() {
541             PersistableBundle b =
542                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
543             return b != null &&
544                     b.getBoolean(CarrierConfigManager.KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL);
545         }
546 
547         /**
548          * Determines from carrier config whether video conferencing is supported.
549          *
550          * @return {@code true} if video conferencing is supported, {@code false} otherwise.
551          */
isCarrierVideoConferencingSupported()552         private boolean isCarrierVideoConferencingSupported() {
553             PersistableBundle b =
554                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
555             return b != null &&
556                     b.getBoolean(CarrierConfigManager.KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL);
557         }
558 
559         /**
560          * Determines from carrier config whether merging of wifi calls is allowed when VoWIFI is
561          * turned off.
562          *
563          * @return {@code true} merging of wifi calls when VoWIFI is disabled should be prevented,
564          *      {@code false} otherwise.
565          */
isCarrierMergeOfWifiCallsAllowedWhenVoWifiOff()566         private boolean isCarrierMergeOfWifiCallsAllowedWhenVoWifiOff() {
567             PersistableBundle b =
568                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
569             return b != null && b.getBoolean(
570                     CarrierConfigManager.KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL);
571         }
572 
573         /**
574          * Determines from carrier config whether managing IMS conference calls is supported.
575          *
576          * @return {@code true} if managing IMS conference calls is supported,
577          *         {@code false} otherwise.
578          */
isCarrierManageImsConferenceCallSupported()579         private boolean isCarrierManageImsConferenceCallSupported() {
580             PersistableBundle b =
581                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
582             return b.getBoolean(CarrierConfigManager.KEY_SUPPORT_MANAGE_IMS_CONFERENCE_CALL_BOOL);
583         }
584 
585         /**
586          * Determines from carrier config whether the carrier uses a sim call manager.
587          *
588          * @return {@code true} if the carrier uses a sim call manager,
589          *         {@code false} otherwise.
590          */
isCarrierUsingSimCallManager()591         private boolean isCarrierUsingSimCallManager() {
592             PersistableBundle b =
593                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
594             return !TextUtils.isEmpty(
595                     b.getString(CarrierConfigManager.KEY_DEFAULT_SIM_CALL_MANAGER_STRING));
596         }
597 
598         /**
599          * Determines from carrier config whether showing percise call diconnect cause to user
600          * is supported.
601          *
602          * @return {@code true} if showing percise call diconnect cause to user is supported,
603          *         {@code false} otherwise.
604          */
isCarrierShowPreciseFailedCause()605         private boolean isCarrierShowPreciseFailedCause() {
606             PersistableBundle b =
607                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
608             return b.getBoolean(CarrierConfigManager.KEY_SHOW_PRECISE_FAILED_CAUSE_BOOL);
609         }
610 
611         /**
612          * Determines from carrier config whether the carrier requires the use of a call recording
613          * tone.
614          *
615          * @return {@code true} if a call recording tone should be used, {@code false} otherwise.
616          */
isCarrierUseCallRecordingTone()617         private boolean isCarrierUseCallRecordingTone() {
618             PersistableBundle b =
619                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
620             return b.getBoolean(CarrierConfigManager.KEY_PLAY_CALL_RECORDING_TONE_BOOL);
621         }
622 
623         /**
624          * Where a device supports instant lettering and call subjects, retrieves the necessary
625          * PhoneAccount extras for those features.
626          *
627          * @return The {@link PhoneAccount} extras associated with the current subscription.
628          */
getPhoneAccountExtras()629         private Bundle getPhoneAccountExtras() {
630             PersistableBundle b =
631                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
632 
633             int instantLetteringMaxLength = b.getInt(
634                     CarrierConfigManager.KEY_CARRIER_INSTANT_LETTERING_LENGTH_LIMIT_INT);
635             String instantLetteringEncoding = b.getString(
636                     CarrierConfigManager.KEY_CARRIER_INSTANT_LETTERING_ENCODING_STRING);
637             Bundle phoneAccountExtras = new Bundle();
638             phoneAccountExtras.putInt(PhoneAccount.EXTRA_CALL_SUBJECT_MAX_LENGTH,
639                     instantLetteringMaxLength);
640             phoneAccountExtras.putString(PhoneAccount.EXTRA_CALL_SUBJECT_CHARACTER_ENCODING,
641                     instantLetteringEncoding);
642             return phoneAccountExtras;
643         }
644 
645         /**
646          * Receives callback from {@link PstnPhoneCapabilitiesNotifier} when the video capabilities
647          * have changed.
648          *
649          * @param isVideoCapable {@code true} if video is capable.
650          */
651         @Override
onVideoCapabilitiesChanged(boolean isVideoCapable)652         public void onVideoCapabilitiesChanged(boolean isVideoCapable) {
653             mIsVideoCapable = isVideoCapable;
654             synchronized (mAccountsLock) {
655                 if (!mAccounts.contains(this)) {
656                     // Account has already been torn down, don't try to register it again.
657                     // This handles the case where teardown has already happened, and we got a video
658                     // update that lost the race for the mAccountsLock.  In such a scenario by the
659                     // time we get here, the original phone account could have been torn down.
660                     return;
661                 }
662                 mAccount = registerPstnPhoneAccount(mIsEmergency, mIsDummy);
663             }
664         }
665 
updateRttCapability()666         public void updateRttCapability() {
667             boolean isRttEnabled = isRttCurrentlySupported();
668             if (isRttEnabled != mIsRttCapable) {
669                 Log.i(this, "updateRttCapability - changed, new value: " + isRttEnabled);
670                 mAccount = registerPstnPhoneAccount(mIsEmergency, mIsDummy);
671             }
672         }
673 
updateDefaultDataSubId(int activeDataSubId)674         public void updateDefaultDataSubId(int activeDataSubId) {
675             boolean isEmergencyPreferred = isEmergencyPreferredAccount(mPhone.getSubId(),
676                     activeDataSubId);
677             if (isEmergencyPreferred != mIsEmergencyPreferred) {
678                 Log.i(this, "updateDefaultDataSubId - changed, new value: " + isEmergencyPreferred);
679                 mAccount = registerPstnPhoneAccount(mIsEmergency, mIsDummy);
680             }
681         }
682 
683         /**
684          * Determines whether RTT is supported given the current state of the
685          * device.
686          */
isRttCurrentlySupported()687         private boolean isRttCurrentlySupported() {
688             boolean hasVoiceAvailability = isImsVoiceAvailable();
689 
690             boolean isRttSupported = PhoneGlobals.getInstance().phoneMgr
691                     .isRttEnabled(mPhone.getSubId());
692 
693             boolean isRoaming = mTelephonyManager.isNetworkRoaming(mPhone.getSubId());
694 
695             return hasVoiceAvailability && isRttSupported && !isRoaming;
696         }
697 
698         /**
699          * Indicates whether this account supports pausing video calls.
700          * @return {@code true} if the account supports pausing video calls, {@code false}
701          * otherwise.
702          */
isVideoPauseSupported()703         public boolean isVideoPauseSupported() {
704             return mIsVideoCapable && mIsVideoPauseSupported;
705         }
706 
707         /**
708          * Indicates whether this account supports merging calls (i.e. conferencing).
709          * @return {@code true} if the account supports merging calls, {@code false} otherwise.
710          */
isMergeCallSupported()711         public boolean isMergeCallSupported() {
712             return mIsMergeCallSupported;
713         }
714 
715         /**
716          * Indicates whether this account supports merging IMS calls (i.e. conferencing).
717          * @return {@code true} if the account supports merging IMS calls, {@code false} otherwise.
718          */
isMergeImsCallSupported()719         public boolean isMergeImsCallSupported() {
720             return mIsMergeImsCallSupported;
721         }
722 
723         /**
724          * Indicates whether this account supports video conferencing.
725          * @return {@code true} if the account supports video conferencing, {@code false} otherwise.
726          */
isVideoConferencingSupported()727         public boolean isVideoConferencingSupported() {
728             return mIsVideoConferencingSupported;
729         }
730 
731         /**
732          * Indicate whether this account allow merging of wifi calls when VoWIFI is off.
733          * @return {@code true} if allowed, {@code false} otherwise.
734          */
isMergeOfWifiCallsAllowedWhenVoWifiOff()735         public boolean isMergeOfWifiCallsAllowedWhenVoWifiOff() {
736             return mIsMergeOfWifiCallsAllowedWhenVoWifiOff;
737         }
738 
739         /**
740          * Indicates whether this account supports managing IMS conference calls
741          * @return {@code true} if the account supports managing IMS conference calls,
742          *         {@code false} otherwise.
743          */
isManageImsConferenceCallSupported()744         public boolean isManageImsConferenceCallSupported() {
745             return mIsManageImsConferenceCallSupported;
746         }
747 
748         /**
749          * Indicates whether this account uses a sim call manger.
750          * @return {@code true} if the account uses a sim call manager,
751          *         {@code false} otherwise.
752          */
isUsingSimCallManager()753         public boolean isUsingSimCallManager() {
754             return mIsUsingSimCallManager;
755         }
756 
757         /**
758          * Indicates whether this account supports showing the precise call disconnect cause
759          * to user (i.e. conferencing).
760          * @return {@code true} if the account supports showing the precise call disconnect cause,
761          *         {@code false} otherwise.
762          */
isShowPreciseFailedCause()763         public boolean isShowPreciseFailedCause() {
764             return mIsShowPreciseFailedCause;
765         }
766 
isImsVoiceAvailable()767         private boolean isImsVoiceAvailable() {
768             if (mMmTelCapabilities != null) {
769                 return mMmTelCapabilities.isCapable(
770                         MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
771             }
772 
773             if (mMmTelManager == null) {
774                 // The Subscription is invalid, so IMS is unavailable.
775                 return false;
776             }
777 
778             // In the rare case that mMmTelCapabilities hasn't been set, try fetching it
779             // directly and register callback.
780             registerMmTelCapabilityCallback();
781             return mMmTelManager.isAvailable(ImsRegistrationImplBase.REGISTRATION_TECH_LTE,
782                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE)
783                     || mMmTelManager.isAvailable(ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN,
784                     MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE);
785         }
786     }
787 
788     private OnSubscriptionsChangedListener mOnSubscriptionsChangedListener =
789             new OnSubscriptionsChangedListener() {
790         @Override
791         public void onSubscriptionsChanged() {
792             // Any time the SubscriptionInfo changes...rerun the setup
793             Log.i(this, "onSubscriptionsChanged - update accounts");
794             tearDownAccounts();
795             setupAccounts();
796         }
797     };
798 
799     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
800         @Override
801         public void onReceive(Context context, Intent intent) {
802             if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
803                 Log.i(this, "User changed, re-registering phone accounts.");
804 
805                 int userHandleId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
806                 UserHandle currentUserHandle = new UserHandle(userHandleId);
807                 mIsPrimaryUser = UserManager.get(mContext).getPrimaryUser().getUserHandle()
808                         .equals(currentUserHandle);
809 
810                 // Any time the user changes, re-register the accounts.
811                 tearDownAccounts();
812                 setupAccounts();
813             } else if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(
814                     intent.getAction())) {
815                 Log.i(this, "Carrier-config changed, checking for phone account updates.");
816                 int subId = intent.getIntExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX,
817                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
818                 handleCarrierConfigChange(subId);
819             }
820         }
821     };
822 
823     private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
824         @Override
825         public void onServiceStateChanged(ServiceState serviceState) {
826             int newState = serviceState.getState();
827             if (newState == ServiceState.STATE_IN_SERVICE && mServiceState != newState) {
828                 tearDownAccounts();
829                 setupAccounts();
830             } else {
831                 synchronized (mAccountsLock) {
832                     for (AccountEntry account : mAccounts) {
833                         account.updateRttCapability();
834                     }
835                 }
836             }
837             mServiceState = newState;
838         }
839 
840         @Override
841         public void onActiveDataSubscriptionIdChanged(int subId) {
842             mActiveDataSubscriptionId = subId;
843             synchronized (mAccountsLock) {
844                 for (AccountEntry account : mAccounts) {
845                     account.updateDefaultDataSubId(mActiveDataSubscriptionId);
846                 }
847             }
848         }
849     };
850 
851     private static TelecomAccountRegistry sInstance;
852     private final Context mContext;
853     private final TelecomManager mTelecomManager;
854     private final TelephonyManager mTelephonyManager;
855     private final SubscriptionManager mSubscriptionManager;
856     private List<AccountEntry> mAccounts = new LinkedList<AccountEntry>();
857     private final Object mAccountsLock = new Object();
858     private int mServiceState = ServiceState.STATE_POWER_OFF;
859     private int mActiveDataSubscriptionId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
860     private boolean mIsPrimaryUser = true;
861 
862     // TODO: Remove back-pointer from app singleton to Service, since this is not a preferred
863     // pattern; redesign. This was added to fix a late release bug.
864     private TelephonyConnectionService mTelephonyConnectionService;
865 
TelecomAccountRegistry(Context context)866     TelecomAccountRegistry(Context context) {
867         mContext = context;
868         mTelecomManager = TelecomManager.from(context);
869         mTelephonyManager = TelephonyManager.from(context);
870         mSubscriptionManager = SubscriptionManager.from(context);
871     }
872 
873     /**
874      * Get the singleton instance.
875      */
getInstance(Context context)876     public static synchronized TelecomAccountRegistry getInstance(Context context) {
877         if (sInstance == null && context != null) {
878             sInstance = new TelecomAccountRegistry(context);
879         }
880         return sInstance;
881     }
882 
setTelephonyConnectionService(TelephonyConnectionService telephonyConnectionService)883     void setTelephonyConnectionService(TelephonyConnectionService telephonyConnectionService) {
884         this.mTelephonyConnectionService = telephonyConnectionService;
885     }
886 
getTelephonyConnectionService()887     TelephonyConnectionService getTelephonyConnectionService() {
888         return mTelephonyConnectionService;
889     }
890 
891     /**
892      * Determines if the {@link AccountEntry} associated with a {@link PhoneAccountHandle} supports
893      * pausing video calls.
894      *
895      * @param handle The {@link PhoneAccountHandle}.
896      * @return {@code True} if video pausing is supported.
897      */
isVideoPauseSupported(PhoneAccountHandle handle)898     boolean isVideoPauseSupported(PhoneAccountHandle handle) {
899         synchronized (mAccountsLock) {
900             for (AccountEntry entry : mAccounts) {
901                 if (entry.getPhoneAccountHandle().equals(handle)) {
902                     return entry.isVideoPauseSupported();
903                 }
904             }
905         }
906         return false;
907     }
908 
909     /**
910      * Determines if the {@link AccountEntry} associated with a {@link PhoneAccountHandle} supports
911      * merging calls.
912      *
913      * @param handle The {@link PhoneAccountHandle}.
914      * @return {@code True} if merging calls is supported.
915      */
isMergeCallSupported(PhoneAccountHandle handle)916     boolean isMergeCallSupported(PhoneAccountHandle handle) {
917         synchronized (mAccountsLock) {
918             for (AccountEntry entry : mAccounts) {
919                 if (entry.getPhoneAccountHandle().equals(handle)) {
920                     return entry.isMergeCallSupported();
921                 }
922             }
923         }
924         return false;
925     }
926 
927     /**
928      * Determines if the {@link AccountEntry} associated with a {@link PhoneAccountHandle} supports
929      * video conferencing.
930      *
931      * @param handle The {@link PhoneAccountHandle}.
932      * @return {@code True} if video conferencing is supported.
933      */
isVideoConferencingSupported(PhoneAccountHandle handle)934     boolean isVideoConferencingSupported(PhoneAccountHandle handle) {
935         synchronized (mAccountsLock) {
936             for (AccountEntry entry : mAccounts) {
937                 if (entry.getPhoneAccountHandle().equals(handle)) {
938                     return entry.isVideoConferencingSupported();
939                 }
940             }
941         }
942         return false;
943     }
944 
945     /**
946      * Determines if the {@link AccountEntry} associated with a {@link PhoneAccountHandle} allows
947      * merging of wifi calls when VoWIFI is disabled.
948      *
949      * @param handle The {@link PhoneAccountHandle}.
950      * @return {@code True} if merging of wifi calls is allowed when VoWIFI is disabled.
951      */
isMergeOfWifiCallsAllowedWhenVoWifiOff(final PhoneAccountHandle handle)952     boolean isMergeOfWifiCallsAllowedWhenVoWifiOff(final PhoneAccountHandle handle) {
953         synchronized (mAccountsLock) {
954             Optional<AccountEntry> result = mAccounts.stream().filter(
955                     entry -> entry.getPhoneAccountHandle().equals(handle)).findFirst();
956 
957             if (result.isPresent()) {
958                 return result.get().isMergeOfWifiCallsAllowedWhenVoWifiOff();
959             } else {
960                 return false;
961             }
962         }
963     }
964 
965     /**
966      * Determines if the {@link AccountEntry} associated with a {@link PhoneAccountHandle} supports
967      * merging IMS calls.
968      *
969      * @param handle The {@link PhoneAccountHandle}.
970      * @return {@code True} if merging IMS calls is supported.
971      */
isMergeImsCallSupported(PhoneAccountHandle handle)972     boolean isMergeImsCallSupported(PhoneAccountHandle handle) {
973         synchronized (mAccountsLock) {
974             for (AccountEntry entry : mAccounts) {
975                 if (entry.getPhoneAccountHandle().equals(handle)) {
976                     return entry.isMergeImsCallSupported();
977                 }
978             }
979         }
980         return false;
981     }
982 
983     /**
984      * Determines if the {@link AccountEntry} associated with a {@link PhoneAccountHandle} supports
985      * managing IMS conference calls.
986      *
987      * @param handle The {@link PhoneAccountHandle}.
988      * @return {@code True} if managing IMS conference calls is supported.
989      */
isManageImsConferenceCallSupported(PhoneAccountHandle handle)990     boolean isManageImsConferenceCallSupported(PhoneAccountHandle handle) {
991         synchronized (mAccountsLock) {
992             for (AccountEntry entry : mAccounts) {
993                 if (entry.getPhoneAccountHandle().equals(handle)) {
994                     return entry.isManageImsConferenceCallSupported();
995                 }
996             }
997         }
998         return false;
999     }
1000 
1001     /**
1002      * showing precise call disconnect cause to the user.
1003      *
1004      * @param handle The {@link PhoneAccountHandle}.
1005      * @return {@code True} if showing precise call disconnect cause to the user is supported.
1006      */
isShowPreciseFailedCause(PhoneAccountHandle handle)1007     boolean isShowPreciseFailedCause(PhoneAccountHandle handle) {
1008         synchronized (mAccountsLock) {
1009             for (AccountEntry entry : mAccounts) {
1010                 if (entry.getPhoneAccountHandle().equals(handle)) {
1011                     return entry.isShowPreciseFailedCause();
1012                 }
1013             }
1014         }
1015         return false;
1016     }
1017 
1018     /**
1019      * @return Reference to the {@code TelecomAccountRegistry}'s subscription manager.
1020      */
getSubscriptionManager()1021     SubscriptionManager getSubscriptionManager() {
1022         return mSubscriptionManager;
1023     }
1024 
1025     /**
1026      * Returns the address (e.g. the phone number) associated with a subscription.
1027      *
1028      * @param handle The phone account handle to find the subscription address for.
1029      * @return The address.
1030      */
getAddress(PhoneAccountHandle handle)1031     public Uri getAddress(PhoneAccountHandle handle) {
1032         synchronized (mAccountsLock) {
1033             for (AccountEntry entry : mAccounts) {
1034                 if (entry.getPhoneAccountHandle().equals(handle)) {
1035                     return entry.mAccount.getAddress();
1036                 }
1037             }
1038         }
1039         return null;
1040     }
1041 
1042     /**
1043      * Returns whethere a the subscription associated with a {@link PhoneAccountHandle} is using a
1044      * sim call manager.
1045      *
1046      * @param handle The phone account handle to find the subscription address for.
1047      * @return {@code true} if a sim call manager is in use, {@code false} otherwise.
1048      */
isUsingSimCallManager(PhoneAccountHandle handle)1049     public boolean isUsingSimCallManager(PhoneAccountHandle handle) {
1050         synchronized (mAccountsLock) {
1051             for (AccountEntry entry : mAccounts) {
1052                 if (entry.getPhoneAccountHandle().equals(handle)) {
1053                     return entry.isUsingSimCallManager();
1054                 }
1055             }
1056         }
1057         return false;
1058     }
1059 
1060     /**
1061      * Sets up all the phone accounts for SIMs on first boot.
1062      */
setupOnBoot()1063     public void setupOnBoot() {
1064         // TODO: When this object "finishes" we should unregister by invoking
1065         // SubscriptionManager.getInstance(mContext).unregister(mOnSubscriptionsChangedListener);
1066         // This is not strictly necessary because it will be unregistered if the
1067         // notification fails but it is good form.
1068 
1069         // Register for SubscriptionInfo list changes which is guaranteed
1070         // to invoke onSubscriptionsChanged the first time.
1071         SubscriptionManager.from(mContext).addOnSubscriptionsChangedListener(
1072                 mOnSubscriptionsChangedListener);
1073 
1074         // We also need to listen for changes to the service state (e.g. emergency -> in service)
1075         // because this could signal a removal or addition of a SIM in a single SIM phone.
1076         mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE
1077                 | PhoneStateListener.LISTEN_ACTIVE_DATA_SUBSCRIPTION_ID_CHANGE);
1078 
1079         // Listen for user switches.  When the user switches, we need to ensure that if the current
1080         // use is not the primary user we disable video calling.
1081         IntentFilter filter = new IntentFilter();
1082         filter.addAction(Intent.ACTION_USER_SWITCHED);
1083         filter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
1084         mContext.registerReceiver(mReceiver, filter);
1085 
1086         // Listen to the RTT system setting so that we update it when the user flips it.
1087         ContentObserver rttUiSettingObserver = new ContentObserver(
1088                 new Handler(Looper.getMainLooper())) {
1089             @Override
1090             public void onChange(boolean selfChange) {
1091                 synchronized (mAccountsLock) {
1092                     for (AccountEntry account : mAccounts) {
1093                         account.updateRttCapability();
1094                     }
1095                 }
1096             }
1097         };
1098 
1099         Uri rttSettingUri = Settings.Secure.getUriFor(Settings.Secure.RTT_CALLING_MODE);
1100         mContext.getContentResolver().registerContentObserver(
1101                 rttSettingUri, false, rttUiSettingObserver);
1102     }
1103 
1104     /**
1105      * Determines if the list of {@link AccountEntry}(s) contains an {@link AccountEntry} with a
1106      * specified {@link PhoneAccountHandle}.
1107      *
1108      * @param handle The {@link PhoneAccountHandle}.
1109      * @return {@code True} if an entry exists.
1110      */
hasAccountEntryForPhoneAccount(PhoneAccountHandle handle)1111     boolean hasAccountEntryForPhoneAccount(PhoneAccountHandle handle) {
1112         synchronized (mAccountsLock) {
1113             for (AccountEntry entry : mAccounts) {
1114                 if (entry.getPhoneAccountHandle().equals(handle)) {
1115                     return true;
1116                 }
1117             }
1118         }
1119         return false;
1120     }
1121 
1122     /**
1123      * Un-registers any {@link PhoneAccount}s which are no longer present in the list
1124      * {@code AccountEntry}(s).
1125      */
cleanupPhoneAccounts()1126     private void cleanupPhoneAccounts() {
1127         ComponentName telephonyComponentName =
1128                 new ComponentName(mContext, TelephonyConnectionService.class);
1129         // This config indicates whether the emergency account was flagged as emergency calls only
1130         // in which case we need to consider all phone accounts, not just the call capable ones.
1131         final boolean emergencyCallsOnlyEmergencyAccount = mContext.getResources().getBoolean(
1132                 R.bool.config_emergency_account_emergency_calls_only);
1133         List<PhoneAccountHandle> accountHandles = emergencyCallsOnlyEmergencyAccount
1134                 ? mTelecomManager.getAllPhoneAccountHandles()
1135                 : mTelecomManager.getCallCapablePhoneAccounts(true /* includeDisabled */);
1136 
1137         for (PhoneAccountHandle handle : accountHandles) {
1138             if (telephonyComponentName.equals(handle.getComponentName()) &&
1139                     !hasAccountEntryForPhoneAccount(handle)) {
1140                 Log.i(this, "Unregistering phone account %s.", handle);
1141                 mTelecomManager.unregisterPhoneAccount(handle);
1142             }
1143         }
1144     }
1145 
setupAccounts()1146     private void setupAccounts() {
1147         // Go through SIM-based phones and register ourselves -- registering an existing account
1148         // will cause the existing entry to be replaced.
1149         Phone[] phones = PhoneFactory.getPhones();
1150         Log.i(this, "setupAccounts: Found %d phones.  Attempting to register.", phones.length);
1151 
1152         final boolean phoneAccountsEnabled = mContext.getResources().getBoolean(
1153                 R.bool.config_pstn_phone_accounts_enabled);
1154 
1155         synchronized (mAccountsLock) {
1156             try {
1157                 if (phoneAccountsEnabled) {
1158                     for (Phone phone : phones) {
1159                         int subscriptionId = phone.getSubId();
1160                         Log.i(this, "setupAccounts: Phone with subscription id %d", subscriptionId);
1161                         // setupAccounts can be called multiple times during service changes.
1162                         // Don't add an account if the Icc has not been set yet.
1163                         if (!SubscriptionManager.isValidSubscriptionId(subscriptionId)
1164                                 || phone.getFullIccSerialNumber() == null) {
1165                             Log.d(this, "setupAccounts: skipping invalid subid %d", subscriptionId);
1166                             continue;
1167                         }
1168                         // Don't add account if it's opportunistic subscription, which is considered
1169                         // data only for now.
1170                         SubscriptionInfo info = SubscriptionManager.from(mContext)
1171                                 .getActiveSubscriptionInfo(subscriptionId);
1172                         if (info == null || info.isOpportunistic()) {
1173                             Log.d(this, "setupAccounts: skipping unknown or opportunistic subid %d",
1174                                     subscriptionId);
1175                             continue;
1176                         }
1177 
1178                         mAccounts.add(new AccountEntry(phone, false /* emergency */,
1179                                 false /* isDummy */));
1180                     }
1181                 }
1182             } finally {
1183                 // If we did not list ANY accounts, we need to provide a "default" SIM account
1184                 // for emergency numbers since no actual SIM is needed for dialing emergency
1185                 // numbers but a phone account is.
1186                 if (mAccounts.isEmpty()) {
1187                     Log.i(this, "setupAccounts: adding default");
1188                     mAccounts.add(
1189                             new AccountEntry(PhoneFactory.getDefaultPhone(), true /* emergency */,
1190                                     false /* isDummy */));
1191                 }
1192             }
1193 
1194             // Add a fake account entry.
1195             if (DBG && phones.length > 0 && "TRUE".equals(System.getProperty("dummy_sim"))) {
1196                 mAccounts.add(new AccountEntry(phones[0], false /* emergency */,
1197                         true /* isDummy */));
1198             }
1199         }
1200 
1201         // Clean up any PhoneAccounts that are no longer relevant
1202         cleanupPhoneAccounts();
1203 
1204         // At some point, the phone account ID was switched from the subId to the iccId.
1205         // If there is a default account, check if this is the case, and upgrade the default account
1206         // from using the subId to iccId if so.
1207         PhoneAccountHandle defaultPhoneAccount =
1208                 mTelecomManager.getUserSelectedOutgoingPhoneAccount();
1209         ComponentName telephonyComponentName =
1210                 new ComponentName(mContext, TelephonyConnectionService.class);
1211 
1212         if (defaultPhoneAccount != null &&
1213                 telephonyComponentName.equals(defaultPhoneAccount.getComponentName()) &&
1214                 !hasAccountEntryForPhoneAccount(defaultPhoneAccount)) {
1215 
1216             String phoneAccountId = defaultPhoneAccount.getId();
1217             if (!TextUtils.isEmpty(phoneAccountId) && TextUtils.isDigitsOnly(phoneAccountId)) {
1218                 PhoneAccountHandle upgradedPhoneAccount =
1219                         PhoneUtils.makePstnPhoneAccountHandle(
1220                                 PhoneGlobals.getPhone(Integer.parseInt(phoneAccountId)));
1221 
1222                 if (hasAccountEntryForPhoneAccount(upgradedPhoneAccount)) {
1223                     mTelecomManager.setUserSelectedOutgoingPhoneAccount(upgradedPhoneAccount);
1224                 }
1225             }
1226         }
1227     }
1228 
tearDownAccounts()1229     private void tearDownAccounts() {
1230         synchronized (mAccountsLock) {
1231             for (AccountEntry entry : mAccounts) {
1232                 entry.teardown();
1233             }
1234             mAccounts.clear();
1235         }
1236     }
1237 
1238     /**
1239      * Handles changes to the carrier configuration which may impact a phone account.  There are
1240      * some extras defined in the {@link PhoneAccount} which are based on carrier config options.
1241      * Only checking for carrier config changes when the subscription is configured runs the risk of
1242      * missing carrier config changes which happen later.
1243      * @param subId The subid the carrier config changed for, if applicable.  Will be
1244      *              {@link SubscriptionManager#INVALID_SUBSCRIPTION_ID} if not specified.
1245      */
handleCarrierConfigChange(int subId)1246     private void handleCarrierConfigChange(int subId) {
1247         if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
1248             return;
1249         }
1250         synchronized (mAccountsLock) {
1251             for (AccountEntry entry : mAccounts) {
1252                 if (entry.getSubId() == subId) {
1253                     Log.d(this, "handleCarrierConfigChange: subId=%d, accountSubId=%d", subId,
1254                             entry.getSubId());
1255                     entry.reRegisterPstnPhoneAccount();
1256                 }
1257             }
1258         }
1259     }
1260 }
1261