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