/* * 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.app.AlarmManager; import android.app.PendingIntent; import android.app.admin.DevicePolicyManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.net.Uri; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Message; import android.os.PatternMatcher; import android.os.PersistableBundle; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.util.Log; import android.util.Slog; import android.service.trust.ITrustAgentService; import android.service.trust.ITrustAgentServiceCallback; import java.util.Collections; import java.util.List; /** * A wrapper around a TrustAgentService interface. Coordinates communication between * TrustManager and the actual TrustAgent. */ public class TrustAgentWrapper { private static final String EXTRA_COMPONENT_NAME = "componentName"; private static final String TRUST_EXPIRED_ACTION = "android.server.trust.TRUST_EXPIRED_ACTION"; private static final String PERMISSION = android.Manifest.permission.PROVIDE_TRUST_AGENT; private static final boolean DEBUG = false; private static final String TAG = "TrustAgentWrapper"; private static final int MSG_GRANT_TRUST = 1; private static final int MSG_REVOKE_TRUST = 2; private static final int MSG_TRUST_TIMEOUT = 3; private static final int MSG_RESTART_TIMEOUT = 4; private static final int MSG_SET_TRUST_AGENT_FEATURES_COMPLETED = 5; private static final int MSG_MANAGING_TRUST = 6; /** * Time in uptime millis that we wait for the service connection, both when starting * and when the service disconnects. */ private static final long RESTART_TIMEOUT_MILLIS = 5 * 60000; /** * Long extra for {@link #MSG_GRANT_TRUST} */ private static final String DATA_DURATION = "duration"; private final TrustManagerService mTrustManagerService; private final int mUserId; private final Context mContext; private final ComponentName mName; private ITrustAgentService mTrustAgentService; private boolean mBound; private long mScheduledRestartUptimeMillis; private long mMaximumTimeToLock; // from DevicePolicyManager private boolean mPendingSuccessfulUnlock = false; // Trust state private boolean mTrusted; private CharSequence mMessage; private boolean mTrustDisabledByDpm; private boolean mManagingTrust; private IBinder mSetTrustAgentFeaturesToken; private AlarmManager mAlarmManager; private final Intent mAlarmIntent; private PendingIntent mAlarmPendingIntent; private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { ComponentName component = intent.getParcelableExtra(EXTRA_COMPONENT_NAME); if (TRUST_EXPIRED_ACTION.equals(intent.getAction()) && mName.equals(component)) { mHandler.removeMessages(MSG_TRUST_TIMEOUT); mHandler.sendEmptyMessage(MSG_TRUST_TIMEOUT); } } }; private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_GRANT_TRUST: if (!isConnected()) { Log.w(TAG, "Agent is not connected, cannot grant trust: " + mName.flattenToShortString()); return; } mTrusted = true; mMessage = (CharSequence) msg.obj; int flags = msg.arg1; long durationMs = msg.getData().getLong(DATA_DURATION); if (durationMs > 0) { final long duration; if (mMaximumTimeToLock != 0) { // Enforce DevicePolicyManager timeout. This is here as a safeguard to // ensure trust agents are evaluating trust state at least as often as // the policy dictates. Admins that want more guarantees should be using // DevicePolicyManager#KEYGUARD_DISABLE_TRUST_AGENTS. duration = Math.min(durationMs, mMaximumTimeToLock); if (DEBUG) { Log.v(TAG, "DPM lock timeout in effect. Timeout adjusted from " + durationMs + " to " + duration); } } else { duration = durationMs; } long expiration = SystemClock.elapsedRealtime() + duration; mAlarmPendingIntent = PendingIntent.getBroadcast(mContext, 0, mAlarmIntent, PendingIntent.FLAG_CANCEL_CURRENT); mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, expiration, mAlarmPendingIntent); } mTrustManagerService.mArchive.logGrantTrust(mUserId, mName, (mMessage != null ? mMessage.toString() : null), durationMs, flags); mTrustManagerService.updateTrust(mUserId, flags); break; case MSG_TRUST_TIMEOUT: if (DEBUG) Slog.v(TAG, "Trust timed out : " + mName.flattenToShortString()); mTrustManagerService.mArchive.logTrustTimeout(mUserId, mName); onTrustTimeout(); // Fall through. case MSG_REVOKE_TRUST: mTrusted = false; mMessage = null; mHandler.removeMessages(MSG_TRUST_TIMEOUT); if (msg.what == MSG_REVOKE_TRUST) { mTrustManagerService.mArchive.logRevokeTrust(mUserId, mName); } mTrustManagerService.updateTrust(mUserId, 0); break; case MSG_RESTART_TIMEOUT: destroy(); mTrustManagerService.resetAgent(mName, mUserId); break; case MSG_SET_TRUST_AGENT_FEATURES_COMPLETED: IBinder token = (IBinder) msg.obj; boolean result = msg.arg1 != 0; if (mSetTrustAgentFeaturesToken == token) { mSetTrustAgentFeaturesToken = null; if (mTrustDisabledByDpm && result) { if (DEBUG) Log.v(TAG, "Re-enabling agent because it acknowledged " + "enabled features: " + mName); mTrustDisabledByDpm = false; mTrustManagerService.updateTrust(mUserId, 0); } } else { if (DEBUG) Log.w(TAG, "Ignoring MSG_SET_TRUST_AGENT_FEATURES_COMPLETED " + "with obsolete token: " + mName); } break; case MSG_MANAGING_TRUST: mManagingTrust = msg.arg1 != 0; if (!mManagingTrust) { mTrusted = false; mMessage = null; } mTrustManagerService.mArchive.logManagingTrust(mUserId, mName, mManagingTrust); mTrustManagerService.updateTrust(mUserId, 0); break; } } }; private ITrustAgentServiceCallback mCallback = new ITrustAgentServiceCallback.Stub() { @Override public void grantTrust(CharSequence userMessage, long durationMs, int flags) { if (DEBUG) Slog.v(TAG, "enableTrust(" + userMessage + ", durationMs = " + durationMs + ", flags = " + flags + ")"); Message msg = mHandler.obtainMessage( MSG_GRANT_TRUST, flags, 0, userMessage); msg.getData().putLong(DATA_DURATION, durationMs); msg.sendToTarget(); } @Override public void revokeTrust() { if (DEBUG) Slog.v(TAG, "revokeTrust()"); mHandler.sendEmptyMessage(MSG_REVOKE_TRUST); } @Override public void setManagingTrust(boolean managingTrust) { if (DEBUG) Slog.v(TAG, "managingTrust()"); mHandler.obtainMessage(MSG_MANAGING_TRUST, managingTrust ? 1 : 0, 0).sendToTarget(); } @Override public void onConfigureCompleted(boolean result, IBinder token) { if (DEBUG) Slog.v(TAG, "onSetTrustAgentFeaturesEnabledCompleted(result=" + result); mHandler.obtainMessage(MSG_SET_TRUST_AGENT_FEATURES_COMPLETED, result ? 1 : 0, 0, token).sendToTarget(); } }; private final ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { if (DEBUG) Log.v(TAG, "TrustAgent started : " + name.flattenToString()); mHandler.removeMessages(MSG_RESTART_TIMEOUT); mTrustAgentService = ITrustAgentService.Stub.asInterface(service); mTrustManagerService.mArchive.logAgentConnected(mUserId, name); setCallback(mCallback); updateDevicePolicyFeatures(); if (mPendingSuccessfulUnlock) { onUnlockAttempt(true); mPendingSuccessfulUnlock = false; } if (mTrustManagerService.isDeviceLockedInner(mUserId)) { onDeviceLocked(); } else { onDeviceUnlocked(); } } @Override public void onServiceDisconnected(ComponentName name) { if (DEBUG) Log.v(TAG, "TrustAgent disconnected : " + name.flattenToShortString()); mTrustAgentService = null; mManagingTrust = false; mSetTrustAgentFeaturesToken = null; mTrustManagerService.mArchive.logAgentDied(mUserId, name); mHandler.sendEmptyMessage(MSG_REVOKE_TRUST); if (mBound) { scheduleRestart(); } // mTrustDisabledByDpm maintains state } }; public TrustAgentWrapper(Context context, TrustManagerService trustManagerService, Intent intent, UserHandle user) { mContext = context; mTrustManagerService = trustManagerService; mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE); mUserId = user.getIdentifier(); mName = intent.getComponent(); mAlarmIntent = new Intent(TRUST_EXPIRED_ACTION).putExtra(EXTRA_COMPONENT_NAME, mName); mAlarmIntent.setData(Uri.parse(mAlarmIntent.toUri(Intent.URI_INTENT_SCHEME))); mAlarmIntent.setPackage(context.getPackageName()); final IntentFilter alarmFilter = new IntentFilter(TRUST_EXPIRED_ACTION); alarmFilter.addDataScheme(mAlarmIntent.getScheme()); final String pathUri = mAlarmIntent.toUri(Intent.URI_INTENT_SCHEME); alarmFilter.addDataPath(pathUri, PatternMatcher.PATTERN_LITERAL); // Schedules a restart for when connecting times out. If the connection succeeds, // the restart is canceled in mCallback's onConnected. scheduleRestart(); mBound = context.bindServiceAsUser(intent, mConnection, Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE, user); if (mBound) { mContext.registerReceiver(mBroadcastReceiver, alarmFilter, PERMISSION, null); } else { Log.e(TAG, "Can't bind to TrustAgent " + mName.flattenToShortString()); } } private void onError(Exception e) { Slog.w(TAG , "Remote Exception", e); } private void onTrustTimeout() { try { if (mTrustAgentService != null) mTrustAgentService.onTrustTimeout(); } catch (RemoteException e) { onError(e); } } /** * @see android.service.trust.TrustAgentService#onUnlockAttempt(boolean) */ public void onUnlockAttempt(boolean successful) { try { if (mTrustAgentService != null) { mTrustAgentService.onUnlockAttempt(successful); } else { mPendingSuccessfulUnlock = successful; } } catch (RemoteException e) { onError(e); } } /** * @see android.service.trust.TrustAgentService#onDeviceLocked() */ public void onDeviceLocked() { try { if (mTrustAgentService != null) mTrustAgentService.onDeviceLocked(); } catch (RemoteException e) { onError(e); } } /** * @see android.service.trust.TrustAgentService#onDeviceUnlocked() */ public void onDeviceUnlocked() { try { if (mTrustAgentService != null) mTrustAgentService.onDeviceUnlocked(); } catch (RemoteException e) { onError(e); } } private void setCallback(ITrustAgentServiceCallback callback) { try { if (mTrustAgentService != null) { mTrustAgentService.setCallback(callback); } } catch (RemoteException e) { onError(e); } } boolean updateDevicePolicyFeatures() { boolean trustDisabled = false; if (DEBUG) Slog.v(TAG, "updateDevicePolicyFeatures(" + mName + ")"); try { if (mTrustAgentService != null) { DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); if ((dpm.getKeyguardDisabledFeatures(null, mUserId) & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0) { List config = dpm.getTrustAgentConfiguration( null, mName, mUserId); trustDisabled = true; if (DEBUG) Slog.v(TAG, "Detected trust agents disabled. Config = " + config); if (config != null && config.size() > 0) { if (DEBUG) { Slog.v(TAG, "TrustAgent " + mName.flattenToShortString() + " disabled until it acknowledges "+ config); } mSetTrustAgentFeaturesToken = new Binder(); mTrustAgentService.onConfigure(config, mSetTrustAgentFeaturesToken); } } else { mTrustAgentService.onConfigure(Collections.EMPTY_LIST, null); } final long maxTimeToLock = dpm.getMaximumTimeToLockForUserAndProfiles(mUserId); if (maxTimeToLock != mMaximumTimeToLock) { // If the timeout changes, cancel the alarm and send a timeout event to have // the agent re-evaluate trust. mMaximumTimeToLock = maxTimeToLock; if (mAlarmPendingIntent != null) { mAlarmManager.cancel(mAlarmPendingIntent); mAlarmPendingIntent = null; mHandler.sendEmptyMessage(MSG_TRUST_TIMEOUT); } } } } catch (RemoteException e) { onError(e); } if (mTrustDisabledByDpm != trustDisabled) { mTrustDisabledByDpm = trustDisabled; mTrustManagerService.updateTrust(mUserId, 0); } return trustDisabled; } public boolean isTrusted() { return mTrusted && mManagingTrust && !mTrustDisabledByDpm; } public boolean isManagingTrust() { return mManagingTrust && !mTrustDisabledByDpm; } public CharSequence getMessage() { return mMessage; } public void destroy() { mHandler.removeMessages(MSG_RESTART_TIMEOUT); if (!mBound) { return; } if (DEBUG) Log.v(TAG, "TrustAgent unbound : " + mName.flattenToShortString()); mTrustManagerService.mArchive.logAgentStopped(mUserId, mName); mContext.unbindService(mConnection); mBound = false; mContext.unregisterReceiver(mBroadcastReceiver); mTrustAgentService = null; mSetTrustAgentFeaturesToken = null; mHandler.sendEmptyMessage(MSG_REVOKE_TRUST); } public boolean isConnected() { return mTrustAgentService != null; } public boolean isBound() { return mBound; } /** * If not connected, returns the time at which the agent is restarted. * * @return restart time in uptime millis. */ public long getScheduledRestartUptimeMillis() { return mScheduledRestartUptimeMillis; } private void scheduleRestart() { mHandler.removeMessages(MSG_RESTART_TIMEOUT); mScheduledRestartUptimeMillis = SystemClock.uptimeMillis() + RESTART_TIMEOUT_MILLIS; mHandler.sendEmptyMessageAtTime(MSG_RESTART_TIMEOUT, mScheduledRestartUptimeMillis); } }