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