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