1/*
2 * Copyright (C) 2010 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.app.admin;
18
19import android.annotation.SdkConstant;
20import android.annotation.SdkConstant.SdkConstantType;
21import android.app.Service;
22import android.content.BroadcastReceiver;
23import android.content.ComponentName;
24import android.content.Context;
25import android.content.Intent;
26import android.os.Bundle;
27
28/**
29 * Base class for implementing a device administration component.  This
30 * class provides a convenience for interpreting the raw intent actions
31 * that are sent by the system.
32 *
33 * <p>The callback methods, like the base
34 * {@link BroadcastReceiver#onReceive(Context, Intent) BroadcastReceiver.onReceive()}
35 * method, happen on the main thread of the process.  Thus long running
36 * operations must be done on another thread.  Note that because a receiver
37 * is done once returning from its receive function, such long-running operations
38 * should probably be done in a {@link Service}.
39 *
40 * <p>When publishing your DeviceAdmin subclass as a receiver, it must
41 * handle {@link #ACTION_DEVICE_ADMIN_ENABLED} and require the
42 * {@link android.Manifest.permission#BIND_DEVICE_ADMIN} permission.  A typical
43 * manifest entry would look like:</p>
44 *
45 * {@sample development/samples/ApiDemos/AndroidManifest.xml device_admin_declaration}
46 *
47 * <p>The meta-data referenced here provides addition information specific
48 * to the device administrator, as parsed by the {@link DeviceAdminInfo} class.
49 * A typical file would be:</p>
50 *
51 * {@sample development/samples/ApiDemos/res/xml/device_admin_sample.xml meta_data}
52 *
53 * <div class="special reference">
54 * <h3>Developer Guides</h3>
55 * <p>For more information about device administration, read the
56 * <a href="{@docRoot}guide/topics/admin/device-admin.html">Device Administration</a>
57 * developer guide.</p>
58 * </div>
59 */
60public class DeviceAdminReceiver extends BroadcastReceiver {
61    private static String TAG = "DevicePolicy";
62    private static boolean localLOGV = false;
63
64    /**
65     * This is the primary action that a device administrator must implement to be
66     * allowed to manage a device.  This will be set to the receiver
67     * when the user enables it for administration.  You will generally
68     * handle this in {@link DeviceAdminReceiver#onEnabled(Context, Intent)}.  To be
69     * supported, the receiver must also require the
70     * {@link android.Manifest.permission#BIND_DEVICE_ADMIN} permission so
71     * that other applications can not abuse it.
72     */
73    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
74    public static final String ACTION_DEVICE_ADMIN_ENABLED
75            = "android.app.action.DEVICE_ADMIN_ENABLED";
76
77    /**
78     * Action sent to a device administrator when the user has requested to
79     * disable it, but before this has actually been done.  This gives you
80     * a chance to supply a message to the user about the impact of
81     * disabling your admin, by setting the extra field
82     * {@link #EXTRA_DISABLE_WARNING} in the result Intent.  If not set,
83     * no warning will be displayed.  If set, the given text will be shown
84     * to the user before they disable your admin.
85     */
86    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
87    public static final String ACTION_DEVICE_ADMIN_DISABLE_REQUESTED
88            = "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED";
89
90    /**
91     * A CharSequence that can be shown to the user informing them of the
92     * impact of disabling your admin.
93     *
94     * @see #ACTION_DEVICE_ADMIN_DISABLE_REQUESTED
95     */
96    public static final String EXTRA_DISABLE_WARNING = "android.app.extra.DISABLE_WARNING";
97
98    /**
99     * Action sent to a device administrator when the user has disabled
100     * it.  Upon return, the application no longer has access to the
101     * protected device policy manager APIs.  You will generally
102     * handle this in {@link DeviceAdminReceiver#onDisabled(Context, Intent)}.  Note
103     * that this action will be
104     * sent the receiver regardless of whether it is explicitly listed in
105     * its intent filter.
106     */
107    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
108    public static final String ACTION_DEVICE_ADMIN_DISABLED
109            = "android.app.action.DEVICE_ADMIN_DISABLED";
110
111    /**
112     * Action sent to a device administrator when the user has changed the
113     * password of their device.  You can at this point check the characteristics
114     * of the new password with {@link DevicePolicyManager#isActivePasswordSufficient()
115     * DevicePolicyManager.isActivePasswordSufficient()}.
116     * You will generally
117     * handle this in {@link DeviceAdminReceiver#onPasswordChanged}.
118     *
119     * <p>The calling device admin must have requested
120     * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to receive
121     * this broadcast.
122     */
123    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
124    public static final String ACTION_PASSWORD_CHANGED
125            = "android.app.action.ACTION_PASSWORD_CHANGED";
126
127    /**
128     * Action sent to a device administrator when the user has failed at
129     * attempted to enter the password.  You can at this point check the
130     * number of failed password attempts there have been with
131     * {@link DevicePolicyManager#getCurrentFailedPasswordAttempts
132     * DevicePolicyManager.getCurrentFailedPasswordAttempts()}.  You will generally
133     * handle this in {@link DeviceAdminReceiver#onPasswordFailed}.
134     *
135     * <p>The calling device admin must have requested
136     * {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN} to receive
137     * this broadcast.
138     */
139    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
140    public static final String ACTION_PASSWORD_FAILED
141            = "android.app.action.ACTION_PASSWORD_FAILED";
142
143    /**
144     * Action sent to a device administrator when the user has successfully
145     * entered their password, after failing one or more times.
146     *
147     * <p>The calling device admin must have requested
148     * {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN} to receive
149     * this broadcast.
150     */
151    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
152    public static final String ACTION_PASSWORD_SUCCEEDED
153            = "android.app.action.ACTION_PASSWORD_SUCCEEDED";
154
155    /**
156     * Action periodically sent to a device administrator when the device password
157     * is expiring.
158     *
159     * <p>The calling device admin must have requested
160     * {@link DeviceAdminInfo#USES_POLICY_EXPIRE_PASSWORD} to receive
161     * this broadcast.
162     */
163    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
164    public static final String ACTION_PASSWORD_EXPIRING
165            = "android.app.action.ACTION_PASSWORD_EXPIRING";
166
167    /**
168     * Name under which a DevicePolicy component publishes information
169     * about itself.  This meta-data must reference an XML resource containing
170     * a device-admin tag.  XXX TO DO: describe syntax.
171     */
172    public static final String DEVICE_ADMIN_META_DATA = "android.app.device_admin";
173
174    private DevicePolicyManager mManager;
175    private ComponentName mWho;
176
177    /**
178     * Retrieve the DevicePolicyManager interface for this administrator to work
179     * with the system.
180     */
181    public DevicePolicyManager getManager(Context context) {
182        if (mManager != null) {
183            return mManager;
184        }
185        mManager = (DevicePolicyManager)context.getSystemService(
186                Context.DEVICE_POLICY_SERVICE);
187        return mManager;
188    }
189
190    /**
191     * Retrieve the ComponentName describing who this device administrator is, for
192     * use in {@link DevicePolicyManager} APIs that require the administrator to
193     * identify itself.
194     */
195    public ComponentName getWho(Context context) {
196        if (mWho != null) {
197            return mWho;
198        }
199        mWho = new ComponentName(context, getClass());
200        return mWho;
201    }
202
203    /**
204     * Called after the administrator is first enabled, as a result of
205     * receiving {@link #ACTION_DEVICE_ADMIN_ENABLED}.  At this point you
206     * can use {@link DevicePolicyManager} to set your desired policies.
207     * @param context The running context as per {@link #onReceive}.
208     * @param intent The received intent as per {@link #onReceive}.
209     */
210    public void onEnabled(Context context, Intent intent) {
211    }
212
213    /**
214     * Called when the user has asked to disable the administrator, as a result of
215     * receiving {@link #ACTION_DEVICE_ADMIN_DISABLE_REQUESTED}, giving you
216     * a chance to present a warning message to them.  The message is returned
217     * as the result; if null is returned (the default implementation), no
218     * message will be displayed.
219     * @param context The running context as per {@link #onReceive}.
220     * @param intent The received intent as per {@link #onReceive}.
221     * @return Return the warning message to display to the user before
222     * being disabled; if null is returned, no message is displayed.
223     */
224    public CharSequence onDisableRequested(Context context, Intent intent) {
225        return null;
226    }
227
228    /**
229     * Called prior to the administrator being disabled, as a result of
230     * receiving {@link #ACTION_DEVICE_ADMIN_DISABLED}.  Upon return, you
231     * can no longer use the protected parts of the {@link DevicePolicyManager}
232     * API.
233     * @param context The running context as per {@link #onReceive}.
234     * @param intent The received intent as per {@link #onReceive}.
235     */
236    public void onDisabled(Context context, Intent intent) {
237    }
238
239    /**
240     * Called after the user has changed their password, as a result of
241     * receiving {@link #ACTION_PASSWORD_CHANGED}.  At this point you
242     * can use {@link DevicePolicyManager#getCurrentFailedPasswordAttempts()
243     * DevicePolicyManager.getCurrentFailedPasswordAttempts()}
244     * to retrieve the active password characteristics.
245     * @param context The running context as per {@link #onReceive}.
246     * @param intent The received intent as per {@link #onReceive}.
247     */
248    public void onPasswordChanged(Context context, Intent intent) {
249    }
250
251    /**
252     * Called after the user has failed at entering their current password, as a result of
253     * receiving {@link #ACTION_PASSWORD_FAILED}.  At this point you
254     * can use {@link DevicePolicyManager} to retrieve the number of failed
255     * password attempts.
256     * @param context The running context as per {@link #onReceive}.
257     * @param intent The received intent as per {@link #onReceive}.
258     */
259    public void onPasswordFailed(Context context, Intent intent) {
260    }
261
262    /**
263     * Called after the user has succeeded at entering their current password,
264     * as a result of receiving {@link #ACTION_PASSWORD_SUCCEEDED}.  This will
265     * only be received the first time they succeed after having previously
266     * failed.
267     * @param context The running context as per {@link #onReceive}.
268     * @param intent The received intent as per {@link #onReceive}.
269     */
270    public void onPasswordSucceeded(Context context, Intent intent) {
271    }
272
273    /**
274     * Called periodically when the password is about to expire or has expired.  It will typically
275     * be called at these times: on device boot, once per day before the password expires,
276     * and at the time when the password expires.
277     *
278     * <p>If the password is not updated by the user, this method will continue to be called
279     * once per day until the password is changed or the device admin disables password expiration.
280     *
281     * <p>The admin will typically post a notification requesting the user to change their password
282     * in response to this call. The actual password expiration time can be obtained by calling
283     * {@link DevicePolicyManager#getPasswordExpiration(ComponentName) }
284     *
285     * <p>The admin should be sure to take down any notifications it posted in response to this call
286     * when it receives {@link DeviceAdminReceiver#onPasswordChanged(Context, Intent) }.
287     *
288     * @param context The running context as per {@link #onReceive}.
289     * @param intent The received intent as per {@link #onReceive}.
290     */
291    public void onPasswordExpiring(Context context, Intent intent) {
292    }
293
294    /**
295     * Intercept standard device administrator broadcasts.  Implementations
296     * should not override this method; it is better to implement the
297     * convenience callbacks for each action.
298     */
299    @Override
300    public void onReceive(Context context, Intent intent) {
301        String action = intent.getAction();
302        if (ACTION_PASSWORD_CHANGED.equals(action)) {
303            onPasswordChanged(context, intent);
304        } else if (ACTION_PASSWORD_FAILED.equals(action)) {
305            onPasswordFailed(context, intent);
306        } else if (ACTION_PASSWORD_SUCCEEDED.equals(action)) {
307            onPasswordSucceeded(context, intent);
308        } else if (ACTION_DEVICE_ADMIN_ENABLED.equals(action)) {
309            onEnabled(context, intent);
310        } else if (ACTION_DEVICE_ADMIN_DISABLE_REQUESTED.equals(action)) {
311            CharSequence res = onDisableRequested(context, intent);
312            if (res != null) {
313                Bundle extras = getResultExtras(true);
314                extras.putCharSequence(EXTRA_DISABLE_WARNING, res);
315            }
316        } else if (ACTION_DEVICE_ADMIN_DISABLED.equals(action)) {
317            onDisabled(context, intent);
318        } else if (ACTION_PASSWORD_EXPIRING.equals(action)) {
319            onPasswordExpiring(context, intent);
320        }
321    }
322}
323