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