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