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