/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.keyguard; import android.app.ActivityManagerNative; import android.app.AlarmManager; import android.app.IUserSwitchObserver; import android.app.PendingIntent; import android.app.admin.DevicePolicyManager; import android.app.trust.TrustManager; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.graphics.Bitmap; import static android.os.BatteryManager.BATTERY_STATUS_FULL; import static android.os.BatteryManager.BATTERY_STATUS_UNKNOWN; import static android.os.BatteryManager.BATTERY_HEALTH_UNKNOWN; import static android.os.BatteryManager.EXTRA_STATUS; import static android.os.BatteryManager.EXTRA_PLUGGED; import static android.os.BatteryManager.EXTRA_LEVEL; import static android.os.BatteryManager.EXTRA_HEALTH; import android.media.AudioManager; import android.media.IRemoteControlDisplay; import android.os.BatteryManager; import android.os.Bundle; import android.os.Handler; import android.os.IRemoteCallback; import android.os.Message; import android.os.RemoteException; import android.os.UserHandle; import android.provider.Settings; import com.android.internal.telephony.IccCardConstants; import com.android.internal.telephony.TelephonyIntents; import android.service.fingerprint.FingerprintManager; import android.service.fingerprint.FingerprintManagerReceiver; import android.service.fingerprint.FingerprintUtils; import android.telephony.TelephonyManager; import android.util.Log; import android.util.SparseBooleanArray; import com.google.android.collect.Lists; import java.lang.ref.WeakReference; import java.util.ArrayList; /** * Watches for updates that may be interesting to the keyguard, and provides * the up to date information as well as a registration for callbacks that care * to be updated. * * Note: under time crunch, this has been extended to include some stuff that * doesn't really belong here. see {@link #handleBatteryUpdate} where it shutdowns * the device, and {@link #getFailedUnlockAttempts()}, {@link #reportFailedAttempt()} * and {@link #clearFailedUnlockAttempts()}. Maybe we should rename this 'KeyguardContext'... */ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { private static final String TAG = "KeyguardUpdateMonitor"; private static final boolean DEBUG = KeyguardConstants.DEBUG; private static final boolean DEBUG_SIM_STATES = DEBUG || false; private static final int FAILED_BIOMETRIC_UNLOCK_ATTEMPTS_BEFORE_BACKUP = 3; private static final int LOW_BATTERY_THRESHOLD = 20; private static final String ACTION_FACE_UNLOCK_STARTED = "com.android.facelock.FACE_UNLOCK_STARTED"; private static final String ACTION_FACE_UNLOCK_STOPPED = "com.android.facelock.FACE_UNLOCK_STOPPED"; // Callback messages private static final int MSG_TIME_UPDATE = 301; private static final int MSG_BATTERY_UPDATE = 302; private static final int MSG_CARRIER_INFO_UPDATE = 303; private static final int MSG_SIM_STATE_CHANGE = 304; private static final int MSG_RINGER_MODE_CHANGED = 305; private static final int MSG_PHONE_STATE_CHANGED = 306; private static final int MSG_CLOCK_VISIBILITY_CHANGED = 307; private static final int MSG_DEVICE_PROVISIONED = 308; private static final int MSG_DPM_STATE_CHANGED = 309; private static final int MSG_USER_SWITCHING = 310; private static final int MSG_USER_REMOVED = 311; private static final int MSG_KEYGUARD_VISIBILITY_CHANGED = 312; private static final int MSG_BOOT_COMPLETED = 313; private static final int MSG_USER_SWITCH_COMPLETE = 314; private static final int MSG_SET_CURRENT_CLIENT_ID = 315; private static final int MSG_SET_PLAYBACK_STATE = 316; private static final int MSG_USER_INFO_CHANGED = 317; private static final int MSG_REPORT_EMERGENCY_CALL_ACTION = 318; private static final int MSG_SCREEN_TURNED_ON = 319; private static final int MSG_SCREEN_TURNED_OFF = 320; private static final int MSG_KEYGUARD_BOUNCER_CHANGED = 322; private static final int MSG_FINGERPRINT_PROCESSED = 323; private static final int MSG_FINGERPRINT_ACQUIRED = 324; private static final int MSG_FACE_UNLOCK_STATE_CHANGED = 325; private static KeyguardUpdateMonitor sInstance; private final Context mContext; // Telephony state private IccCardConstants.State mSimState = IccCardConstants.State.READY; private CharSequence mTelephonyPlmn; private CharSequence mTelephonySpn; private int mRingMode; private int mPhoneState; private boolean mKeyguardIsVisible; private boolean mBouncer; private boolean mBootCompleted; // Device provisioning state private boolean mDeviceProvisioned; // Battery status private BatteryStatus mBatteryStatus; // Password attempts private int mFailedAttempts = 0; private int mFailedBiometricUnlockAttempts = 0; private boolean mAlternateUnlockEnabled; private boolean mClockVisible; private final ArrayList> mCallbacks = Lists.newArrayList(); private ContentObserver mDeviceProvisionedObserver; private boolean mSwitchingUser; private boolean mScreenOn; private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_TIME_UPDATE: handleTimeUpdate(); break; case MSG_BATTERY_UPDATE: handleBatteryUpdate((BatteryStatus) msg.obj); break; case MSG_CARRIER_INFO_UPDATE: handleCarrierInfoUpdate(); break; case MSG_SIM_STATE_CHANGE: handleSimStateChange((SimArgs) msg.obj); break; case MSG_RINGER_MODE_CHANGED: handleRingerModeChange(msg.arg1); break; case MSG_PHONE_STATE_CHANGED: handlePhoneStateChanged((String) msg.obj); break; case MSG_CLOCK_VISIBILITY_CHANGED: handleClockVisibilityChanged(); break; case MSG_DEVICE_PROVISIONED: handleDeviceProvisioned(); break; case MSG_DPM_STATE_CHANGED: handleDevicePolicyManagerStateChanged(); break; case MSG_USER_SWITCHING: handleUserSwitching(msg.arg1, (IRemoteCallback) msg.obj); break; case MSG_USER_SWITCH_COMPLETE: handleUserSwitchComplete(msg.arg1); break; case MSG_USER_REMOVED: handleUserRemoved(msg.arg1); break; case MSG_KEYGUARD_VISIBILITY_CHANGED: handleKeyguardVisibilityChanged(msg.arg1); break; case MSG_KEYGUARD_BOUNCER_CHANGED: handleKeyguardBouncerChanged(msg.arg1); break; case MSG_BOOT_COMPLETED: handleBootCompleted(); break; case MSG_USER_INFO_CHANGED: handleUserInfoChanged(msg.arg1); break; case MSG_REPORT_EMERGENCY_CALL_ACTION: handleReportEmergencyCallAction(); break; case MSG_SCREEN_TURNED_OFF: handleScreenTurnedOff(msg.arg1); break; case MSG_SCREEN_TURNED_ON: handleScreenTurnedOn(); break; case MSG_FINGERPRINT_ACQUIRED: handleFingerprintAcquired(msg.arg1); break; case MSG_FINGERPRINT_PROCESSED: handleFingerprintProcessed(msg.arg1); break; case MSG_FACE_UNLOCK_STATE_CHANGED: handleFaceUnlockStateChanged(msg.arg1 != 0, msg.arg2); break; } } }; private SparseBooleanArray mUserHasTrust = new SparseBooleanArray(); private SparseBooleanArray mUserTrustIsManaged = new SparseBooleanArray(); private SparseBooleanArray mUserFingerprintRecognized = new SparseBooleanArray(); private SparseBooleanArray mUserFaceUnlockRunning = new SparseBooleanArray(); @Override public void onTrustChanged(boolean enabled, int userId, boolean initiatedByUser) { mUserHasTrust.put(userId, enabled); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onTrustChanged(userId); if (enabled && initiatedByUser) { cb.onTrustInitiatedByUser(userId); } } } } @Override public void onTrustManagedChanged(boolean managed, int userId) { mUserTrustIsManaged.put(userId, managed); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onTrustManagedChanged(userId); } } } private void onFingerprintRecognized(int userId) { mUserFingerprintRecognized.put(userId, true); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onFingerprintRecognized(userId); } } } private void handleFingerprintProcessed(int fingerprintId) { if (fingerprintId == 0) return; // not a valid fingerprint final int userId; try { userId = ActivityManagerNative.getDefault().getCurrentUser().id; } catch (RemoteException e) { Log.e(TAG, "Failed to get current user id: ", e); return; } if (isFingerprintDisabled(userId)) { Log.d(TAG, "Fingerprint disabled by DPM for userId: " + userId); return; } final ContentResolver res = mContext.getContentResolver(); final int ids[] = FingerprintUtils.getFingerprintIdsForUser(res, userId); for (int i = 0; i < ids.length; i++) { if (ids[i] == fingerprintId) { onFingerprintRecognized(userId); } } } private void handleFingerprintAcquired(int info) { for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onFingerprintAcquired(info); } } } private void handleFaceUnlockStateChanged(boolean running, int userId) { mUserFaceUnlockRunning.put(userId, running); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onFaceUnlockStateChanged(running, userId); } } } public boolean isFaceUnlockRunning(int userId) { return mUserFaceUnlockRunning.get(userId); } private boolean isTrustDisabled(int userId) { final DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); if (dpm != null) { // TODO once UI is finalized final boolean disabledByGlobalActions = false; final boolean disabledBySettings = false; // Don't allow trust agent if device is secured with a SIM PIN. This is here // mainly because there's no other way to prompt the user to enter their SIM PIN // once they get past the keyguard screen. final boolean disabledBySimPin = isSimPinSecure(); final boolean disabledByDpm = (dpm.getKeyguardDisabledFeatures(null, userId) & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0; return disabledByDpm || disabledByGlobalActions || disabledBySettings || disabledBySimPin; } return false; } private boolean isFingerprintDisabled(int userId) { final DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); return dpm != null && (dpm.getKeyguardDisabledFeatures(null, userId) & DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT) != 0; } public boolean getUserHasTrust(int userId) { return !isTrustDisabled(userId) && mUserHasTrust.get(userId) || mUserFingerprintRecognized.get(userId); } public boolean getUserTrustIsManaged(int userId) { return mUserTrustIsManaged.get(userId) && !isTrustDisabled(userId); } static class DisplayClientState { public int clientGeneration; public boolean clearing; public PendingIntent intent; public int playbackState; public long playbackEventTime; } private DisplayClientState mDisplayClientState = new DisplayClientState(); private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (DEBUG) Log.d(TAG, "received broadcast " + action); if (Intent.ACTION_TIME_TICK.equals(action) || Intent.ACTION_TIME_CHANGED.equals(action) || Intent.ACTION_TIMEZONE_CHANGED.equals(action)) { mHandler.sendEmptyMessage(MSG_TIME_UPDATE); } else if (TelephonyIntents.SPN_STRINGS_UPDATED_ACTION.equals(action)) { mTelephonyPlmn = getTelephonyPlmnFrom(intent); mTelephonySpn = getTelephonySpnFrom(intent); mHandler.sendEmptyMessage(MSG_CARRIER_INFO_UPDATE); } else if (Intent.ACTION_BATTERY_CHANGED.equals(action)) { final int status = intent.getIntExtra(EXTRA_STATUS, BATTERY_STATUS_UNKNOWN); final int plugged = intent.getIntExtra(EXTRA_PLUGGED, 0); final int level = intent.getIntExtra(EXTRA_LEVEL, 0); final int health = intent.getIntExtra(EXTRA_HEALTH, BATTERY_HEALTH_UNKNOWN); final Message msg = mHandler.obtainMessage( MSG_BATTERY_UPDATE, new BatteryStatus(status, level, plugged, health)); mHandler.sendMessage(msg); } else if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)) { if (DEBUG_SIM_STATES) { Log.v(TAG, "action " + action + " state" + intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE)); } mHandler.sendMessage(mHandler.obtainMessage( MSG_SIM_STATE_CHANGE, SimArgs.fromIntent(intent))); } else if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) { mHandler.sendMessage(mHandler.obtainMessage(MSG_RINGER_MODE_CHANGED, intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1), 0)); } else if (TelephonyManager.ACTION_PHONE_STATE_CHANGED.equals(action)) { String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE); mHandler.sendMessage(mHandler.obtainMessage(MSG_PHONE_STATE_CHANGED, state)); } else if (Intent.ACTION_USER_REMOVED.equals(action)) { mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_REMOVED, intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0), 0)); } else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) { dispatchBootCompleted(); } } }; private final BroadcastReceiver mBroadcastAllReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED.equals(action)) { mHandler.sendEmptyMessage(MSG_TIME_UPDATE); } else if (Intent.ACTION_USER_INFO_CHANGED.equals(action)) { mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_INFO_CHANGED, intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId()), 0)); } else if (ACTION_FACE_UNLOCK_STARTED.equals(action)) { mHandler.sendMessage(mHandler.obtainMessage(MSG_FACE_UNLOCK_STATE_CHANGED, 1, getSendingUserId())); } else if (ACTION_FACE_UNLOCK_STOPPED.equals(action)) { mHandler.sendMessage(mHandler.obtainMessage(MSG_FACE_UNLOCK_STATE_CHANGED, 0, getSendingUserId())); } else if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED .equals(action)) { mHandler.sendEmptyMessage(MSG_DPM_STATE_CHANGED); } } }; private FingerprintManagerReceiver mFingerprintManagerReceiver = new FingerprintManagerReceiver() { @Override public void onProcessed(int fingerprintId) { mHandler.obtainMessage(MSG_FINGERPRINT_PROCESSED, fingerprintId, 0).sendToTarget(); }; @Override public void onAcquired(int info) { mHandler.obtainMessage(MSG_FINGERPRINT_ACQUIRED, info, 0).sendToTarget(); } @Override public void onError(int error) { if (DEBUG) Log.w(TAG, "FingerprintManager reported error: " + error); } }; /** * When we receive a * {@link com.android.internal.telephony.TelephonyIntents#ACTION_SIM_STATE_CHANGED} broadcast, * and then pass a result via our handler to {@link KeyguardUpdateMonitor#handleSimStateChange}, * we need a single object to pass to the handler. This class helps decode * the intent and provide a {@link SimCard.State} result. */ private static class SimArgs { public final IccCardConstants.State simState; SimArgs(IccCardConstants.State state) { simState = state; } static SimArgs fromIntent(Intent intent) { IccCardConstants.State state; if (!TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(intent.getAction())) { throw new IllegalArgumentException("only handles intent ACTION_SIM_STATE_CHANGED"); } String stateExtra = intent.getStringExtra(IccCardConstants.INTENT_KEY_ICC_STATE); if (IccCardConstants.INTENT_VALUE_ICC_ABSENT.equals(stateExtra)) { final String absentReason = intent .getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON); if (IccCardConstants.INTENT_VALUE_ABSENT_ON_PERM_DISABLED.equals( absentReason)) { state = IccCardConstants.State.PERM_DISABLED; } else { state = IccCardConstants.State.ABSENT; } } else if (IccCardConstants.INTENT_VALUE_ICC_READY.equals(stateExtra)) { state = IccCardConstants.State.READY; } else if (IccCardConstants.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) { final String lockedReason = intent .getStringExtra(IccCardConstants.INTENT_KEY_LOCKED_REASON); if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) { state = IccCardConstants.State.PIN_REQUIRED; } else if (IccCardConstants.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) { state = IccCardConstants.State.PUK_REQUIRED; } else { state = IccCardConstants.State.UNKNOWN; } } else if (IccCardConstants.INTENT_VALUE_LOCKED_NETWORK.equals(stateExtra)) { state = IccCardConstants.State.NETWORK_LOCKED; } else if (IccCardConstants.INTENT_VALUE_ICC_LOADED.equals(stateExtra) || IccCardConstants.INTENT_VALUE_ICC_IMSI.equals(stateExtra)) { // This is required because telephony doesn't return to "READY" after // these state transitions. See bug 7197471. state = IccCardConstants.State.READY; } else { state = IccCardConstants.State.UNKNOWN; } return new SimArgs(state); } public String toString() { return simState.toString(); } } public static class BatteryStatus { public final int status; public final int level; public final int plugged; public final int health; public BatteryStatus(int status, int level, int plugged, int health) { this.status = status; this.level = level; this.plugged = plugged; this.health = health; } /** * Determine whether the device is plugged in (USB, power, or wireless). * @return true if the device is plugged in. */ boolean isPluggedIn() { return plugged == BatteryManager.BATTERY_PLUGGED_AC || plugged == BatteryManager.BATTERY_PLUGGED_USB || plugged == BatteryManager.BATTERY_PLUGGED_WIRELESS; } /** * Whether or not the device is charged. Note that some devices never return 100% for * battery level, so this allows either battery level or status to determine if the * battery is charged. * @return true if the device is charged */ public boolean isCharged() { return status == BATTERY_STATUS_FULL || level >= 100; } /** * Whether battery is low and needs to be charged. * @return true if battery is low */ public boolean isBatteryLow() { return level < LOW_BATTERY_THRESHOLD; } } public static KeyguardUpdateMonitor getInstance(Context context) { if (sInstance == null) { sInstance = new KeyguardUpdateMonitor(context); } return sInstance; } protected void handleScreenTurnedOn() { final int count = mCallbacks.size(); for (int i = 0; i < count; i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onScreenTurnedOn(); } } } protected void handleScreenTurnedOff(int arg1) { clearFingerprintRecognized(); final int count = mCallbacks.size(); for (int i = 0; i < count; i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onScreenTurnedOff(arg1); } } } /** * IMPORTANT: Must be called from UI thread. */ public void dispatchSetBackground(Bitmap bmp) { if (DEBUG) Log.d(TAG, "dispatchSetBackground"); final int count = mCallbacks.size(); for (int i = 0; i < count; i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onSetBackground(bmp); } } } private void handleUserInfoChanged(int userId) { for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onUserInfoChanged(userId); } } } private KeyguardUpdateMonitor(Context context) { mContext = context; mDeviceProvisioned = isDeviceProvisionedInSettingsDb(); // Since device can't be un-provisioned, we only need to register a content observer // to update mDeviceProvisioned when we are... if (!mDeviceProvisioned) { watchForDeviceProvisioning(); } // Take a guess at initial SIM state, battery status and PLMN until we get an update mSimState = IccCardConstants.State.NOT_READY; mBatteryStatus = new BatteryStatus(BATTERY_STATUS_UNKNOWN, 100, 0, 0); mTelephonyPlmn = getDefaultPlmn(); // Watch for interesting updates final IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_TIME_TICK); filter.addAction(Intent.ACTION_TIME_CHANGED); filter.addAction(Intent.ACTION_BATTERY_CHANGED); filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED); filter.addAction(TelephonyManager.ACTION_PHONE_STATE_CHANGED); filter.addAction(TelephonyIntents.SPN_STRINGS_UPDATED_ACTION); filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); filter.addAction(Intent.ACTION_USER_REMOVED); context.registerReceiver(mBroadcastReceiver, filter); final IntentFilter bootCompleteFilter = new IntentFilter(); bootCompleteFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); bootCompleteFilter.addAction(Intent.ACTION_BOOT_COMPLETED); context.registerReceiver(mBroadcastReceiver, bootCompleteFilter); final IntentFilter allUserFilter = new IntentFilter(); allUserFilter.addAction(Intent.ACTION_USER_INFO_CHANGED); allUserFilter.addAction(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED); allUserFilter.addAction(ACTION_FACE_UNLOCK_STARTED); allUserFilter.addAction(ACTION_FACE_UNLOCK_STOPPED); allUserFilter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); context.registerReceiverAsUser(mBroadcastAllReceiver, UserHandle.ALL, allUserFilter, null, null); try { ActivityManagerNative.getDefault().registerUserSwitchObserver( new IUserSwitchObserver.Stub() { @Override public void onUserSwitching(int newUserId, IRemoteCallback reply) { mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCHING, newUserId, 0, reply)); mSwitchingUser = true; } @Override public void onUserSwitchComplete(int newUserId) throws RemoteException { mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_SWITCH_COMPLETE, newUserId, 0)); mSwitchingUser = false; } }); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } TrustManager trustManager = (TrustManager) context.getSystemService(Context.TRUST_SERVICE); trustManager.registerTrustListener(this); FingerprintManager fpm; fpm = (FingerprintManager) context.getSystemService(Context.FINGERPRINT_SERVICE); fpm.startListening(mFingerprintManagerReceiver); } private boolean isDeviceProvisionedInSettingsDb() { return Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 0) != 0; } private void watchForDeviceProvisioning() { mDeviceProvisionedObserver = new ContentObserver(mHandler) { @Override public void onChange(boolean selfChange) { super.onChange(selfChange); mDeviceProvisioned = isDeviceProvisionedInSettingsDb(); if (mDeviceProvisioned) { mHandler.sendEmptyMessage(MSG_DEVICE_PROVISIONED); } if (DEBUG) Log.d(TAG, "DEVICE_PROVISIONED state = " + mDeviceProvisioned); } }; mContext.getContentResolver().registerContentObserver( Settings.Global.getUriFor(Settings.Global.DEVICE_PROVISIONED), false, mDeviceProvisionedObserver); // prevent a race condition between where we check the flag and where we register the // observer by grabbing the value once again... boolean provisioned = isDeviceProvisionedInSettingsDb(); if (provisioned != mDeviceProvisioned) { mDeviceProvisioned = provisioned; if (mDeviceProvisioned) { mHandler.sendEmptyMessage(MSG_DEVICE_PROVISIONED); } } } /** * Handle {@link #MSG_DPM_STATE_CHANGED} */ protected void handleDevicePolicyManagerStateChanged() { for (int i = mCallbacks.size() - 1; i >= 0; i--) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onDevicePolicyManagerStateChanged(); } } } /** * Handle {@link #MSG_USER_SWITCHING} */ protected void handleUserSwitching(int userId, IRemoteCallback reply) { for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onUserSwitching(userId); } } try { reply.sendResult(null); } catch (RemoteException e) { } } /** * Handle {@link #MSG_USER_SWITCH_COMPLETE} */ protected void handleUserSwitchComplete(int userId) { for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onUserSwitchComplete(userId); } } } /** * This is exposed since {@link Intent#ACTION_BOOT_COMPLETED} is not sticky. If * keyguard crashes sometime after boot, then it will never receive this * broadcast and hence not handle the event. This method is ultimately called by * PhoneWindowManager in this case. */ public void dispatchBootCompleted() { mHandler.sendEmptyMessage(MSG_BOOT_COMPLETED); } /** * Handle {@link #MSG_BOOT_COMPLETED} */ protected void handleBootCompleted() { if (mBootCompleted) return; mBootCompleted = true; for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onBootCompleted(); } } } /** * We need to store this state in the KeyguardUpdateMonitor since this class will not be * destroyed. */ public boolean hasBootCompleted() { return mBootCompleted; } /** * Handle {@link #MSG_USER_REMOVED} */ protected void handleUserRemoved(int userId) { for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onUserRemoved(userId); } } } /** * Handle {@link #MSG_DEVICE_PROVISIONED} */ protected void handleDeviceProvisioned() { for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onDeviceProvisioned(); } } if (mDeviceProvisionedObserver != null) { // We don't need the observer anymore... mContext.getContentResolver().unregisterContentObserver(mDeviceProvisionedObserver); mDeviceProvisionedObserver = null; } } /** * Handle {@link #MSG_PHONE_STATE_CHANGED} */ protected void handlePhoneStateChanged(String newState) { if (DEBUG) Log.d(TAG, "handlePhoneStateChanged(" + newState + ")"); if (TelephonyManager.EXTRA_STATE_IDLE.equals(newState)) { mPhoneState = TelephonyManager.CALL_STATE_IDLE; } else if (TelephonyManager.EXTRA_STATE_OFFHOOK.equals(newState)) { mPhoneState = TelephonyManager.CALL_STATE_OFFHOOK; } else if (TelephonyManager.EXTRA_STATE_RINGING.equals(newState)) { mPhoneState = TelephonyManager.CALL_STATE_RINGING; } for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onPhoneStateChanged(mPhoneState); } } } /** * Handle {@link #MSG_RINGER_MODE_CHANGED} */ protected void handleRingerModeChange(int mode) { if (DEBUG) Log.d(TAG, "handleRingerModeChange(" + mode + ")"); mRingMode = mode; for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onRingerModeChanged(mode); } } } /** * Handle {@link #MSG_TIME_UPDATE} */ private void handleTimeUpdate() { if (DEBUG) Log.d(TAG, "handleTimeUpdate"); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onTimeChanged(); } } } /** * Handle {@link #MSG_BATTERY_UPDATE} */ private void handleBatteryUpdate(BatteryStatus status) { if (DEBUG) Log.d(TAG, "handleBatteryUpdate"); final boolean batteryUpdateInteresting = isBatteryUpdateInteresting(mBatteryStatus, status); mBatteryStatus = status; if (batteryUpdateInteresting) { for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onRefreshBatteryInfo(status); } } } } /** * Handle {@link #MSG_CARRIER_INFO_UPDATE} */ private void handleCarrierInfoUpdate() { if (DEBUG) Log.d(TAG, "handleCarrierInfoUpdate: plmn = " + mTelephonyPlmn + ", spn = " + mTelephonySpn); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onRefreshCarrierInfo(mTelephonyPlmn, mTelephonySpn); } } } /** * Handle {@link #MSG_SIM_STATE_CHANGE} */ private void handleSimStateChange(SimArgs simArgs) { final IccCardConstants.State state = simArgs.simState; if (DEBUG) { Log.d(TAG, "handleSimStateChange: intentValue = " + simArgs + " " + "state resolved to " + state.toString()); } if (state != IccCardConstants.State.UNKNOWN && state != mSimState) { mSimState = state; for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onSimStateChanged(state); } } } } /** * Handle {@link #MSG_CLOCK_VISIBILITY_CHANGED} */ private void handleClockVisibilityChanged() { if (DEBUG) Log.d(TAG, "handleClockVisibilityChanged()"); for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onClockVisibilityChanged(); } } } /** * Handle {@link #MSG_KEYGUARD_VISIBILITY_CHANGED} */ private void handleKeyguardVisibilityChanged(int showing) { if (DEBUG) Log.d(TAG, "handleKeyguardVisibilityChanged(" + showing + ")"); boolean isShowing = (showing == 1); mKeyguardIsVisible = isShowing; for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onKeyguardVisibilityChangedRaw(isShowing); } } } /** * Handle {@link #MSG_KEYGUARD_BOUNCER_CHANGED} * @see #sendKeyguardBouncerChanged(boolean) */ private void handleKeyguardBouncerChanged(int bouncer) { if (DEBUG) Log.d(TAG, "handleKeyguardBouncerChanged(" + bouncer + ")"); boolean isBouncer = (bouncer == 1); mBouncer = isBouncer; for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onKeyguardBouncerChanged(isBouncer); } } } /** * Handle {@link #MSG_REPORT_EMERGENCY_CALL_ACTION} */ private void handleReportEmergencyCallAction() { for (int i = 0; i < mCallbacks.size(); i++) { KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get(); if (cb != null) { cb.onEmergencyCallAction(); } } } public boolean isKeyguardVisible() { return mKeyguardIsVisible; } /** * @return if the keyguard is currently in bouncer mode. */ public boolean isKeyguardBouncer() { return mBouncer; } public boolean isSwitchingUser() { return mSwitchingUser; } private static boolean isBatteryUpdateInteresting(BatteryStatus old, BatteryStatus current) { final boolean nowPluggedIn = current.isPluggedIn(); final boolean wasPluggedIn = old.isPluggedIn(); final boolean stateChangedWhilePluggedIn = wasPluggedIn == true && nowPluggedIn == true && (old.status != current.status); // change in plug state is always interesting if (wasPluggedIn != nowPluggedIn || stateChangedWhilePluggedIn) { return true; } // change in battery level while plugged in if (nowPluggedIn && old.level != current.level) { return true; } // change where battery needs charging if (!nowPluggedIn && current.isBatteryLow() && current.level != old.level) { return true; } return false; } /** * @param intent The intent with action {@link TelephonyIntents#SPN_STRINGS_UPDATED_ACTION} * @return The string to use for the plmn, or null if it should not be shown. */ private CharSequence getTelephonyPlmnFrom(Intent intent) { if (intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_PLMN, false)) { final String plmn = intent.getStringExtra(TelephonyIntents.EXTRA_PLMN); return (plmn != null) ? plmn : getDefaultPlmn(); } return null; } /** * @return The default plmn (no service) */ private CharSequence getDefaultPlmn() { return mContext.getResources().getText(R.string.keyguard_carrier_default); } /** * @param intent The intent with action {@link Telephony.Intents#SPN_STRINGS_UPDATED_ACTION} * @return The string to use for the plmn, or null if it should not be shown. */ private CharSequence getTelephonySpnFrom(Intent intent) { if (intent.getBooleanExtra(TelephonyIntents.EXTRA_SHOW_SPN, false)) { final String spn = intent.getStringExtra(TelephonyIntents.EXTRA_SPN); if (spn != null) { return spn; } } return null; } /** * Remove the given observer's callback. * * @param callback The callback to remove */ public void removeCallback(KeyguardUpdateMonitorCallback callback) { if (DEBUG) Log.v(TAG, "*** unregister callback for " + callback); for (int i = mCallbacks.size() - 1; i >= 0; i--) { if (mCallbacks.get(i).get() == callback) { mCallbacks.remove(i); } } } /** * Register to receive notifications about general keyguard information * (see {@link InfoCallback}. * @param callback The callback to register */ public void registerCallback(KeyguardUpdateMonitorCallback callback) { if (DEBUG) Log.v(TAG, "*** register callback for " + callback); // Prevent adding duplicate callbacks for (int i = 0; i < mCallbacks.size(); i++) { if (mCallbacks.get(i).get() == callback) { if (DEBUG) Log.e(TAG, "Object tried to add another callback", new Exception("Called by")); return; } } mCallbacks.add(new WeakReference(callback)); removeCallback(null); // remove unused references sendUpdates(callback); } private void sendUpdates(KeyguardUpdateMonitorCallback callback) { // Notify listener of the current state callback.onRefreshBatteryInfo(mBatteryStatus); callback.onTimeChanged(); callback.onRingerModeChanged(mRingMode); callback.onPhoneStateChanged(mPhoneState); callback.onRefreshCarrierInfo(mTelephonyPlmn, mTelephonySpn); callback.onClockVisibilityChanged(); callback.onSimStateChanged(mSimState); } public void sendKeyguardVisibilityChanged(boolean showing) { if (DEBUG) Log.d(TAG, "sendKeyguardVisibilityChanged(" + showing + ")"); Message message = mHandler.obtainMessage(MSG_KEYGUARD_VISIBILITY_CHANGED); message.arg1 = showing ? 1 : 0; message.sendToTarget(); } /** * @see #handleKeyguardBouncerChanged(int) */ public void sendKeyguardBouncerChanged(boolean showingBouncer) { if (DEBUG) Log.d(TAG, "sendKeyguardBouncerChanged(" + showingBouncer + ")"); Message message = mHandler.obtainMessage(MSG_KEYGUARD_BOUNCER_CHANGED); message.arg1 = showingBouncer ? 1 : 0; message.sendToTarget(); } public void reportClockVisible(boolean visible) { mClockVisible = visible; mHandler.obtainMessage(MSG_CLOCK_VISIBILITY_CHANGED).sendToTarget(); } public IccCardConstants.State getSimState() { return mSimState; } /** * Report that the user successfully entered the SIM PIN or PUK/SIM PIN so we * have the information earlier than waiting for the intent * broadcast from the telephony code. * * NOTE: Because handleSimStateChange() invokes callbacks immediately without going * through mHandler, this *must* be called from the UI thread. */ public void reportSimUnlocked() { handleSimStateChange(new SimArgs(IccCardConstants.State.READY)); } /** * Report that the emergency call button has been pressed and the emergency dialer is * about to be displayed. * * @param bypassHandler runs immediately. * * NOTE: Must be called from UI thread if bypassHandler == true. */ public void reportEmergencyCallAction(boolean bypassHandler) { if (!bypassHandler) { mHandler.obtainMessage(MSG_REPORT_EMERGENCY_CALL_ACTION).sendToTarget(); } else { handleReportEmergencyCallAction(); } } public CharSequence getTelephonyPlmn() { return mTelephonyPlmn; } public CharSequence getTelephonySpn() { return mTelephonySpn; } /** * @return Whether the device is provisioned (whether they have gone through * the setup wizard) */ public boolean isDeviceProvisioned() { return mDeviceProvisioned; } public int getFailedUnlockAttempts() { return mFailedAttempts; } public void clearFailedUnlockAttempts() { mFailedAttempts = 0; mFailedBiometricUnlockAttempts = 0; } public void clearFingerprintRecognized() { mUserFingerprintRecognized.clear(); } public void reportFailedUnlockAttempt() { mFailedAttempts++; } public boolean isClockVisible() { return mClockVisible; } public int getPhoneState() { return mPhoneState; } public void reportFailedBiometricUnlockAttempt() { mFailedBiometricUnlockAttempts++; } public boolean getMaxBiometricUnlockAttemptsReached() { return mFailedBiometricUnlockAttempts >= FAILED_BIOMETRIC_UNLOCK_ATTEMPTS_BEFORE_BACKUP; } public boolean isAlternateUnlockEnabled() { return mAlternateUnlockEnabled; } public void setAlternateUnlockEnabled(boolean enabled) { mAlternateUnlockEnabled = enabled; } public boolean isSimLocked() { return isSimLocked(mSimState); } public static boolean isSimLocked(IccCardConstants.State state) { return state == IccCardConstants.State.PIN_REQUIRED || state == IccCardConstants.State.PUK_REQUIRED || state == IccCardConstants.State.PERM_DISABLED; } public boolean isSimPinSecure() { return isSimPinSecure(mSimState); } public static boolean isSimPinSecure(IccCardConstants.State state) { final IccCardConstants.State simState = state; return (simState == IccCardConstants.State.PIN_REQUIRED || simState == IccCardConstants.State.PUK_REQUIRED || simState == IccCardConstants.State.PERM_DISABLED); } public DisplayClientState getCachedDisplayClientState() { return mDisplayClientState; } // TODO: use these callbacks elsewhere in place of the existing notifyScreen*() // (KeyguardViewMediator, KeyguardHostView) public void dispatchScreenTurnedOn() { synchronized (this) { mScreenOn = true; } mHandler.sendEmptyMessage(MSG_SCREEN_TURNED_ON); } public void dispatchScreenTurndOff(int why) { synchronized(this) { mScreenOn = false; } mHandler.sendMessage(mHandler.obtainMessage(MSG_SCREEN_TURNED_OFF, why, 0)); } public boolean isScreenOn() { return mScreenOn; } }