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