Home | History | Annotate | Download | only in telephony
      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 
     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 
     96         void teardown() {
     97             mIncomingCallNotifier.teardown();
     98             mPhoneCapabilitiesNotifier.teardown();
     99         }
    100 
    101         /**
    102          * Registers the specified account with Telecom as a PhoneAccountHandle.
    103          */
    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 
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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
    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          */
    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          */
    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          */
    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          */
    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 
    492     TelecomAccountRegistry(Context context) {
    493         mContext = context;
    494         mTelecomManager = TelecomManager.from(context);
    495         mTelephonyManager = TelephonyManager.from(context);
    496         mSubscriptionManager = SubscriptionManager.from(context);
    497     }
    498 
    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 
    506     void setTelephonyConnectionService(TelephonyConnectionService telephonyConnectionService) {
    507         this.mTelephonyConnectionService = telephonyConnectionService;
    508     }
    509 
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 
    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 
    744     private void tearDownAccounts() {
    745         synchronized (mAccountsLock) {
    746             for (AccountEntry entry : mAccounts) {
    747                 entry.teardown();
    748             }
    749             mAccounts.clear();
    750         }
    751     }
    752 }
    753