| /* |
| * 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 com.android.internal.annotations.GuardedBy; |
| import com.android.internal.content.PackageMonitor; |
| import com.android.internal.widget.LockPatternUtils; |
| import com.android.server.SystemService; |
| |
| import org.xmlpull.v1.XmlPullParser; |
| import org.xmlpull.v1.XmlPullParserException; |
| |
| import android.Manifest; |
| import android.app.ActivityManager; |
| import android.app.ActivityManagerNative; |
| 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.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.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.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 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 boolean DEBUG = false; |
| private static final String TAG = "TrustManagerService"; |
| |
| 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_SET_DEVICE_LOCKED = 10; |
| private static final int MSG_FLUSH_TRUST_USUALLY_MANAGED = 11; |
| |
| 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("mDeviceLockedForUser") |
| private final SparseBooleanArray mTrustUsuallyManagedForUser = 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); |
| } 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 |
| ComponentName 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); |
| } |
| |
| 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()) continue; |
| if (!mActivityManager.isUserRunning(userInfo.id)) continue; |
| if (!lockPatternUtils.isSecure(userInfo.id)) continue; |
| if (!mStrongAuthTracker.canAgentsRunForUser(userInfo.id)) 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) { |
| continue; |
| } |
| List<ResolveInfo> resolveInfos = resolveAllowedTrustAgents(pm, userInfo.id); |
| for (ResolveInfo resolveInfo : resolveInfos) { |
| ComponentName name = getComponentName(resolveInfo); |
| |
| if (!enabledAgents.contains(name)) 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()) 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 = getSettingsComponentName(pm, resolveInfo); |
| agentInfo.agent = new TrustAgentWrapper(mContext, this, |
| new Intent().setComponent(name), userInfo.getUserHandle()); |
| 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); |
| } |
| } |
| } |
| |
| public void setDeviceLockedForUser(int userId, boolean locked) { |
| if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)) { |
| synchronized (mDeviceLockedForUser) { |
| mDeviceLockedForUser.put(userId, locked); |
| } |
| if (locked) { |
| try { |
| ActivityManagerNative.getDefault().notifyLockedProfile(userId); |
| } catch (RemoteException e) { |
| } |
| } |
| } |
| } |
| |
| 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; |
| if (mCurrentUser == id) { |
| try { |
| showingKeyguard = wm.isKeyguardLocked(); |
| } catch (RemoteException e) { |
| } |
| } |
| boolean deviceLocked = secure && showingKeyguard && !trusted; |
| |
| boolean changed; |
| synchronized (mDeviceLockedForUser) { |
| changed = isDeviceLockedInner(id) != deviceLocked; |
| mDeviceLockedForUser.put(id, deviceLocked); |
| } |
| if (changed) { |
| dispatchDeviceLocked(id, deviceLocked); |
| } |
| } |
| } |
| |
| 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() { |
| for (int i = 0; i < mActiveAgents.size(); i++) { |
| AgentInfo info = mActiveAgents.valueAt(i); |
| if (info.agent.isConnected()) { |
| info.agent.updateDevicePolicyFeatures(); |
| } |
| } |
| } |
| |
| 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 ComponentName getSettingsComponentName(PackageManager pm, ResolveInfo resolveInfo) { |
| if (resolveInfo == null || resolveInfo.serviceInfo == null |
| || resolveInfo.serviceInfo.metaData == null) return null; |
| String cn = null; |
| 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); |
| 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 ComponentName.unflattenFromString(cn); |
| } |
| |
| 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); |
| ArraySet<ComponentName> discoveredAgents = new ArraySet<>(); |
| 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); |
| } |
| |
| private List<ResolveInfo> resolveAllowedTrustAgents(PackageManager pm, int userId) { |
| List<ResolveInfo> resolveInfos = pm.queryIntentServicesAsUser(TRUST_AGENT_INTENT, |
| 0 /* flags */, 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); |
| } |
| } |
| } |
| |
| // 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); |
| } |
| } |
| } |
| |
| // 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(); |
| } |
| |
| // 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 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); |
| } |
| |
| @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) { |
| mContext.enforceCallingPermission(Manifest.permission.DUMP, |
| "dumping TrustManagerService"); |
| 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 value) { |
| enforceReportPermission(); |
| mHandler.obtainMessage(MSG_SET_DEVICE_LOCKED, value ? 1 : 0, userId) |
| .sendToTarget(); |
| } |
| |
| @Override |
| public boolean isTrustUsuallyManaged(int userId) { |
| mContext.enforceCallingPermission(Manifest.permission.TRUST_LISTENER, |
| "query trust state"); |
| return isTrustUsuallyManagedInternal(userId); |
| } |
| }; |
| |
| 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_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: |
| refreshAgentList(msg.arg1); |
| break; |
| case MSG_SWITCH_USER: |
| mCurrentUser = msg.arg1; |
| refreshDeviceLockedForUser(UserHandle.USER_ALL); |
| break; |
| case MSG_SET_DEVICE_LOCKED: |
| setDeviceLockedForUser(msg.arg2, msg.arg1 != 0); |
| 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); |
| } |
| } |
| } |
| } |
| }; |
| |
| 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 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); |
| } |
| 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); |
| } |
| } |
| } |
| } |