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