@@ -27,6 +27,12 @@ page.title=Device Administration
2727 <li>{@link android.app.admin.DevicePolicyManager}</li>
2828 <li>{@link android.app.admin.DeviceAdminInfo}</li>
2929 </ol>
30+ <h2>Related samples</h2>
31+ <ol>
32+ <li><a
33+ href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.html">
34+ DeviceAdminSample</a></li>
35+ </ol>
3036</div>
3137</div>
3238
@@ -201,6 +207,16 @@ access data. The value can be between 1 and 60 minutes.</td> </tr>
201207<td>Specifies that the storage area should be encrypted, if the device supports it.
202208Introduced in Android 3.0.</td> </tr>
203209
210+ <tr>
211+ <td>Disable camera</td>
212+
213+ <td>Specifies that the camera should be disabled. Note that this doesn't have
214+ to be a permanent disabling. The camera can be enabled/disabled dynamically
215+ based on context, time, and so on. Introduced in Android 4.0.</td>
216+
217+ </tr>
218+
219+
204220</table>
205221
206222<h4>Other features</h4>
@@ -247,6 +263,7 @@ one of the last <em>n</em> passwords they previously used.</li>
247263locks.</li>
248264 <li>Make the device lock immediately.</li>
249265 <li>Wipe the device's data (that is, restore factory settings).</li>
266+ <li>Disable the camera.</li>
250267
251268</ul>
252269
@@ -280,46 +297,38 @@ intent, expressed in the manifest as an intent filter.</li>
280297 <li>A declaration of security policies used in metadata.</li>
281298</ul>
282299<p>Here is an excerpt from the Device Administration sample manifest:</p>
283- <pre><activity android:name=".app.DeviceAdminSample$Controller "
284- android:label="@string/activity_sample_device_admin">
285- <intent-filter>
286- <action android:name="android.intent.action.MAIN" />
287- <category android:name="android.intent.category.SAMPLE_CODE" />
288- </intent-filter>
300+ <pre><activity android:name=".app.DeviceAdminSample"
301+ android:label="@string/activity_sample_device_admin">
302+ <intent-filter>
303+ <action android:name="android.intent.action.MAIN" />
304+ <category android:name="android.intent.category.SAMPLE_CODE" />
305+ </intent-filter>
289306</activity>
290-
291- <receiver android:name=".app.DeviceAdminSample"
292- android:label="@string/sample_device_admin"
293- android:description="@string/sample_device_admin_description"
294- android:permission="android.permission.BIND_DEVICE_ADMIN">
295- <meta-data android:name="android.app.device_admin"
296- android:resource="@xml/device_admin_sample" />
297- <intent-filter>
298- <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
299- </intent-filter>
307+ <receiver android:name=".app.DeviceAdminSample$DeviceAdminSampleReceiver"
308+ android:label="@string/sample_device_admin"
309+ android:description="@string/sample_device_admin_description"
310+ android:permission="android.permission.BIND_DEVICE_ADMIN">
311+ <meta-data android:name="android.app.device_admin"
312+ android:resource="@xml/device_admin_sample" />
313+ <intent-filter>
314+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
315+ </intent-filter>
300316</receiver></pre>
301317
302318 <p>Note that:</p>
303319<ul>
304- <li>The activity in the sample application is an {@link android.app.Activity}
305- subclass called <code>Controller</code>. The syntax
306- <code>".app.DeviceAdminSample$Controller"</code> indicates that
307- <code>Controller</code> is an inner class that is nested inside the
308- <code>DeviceAdminSample</code> class. Note that an Activity does not need to be
309- an inner class; it just is in this example.</li>
310-
311320<li>The following attributes refer to string resources that for the sample application reside in
312321<code>ApiDemos/res/values/strings.xml</code>. For more information about resources, see
313322<a
314323href="{@docRoot}guide/topics/resources/index.html">Application Resources</a>.
315324<ul>
316- <li><code>android:label="@ string/activity_sample_device_admin"</code> refers to the
325+ <li><code>android:label="@ string/activity_sample_device_admin"</code> refers to the
317326user-readable label for the activity.</li>
318327
319- <li><code>android:label="@ string/sample_device_admin"</code> refers to the
328+ <li><code>android:label="@ string/sample_device_admin"</code> refers to the
320329user-readable label for the permission.</li>
321330
322- <li><code>android:description="@ string/sample_device_admin_description"</code> refers to
331+ <li><code>android:description="@ string/sample_device_admin_description"</code> refers to
323332the user-readable description of the permission. A descripton is typically longer and more
324333informative than
325334a label.</li>
@@ -357,6 +366,9 @@ android.app.admin.DeviceAdminInfo} class. Here are the contents of
357366 <reset-password />
358367 <force-lock />
359368 <wipe-data />
369+ <expire-password />
370+ <encrypted-storage />
371+ <disable-camera />
360372 </uses-policies>
361373</device-admin>
362374</pre>
@@ -401,33 +413,34 @@ simply displays a {@link android.widget.Toast} notification in response to parti
401413events. For example:</p>
402414<pre>public class DeviceAdminSample extends DeviceAdminReceiver {
403415
404- ...
416+ void showToast(Context context, String msg) {
417+ String status = context.getString(R.string.admin_receiver_status, msg);
418+ Toast.makeText(context, status, Toast.LENGTH_SHORT).show();
419+ }
420+
421+ @Override
422+ public void onEnabled(Context context, Intent intent) {
423+ showToast(context, context.getString(R.string.admin_receiver_status_enabled));
424+ }
425+
405426 @Override
406- public void onEnabled(Context context, Intent intent) {
407- showToast(context, "Sample Device Admin: enabled");
408- }
409-
410- @Override
411- public CharSequence onDisableRequested(Context context, Intent intent) {
412- return "This is an optional message to warn the user about disabling.";
413- }
414-
415- @Override
416- public void onDisabled(Context context, Intent intent) {
417- showToast(context, "Sample Device Admin: disabled");
418- }
419-
420- @Override
421- public void onPasswordChanged(Context context, Intent intent) {
422- showToast(context, "Sample Device Admin: pw changed");
423- }
424-
425- void showToast(Context context, CharSequence msg) {
426- Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
427+ public CharSequence onDisableRequested(Context context, Intent intent) {
428+ return context.getString(R.string.admin_receiver_status_disable_warning);
429+ }
430+
431+ @Override
432+ public void onDisabled(Context context, Intent intent) {
433+ showToast(context, context.getString(R.string.admin_receiver_status_disabled));
434+ }
435+
436+ @Override
437+ public void onPasswordChanged(Context context, Intent intent) {
438+ showToast(context, context.getString(R.string.admin_receiver_status_pw_changed));
427439 }
428440...
429441}</pre>
430442
443+
431444<h4 id="enabling">Enabling the application</h4>
432445<p>One of the major events a device admin application has to handle is the user
433446enabling the application. The user must explicitly enable the application for
@@ -438,43 +451,50 @@ get any of the application's benefits.</p>
438451action that triggers the {@link android.app.admin.DevicePolicyManager#ACTION_ADD_DEVICE_ADMIN}
439452intent. In the
440453sample application, this happens when the user clicks the <strong>Enable
441- Admin</strong> button . </p>
442- <p>When the user clicks the <strong>Enable Admin</strong> button , the display
443- changes to prompt the user to enable the device admin application, as shown in figure
454+ Admin</strong> checkbox . </p>
455+ <p>When the user clicks the <strong>Enable Admin</strong> checkbox , the display
456+ changes to prompt the user to activate the device admin application, as shown in figure
4444572.</p>
445458
446459<img src="{@docRoot}images/admin/device-admin-activate-prompt.png"/>
447460<p class="img-caption"><strong>Figure 2.</strong> Sample Application: Activating the Application</p>
448- <p>Below is the code that gets executed when the user clicks the <strong>Enable
449- Admin</strong> button shown in figure 1. </p>
450-
451- <pre> private OnClickListener mEnableListener = new OnClickListener() {
452- public void onClick(View v) {
453- // Launch the activity to have the user enable our admin.
454- Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
455- intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
456- mDeviceAdminSample);
457- intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION,
458- "Additional text explaining why this needs to be added.");
459- startActivityForResult(intent, RESULT_ENABLE);
460- }
461- };
462461
463- ...
464- // This code checks whether the device admin app was successfully enabled.
465- @Override
466- protected void onActivityResult(int requestCode, int resultCode, Intent data) {
467- switch (requestCode) {
468- case RESULT_ENABLE:
469- if (resultCode == Activity.RESULT_OK) {
470- Log.i("DeviceAdminSample", "Administration enabled!");
471- } else {
472- Log.i("DeviceAdminSample", "Administration enable FAILED!");
473- }
474- return;
475- }
476- super.onActivityResult(requestCode, resultCode, data);
477- }</pre>
462+ <p>Below is the code that gets executed when the user clicks the <strong>Enable Admin</strong> checkbox. This has the effect of triggering the
463+ {@link android.preference.Preference.OnPreferenceChangeListener#onPreferenceChange(android.preference.Preference, java.lang.Object) onPreferenceChange()}
464+ callback. This callback is invoked when the value of this {@link android.preference.Preference} has been changed by the user and is about to be set and/or persisted. If the user is enabling the application, the display
465+ changes to prompt the user to activate the device admin application, as shown in figure
466+ 2. Otherwise, the device admin application is disabled. </p>
467+
468+ <pre>@Override
469+ public boolean onPreferenceChange(Preference preference, Object newValue) {
470+ if (super.onPreferenceChange(preference, newValue)) {
471+ return true;
472+ }
473+ boolean value = (Boolean) newValue;
474+ if (preference == mEnableCheckbox) {
475+ if (value != mAdminActive) {
476+ if (value) {
477+ // Launch the activity to have the user enable our admin.
478+ Intent intent = new Intent(DevicePolicyManager.ACTION_ADD_DEVICE_ADMIN);
479+ intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN, mDeviceAdminSample);
480+ intent.putExtra(DevicePolicyManager.EXTRA_ADD_EXPLANATION,
481+ mActivity.getString(R.string.add_admin_extra_app_text));
482+ startActivityForResult(intent, REQUEST_CODE_ENABLE_ADMIN);
483+ // return false - don't update checkbox until we're really active
484+ return false;
485+ } else {
486+ mDPM.removeActiveAdmin(mDeviceAdminSample);
487+ enableDeviceCapabilitiesArea(false);
488+ mAdminActive = false;
489+ }
490+ }
491+ } else if (preference == mDisableCameraCheckbox) {
492+ mDPM.setCameraDisabled(mDeviceAdminSample, value);
493+ ...
494+ }
495+ return true;
496+ }</pre>
497+
478498
479499<p>The line
480500<code>intent.putExtra(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
@@ -489,18 +509,17 @@ active. To do this it uses the {@link android.app.admin.DevicePolicyManager} met
489509{@link android.app.admin.DevicePolicyManager#isAdminActive(android.content.ComponentName) isAdminActive()}. Notice that the {@link android.app.admin.DevicePolicyManager}
490510method {@link android.app.admin.DevicePolicyManager#isAdminActive(android.content.ComponentName) isAdminActive()} takes a {@link android.app.admin.DeviceAdminReceiver}
491511component as its argument:</p>
512+
492513<pre>
493514DevicePolicyManager mDPM;
494515...
495- boolean active = mDPM.isAdminActive(mDeviceAdminSample);
496- if (active) {
497- // Admin app is active, so do some admin stuff
498- ...
499- } else {
500- // do something else
516+ private boolean isActiveAdmin() {
517+ return mDPM.isAdminActive(mDeviceAdminSample);
501518}
502519</pre>
503520
521+
522+
504523<h3 id="admin_ops">Managing policies</h3>
505524<p>{@link android.app.admin.DevicePolicyManager} is a public class for managing policies
506525enforced on a device. {@link android.app.admin.DevicePolicyManager} manages policies for one
@@ -618,49 +637,6 @@ long pwExpiration;
618637...
619638mDPM.setPasswordExpirationTimeout(mDeviceAdminSample, pwExpiration);
620639</pre>
621-
622- <p>From the <a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/DeviceAdminSample.html"
623- >Device Administration API sample</a>, here is the code
624- that updates the password expiration status:</p>
625-
626- <pre>
627- DevicePolicyManager mDPM;
628- ComponentName mDeviceAdminSample;
629- private TextView mPasswordExpirationStatus;
630- ...
631- void updatePasswordExpirationStatus() {
632- boolean active = mDPM.isAdminActive(mDeviceAdminSample);
633- String statusText;
634- if (active) {
635- long now = System.currentTimeMillis();
636- // Query the DevicePolicyManager twice - first for the expiration values
637- // set by the sample app, and later, for the system values (which may be different
638- // if there is another administrator active.)
639- long expirationDate = mDPM.getPasswordExpiration(mDeviceAdminSample);
640- long mSecUntilExpiration = expirationDate - now;
641- if (mSecUntilExpiration >= 0) {
642- statusText = "Expiration in " + countdownString(mSecUntilExpiration);
643- } else {
644- statusText = "Expired " + countdownString(-mSecUntilExpiration) + " ago";
645- }
646-
647- // expirationTimeout is the cycle time between required password refresh
648- long expirationTimeout = mDPM.getPasswordExpirationTimeout(mDeviceAdminSample);
649- statusText += " / timeout period " + countdownString(expirationTimeout);
650-
651- // Now report the aggregate (global) expiration time
652- statusText += " / Aggregate ";
653- expirationDate = mDPM.getPasswordExpiration(null);
654- mSecUntilExpiration = expirationDate - now;
655- if (mSecUntilExpiration >= 0) {
656- statusText += "expiration in " + countdownString(mSecUntilExpiration);
657- } else {
658- statusText += "expired " + countdownString(-mSecUntilExpiration) + " ago";
659- }
660- } else {
661- statusText = "<inactive>";
662- }
663- mPasswordExpirationStatus.setText(statusText);</pre>
664640
665641<h5 id="history">Restrict password based on history</h5>
666642
@@ -718,6 +694,19 @@ mDPM.wipeData(0);</pre>
718694<p>The {@link android.app.admin.DevicePolicyManager#wipeData wipeData()} method takes as its parameter a bit mask of
719695additional options. Currently the value must be 0. </p>
720696
697+ <h4>Disable camera</h4>
698+ <p>Beginning with Android 4.0, you can disable the camera. Note that this doesn't have to be a permanent disabling. The camera can be enabled/disabled dynamically based on context, time, and so on. </p>
699+ <p>You control whether the camera is disabled by using the
700+ {@link android.app.admin.DevicePolicyManager#setCameraDisabled(android.content.ComponentName, boolean) setCameraDisabled()} method. For example, this snippet sets the camera to be enabled or disabled based on a checkbox setting:</p>
701+
702+ <pre>private CheckBoxPreference mDisableCameraCheckbox;
703+ DevicePolicyManager mDPM;
704+ ComponentName mDeviceAdminSample;
705+ ...
706+ mDPM.setCameraDisabled(mDeviceAdminSample, mDisableCameraCheckbox.isChecked());<br />
707+ </pre>
708+
709+
721710<h4 id=storage">Storage encryption</h4>
722711<p>Beginning with Android 3.0, you can use the
723712{@link android.app.admin.DevicePolicyManager#setStorageEncryption(android.content.ComponentName,boolean) setStorageEncryption()}
0 commit comments