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