1 /* 2 * Copyright (C) 2019 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.keyguard; 18 19 import static com.android.systemui.Flags.simPinUseSlotId; 20 import static com.android.keyguard.logging.CarrierTextManagerLogger.REASON_ACTIVE_DATA_SUB_CHANGED; 21 import static com.android.keyguard.logging.CarrierTextManagerLogger.REASON_ON_TELEPHONY_CAPABLE; 22 import static com.android.keyguard.logging.CarrierTextManagerLogger.REASON_REFRESH_CARRIER_INFO; 23 import static com.android.keyguard.logging.CarrierTextManagerLogger.REASON_SATELLITE_CHANGED; 24 import static com.android.keyguard.logging.CarrierTextManagerLogger.REASON_SIM_ERROR_STATE_CHANGED; 25 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.IntentFilter; 29 import android.content.pm.PackageManager; 30 import android.content.res.Resources; 31 import android.os.Trace; 32 import android.telephony.ServiceState; 33 import android.telephony.SubscriptionInfo; 34 import android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener; 35 import android.telephony.TelephonyManager; 36 import android.text.TextUtils; 37 import android.util.Log; 38 39 import androidx.annotation.Nullable; 40 import androidx.annotation.VisibleForTesting; 41 42 import com.android.keyguard.logging.CarrierTextManagerLogger; 43 import com.android.settingslib.WirelessUtils; 44 import com.android.systemui.dagger.qualifiers.Background; 45 import com.android.systemui.dagger.qualifiers.Main; 46 import com.android.systemui.keyguard.WakefulnessLifecycle; 47 import com.android.systemui.res.R; 48 import com.android.systemui.statusbar.pipeline.satellite.ui.viewmodel.DeviceBasedSatelliteViewModel; 49 import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository; 50 import com.android.systemui.telephony.TelephonyListenerManager; 51 import com.android.systemui.util.kotlin.JavaAdapter; 52 53 import kotlinx.coroutines.Job; 54 55 import java.util.Arrays; 56 import java.util.List; 57 import java.util.Objects; 58 import java.util.concurrent.CancellationException; 59 import java.util.concurrent.Executor; 60 import java.util.concurrent.atomic.AtomicBoolean; 61 62 import javax.inject.Inject; 63 64 /** 65 * Controller that generates text including the carrier names and/or the status of all the SIM 66 * interfaces in the device. Through a callback, the updates can be retrieved either as a list or 67 * separated by a given separator {@link CharSequence}. 68 * 69 * @deprecated use {@link com.android.systemui.statusbar.pipeline.wifi} instead 70 */ 71 @Deprecated 72 public class CarrierTextManager { 73 private static final boolean DEBUG = KeyguardConstants.DEBUG; 74 private static final String TAG = "CarrierTextController"; 75 76 private final boolean mIsEmergencyCallCapable; 77 private final Executor mMainExecutor; 78 private final Executor mBgExecutor; 79 private boolean mTelephonyCapable; 80 private final boolean mShowMissingSim; 81 private final boolean mShowAirplaneMode; 82 private final AtomicBoolean mNetworkSupported = new AtomicBoolean(); 83 @VisibleForTesting 84 protected KeyguardUpdateMonitor mKeyguardUpdateMonitor; 85 private final CarrierTextManagerLogger mLogger; 86 private final WifiRepository mWifiRepository; 87 private final DeviceBasedSatelliteViewModel mDeviceBasedSatelliteViewModel; 88 private final JavaAdapter mJavaAdapter; 89 private final boolean[] mSimErrorState; 90 private final int mSimSlotsNumber; 91 @Nullable // Check for nullability before dispatching 92 private CarrierTextCallback mCarrierTextCallback; 93 @Nullable 94 private Job mSatelliteConnectionJob; 95 96 @Nullable private String mSatelliteCarrierText; 97 98 private final Context mContext; 99 private final TelephonyManager mTelephonyManager; 100 private final CharSequence mSeparator; 101 private final TelephonyListenerManager mTelephonyListenerManager; 102 private final WakefulnessLifecycle mWakefulnessLifecycle; 103 private final WakefulnessLifecycle.Observer mWakefulnessObserver = 104 new WakefulnessLifecycle.Observer() { 105 @Override 106 public void onFinishedWakingUp() { 107 final CarrierTextCallback callback = mCarrierTextCallback; 108 if (callback != null) callback.finishedWakingUp(); 109 } 110 111 @Override 112 public void onStartedGoingToSleep() { 113 final CarrierTextCallback callback = mCarrierTextCallback; 114 if (callback != null) callback.startedGoingToSleep(); 115 } 116 }; 117 118 @VisibleForTesting 119 protected final KeyguardUpdateMonitorCallback mCallback = new KeyguardUpdateMonitorCallback() { 120 @Override 121 public void onRefreshCarrierInfo() { 122 mLogger.logUpdateCarrierTextForReason(REASON_REFRESH_CARRIER_INFO); 123 updateCarrierText(); 124 } 125 126 @Override 127 public void onTelephonyCapable(boolean capable) { 128 mLogger.logUpdateCarrierTextForReason(REASON_ON_TELEPHONY_CAPABLE); 129 mTelephonyCapable = capable; 130 updateCarrierText(); 131 } 132 133 public void onSimStateChanged(int subId, int slotId, int simState) { 134 if (slotId < 0 || slotId >= mSimSlotsNumber) { 135 Log.d(TAG, "onSimStateChanged() - slotId invalid: " + slotId 136 + " mTelephonyCapable: " + Boolean.toString(mTelephonyCapable)); 137 return; 138 } 139 140 141 mLogger.logSimStateChangedCallback(subId, slotId, simState); 142 if (getStatusForIccState(simState) == CarrierTextManager.StatusMode.SimIoError) { 143 mSimErrorState[slotId] = true; 144 mLogger.logUpdateCarrierTextForReason(REASON_SIM_ERROR_STATE_CHANGED); 145 updateCarrierText(); 146 } else if (mSimErrorState[slotId]) { 147 mSimErrorState[slotId] = false; 148 mLogger.logUpdateCarrierTextForReason(REASON_SIM_ERROR_STATE_CHANGED); 149 updateCarrierText(); 150 } 151 } 152 }; 153 154 private final ActiveDataSubscriptionIdListener mPhoneStateListener = 155 new ActiveDataSubscriptionIdListener() { 156 @Override 157 public void onActiveDataSubscriptionIdChanged(int subId) { 158 if (mNetworkSupported.get() && mCarrierTextCallback != null) { 159 mLogger.logUpdateCarrierTextForReason(REASON_ACTIVE_DATA_SUB_CHANGED); 160 updateCarrierText(); 161 } 162 } 163 }; 164 165 /** 166 * The status of this lock screen. Primarily used for widgets on LockScreen. 167 */ 168 @VisibleForTesting 169 protected enum StatusMode { 170 Normal, // Normal case (sim card present, it's not locked) 171 NetworkLocked, // SIM card is 'network locked'. 172 SimMissing, // SIM card is missing. 173 SimMissingLocked, // SIM card is missing, and device isn't provisioned; don't allow access 174 SimPukLocked, // SIM card is PUK locked because SIM entered wrong too many times 175 SimLocked, // SIM card is currently locked 176 SimPermDisabled, // SIM card is permanently disabled due to PUK unlock failure 177 SimNotReady, // SIM is not ready yet. May never be on devices w/o a SIM. 178 SimIoError, // SIM card is faulty 179 SimRestricted, // SIM Card restricted, present but not usable due to carrier restrictions. 180 SimUnknown // SIM card is unknown 181 } 182 183 /** 184 * Controller that provides updates on text with carriers names or SIM status. 185 * Used by {@link CarrierText}. 186 * 187 * @param separator Separator between different parts of the text 188 */ CarrierTextManager( Context context, CharSequence separator, boolean showAirplaneMode, boolean showMissingSim, WifiRepository wifiRepository, DeviceBasedSatelliteViewModel deviceBasedSatelliteViewModel, JavaAdapter javaAdapter, TelephonyManager telephonyManager, TelephonyListenerManager telephonyListenerManager, WakefulnessLifecycle wakefulnessLifecycle, @Main Executor mainExecutor, @Background Executor bgExecutor, KeyguardUpdateMonitor keyguardUpdateMonitor, CarrierTextManagerLogger logger)189 private CarrierTextManager( 190 Context context, 191 CharSequence separator, 192 boolean showAirplaneMode, 193 boolean showMissingSim, 194 WifiRepository wifiRepository, 195 DeviceBasedSatelliteViewModel deviceBasedSatelliteViewModel, 196 JavaAdapter javaAdapter, 197 TelephonyManager telephonyManager, 198 TelephonyListenerManager telephonyListenerManager, 199 WakefulnessLifecycle wakefulnessLifecycle, 200 @Main Executor mainExecutor, 201 @Background Executor bgExecutor, 202 KeyguardUpdateMonitor keyguardUpdateMonitor, 203 CarrierTextManagerLogger logger) { 204 205 mContext = context; 206 boolean hasTelephony = mContext.getPackageManager() 207 .hasSystemFeature(PackageManager.FEATURE_TELEPHONY); 208 mIsEmergencyCallCapable = telephonyManager.isVoiceCapable() && hasTelephony; 209 210 mShowAirplaneMode = showAirplaneMode; 211 mShowMissingSim = showMissingSim; 212 mWifiRepository = wifiRepository; 213 mDeviceBasedSatelliteViewModel = deviceBasedSatelliteViewModel; 214 mJavaAdapter = javaAdapter; 215 mTelephonyManager = telephonyManager; 216 mSeparator = separator; 217 mTelephonyListenerManager = telephonyListenerManager; 218 mWakefulnessLifecycle = wakefulnessLifecycle; 219 mSimSlotsNumber = getTelephonyManager().getSupportedModemCount(); 220 mSimErrorState = new boolean[mSimSlotsNumber]; 221 mMainExecutor = mainExecutor; 222 mBgExecutor = bgExecutor; 223 mKeyguardUpdateMonitor = keyguardUpdateMonitor; 224 mLogger = logger; 225 mBgExecutor.execute(() -> { 226 if (hasTelephony && mNetworkSupported.compareAndSet(false, hasTelephony)) { 227 // This will set/remove the listeners appropriately. Note that it will never double 228 // add the listeners. 229 handleSetListening(mCarrierTextCallback); 230 mainExecutor.execute(() -> { 231 mKeyguardUpdateMonitor.registerCallback(mCallback); 232 }); 233 } 234 }); 235 } 236 getTelephonyManager()237 private TelephonyManager getTelephonyManager() { 238 return mTelephonyManager; 239 } 240 241 /** 242 * Checks if there are faulty cards. Adds the text depending on the slot of the card 243 * 244 * @param text: current carrier text based on the sim state 245 * @param carrierNames names order by subscription order 246 * @param subOrderBySlot array containing the sub index for each slot ID 247 * @param noSims: whether a valid sim card is inserted 248 * @return text 249 */ updateCarrierTextWithSimIoError(CharSequence text, CharSequence[] carrierNames, int[] subOrderBySlot, boolean noSims)250 private CharSequence updateCarrierTextWithSimIoError(CharSequence text, 251 CharSequence[] carrierNames, int[] subOrderBySlot, boolean noSims) { 252 final CharSequence carrier = ""; 253 CharSequence carrierTextForSimIOError = getCarrierTextForSimState( 254 TelephonyManager.SIM_STATE_CARD_IO_ERROR, carrier); 255 // mSimErrorState has the state of each sim indexed by slotID. 256 for (int index = 0; index < getTelephonyManager().getActiveModemCount(); index++) { 257 if (!mSimErrorState[index]) { 258 continue; 259 } 260 // In the case when no sim cards are detected but a faulty card is inserted 261 // overwrite the text and only show "Invalid card" 262 if (noSims) { 263 return concatenate(carrierTextForSimIOError, 264 getContext().getText( 265 com.android.internal.R.string.emergency_calls_only), 266 mSeparator); 267 } else if (subOrderBySlot[index] != -1) { 268 int subIndex = subOrderBySlot[index]; 269 // prepend "Invalid card" when faulty card is inserted in slot 0 or 1 270 carrierNames[subIndex] = concatenate(carrierTextForSimIOError, 271 carrierNames[subIndex], 272 mSeparator); 273 } else { 274 // concatenate "Invalid card" when faulty card is inserted in other slot 275 text = concatenate(text, carrierTextForSimIOError, mSeparator); 276 } 277 278 } 279 return text; 280 } 281 282 /** 283 * This may be called internally after retrieving the correct value of {@code mNetworkSupported} 284 * (assumed false to start). In that case, the following happens: 285 * <ul> 286 * <li> If there was a registered callback, and the network is supported, it will register 287 * listeners. 288 * <li> If there was not a registered callback, it will try to remove unregistered listeners 289 * which is a no-op 290 * </ul> 291 * 292 * This call will always be processed in a background thread. 293 */ handleSetListening(CarrierTextCallback callback)294 private void handleSetListening(CarrierTextCallback callback) { 295 if (callback != null) { 296 mCarrierTextCallback = callback; 297 if (mNetworkSupported.get()) { 298 // Keyguard update monitor expects callbacks from main thread 299 mMainExecutor.execute(() -> { 300 mWakefulnessLifecycle.addObserver(mWakefulnessObserver); 301 }); 302 mTelephonyListenerManager.addActiveDataSubscriptionIdListener(mPhoneStateListener); 303 cancelSatelliteCollectionJob(/* reason= */ "Starting new job"); 304 mLogger.logStartListeningForSatelliteCarrierText(); 305 mSatelliteConnectionJob = 306 mJavaAdapter.alwaysCollectFlow( 307 mDeviceBasedSatelliteViewModel.getCarrierText(), 308 this::onSatelliteCarrierTextChanged); 309 } else { 310 // Don't listen and clear out the text when the device isn't a phone. 311 mMainExecutor.execute(() -> callback.updateCarrierInfo( 312 new CarrierTextCallbackInfo( 313 /* carrierText= */ "", 314 /* listOfCarriers= */ null, 315 /* anySimReady= */ false, 316 /* isInSatelliteMode= */ false, 317 /* subscriptionIds= */ null, 318 /* airplaneMode= */ false) 319 )); 320 } 321 } else { 322 mCarrierTextCallback = null; 323 mMainExecutor.execute(() -> { 324 mWakefulnessLifecycle.removeObserver(mWakefulnessObserver); 325 }); 326 mTelephonyListenerManager.removeActiveDataSubscriptionIdListener(mPhoneStateListener); 327 cancelSatelliteCollectionJob(/* reason= */ "#handleSetListening has null callback"); 328 } 329 } 330 331 /** 332 * Sets the listening status of this controller. If the callback is null, it is set to 333 * not listening. 334 * 335 * @param callback Callback to provide text updates 336 */ setListening(CarrierTextCallback callback)337 public void setListening(CarrierTextCallback callback) { 338 mBgExecutor.execute(() -> handleSetListening(callback)); 339 } 340 getSubscriptionInfo()341 protected List<SubscriptionInfo> getSubscriptionInfo() { 342 return mKeyguardUpdateMonitor.getFilteredSubscriptionInfo(); 343 } 344 onSatelliteCarrierTextChanged(@ullable String text)345 private void onSatelliteCarrierTextChanged(@Nullable String text) { 346 mLogger.logUpdateCarrierTextForReason(REASON_SATELLITE_CHANGED); 347 mLogger.logNewSatelliteCarrierText(text); 348 mSatelliteCarrierText = text; 349 updateCarrierText(); 350 } 351 updateCarrierText()352 protected void updateCarrierText() { 353 Trace.beginSection("CarrierTextManager#updateCarrierText"); 354 boolean allSimsMissing = true; 355 boolean anySimReadyAndInService = false; 356 CharSequence displayText = null; 357 List<SubscriptionInfo> subs = getSubscriptionInfo(); 358 359 final int numSubs = subs.size(); 360 final int[] subsIds = new int[numSubs]; 361 // This array will contain in position i, the index of subscription in slot ID i. 362 // -1 if no subscription in that slot 363 final int[] subOrderBySlot = new int[mSimSlotsNumber]; 364 for (int i = 0; i < mSimSlotsNumber; i++) { 365 subOrderBySlot[i] = -1; 366 } 367 final CharSequence[] carrierNames = new CharSequence[numSubs]; 368 mLogger.logUpdate(numSubs); 369 370 for (int i = 0; i < numSubs; i++) { 371 int subId = subs.get(i).getSubscriptionId(); 372 int slotId = subs.get(i).getSimSlotIndex(); 373 carrierNames[i] = ""; 374 subsIds[i] = subId; 375 subOrderBySlot[slotId] = i; 376 int simState = simPinUseSlotId() ? mKeyguardUpdateMonitor.getSimStateForSlotId(slotId) 377 : mKeyguardUpdateMonitor.getSimState(subId); 378 CharSequence carrierName = subs.get(i).getCarrierName(); 379 CharSequence carrierTextForSimState = getCarrierTextForSimState(simState, carrierName); 380 mLogger.logUpdateLoopStart(subId, simState, String.valueOf(carrierName)); 381 if (carrierTextForSimState != null) { 382 allSimsMissing = false; 383 carrierNames[i] = carrierTextForSimState; 384 } 385 if (simState == TelephonyManager.SIM_STATE_READY) { 386 Trace.beginSection("WFC check"); 387 ServiceState ss = mKeyguardUpdateMonitor.mServiceStates.get(subId); 388 if (ss != null && ss.getDataRegistrationState() == ServiceState.STATE_IN_SERVICE) { 389 // hack for WFC (IWLAN) not turning off immediately once 390 // Wi-Fi is disassociated or disabled 391 if (ss.getRilDataRadioTechnology() != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN 392 || mWifiRepository.isWifiConnectedWithValidSsid()) { 393 mLogger.logUpdateWfcCheck(); 394 anySimReadyAndInService = true; 395 } 396 } 397 Trace.endSection(); 398 } 399 } 400 // Only create "No SIM card" if no cards with CarrierName && no wifi when some sim is READY 401 // This condition will also be true always when numSubs == 0 402 if (allSimsMissing && !anySimReadyAndInService) { 403 if (numSubs != 0) { 404 // Shows "No SIM card | Emergency calls only" on devices that are voice-capable. 405 // This depends on mPlmn containing the text "Emergency calls only" when the radio 406 // has some connectivity. Otherwise, it should be null or empty and just show 407 // "No SIM card" 408 // Grab the first subscripton, because they all should contain the emergency text, 409 // described above. 410 displayText = makeCarrierStringOnEmergencyCapable( 411 getMissingSimMessage(), subs.get(0).getCarrierName()); 412 } else { 413 // We don't have a SubscriptionInfo to get the emergency calls only from. 414 // Grab it from the old sticky broadcast if possible instead. We can use it 415 // here because no subscriptions are active, so we don't have 416 // to worry about MSIM clashing. 417 CharSequence text = 418 getContext().getText(com.android.internal.R.string.emergency_calls_only); 419 Intent i = getContext().registerReceiver(null, 420 new IntentFilter(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED)); 421 if (i != null) { 422 String spn = ""; 423 String plmn = ""; 424 if (i.getBooleanExtra(TelephonyManager.EXTRA_SHOW_SPN, false)) { 425 spn = i.getStringExtra(TelephonyManager.EXTRA_SPN); 426 } 427 if (i.getBooleanExtra(TelephonyManager.EXTRA_SHOW_PLMN, false)) { 428 plmn = i.getStringExtra(TelephonyManager.EXTRA_PLMN); 429 } 430 mLogger.logUpdateFromStickyBroadcast(plmn, spn); 431 if (Objects.equals(plmn, spn)) { 432 text = plmn; 433 } else { 434 text = concatenate(plmn, spn, mSeparator); 435 } 436 } 437 displayText = makeCarrierStringOnEmergencyCapable(getMissingSimMessage(), text); 438 } 439 } 440 441 if (TextUtils.isEmpty(displayText)) displayText = joinNotEmpty(mSeparator, carrierNames); 442 443 displayText = updateCarrierTextWithSimIoError(displayText, carrierNames, subOrderBySlot, 444 allSimsMissing); 445 446 boolean airplaneMode = false; 447 // APM (airplane mode) != no carrier state. There are carrier services 448 // (e.g. WFC = Wi-Fi calling) which may operate in APM. 449 if (!anySimReadyAndInService && WirelessUtils.isAirplaneModeOn(mContext)) { 450 displayText = getAirplaneModeMessage(); 451 airplaneMode = true; 452 } 453 454 String currentSatelliteText = mSatelliteCarrierText; 455 if (currentSatelliteText != null) { 456 mLogger.logUsingSatelliteText(currentSatelliteText); 457 displayText = currentSatelliteText; 458 } 459 460 boolean isInSatelliteMode = mSatelliteCarrierText != null; 461 final CarrierTextCallbackInfo info = new CarrierTextCallbackInfo( 462 displayText, 463 carrierNames, 464 !allSimsMissing, 465 isInSatelliteMode, 466 subsIds, 467 airplaneMode); 468 mLogger.logCallbackSentFromUpdate(info); 469 postToCallback(info); 470 Trace.endSection(); 471 } 472 473 @VisibleForTesting postToCallback(CarrierTextCallbackInfo info)474 protected void postToCallback(CarrierTextCallbackInfo info) { 475 final CarrierTextCallback callback = mCarrierTextCallback; 476 if (callback != null) { 477 mMainExecutor.execute(() -> callback.updateCarrierInfo(info)); 478 } 479 } 480 getContext()481 private Context getContext() { 482 return mContext; 483 } 484 getMissingSimMessage()485 private String getMissingSimMessage() { 486 return mShowMissingSim && mTelephonyCapable 487 ? getContext().getString(R.string.keyguard_missing_sim_message_short) : ""; 488 } 489 getAirplaneModeMessage()490 private String getAirplaneModeMessage() { 491 return mShowAirplaneMode 492 ? getContext().getString(R.string.airplane_mode) : ""; 493 } 494 495 /** 496 * Top-level function for creating carrier text. Makes text based on simState, PLMN 497 * and SPN as well as device capabilities, such as being emergency call capable. 498 * 499 * @return Carrier text if not in missing state, null otherwise. 500 */ getCarrierTextForSimState(int simState, CharSequence text)501 private CharSequence getCarrierTextForSimState(int simState, CharSequence text) { 502 CharSequence carrierText = null; 503 CarrierTextManager.StatusMode status = getStatusForIccState(simState); 504 switch (status) { 505 case Normal: 506 carrierText = text; 507 break; 508 509 case SimNotReady: 510 // Null is reserved for denoting missing, in this case we have nothing to display. 511 carrierText = ""; // nothing to display yet. 512 break; 513 514 case NetworkLocked: 515 carrierText = makeCarrierStringOnEmergencyCapable( 516 mContext.getText(R.string.keyguard_network_locked_message), text); 517 break; 518 519 case SimMissing: 520 carrierText = null; 521 break; 522 523 case SimPermDisabled: 524 carrierText = makeCarrierStringOnEmergencyCapable( 525 getContext().getText( 526 R.string.keyguard_permanent_disabled_sim_message_short), 527 text); 528 break; 529 530 case SimMissingLocked: 531 carrierText = null; 532 break; 533 534 case SimLocked: 535 carrierText = makeCarrierStringOnLocked( 536 getContext().getText(R.string.keyguard_sim_locked_message), 537 text); 538 break; 539 540 case SimPukLocked: 541 carrierText = makeCarrierStringOnLocked( 542 getContext().getText(R.string.keyguard_sim_puk_locked_message), 543 text); 544 break; 545 case SimIoError: 546 carrierText = makeCarrierStringOnEmergencyCapable( 547 getContext().getText(R.string.keyguard_sim_error_message_short), 548 text); 549 break; 550 case SimRestricted: // fall through 551 case SimUnknown: 552 carrierText = null; 553 break; 554 } 555 556 return carrierText; 557 } 558 559 /* 560 * Add emergencyCallMessage to carrier string only if phone supports emergency calls. 561 */ makeCarrierStringOnEmergencyCapable( CharSequence simMessage, CharSequence emergencyCallMessage)562 private CharSequence makeCarrierStringOnEmergencyCapable( 563 CharSequence simMessage, CharSequence emergencyCallMessage) { 564 if (mIsEmergencyCallCapable) { 565 return concatenate(simMessage, emergencyCallMessage, mSeparator); 566 } 567 return simMessage; 568 } 569 570 /* 571 * Add "SIM card is locked" in parenthesis after carrier name, so it is easily associated in 572 * DSDS 573 */ makeCarrierStringOnLocked(CharSequence simMessage, CharSequence carrierName)574 private CharSequence makeCarrierStringOnLocked(CharSequence simMessage, 575 CharSequence carrierName) { 576 final boolean simMessageValid = !TextUtils.isEmpty(simMessage); 577 final boolean carrierNameValid = !TextUtils.isEmpty(carrierName); 578 if (simMessageValid && carrierNameValid) { 579 return mContext.getString(R.string.keyguard_carrier_name_with_sim_locked_template, 580 carrierName, simMessage); 581 } else if (simMessageValid) { 582 return simMessage; 583 } else if (carrierNameValid) { 584 return carrierName; 585 } else { 586 return ""; 587 } 588 } 589 590 /** 591 * Determine the current status of the lock screen given the SIM state and other stuff. 592 */ 593 @VisibleForTesting getStatusForIccState(int simState)594 protected CarrierTextManager.StatusMode getStatusForIccState(int simState) { 595 if (!mKeyguardUpdateMonitor.isDeviceProvisioned() 596 && (simState == TelephonyManager.SIM_STATE_ABSENT 597 || simState == TelephonyManager.SIM_STATE_PERM_DISABLED)) { 598 return CarrierTextManager.StatusMode.SimMissingLocked; 599 } 600 601 switch (simState) { 602 case TelephonyManager.SIM_STATE_ABSENT: 603 return CarrierTextManager.StatusMode.SimMissing; 604 case TelephonyManager.SIM_STATE_NETWORK_LOCKED: 605 return CarrierTextManager.StatusMode.NetworkLocked; 606 case TelephonyManager.SIM_STATE_NOT_READY: 607 return CarrierTextManager.StatusMode.SimNotReady; 608 case TelephonyManager.SIM_STATE_PIN_REQUIRED: 609 return CarrierTextManager.StatusMode.SimLocked; 610 case TelephonyManager.SIM_STATE_PUK_REQUIRED: 611 return CarrierTextManager.StatusMode.SimPukLocked; 612 case TelephonyManager.SIM_STATE_READY: 613 return CarrierTextManager.StatusMode.Normal; 614 case TelephonyManager.SIM_STATE_PERM_DISABLED: 615 return CarrierTextManager.StatusMode.SimPermDisabled; 616 case TelephonyManager.SIM_STATE_UNKNOWN: 617 return CarrierTextManager.StatusMode.SimUnknown; 618 case TelephonyManager.SIM_STATE_CARD_IO_ERROR: 619 return CarrierTextManager.StatusMode.SimIoError; 620 case TelephonyManager.SIM_STATE_CARD_RESTRICTED: 621 return CarrierTextManager.StatusMode.SimRestricted; 622 } 623 return CarrierTextManager.StatusMode.SimUnknown; 624 } 625 concatenate(CharSequence plmn, CharSequence spn, CharSequence separator)626 private static CharSequence concatenate(CharSequence plmn, CharSequence spn, 627 CharSequence separator) { 628 final boolean plmnValid = !TextUtils.isEmpty(plmn); 629 final boolean spnValid = !TextUtils.isEmpty(spn); 630 if (plmnValid && spnValid) { 631 return new StringBuilder().append(plmn).append(separator).append(spn).toString(); 632 } else if (plmnValid) { 633 return plmn; 634 } else if (spnValid) { 635 return spn; 636 } else { 637 return ""; 638 } 639 } 640 641 /** 642 * Joins the strings in a sequence using a separator. Empty strings are discarded with no extra 643 * separator added so there are no extra separators that are not needed. 644 */ joinNotEmpty(CharSequence separator, CharSequence[] sequences)645 private static CharSequence joinNotEmpty(CharSequence separator, CharSequence[] sequences) { 646 int length = sequences.length; 647 if (length == 0) return ""; 648 StringBuilder sb = new StringBuilder(); 649 for (int i = 0; i < length; i++) { 650 if (!TextUtils.isEmpty(sequences[i])) { 651 if (!TextUtils.isEmpty(sb)) { 652 sb.append(separator); 653 } 654 sb.append(sequences[i]); 655 } 656 } 657 return sb.toString(); 658 } 659 append(List<CharSequence> list, CharSequence string)660 private static List<CharSequence> append(List<CharSequence> list, CharSequence string) { 661 if (!TextUtils.isEmpty(string)) { 662 list.add(string); 663 } 664 return list; 665 } 666 cancelSatelliteCollectionJob(String reason)667 private void cancelSatelliteCollectionJob(String reason) { 668 Job job = mSatelliteConnectionJob; 669 if (job != null) { 670 mLogger.logStopListeningForSatelliteCarrierText(reason); 671 job.cancel(new CancellationException(reason)); 672 } 673 } 674 675 /** Injectable Buildeer for {@#link CarrierTextManager}. */ 676 public static class Builder { 677 private final Context mContext; 678 private final String mSeparator; 679 private final WifiRepository mWifiRepository; 680 private final DeviceBasedSatelliteViewModel mDeviceBasedSatelliteViewModel; 681 private final JavaAdapter mJavaAdapter; 682 private final TelephonyManager mTelephonyManager; 683 private final TelephonyListenerManager mTelephonyListenerManager; 684 private final WakefulnessLifecycle mWakefulnessLifecycle; 685 private final Executor mMainExecutor; 686 private final Executor mBgExecutor; 687 private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; 688 private final CarrierTextManagerLogger mLogger; 689 private boolean mShowAirplaneMode; 690 private boolean mShowMissingSim; 691 private String mDebugLocation; 692 693 @Inject Builder( Context context, @Main Resources resources, @Nullable WifiRepository wifiRepository, DeviceBasedSatelliteViewModel deviceBasedSatelliteViewModel, JavaAdapter javaAdapter, TelephonyManager telephonyManager, TelephonyListenerManager telephonyListenerManager, WakefulnessLifecycle wakefulnessLifecycle, @Main Executor mainExecutor, @Background Executor bgExecutor, KeyguardUpdateMonitor keyguardUpdateMonitor, CarrierTextManagerLogger logger)694 public Builder( 695 Context context, 696 @Main Resources resources, 697 @Nullable WifiRepository wifiRepository, 698 DeviceBasedSatelliteViewModel deviceBasedSatelliteViewModel, 699 JavaAdapter javaAdapter, 700 TelephonyManager telephonyManager, 701 TelephonyListenerManager telephonyListenerManager, 702 WakefulnessLifecycle wakefulnessLifecycle, 703 @Main Executor mainExecutor, 704 @Background Executor bgExecutor, 705 KeyguardUpdateMonitor keyguardUpdateMonitor, 706 CarrierTextManagerLogger logger) { 707 mContext = context; 708 mSeparator = resources.getString( 709 com.android.internal.R.string.kg_text_message_separator); 710 mWifiRepository = wifiRepository; 711 mDeviceBasedSatelliteViewModel = deviceBasedSatelliteViewModel; 712 mJavaAdapter = javaAdapter; 713 mTelephonyManager = telephonyManager; 714 mTelephonyListenerManager = telephonyListenerManager; 715 mWakefulnessLifecycle = wakefulnessLifecycle; 716 mMainExecutor = mainExecutor; 717 mBgExecutor = bgExecutor; 718 mKeyguardUpdateMonitor = keyguardUpdateMonitor; 719 mLogger = logger; 720 } 721 722 /** */ setShowAirplaneMode(boolean showAirplaneMode)723 public Builder setShowAirplaneMode(boolean showAirplaneMode) { 724 mShowAirplaneMode = showAirplaneMode; 725 return this; 726 } 727 728 /** */ setShowMissingSim(boolean showMissingSim)729 public Builder setShowMissingSim(boolean showMissingSim) { 730 mShowMissingSim = showMissingSim; 731 return this; 732 } 733 734 /** 735 * To help disambiguate logs, set a location to be used in the LogBuffer calls, e.g.: 736 * "keyguard" or "keyguard emergency status bar" 737 */ setDebugLocationString(String debugLocationString)738 public Builder setDebugLocationString(String debugLocationString) { 739 mDebugLocation = debugLocationString; 740 return this; 741 } 742 743 /** Create a CarrierTextManager. */ build()744 public CarrierTextManager build() { 745 mLogger.setLocation(mDebugLocation); 746 return new CarrierTextManager( 747 mContext, 748 mSeparator, 749 mShowAirplaneMode, 750 mShowMissingSim, 751 mWifiRepository, 752 mDeviceBasedSatelliteViewModel, 753 mJavaAdapter, 754 mTelephonyManager, 755 mTelephonyListenerManager, 756 mWakefulnessLifecycle, 757 mMainExecutor, 758 mBgExecutor, 759 mKeyguardUpdateMonitor, 760 mLogger); 761 } 762 } 763 764 /** 765 * Data structure for passing information to CarrierTextController subscribers 766 */ 767 public static final class CarrierTextCallbackInfo { 768 public final CharSequence carrierText; 769 public final CharSequence[] listOfCarriers; 770 public final boolean anySimReady; 771 public final boolean isInSatelliteMode; 772 public final int[] subscriptionIds; 773 public boolean airplaneMode; 774 775 @VisibleForTesting CarrierTextCallbackInfo( CharSequence carrierText, CharSequence[] listOfCarriers, boolean anySimReady, int[] subscriptionIds)776 public CarrierTextCallbackInfo( 777 CharSequence carrierText, 778 CharSequence[] listOfCarriers, 779 boolean anySimReady, 780 int[] subscriptionIds) { 781 this(carrierText, 782 listOfCarriers, 783 anySimReady, 784 /* isInSatelliteMode= */ false, 785 subscriptionIds, 786 /* airplaneMode= */ false); 787 } 788 CarrierTextCallbackInfo( CharSequence carrierText, CharSequence[] listOfCarriers, boolean anySimReady, boolean isInSatelliteMode, int[] subscriptionIds, boolean airplaneMode)789 public CarrierTextCallbackInfo( 790 CharSequence carrierText, 791 CharSequence[] listOfCarriers, 792 boolean anySimReady, 793 boolean isInSatelliteMode, 794 int[] subscriptionIds, 795 boolean airplaneMode) { 796 this.carrierText = carrierText; 797 this.listOfCarriers = listOfCarriers; 798 this.anySimReady = anySimReady; 799 this.isInSatelliteMode = isInSatelliteMode; 800 this.subscriptionIds = subscriptionIds; 801 this.airplaneMode = airplaneMode; 802 } 803 804 @Override toString()805 public String toString() { 806 return "CarrierTextCallbackInfo{" 807 + "carrierText=" + carrierText 808 + ", listOfCarriers=" + Arrays.toString(listOfCarriers) 809 + ", anySimReady=" + anySimReady 810 + ", isInSatelliteMode=" + isInSatelliteMode 811 + ", subscriptionIds=" + Arrays.toString(subscriptionIds) 812 + ", airplaneMode=" + airplaneMode 813 + '}'; 814 } 815 } 816 817 /** 818 * Callback to communicate to Views 819 */ 820 public interface CarrierTextCallback { 821 /** 822 * Provides updated carrier information. 823 */ updateCarrierInfo(CarrierTextCallbackInfo info)824 default void updateCarrierInfo(CarrierTextCallbackInfo info) {}; 825 826 /** 827 * Notifies the View that the device is going to sleep 828 */ startedGoingToSleep()829 default void startedGoingToSleep() {}; 830 831 /** 832 * Notifies the View that the device finished waking up 833 */ finishedWakingUp()834 default void finishedWakingUp() {}; 835 } 836 } 837