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