diff options
| -rw-r--r-- | Android.mk | 2 | ||||
| -rw-r--r-- | core/java/android/app/ContextImpl.java | 7 | ||||
| -rw-r--r-- | core/java/android/app/trust/ITrustListener.aidl | 26 | ||||
| -rw-r--r-- | core/java/android/app/trust/ITrustManager.aidl | 31 | ||||
| -rw-r--r-- | core/java/android/app/trust/TrustManager.java | 134 | ||||
| -rw-r--r-- | core/java/android/content/Context.java | 8 | ||||
| -rw-r--r-- | core/java/com/android/internal/widget/LockPatternUtils.java | 65 | ||||
| -rw-r--r-- | core/res/AndroidManifest.xml | 7 | ||||
| -rw-r--r-- | core/res/res/values/strings.xml | 5 | ||||
| -rw-r--r-- | services/core/java/com/android/server/trust/TrustAgentWrapper.java | 166 | ||||
| -rw-r--r-- | services/core/java/com/android/server/trust/TrustManagerService.java | 387 | ||||
| -rw-r--r-- | services/java/com/android/server/SystemServer.java | 8 |
12 files changed, 841 insertions, 5 deletions
@@ -85,6 +85,8 @@ LOCAL_SRC_FILES += \ core/java/android/app/IWallpaperManager.aidl \ core/java/android/app/IWallpaperManagerCallback.aidl \ core/java/android/app/admin/IDevicePolicyManager.aidl \ + core/java/android/app/trust/ITrustManager.aidl \ + core/java/android/app/trust/ITrustListener.aidl \ core/java/android/app/backup/IBackupManager.aidl \ core/java/android/app/backup/IFullBackupRestoreObserver.aidl \ core/java/android/app/backup/IRestoreObserver.aidl \ diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index b2b2571..d1f94f0 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -119,6 +119,7 @@ import android.view.textservice.TextServicesManager; import android.accounts.AccountManager; import android.accounts.IAccountManager; import android.app.admin.DevicePolicyManager; +import android.app.trust.TrustManager; import com.android.internal.annotations.GuardedBy; import com.android.internal.app.IAppOpsService; @@ -621,6 +622,12 @@ class ContextImpl extends Context { return new MediaSessionManager(ctx); } }); + registerService(TRUST_SERVICE, new ServiceFetcher() { + public Object createService(ContextImpl ctx) { + IBinder b = ServiceManager.getService(TRUST_SERVICE); + return new TrustManager(b); + } + }); } static ContextImpl getImpl(Context context) { diff --git a/core/java/android/app/trust/ITrustListener.aidl b/core/java/android/app/trust/ITrustListener.aidl new file mode 100644 index 0000000..4680043 --- /dev/null +++ b/core/java/android/app/trust/ITrustListener.aidl @@ -0,0 +1,26 @@ +/* +** +** Copyright 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 android.app.trust; + +/** + * Private API to be notified about trust changes. + * + * {@hide} + */ +oneway interface ITrustListener { + void onTrustChanged(boolean enabled, int userId); +}
\ No newline at end of file diff --git a/core/java/android/app/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl new file mode 100644 index 0000000..ad4ccbb --- /dev/null +++ b/core/java/android/app/trust/ITrustManager.aidl @@ -0,0 +1,31 @@ +/* +** +** Copyright 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 android.app.trust; + +import android.app.trust.ITrustListener; + +/** + * System private API to comunicate with trust service. + * + * {@hide} + */ +interface ITrustManager { + void reportUnlockAttempt(boolean successful, int userId); + void reportEnabledTrustAgentsChanged(int userId); + void registerTrustListener(in ITrustListener trustListener); + void unregisterTrustListener(in ITrustListener trustListener); +} diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java new file mode 100644 index 0000000..e31c624 --- /dev/null +++ b/core/java/android/app/trust/TrustManager.java @@ -0,0 +1,134 @@ +/* + * 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 android.app.trust; + +import android.os.Handler; +import android.os.IBinder; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.util.ArrayMap; +import android.util.Log; + +/** + * See {@link com.android.server.trust.TrustManagerService} + * @hide + */ +public class TrustManager { + + private static final int MSG_TRUST_CHANGED = 1; + + private static final String TAG = "TrustManager"; + + private final ITrustManager mService; + private final ArrayMap<TrustListener, ITrustListener> mTrustListeners; + + public TrustManager(IBinder b) { + mService = ITrustManager.Stub.asInterface(b); + mTrustListeners = new ArrayMap<TrustListener, ITrustListener>(); + } + + /** + * Reports that user {@param userId} has tried to unlock the device. + * + * @param successful if true, the unlock attempt was successful. + * + * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission. + */ + public void reportUnlockAttempt(boolean successful, int userId) { + try { + mService.reportUnlockAttempt(successful, userId); + } catch (RemoteException e) { + onError(e); + } + } + + /** + * Reports that the list of enabled trust agents changed for user {@param userId}. + * + * Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission. + */ + public void reportEnabledTrustAgentsChanged(int userId) { + try { + mService.reportEnabledTrustAgentsChanged(userId); + } catch (RemoteException e) { + onError(e); + } + } + + /** + * Registers a listener for trust events. + * + * Requires the {@link android.Manifest.permission#TRUST_LISTENER} permission. + */ + public void registerTrustListener(final TrustListener trustListener) { + try { + ITrustListener.Stub iTrustListener = new ITrustListener.Stub() { + @Override + public void onTrustChanged(boolean enabled, int userId) throws RemoteException { + mHandler.obtainMessage(MSG_TRUST_CHANGED, (enabled ? 1 : 0), userId, + trustListener).sendToTarget(); + } + }; + mService.registerTrustListener(iTrustListener); + mTrustListeners.put(trustListener, iTrustListener); + } catch (RemoteException e) { + onError(e); + } + } + + /** + * Unregisters a listener for trust events. + * + * Requires the {@link android.Manifest.permission#TRUST_LISTENER} permission. + */ + public void unregisterTrustListener(final TrustListener trustListener) { + ITrustListener iTrustListener = mTrustListeners.remove(trustListener); + if (iTrustListener != null) { + try { + mService.unregisterTrustListener(iTrustListener); + } catch (RemoteException e) { + onError(e); + } + } + } + + private void onError(Exception e) { + Log.e(TAG, "Error while calling TrustManagerService", e); + } + + private final Handler mHandler = new Handler(Looper.getMainLooper()) { + @Override + public void handleMessage(Message msg) { + switch(msg.what) { + case MSG_TRUST_CHANGED: + ((TrustListener)msg.obj).onTrustChanged(msg.arg1 != 0, msg.arg2); + break; + } + } + }; + + public interface TrustListener { + + /** + * Reports that the trust state has changed. + * @param enabled if true, the system believes the environment to be trusted. + * @param userId the user, for which the trust changed. + */ + void onTrustChanged(boolean enabled, int userId); + } +} diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index cd91f3b..3fdaef2 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2595,6 +2595,14 @@ public abstract class Context { public static final String CONSUMER_IR_SERVICE = "consumer_ir"; /** + * {@link android.app.trust.TrustManager} for managing trust agents. + * @see #getSystemService + * @see android.app.trust.TrustManager + * @hide + */ + public static final String TRUST_SERVICE = "trust"; + + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. * diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 2d79491..4752da5 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -19,11 +19,14 @@ package com.android.internal.widget; import android.Manifest; import android.app.ActivityManagerNative; import android.app.admin.DevicePolicyManager; +import android.app.trust.TrustManager; import android.appwidget.AppWidgetManager; +import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; +import android.net.Uri; import android.os.IBinder; import android.os.RemoteException; import android.os.ServiceManager; @@ -46,6 +49,8 @@ import com.google.android.collect.Lists; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Collection; import java.util.List; /** @@ -145,6 +150,8 @@ public class LockPatternUtils { private static final String LOCK_SCREEN_OWNER_INFO_ENABLED = Settings.Secure.LOCK_SCREEN_OWNER_INFO_ENABLED; + private static final String ENABLED_TRUST_AGENTS = "lockscreen.enabledtrustagents"; + private final Context mContext; private final ContentResolver mContentResolver; private DevicePolicyManager mDevicePolicyManager; @@ -167,6 +174,15 @@ public class LockPatternUtils { return mDevicePolicyManager; } + private TrustManager getTrustManager() { + TrustManager trust = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE); + if (trust == null) { + Log.e(TAG, "Can't get TrustManagerService: is it running?", + new IllegalStateException("Stack trace:")); + } + return trust; + } + /** * @param contentResolver Used to look up and save settings. */ @@ -242,10 +258,14 @@ public class LockPatternUtils { */ public void reportFailedPasswordAttempt() { getDevicePolicyManager().reportFailedPasswordAttempt(getCurrentOrCallingUserId()); + getTrustManager().reportUnlockAttempt(false /* authenticated */, + getCurrentOrCallingUserId()); } public void reportSuccessfulPasswordAttempt() { getDevicePolicyManager().reportSuccessfulPasswordAttempt(getCurrentOrCallingUserId()); + getTrustManager().reportUnlockAttempt(true /* authenticated */, + getCurrentOrCallingUserId()); } public void setCurrentUser(int userId) { @@ -496,11 +516,12 @@ public class LockPatternUtils { */ public void saveLockPattern(List<LockPatternView.Cell> pattern, boolean isFallback) { try { - getLockSettings().setLockPattern(patternToString(pattern), getCurrentOrCallingUserId()); + int userId = getCurrentOrCallingUserId(); + getLockSettings().setLockPattern(patternToString(pattern), userId); DevicePolicyManager dpm = getDevicePolicyManager(); if (pattern != null) { - int userHandle = getCurrentOrCallingUserId(); + int userHandle = userId; if (userHandle == UserHandle.USER_OWNER) { String stringPattern = patternToString(pattern); updateEncryptionPassword(StorageManager.CRYPT_TYPE_PATTERN, stringPattern); @@ -511,18 +532,18 @@ public class LockPatternUtils { deleteGallery(); setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, - pattern.size(), 0, 0, 0, 0, 0, 0, getCurrentOrCallingUserId()); + pattern.size(), 0, 0, 0, 0, 0, 0, userId); } else { setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK); setLong(PASSWORD_TYPE_ALTERNATE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); finishBiometricWeak(); dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK, - 0, 0, 0, 0, 0, 0, 0, getCurrentOrCallingUserId()); + 0, 0, 0, 0, 0, 0, 0, userId); } } else { dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, - 0, 0, 0, 0, 0, getCurrentOrCallingUserId()); + 0, 0, 0, 0, 0, userId); } } catch (RemoteException re) { Log.e(TAG, "Couldn't save lock pattern " + re); @@ -1374,4 +1395,38 @@ public class LockPatternUtils { setBoolean(LOCKSCREEN_WIDGETS_ENABLED, enabled, userId); } + public void setEnabledTrustAgents(Collection<ComponentName> activeTrustAgents) { + setEnabledTrustAgents(activeTrustAgents, getCurrentOrCallingUserId()); + } + + public List<ComponentName> getEnabledTrustAgents() { + return getEnabledTrustAgents(getCurrentOrCallingUserId()); + } + + public void setEnabledTrustAgents(Collection<ComponentName> activeTrustAgents, int userId) { + StringBuilder sb = new StringBuilder(); + for (ComponentName cn : activeTrustAgents) { + if (sb.length() > 0) { + sb.append(','); + } + sb.append(cn.flattenToShortString()); + } + setString(ENABLED_TRUST_AGENTS, sb.toString(), userId); + getTrustManager().reportEnabledTrustAgentsChanged(getCurrentOrCallingUserId()); + } + + public List<ComponentName> getEnabledTrustAgents(int userId) { + String serialized = getString(ENABLED_TRUST_AGENTS, userId); + if (TextUtils.isEmpty(serialized)) { + return null; + } + String[] split = serialized.split(","); + ArrayList<ComponentName> activeTrustAgents = new ArrayList<ComponentName>(split.length); + for (String s : split) { + if (!TextUtils.isEmpty(s)) { + activeTrustAgents.add(ComponentName.unflattenFromString(s)); + } + } + return activeTrustAgents; + } } diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 2e0ac08..2a4d872 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -2547,6 +2547,13 @@ android:label="@string/permlab_control_keyguard" android:description="@string/permdesc_control_keyguard" /> + <!-- Allows an application to listen to trust changes. Only allowed for system processes. + @hide --> + <permission android:name="android.permission.TRUST_LISTENER" + android:protectionLevel="signature" + android:label="@string/permlab_trust_listener" + android:description="@string/permdesc_trust_listener" /> + <!-- Must be required by an {@link android.service.trust.TrustAgentService}, to ensure that only the system can bind to it. --> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index eb16bbd..3a4f059 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -3651,6 +3651,11 @@ <!-- Description of an application permission that lets it control keyguard. --> <string name="permdesc_control_keyguard">Allows an application to control keguard.</string> + <!-- Title of an application permission that lets it listen to trust state changes. --> + <string name="permlab_trust_listener">Listen to trust state changes.</string> + <!-- Description of an application permission that lets it listen to trust state changes. --> + <string name="permdesc_trust_listener">Allows an application to listen for changes in trust state.</string> + <!-- Title of an application permission that lets it bind to a trust agent service. --> <string name="permlab_bind_trust_agent_service">Bind to a trust agent service</string> <!-- Description of an application permission that lets it bind to a trust agent service. --> diff --git a/services/core/java/com/android/server/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java new file mode 100644 index 0000000..a83fa87 --- /dev/null +++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java @@ -0,0 +1,166 @@ +/* + * 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.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.RemoteException; +import android.os.UserHandle; +import android.util.Log; +import android.util.Slog; +import android.service.trust.ITrustAgentService; +import android.service.trust.ITrustAgentServiceCallback; + +/** + * A wrapper around a TrustAgentService interface. Coordinates communication between + * TrustManager and the actual TrustAgent. + */ +public class TrustAgentWrapper { + private static final boolean DEBUG = false; + private static final String TAG = "TrustAgentWrapper"; + + private static final int MSG_ENABLE_TRUST = 1; + private static final int MSG_REVOKE_TRUST = 2; + private static final int MSG_TRUST_TIMEOUT = 3; + + private final TrustManagerService mTrustManagerService; + private final int mUserId; + private final Context mContext; + private final ComponentName mName; + + private ITrustAgentService mTrustAgentService; + + // Trust state + private boolean mTrusted; + private String mMessage; + + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_ENABLE_TRUST: + mTrusted = true; + mMessage = (String) msg.obj; + boolean initiatedByUser = msg.arg1 != 0; + // TODO: Handle handle user initiated trust changes. + mTrustManagerService.updateTrust(mUserId); + break; + case MSG_TRUST_TIMEOUT: + if (DEBUG) Slog.v(TAG, "Trust timed out : " + mName.flattenToShortString()); + // Fall through. + case MSG_REVOKE_TRUST: + mTrusted = false; + mMessage = null; + mTrustManagerService.updateTrust(mUserId); + break; + } + } + }; + + private ITrustAgentServiceCallback mCallback = new ITrustAgentServiceCallback.Stub() { + + public void enableTrust(String userMessage, long durationMs, boolean initiatedByUser) { + if (DEBUG) Slog.v(TAG, "enableTrust(" + userMessage + ", durationMs = " + durationMs + + ", initiatedByUser = " + initiatedByUser + ")"); + + mHandler.obtainMessage(MSG_ENABLE_TRUST, initiatedByUser ? 1 : 0, 0, userMessage) + .sendToTarget(); + if (durationMs > 0) { + mHandler.removeMessages(MSG_TRUST_TIMEOUT); + mHandler.sendEmptyMessageDelayed(MSG_TRUST_TIMEOUT, durationMs); + } + } + + public void revokeTrust() { + if (DEBUG) Slog.v(TAG, "revokeTrust()"); + mHandler.sendEmptyMessage(MSG_REVOKE_TRUST); + } + }; + + private final ServiceConnection mConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + if (DEBUG) Log.v(TAG, "TrustAgent started : " + name.flattenToString()); + mTrustAgentService = ITrustAgentService.Stub.asInterface(service); + setCallback(mCallback); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + if (DEBUG) Log.v(TAG, "TrustAgent disconnected : " + name.flattenToShortString()); + mTrustAgentService = null; + mHandler.sendEmptyMessage(MSG_REVOKE_TRUST); + } + }; + + + public TrustAgentWrapper(Context context, TrustManagerService trustManagerService, + Intent intent, UserHandle user) { + mContext = context; + mTrustManagerService = trustManagerService; + mUserId = user.getIdentifier(); + mName = intent.getComponent(); + if (!context.bindServiceAsUser(intent, mConnection, Context.BIND_AUTO_CREATE, user)) { + if (DEBUG) Log.v(TAG, "can't bind to TrustAgent " + mName.flattenToShortString()); + // TODO: retry somehow? + } + } + + private void onError(Exception e) { + Slog.w(TAG , "Remote Exception", e); + } + + /** + * @see android.service.trust.TrustAgentService#onUnlockAttempt(boolean) + */ + public void onUnlockAttempt(boolean successful) { + try { + if (mTrustAgentService != null) mTrustAgentService.onUnlockAttempt(successful); + } catch (RemoteException e) { + onError(e); + } + } + + private void setCallback(ITrustAgentServiceCallback callback) { + try { + if (mTrustAgentService != null) { + mTrustAgentService.setCallback(callback); + } + } catch (RemoteException e) { + onError(e); + } + } + + public boolean isTrusted() { + return mTrusted; + } + + public String getMessage() { + return mMessage; + } + + public void unbind() { + if (DEBUG) Log.v(TAG, "TrustAgent unbound : " + mName.flattenToShortString()); + mContext.unbindService(mConnection); + } +} diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java new file mode 100644 index 0000000..a2a49c9 --- /dev/null +++ b/services/core/java/com/android/server/trust/TrustManagerService.java @@ -0,0 +1,387 @@ +/* + * 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.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.trust.ITrustListener; +import android.app.trust.ITrustManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +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.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.RemoteException; +import android.os.UserHandle; +import android.os.UserManager; +import android.service.trust.TrustAgentService; +import android.util.ArraySet; +import android.util.AttributeSet; +import android.util.Slog; +import android.util.Xml; + +import java.io.IOException; +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 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 final ArraySet<AgentInfo> mActiveAgents = new ArraySet<AgentInfo>(); + private final ArrayList<ITrustListener> mTrustListeners = new ArrayList<ITrustListener>(); + private final Context mContext; + + private UserManager mUserManager; + + /** + * Cache for {@link #refreshAgentList()} + */ + private final ArraySet<AgentInfo> mObsoleteAgents = new ArraySet<AgentInfo>(); + + + public TrustManagerService(Context context) { + super(context); + mContext = context; + mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); + } + + @Override + public void onStart() { + publishBinderService(Context.TRUST_SERVICE, mService); + } + + @Override + public void onBootPhase(int phase) { + if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY && !isSafeMode()) { + // Listen for package changes + mPackageMonitor.register(mContext, mHandler.getLooper(), UserHandle.ALL, true); + refreshAgentList(); + } + } + + // 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); + } + } + + public void updateTrust(int userId) { + dispatchOnTrustChanged(aggregateIsTrusted(userId), userId); + } + + protected void refreshAgentList() { + if (DEBUG) Slog.d(TAG, "refreshAgentList()"); + PackageManager pm = mContext.getPackageManager(); + + List<UserInfo> userInfos = mUserManager.getUsers(true /* excludeDying */); + LockPatternUtils lockPatternUtils = new LockPatternUtils(mContext); + + mObsoleteAgents.clear(); + mObsoleteAgents.addAll(mActiveAgents); + + for (UserInfo userInfo : userInfos) { + List<ComponentName> enabledAgents = lockPatternUtils.getEnabledTrustAgents(userInfo.id); + if (enabledAgents == null) { + continue; + } + List<ResolveInfo> resolveInfos = pm.queryIntentServicesAsUser(TRUST_AGENT_INTENT, + PackageManager.GET_META_DATA, userInfo.id); + for (ResolveInfo resolveInfo : resolveInfos) { + if (resolveInfo.serviceInfo == null) continue; + ComponentName name = getComponentName(resolveInfo); + if (!enabledAgents.contains(name)) 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 { + mObsoleteAgents.remove(agentInfo); + } + } + } + + boolean trustMayHaveChanged = false; + for (int i = 0; i < mObsoleteAgents.size(); i++) { + AgentInfo info = mActiveAgents.valueAt(i); + if (info.agent.isTrusted()) { + trustMayHaveChanged = true; + } + info.agent.unbind(); + mActiveAgents.remove(info); + } + + if (trustMayHaveChanged) { + updateTrustAll(); + } + } + + 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); + } + + // Agent dispatch and aggregation + + private boolean aggregateIsTrusted(int userId) { + 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 void dispatchUnlockAttempt(boolean successful, int 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); + } + + private void removeListener(ITrustListener listener) { + for (int i = 0; i < mTrustListeners.size(); i++) { + if (mTrustListeners.get(i).asBinder() == listener.asBinder()) { + mTrustListeners.get(i); + return; + } + } + } + + private void dispatchOnTrustChanged(boolean enabled, int userId) { + for (int i = 0; i < mTrustListeners.size(); i++) { + try { + mTrustListeners.get(i).onTrustChanged(enabled, userId); + } catch (RemoteException e) { + Slog.e(TAG, "Exception while notifying TrustListener. Removing listener.", e); + mTrustListeners.get(i); + i--; + } + } + } + + // 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 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(); + } + + private void enforceReportPermission() { + mContext.enforceCallingPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE, + "reporting trust events"); + } + + private void enforceListenerPermission() { + mContext.enforceCallingPermission(Manifest.permission.TRUST_LISTENER, + "register trust listener"); + } + }; + + 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(); + break; + } + } + }; + + private final PackageMonitor mPackageMonitor = new PackageMonitor() { + @Override + public void onSomePackagesChanged() { + refreshAgentList(); + } + + @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; + } + }; +} diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index d98139f..ab4c89e 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -76,6 +76,7 @@ import com.android.server.power.ShutdownThread; import com.android.server.search.SearchManagerService; import com.android.server.statusbar.StatusBarManagerService; import com.android.server.storage.DeviceStorageMonitorService; +import com.android.server.trust.TrustManagerService; import com.android.server.twilight.TwilightService; import com.android.server.usb.UsbService; import com.android.server.wallpaper.WallpaperManagerService; @@ -913,6 +914,13 @@ public final class SystemServer { } catch (Throwable e) { reportWtf("starting MediaRouterService", e); } + + try { + Slog.i(TAG, "Trust Manager"); + mSystemServiceManager.startService(TrustManagerService.class); + } catch (Throwable e) { + Slog.e(TAG, "Failure starting TrustManagerService", e); + } } } |
