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