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