TrustAgentWrapper.java revision 604e7558ef32098644b2f9456d7743a07ae789dc
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.admin.DevicePolicyManager;
20import android.content.BroadcastReceiver;
21import android.content.ComponentName;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
25import android.content.ServiceConnection;
26import android.os.Bundle;
27import android.os.Handler;
28import android.os.IBinder;
29import android.os.Message;
30import android.os.RemoteException;
31import android.os.SystemClock;
32import android.os.UserHandle;
33import android.util.Log;
34import android.util.Slog;
35import android.service.trust.ITrustAgentService;
36import android.service.trust.ITrustAgentServiceCallback;
37import android.service.trust.TrustAgentService;
38
39import java.util.ArrayList;
40import java.util.List;
41
42/**
43 * A wrapper around a TrustAgentService interface. Coordinates communication between
44 * TrustManager and the actual TrustAgent.
45 */
46public class TrustAgentWrapper {
47    private static final boolean DEBUG = false;
48    private static final String TAG = "TrustAgentWrapper";
49
50    private static final int MSG_GRANT_TRUST = 1;
51    private static final int MSG_REVOKE_TRUST = 2;
52    private static final int MSG_TRUST_TIMEOUT = 3;
53    private static final int MSG_RESTART_TIMEOUT = 4;
54    private static final int MSG_DPM_CHANGED = 5;
55
56    /**
57     * Time in uptime millis that we wait for the service connection, both when starting
58     * and when the service disconnects.
59     */
60    private static final long RESTART_TIMEOUT_MILLIS = 5 * 60000;
61
62    /**
63     * Long extra for {@link #MSG_GRANT_TRUST}
64     */
65    private static final String DATA_DURATION = "duration";
66
67    private final TrustManagerService mTrustManagerService;
68    private final int mUserId;
69    private final Context mContext;
70    private final ComponentName mName;
71
72    private ITrustAgentService mTrustAgentService;
73    private boolean mBound;
74    private long mScheduledRestartUptimeMillis;
75
76    // Trust state
77    private boolean mTrusted;
78    private CharSequence mMessage;
79    private boolean mTrustDisabledByDpm;
80
81    private final Handler mHandler = new Handler() {
82        @Override
83        public void handleMessage(Message msg) {
84            switch (msg.what) {
85                case MSG_GRANT_TRUST:
86                    if (!isConnected()) {
87                        Log.w(TAG, "Agent is not connected, cannot grant trust: "
88                                + mName.flattenToShortString());
89                        return;
90                    }
91                    mTrusted = true;
92                    mMessage = (CharSequence) msg.obj;
93                    boolean initiatedByUser = msg.arg1 != 0;
94                    // TODO: Handle initiatedByUser.
95                    long durationMs = msg.getData().getLong(DATA_DURATION);
96                    if (durationMs > 0) {
97                        mHandler.removeMessages(MSG_TRUST_TIMEOUT);
98                        mHandler.sendEmptyMessageDelayed(MSG_TRUST_TIMEOUT, durationMs);
99                    }
100                    mTrustManagerService.mArchive.logGrantTrust(mUserId, mName,
101                            (mMessage != null ? mMessage.toString() : null),
102                            durationMs, initiatedByUser);
103                    mTrustManagerService.updateTrust(mUserId);
104                    break;
105                case MSG_TRUST_TIMEOUT:
106                    if (DEBUG) Slog.v(TAG, "Trust timed out : " + mName.flattenToShortString());
107                    mTrustManagerService.mArchive.logTrustTimeout(mUserId, mName);
108                    // Fall through.
109                case MSG_REVOKE_TRUST:
110                    mTrusted = false;
111                    mMessage = null;
112                    mHandler.removeMessages(MSG_TRUST_TIMEOUT);
113                    if (msg.what == MSG_REVOKE_TRUST) {
114                        mTrustManagerService.mArchive.logRevokeTrust(mUserId, mName);
115                    }
116                    mTrustManagerService.updateTrust(mUserId);
117                    break;
118                case MSG_RESTART_TIMEOUT:
119                    unbind();
120                    mTrustManagerService.resetAgent(mName, mUserId);
121                    break;
122                case MSG_DPM_CHANGED:
123                    updateDevicePolicyFeatures(mName);
124                    break;
125            }
126        }
127    };
128
129    private ITrustAgentServiceCallback mCallback = new ITrustAgentServiceCallback.Stub() {
130
131        @Override
132        public void grantTrust(CharSequence userMessage, long durationMs, boolean initiatedByUser) {
133            if (DEBUG) Slog.v(TAG, "enableTrust(" + userMessage + ", durationMs = " + durationMs
134                        + ", initiatedByUser = " + initiatedByUser + ")");
135
136            Message msg = mHandler.obtainMessage(
137                    MSG_GRANT_TRUST, initiatedByUser ? 1 : 0, 0, userMessage);
138            msg.getData().putLong(DATA_DURATION, durationMs);
139            msg.sendToTarget();
140        }
141
142        @Override
143        public void revokeTrust() {
144            if (DEBUG) Slog.v(TAG, "revokeTrust()");
145            mHandler.sendEmptyMessage(MSG_REVOKE_TRUST);
146        }
147    };
148
149    private final ServiceConnection mConnection = new ServiceConnection() {
150        @Override
151        public void onServiceConnected(ComponentName name, IBinder service) {
152            if (DEBUG) Log.v(TAG, "TrustAgent started : " + name.flattenToString());
153            mHandler.removeMessages(MSG_RESTART_TIMEOUT);
154            mTrustAgentService = ITrustAgentService.Stub.asInterface(service);
155            mTrustManagerService.mArchive.logAgentConnected(mUserId, name);
156            setCallback(mCallback);
157            updateDevicePolicyFeatures(name);
158            watchForDpmChanges(true);
159        }
160
161        @Override
162        public void onServiceDisconnected(ComponentName name) {
163            if (DEBUG) Log.v(TAG, "TrustAgent disconnected : " + name.flattenToShortString());
164            mTrustAgentService = null;
165            mTrustManagerService.mArchive.logAgentDied(mUserId, name);
166            mHandler.sendEmptyMessage(MSG_REVOKE_TRUST);
167            if (mBound) {
168                scheduleRestart();
169            }
170            // mTrustDisabledByDpm maintains state
171            watchForDpmChanges(false);
172        }
173    };
174
175    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
176
177        @Override
178        public void onReceive(Context context, Intent intent) {
179            if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED
180                    .equals(intent.getAction())) {
181                mHandler.sendEmptyMessage(MSG_DPM_CHANGED);
182            }
183        }
184    };
185
186    public TrustAgentWrapper(Context context, TrustManagerService trustManagerService,
187            Intent intent, UserHandle user) {
188        mContext = context;
189        mTrustManagerService = trustManagerService;
190        mUserId = user.getIdentifier();
191        mName = intent.getComponent();
192        // Schedules a restart for when connecting times out. If the connection succeeds,
193        // the restart is canceled in mCallback's onConnected.
194        scheduleRestart();
195        mBound = context.bindServiceAsUser(intent, mConnection, Context.BIND_AUTO_CREATE, user);
196        if (!mBound) {
197            Log.e(TAG, "Can't bind to TrustAgent " + mName.flattenToShortString());
198        }
199    }
200
201    private void onError(Exception e) {
202        Slog.w(TAG , "Remote Exception", e);
203    }
204
205    /**
206     * @see android.service.trust.TrustAgentService#onUnlockAttempt(boolean)
207     */
208    public void onUnlockAttempt(boolean successful) {
209        try {
210            if (mTrustAgentService != null) mTrustAgentService.onUnlockAttempt(successful);
211        } catch (RemoteException e) {
212            onError(e);
213        }
214    }
215
216    private void setCallback(ITrustAgentServiceCallback callback) {
217        try {
218            if (mTrustAgentService != null) {
219                mTrustAgentService.setCallback(callback);
220            }
221        } catch (RemoteException e) {
222            onError(e);
223        }
224    }
225
226    private void watchForDpmChanges(boolean start) {
227        if (start) {
228            final IntentFilter filter = new IntentFilter();
229            filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
230            filter.addAction(Intent.ACTION_USER_REMOVED);
231            mContext.registerReceiver(mBroadcastReceiver, filter);
232        } else {
233            mContext.unregisterReceiver(mBroadcastReceiver);
234        }
235    }
236
237    private boolean updateDevicePolicyFeatures(ComponentName name) {
238        boolean trustDisabled = false;
239        if (DEBUG) Slog.v(TAG, "updateDevicePolicyFeatures(" + name + ")");
240        try {
241            if (mTrustAgentService != null) {
242                DevicePolicyManager dpm =
243                    (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
244                if (dpm != null) {
245                    // If trust disabled, only enable it if the options bundle is set and
246                    // accepted by the TrustAgent.
247                    if ((dpm.getKeyguardDisabledFeatures(null)
248                            & DevicePolicyManager.KEYGUARD_DISABLE_TRUST_AGENTS) != 0) {
249                        List<String> features = dpm.getTrustAgentFeaturesEnabled(null, name);
250                        if (DEBUG) Slog.v(TAG, "Detected trust agents disabled. Features = " + features);
251                        if (features != null && features.size() > 0) {
252                            Bundle bundle = new Bundle();
253                            bundle.putStringArrayList(TrustAgentService.KEY_FEATURES,
254                                    (ArrayList<String>)features);
255                            if (DEBUG) {
256                                Slog.v(TAG, "TrustAgent " + name.flattenToShortString()
257                                        + " disabled except "+ features);
258                            }
259                            trustDisabled = mTrustAgentService.setTrustAgentFeaturesEnabled(bundle);
260                        } else {
261                            if (DEBUG) Slog.v(TAG, "TrustAgent " + name + " disabled by flag");
262                            trustDisabled = true; // trust agent should be disabled
263                        }
264                    }
265                } else {
266                    Log.e(TAG, "Can't get DevicePolicyManagerService: is it running?",
267                            new IllegalStateException("Stack trace:"));
268                }
269            }
270        } catch (RemoteException e) {
271            onError(e);
272        }
273        if (mTrustDisabledByDpm != trustDisabled) {
274            mTrustDisabledByDpm = trustDisabled;
275            mTrustManagerService.updateTrust(mUserId);
276        }
277        return trustDisabled;
278    }
279
280    public boolean isTrusted() {
281        return mTrusted && !mTrustDisabledByDpm;
282    }
283
284    public CharSequence getMessage() {
285        return mMessage;
286    }
287
288    public void unbind() {
289        if (!mBound) {
290            return;
291        }
292        if (DEBUG) Log.v(TAG, "TrustAgent unbound : " + mName.flattenToShortString());
293        mTrustManagerService.mArchive.logAgentStopped(mUserId, mName);
294        mContext.unbindService(mConnection);
295        mBound = false;
296        mTrustAgentService = null;
297        mHandler.sendEmptyMessage(MSG_REVOKE_TRUST);
298        mHandler.removeMessages(MSG_RESTART_TIMEOUT);
299    }
300
301    public boolean isConnected() {
302        return mTrustAgentService != null;
303    }
304
305    public boolean isBound() {
306        return mBound;
307    }
308
309    /**
310     * If not connected, returns the time at which the agent is restarted.
311     *
312     * @return restart time in uptime millis.
313     */
314    public long getScheduledRestartUptimeMillis() {
315        return mScheduledRestartUptimeMillis;
316    }
317
318    private void scheduleRestart() {
319        mHandler.removeMessages(MSG_RESTART_TIMEOUT);
320        mScheduledRestartUptimeMillis = SystemClock.uptimeMillis() + RESTART_TIMEOUT_MILLIS;
321        mHandler.sendEmptyMessageAtTime(MSG_RESTART_TIMEOUT, mScheduledRestartUptimeMillis);
322    }
323}
324