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