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