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