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