TrustAgentWrapper.java revision 1221b0628d0b896102c20545b30fa7bafa08ae32
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.trust;
18
19import android.app.AlarmManager;
20import android.app.PendingIntent;
21import android.app.admin.DevicePolicyManager;
22import android.content.BroadcastReceiver;
23import android.content.ComponentName;
24import android.content.Context;
25import android.content.Intent;
26import android.content.IntentFilter;
27import android.content.ServiceConnection;
28import android.net.Uri;
29import android.os.Binder;
30import android.os.Handler;
31import android.os.IBinder;
32import android.os.Message;
33import android.os.PatternMatcher;
34import android.os.PersistableBundle;
35import android.os.RemoteException;
36import android.os.SystemClock;
37import android.os.UserHandle;
38import android.util.Log;
39import android.util.Slog;
40import android.service.trust.ITrustAgentService;
41import android.service.trust.ITrustAgentServiceCallback;
42
43import java.util.Collections;
44import java.util.List;
45
46/**
47 * A wrapper around a TrustAgentService interface. Coordinates communication between
48 * TrustManager and the actual TrustAgent.
49 */
50public class TrustAgentWrapper {
51    private static final String EXTRA_COMPONENT_NAME = "componentName";
52    private static final String TRUST_EXPIRED_ACTION = "android.server.trust.TRUST_EXPIRED_ACTION";
53    private static final String PERMISSION = android.Manifest.permission.PROVIDE_TRUST_AGENT;
54    private static final boolean DEBUG = false;
55    private static final String TAG = "TrustAgentWrapper";
56
57    private static final int MSG_GRANT_TRUST = 1;
58    private static final int MSG_REVOKE_TRUST = 2;
59    private static final int MSG_TRUST_TIMEOUT = 3;
60    private static final int MSG_RESTART_TIMEOUT = 4;
61    private static final int MSG_SET_TRUST_AGENT_FEATURES_COMPLETED = 5;
62    private static final int MSG_MANAGING_TRUST = 6;
63
64    /**
65     * Time in uptime millis that we wait for the service connection, both when starting
66     * and when the service disconnects.
67     */
68    private static final long RESTART_TIMEOUT_MILLIS = 5 * 60000;
69
70    /**
71     * Long extra for {@link #MSG_GRANT_TRUST}
72     */
73    private static final String DATA_DURATION = "duration";
74
75    private final TrustManagerService mTrustManagerService;
76    private final int mUserId;
77    private final Context mContext;
78    private final ComponentName mName;
79
80    private ITrustAgentService mTrustAgentService;
81    private boolean mBound;
82    private long mScheduledRestartUptimeMillis;
83    private long mMaximumTimeToLock; // from DevicePolicyManager
84
85    // Trust state
86    private boolean mTrusted;
87    private CharSequence mMessage;
88    private boolean mTrustDisabledByDpm;
89    private boolean mManagingTrust;
90    private IBinder mSetTrustAgentFeaturesToken;
91    private AlarmManager mAlarmManager;
92    private final Intent mAlarmIntent;
93    private PendingIntent mAlarmPendingIntent;
94
95    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
96        @Override
97        public void onReceive(Context context, Intent intent) {
98            ComponentName component = intent.getParcelableExtra(EXTRA_COMPONENT_NAME);
99            if (TRUST_EXPIRED_ACTION.equals(intent.getAction())
100                    && mName.equals(component)) {
101                mHandler.removeMessages(MSG_TRUST_TIMEOUT);
102                mHandler.sendEmptyMessage(MSG_TRUST_TIMEOUT);
103            }
104        }
105    };
106
107    private final Handler mHandler = new Handler() {
108        @Override
109        public void handleMessage(Message msg) {
110            switch (msg.what) {
111                case MSG_GRANT_TRUST:
112                    if (!isConnected()) {
113                        Log.w(TAG, "Agent is not connected, cannot grant trust: "
114                                + mName.flattenToShortString());
115                        return;
116                    }
117                    mTrusted = true;
118                    mMessage = (CharSequence) msg.obj;
119                    boolean initiatedByUser = msg.arg1 != 0;
120                    long durationMs = msg.getData().getLong(DATA_DURATION);
121                    if (durationMs > 0) {
122                        final long duration;
123                        if (mMaximumTimeToLock != 0) {
124                            // Enforce DevicePolicyManager timeout.  This is here as a safeguard to
125                            // ensure trust agents are evaluating trust state at least as often as
126                            // the policy dictates. Admins that want more guarantees should be using
127                            // DevicePolicyManager#KEYGUARD_DISABLE_TRUST_AGENTS.
128                            duration = Math.min(durationMs, mMaximumTimeToLock);
129                            if (DEBUG) {
130                                Log.v(TAG, "DPM lock timeout in effect. Timeout adjusted from "
131                                    + durationMs + " to " + duration);
132                            }
133                        } else {
134                            duration = durationMs;
135                        }
136                        long expiration = SystemClock.elapsedRealtime() + duration;
137                        mAlarmPendingIntent = PendingIntent.getBroadcast(mContext, 0, mAlarmIntent,
138                                PendingIntent.FLAG_CANCEL_CURRENT);
139                        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, expiration,
140                                mAlarmPendingIntent);
141                    }
142                    mTrustManagerService.mArchive.logGrantTrust(mUserId, mName,
143                            (mMessage != null ? mMessage.toString() : null),
144                            durationMs, initiatedByUser);
145                    mTrustManagerService.updateTrust(mUserId, initiatedByUser);
146                    break;
147                case MSG_TRUST_TIMEOUT:
148                    if (DEBUG) Slog.v(TAG, "Trust timed out : " + mName.flattenToShortString());
149                    mTrustManagerService.mArchive.logTrustTimeout(mUserId, mName);
150                    onTrustTimeout();
151                    // Fall through.
152                case MSG_REVOKE_TRUST:
153                    mTrusted = false;
154                    mMessage = null;
155                    mHandler.removeMessages(MSG_TRUST_TIMEOUT);
156                    if (msg.what == MSG_REVOKE_TRUST) {
157                        mTrustManagerService.mArchive.logRevokeTrust(mUserId, mName);
158                    }
159                    mTrustManagerService.updateTrust(mUserId, false);
160                    break;
161                case MSG_RESTART_TIMEOUT:
162                    destroy();
163                    mTrustManagerService.resetAgent(mName, mUserId);
164                    break;
165                case MSG_SET_TRUST_AGENT_FEATURES_COMPLETED:
166                    IBinder token = (IBinder) msg.obj;
167                    boolean result = msg.arg1 != 0;
168                    if (mSetTrustAgentFeaturesToken == token) {
169                        mSetTrustAgentFeaturesToken = null;
170                        if (mTrustDisabledByDpm && result) {
171                            if (DEBUG) Log.v(TAG, "Re-enabling agent because it acknowledged "
172                                    + "enabled features: " + mName);
173                            mTrustDisabledByDpm = false;
174                            mTrustManagerService.updateTrust(mUserId, false);
175                        }
176                    } else {
177                        if (DEBUG) Log.w(TAG, "Ignoring MSG_SET_TRUST_AGENT_FEATURES_COMPLETED "
178                                + "with obsolete token: " + mName);
179                    }
180                    break;
181                case MSG_MANAGING_TRUST:
182                    mManagingTrust = msg.arg1 != 0;
183                    if (!mManagingTrust) {
184                        mTrusted = false;
185                        mMessage = null;
186                    }
187                    mTrustManagerService.mArchive.logManagingTrust(mUserId, mName, mManagingTrust);
188                    mTrustManagerService.updateTrust(mUserId, false);
189                    break;
190            }
191        }
192    };
193
194    private ITrustAgentServiceCallback mCallback = new ITrustAgentServiceCallback.Stub() {
195
196        @Override
197        public void grantTrust(CharSequence userMessage, long durationMs, boolean initiatedByUser) {
198            if (DEBUG) Slog.v(TAG, "enableTrust(" + userMessage + ", durationMs = " + durationMs
199                        + ", initiatedByUser = " + initiatedByUser + ")");
200
201            Message msg = mHandler.obtainMessage(
202                    MSG_GRANT_TRUST, initiatedByUser ? 1 : 0, 0, userMessage);
203            msg.getData().putLong(DATA_DURATION, durationMs);
204            msg.sendToTarget();
205        }
206
207        @Override
208        public void revokeTrust() {
209            if (DEBUG) Slog.v(TAG, "revokeTrust()");
210            mHandler.sendEmptyMessage(MSG_REVOKE_TRUST);
211        }
212
213        @Override
214        public void setManagingTrust(boolean managingTrust) {
215            if (DEBUG) Slog.v(TAG, "managingTrust()");
216            mHandler.obtainMessage(MSG_MANAGING_TRUST, managingTrust ? 1 : 0, 0).sendToTarget();
217        }
218
219        @Override
220        public void onConfigureCompleted(boolean result, IBinder token) {
221            if (DEBUG) Slog.v(TAG, "onSetTrustAgentFeaturesEnabledCompleted(result=" + result);
222            mHandler.obtainMessage(MSG_SET_TRUST_AGENT_FEATURES_COMPLETED,
223                    result ? 1 : 0, 0, token).sendToTarget();
224        }
225    };
226
227    private final ServiceConnection mConnection = new ServiceConnection() {
228        @Override
229        public void onServiceConnected(ComponentName name, IBinder service) {
230            if (DEBUG) Log.v(TAG, "TrustAgent started : " + name.flattenToString());
231            mHandler.removeMessages(MSG_RESTART_TIMEOUT);
232            mTrustAgentService = ITrustAgentService.Stub.asInterface(service);
233            mTrustManagerService.mArchive.logAgentConnected(mUserId, name);
234            setCallback(mCallback);
235            updateDevicePolicyFeatures();
236
237            if (mTrustManagerService.isDeviceLockedInner(mUserId)) {
238                onDeviceLocked();
239            } else {
240                onDeviceUnlocked();
241            }
242        }
243
244        @Override
245        public void onServiceDisconnected(ComponentName name) {
246            if (DEBUG) Log.v(TAG, "TrustAgent disconnected : " + name.flattenToShortString());
247            mTrustAgentService = null;
248            mManagingTrust = false;
249            mSetTrustAgentFeaturesToken = null;
250            mTrustManagerService.mArchive.logAgentDied(mUserId, name);
251            mHandler.sendEmptyMessage(MSG_REVOKE_TRUST);
252            if (mBound) {
253                scheduleRestart();
254            }
255            // mTrustDisabledByDpm maintains state
256        }
257    };
258
259    public TrustAgentWrapper(Context context, TrustManagerService trustManagerService,
260            Intent intent, UserHandle user) {
261        mContext = context;
262        mTrustManagerService = trustManagerService;
263        mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
264        mUserId = user.getIdentifier();
265        mName = intent.getComponent();
266
267        mAlarmIntent = new Intent(TRUST_EXPIRED_ACTION).putExtra(EXTRA_COMPONENT_NAME, mName);
268        mAlarmIntent.setData(Uri.parse(mAlarmIntent.toUri(Intent.URI_INTENT_SCHEME)));
269        mAlarmIntent.setPackage(context.getPackageName());
270
271        final IntentFilter alarmFilter = new IntentFilter(TRUST_EXPIRED_ACTION);
272        alarmFilter.addDataScheme(mAlarmIntent.getScheme());
273        final String pathUri = mAlarmIntent.toUri(Intent.URI_INTENT_SCHEME);
274        alarmFilter.addDataPath(pathUri, PatternMatcher.PATTERN_LITERAL);
275
276        // Schedules a restart for when connecting times out. If the connection succeeds,
277        // the restart is canceled in mCallback's onConnected.
278        scheduleRestart();
279        mBound = context.bindServiceAsUser(intent, mConnection, Context.BIND_AUTO_CREATE, user);
280        if (mBound) {
281            mContext.registerReceiver(mBroadcastReceiver, alarmFilter, PERMISSION, null);
282        } else {
283            Log.e(TAG, "Can't bind to TrustAgent " + mName.flattenToShortString());
284        }
285    }
286
287    private void onError(Exception e) {
288        Slog.w(TAG , "Remote Exception", e);
289    }
290
291    private void onTrustTimeout() {
292        try {
293            if (mTrustAgentService != null) mTrustAgentService.onTrustTimeout();
294        } catch (RemoteException e) {
295            onError(e);
296        }
297    }
298
299    /**
300     * @see android.service.trust.TrustAgentService#onUnlockAttempt(boolean)
301     */
302    public void onUnlockAttempt(boolean successful) {
303        try {
304            if (mTrustAgentService != null) mTrustAgentService.onUnlockAttempt(successful);
305        } catch (RemoteException e) {
306            onError(e);
307        }
308    }
309
310    /**
311     * @see android.service.trust.TrustAgentService#onDeviceLocked()
312     */
313    public void onDeviceLocked() {
314        try {
315            if (mTrustAgentService != null) mTrustAgentService.onDeviceLocked();
316        } catch (RemoteException e) {
317            onError(e);
318        }
319    }
320
321    /**
322     * @see android.service.trust.TrustAgentService#onDeviceUnlocked()
323     */
324    public void onDeviceUnlocked() {
325        try {
326            if (mTrustAgentService != null) mTrustAgentService.onDeviceUnlocked();
327        } catch (RemoteException e) {
328            onError(e);
329        }
330    }
331
332    private void setCallback(ITrustAgentServiceCallback callback) {
333        try {
334            if (mTrustAgentService != null) {
335                mTrustAgentService.setCallback(callback);
336            }
337        } catch (RemoteException e) {
338            onError(e);
339        }
340    }
341
342    boolean updateDevicePolicyFeatures() {
343        boolean trustDisabled = false;
344        if (DEBUG) Slog.v(TAG, "updateDevicePolicyFeatures(" + mName + ")");
345        try {
346            if (mTrustAgentService != null) {
347                DevicePolicyManager dpm =
348                    (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
349
350                if ((dpm.getKeyguardDisabledFeatures(null, mUserId)
351                        & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0) {
352                    List<PersistableBundle> config = dpm.getTrustAgentConfiguration(
353                            null, mName, mUserId);
354                    trustDisabled = true;
355                    if (DEBUG) Slog.v(TAG, "Detected trust agents disabled. Config = " + config);
356                    if (config != null && config.size() > 0) {
357                        if (DEBUG) {
358                            Slog.v(TAG, "TrustAgent " + mName.flattenToShortString()
359                                    + " disabled until it acknowledges "+ config);
360                        }
361                        mSetTrustAgentFeaturesToken = new Binder();
362                        mTrustAgentService.onConfigure(config, mSetTrustAgentFeaturesToken);
363                    }
364                } else {
365                    mTrustAgentService.onConfigure(Collections.EMPTY_LIST, null);
366                }
367                final long maxTimeToLock = dpm.getMaximumTimeToLock(null);
368                if (maxTimeToLock != mMaximumTimeToLock) {
369                    // If the timeout changes, cancel the alarm and send a timeout event to have
370                    // the agent re-evaluate trust.
371                    mMaximumTimeToLock = maxTimeToLock;
372                    if (mAlarmPendingIntent != null) {
373                        mAlarmManager.cancel(mAlarmPendingIntent);
374                        mAlarmPendingIntent = null;
375                        mHandler.sendEmptyMessage(MSG_TRUST_TIMEOUT);
376                    }
377                }
378            }
379        } catch (RemoteException e) {
380            onError(e);
381        }
382        if (mTrustDisabledByDpm != trustDisabled) {
383            mTrustDisabledByDpm = trustDisabled;
384            mTrustManagerService.updateTrust(mUserId, false);
385        }
386        return trustDisabled;
387    }
388
389    public boolean isTrusted() {
390        return mTrusted && mManagingTrust && !mTrustDisabledByDpm;
391    }
392
393    public boolean isManagingTrust() {
394        return mManagingTrust && !mTrustDisabledByDpm;
395    }
396
397    public CharSequence getMessage() {
398        return mMessage;
399    }
400
401    public void destroy() {
402        mHandler.removeMessages(MSG_RESTART_TIMEOUT);
403
404        if (!mBound) {
405            return;
406        }
407        if (DEBUG) Log.v(TAG, "TrustAgent unbound : " + mName.flattenToShortString());
408        mTrustManagerService.mArchive.logAgentStopped(mUserId, mName);
409        mContext.unbindService(mConnection);
410        mBound = false;
411        mContext.unregisterReceiver(mBroadcastReceiver);
412        mTrustAgentService = null;
413        mSetTrustAgentFeaturesToken = null;
414        mHandler.sendEmptyMessage(MSG_REVOKE_TRUST);
415    }
416
417    public boolean isConnected() {
418        return mTrustAgentService != null;
419    }
420
421    public boolean isBound() {
422        return mBound;
423    }
424
425    /**
426     * If not connected, returns the time at which the agent is restarted.
427     *
428     * @return restart time in uptime millis.
429     */
430    public long getScheduledRestartUptimeMillis() {
431        return mScheduledRestartUptimeMillis;
432    }
433
434    private void scheduleRestart() {
435        mHandler.removeMessages(MSG_RESTART_TIMEOUT);
436        mScheduledRestartUptimeMillis = SystemClock.uptimeMillis() + RESTART_TIMEOUT_MILLIS;
437        mHandler.sendEmptyMessageAtTime(MSG_RESTART_TIMEOUT, mScheduledRestartUptimeMillis);
438    }
439}
440