blob: 7408dd40b5cad88784541aa26d844d13b36f60db [file] [log] [blame]
/*
* Copyright (C) 2014 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.server.trust;
import android.Manifest;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AlarmManager;
import android.app.AlarmManager.OnAlarmListener;
import android.app.admin.DevicePolicyManager;
import android.app.trust.ITrustListener;
import android.app.trust.ITrustManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.database.ContentObserver;
import android.graphics.drawable.Drawable;
import android.hardware.biometrics.BiometricSourceType;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.DeadObjectException;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.service.trust.TrustAgentService;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.Xml;
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
import com.android.internal.util.DumpUtils;
import com.android.internal.widget.LockPatternUtils;
import com.android.server.SystemService;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
/**
* Manages trust agents and trust listeners.
*
* It is responsible for binding to the enabled {@link android.service.trust.TrustAgentService}s
* of each user and notifies them about events that are relevant to them.
* It start and stops them based on the value of
* {@link com.android.internal.widget.LockPatternUtils#getEnabledTrustAgents(int)}.
*
* It also keeps a set of {@link android.app.trust.ITrustListener}s that are notified whenever the
* trust state changes for any user.
*
* Trust state and the setting of enabled agents is kept per user and each user has its own
* instance of a {@link android.service.trust.TrustAgentService}.
*/
public class TrustManagerService extends SystemService {
private static final String TAG = "TrustManagerService";
static final boolean DEBUG = Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.VERBOSE);
private static final Intent TRUST_AGENT_INTENT =
new Intent(TrustAgentService.SERVICE_INTERFACE);
private static final String PERMISSION_PROVIDE_AGENT = Manifest.permission.PROVIDE_TRUST_AGENT;
private static final int MSG_REGISTER_LISTENER = 1;
private static final int MSG_UNREGISTER_LISTENER = 2;
private static final int MSG_DISPATCH_UNLOCK_ATTEMPT = 3;
private static final int MSG_ENABLED_AGENTS_CHANGED = 4;
private static final int MSG_KEYGUARD_SHOWING_CHANGED = 6;
private static final int MSG_START_USER = 7;
private static final int MSG_CLEANUP_USER = 8;
private static final int MSG_SWITCH_USER = 9;
private static final int MSG_FLUSH_TRUST_USUALLY_MANAGED = 10;
private static final int MSG_UNLOCK_USER = 11;
private static final int MSG_STOP_USER = 12;
private static final int MSG_DISPATCH_UNLOCK_LOCKOUT = 13;
private static final int MSG_REFRESH_DEVICE_LOCKED_FOR_USER = 14;
private static final int MSG_SCHEDULE_TRUST_TIMEOUT = 15;
private static final int TRUST_USUALLY_MANAGED_FLUSH_DELAY = 2 * 60 * 1000;
private static final String TRUST_TIMEOUT_ALARM_TAG = "TrustManagerService.trustTimeoutForUser";
private static final long TRUST_TIMEOUT_IN_MILLIS = 4 * 60 * 60 * 1000;
private final ArraySet<AgentInfo> mActiveAgents = new ArraySet<>();
private final ArrayList<ITrustListener> mTrustListeners = new ArrayList<>();
private final Receiver mReceiver = new Receiver();
/* package */ final TrustArchive mArchive = new TrustArchive();
private final Context mContext;
private final LockPatternUtils mLockPatternUtils;
private final UserManager mUserManager;
private final ActivityManager mActivityManager;
@GuardedBy("mUserIsTrusted")
private final SparseBooleanArray mUserIsTrusted = new SparseBooleanArray();
@GuardedBy("mDeviceLockedForUser")
private final SparseBooleanArray mDeviceLockedForUser = new SparseBooleanArray();
@GuardedBy("mTrustUsuallyManagedForUser")
private final SparseBooleanArray mTrustUsuallyManagedForUser = new SparseBooleanArray();
// set to true only if user can skip bouncer
@GuardedBy("mUsersUnlockedByBiometric")
private final SparseBooleanArray mUsersUnlockedByBiometric = new SparseBooleanArray();
private final ArrayMap<Integer, TrustTimeoutAlarmListener> mTrustTimeoutAlarmListenerForUser =
new ArrayMap<>();
private AlarmManager mAlarmManager;
private final SettingsObserver mSettingsObserver;
private final StrongAuthTracker mStrongAuthTracker;
private boolean mTrustAgentsCanRun = false;
private int mCurrentUser = UserHandle.USER_SYSTEM;
public TrustManagerService(Context context) {
super(context);
mContext = context;
mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
mActivityManager = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
mLockPatternUtils = new LockPatternUtils(context);
mStrongAuthTracker = new StrongAuthTracker(context);
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
mSettingsObserver = new SettingsObserver(mHandler);
}
@Override
public void onStart() {
publishBinderService(Context.TRUST_SERVICE, mService);
}
@Override
public void onBootPhase(int phase) {
if (isSafeMode()) {
// No trust agents in safe mode.
return;
}
if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) {
mPackageMonitor.register(mContext, mHandler.getLooper(), UserHandle.ALL, true);
mReceiver.register(mContext);
mLockPatternUtils.registerStrongAuthTracker(mStrongAuthTracker);
} else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
mTrustAgentsCanRun = true;
refreshAgentList(UserHandle.USER_ALL);
refreshDeviceLockedForUser(UserHandle.USER_ALL);
} else if (phase == SystemService.PHASE_BOOT_COMPLETED) {
maybeEnableFactoryTrustAgents(mLockPatternUtils, UserHandle.USER_SYSTEM);
}
}
// Extend unlock config and logic
private final class SettingsObserver extends ContentObserver {
private final Uri TRUST_AGENTS_EXTEND_UNLOCK =
Settings.Secure.getUriFor(Settings.Secure.TRUST_AGENTS_EXTEND_UNLOCK);
private final Uri LOCK_SCREEN_WHEN_TRUST_LOST =
Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_WHEN_TRUST_LOST);
private final boolean mIsAutomotive;
private final ContentResolver mContentResolver;
private boolean mTrustAgentsExtendUnlock;
private boolean mLockWhenTrustLost;
/**
* Creates a settings observer
*
* @param handler The handler to run {@link #onChange} on, or null if none.
*/
SettingsObserver(Handler handler) {
super(handler);
PackageManager packageManager = getContext().getPackageManager();
mIsAutomotive = packageManager.hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
mContentResolver = getContext().getContentResolver();
updateContentObserver();
}
void updateContentObserver() {
mContentResolver.unregisterContentObserver(this);
mContentResolver.registerContentObserver(TRUST_AGENTS_EXTEND_UNLOCK,
false /* notifyForDescendents */,
this /* observer */,
mCurrentUser);
mContentResolver.registerContentObserver(LOCK_SCREEN_WHEN_TRUST_LOST,
false /* notifyForDescendents */,
this /* observer */,
mCurrentUser);
// Update the value immediately
onChange(true /* selfChange */, TRUST_AGENTS_EXTEND_UNLOCK);
onChange(true /* selfChange */, LOCK_SCREEN_WHEN_TRUST_LOST);
}
@Override
public void onChange(boolean selfChange, Uri uri) {
if (TRUST_AGENTS_EXTEND_UNLOCK.equals(uri)) {
// Smart lock should only extend unlock. The only exception is for automotive,
// where it can actively unlock the head unit.
int defaultValue = mIsAutomotive ? 0 : 1;
mTrustAgentsExtendUnlock =
Settings.Secure.getIntForUser(
mContentResolver,
Settings.Secure.TRUST_AGENTS_EXTEND_UNLOCK,
defaultValue,
mCurrentUser) != 0;
} else if (LOCK_SCREEN_WHEN_TRUST_LOST.equals(uri)) {
mLockWhenTrustLost =
Settings.Secure.getIntForUser(
mContentResolver,
Settings.Secure.LOCK_SCREEN_WHEN_TRUST_LOST,
0 /* default */,
mCurrentUser) != 0;
}
}
boolean getTrustAgentsExtendUnlock() {
return mTrustAgentsExtendUnlock;
}
boolean getLockWhenTrustLost() {
return mLockWhenTrustLost;
}
}
private void maybeLockScreen(int userId) {
if (userId != mCurrentUser) {
return;
}
if (mSettingsObserver.getLockWhenTrustLost()) {
if (DEBUG) Slog.d(TAG, "Locking device because trust was lost");
try {
WindowManagerGlobal.getWindowManagerService().lockNow(null);
} catch (RemoteException e) {
Slog.e(TAG, "Error locking screen when trust was lost");
}
// If active unlocking is not allowed, cancel any pending trust timeouts because the
// screen is already locked.
TrustTimeoutAlarmListener alarm = mTrustTimeoutAlarmListenerForUser.get(userId);
if (alarm != null && mSettingsObserver.getTrustAgentsExtendUnlock()) {
mAlarmManager.cancel(alarm);
alarm.setQueued(false /* isQueued */);
}
}
}
private void scheduleTrustTimeout(int userId, boolean override) {
int shouldOverride = override ? 1 : 0;
if (override) {
shouldOverride = 1;
}
mHandler.obtainMessage(MSG_SCHEDULE_TRUST_TIMEOUT, userId, shouldOverride).sendToTarget();
}
private void handleScheduleTrustTimeout(int userId, int shouldOverride) {
long when = SystemClock.elapsedRealtime() + TRUST_TIMEOUT_IN_MILLIS;
userId = mCurrentUser;
TrustTimeoutAlarmListener alarm = mTrustTimeoutAlarmListenerForUser.get(userId);
// Cancel existing trust timeouts for this user if needed.
if (alarm != null) {
if (shouldOverride == 0 && alarm.isQueued()) {
if (DEBUG) Slog.d(TAG, "Found existing trust timeout alarm. Skipping.");
return;
}
mAlarmManager.cancel(alarm);
} else {
alarm = new TrustTimeoutAlarmListener(userId);
mTrustTimeoutAlarmListenerForUser.put(userId, alarm);
}
if (DEBUG) Slog.d(TAG, "\tSetting up trust timeout alarm");
alarm.setQueued(true /* isQueued */);
mAlarmManager.setExact(
AlarmManager.ELAPSED_REALTIME_WAKEUP, when, TRUST_TIMEOUT_ALARM_TAG, alarm,
mHandler);
}
// Agent management
private static final class AgentInfo {
CharSequence label;
Drawable icon;
ComponentName component; // service that implements ITrustAgent
SettingsAttrs settings; // setting to launch to modify agent.
TrustAgentWrapper agent;
int userId;
@Override
public boolean equals(Object other) {
if (!(other instanceof AgentInfo)) {
return false;
}
AgentInfo o = (AgentInfo) other;
return component.equals(o.component) && userId == o.userId;
}
@Override
public int hashCode() {
return component.hashCode() * 31 + userId;
}
}
private void updateTrustAll() {
List<UserInfo> userInfos = mUserManager.getUsers(true /* excludeDying */);
for (UserInfo userInfo : userInfos) {
updateTrust(userInfo.id, 0);
}
}
public void updateTrust(int userId, int flags) {
updateTrust(userId, flags, false /* isFromUnlock */);
}
private void updateTrust(int userId, int flags, boolean isFromUnlock) {
boolean managed = aggregateIsTrustManaged(userId);
dispatchOnTrustManagedChanged(managed, userId);
if (mStrongAuthTracker.isTrustAllowedForUser(userId)
&& isTrustUsuallyManagedInternal(userId) != managed) {
updateTrustUsuallyManaged(userId, managed);
}
boolean trusted = aggregateIsTrusted(userId);
IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
boolean showingKeyguard = true;
try {
showingKeyguard = wm.isKeyguardLocked();
} catch (RemoteException e) {
}
boolean changed;
synchronized (mUserIsTrusted) {
if (mSettingsObserver.getTrustAgentsExtendUnlock()) {
// In extend unlock trust agents can only set the device to trusted if it already
// trusted or the device is unlocked. Attempting to set the device as trusted
// when the device is locked will be ignored.
changed = mUserIsTrusted.get(userId) != trusted;
trusted = trusted
&& (!showingKeyguard || isFromUnlock || !changed)
&& userId == mCurrentUser;
if (DEBUG) {
Slog.d(TAG, "Extend unlock setting trusted as " + Boolean.toString(trusted)
+ " && " + Boolean.toString(!showingKeyguard)
+ " && " + Boolean.toString(userId == mCurrentUser));
}
}
changed = mUserIsTrusted.get(userId) != trusted;
mUserIsTrusted.put(userId, trusted);
}
dispatchOnTrustChanged(trusted, userId, flags);
if (changed) {
refreshDeviceLockedForUser(userId);
if (!trusted) {
maybeLockScreen(userId);
} else {
scheduleTrustTimeout(userId, false /* override */);
}
}
}
private void updateTrustUsuallyManaged(int userId, boolean managed) {
synchronized (mTrustUsuallyManagedForUser) {
mTrustUsuallyManagedForUser.put(userId, managed);
}
// Wait a few minutes before committing to flash, in case the trust agent is transiently not
// managing trust (crashed, needs to acknowledge DPM restrictions, etc).
mHandler.removeMessages(MSG_FLUSH_TRUST_USUALLY_MANAGED);
mHandler.sendMessageDelayed(
mHandler.obtainMessage(MSG_FLUSH_TRUST_USUALLY_MANAGED),
TRUST_USUALLY_MANAGED_FLUSH_DELAY);
}
public long addEscrowToken(byte[] token, int userId) {
return mLockPatternUtils.addEscrowToken(token, userId,
(long handle, int userid) -> {
dispatchEscrowTokenActivatedLocked(handle, userid);
});
}
public boolean removeEscrowToken(long handle, int userId) {
return mLockPatternUtils.removeEscrowToken(handle, userId);
}
public boolean isEscrowTokenActive(long handle, int userId) {
return mLockPatternUtils.isEscrowTokenActive(handle, userId);
}
public void unlockUserWithToken(long handle, byte[] token, int userId) {
mLockPatternUtils.unlockUserWithToken(handle, token, userId);
}
void showKeyguardErrorMessage(CharSequence message) {
dispatchOnTrustError(message);
}
void refreshAgentList(int userIdOrAll) {
if (DEBUG) Slog.d(TAG, "refreshAgentList(" + userIdOrAll + ")");
if (!mTrustAgentsCanRun) {
return;
}
if (userIdOrAll != UserHandle.USER_ALL && userIdOrAll < UserHandle.USER_SYSTEM) {
Log.e(TAG, "refreshAgentList(userId=" + userIdOrAll + "): Invalid user handle,"
+ " must be USER_ALL or a specific user.", new Throwable("here"));
userIdOrAll = UserHandle.USER_ALL;
}
PackageManager pm = mContext.getPackageManager();
List<UserInfo> userInfos;
if (userIdOrAll == UserHandle.USER_ALL) {
userInfos = mUserManager.getUsers(true /* excludeDying */);
} else {
userInfos = new ArrayList<>();
userInfos.add(mUserManager.getUserInfo(userIdOrAll));
}
LockPatternUtils lockPatternUtils = mLockPatternUtils;
ArraySet<AgentInfo> obsoleteAgents = new ArraySet<>();
obsoleteAgents.addAll(mActiveAgents);
for (UserInfo userInfo : userInfos) {
if (userInfo == null || userInfo.partial || !userInfo.isEnabled()
|| userInfo.guestToRemove) continue;
if (!userInfo.supportsSwitchToByUser()) {
if (DEBUG) Slog.d(TAG, "refreshAgentList: skipping user " + userInfo.id
+ ": switchToByUser=false");
continue;
}
if (!mActivityManager.isUserRunning(userInfo.id)) {
if (DEBUG) Slog.d(TAG, "refreshAgentList: skipping user " + userInfo.id
+ ": user not started");
continue;
}
if (!lockPatternUtils.isSecure(userInfo.id)) {
if (DEBUG) Slog.d(TAG, "refreshAgentList: skipping user " + userInfo.id
+ ": no secure credential");
continue;
}
DevicePolicyManager dpm = lockPatternUtils.getDevicePolicyManager();
int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, userInfo.id);
final boolean disableTrustAgents =
(disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0;
List<ComponentName> enabledAgents = lockPatternUtils.getEnabledTrustAgents(userInfo.id);
if (enabledAgents == null) {
if (DEBUG) Slog.d(TAG, "refreshAgentList: skipping user " + userInfo.id
+ ": no agents enabled by user");
continue;
}
List<ResolveInfo> resolveInfos = resolveAllowedTrustAgents(pm, userInfo.id);
for (ResolveInfo resolveInfo : resolveInfos) {
ComponentName name = getComponentName(resolveInfo);
if (!enabledAgents.contains(name)) {
if (DEBUG) Slog.d(TAG, "refreshAgentList: skipping "
+ name.flattenToShortString() + " u"+ userInfo.id
+ ": not enabled by user");
continue;
}
if (disableTrustAgents) {
List<PersistableBundle> config =
dpm.getTrustAgentConfiguration(null /* admin */, name, userInfo.id);
// Disable agent if no features are enabled.
if (config == null || config.isEmpty()) {
if (DEBUG) Slog.d(TAG, "refreshAgentList: skipping "
+ name.flattenToShortString() + " u"+ userInfo.id
+ ": not allowed by DPM");
continue;
}
}
AgentInfo agentInfo = new AgentInfo();
agentInfo.component = name;
agentInfo.userId = userInfo.id;
if (!mActiveAgents.contains(agentInfo)) {
agentInfo.label = resolveInfo.loadLabel(pm);
agentInfo.icon = resolveInfo.loadIcon(pm);
agentInfo.settings = getSettingsAttrs(pm, resolveInfo);
} else {
int index = mActiveAgents.indexOf(agentInfo);
agentInfo = mActiveAgents.valueAt(index);
}
boolean directUnlock = false;
if (agentInfo.settings != null) {
directUnlock = resolveInfo.serviceInfo.directBootAware
&& agentInfo.settings.canUnlockProfile;
}
if (directUnlock) {
if (DEBUG) Slog.d(TAG, "refreshAgentList: trustagent " + name
+ "of user " + userInfo.id + "can unlock user profile.");
}
if (!mUserManager.isUserUnlockingOrUnlocked(userInfo.id)
&& !directUnlock) {
if (DEBUG) Slog.d(TAG, "refreshAgentList: skipping user " + userInfo.id
+ "'s trust agent " + name + ": FBE still locked and "
+ " the agent cannot unlock user profile.");
continue;
}
if (!mStrongAuthTracker.canAgentsRunForUser(userInfo.id)) {
int flag = mStrongAuthTracker.getStrongAuthForUser(userInfo.id);
if (flag != StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_LOCKOUT) {
if (flag != StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
|| !directUnlock) {
if (DEBUG)
Slog.d(TAG, "refreshAgentList: skipping user " + userInfo.id
+ ": prevented by StrongAuthTracker = 0x"
+ Integer.toHexString(mStrongAuthTracker.getStrongAuthForUser(
userInfo.id)));
continue;
}
}
}
if (agentInfo.agent == null) {
agentInfo.agent = new TrustAgentWrapper(mContext, this,
new Intent().setComponent(name), userInfo.getUserHandle());
}
if (!mActiveAgents.contains(agentInfo)) {
mActiveAgents.add(agentInfo);
} else {
obsoleteAgents.remove(agentInfo);
}
}
}
boolean trustMayHaveChanged = false;
for (int i = 0; i < obsoleteAgents.size(); i++) {
AgentInfo info = obsoleteAgents.valueAt(i);
if (userIdOrAll == UserHandle.USER_ALL || userIdOrAll == info.userId) {
if (info.agent.isManagingTrust()) {
trustMayHaveChanged = true;
}
info.agent.destroy();
mActiveAgents.remove(info);
}
}
if (trustMayHaveChanged) {
if (userIdOrAll == UserHandle.USER_ALL) {
updateTrustAll();
} else {
updateTrust(userIdOrAll, 0);
}
}
}
boolean isDeviceLockedInner(int userId) {
synchronized (mDeviceLockedForUser) {
return mDeviceLockedForUser.get(userId, true);
}
}
private void refreshDeviceLockedForUser(int userId) {
if (userId != UserHandle.USER_ALL && userId < UserHandle.USER_SYSTEM) {
Log.e(TAG, "refreshDeviceLockedForUser(userId=" + userId + "): Invalid user handle,"
+ " must be USER_ALL or a specific user.", new Throwable("here"));
userId = UserHandle.USER_ALL;
}
List<UserInfo> userInfos;
if (userId == UserHandle.USER_ALL) {
userInfos = mUserManager.getUsers(true /* excludeDying */);
} else {
userInfos = new ArrayList<>();
userInfos.add(mUserManager.getUserInfo(userId));
}
IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
for (int i = 0; i < userInfos.size(); i++) {
UserInfo info = userInfos.get(i);
if (info == null || info.partial || !info.isEnabled() || info.guestToRemove) {
continue;
}
int id = info.id;
boolean secure = mLockPatternUtils.isSecure(id);
if (!info.supportsSwitchToByUser()) {
if (info.isManagedProfile() && !secure) {
setDeviceLockedForUser(id, false);
}
continue;
}
boolean trusted = aggregateIsTrusted(id);
boolean showingKeyguard = true;
boolean biometricAuthenticated = false;
if (mCurrentUser == id) {
synchronized(mUsersUnlockedByBiometric) {
biometricAuthenticated = mUsersUnlockedByBiometric.get(id, false);
}
try {
showingKeyguard = wm.isKeyguardLocked();
} catch (RemoteException e) {
}
}
boolean deviceLocked = secure && showingKeyguard && !trusted &&
!biometricAuthenticated;
setDeviceLockedForUser(id, deviceLocked);
}
}
private void setDeviceLockedForUser(@UserIdInt int userId, boolean locked) {
final boolean changed;
synchronized (mDeviceLockedForUser) {
changed = isDeviceLockedInner(userId) != locked;
mDeviceLockedForUser.put(userId, locked);
}
if (changed) {
dispatchDeviceLocked(userId, locked);
}
}
private void dispatchDeviceLocked(int userId, boolean isLocked) {
for (int i = 0; i < mActiveAgents.size(); i++) {
AgentInfo agent = mActiveAgents.valueAt(i);
if (agent.userId == userId) {
if (isLocked) {
agent.agent.onDeviceLocked();
} else{
agent.agent.onDeviceUnlocked();
}
}
}
}
private void dispatchEscrowTokenActivatedLocked(long handle, int userId) {
for (int i = 0; i < mActiveAgents.size(); i++) {
AgentInfo agent = mActiveAgents.valueAt(i);
if (agent.userId == userId) {
agent.agent.onEscrowTokenActivated(handle, userId);
}
}
}
void updateDevicePolicyFeatures() {
boolean changed = false;
for (int i = 0; i < mActiveAgents.size(); i++) {
AgentInfo info = mActiveAgents.valueAt(i);
if (info.agent.isConnected()) {
info.agent.updateDevicePolicyFeatures();
changed = true;
}
}
if (changed) {
mArchive.logDevicePolicyChanged();
}
}
private void removeAgentsOfPackage(String packageName) {
boolean trustMayHaveChanged = false;
for (int i = mActiveAgents.size() - 1; i >= 0; i--) {
AgentInfo info = mActiveAgents.valueAt(i);
if (packageName.equals(info.component.getPackageName())) {
Log.i(TAG, "Resetting agent " + info.component.flattenToShortString());
if (info.agent.isManagingTrust()) {
trustMayHaveChanged = true;
}
info.agent.destroy();
mActiveAgents.removeAt(i);
}
}
if (trustMayHaveChanged) {
updateTrustAll();
}
}
public void resetAgent(ComponentName name, int userId) {
boolean trustMayHaveChanged = false;
for (int i = mActiveAgents.size() - 1; i >= 0; i--) {
AgentInfo info = mActiveAgents.valueAt(i);
if (name.equals(info.component) && userId == info.userId) {
Log.i(TAG, "Resetting agent " + info.component.flattenToShortString());
if (info.agent.isManagingTrust()) {
trustMayHaveChanged = true;
}
info.agent.destroy();
mActiveAgents.removeAt(i);
}
}
if (trustMayHaveChanged) {
updateTrust(userId, 0);
}
refreshAgentList(userId);
}
private SettingsAttrs getSettingsAttrs(PackageManager pm, ResolveInfo resolveInfo) {
if (resolveInfo == null || resolveInfo.serviceInfo == null
|| resolveInfo.serviceInfo.metaData == null) return null;
String cn = null;
boolean canUnlockProfile = false;
XmlResourceParser parser = null;
Exception caughtException = null;
try {
parser = resolveInfo.serviceInfo.loadXmlMetaData(pm,
TrustAgentService.TRUST_AGENT_META_DATA);
if (parser == null) {
Slog.w(TAG, "Can't find " + TrustAgentService.TRUST_AGENT_META_DATA + " meta-data");
return null;
}
Resources res = pm.getResourcesForApplication(resolveInfo.serviceInfo.applicationInfo);
AttributeSet attrs = Xml.asAttributeSet(parser);
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& type != XmlPullParser.START_TAG) {
// Drain preamble.
}
String nodeName = parser.getName();
if (!"trust-agent".equals(nodeName)) {
Slog.w(TAG, "Meta-data does not start with trust-agent tag");
return null;
}
TypedArray sa = res
.obtainAttributes(attrs, com.android.internal.R.styleable.TrustAgent);
cn = sa.getString(com.android.internal.R.styleable.TrustAgent_settingsActivity);
canUnlockProfile = sa.getBoolean(
com.android.internal.R.styleable.TrustAgent_unlockProfile, false);
sa.recycle();
} catch (PackageManager.NameNotFoundException e) {
caughtException = e;
} catch (IOException e) {
caughtException = e;
} catch (XmlPullParserException e) {
caughtException = e;
} finally {
if (parser != null) parser.close();
}
if (caughtException != null) {
Slog.w(TAG, "Error parsing : " + resolveInfo.serviceInfo.packageName, caughtException);
return null;
}
if (cn == null) {
return null;
}
if (cn.indexOf('/') < 0) {
cn = resolveInfo.serviceInfo.packageName + "/" + cn;
}
return new SettingsAttrs(ComponentName.unflattenFromString(cn), canUnlockProfile);
}
private ComponentName getComponentName(ResolveInfo resolveInfo) {
if (resolveInfo == null || resolveInfo.serviceInfo == null) return null;
return new ComponentName(resolveInfo.serviceInfo.packageName, resolveInfo.serviceInfo.name);
}
private void maybeEnableFactoryTrustAgents(LockPatternUtils utils, int userId) {
if (0 != Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.TRUST_AGENTS_INITIALIZED, 0, userId)) {
return;
}
PackageManager pm = mContext.getPackageManager();
List<ResolveInfo> resolveInfos = resolveAllowedTrustAgents(pm, userId);
ComponentName defaultAgent = getDefaultFactoryTrustAgent(mContext);
boolean shouldUseDefaultAgent = defaultAgent != null;
ArraySet<ComponentName> discoveredAgents = new ArraySet<>();
if (shouldUseDefaultAgent) {
discoveredAgents.add(defaultAgent);
Log.i(TAG, "Enabling " + defaultAgent + " because it is a default agent.");
} else { // A default agent is not set; perform regular trust agent discovery
for (ResolveInfo resolveInfo : resolveInfos) {
ComponentName componentName = getComponentName(resolveInfo);
int applicationInfoFlags = resolveInfo.serviceInfo.applicationInfo.flags;
if ((applicationInfoFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {
Log.i(TAG, "Leaving agent " + componentName + " disabled because package "
+ "is not a system package.");
continue;
}
discoveredAgents.add(componentName);
}
}
List<ComponentName> previouslyEnabledAgents = utils.getEnabledTrustAgents(userId);
if (previouslyEnabledAgents != null) {
discoveredAgents.addAll(previouslyEnabledAgents);
}
utils.setEnabledTrustAgents(discoveredAgents, userId);
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.TRUST_AGENTS_INITIALIZED, 1, userId);
}
/**
* Returns the {@link ComponentName} for the default trust agent, or {@code null} if there
* is no trust agent set.
*/
private static ComponentName getDefaultFactoryTrustAgent(Context context) {
String defaultTrustAgent = context.getResources()
.getString(com.android.internal.R.string.config_defaultTrustAgent);
if (TextUtils.isEmpty(defaultTrustAgent)) {
return null;
}
return ComponentName.unflattenFromString(defaultTrustAgent);
}
private List<ResolveInfo> resolveAllowedTrustAgents(PackageManager pm, int userId) {
List<ResolveInfo> resolveInfos = pm.queryIntentServicesAsUser(TRUST_AGENT_INTENT,
PackageManager.GET_META_DATA |
PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
userId);
ArrayList<ResolveInfo> allowedAgents = new ArrayList<>(resolveInfos.size());
for (ResolveInfo resolveInfo : resolveInfos) {
if (resolveInfo.serviceInfo == null) continue;
if (resolveInfo.serviceInfo.applicationInfo == null) continue;
String packageName = resolveInfo.serviceInfo.packageName;
if (pm.checkPermission(PERMISSION_PROVIDE_AGENT, packageName)
!= PackageManager.PERMISSION_GRANTED) {
ComponentName name = getComponentName(resolveInfo);
Log.w(TAG, "Skipping agent " + name + " because package does not have"
+ " permission " + PERMISSION_PROVIDE_AGENT + ".");
continue;
}
allowedAgents.add(resolveInfo);
}
return allowedAgents;
}
// Agent dispatch and aggregation
private boolean aggregateIsTrusted(int userId) {
if (!mStrongAuthTracker.isTrustAllowedForUser(userId)) {
return false;
}
for (int i = 0; i < mActiveAgents.size(); i++) {
AgentInfo info = mActiveAgents.valueAt(i);
if (info.userId == userId) {
if (info.agent.isTrusted()) {
return true;
}
}
}
return false;
}
private boolean aggregateIsTrustManaged(int userId) {
if (!mStrongAuthTracker.isTrustAllowedForUser(userId)) {
return false;
}
for (int i = 0; i < mActiveAgents.size(); i++) {
AgentInfo info = mActiveAgents.valueAt(i);
if (info.userId == userId) {
if (info.agent.isManagingTrust()) {
return true;
}
}
}
return false;
}
private void dispatchUnlockAttempt(boolean successful, int userId) {
if (successful) {
mStrongAuthTracker.allowTrustFromUnlock(userId);
// Allow the presence of trust on a successful unlock attempt to extend unlock.
updateTrust(userId, 0 /* flags */, true);
}
for (int i = 0; i < mActiveAgents.size(); i++) {
AgentInfo info = mActiveAgents.valueAt(i);
if (info.userId == userId) {
info.agent.onUnlockAttempt(successful);
}
}
}
private void dispatchUnlockLockout(int timeoutMs, int userId) {
for (int i = 0; i < mActiveAgents.size(); i++) {
AgentInfo info = mActiveAgents.valueAt(i);
if (info.userId == userId) {
info.agent.onUnlockLockout(timeoutMs);
}
}
}
// Listeners
private void addListener(ITrustListener listener) {
for (int i = 0; i < mTrustListeners.size(); i++) {
if (mTrustListeners.get(i).asBinder() == listener.asBinder()) {
return;
}
}
mTrustListeners.add(listener);
updateTrustAll();
}
private void removeListener(ITrustListener listener) {
for (int i = 0; i < mTrustListeners.size(); i++) {
if (mTrustListeners.get(i).asBinder() == listener.asBinder()) {
mTrustListeners.remove(i);
return;
}
}
}
private void dispatchOnTrustChanged(boolean enabled, int userId, int flags) {
if (DEBUG) {
Log.i(TAG, "onTrustChanged(" + enabled + ", " + userId + ", 0x"
+ Integer.toHexString(flags) + ")");
}
if (!enabled) flags = 0;
for (int i = 0; i < mTrustListeners.size(); i++) {
try {
mTrustListeners.get(i).onTrustChanged(enabled, userId, flags);
} catch (DeadObjectException e) {
Slog.d(TAG, "Removing dead TrustListener.");
mTrustListeners.remove(i);
i--;
} catch (RemoteException e) {
Slog.e(TAG, "Exception while notifying TrustListener.", e);
}
}
}
private void dispatchOnTrustManagedChanged(boolean managed, int userId) {
if (DEBUG) {
Log.i(TAG, "onTrustManagedChanged(" + managed + ", " + userId + ")");
}
for (int i = 0; i < mTrustListeners.size(); i++) {
try {
mTrustListeners.get(i).onTrustManagedChanged(managed, userId);
} catch (DeadObjectException e) {
Slog.d(TAG, "Removing dead TrustListener.");
mTrustListeners.remove(i);
i--;
} catch (RemoteException e) {
Slog.e(TAG, "Exception while notifying TrustListener.", e);
}
}
}
private void dispatchOnTrustError(CharSequence message) {
if (DEBUG) {
Log.i(TAG, "onTrustError(" + message + ")");
}
for (int i = 0; i < mTrustListeners.size(); i++) {
try {
mTrustListeners.get(i).onTrustError(message);
} catch (DeadObjectException e) {
Slog.d(TAG, "Removing dead TrustListener.");
mTrustListeners.remove(i);
i--;
} catch (RemoteException e) {
Slog.e(TAG, "Exception while notifying TrustListener.", e);
}
}
}
// User lifecycle
@Override
public void onStartUser(int userId) {
mHandler.obtainMessage(MSG_START_USER, userId, 0, null).sendToTarget();
}
@Override
public void onCleanupUser(int userId) {
mHandler.obtainMessage(MSG_CLEANUP_USER, userId, 0, null).sendToTarget();
}
@Override
public void onSwitchUser(int userId) {
mHandler.obtainMessage(MSG_SWITCH_USER, userId, 0, null).sendToTarget();
}
@Override
public void onUnlockUser(int userId) {
mHandler.obtainMessage(MSG_UNLOCK_USER, userId, 0, null).sendToTarget();
}
@Override
public void onStopUser(@UserIdInt int userId) {
mHandler.obtainMessage(MSG_STOP_USER, userId, 0, null).sendToTarget();
}
// Plumbing
private final IBinder mService = new ITrustManager.Stub() {
@Override
public void reportUnlockAttempt(boolean authenticated, int userId) throws RemoteException {
enforceReportPermission();
mHandler.obtainMessage(MSG_DISPATCH_UNLOCK_ATTEMPT, authenticated ? 1 : 0, userId)
.sendToTarget();
}
@Override
public void reportUnlockLockout(int timeoutMs, int userId) throws RemoteException {
enforceReportPermission();
mHandler.obtainMessage(MSG_DISPATCH_UNLOCK_LOCKOUT, timeoutMs, userId)
.sendToTarget();
}
@Override
public void reportEnabledTrustAgentsChanged(int userId) throws RemoteException {
enforceReportPermission();
// coalesce refresh messages.
mHandler.removeMessages(MSG_ENABLED_AGENTS_CHANGED);
mHandler.sendEmptyMessage(MSG_ENABLED_AGENTS_CHANGED);
}
@Override
public void reportKeyguardShowingChanged() throws RemoteException {
enforceReportPermission();
// coalesce refresh messages.
mHandler.removeMessages(MSG_KEYGUARD_SHOWING_CHANGED);
mHandler.sendEmptyMessage(MSG_KEYGUARD_SHOWING_CHANGED);
// Make sure handler processes the message before returning, such that isDeviceLocked
// after this call will retrieve the correct value.
mHandler.runWithScissors(() -> {}, 0);
}
@Override
public void registerTrustListener(ITrustListener trustListener) throws RemoteException {
enforceListenerPermission();
mHandler.obtainMessage(MSG_REGISTER_LISTENER, trustListener).sendToTarget();
}
@Override
public void unregisterTrustListener(ITrustListener trustListener) throws RemoteException {
enforceListenerPermission();
mHandler.obtainMessage(MSG_UNREGISTER_LISTENER, trustListener).sendToTarget();
}
@Override
public boolean isDeviceLocked(int userId) throws RemoteException {
userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
false /* allowAll */, true /* requireFull */, "isDeviceLocked", null);
long token = Binder.clearCallingIdentity();
try {
if (!mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
userId = resolveProfileParent(userId);
}
return isDeviceLockedInner(userId);
} finally {
Binder.restoreCallingIdentity(token);
}
}
@Override
public boolean isDeviceSecure(int userId) throws RemoteException {
userId = ActivityManager.handleIncomingUser(getCallingPid(), getCallingUid(), userId,
false /* allowAll */, true /* requireFull */, "isDeviceSecure", null);
long token = Binder.clearCallingIdentity();
try {
if (!mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) {
userId = resolveProfileParent(userId);
}
return mLockPatternUtils.isSecure(userId);
} finally {
Binder.restoreCallingIdentity(token);
}
}
private void enforceReportPermission() {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE, "reporting trust events");
}
private void enforceListenerPermission() {
mContext.enforceCallingPermission(Manifest.permission.TRUST_LISTENER,
"register trust listener");
}
@Override
protected void dump(FileDescriptor fd, final PrintWriter fout, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, fout)) return;
if (isSafeMode()) {
fout.println("disabled because the system is in safe mode.");
return;
}
if (!mTrustAgentsCanRun) {
fout.println("disabled because the third-party apps can't run yet.");
return;
}
final List<UserInfo> userInfos = mUserManager.getUsers(true /* excludeDying */);
mHandler.runWithScissors(new Runnable() {
@Override
public void run() {
fout.println("Trust manager state:");
for (UserInfo user : userInfos) {
dumpUser(fout, user, user.id == mCurrentUser);
}
}
}, 1500);
}
private void dumpUser(PrintWriter fout, UserInfo user, boolean isCurrent) {
fout.printf(" User \"%s\" (id=%d, flags=%#x)",
user.name, user.id, user.flags);
if (!user.supportsSwitchToByUser()) {
fout.println("(managed profile)");
fout.println(" disabled because switching to this user is not possible.");
return;
}
if (isCurrent) {
fout.print(" (current)");
}
fout.print(": trusted=" + dumpBool(aggregateIsTrusted(user.id)));
fout.print(", trustManaged=" + dumpBool(aggregateIsTrustManaged(user.id)));
fout.print(", deviceLocked=" + dumpBool(isDeviceLockedInner(user.id)));
fout.print(", strongAuthRequired=" + dumpHex(
mStrongAuthTracker.getStrongAuthForUser(user.id)));
fout.println();
fout.println(" Enabled agents:");
boolean duplicateSimpleNames = false;
ArraySet<String> simpleNames = new ArraySet<String>();
for (AgentInfo info : mActiveAgents) {
if (info.userId != user.id) { continue; }
boolean trusted = info.agent.isTrusted();
fout.print(" "); fout.println(info.component.flattenToShortString());
fout.print(" bound=" + dumpBool(info.agent.isBound()));
fout.print(", connected=" + dumpBool(info.agent.isConnected()));
fout.print(", managingTrust=" + dumpBool(info.agent.isManagingTrust()));
fout.print(", trusted=" + dumpBool(trusted));
fout.println();
if (trusted) {
fout.println(" message=\"" + info.agent.getMessage() + "\"");
}
if (!info.agent.isConnected()) {
String restartTime = TrustArchive.formatDuration(
info.agent.getScheduledRestartUptimeMillis()
- SystemClock.uptimeMillis());
fout.println(" restartScheduledAt=" + restartTime);
}
if (!simpleNames.add(TrustArchive.getSimpleName(info.component))) {
duplicateSimpleNames = true;
}
}
fout.println(" Events:");
mArchive.dump(fout, 50, user.id, " " /* linePrefix */, duplicateSimpleNames);
fout.println();
}
private String dumpBool(boolean b) {
return b ? "1" : "0";
}
private String dumpHex(int i) {
return "0x" + Integer.toHexString(i);
}
@Override
public void setDeviceLockedForUser(int userId, boolean locked) {
enforceReportPermission();
final long identity = Binder.clearCallingIdentity();
try {
if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
&& mLockPatternUtils.isSecure(userId)) {
synchronized (mDeviceLockedForUser) {
mDeviceLockedForUser.put(userId, locked);
}
if (locked) {
try {
ActivityManager.getService().notifyLockedProfile(userId);
} catch (RemoteException e) {
}
}
final Intent lockIntent = new Intent(Intent.ACTION_DEVICE_LOCKED_CHANGED);
lockIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
lockIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
mContext.sendBroadcastAsUser(lockIntent, UserHandle.SYSTEM,
Manifest.permission.TRUST_LISTENER, /* options */ null);
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
@Override
public boolean isTrustUsuallyManaged(int userId) {
mContext.enforceCallingPermission(Manifest.permission.TRUST_LISTENER,
"query trust state");
return isTrustUsuallyManagedInternal(userId);
}
@Override
public void unlockedByBiometricForUser(int userId, BiometricSourceType biometricSource) {
enforceReportPermission();
synchronized(mUsersUnlockedByBiometric) {
mUsersUnlockedByBiometric.put(userId, true);
}
// In extend unlock mode we need to refresh trust state here, which will call
// refreshDeviceLockedForUser()
int updateTrustOnUnlock = mSettingsObserver.getTrustAgentsExtendUnlock() ? 1 : 0;
mHandler.obtainMessage(MSG_REFRESH_DEVICE_LOCKED_FOR_USER, userId,
updateTrustOnUnlock).sendToTarget();
}
@Override
public void clearAllBiometricRecognized(BiometricSourceType biometricSource) {
enforceReportPermission();
synchronized(mUsersUnlockedByBiometric) {
mUsersUnlockedByBiometric.clear();
}
mHandler.obtainMessage(MSG_REFRESH_DEVICE_LOCKED_FOR_USER, UserHandle.USER_ALL,
0 /* arg2 */).sendToTarget();
}
};
private boolean isTrustUsuallyManagedInternal(int userId) {
synchronized (mTrustUsuallyManagedForUser) {
int i = mTrustUsuallyManagedForUser.indexOfKey(userId);
if (i >= 0) {
return mTrustUsuallyManagedForUser.valueAt(i);
}
}
// It's not in memory yet, get the value from persisted storage instead
boolean persistedValue = mLockPatternUtils.isTrustUsuallyManaged(userId);
synchronized (mTrustUsuallyManagedForUser) {
int i = mTrustUsuallyManagedForUser.indexOfKey(userId);
if (i >= 0) {
// Someone set the trust usually managed in the mean time. Better use that.
return mTrustUsuallyManagedForUser.valueAt(i);
} else {
// .. otherwise it's safe to cache the fetched value now.
mTrustUsuallyManagedForUser.put(userId, persistedValue);
return persistedValue;
}
}
}
private int resolveProfileParent(int userId) {
long identity = Binder.clearCallingIdentity();
try {
UserInfo parent = mUserManager.getProfileParent(userId);
if (parent != null) {
return parent.getUserHandle().getIdentifier();
}
return userId;
} finally {
Binder.restoreCallingIdentity(identity);
}
}
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REGISTER_LISTENER:
addListener((ITrustListener) msg.obj);
break;
case MSG_UNREGISTER_LISTENER:
removeListener((ITrustListener) msg.obj);
break;
case MSG_DISPATCH_UNLOCK_ATTEMPT:
dispatchUnlockAttempt(msg.arg1 != 0, msg.arg2);
break;
case MSG_DISPATCH_UNLOCK_LOCKOUT:
dispatchUnlockLockout(msg.arg1, msg.arg2);
break;
case MSG_ENABLED_AGENTS_CHANGED:
refreshAgentList(UserHandle.USER_ALL);
// This is also called when the security mode of a user changes.
refreshDeviceLockedForUser(UserHandle.USER_ALL);
break;
case MSG_KEYGUARD_SHOWING_CHANGED:
refreshDeviceLockedForUser(mCurrentUser);
break;
case MSG_START_USER:
case MSG_CLEANUP_USER:
case MSG_UNLOCK_USER:
refreshAgentList(msg.arg1);
break;
case MSG_SWITCH_USER:
mCurrentUser = msg.arg1;
mSettingsObserver.updateContentObserver();
refreshDeviceLockedForUser(UserHandle.USER_ALL);
break;
case MSG_STOP_USER:
setDeviceLockedForUser(msg.arg1, true);
break;
case MSG_FLUSH_TRUST_USUALLY_MANAGED:
SparseBooleanArray usuallyManaged;
synchronized (mTrustUsuallyManagedForUser) {
usuallyManaged = mTrustUsuallyManagedForUser.clone();
}
for (int i = 0; i < usuallyManaged.size(); i++) {
int userId = usuallyManaged.keyAt(i);
boolean value = usuallyManaged.valueAt(i);
if (value != mLockPatternUtils.isTrustUsuallyManaged(userId)) {
mLockPatternUtils.setTrustUsuallyManaged(value, userId);
}
}
break;
case MSG_REFRESH_DEVICE_LOCKED_FOR_USER:
if (msg.arg2 == 1) {
updateTrust(msg.arg1, 0 /* flags */, true);
}
refreshDeviceLockedForUser(msg.arg1);
break;
case MSG_SCHEDULE_TRUST_TIMEOUT:
handleScheduleTrustTimeout(msg.arg1, msg.arg2);
break;
}
}
};
private final PackageMonitor mPackageMonitor = new PackageMonitor() {
@Override
public void onSomePackagesChanged() {
refreshAgentList(UserHandle.USER_ALL);
}
@Override
public boolean onPackageChanged(String packageName, int uid, String[] components) {
// We're interested in all changes, even if just some components get enabled / disabled.
return true;
}
@Override
public void onPackageDisappeared(String packageName, int reason) {
removeAgentsOfPackage(packageName);
}
};
private static class SettingsAttrs {
public ComponentName componentName;
public boolean canUnlockProfile;
public SettingsAttrs(
ComponentName componentName,
boolean canUnlockProfile) {
this.componentName = componentName;
this.canUnlockProfile = canUnlockProfile;
}
};
private class Receiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action)) {
refreshAgentList(getSendingUserId());
updateDevicePolicyFeatures();
} else if (Intent.ACTION_USER_ADDED.equals(action)) {
int userId = getUserId(intent);
if (userId > 0) {
maybeEnableFactoryTrustAgents(mLockPatternUtils, userId);
}
} else if (Intent.ACTION_USER_REMOVED.equals(action)) {
int userId = getUserId(intent);
if (userId > 0) {
synchronized (mUserIsTrusted) {
mUserIsTrusted.delete(userId);
}
synchronized (mDeviceLockedForUser) {
mDeviceLockedForUser.delete(userId);
}
synchronized (mTrustUsuallyManagedForUser) {
mTrustUsuallyManagedForUser.delete(userId);
}
synchronized (mUsersUnlockedByBiometric) {
mUsersUnlockedByBiometric.delete(userId);
}
refreshAgentList(userId);
refreshDeviceLockedForUser(userId);
}
}
}
private int getUserId(Intent intent) {
int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -100);
if (userId > 0) {
return userId;
} else {
Slog.wtf(TAG, "EXTRA_USER_HANDLE missing or invalid, value=" + userId);
return -100;
}
}
public void register(Context context) {
IntentFilter filter = new IntentFilter();
filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
filter.addAction(Intent.ACTION_USER_ADDED);
filter.addAction(Intent.ACTION_USER_REMOVED);
context.registerReceiverAsUser(this,
UserHandle.ALL,
filter,
null /* permission */,
null /* scheduler */);
}
}
private class StrongAuthTracker extends LockPatternUtils.StrongAuthTracker {
SparseBooleanArray mStartFromSuccessfulUnlock = new SparseBooleanArray();
public StrongAuthTracker(Context context) {
super(context);
}
@Override
public void onStrongAuthRequiredChanged(int userId) {
mStartFromSuccessfulUnlock.delete(userId);
if (DEBUG) {
Log.i(TAG, "onStrongAuthRequiredChanged(" + userId + ") ->"
+ " trustAllowed=" + isTrustAllowedForUser(userId)
+ " agentsCanRun=" + canAgentsRunForUser(userId));
}
// Cancel pending alarms if we require some auth anyway.
if (!isTrustAllowedForUser(userId)) {
TrustTimeoutAlarmListener alarm = mTrustTimeoutAlarmListenerForUser.get(userId);
if (alarm != null && alarm.isQueued()) {
alarm.setQueued(false /* isQueued */);
mAlarmManager.cancel(alarm);
}
}
refreshAgentList(userId);
// The list of active trust agents may not have changed, if there was a previous call
// to allowTrustFromUnlock, so we update the trust here too.
updateTrust(userId, 0 /* flags */);
}
boolean canAgentsRunForUser(int userId) {
return mStartFromSuccessfulUnlock.get(userId)
|| super.isTrustAllowedForUser(userId);
}
/**
* Temporarily suppress strong auth requirements for {@param userId} until strong auth
* changes again. Must only be called when we know about a successful unlock already
* before the underlying StrongAuthTracker.
*
* Note that this only changes whether trust agents can be started, not the actual trusted
* value.
*/
void allowTrustFromUnlock(int userId) {
if (userId < UserHandle.USER_SYSTEM) {
throw new IllegalArgumentException("userId must be a valid user: " + userId);
}
boolean previous = canAgentsRunForUser(userId);
mStartFromSuccessfulUnlock.put(userId, true);
if (DEBUG) {
Log.i(TAG, "allowTrustFromUnlock(" + userId + ") ->"
+ " trustAllowed=" + isTrustAllowedForUser(userId)
+ " agentsCanRun=" + canAgentsRunForUser(userId));
}
if (canAgentsRunForUser(userId) != previous) {
refreshAgentList(userId);
}
}
}
private class TrustTimeoutAlarmListener implements OnAlarmListener {
private final int mUserId;
private boolean mIsQueued = false;
TrustTimeoutAlarmListener(int userId) {
mUserId = userId;
}
@Override
public void onAlarm() {
mIsQueued = false;
int strongAuthState = mStrongAuthTracker.getStrongAuthForUser(mUserId);
// Only fire if trust can unlock.
if (mStrongAuthTracker.isTrustAllowedForUser(mUserId)) {
if (DEBUG) Slog.d(TAG, "Revoking all trust because of trust timeout");
mLockPatternUtils.requireStrongAuth(
mStrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST, mUserId);
}
maybeLockScreen(mUserId);
}
public void setQueued(boolean isQueued) {
mIsQueued = isQueued;
}
public boolean isQueued() {
return mIsQueued;
}
}
}