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