TrustAgentService.java revision a43fd03b9b23f9ad0deae9ee69ee80aaa1433cdb
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 android.service.trust;
18
19import android.Manifest;
20import android.annotation.SdkConstant;
21import android.annotation.SystemApi;
22import android.app.Service;
23import android.app.admin.DevicePolicyManager;
24import android.content.ComponentName;
25import android.content.Intent;
26import android.content.pm.PackageManager;
27import android.content.pm.ServiceInfo;
28import android.os.Handler;
29import android.os.IBinder;
30import android.os.PersistableBundle;
31import android.os.RemoteException;
32import android.util.Log;
33import android.util.Slog;
34
35import java.util.List;
36
37/**
38 * A service that notifies the system about whether it believes the environment of the device
39 * to be trusted.
40 *
41 * <p>Trust agents may only be provided by the platform. It is expected that there is only
42 * one trust agent installed on the platform. In the event there is more than one,
43 * either trust agent can enable trust.
44 * </p>
45 *
46 * <p>To extend this class, you must declare the service in your manifest file with
47 * the {@link android.Manifest.permission#BIND_TRUST_AGENT} permission
48 * and include an intent filter with the {@link #SERVICE_INTERFACE} action. For example:</p>
49 * <pre>
50 * &lt;service android:name=".TrustAgent"
51 *          android:label="&#64;string/service_name"
52 *          android:permission="android.permission.BIND_TRUST_AGENT">
53 *     &lt;intent-filter>
54 *         &lt;action android:name="android.service.trust.TrustAgentService" />
55 *     &lt;/intent-filter>
56 *     &lt;meta-data android:name="android.service.trust.trustagent"
57 *          android:value="&#64;xml/trust_agent" />
58 * &lt;/service></pre>
59 *
60 * <p>The associated meta-data file can specify an activity that is accessible through Settings
61 * and should allow configuring the trust agent, as defined in
62 * {@link android.R.styleable#TrustAgent}. For example:</p>
63 *
64 * <pre>
65 * &lt;trust-agent xmlns:android="http://schemas.android.com/apk/res/android"
66 *          android:settingsActivity=".TrustAgentSettings" /></pre>
67 *
68 * @hide
69 */
70@SystemApi
71public class TrustAgentService extends Service {
72    private final String TAG = TrustAgentService.class.getSimpleName() +
73            "[" + getClass().getSimpleName() + "]";
74    private static final boolean DEBUG = false;
75
76    /**
77     * The {@link Intent} that must be declared as handled by the service.
78     */
79    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
80    public static final String SERVICE_INTERFACE
81            = "android.service.trust.TrustAgentService";
82
83    /**
84     * The name of the {@code meta-data} tag pointing to additional configuration of the trust
85     * agent.
86     */
87    public static final String TRUST_AGENT_META_DATA = "android.service.trust.trustagent";
88
89    private static final int MSG_UNLOCK_ATTEMPT = 1;
90    private static final int MSG_CONFIGURE = 2;
91    private static final int MSG_TRUST_TIMEOUT = 3;
92    private static final int MSG_DEVICE_LOCKED = 4;
93    private static final int MSG_DEVICE_UNLOCKED = 5;
94
95    /**
96     * Class containing raw data for a given configuration request.
97     */
98    private static final class ConfigurationData {
99        final IBinder token;
100        final List<PersistableBundle> options;
101        ConfigurationData(List<PersistableBundle> opts, IBinder t) {
102            options = opts;
103            token = t;
104        }
105    }
106
107    private ITrustAgentServiceCallback mCallback;
108
109    private Runnable mPendingGrantTrustTask;
110
111    private boolean mManagingTrust;
112
113    // Lock used to access mPendingGrantTrustTask and mCallback.
114    private final Object mLock = new Object();
115
116    private Handler mHandler = new Handler() {
117        public void handleMessage(android.os.Message msg) {
118            switch (msg.what) {
119                case MSG_UNLOCK_ATTEMPT:
120                    onUnlockAttempt(msg.arg1 != 0);
121                    break;
122                case MSG_CONFIGURE:
123                    ConfigurationData data = (ConfigurationData) msg.obj;
124                    boolean result = onConfigure(data.options);
125                    if (data.token != null) {
126                        try {
127                            synchronized (mLock) {
128                                mCallback.onConfigureCompleted(result, data.token);
129                            }
130                        } catch (RemoteException e) {
131                            onError("calling onSetTrustAgentFeaturesEnabledCompleted()");
132                        }
133                    }
134                    break;
135                case MSG_TRUST_TIMEOUT:
136                    onTrustTimeout();
137                    break;
138                case MSG_DEVICE_LOCKED:
139                    onDeviceLocked();
140                    break;
141                case MSG_DEVICE_UNLOCKED:
142                    onDeviceUnlocked();
143                    break;
144            }
145        }
146    };
147
148    @Override
149    public void onCreate() {
150        super.onCreate();
151        ComponentName component = new ComponentName(this, getClass());
152        try {
153            ServiceInfo serviceInfo = getPackageManager().getServiceInfo(component, 0 /* flags */);
154            if (!Manifest.permission.BIND_TRUST_AGENT.equals(serviceInfo.permission)) {
155                throw new IllegalStateException(component.flattenToShortString()
156                        + " is not declared with the permission "
157                        + "\"" + Manifest.permission.BIND_TRUST_AGENT + "\"");
158            }
159        } catch (PackageManager.NameNotFoundException e) {
160            Log.e(TAG, "Can't get ServiceInfo for " + component.toShortString());
161        }
162    }
163
164    /**
165     * Called after the user attempts to authenticate in keyguard with their device credentials,
166     * such as pin, pattern or password.
167     *
168     * @param successful true if the user successfully completed the challenge.
169     */
170    public void onUnlockAttempt(boolean successful) {
171    }
172
173    /**
174     * Called when the timeout provided by the agent expires.  Note that this may be called earlier
175     * than requested by the agent if the trust timeout is adjusted by the system or
176     * {@link DevicePolicyManager}.  The agent is expected to re-evaluate the trust state and only
177     * call {@link #grantTrust(CharSequence, long, boolean)} if the trust state should be
178     * continued.
179     */
180    public void onTrustTimeout() {
181    }
182
183    /**
184     * Called when the device enters a state where a PIN, pattern or
185     * password must be entered to unlock it.
186     */
187    public void onDeviceLocked() {
188    }
189
190    /**
191     * Called when the device leaves a state where a PIN, pattern or
192     * password must be entered to unlock it.
193     */
194    public void onDeviceUnlocked() {
195    }
196
197    private void onError(String msg) {
198        Slog.v(TAG, "Remote exception while " + msg);
199    }
200
201    /**
202     * Called when device policy admin wants to enable specific options for agent in response to
203     * {@link DevicePolicyManager#setKeyguardDisabledFeatures(ComponentName, int)} and
204     * {@link DevicePolicyManager#setTrustAgentConfiguration(ComponentName, ComponentName,
205     * PersistableBundle)}.
206     * <p>Agents that support configuration options should overload this method and return 'true'.
207     *
208     * @param options The aggregated list of options or an empty list if no restrictions apply.
209     * @return true if the {@link TrustAgentService} supports configuration options.
210     */
211    public boolean onConfigure(List<PersistableBundle> options) {
212        return false;
213    }
214
215    /**
216     * Call to grant trust on the device.
217     *
218     * @param message describes why the device is trusted, e.g. "Trusted by location".
219     * @param durationMs amount of time in milliseconds to keep the device in a trusted state.
220     *    Trust for this agent will automatically be revoked when the timeout expires unless
221     *    extended by a subsequent call to this function. The timeout is measured from the
222     *    invocation of this function as dictated by {@link SystemClock#elapsedRealtime())}.
223     *    For security reasons, the value should be no larger than necessary.
224     *    The value may be adjusted by the system as necessary to comply with a policy controlled
225     *    by the system or {@link DevicePolicyManager} restrictions. See {@link #onTrustTimeout()}
226     *    for determining when trust expires.
227     * @param initiatedByUser this is a hint to the system that trust is being granted as the
228     *    direct result of user action - such as solving a security challenge. The hint is used
229     *    by the system to optimize the experience. Behavior may vary by device and release, so
230     *    one should only set this parameter if it meets the above criteria rather than relying on
231     *    the behavior of any particular device or release.
232     * @throws IllegalStateException if the agent is not currently managing trust.
233     */
234    public final void grantTrust(
235            final CharSequence message, final long durationMs, final boolean initiatedByUser) {
236        synchronized (mLock) {
237            if (!mManagingTrust) {
238                throw new IllegalStateException("Cannot grant trust if agent is not managing trust."
239                        + " Call setManagingTrust(true) first.");
240            }
241            if (mCallback != null) {
242                try {
243                    mCallback.grantTrust(message.toString(), durationMs, initiatedByUser);
244                } catch (RemoteException e) {
245                    onError("calling enableTrust()");
246                }
247            } else {
248                // Remember trust has been granted so we can effectively grant it once the service
249                // is bound.
250                mPendingGrantTrustTask = new Runnable() {
251                    @Override
252                    public void run() {
253                        grantTrust(message, durationMs, initiatedByUser);
254                    }
255                };
256            }
257        }
258    }
259
260    /**
261     * Call to revoke trust on the device.
262     */
263    public final void revokeTrust() {
264        synchronized (mLock) {
265            if (mPendingGrantTrustTask != null) {
266                mPendingGrantTrustTask = null;
267            }
268            if (mCallback != null) {
269                try {
270                    mCallback.revokeTrust();
271                } catch (RemoteException e) {
272                    onError("calling revokeTrust()");
273                }
274            }
275        }
276    }
277
278    /**
279     * Call to notify the system if the agent is ready to manage trust.
280     *
281     * This property is not persistent across recreating the service and defaults to false.
282     * Therefore this method is typically called when initializing the agent in {@link #onCreate}.
283     *
284     * @param managingTrust indicates if the agent would like to manage trust.
285     */
286    public final void setManagingTrust(boolean managingTrust) {
287        synchronized (mLock) {
288            if (mManagingTrust != managingTrust) {
289                mManagingTrust = managingTrust;
290                if (mCallback != null) {
291                    try {
292                        mCallback.setManagingTrust(managingTrust);
293                    } catch (RemoteException e) {
294                        onError("calling setManagingTrust()");
295                    }
296                }
297            }
298        }
299    }
300
301    @Override
302    public final IBinder onBind(Intent intent) {
303        if (DEBUG) Slog.v(TAG, "onBind() intent = " + intent);
304        return new TrustAgentServiceWrapper();
305    }
306
307    private final class TrustAgentServiceWrapper extends ITrustAgentService.Stub {
308        @Override /* Binder API */
309        public void onUnlockAttempt(boolean successful) {
310            mHandler.obtainMessage(MSG_UNLOCK_ATTEMPT, successful ? 1 : 0, 0).sendToTarget();
311        }
312
313        @Override /* Binder API */
314        public void onTrustTimeout() {
315            mHandler.sendEmptyMessage(MSG_TRUST_TIMEOUT);
316        }
317
318        @Override /* Binder API */
319        public void onConfigure(List<PersistableBundle> args, IBinder token) {
320            mHandler.obtainMessage(MSG_CONFIGURE, new ConfigurationData(args, token))
321                    .sendToTarget();
322        }
323
324        @Override
325        public void onDeviceLocked() throws RemoteException {
326            mHandler.obtainMessage(MSG_DEVICE_LOCKED).sendToTarget();
327        }
328
329        @Override
330        public void onDeviceUnlocked() throws RemoteException {
331            mHandler.obtainMessage(MSG_DEVICE_UNLOCKED).sendToTarget();
332        }
333
334        @Override /* Binder API */
335        public void setCallback(ITrustAgentServiceCallback callback) {
336            synchronized (mLock) {
337                mCallback = callback;
338                // The managingTrust property is false implicitly on the server-side, so we only
339                // need to set it here if the agent has decided to manage trust.
340                if (mManagingTrust) {
341                    try {
342                        mCallback.setManagingTrust(mManagingTrust);
343                    } catch (RemoteException e ) {
344                        onError("calling setManagingTrust()");
345                    }
346                }
347                if (mPendingGrantTrustTask != null) {
348                    mPendingGrantTrustTask.run();
349                    mPendingGrantTrustTask = null;
350                }
351            }
352        }
353    }
354
355}
356