| /* |
| * 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.admin.DevicePolicyManager; |
| import android.hardware.biometrics.BiometricSourceType; |
| import android.app.trust.ITrustListener; |
| import android.app.trust.ITrustManager; |
| import android.content.BroadcastReceiver; |
| import android.content.ComponentName; |
| 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.graphics.drawable.Drawable; |
| 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.os.storage.StorageManager; |
| import android.provider.Settings; |
| import android.service.trust.TrustAgentService; |
| import android.text.TextUtils; |
| 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.policy.IKeyguardDismissCallback; |
| import com.android.internal.util.DumpUtils; |
| import com.android.internal.widget.LockPatternUtils; |
| import com.android.server.SystemService; |
| import java.io.FileDescriptor; |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import java.util.ArrayList; |
| import java.util.List; |
| import org.xmlpull.v1.XmlPullParser; |
| import org.xmlpull.v1.XmlPullParserException; |
| |
| /** |
| * 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 TRUST_USUALLY_MANAGED_FLUSH_DELAY = 2 * 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 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); |
| } |
| |
| @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); |
| } |
| } |
| |
| // 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) { |
| boolean managed = aggregateIsTrustManaged(userId); |
| dispatchOnTrustManagedChanged(managed, userId); |
| if (mStrongAuthTracker.isTrustAllowedForUser(userId) |
| && isTrustUsuallyManagedInternal(userId) != managed) { |
| updateTrustUsuallyManaged(userId, managed); |
| } |
| boolean trusted = aggregateIsTrusted(userId); |
| boolean changed; |
| synchronized (mUserIsTrusted) { |
| changed = mUserIsTrusted.get(userId) != trusted; |
| mUserIsTrusted.put(userId, trusted); |
| } |
| dispatchOnTrustChanged(trusted, userId, flags); |
| if (changed) { |
| refreshDeviceLockedForUser(userId); |
| } |
| } |
| |
| 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); |
| } |
| |
| 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 = 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 |
| || !info.supportsSwitchToByUser()) { |
| continue; |
| } |
| |
| int id = info.id; |
| boolean secure = mLockPatternUtils.isSecure(id); |
| 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(); |
| } |
| } |
| } |
| } |
| |
| 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); |
| } |
| |
| 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)) { |
| 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); |
| } |
| mHandler.obtainMessage(MSG_REFRESH_DEVICE_LOCKED_FOR_USER, userId, |
| 0 /* arg2 */).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; |
| 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: |
| refreshDeviceLockedForUser(msg.arg1); |
| 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)); |
| } |
| |
| 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); |
| } |
| } |
| } |
| } |