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