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