• 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.res.Resources;
25 import android.graphics.Bitmap;
26 import android.graphics.Canvas;
27 import android.graphics.PorterDuff;
28 import android.graphics.drawable.Drawable;
29 import android.graphics.drawable.Icon;
30 import android.net.Uri;
31 import android.os.Bundle;
32 import android.os.PersistableBundle;
33 import android.os.UserHandle;
34 import android.os.UserManager;
35 import android.telecom.PhoneAccount;
36 import android.telecom.PhoneAccountHandle;
37 import android.telecom.TelecomManager;
38 import android.telephony.CarrierConfigManager;
39 import android.telephony.PhoneStateListener;
40 import android.telephony.ServiceState;
41 import android.telephony.SubscriptionInfo;
42 import android.telephony.SubscriptionManager;
43 import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
44 import android.telephony.TelephonyManager;
45 import android.text.TextUtils;
46 
47 import com.android.internal.telephony.Phone;
48 import com.android.internal.telephony.PhoneFactory;
49 import com.android.phone.PhoneGlobals;
50 import com.android.phone.PhoneUtils;
51 import com.android.phone.R;
52 
53 import java.util.Arrays;
54 import java.util.LinkedList;
55 import java.util.List;
56 import java.util.Optional;
57 
58 /**
59  * Owns all data we have registered with Telecom including handling dynamic addition and
60  * removal of SIMs and SIP accounts.
61  */
62 final class TelecomAccountRegistry {
63     private static final boolean DBG = false; /* STOP SHIP if true */
64 
65     // This icon is the one that is used when the Slot ID that we have for a particular SIM
66     // is not supported, i.e. SubscriptionManager.INVALID_SLOT_ID or the 5th SIM in a phone.
67     private final static int DEFAULT_SIM_ICON =  R.drawable.ic_multi_sim;
68     private final static String GROUP_PREFIX = "group_";
69 
70     final class AccountEntry implements PstnPhoneCapabilitiesNotifier.Listener {
71         private final Phone mPhone;
72         private PhoneAccount mAccount;
73         private final PstnIncomingCallNotifier mIncomingCallNotifier;
74         private final PstnPhoneCapabilitiesNotifier mPhoneCapabilitiesNotifier;
75         private boolean mIsEmergency;
76         private boolean mIsDummy;
77         private boolean mIsVideoCapable;
78         private boolean mIsVideoPresenceSupported;
79         private boolean mIsVideoPauseSupported;
80         private boolean mIsMergeCallSupported;
81         private boolean mIsVideoConferencingSupported;
82         private boolean mIsMergeOfWifiCallsAllowedWhenVoWifiOff;
83 
AccountEntry(Phone phone, boolean isEmergency, boolean isDummy)84         AccountEntry(Phone phone, boolean isEmergency, boolean isDummy) {
85             mPhone = phone;
86             mIsEmergency = isEmergency;
87             mIsDummy = isDummy;
88             mAccount = registerPstnPhoneAccount(isEmergency, isDummy);
89             Log.i(this, "Registered phoneAccount: %s with handle: %s",
90                     mAccount, mAccount.getAccountHandle());
91             mIncomingCallNotifier = new PstnIncomingCallNotifier((Phone) mPhone);
92             mPhoneCapabilitiesNotifier = new PstnPhoneCapabilitiesNotifier((Phone) mPhone,
93                     this);
94         }
95 
teardown()96         void teardown() {
97             mIncomingCallNotifier.teardown();
98             mPhoneCapabilitiesNotifier.teardown();
99         }
100 
101         /**
102          * Registers the specified account with Telecom as a PhoneAccountHandle.
103          */
registerPstnPhoneAccount(boolean isEmergency, boolean isDummyAccount)104         private PhoneAccount registerPstnPhoneAccount(boolean isEmergency, boolean isDummyAccount) {
105             String dummyPrefix = isDummyAccount ? "Dummy " : "";
106 
107             // Build the Phone account handle.
108             PhoneAccountHandle phoneAccountHandle =
109                     PhoneUtils.makePstnPhoneAccountHandleWithPrefix(
110                             mPhone, dummyPrefix, isEmergency);
111 
112             // Populate the phone account data.
113             int subId = mPhone.getSubId();
114             String subscriberId = mPhone.getSubscriberId();
115             int color = PhoneAccount.NO_HIGHLIGHT_COLOR;
116             int slotId = SubscriptionManager.INVALID_SIM_SLOT_INDEX;
117             String line1Number = mTelephonyManager.getLine1Number(subId);
118             if (line1Number == null) {
119                 line1Number = "";
120             }
121             String subNumber = mPhone.getLine1Number();
122             if (subNumber == null) {
123                 subNumber = "";
124             }
125 
126             String label;
127             String description;
128             Icon icon = null;
129 
130             // We can only get the real slotId from the SubInfoRecord, we can't calculate the
131             // slotId from the subId or the phoneId in all instances.
132             SubscriptionInfo record =
133                     mSubscriptionManager.getActiveSubscriptionInfo(subId);
134 
135             if (isEmergency) {
136                 label = mContext.getResources().getString(R.string.sim_label_emergency_calls);
137                 description =
138                         mContext.getResources().getString(R.string.sim_description_emergency_calls);
139             } else if (mTelephonyManager.getPhoneCount() == 1) {
140                 // For single-SIM devices, we show the label and description as whatever the name of
141                 // the network is.
142                 description = label = mTelephonyManager.getNetworkOperatorName();
143             } else {
144                 CharSequence subDisplayName = null;
145 
146                 if (record != null) {
147                     subDisplayName = record.getDisplayName();
148                     slotId = record.getSimSlotIndex();
149                     color = record.getIconTint();
150                     icon = Icon.createWithBitmap(record.createIconBitmap(mContext));
151                 }
152 
153                 String slotIdString;
154                 if (SubscriptionManager.isValidSlotId(slotId)) {
155                     slotIdString = Integer.toString(slotId);
156                 } else {
157                     slotIdString = mContext.getResources().getString(R.string.unknown);
158                 }
159 
160                 if (TextUtils.isEmpty(subDisplayName)) {
161                     // Either the sub record is not there or it has an empty display name.
162                     Log.w(this, "Could not get a display name for subid: %d", subId);
163                     subDisplayName = mContext.getResources().getString(
164                             R.string.sim_description_default, slotIdString);
165                 }
166 
167                 // The label is user-visible so let's use the display name that the user may
168                 // have set in Settings->Sim cards.
169                 label = dummyPrefix + subDisplayName;
170                 description = dummyPrefix + mContext.getResources().getString(
171                                 R.string.sim_description_default, slotIdString);
172             }
173 
174             // By default all SIM phone accounts can place emergency calls.
175             int capabilities = PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION |
176                     PhoneAccount.CAPABILITY_CALL_PROVIDER |
177                     PhoneAccount.CAPABILITY_MULTI_USER;
178 
179             if (mContext.getResources().getBoolean(R.bool.config_pstnCanPlaceEmergencyCalls)) {
180                 capabilities |= PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS;
181             }
182 
183             mIsVideoCapable = mPhone.isVideoEnabled();
184 
185             if (!mIsPrimaryUser) {
186                 Log.i(this, "Disabling video calling for secondary user.");
187                 mIsVideoCapable = false;
188             }
189 
190             if (mIsVideoCapable) {
191                 capabilities |= PhoneAccount.CAPABILITY_VIDEO_CALLING;
192             }
193 
194             mIsVideoPresenceSupported = isCarrierVideoPresenceSupported();
195             if (mIsVideoCapable && mIsVideoPresenceSupported) {
196                 capabilities |= PhoneAccount.CAPABILITY_VIDEO_CALLING_RELIES_ON_PRESENCE;
197             }
198 
199             if (mIsVideoCapable && isCarrierEmergencyVideoCallsAllowed()) {
200                 capabilities |= PhoneAccount.CAPABILITY_EMERGENCY_VIDEO_CALLING;
201             }
202 
203             mIsVideoPauseSupported = isCarrierVideoPauseSupported();
204             Bundle instantLetteringExtras = null;
205             if (isCarrierInstantLetteringSupported()) {
206                 capabilities |= PhoneAccount.CAPABILITY_CALL_SUBJECT;
207                 instantLetteringExtras = getPhoneAccountExtras();
208             }
209             mIsMergeCallSupported = isCarrierMergeCallSupported();
210             mIsVideoConferencingSupported = isCarrierVideoConferencingSupported();
211             mIsMergeOfWifiCallsAllowedWhenVoWifiOff =
212                     isCarrierMergeOfWifiCallsAllowedWhenVoWifiOff();
213 
214             if (isEmergency && mContext.getResources().getBoolean(
215                     R.bool.config_emergency_account_emergency_calls_only)) {
216                 capabilities |= PhoneAccount.CAPABILITY_EMERGENCY_CALLS_ONLY;
217             }
218 
219             if (icon == null) {
220                 // TODO: Switch to using Icon.createWithResource() once that supports tinting.
221                 Resources res = mContext.getResources();
222                 Drawable drawable = res.getDrawable(DEFAULT_SIM_ICON, null);
223                 drawable.setTint(res.getColor(R.color.default_sim_icon_tint_color, null));
224                 drawable.setTintMode(PorterDuff.Mode.SRC_ATOP);
225 
226                 int width = drawable.getIntrinsicWidth();
227                 int height = drawable.getIntrinsicHeight();
228                 Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
229                 Canvas canvas = new Canvas(bitmap);
230                 drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
231                 drawable.draw(canvas);
232 
233                 icon = Icon.createWithBitmap(bitmap);
234             }
235 
236             // Check to see if the newly registered account should replace the old account.
237             String groupId = "";
238             String[] mergedImsis = mTelephonyManager.getMergedSubscriberIds();
239             boolean isMergedSim = false;
240             if (mergedImsis != null && subscriberId != null && !isEmergency) {
241                 for (String imsi : mergedImsis) {
242                     if (imsi.equals(subscriberId)) {
243                         isMergedSim = true;
244                         break;
245                     }
246                 }
247             }
248             if(isMergedSim) {
249                 groupId = GROUP_PREFIX + line1Number;
250                 Log.i(this, "Adding Merged Account with group: " + Log.pii(groupId));
251             }
252 
253             PhoneAccount account = PhoneAccount.builder(phoneAccountHandle, label)
254                     .setAddress(Uri.fromParts(PhoneAccount.SCHEME_TEL, line1Number, null))
255                     .setSubscriptionAddress(
256                             Uri.fromParts(PhoneAccount.SCHEME_TEL, subNumber, null))
257                     .setCapabilities(capabilities)
258                     .setIcon(icon)
259                     .setHighlightColor(color)
260                     .setShortDescription(description)
261                     .setSupportedUriSchemes(Arrays.asList(
262                             PhoneAccount.SCHEME_TEL, PhoneAccount.SCHEME_VOICEMAIL))
263                     .setExtras(instantLetteringExtras)
264                     .setGroupId(groupId)
265                     .build();
266 
267             // Register with Telecom and put into the account entry.
268             mTelecomManager.registerPhoneAccount(account);
269 
270             return account;
271         }
272 
getPhoneAccountHandle()273         public PhoneAccountHandle getPhoneAccountHandle() {
274             return mAccount != null ? mAccount.getAccountHandle() : null;
275         }
276 
277         /**
278          * Determines from carrier configuration whether pausing of IMS video calls is supported.
279          *
280          * @return {@code true} if pausing IMS video calls is supported.
281          */
isCarrierVideoPauseSupported()282         private boolean isCarrierVideoPauseSupported() {
283             // Check if IMS video pause is supported.
284             PersistableBundle b =
285                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
286             return b != null &&
287                     b.getBoolean(CarrierConfigManager.KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL);
288         }
289 
290         /**
291          * Determines from carrier configuration whether RCS presence indication for video calls is
292          * supported.
293          *
294          * @return {@code true} if RCS presence indication for video calls is supported.
295          */
isCarrierVideoPresenceSupported()296         private boolean isCarrierVideoPresenceSupported() {
297             PersistableBundle b =
298                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
299             return b != null &&
300                     b.getBoolean(CarrierConfigManager.KEY_USE_RCS_PRESENCE_BOOL);
301         }
302 
303         /**
304          * Determines from carrier config whether instant lettering is supported.
305          *
306          * @return {@code true} if instant lettering is supported, {@code false} otherwise.
307          */
isCarrierInstantLetteringSupported()308         private boolean isCarrierInstantLetteringSupported() {
309             PersistableBundle b =
310                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
311             return b != null &&
312                     b.getBoolean(CarrierConfigManager.KEY_CARRIER_INSTANT_LETTERING_AVAILABLE_BOOL);
313         }
314 
315         /**
316          * Determines from carrier config whether merging calls is supported.
317          *
318          * @return {@code true} if merging calls is supported, {@code false} otherwise.
319          */
isCarrierMergeCallSupported()320         private boolean isCarrierMergeCallSupported() {
321             PersistableBundle b =
322                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
323             return b != null &&
324                     b.getBoolean(CarrierConfigManager.KEY_SUPPORT_CONFERENCE_CALL_BOOL);
325         }
326 
327         /**
328          * Determines from carrier config whether emergency video calls are supported.
329          *
330          * @return {@code true} if emergency video calls are allowed, {@code false} otherwise.
331          */
isCarrierEmergencyVideoCallsAllowed()332         private boolean isCarrierEmergencyVideoCallsAllowed() {
333             PersistableBundle b =
334                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
335             return b != null &&
336                     b.getBoolean(CarrierConfigManager.KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL);
337         }
338 
339         /**
340          * Determines from carrier config whether video conferencing is supported.
341          *
342          * @return {@code true} if video conferencing is supported, {@code false} otherwise.
343          */
isCarrierVideoConferencingSupported()344         private boolean isCarrierVideoConferencingSupported() {
345             PersistableBundle b =
346                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
347             return b != null &&
348                     b.getBoolean(CarrierConfigManager.KEY_SUPPORT_VIDEO_CONFERENCE_CALL_BOOL);
349         }
350 
351         /**
352          * Determines from carrier config whether merging of wifi calls is allowed when VoWIFI is
353          * turned off.
354          *
355          * @return {@code true} merging of wifi calls when VoWIFI is disabled should be prevented,
356          *      {@code false} otherwise.
357          */
isCarrierMergeOfWifiCallsAllowedWhenVoWifiOff()358         private boolean isCarrierMergeOfWifiCallsAllowedWhenVoWifiOff() {
359             PersistableBundle b =
360                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
361             return b != null && b.getBoolean(
362                     CarrierConfigManager.KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL);
363         }
364 
365         /**
366          * @return The {@link PhoneAccount} extras associated with the current subscription.
367          */
getPhoneAccountExtras()368         private Bundle getPhoneAccountExtras() {
369             PersistableBundle b =
370                     PhoneGlobals.getInstance().getCarrierConfigForSubId(mPhone.getSubId());
371 
372             int instantLetteringMaxLength = b.getInt(
373                     CarrierConfigManager.KEY_CARRIER_INSTANT_LETTERING_LENGTH_LIMIT_INT);
374             String instantLetteringEncoding = b.getString(
375                     CarrierConfigManager.KEY_CARRIER_INSTANT_LETTERING_ENCODING_STRING);
376 
377             Bundle phoneAccountExtras = new Bundle();
378             phoneAccountExtras.putInt(PhoneAccount.EXTRA_CALL_SUBJECT_MAX_LENGTH,
379                     instantLetteringMaxLength);
380             phoneAccountExtras.putString(PhoneAccount.EXTRA_CALL_SUBJECT_CHARACTER_ENCODING,
381                     instantLetteringEncoding);
382             return phoneAccountExtras;
383         }
384 
385         /**
386          * Receives callback from {@link PstnPhoneCapabilitiesNotifier} when the video capabilities
387          * have changed.
388          *
389          * @param isVideoCapable {@code true} if video is capable.
390          */
391         @Override
onVideoCapabilitiesChanged(boolean isVideoCapable)392         public void onVideoCapabilitiesChanged(boolean isVideoCapable) {
393             mIsVideoCapable = isVideoCapable;
394             synchronized (mAccountsLock) {
395                 if (!mAccounts.contains(this)) {
396                     // Account has already been torn down, don't try to register it again.
397                     // This handles the case where teardown has already happened, and we got a video
398                     // update that lost the race for the mAccountsLock.  In such a scenario by the
399                     // time we get here, the original phone account could have been torn down.
400                     return;
401                 }
402                 mAccount = registerPstnPhoneAccount(mIsEmergency, mIsDummy);
403             }
404         }
405 
406         /**
407          * Indicates whether this account supports pausing video calls.
408          * @return {@code true} if the account supports pausing video calls, {@code false}
409          * otherwise.
410          */
isVideoPauseSupported()411         public boolean isVideoPauseSupported() {
412             return mIsVideoCapable && mIsVideoPauseSupported;
413         }
414 
415         /**
416          * Indicates whether this account supports merging calls (i.e. conferencing).
417          * @return {@code true} if the account supports merging calls, {@code false} otherwise.
418          */
isMergeCallSupported()419         public boolean isMergeCallSupported() {
420             return mIsMergeCallSupported;
421         }
422 
423         /**
424          * Indicates whether this account supports video conferencing.
425          * @return {@code true} if the account supports video conferencing, {@code false} otherwise.
426          */
isVideoConferencingSupported()427         public boolean isVideoConferencingSupported() {
428             return mIsVideoConferencingSupported;
429         }
430 
431         /**
432          * Indicate whether this account allow merging of wifi calls when VoWIFI is off.
433          * @return {@code true} if allowed, {@code false} otherwise.
434          */
isMergeOfWifiCallsAllowedWhenVoWifiOff()435         public boolean isMergeOfWifiCallsAllowedWhenVoWifiOff() {
436             return mIsMergeOfWifiCallsAllowedWhenVoWifiOff;
437         }
438     }
439 
440     private OnSubscriptionsChangedListener mOnSubscriptionsChangedListener =
441             new OnSubscriptionsChangedListener() {
442         @Override
443         public void onSubscriptionsChanged() {
444             // Any time the SubscriptionInfo changes...rerun the setup
445             tearDownAccounts();
446             setupAccounts();
447         }
448     };
449 
450     private final BroadcastReceiver mUserSwitchedReceiver = new BroadcastReceiver() {
451         @Override
452         public void onReceive(Context context, Intent intent) {
453             Log.i(this, "User changed, re-registering phone accounts.");
454 
455             int userHandleId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
456             UserHandle currentUserHandle = new UserHandle(userHandleId);
457             mIsPrimaryUser = UserManager.get(mContext).getPrimaryUser().getUserHandle()
458                     .equals(currentUserHandle);
459 
460             // Any time the user changes, re-register the accounts.
461             tearDownAccounts();
462             setupAccounts();
463         }
464     };
465 
466     private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
467         @Override
468         public void onServiceStateChanged(ServiceState serviceState) {
469             int newState = serviceState.getState();
470             if (newState == ServiceState.STATE_IN_SERVICE && mServiceState != newState) {
471                 tearDownAccounts();
472                 setupAccounts();
473             }
474             mServiceState = newState;
475         }
476     };
477 
478     private static TelecomAccountRegistry sInstance;
479     private final Context mContext;
480     private final TelecomManager mTelecomManager;
481     private final TelephonyManager mTelephonyManager;
482     private final SubscriptionManager mSubscriptionManager;
483     private List<AccountEntry> mAccounts = new LinkedList<AccountEntry>();
484     private Object mAccountsLock = new Object();
485     private int mServiceState = ServiceState.STATE_POWER_OFF;
486     private boolean mIsPrimaryUser = true;
487 
488     // TODO: Remove back-pointer from app singleton to Service, since this is not a preferred
489     // pattern; redesign. This was added to fix a late release bug.
490     private TelephonyConnectionService mTelephonyConnectionService;
491 
TelecomAccountRegistry(Context context)492     TelecomAccountRegistry(Context context) {
493         mContext = context;
494         mTelecomManager = TelecomManager.from(context);
495         mTelephonyManager = TelephonyManager.from(context);
496         mSubscriptionManager = SubscriptionManager.from(context);
497     }
498 
getInstance(Context context)499     static synchronized final TelecomAccountRegistry getInstance(Context context) {
500         if (sInstance == null && context != null) {
501             sInstance = new TelecomAccountRegistry(context);
502         }
503         return sInstance;
504     }
505 
setTelephonyConnectionService(TelephonyConnectionService telephonyConnectionService)506     void setTelephonyConnectionService(TelephonyConnectionService telephonyConnectionService) {
507         this.mTelephonyConnectionService = telephonyConnectionService;
508     }
509 
getTelephonyConnectionService()510     TelephonyConnectionService getTelephonyConnectionService() {
511         return mTelephonyConnectionService;
512     }
513 
514     /**
515      * Determines if the {@link AccountEntry} associated with a {@link PhoneAccountHandle} supports
516      * pausing video calls.
517      *
518      * @param handle The {@link PhoneAccountHandle}.
519      * @return {@code True} if video pausing is supported.
520      */
isVideoPauseSupported(PhoneAccountHandle handle)521     boolean isVideoPauseSupported(PhoneAccountHandle handle) {
522         synchronized (mAccountsLock) {
523             for (AccountEntry entry : mAccounts) {
524                 if (entry.getPhoneAccountHandle().equals(handle)) {
525                     return entry.isVideoPauseSupported();
526                 }
527             }
528         }
529         return false;
530     }
531 
532     /**
533      * Determines if the {@link AccountEntry} associated with a {@link PhoneAccountHandle} supports
534      * merging calls.
535      *
536      * @param handle The {@link PhoneAccountHandle}.
537      * @return {@code True} if merging calls is supported.
538      */
isMergeCallSupported(PhoneAccountHandle handle)539     boolean isMergeCallSupported(PhoneAccountHandle handle) {
540         synchronized (mAccountsLock) {
541             for (AccountEntry entry : mAccounts) {
542                 if (entry.getPhoneAccountHandle().equals(handle)) {
543                     return entry.isMergeCallSupported();
544                 }
545             }
546         }
547         return false;
548     }
549 
550     /**
551      * Determines if the {@link AccountEntry} associated with a {@link PhoneAccountHandle} supports
552      * video conferencing.
553      *
554      * @param handle The {@link PhoneAccountHandle}.
555      * @return {@code True} if video conferencing is supported.
556      */
isVideoConferencingSupported(PhoneAccountHandle handle)557     boolean isVideoConferencingSupported(PhoneAccountHandle handle) {
558         synchronized (mAccountsLock) {
559             for (AccountEntry entry : mAccounts) {
560                 if (entry.getPhoneAccountHandle().equals(handle)) {
561                     return entry.isVideoConferencingSupported();
562                 }
563             }
564         }
565         return false;
566     }
567 
568     /**
569      * Determines if the {@link AccountEntry} associated with a {@link PhoneAccountHandle} allows
570      * merging of wifi calls when VoWIFI is disabled.
571      *
572      * @param handle The {@link PhoneAccountHandle}.
573      * @return {@code True} if merging of wifi calls is allowed when VoWIFI is disabled.
574      */
isMergeOfWifiCallsAllowedWhenVoWifiOff(final PhoneAccountHandle handle)575     boolean isMergeOfWifiCallsAllowedWhenVoWifiOff(final PhoneAccountHandle handle) {
576         synchronized (mAccountsLock) {
577             Optional<AccountEntry> result = mAccounts.stream().filter(
578                     entry -> entry.getPhoneAccountHandle().equals(handle)).findFirst();
579 
580             if (result.isPresent()) {
581                 return result.get().isMergeOfWifiCallsAllowedWhenVoWifiOff();
582             } else {
583                 return false;
584             }
585         }
586     }
587 
588     /**
589      * @return Reference to the {@code TelecomAccountRegistry}'s subscription manager.
590      */
getSubscriptionManager()591     SubscriptionManager getSubscriptionManager() {
592         return mSubscriptionManager;
593     }
594 
595     /**
596      * Returns the address (e.g. the phone number) associated with a subscription.
597      *
598      * @param handle The phone account handle to find the subscription address for.
599      * @return The address.
600      */
getAddress(PhoneAccountHandle handle)601     Uri getAddress(PhoneAccountHandle handle) {
602         synchronized (mAccountsLock) {
603             for (AccountEntry entry : mAccounts) {
604                 if (entry.getPhoneAccountHandle().equals(handle)) {
605                     return entry.mAccount.getAddress();
606                 }
607             }
608         }
609         return null;
610     }
611 
612     /**
613      * Sets up all the phone accounts for SIMs on first boot.
614      */
setupOnBoot()615     void setupOnBoot() {
616         // TODO: When this object "finishes" we should unregister by invoking
617         // SubscriptionManager.getInstance(mContext).unregister(mOnSubscriptionsChangedListener);
618         // This is not strictly necessary because it will be unregistered if the
619         // notification fails but it is good form.
620 
621         // Register for SubscriptionInfo list changes which is guaranteed
622         // to invoke onSubscriptionsChanged the first time.
623         SubscriptionManager.from(mContext).addOnSubscriptionsChangedListener(
624                 mOnSubscriptionsChangedListener);
625 
626         // We also need to listen for changes to the service state (e.g. emergency -> in service)
627         // because this could signal a removal or addition of a SIM in a single SIM phone.
628         mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_SERVICE_STATE);
629 
630         // Listen for user switches.  When the user switches, we need to ensure that if the current
631         // use is not the primary user we disable video calling.
632         mContext.registerReceiver(mUserSwitchedReceiver,
633                 new IntentFilter(Intent.ACTION_USER_SWITCHED));
634     }
635 
636     /**
637      * Determines if the list of {@link AccountEntry}(s) contains an {@link AccountEntry} with a
638      * specified {@link PhoneAccountHandle}.
639      *
640      * @param handle The {@link PhoneAccountHandle}.
641      * @return {@code True} if an entry exists.
642      */
hasAccountEntryForPhoneAccount(PhoneAccountHandle handle)643     boolean hasAccountEntryForPhoneAccount(PhoneAccountHandle handle) {
644         synchronized (mAccountsLock) {
645             for (AccountEntry entry : mAccounts) {
646                 if (entry.getPhoneAccountHandle().equals(handle)) {
647                     return true;
648                 }
649             }
650         }
651         return false;
652     }
653 
654     /**
655      * Un-registers any {@link PhoneAccount}s which are no longer present in the list
656      * {@code AccountEntry}(s).
657      */
cleanupPhoneAccounts()658     private void cleanupPhoneAccounts() {
659         ComponentName telephonyComponentName =
660                 new ComponentName(mContext, TelephonyConnectionService.class);
661         // This config indicates whether the emergency account was flagged as emergency calls only
662         // in which case we need to consider all phone accounts, not just the call capable ones.
663         final boolean emergencyCallsOnlyEmergencyAccount = mContext.getResources().getBoolean(
664                 R.bool.config_emergency_account_emergency_calls_only);
665         List<PhoneAccountHandle> accountHandles = emergencyCallsOnlyEmergencyAccount
666                 ? mTelecomManager.getAllPhoneAccountHandles()
667                 : mTelecomManager.getCallCapablePhoneAccounts(true /* includeDisabled */);
668 
669         for (PhoneAccountHandle handle : accountHandles) {
670             if (telephonyComponentName.equals(handle.getComponentName()) &&
671                     !hasAccountEntryForPhoneAccount(handle)) {
672                 Log.i(this, "Unregistering phone account %s.", handle);
673                 mTelecomManager.unregisterPhoneAccount(handle);
674             }
675         }
676     }
677 
setupAccounts()678     private void setupAccounts() {
679         // Go through SIM-based phones and register ourselves -- registering an existing account
680         // will cause the existing entry to be replaced.
681         Phone[] phones = PhoneFactory.getPhones();
682         Log.d(this, "Found %d phones.  Attempting to register.", phones.length);
683 
684         final boolean phoneAccountsEnabled = mContext.getResources().getBoolean(
685                 R.bool.config_pstn_phone_accounts_enabled);
686 
687         synchronized (mAccountsLock) {
688             if (phoneAccountsEnabled) {
689                 for (Phone phone : phones) {
690                     int subscriptionId = phone.getSubId();
691                     Log.d(this, "Phone with subscription id %d", subscriptionId);
692                     // setupAccounts can be called multiple times during service changes. Don't add an
693                     // account if the Icc has not been set yet.
694                     if (subscriptionId >= 0 && phone.getFullIccSerialNumber() != null) {
695                         mAccounts.add(new AccountEntry(phone, false /* emergency */,
696                                 false /* isDummy */));
697                     }
698                 }
699             }
700 
701             // If we did not list ANY accounts, we need to provide a "default" SIM account
702             // for emergency numbers since no actual SIM is needed for dialing emergency
703             // numbers but a phone account is.
704             if (mAccounts.isEmpty()) {
705                 mAccounts.add(new AccountEntry(PhoneFactory.getDefaultPhone(), true /* emergency */,
706                         false /* isDummy */));
707             }
708 
709             // Add a fake account entry.
710             if (DBG && phones.length > 0 && "TRUE".equals(System.getProperty("dummy_sim"))) {
711                 mAccounts.add(new AccountEntry(phones[0], false /* emergency */,
712                         true /* isDummy */));
713             }
714         }
715 
716         // Clean up any PhoneAccounts that are no longer relevant
717         cleanupPhoneAccounts();
718 
719         // At some point, the phone account ID was switched from the subId to the iccId.
720         // If there is a default account, check if this is the case, and upgrade the default account
721         // from using the subId to iccId if so.
722         PhoneAccountHandle defaultPhoneAccount =
723                 mTelecomManager.getUserSelectedOutgoingPhoneAccount();
724         ComponentName telephonyComponentName =
725                 new ComponentName(mContext, TelephonyConnectionService.class);
726 
727         if (defaultPhoneAccount != null &&
728                 telephonyComponentName.equals(defaultPhoneAccount.getComponentName()) &&
729                 !hasAccountEntryForPhoneAccount(defaultPhoneAccount)) {
730 
731             String phoneAccountId = defaultPhoneAccount.getId();
732             if (!TextUtils.isEmpty(phoneAccountId) && TextUtils.isDigitsOnly(phoneAccountId)) {
733                 PhoneAccountHandle upgradedPhoneAccount =
734                         PhoneUtils.makePstnPhoneAccountHandle(
735                                 PhoneGlobals.getPhone(Integer.parseInt(phoneAccountId)));
736 
737                 if (hasAccountEntryForPhoneAccount(upgradedPhoneAccount)) {
738                     mTelecomManager.setUserSelectedOutgoingPhoneAccount(upgradedPhoneAccount);
739                 }
740             }
741         }
742     }
743 
tearDownAccounts()744     private void tearDownAccounts() {
745         synchronized (mAccountsLock) {
746             for (AccountEntry entry : mAccounts) {
747                 entry.teardown();
748             }
749             mAccounts.clear();
750         }
751     }
752 }
753