1/* 2 * Copyright (C) 2008 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 17package com.android.internal.policy.impl.keyguard_obsolete; 18 19import android.app.admin.DevicePolicyManager; 20import android.content.BroadcastReceiver; 21import android.content.Context; 22import android.content.Intent; 23import android.content.IntentFilter; 24import android.database.ContentObserver; 25import static android.os.BatteryManager.BATTERY_STATUS_FULL; 26import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN; 27import static android.os.BatteryManager.BATTERY_HEALTH_UNKNOWN; 28import static android.os.BatteryManager.EXTRA_STATUS; 29import static android.os.BatteryManager.EXTRA_PLUGGED; 30import static android.os.BatteryManager.EXTRA_LEVEL; 31import static android.os.BatteryManager.EXTRA_HEALTH; 32import android.media.AudioManager; 33import android.os.BatteryManager; 34import android.os.Handler; 35import android.os.Message; 36import android.provider.Settings; 37 38import com.android.internal.telephony.IccCardConstants; 39import com.android.internal.telephony.TelephonyIntents; 40 41import android.telephony.TelephonyManager; 42import android.util.Log; 43import com.android.internal.R; 44import com.google.android.collect.Lists; 45 46import java.util.ArrayList; 47 48/** 49 * Watches for updates that may be interesting to the keyguard, and provides 50 * the up to date information as well as a registration for callbacks that care 51 * to be updated. 52 * 53 * Note: under time crunch, this has been extended to include some stuff that 54 * doesn't really belong here. see {@link #handleBatteryUpdate} where it shutdowns 55 * the device, and {@link #getFailedAttempts()}, {@link #reportFailedAttempt()} 56 * and {@link #clearFailedAttempts()}. Maybe we should rename this 'KeyguardContext'... 57 */ 58public class KeyguardUpdateMonitor { 59 60 private static final String TAG = "KeyguardUpdateMonitor"; 61 private static final boolean DEBUG = false; 62 private static final boolean DEBUG_SIM_STATES = DEBUG || false; 63 private static final int FAILED_BIOMETRIC_UNLOCK_ATTEMPTS_BEFORE_BACKUP = 3; 64 private static final int LOW_BATTERY_THRESHOLD = 20; 65 66 // Callback messages 67 private static final int MSG_TIME_UPDATE = 301; 68 private static final int MSG_BATTERY_UPDATE = 302; 69 private static final int MSG_CARRIER_INFO_UPDATE = 303; 70 private static final int MSG_SIM_STATE_CHANGE = 304; 71 private static final int MSG_RINGER_MODE_CHANGED = 305; 72 private static final int MSG_PHONE_STATE_CHANGED = 306; 73 private static final int MSG_CLOCK_VISIBILITY_CHANGED = 307; 74 private static final int MSG_DEVICE_PROVISIONED = 308; 75 protected static final int MSG_DPM_STATE_CHANGED = 309; 76 protected static final int MSG_USER_SWITCHED = 310; 77 protected static final int MSG_USER_REMOVED = 311; 78 79 private final Context mContext; 80 81 // Telephony state 82 private IccCardConstants.State mSimState = IccCardConstants.State.READY; 83 private CharSequence mTelephonyPlmn; 84 private CharSequence mTelephonySpn; 85 private int mRingMode; 86 private int mPhoneState; 87 88 private boolean mDeviceProvisioned; 89 90 private BatteryStatus mBatteryStatus; 91 92 private int mFailedAttempts = 0; 93 private int mFailedBiometricUnlockAttempts = 0; 94 95 private boolean mClockVisible; 96 97 private ArrayList<KeyguardUpdateMonitorCallback> mCallbacks = Lists.newArrayList(); 98 private ContentObserver mContentObserver; 99 100 private final Handler mHandler = new Handler() { 101 @Override 102 public void handleMessage(Message msg) { 103 switch (msg.what) { 104 case MSG_TIME_UPDATE: 105 handleTimeUpdate(); 106 break; 107 case MSG_BATTERY_UPDATE: 108 handleBatteryUpdate((BatteryStatus) msg.obj); 109 break; 110 case MSG_CARRIER_INFO_UPDATE: 111 handleCarrierInfoUpdate(); 112 break; 113 case MSG_SIM_STATE_CHANGE: 114 handleSimStateChange((SimArgs) msg.obj); 115 break; 116 case MSG_RINGER_MODE_CHANGED: 117 handleRingerModeChange(msg.arg1); 118 break; 119 case MSG_PHONE_STATE_CHANGED: 120 handlePhoneStateChanged((String)msg.obj); 121 break; 122 case MSG_CLOCK_VISIBILITY_CHANGED: 123 handleClockVisibilityChanged(); 124 break; 125 case MSG_DEVICE_PROVISIONED: 126 handleDeviceProvisioned(); 127 break; 128 case MSG_DPM_STATE_CHANGED: 129 handleDevicePolicyManagerStateChanged(); 130 break; 131 case MSG_USER_SWITCHED: 132 handleUserSwitched(msg.arg1); 133 break; 134 case MSG_USER_REMOVED: 135 handleUserRemoved(msg.arg1); 136 break; 137 } 138 } 139 }; 140 141 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 142 143 public void onReceive(Context context, Intent intent) { 144 final String action = intent.getAction(); 145 if (DEBUG) Log.d(TAG, "received broadcast " + action); 146 147 if (Intent.ACTION_TIME_TICK.equals(action) 148 || Intent.ACTION_TIME_CHANGED.equals(action) 149 || Intent.ACTION_TIMEZONE_CHANGED.equals(action)) { 150 mHandler.sendMessage(mHandler.obtainMessage(MSG_TIME_UPDATE)); 151 } else if (TelephonyIntents.SPN_STRINGS_UPDATED_ACTION.equals(action)) { 152 mTelephonyPlmn = getTelephonyPlmnFrom(intent); 153 mTelephonySpn = getTelephonySpnFrom(intent); 154 mHandler.sendMessage(mHandler.obtainMessage(MSG_CARRIER_INFO_UPDATE)); 155 } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) { 156 final int status = intent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN); 157 final int plugged = intent.getIntExtra(EXTRA_PLUGGED, 0); 158 final int level = intent.getIntExtra(EXTRA_LEVEL, 0); 159 final int health = intent.getIntExtra(EXTRA_HEALTH, BATTERY_HEALTH_UNKNOWN); 160 final Message msg = mHandler.obtainMessage( 161 MSG_BATTERY_UPDATE, new BatteryStatus(status, level, plugged, health)); 162 mHandler.sendMessage(msg); 163 } else if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)) { 164 if (DEBUG_SIM_STATES) { 165 Log.v(TAG, "action " + action + " state" + 166 intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE)); 167 } 168 mHandler.sendMessage(mHandler.obtainMessage( 169 MSG_SIM_STATE_CHANGE, SimArgs.fromIntent(intent))); 170 } else if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) { 171 mHandler.sendMessage(mHandler.obtainMessage(MSG_RINGER_MODE_CHANGED, 172 intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1), 0)); 173 } else if (TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(action)) { 174 String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE); 175 mHandler.sendMessage(mHandler.obtainMessage(MSG_PHONE_STATE_CHANGED, state)); 176 } else if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED 177 .equals(action)) { 178 mHandler.sendMessage(mHandler.obtainMessage(MSG_DPM_STATE_CHANGED)); 179 } else if (Intent.ACTION_USER_SWITCHED.equals(action)) { 180 mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHED, 181 intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0)); 182 } else if (Intent.ACTION_USER_REMOVED.equals(action)) { 183 mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_REMOVED, 184 intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0)); 185 } 186 } 187 }; 188 189 /** 190 * When we receive a 191 * {@link com.android.internal.telephony.TelephonyIntents#ACTION_SIM_STATE_CHANGED} broadcast, 192 * and then pass a result via our handler to {@link KeyguardUpdateMonitor#handleSimStateChange}, 193 * we need a single object to pass to the handler. This class helps decode 194 * the intent and provide a {@link SimCard.State} result. 195 */ 196 private static class SimArgs { 197 public final IccCardConstants.State simState; 198 199 SimArgs(IccCardConstants.State state) { 200 simState = state; 201 } 202 203 static SimArgs fromIntent(Intent intent) { 204 IccCardConstants.State state; 205 if (!TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(intent.getAction())) { 206 throw new IllegalArgumentException("only handles intent ACTION_SIM_STATE_CHANGED"); 207 } 208 String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE); 209 if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) { 210 final String absentReason = intent 211 .getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON); 212 213 if (IccCardConstants.INTENT_VALUE_ABSENT_ON_PERM_DISABLED.equals( 214 absentReason)) { 215 state = IccCardConstants.State.PERM_DISABLED; 216 } else { 217 state = IccCardConstants.State.ABSENT; 218 } 219 } else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) { 220 state = IccCardConstants.State.READY; 221 } else if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) { 222 final String lockedReason = intent 223 .getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON); 224 if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) { 225 state = IccCardConstants.State.PIN_REQUIRED; 226 } else if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) { 227 state = IccCardConstants.State.PUK_REQUIRED; 228 } else { 229 state = IccCardConstants.State.UNKNOWN; 230 } 231 } else if (IccCardConstants.INTENT_VALUE_LOCKED_NETWORK.equals(stateExtra)) { 232 state = IccCardConstants.State.NETWORK_LOCKED; 233 } else { 234 state = IccCardConstants.State.UNKNOWN; 235 } 236 return new SimArgs(state); 237 } 238 239 public String toString() { 240 return simState.toString(); 241 } 242 } 243 244 /* package */ static class BatteryStatus { 245 public final int status; 246 public final int level; 247 public final int plugged; 248 public final int health; 249 public BatteryStatus(int status, int level, int plugged, int health) { 250 this.status = status; 251 this.level = level; 252 this.plugged = plugged; 253 this.health = health; 254 } 255 256 /** 257 * Determine whether the device is plugged in (USB or power). 258 * @return true if the device is plugged in. 259 */ 260 boolean isPluggedIn() { 261 return plugged == BatteryManager.BATTERY_PLUGGED_AC 262 || plugged == BatteryManager.BATTERY_PLUGGED_USB 263 || plugged == BatteryManager.BATTERY_PLUGGED_WIRELESS; 264 } 265 266 /** 267 * Whether or not the device is charged. Note that some devices never return 100% for 268 * battery level, so this allows either battery level or status to determine if the 269 * battery is charged. 270 * @return true if the device is charged 271 */ 272 public boolean isCharged() { 273 return status == BATTERY_STATUS_FULL || level >= 100; 274 } 275 276 /** 277 * Whether battery is low and needs to be charged. 278 * @return true if battery is low 279 */ 280 public boolean isBatteryLow() { 281 return level < LOW_BATTERY_THRESHOLD; 282 } 283 284 } 285 286 public KeyguardUpdateMonitor(Context context) { 287 mContext = context; 288 289 mDeviceProvisioned = Settings.Global.getInt( 290 mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0) != 0; 291 292 // Since device can't be un-provisioned, we only need to register a content observer 293 // to update mDeviceProvisioned when we are... 294 if (!mDeviceProvisioned) { 295 watchForDeviceProvisioning(); 296 } 297 298 // Take a guess at initial SIM state, battery status and PLMN until we get an update 299 mSimState = IccCardConstants.State.NOT_READY; 300 mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, 100, 0, 0); 301 mTelephonyPlmn = getDefaultPlmn(); 302 303 // Watch for interesting updates 304 final IntentFilter filter = new IntentFilter(); 305 filter.addAction(Intent.ACTION_TIME_TICK); 306 filter.addAction(Intent.ACTION_TIME_CHANGED); 307 filter.addAction(Intent.ACTION_BATTERY_CHANGED); 308 filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); 309 filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED); 310 filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); 311 filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION); 312 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); 313 filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); 314 filter.addAction(Intent.ACTION_USER_SWITCHED); 315 filter.addAction(Intent.ACTION_USER_REMOVED); 316 context.registerReceiver(mBroadcastReceiver, filter); 317 } 318 319 private void watchForDeviceProvisioning() { 320 mContentObserver = new ContentObserver(mHandler) { 321 @Override 322 public void onChange(boolean selfChange) { 323 super.onChange(selfChange); 324 mDeviceProvisioned = Settings.Global.getInt(mContext.getContentResolver(), 325 Settings.Global.DEVICE_PROVISIONED, 0) != 0; 326 if (mDeviceProvisioned) { 327 mHandler.sendMessage(mHandler.obtainMessage(MSG_DEVICE_PROVISIONED)); 328 } 329 if (DEBUG) Log.d(TAG, "DEVICE_PROVISIONED state = " + mDeviceProvisioned); 330 } 331 }; 332 333 mContext.getContentResolver().registerContentObserver( 334 Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), 335 false, mContentObserver); 336 337 // prevent a race condition between where we check the flag and where we register the 338 // observer by grabbing the value once again... 339 boolean provisioned = Settings.Global.getInt(mContext.getContentResolver(), 340 Settings.Global.DEVICE_PROVISIONED, 0) != 0; 341 if (provisioned != mDeviceProvisioned) { 342 mDeviceProvisioned = provisioned; 343 if (mDeviceProvisioned) { 344 mHandler.sendMessage(mHandler.obtainMessage(MSG_DEVICE_PROVISIONED)); 345 } 346 } 347 } 348 349 /** 350 * Handle {@link #MSG_DPM_STATE_CHANGED} 351 */ 352 protected void handleDevicePolicyManagerStateChanged() { 353 for (int i = 0; i < mCallbacks.size(); i++) { 354 mCallbacks.get(i).onDevicePolicyManagerStateChanged(); 355 } 356 } 357 358 /** 359 * Handle {@link #MSG_USER_SWITCHED} 360 */ 361 protected void handleUserSwitched(int userId) { 362 for (int i = 0; i < mCallbacks.size(); i++) { 363 mCallbacks.get(i).onUserSwitched(userId); 364 } 365 } 366 367 /** 368 * Handle {@link #MSG_USER_SWITCHED} 369 */ 370 protected void handleUserRemoved(int userId) { 371 for (int i = 0; i < mCallbacks.size(); i++) { 372 mCallbacks.get(i).onUserRemoved(userId); 373 } 374 } 375 376 /** 377 * Handle {@link #MSG_DEVICE_PROVISIONED} 378 */ 379 protected void handleDeviceProvisioned() { 380 for (int i = 0; i < mCallbacks.size(); i++) { 381 mCallbacks.get(i).onDeviceProvisioned(); 382 } 383 if (mContentObserver != null) { 384 // We don't need the observer anymore... 385 mContext.getContentResolver().unregisterContentObserver(mContentObserver); 386 mContentObserver = null; 387 } 388 } 389 390 /** 391 * Handle {@link #MSG_PHONE_STATE_CHANGED} 392 */ 393 protected void handlePhoneStateChanged(String newState) { 394 if (DEBUG) Log.d(TAG, "handlePhoneStateChanged(" + newState + ")"); 395 if (TelephonyManager.EXTRA_STATE_IDLE.equals(newState)) { 396 mPhoneState = TelephonyManager.CALL_STATE_IDLE; 397 } else if (TelephonyManager.EXTRA_STATE_OFFHOOK.equals(newState)) { 398 mPhoneState = TelephonyManager.CALL_STATE_OFFHOOK; 399 } else if (TelephonyManager.EXTRA_STATE_RINGING.equals(newState)) { 400 mPhoneState = TelephonyManager.CALL_STATE_RINGING; 401 } 402 for (int i = 0; i < mCallbacks.size(); i++) { 403 mCallbacks.get(i).onPhoneStateChanged(mPhoneState); 404 } 405 } 406 407 /** 408 * Handle {@link #MSG_RINGER_MODE_CHANGED} 409 */ 410 protected void handleRingerModeChange(int mode) { 411 if (DEBUG) Log.d(TAG, "handleRingerModeChange(" + mode + ")"); 412 mRingMode = mode; 413 for (int i = 0; i < mCallbacks.size(); i++) { 414 mCallbacks.get(i).onRingerModeChanged(mode); 415 } 416 } 417 418 /** 419 * Handle {@link #MSG_TIME_UPDATE} 420 */ 421 private void handleTimeUpdate() { 422 if (DEBUG) Log.d(TAG, "handleTimeUpdate"); 423 for (int i = 0; i < mCallbacks.size(); i++) { 424 mCallbacks.get(i).onTimeChanged(); 425 } 426 } 427 428 /** 429 * Handle {@link #MSG_BATTERY_UPDATE} 430 */ 431 private void handleBatteryUpdate(BatteryStatus status) { 432 if (DEBUG) Log.d(TAG, "handleBatteryUpdate"); 433 final boolean batteryUpdateInteresting = isBatteryUpdateInteresting(mBatteryStatus, status); 434 mBatteryStatus = status; 435 if (batteryUpdateInteresting) { 436 for (int i = 0; i < mCallbacks.size(); i++) { 437 mCallbacks.get(i).onRefreshBatteryInfo(status); 438 } 439 } 440 } 441 442 /** 443 * Handle {@link #MSG_CARRIER_INFO_UPDATE} 444 */ 445 private void handleCarrierInfoUpdate() { 446 if (DEBUG) Log.d(TAG, "handleCarrierInfoUpdate: plmn = " + mTelephonyPlmn 447 + ", spn = " + mTelephonySpn); 448 449 for (int i = 0; i < mCallbacks.size(); i++) { 450 mCallbacks.get(i).onRefreshCarrierInfo(mTelephonyPlmn, mTelephonySpn); 451 } 452 } 453 454 /** 455 * Handle {@link #MSG_SIM_STATE_CHANGE} 456 */ 457 private void handleSimStateChange(SimArgs simArgs) { 458 final IccCardConstants.State state = simArgs.simState; 459 460 if (DEBUG) { 461 Log.d(TAG, "handleSimStateChange: intentValue = " + simArgs + " " 462 + "state resolved to " + state.toString()); 463 } 464 465 if (state != IccCardConstants.State.UNKNOWN && state != mSimState) { 466 mSimState = state; 467 for (int i = 0; i < mCallbacks.size(); i++) { 468 mCallbacks.get(i).onSimStateChanged(state); 469 } 470 } 471 } 472 473 /** 474 * Handle {@link #MSG_CLOCK_VISIBILITY_CHANGED} 475 */ 476 private void handleClockVisibilityChanged() { 477 if (DEBUG) Log.d(TAG, "handleClockVisibilityChanged()"); 478 for (int i = 0; i < mCallbacks.size(); i++) { 479 mCallbacks.get(i).onClockVisibilityChanged(); 480 } 481 } 482 483 private static boolean isBatteryUpdateInteresting(BatteryStatus old, BatteryStatus current) { 484 final boolean nowPluggedIn = current.isPluggedIn(); 485 final boolean wasPluggedIn = old.isPluggedIn(); 486 final boolean stateChangedWhilePluggedIn = 487 wasPluggedIn == true && nowPluggedIn == true 488 && (old.status != current.status); 489 490 // change in plug state is always interesting 491 if (wasPluggedIn != nowPluggedIn || stateChangedWhilePluggedIn) { 492 return true; 493 } 494 495 // change in battery level while plugged in 496 if (nowPluggedIn && old.level != current.level) { 497 return true; 498 } 499 500 // change where battery needs charging 501 if (!nowPluggedIn && current.isBatteryLow() && current.level != old.level) { 502 return true; 503 } 504 return false; 505 } 506 507 /** 508 * @param intent The intent with action {@link TelephonyIntents#SPN_STRINGS_UPDATED_ACTION} 509 * @return The string to use for the plmn, or null if it should not be shown. 510 */ 511 private CharSequence getTelephonyPlmnFrom(Intent intent) { 512 if (intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false)) { 513 final String plmn = intent.getStringExtra(TelephonyIntents.EXTRA_PLMN); 514 return (plmn != null) ? plmn : getDefaultPlmn(); 515 } 516 return null; 517 } 518 519 /** 520 * @return The default plmn (no service) 521 */ 522 private CharSequence getDefaultPlmn() { 523 return mContext.getResources().getText(R.string.lockscreen_carrier_default); 524 } 525 526 /** 527 * @param intent The intent with action {@link Telephony.Intents#SPN_STRINGS_UPDATED_ACTION} 528 * @return The string to use for the plmn, or null if it should not be shown. 529 */ 530 private CharSequence getTelephonySpnFrom(Intent intent) { 531 if (intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false)) { 532 final String spn = intent.getStringExtra(TelephonyIntents.EXTRA_SPN); 533 if (spn != null) { 534 return spn; 535 } 536 } 537 return null; 538 } 539 540 /** 541 * Remove the given observer's callback. 542 * 543 * @param observer The observer to remove 544 */ 545 public void removeCallback(Object observer) { 546 mCallbacks.remove(observer); 547 } 548 549 /** 550 * Register to receive notifications about general keyguard information 551 * (see {@link InfoCallback}. 552 * @param callback The callback. 553 */ 554 public void registerCallback(KeyguardUpdateMonitorCallback callback) { 555 if (!mCallbacks.contains(callback)) { 556 mCallbacks.add(callback); 557 // Notify listener of the current state 558 callback.onRefreshBatteryInfo(mBatteryStatus); 559 callback.onTimeChanged(); 560 callback.onRingerModeChanged(mRingMode); 561 callback.onPhoneStateChanged(mPhoneState); 562 callback.onRefreshCarrierInfo(mTelephonyPlmn, mTelephonySpn); 563 callback.onClockVisibilityChanged(); 564 callback.onSimStateChanged(mSimState); 565 } else { 566 if (DEBUG) Log.e(TAG, "Object tried to add another callback", 567 new Exception("Called by")); 568 } 569 } 570 571 public void reportClockVisible(boolean visible) { 572 mClockVisible = visible; 573 mHandler.obtainMessage(MSG_CLOCK_VISIBILITY_CHANGED).sendToTarget(); 574 } 575 576 public IccCardConstants.State getSimState() { 577 return mSimState; 578 } 579 580 /** 581 * Report that the user successfully entered the SIM PIN or PUK/SIM PIN so we 582 * have the information earlier than waiting for the intent 583 * broadcast from the telephony code. 584 * 585 * NOTE: Because handleSimStateChange() invokes callbacks immediately without going 586 * through mHandler, this *must* be called from the UI thread. 587 */ 588 public void reportSimUnlocked() { 589 handleSimStateChange(new SimArgs(IccCardConstants.State.READY)); 590 } 591 592 public CharSequence getTelephonyPlmn() { 593 return mTelephonyPlmn; 594 } 595 596 public CharSequence getTelephonySpn() { 597 return mTelephonySpn; 598 } 599 600 /** 601 * @return Whether the device is provisioned (whether they have gone through 602 * the setup wizard) 603 */ 604 public boolean isDeviceProvisioned() { 605 return mDeviceProvisioned; 606 } 607 608 public int getFailedAttempts() { 609 return mFailedAttempts; 610 } 611 612 public void clearFailedAttempts() { 613 mFailedAttempts = 0; 614 mFailedBiometricUnlockAttempts = 0; 615 } 616 617 public void reportFailedAttempt() { 618 mFailedAttempts++; 619 } 620 621 public boolean isClockVisible() { 622 return mClockVisible; 623 } 624 625 public int getPhoneState() { 626 return mPhoneState; 627 } 628 629 public void reportFailedBiometricUnlockAttempt() { 630 mFailedBiometricUnlockAttempts++; 631 } 632 633 public boolean getMaxBiometricUnlockAttemptsReached() { 634 return mFailedBiometricUnlockAttempts >= FAILED_BIOMETRIC_UNLOCK_ATTEMPTS_BEFORE_BACKUP; 635 } 636 637 public boolean isSimLocked() { 638 return mSimState == IccCardConstants.State.PIN_REQUIRED 639 || mSimState == IccCardConstants.State.PUK_REQUIRED 640 || mSimState == IccCardConstants.State.PERM_DISABLED; 641 } 642} 643