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