OSDN Git Service

5ed67a975fa78b221ee060721e80695b99b9ec98
[android-x86/frameworks-base.git] / policy / src / com / android / internal / policy / impl / KeyguardUpdateMonitor.java
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.content.res.Configuration;
24 import android.database.ContentObserver;
25 import static android.os.BatteryManager.BATTERY_STATUS_CHARGING;
26 import static android.os.BatteryManager.BATTERY_STATUS_FULL;
27 import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN;
28 import android.media.AudioManager;
29 import android.os.BatteryManager;
30 import android.os.Handler;
31 import android.os.Message;
32 import android.os.SystemClock;
33 import android.provider.Settings;
34 import android.provider.Telephony;
35 import static android.provider.Telephony.Intents.EXTRA_PLMN;
36 import static android.provider.Telephony.Intents.EXTRA_SHOW_PLMN;
37 import static android.provider.Telephony.Intents.EXTRA_SHOW_SPN;
38 import static android.provider.Telephony.Intents.EXTRA_SPN;
39 import static android.provider.Telephony.Intents.SPN_STRINGS_UPDATED_ACTION;
40
41 import com.android.internal.telephony.IccCard;
42 import com.android.internal.telephony.TelephonyIntents;
43
44 import android.telephony.TelephonyManager;
45 import android.util.Log;
46 import com.android.internal.R;
47 import com.google.android.collect.Lists;
48
49 import 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  */
61 public class KeyguardUpdateMonitor {
62
63     static private final String TAG = "KeyguardUpdateMonitor";
64     static private final boolean DEBUG = false;
65
66     /* package */ 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 }