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