More work on device admins:
- You can now show a dynamic message to the user when asking to have your DeviceAdmin added. - A DeviceAdmin can now provide a warning message that is displayed before a user disables it. - Better ordering (and text) of the policy warnings. - New API to set the maximum failed password attempts before the device wipes itself. - We now store the number of failed unlock attempts in persistent storage. - New managed dialog APIs that will be used by the settings app. Also a little bit of cleanup as I was working on this - removed the long unused MailboxNotAvailableException, fixed a java doc in Messenger.
This commit is contained in:
@@ -117,6 +117,7 @@ LOCAL_SRC_FILES += \
|
||||
core/java/android/os/IParentalControlCallback.aidl \
|
||||
core/java/android/os/IPermissionController.aidl \
|
||||
core/java/android/os/IPowerManager.aidl \
|
||||
core/java/android/os/IRemoteCallback.aidl \
|
||||
core/java/android/os/IVibratorService.aidl \
|
||||
core/java/android/service/wallpaper/IWallpaperConnection.aidl \
|
||||
core/java/android/service/wallpaper/IWallpaperEngine.aidl \
|
||||
|
||||
110
api/current.xml
110
api/current.xml
@@ -16169,6 +16169,19 @@
|
||||
visibility="public"
|
||||
>
|
||||
</method>
|
||||
<method name="onCreateDialog"
|
||||
return="android.app.Dialog"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="deprecated"
|
||||
visibility="protected"
|
||||
>
|
||||
<parameter name="id" type="int">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="onCreateDialog"
|
||||
return="android.app.Dialog"
|
||||
abstract="false"
|
||||
@@ -16181,6 +16194,8 @@
|
||||
>
|
||||
<parameter name="id" type="int">
|
||||
</parameter>
|
||||
<parameter name="args" type="android.os.Bundle">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="onCreateOptionsMenu"
|
||||
return="boolean"
|
||||
@@ -16469,6 +16484,21 @@
|
||||
visibility="protected"
|
||||
>
|
||||
</method>
|
||||
<method name="onPrepareDialog"
|
||||
return="void"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="deprecated"
|
||||
visibility="protected"
|
||||
>
|
||||
<parameter name="id" type="int">
|
||||
</parameter>
|
||||
<parameter name="dialog" type="android.app.Dialog">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="onPrepareDialog"
|
||||
return="void"
|
||||
abstract="false"
|
||||
@@ -16483,6 +16513,8 @@
|
||||
</parameter>
|
||||
<parameter name="dialog" type="android.app.Dialog">
|
||||
</parameter>
|
||||
<parameter name="args" type="android.os.Bundle">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="onPrepareOptionsMenu"
|
||||
return="boolean"
|
||||
@@ -17110,6 +17142,21 @@
|
||||
<parameter name="id" type="int">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="showDialog"
|
||||
return="boolean"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="true"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="id" type="int">
|
||||
</parameter>
|
||||
<parameter name="args" type="android.os.Bundle">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="startActivityForResult"
|
||||
return="void"
|
||||
abstract="false"
|
||||
@@ -19895,6 +19942,21 @@
|
||||
<parameter name="context" type="android.content.Context">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="onDisableRequested"
|
||||
return="java.lang.CharSequence"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="context" type="android.content.Context">
|
||||
</parameter>
|
||||
<parameter name="intent" type="android.content.Intent">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="onDisabled"
|
||||
return="void"
|
||||
abstract="false"
|
||||
@@ -19996,6 +20058,17 @@
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
<field name="ACTION_DEVICE_ADMIN_DISABLE_REQUESTED"
|
||||
type="java.lang.String"
|
||||
transient="false"
|
||||
volatile="false"
|
||||
value=""android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED""
|
||||
static="true"
|
||||
final="true"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
<field name="ACTION_DEVICE_ADMIN_ENABLED"
|
||||
type="java.lang.String"
|
||||
transient="false"
|
||||
@@ -20051,6 +20124,17 @@
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
<field name="EXTRA_DISABLE_WARNING"
|
||||
type="java.lang.String"
|
||||
transient="false"
|
||||
volatile="false"
|
||||
value=""android.app.extra.DISABLE_WARNING""
|
||||
static="true"
|
||||
final="true"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
</class>
|
||||
<class name="DeviceAdminInfo"
|
||||
extends="java.lang.Object"
|
||||
@@ -20420,6 +20504,21 @@
|
||||
<parameter name="password" type="java.lang.String">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="setMaximumFailedPasswordsForWipe"
|
||||
return="void"
|
||||
abstract="false"
|
||||
native="false"
|
||||
synchronized="false"
|
||||
static="false"
|
||||
final="false"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
<parameter name="admin" type="android.content.ComponentName">
|
||||
</parameter>
|
||||
<parameter name="num" type="int">
|
||||
</parameter>
|
||||
</method>
|
||||
<method name="setMaximumTimeToLock"
|
||||
return="void"
|
||||
abstract="false"
|
||||
@@ -20500,6 +20599,17 @@
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
<field name="EXTRA_ADD_EXPLANATION"
|
||||
type="java.lang.String"
|
||||
transient="false"
|
||||
volatile="false"
|
||||
value=""android.app.extra.ADD_EXPLANATION""
|
||||
static="true"
|
||||
final="true"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
<field name="EXTRA_DEVICE_ADMIN"
|
||||
type="java.lang.String"
|
||||
transient="false"
|
||||
|
||||
@@ -609,8 +609,13 @@ public class Activity extends ContextThemeWrapper
|
||||
private static final String SAVED_DIALOG_IDS_KEY = "android:savedDialogIds";
|
||||
private static final String SAVED_DIALOGS_TAG = "android:savedDialogs";
|
||||
private static final String SAVED_DIALOG_KEY_PREFIX = "android:dialog_";
|
||||
private static final String SAVED_DIALOG_ARGS_KEY_PREFIX = "android:dialog_args_";
|
||||
|
||||
private SparseArray<Dialog> mManagedDialogs;
|
||||
private static class ManagedDialog {
|
||||
Dialog mDialog;
|
||||
Bundle mArgs;
|
||||
}
|
||||
private SparseArray<ManagedDialog> mManagedDialogs;
|
||||
|
||||
// set by the thread after the constructor and before onCreate(Bundle savedInstanceState) is called.
|
||||
private Instrumentation mInstrumentation;
|
||||
@@ -851,35 +856,41 @@ public class Activity extends ContextThemeWrapper
|
||||
|
||||
final int[] ids = b.getIntArray(SAVED_DIALOG_IDS_KEY);
|
||||
final int numDialogs = ids.length;
|
||||
mManagedDialogs = new SparseArray<Dialog>(numDialogs);
|
||||
mManagedDialogs = new SparseArray<ManagedDialog>(numDialogs);
|
||||
for (int i = 0; i < numDialogs; i++) {
|
||||
final Integer dialogId = ids[i];
|
||||
Bundle dialogState = b.getBundle(savedDialogKeyFor(dialogId));
|
||||
if (dialogState != null) {
|
||||
// Calling onRestoreInstanceState() below will invoke dispatchOnCreate
|
||||
// so tell createDialog() not to do it, otherwise we get an exception
|
||||
final Dialog dialog = createDialog(dialogId, dialogState);
|
||||
mManagedDialogs.put(dialogId, dialog);
|
||||
onPrepareDialog(dialogId, dialog);
|
||||
dialog.onRestoreInstanceState(dialogState);
|
||||
final ManagedDialog md = new ManagedDialog();
|
||||
md.mArgs = b.getBundle(savedDialogArgsKeyFor(dialogId));
|
||||
md.mDialog = createDialog(dialogId, dialogState, md.mArgs);
|
||||
if (md.mDialog != null) {
|
||||
mManagedDialogs.put(dialogId, md);
|
||||
onPrepareDialog(dialogId, md.mDialog, md.mArgs);
|
||||
md.mDialog.onRestoreInstanceState(dialogState);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Dialog createDialog(Integer dialogId, Bundle state) {
|
||||
final Dialog dialog = onCreateDialog(dialogId);
|
||||
private Dialog createDialog(Integer dialogId, Bundle state, Bundle args) {
|
||||
final Dialog dialog = onCreateDialog(dialogId, args);
|
||||
if (dialog == null) {
|
||||
throw new IllegalArgumentException("Activity#onCreateDialog did "
|
||||
+ "not create a dialog for id " + dialogId);
|
||||
return null;
|
||||
}
|
||||
dialog.dispatchOnCreate(state);
|
||||
return dialog;
|
||||
}
|
||||
|
||||
private String savedDialogKeyFor(int key) {
|
||||
private static String savedDialogKeyFor(int key) {
|
||||
return SAVED_DIALOG_KEY_PREFIX + key;
|
||||
}
|
||||
|
||||
private static String savedDialogArgsKeyFor(int key) {
|
||||
return SAVED_DIALOG_ARGS_KEY_PREFIX + key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when activity start-up is complete (after {@link #onStart}
|
||||
@@ -1096,8 +1107,11 @@ public class Activity extends ContextThemeWrapper
|
||||
for (int i = 0; i < numDialogs; i++) {
|
||||
final int key = mManagedDialogs.keyAt(i);
|
||||
ids[i] = key;
|
||||
final Dialog dialog = mManagedDialogs.valueAt(i);
|
||||
dialogState.putBundle(savedDialogKeyFor(key), dialog.onSaveInstanceState());
|
||||
final ManagedDialog md = mManagedDialogs.valueAt(i);
|
||||
dialogState.putBundle(savedDialogKeyFor(key), md.mDialog.onSaveInstanceState());
|
||||
if (md.mArgs != null) {
|
||||
dialogState.putBundle(savedDialogArgsKeyFor(key), md.mArgs);
|
||||
}
|
||||
}
|
||||
|
||||
dialogState.putIntArray(SAVED_DIALOG_IDS_KEY, ids);
|
||||
@@ -1283,14 +1297,14 @@ public class Activity extends ContextThemeWrapper
|
||||
|
||||
// dismiss any dialogs we are managing.
|
||||
if (mManagedDialogs != null) {
|
||||
|
||||
final int numDialogs = mManagedDialogs.size();
|
||||
for (int i = 0; i < numDialogs; i++) {
|
||||
final Dialog dialog = mManagedDialogs.valueAt(i);
|
||||
if (dialog.isShowing()) {
|
||||
dialog.dismiss();
|
||||
final ManagedDialog md = mManagedDialogs.valueAt(i);
|
||||
if (md.mDialog.isShowing()) {
|
||||
md.mDialog.dismiss();
|
||||
}
|
||||
}
|
||||
mManagedDialogs = null;
|
||||
}
|
||||
|
||||
// close any cursors we are managing.
|
||||
@@ -1301,6 +1315,7 @@ public class Activity extends ContextThemeWrapper
|
||||
c.mCursor.close();
|
||||
}
|
||||
}
|
||||
mManagedCursors.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2411,36 +2426,57 @@ public class Activity extends ContextThemeWrapper
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for creating dialogs that are managed (saved and restored) for you
|
||||
* by the activity.
|
||||
*
|
||||
* If you use {@link #showDialog(int)}, the activity will call through to
|
||||
* this method the first time, and hang onto it thereafter. Any dialog
|
||||
* that is created by this method will automatically be saved and restored
|
||||
* for you, including whether it is showing.
|
||||
*
|
||||
* If you would like the activity to manage the saving and restoring dialogs
|
||||
* for you, you should override this method and handle any ids that are
|
||||
* passed to {@link #showDialog}.
|
||||
*
|
||||
* If you would like an opportunity to prepare your dialog before it is shown,
|
||||
* override {@link #onPrepareDialog(int, Dialog)}.
|
||||
*
|
||||
* @param id The id of the dialog.
|
||||
* @return The dialog
|
||||
*
|
||||
* @see #onPrepareDialog(int, Dialog)
|
||||
* @see #showDialog(int)
|
||||
* @see #dismissDialog(int)
|
||||
* @see #removeDialog(int)
|
||||
* @deprecated Old no-arguments version of {@link #onCreateDialog(int, Bundle)}.
|
||||
*/
|
||||
@Deprecated
|
||||
protected Dialog onCreateDialog(int id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for creating dialogs that are managed (saved and restored) for you
|
||||
* by the activity. The default implementation calls through to
|
||||
* {@link #onCreateDialog(int)} for compatibility.
|
||||
*
|
||||
* <p>If you use {@link #showDialog(int)}, the activity will call through to
|
||||
* this method the first time, and hang onto it thereafter. Any dialog
|
||||
* that is created by this method will automatically be saved and restored
|
||||
* for you, including whether it is showing.
|
||||
*
|
||||
* <p>If you would like the activity to manage saving and restoring dialogs
|
||||
* for you, you should override this method and handle any ids that are
|
||||
* passed to {@link #showDialog}.
|
||||
*
|
||||
* <p>If you would like an opportunity to prepare your dialog before it is shown,
|
||||
* override {@link #onPrepareDialog(int, Dialog, Bundle)}.
|
||||
*
|
||||
* @param id The id of the dialog.
|
||||
* @param args The dialog arguments provided to {@link #showDialog(int, Bundle)}.
|
||||
* @return The dialog. If you return null, the dialog will not be created.
|
||||
*
|
||||
* @see #onPrepareDialog(int, Dialog, Bundle)
|
||||
* @see #showDialog(int, Bundle)
|
||||
* @see #dismissDialog(int)
|
||||
* @see #removeDialog(int)
|
||||
*/
|
||||
protected Dialog onCreateDialog(int id, Bundle args) {
|
||||
return onCreateDialog(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Old no-arguments version of
|
||||
* {@link #onPrepareDialog(int, Dialog, Bundle)}.
|
||||
*/
|
||||
@Deprecated
|
||||
protected void onPrepareDialog(int id, Dialog dialog) {
|
||||
dialog.setOwnerActivity(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides an opportunity to prepare a managed dialog before it is being
|
||||
* shown.
|
||||
* shown. The default implementation calls through to
|
||||
* {@link #onPrepareDialog(int, Dialog)} for compatibility.
|
||||
*
|
||||
* <p>
|
||||
* Override this if you need to update a managed dialog based on the state
|
||||
* of the application each time it is shown. For example, a time picker
|
||||
@@ -2450,43 +2486,66 @@ public class Activity extends ContextThemeWrapper
|
||||
*
|
||||
* @param id The id of the managed dialog.
|
||||
* @param dialog The dialog.
|
||||
* @see #onCreateDialog(int)
|
||||
* @param args The dialog arguments provided to {@link #showDialog(int, Bundle)}.
|
||||
* @see #onCreateDialog(int, Bundle)
|
||||
* @see #showDialog(int)
|
||||
* @see #dismissDialog(int)
|
||||
* @see #removeDialog(int)
|
||||
*/
|
||||
protected void onPrepareDialog(int id, Dialog dialog) {
|
||||
dialog.setOwnerActivity(this);
|
||||
protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
|
||||
onPrepareDialog(id, dialog);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a dialog managed by this activity. A call to {@link #onCreateDialog(int)}
|
||||
* Simple version of {@link #showDialog(int, Bundle)} that does not
|
||||
* take any arguments. Simply calls {@link #showDialog(int, Bundle)}
|
||||
* with null arguments.
|
||||
*/
|
||||
public final void showDialog(int id) {
|
||||
showDialog(id, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a dialog managed by this activity. A call to {@link #onCreateDialog(int, Bundle)}
|
||||
* will be made with the same id the first time this is called for a given
|
||||
* id. From thereafter, the dialog will be automatically saved and restored.
|
||||
*
|
||||
* Each time a dialog is shown, {@link #onPrepareDialog(int, Dialog)} will
|
||||
* <p>Each time a dialog is shown, {@link #onPrepareDialog(int, Dialog, Bundle)} will
|
||||
* be made to provide an opportunity to do any timely preparation.
|
||||
*
|
||||
* @param id The id of the managed dialog.
|
||||
*
|
||||
* @param args Arguments to pass through to the dialog. These will be saved
|
||||
* and restored for you. Note that if the dialog is already created,
|
||||
* {@link #onCreateDialog(int, Bundle)} will not be called with the new
|
||||
* arguments but {@link #onPrepareDialog(int, Dialog, Bundle)} will be.
|
||||
* If you need to rebuild the dialog, call {@link #removeDialog(int)}<7D>first.
|
||||
* @return Returns true if the Dialog was created; false is returned if
|
||||
* it is not created because {@link #onCreateDialog(int, Bundle)} returns false.
|
||||
*
|
||||
* @see Dialog
|
||||
* @see #onCreateDialog(int)
|
||||
* @see #onPrepareDialog(int, Dialog)
|
||||
* @see #onCreateDialog(int, Bundle)
|
||||
* @see #onPrepareDialog(int, Dialog, Bundle)
|
||||
* @see #dismissDialog(int)
|
||||
* @see #removeDialog(int)
|
||||
*/
|
||||
public final void showDialog(int id) {
|
||||
public final boolean showDialog(int id, Bundle args) {
|
||||
if (mManagedDialogs == null) {
|
||||
mManagedDialogs = new SparseArray<Dialog>();
|
||||
mManagedDialogs = new SparseArray<ManagedDialog>();
|
||||
}
|
||||
Dialog dialog = mManagedDialogs.get(id);
|
||||
if (dialog == null) {
|
||||
dialog = createDialog(id, null);
|
||||
mManagedDialogs.put(id, dialog);
|
||||
ManagedDialog md = mManagedDialogs.get(id);
|
||||
if (md == null) {
|
||||
md = new ManagedDialog();
|
||||
md.mDialog = createDialog(id, null, args);
|
||||
if (md.mDialog == null) {
|
||||
return false;
|
||||
}
|
||||
mManagedDialogs.put(id, md);
|
||||
}
|
||||
|
||||
onPrepareDialog(id, dialog);
|
||||
dialog.show();
|
||||
md.mArgs = args;
|
||||
onPrepareDialog(id, md.mDialog, args);
|
||||
md.mDialog.show();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2497,21 +2556,21 @@ public class Activity extends ContextThemeWrapper
|
||||
* @throws IllegalArgumentException if the id was not previously shown via
|
||||
* {@link #showDialog(int)}.
|
||||
*
|
||||
* @see #onCreateDialog(int)
|
||||
* @see #onPrepareDialog(int, Dialog)
|
||||
* @see #onCreateDialog(int, Bundle)
|
||||
* @see #onPrepareDialog(int, Dialog, Bundle)
|
||||
* @see #showDialog(int)
|
||||
* @see #removeDialog(int)
|
||||
*/
|
||||
public final void dismissDialog(int id) {
|
||||
if (mManagedDialogs == null) {
|
||||
throw missingDialog(id);
|
||||
|
||||
}
|
||||
final Dialog dialog = mManagedDialogs.get(id);
|
||||
if (dialog == null) {
|
||||
|
||||
final ManagedDialog md = mManagedDialogs.get(id);
|
||||
if (md == null) {
|
||||
throw missingDialog(id);
|
||||
}
|
||||
dialog.dismiss();
|
||||
md.mDialog.dismiss();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2527,28 +2586,27 @@ public class Activity extends ContextThemeWrapper
|
||||
* Removes any internal references to a dialog managed by this Activity.
|
||||
* If the dialog is showing, it will dismiss it as part of the clean up.
|
||||
*
|
||||
* This can be useful if you know that you will never show a dialog again and
|
||||
* <p>This can be useful if you know that you will never show a dialog again and
|
||||
* want to avoid the overhead of saving and restoring it in the future.
|
||||
*
|
||||
* @param id The id of the managed dialog.
|
||||
*
|
||||
* @see #onCreateDialog(int)
|
||||
* @see #onPrepareDialog(int, Dialog)
|
||||
* @see #onCreateDialog(int, Bundle)
|
||||
* @see #onPrepareDialog(int, Dialog, Bundle)
|
||||
* @see #showDialog(int)
|
||||
* @see #dismissDialog(int)
|
||||
*/
|
||||
public final void removeDialog(int id) {
|
||||
|
||||
if (mManagedDialogs == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Dialog dialog = mManagedDialogs.get(id);
|
||||
if (dialog == null) {
|
||||
final ManagedDialog md = mManagedDialogs.get(id);
|
||||
if (md == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
dialog.dismiss();
|
||||
md.mDialog.dismiss();
|
||||
mManagedDialogs.remove(id);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
/**
|
||||
* Base class for implementing a device administration component. This
|
||||
@@ -61,6 +62,27 @@ public class DeviceAdmin extends BroadcastReceiver {
|
||||
public static final String ACTION_DEVICE_ADMIN_ENABLED
|
||||
= "android.app.action.DEVICE_ADMIN_ENABLED";
|
||||
|
||||
/**
|
||||
* Action sent to a device administrator when the user has requested to
|
||||
* disable it, but before this has actually been done. This gives you
|
||||
* a chance to supply a message to the user about the impact of
|
||||
* disabling your admin, by setting the extra field
|
||||
* {@link #EXTRA_DISABLE_WARNING} in the result Intent. If not set,
|
||||
* no warning will be displayed. If set, the given text will be shown
|
||||
* to the user before they disable your admin.
|
||||
*/
|
||||
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
|
||||
public static final String ACTION_DEVICE_ADMIN_DISABLE_REQUESTED
|
||||
= "android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED";
|
||||
|
||||
/**
|
||||
* A CharSequence that can be shown to the user informing them of the
|
||||
* impact of disabling your admin.
|
||||
*
|
||||
* @see #ACTION_DEVICE_ADMIN_DISABLE_REQUESTED
|
||||
*/
|
||||
public static final String EXTRA_DISABLE_WARNING = "android.app.extra.DISABLE_WARNING";
|
||||
|
||||
/**
|
||||
* Action sent to a device administrator when the user has disabled
|
||||
* it. Upon return, the application no longer has access to the
|
||||
@@ -165,6 +187,21 @@ public class DeviceAdmin extends BroadcastReceiver {
|
||||
public void onEnabled(Context context, Intent intent) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the user has asked to disable the administrator, as a result of
|
||||
* receiving {@link #ACTION_DEVICE_ADMIN_DISABLE_REQUESTED}, giving you
|
||||
* a chance to present a warning message to them. The message is returned
|
||||
* as the result; if null is returned (the default implementation), no
|
||||
* message will be displayed.
|
||||
* @param context The running context as per {@link #onReceive}.
|
||||
* @param intent The received intent as per {@link #onReceive}.
|
||||
* @return Return the warning message to display to the user before
|
||||
* being disabled; if null is returned, no message is displayed.
|
||||
*/
|
||||
public CharSequence onDisableRequested(Context context, Intent intent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called prior to the administrator being disabled, as a result of
|
||||
* receiving {@link #ACTION_DEVICE_ADMIN_DISABLED}. Upon return, you
|
||||
@@ -226,6 +263,12 @@ public class DeviceAdmin extends BroadcastReceiver {
|
||||
onPasswordSucceeded(context, intent);
|
||||
} else if (ACTION_DEVICE_ADMIN_ENABLED.equals(action)) {
|
||||
onEnabled(context, intent);
|
||||
} else if (ACTION_DEVICE_ADMIN_DISABLE_REQUESTED.equals(action)) {
|
||||
CharSequence res = onDisableRequested(context, intent);
|
||||
if (res != null) {
|
||||
Bundle extras = getResultExtras(true);
|
||||
extras.putCharSequence(EXTRA_DISABLE_WARNING, res);
|
||||
}
|
||||
} else if (ACTION_DEVICE_ADMIN_DISABLED.equals(action)) {
|
||||
onDisabled(context, intent);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ package android.app;
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
|
||||
import android.R;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
@@ -111,48 +110,47 @@ public final class DeviceAdminInfo implements Parcelable {
|
||||
|
||||
/** @hide */
|
||||
public static class PolicyInfo {
|
||||
public final int ident;
|
||||
final public String tag;
|
||||
final public int label;
|
||||
final public int description;
|
||||
|
||||
public PolicyInfo(String tagIn, int labelIn, int descriptionIn) {
|
||||
public PolicyInfo(int identIn, String tagIn, int labelIn, int descriptionIn) {
|
||||
ident = identIn;
|
||||
tag = tagIn;
|
||||
label = labelIn;
|
||||
description = descriptionIn;
|
||||
}
|
||||
}
|
||||
|
||||
static ArrayList<PolicyInfo> sPoliciesDisplayOrder = new ArrayList<PolicyInfo>();
|
||||
static HashMap<String, Integer> sKnownPolicies = new HashMap<String, Integer>();
|
||||
static SparseArray<PolicyInfo> sRevKnownPolicies = new SparseArray<PolicyInfo>();
|
||||
|
||||
static {
|
||||
sRevKnownPolicies.put(USES_POLICY_LIMIT_PASSWORD,
|
||||
new PolicyInfo("limit-password",
|
||||
com.android.internal.R.string.policylab_limitPassword,
|
||||
com.android.internal.R.string.policydesc_limitPassword));
|
||||
sRevKnownPolicies.put(USES_POLICY_WATCH_LOGIN,
|
||||
new PolicyInfo("watch-login",
|
||||
com.android.internal.R.string.policylab_watchLogin,
|
||||
com.android.internal.R.string.policydesc_watchLogin));
|
||||
sRevKnownPolicies.put(USES_POLICY_RESET_PASSWORD,
|
||||
new PolicyInfo("reset-password",
|
||||
com.android.internal.R.string.policylab_resetPassword,
|
||||
com.android.internal.R.string.policydesc_resetPassword));
|
||||
sRevKnownPolicies.put(USES_POLICY_LIMIT_UNLOCK,
|
||||
new PolicyInfo("limit-unlock",
|
||||
com.android.internal.R.string.policylab_limitUnlock,
|
||||
com.android.internal.R.string.policydesc_limitUnlock));
|
||||
sRevKnownPolicies.put(USES_POLICY_FORCE_LOCK,
|
||||
new PolicyInfo("force-lock",
|
||||
com.android.internal.R.string.policylab_forceLock,
|
||||
com.android.internal.R.string.policydesc_forceLock));
|
||||
sRevKnownPolicies.put(USES_POLICY_WIPE_DATA,
|
||||
new PolicyInfo("wipe-data",
|
||||
com.android.internal.R.string.policylab_wipeData,
|
||||
com.android.internal.R.string.policydesc_wipeData));
|
||||
for (int i=0; i<sRevKnownPolicies.size(); i++) {
|
||||
sKnownPolicies.put(sRevKnownPolicies.valueAt(i).tag,
|
||||
sRevKnownPolicies.keyAt(i));
|
||||
sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_WIPE_DATA, "wipe-data",
|
||||
com.android.internal.R.string.policylab_wipeData,
|
||||
com.android.internal.R.string.policydesc_wipeData));
|
||||
sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_RESET_PASSWORD, "reset-password",
|
||||
com.android.internal.R.string.policylab_resetPassword,
|
||||
com.android.internal.R.string.policydesc_resetPassword));
|
||||
sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_LIMIT_PASSWORD, "limit-password",
|
||||
com.android.internal.R.string.policylab_limitPassword,
|
||||
com.android.internal.R.string.policydesc_limitPassword));
|
||||
sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_WATCH_LOGIN, "watch-login",
|
||||
com.android.internal.R.string.policylab_watchLogin,
|
||||
com.android.internal.R.string.policydesc_watchLogin));
|
||||
sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_LIMIT_UNLOCK, "limit-unlock",
|
||||
com.android.internal.R.string.policylab_limitUnlock,
|
||||
com.android.internal.R.string.policydesc_limitUnlock));
|
||||
sPoliciesDisplayOrder.add(new PolicyInfo(USES_POLICY_FORCE_LOCK, "force-lock",
|
||||
com.android.internal.R.string.policylab_forceLock,
|
||||
com.android.internal.R.string.policydesc_forceLock));
|
||||
|
||||
for (int i=0; i<sPoliciesDisplayOrder.size(); i++) {
|
||||
PolicyInfo pi = sPoliciesDisplayOrder.get(i);
|
||||
sRevKnownPolicies.put(pi.ident, pi);
|
||||
sKnownPolicies.put(pi.tag, pi.ident);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,10 +333,10 @@ public final class DeviceAdminInfo implements Parcelable {
|
||||
/** @hide */
|
||||
public ArrayList<PolicyInfo> getUsedPolicies() {
|
||||
ArrayList<PolicyInfo> res = new ArrayList<PolicyInfo>();
|
||||
for (int i=0; i<sRevKnownPolicies.size(); i++) {
|
||||
int ident = sRevKnownPolicies.keyAt(i);
|
||||
if (usesPolicy(ident)) {
|
||||
res.add(sRevKnownPolicies.valueAt(i));
|
||||
for (int i=0; i<sPoliciesDisplayOrder.size(); i++) {
|
||||
PolicyInfo pi = sPoliciesDisplayOrder.get(i);
|
||||
if (usesPolicy(pi.ident)) {
|
||||
res.add(pi);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
|
||||
@@ -26,6 +26,7 @@ import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.Handler;
|
||||
import android.os.RemoteCallback;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.util.Log;
|
||||
@@ -43,8 +44,9 @@ public class DevicePolicyManager {
|
||||
private static boolean localLOGV = DEBUG || android.util.Config.LOGV;
|
||||
|
||||
private final Context mContext;
|
||||
private final Handler mHandler;
|
||||
private final IDevicePolicyManager mService;
|
||||
|
||||
private final Handler mHandler;
|
||||
|
||||
/*package*/ DevicePolicyManager(Context context, Handler handler) {
|
||||
mContext = context;
|
||||
@@ -60,6 +62,10 @@ public class DevicePolicyManager {
|
||||
* bring the user through adding the device administrator to the system (or
|
||||
* allowing them to reject it).
|
||||
*
|
||||
* <p>You can optionally include the {@link #EXTRA_ADD_EXPLANATION}
|
||||
* field to provide the user with additional explanation (in addition
|
||||
* to your component's description) about what is being added.
|
||||
*
|
||||
* <p>Note: the current platform can only have one device administrator
|
||||
* active at a time. If you make this request while there is already
|
||||
* an active administrator, this new request will be canceled automatically.
|
||||
@@ -75,6 +81,14 @@ public class DevicePolicyManager {
|
||||
*/
|
||||
public static final String EXTRA_DEVICE_ADMIN = "android.app.extra.DEVICE_ADMIN";
|
||||
|
||||
/**
|
||||
* An optional CharSequence providing additional explanation for why the
|
||||
* admin is being added.
|
||||
*
|
||||
* @see #ACTION_ADD_DEVICE_ADMIN
|
||||
*/
|
||||
public static final String EXTRA_ADD_EXPLANATION = "android.app.extra.ADD_EXPLANATION";
|
||||
|
||||
/**
|
||||
* Activity action: have the user enter a new password. This activity
|
||||
* should be launched after using {@link #setPasswordMode(ComponentName, int)}
|
||||
@@ -284,6 +298,29 @@ public class DevicePolicyManager {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum number of failed password attempts that are allowed
|
||||
* before the device wipes its data. This is convenience for implementing
|
||||
* the corresponding functionality with a combination of watching failed
|
||||
* password attempts and calling {@link #wipeData} upon reaching a certain
|
||||
* count, and as such requires that you request both
|
||||
* {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN} and
|
||||
* {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA}}.
|
||||
*
|
||||
* @param admin Which {@link DeviceAdmin} this request is associated with.
|
||||
* @param num The number of failed password attempts at which point the
|
||||
* device will wipe its data.
|
||||
*/
|
||||
public void setMaximumFailedPasswordsForWipe(ComponentName admin, int num) {
|
||||
if (mService != null) {
|
||||
try {
|
||||
mService.setMaximumFailedPasswordsForWipe(admin, num);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed talking with device policy service", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Force a new password on the user. This takes effect immediately. The
|
||||
* given password must meet the current password minimum length constraint
|
||||
@@ -448,6 +485,19 @@ public class DevicePolicyManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public void getRemoveWarning(ComponentName admin, RemoteCallback result) {
|
||||
if (mService != null) {
|
||||
try {
|
||||
mService.getRemoveWarning(admin, result);
|
||||
} catch (RemoteException e) {
|
||||
Log.w(TAG, "Failed talking with device policy service", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
package android.app;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.os.RemoteCallback;
|
||||
|
||||
/**
|
||||
* Internal IPC interface to the device policy service.
|
||||
@@ -32,6 +33,7 @@ interface IDevicePolicyManager {
|
||||
|
||||
boolean isActivePasswordSufficient();
|
||||
int getCurrentFailedPasswordAttempts();
|
||||
void setMaximumFailedPasswordsForWipe(in ComponentName admin, int num);
|
||||
|
||||
boolean resetPassword(String password);
|
||||
|
||||
@@ -44,6 +46,7 @@ interface IDevicePolicyManager {
|
||||
|
||||
void setActiveAdmin(in ComponentName policyReceiver);
|
||||
ComponentName getActiveAdmin();
|
||||
void getRemoveWarning(in ComponentName policyReceiver, in RemoteCallback result);
|
||||
void removeActiveAdmin(in ComponentName policyReceiver);
|
||||
|
||||
void setActivePasswordState(int mode, int length);
|
||||
|
||||
25
core/java/android/os/IRemoteCallback.aidl
Normal file
25
core/java/android/os/IRemoteCallback.aidl
Normal file
@@ -0,0 +1,25 @@
|
||||
/* //device/java/android/android/app/IActivityPendingResult.aidl
|
||||
**
|
||||
** Copyright 2010, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
|
||||
package android.os;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
/** @hide */
|
||||
oneway interface IRemoteCallback {
|
||||
void sendResult(in Bundle data);
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2006 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.os;
|
||||
|
||||
/** @hide */
|
||||
public class MailboxNotAvailableException extends Throwable
|
||||
{
|
||||
/**
|
||||
* This exception represents the case when a request for a
|
||||
* named, published mailbox fails because the requested name has not been published
|
||||
*/
|
||||
|
||||
public
|
||||
MailboxNotAvailableException()
|
||||
{
|
||||
}
|
||||
|
||||
public
|
||||
MailboxNotAvailableException(String s)
|
||||
{
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ public final class Messenger implements Parcelable {
|
||||
* Create a new Messenger pointing to the given Handler. Any Message
|
||||
* objects sent through this Messenger will appear in the Handler as if
|
||||
* {@link Handler#sendMessage(Message) Handler.sendMessage(Message)} had
|
||||
* be called directly.
|
||||
* been called directly.
|
||||
*
|
||||
* @param target The Handler that will receive sent messages.
|
||||
*/
|
||||
|
||||
19
core/java/android/os/RemoteCallback.aidl
Normal file
19
core/java/android/os/RemoteCallback.aidl
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
** Copyright 2010, The Android Open Source Project
|
||||
**
|
||||
** Licensed under the Apache License, Version 2.0 (the "License");
|
||||
** you may not use this file except in compliance with the License.
|
||||
** You may obtain a copy of the License at
|
||||
**
|
||||
** http://www.apache.org/licenses/LICENSE-2.0
|
||||
**
|
||||
** Unless required by applicable law or agreed to in writing, software
|
||||
** distributed under the License is distributed on an "AS IS" BASIS,
|
||||
** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
** See the License for the specific language governing permissions and
|
||||
** limitations under the License.
|
||||
*/
|
||||
|
||||
package android.os;
|
||||
|
||||
parcelable RemoteCallback;
|
||||
107
core/java/android/os/RemoteCallback.java
Normal file
107
core/java/android/os/RemoteCallback.java
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2010 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.os;
|
||||
|
||||
/**
|
||||
* TODO: Make this a public API? Let's see how it goes with a few use
|
||||
* cases first.
|
||||
* @hide
|
||||
*/
|
||||
public abstract class RemoteCallback implements Parcelable {
|
||||
final Handler mHandler;
|
||||
final IRemoteCallback mTarget;
|
||||
|
||||
class DeliverResult implements Runnable {
|
||||
final Bundle mResult;
|
||||
|
||||
DeliverResult(Bundle result) {
|
||||
mResult = result;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
onResult(mResult);
|
||||
}
|
||||
}
|
||||
|
||||
class LocalCallback extends IRemoteCallback.Stub {
|
||||
public void sendResult(Bundle bundle) {
|
||||
mHandler.post(new DeliverResult(bundle));
|
||||
}
|
||||
}
|
||||
|
||||
static class RemoteCallbackProxy extends RemoteCallback {
|
||||
RemoteCallbackProxy(IRemoteCallback target) {
|
||||
super(target);
|
||||
}
|
||||
|
||||
protected void onResult(Bundle bundle) {
|
||||
}
|
||||
}
|
||||
|
||||
public RemoteCallback(Handler handler) {
|
||||
mHandler = handler;
|
||||
mTarget = new LocalCallback();
|
||||
}
|
||||
|
||||
RemoteCallback(IRemoteCallback target) {
|
||||
mHandler = null;
|
||||
mTarget = target;
|
||||
}
|
||||
|
||||
public void sendResult(Bundle bundle) throws RemoteException {
|
||||
mTarget.sendResult(bundle);
|
||||
}
|
||||
|
||||
protected abstract void onResult(Bundle bundle);
|
||||
|
||||
public boolean equals(Object otherObj) {
|
||||
if (otherObj == null) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
return mTarget.asBinder().equals(((RemoteCallback)otherObj)
|
||||
.mTarget.asBinder());
|
||||
} catch (ClassCastException e) {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return mTarget.asBinder().hashCode();
|
||||
}
|
||||
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
out.writeStrongBinder(mTarget.asBinder());
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<RemoteCallback> CREATOR
|
||||
= new Parcelable.Creator<RemoteCallback>() {
|
||||
public RemoteCallback createFromParcel(Parcel in) {
|
||||
IBinder target = in.readStrongBinder();
|
||||
return target != null ? new RemoteCallbackProxy(
|
||||
IRemoteCallback.Stub.asInterface(target)) : null;
|
||||
}
|
||||
|
||||
public RemoteCallback[] newArray(int size) {
|
||||
return new RemoteCallback[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1192,10 +1192,10 @@
|
||||
<!-- Title of policy access to watch user login attempts -->
|
||||
<string name="policylab_watchLogin">Watch login attempts</string>
|
||||
<!-- Description of policy access to watch user login attempts -->
|
||||
<string name="policydesc_watchLogin">Monitor attempts to login to
|
||||
the device, in particular to respond to failed login attempts.</string>
|
||||
<string name="policydesc_watchLogin">Monitor failed attempts to login to
|
||||
the device, to perform some action.</string>
|
||||
<!-- Title of policy access to reset user's password -->
|
||||
<string name="policylab_resetPassword">Reset your password</string>
|
||||
<string name="policylab_resetPassword">Reset password</string>
|
||||
<!-- Description of policy access to reset user's password -->
|
||||
<string name="policydesc_resetPassword">Force your password
|
||||
to a new value, requiring the administrator give it to you
|
||||
@@ -1209,12 +1209,12 @@
|
||||
<string name="policylab_forceLock">Force lock</string>
|
||||
<!-- Description of policy access to limiting the user's password choices -->
|
||||
<string name="policydesc_forceLock">Force the device to immediately lock,
|
||||
requiring that its password is re-entered.</string>
|
||||
requiring that you re-enter its password.</string>
|
||||
<!-- Title of policy access to wipe the user's data -->
|
||||
<string name="policylab_wipeData">Erase all data</string>
|
||||
<!-- Description of policy access to wipe the user's data -->
|
||||
<string name="policydesc_wipeData">Perform a factory reset, deleting
|
||||
all of your data without any confirmation from you.</string>
|
||||
all of your data without any confirmation.</string>
|
||||
|
||||
<!-- The order of these is important, don't reorder without changing Contacts.java --> <skip />
|
||||
<!-- Phone number types from android.provider.Contacts. This could be used when adding a new phone number for a contact, for example. -->
|
||||
|
||||
@@ -23,19 +23,23 @@ import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.DeviceAdmin;
|
||||
import android.app.DeviceAdminInfo;
|
||||
import android.app.DevicePolicyManager;
|
||||
import android.app.IDevicePolicyManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.os.Binder;
|
||||
import android.os.Handler;
|
||||
import android.os.IBinder;
|
||||
import android.os.IPowerManager;
|
||||
import android.os.RecoverySystem;
|
||||
import android.os.RemoteCallback;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.util.Log;
|
||||
@@ -64,16 +68,68 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|
||||
ActiveAdmin mActiveAdmin;
|
||||
|
||||
static class ActiveAdmin {
|
||||
ActiveAdmin(DeviceAdminInfo _info) {
|
||||
info = _info;
|
||||
}
|
||||
|
||||
final DeviceAdminInfo info;
|
||||
int getUid() { return info.getActivityInfo().applicationInfo.uid; }
|
||||
|
||||
int passwordMode = DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED;
|
||||
int minimumPasswordLength = 0;
|
||||
long maximumTimeToUnlock = 0;
|
||||
int maximumFailedPasswordsForWipe = 0;
|
||||
|
||||
ActiveAdmin(DeviceAdminInfo _info) {
|
||||
info = _info;
|
||||
}
|
||||
|
||||
int getUid() { return info.getActivityInfo().applicationInfo.uid; }
|
||||
|
||||
void writeToXml(XmlSerializer out)
|
||||
throws IllegalArgumentException, IllegalStateException, IOException {
|
||||
if (passwordMode != DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED) {
|
||||
out.startTag(null, "password-mode");
|
||||
out.attribute(null, "value", Integer.toString(passwordMode));
|
||||
out.endTag(null, "password-mode");
|
||||
if (minimumPasswordLength > 0) {
|
||||
out.startTag(null, "min-password-length");
|
||||
out.attribute(null, "value", Integer.toString(minimumPasswordLength));
|
||||
out.endTag(null, "mn-password-length");
|
||||
}
|
||||
}
|
||||
if (maximumTimeToUnlock != DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED) {
|
||||
out.startTag(null, "max-time-to-unlock");
|
||||
out.attribute(null, "value", Long.toString(maximumTimeToUnlock));
|
||||
out.endTag(null, "max-time-to-unlock");
|
||||
}
|
||||
if (maximumFailedPasswordsForWipe != 0) {
|
||||
out.startTag(null, "max-failed-password-wipe");
|
||||
out.attribute(null, "value", Integer.toString(maximumFailedPasswordsForWipe));
|
||||
out.endTag(null, "max-failed-password-wipe");
|
||||
}
|
||||
}
|
||||
|
||||
void readFromXml(XmlPullParser parser)
|
||||
throws XmlPullParserException, IOException {
|
||||
int outerDepth = parser.getDepth();
|
||||
int type;
|
||||
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
|
||||
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
|
||||
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
|
||||
continue;
|
||||
}
|
||||
String tag = parser.getName();
|
||||
if ("password-mode".equals(tag)) {
|
||||
passwordMode = Integer.parseInt(
|
||||
parser.getAttributeValue(null, "value"));
|
||||
} else if ("min-password-length".equals(tag)) {
|
||||
minimumPasswordLength = Integer.parseInt(
|
||||
parser.getAttributeValue(null, "value"));
|
||||
} else if ("max-time-to-unlock".equals(tag)) {
|
||||
maximumTimeToUnlock = Long.parseLong(
|
||||
parser.getAttributeValue(null, "value"));
|
||||
} else if ("max-failed-password-wipe".equals(tag)) {
|
||||
maximumFailedPasswordsForWipe = Integer.parseInt(
|
||||
parser.getAttributeValue(null, "value"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -91,25 +147,42 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|
||||
return mIPowerManager;
|
||||
}
|
||||
|
||||
ActiveAdmin getActiveAdminForCallerLocked(ComponentName who, int reqPolicy)
|
||||
ActiveAdmin getActiveAdminUncheckedLocked(ComponentName who) {
|
||||
ActiveAdmin admin = mActiveAdmin;
|
||||
if (admin != null
|
||||
&& who.getPackageName().equals(admin.info.getActivityInfo().packageName)
|
||||
&& who.getClassName().equals(admin.info.getActivityInfo().name)) {
|
||||
return admin;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
ActiveAdmin getActiveAdminForCallerLocked(ComponentName who)
|
||||
throws SecurityException {
|
||||
if (mActiveAdmin != null && mActiveAdmin.getUid() == Binder.getCallingUid()) {
|
||||
ActiveAdmin admin = mActiveAdmin;
|
||||
if (admin != null && admin.getUid() == Binder.getCallingUid()) {
|
||||
if (who != null) {
|
||||
if (!who.getPackageName().equals(mActiveAdmin.info.getActivityInfo().packageName)
|
||||
|| !who.getClassName().equals(mActiveAdmin.info.getActivityInfo().name)) {
|
||||
if (!who.getPackageName().equals(admin.info.getActivityInfo().packageName)
|
||||
|| !who.getClassName().equals(admin.info.getActivityInfo().name)) {
|
||||
throw new SecurityException("Current admin is not " + who);
|
||||
}
|
||||
}
|
||||
if (!mActiveAdmin.info.usesPolicy(reqPolicy)) {
|
||||
throw new SecurityException("Admin " + mActiveAdmin.info.getComponent()
|
||||
+ " did not specify uses-policy for: "
|
||||
+ mActiveAdmin.info.getTagForPolicy(reqPolicy));
|
||||
}
|
||||
return mActiveAdmin;
|
||||
}
|
||||
throw new SecurityException("Current admin is not owned by uid " + Binder.getCallingUid());
|
||||
}
|
||||
|
||||
ActiveAdmin getActiveAdminForCallerLocked(ComponentName who, int reqPolicy)
|
||||
throws SecurityException {
|
||||
ActiveAdmin admin = getActiveAdminForCallerLocked(who);
|
||||
if (!admin.info.usesPolicy(reqPolicy)) {
|
||||
throw new SecurityException("Admin " + admin.info.getComponent()
|
||||
+ " did not specify uses-policy for: "
|
||||
+ admin.info.getTagForPolicy(reqPolicy));
|
||||
}
|
||||
return admin;
|
||||
}
|
||||
|
||||
void sendAdminCommandLocked(ActiveAdmin admin, String action) {
|
||||
Intent intent = new Intent(action);
|
||||
intent.setComponent(admin.info.getComponent());
|
||||
@@ -182,25 +255,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|
||||
if (ap != null) {
|
||||
out.startTag(null, "admin");
|
||||
out.attribute(null, "name", ap.info.getComponent().flattenToString());
|
||||
if (ap.passwordMode != DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED) {
|
||||
out.startTag(null, "password-mode");
|
||||
out.attribute(null, "value", Integer.toString(ap.passwordMode));
|
||||
out.endTag(null, "password-mode");
|
||||
if (ap.minimumPasswordLength > 0) {
|
||||
out.startTag(null, "min-password-length");
|
||||
out.attribute(null, "value", Integer.toString(ap.minimumPasswordLength));
|
||||
out.endTag(null, "mn-password-length");
|
||||
}
|
||||
}
|
||||
if (ap.maximumTimeToUnlock != DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED) {
|
||||
out.startTag(null, "max-time-to-unlock");
|
||||
out.attribute(null, "value", Long.toString(ap.maximumTimeToUnlock));
|
||||
out.endTag(null, "max-time-to-unlock");
|
||||
}
|
||||
ap.writeToXml(out);
|
||||
out.endTag(null, "admin");
|
||||
}
|
||||
out.endTag(null, "policies");
|
||||
|
||||
if (mFailedPasswordAttempts != 0) {
|
||||
out.startTag(null, "failed-password-attempts");
|
||||
out.attribute(null, "value", Integer.toString(mFailedPasswordAttempts));
|
||||
out.endTag(null, "failed-password-attempts");
|
||||
}
|
||||
|
||||
out.endDocument();
|
||||
stream.close();
|
||||
journal.commit();
|
||||
@@ -220,51 +285,41 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|
||||
JournaledFile journal = makeJournaledFile();
|
||||
FileInputStream stream = null;
|
||||
File file = journal.chooseForRead();
|
||||
boolean success = false;
|
||||
try {
|
||||
stream = new FileInputStream(file);
|
||||
XmlPullParser parser = Xml.newPullParser();
|
||||
parser.setInput(stream, null);
|
||||
|
||||
int type = parser.next();
|
||||
while (type != XmlPullParser.START_TAG) {
|
||||
type = parser.next();
|
||||
int type;
|
||||
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
|
||||
&& type != XmlPullParser.START_TAG) {
|
||||
}
|
||||
String tag = parser.getName();
|
||||
if ("policies".equals(tag)) {
|
||||
ActiveAdmin ap = null;
|
||||
do {
|
||||
type = parser.next();
|
||||
if (type == XmlPullParser.START_TAG) {
|
||||
tag = parser.getName();
|
||||
if (ap == null) {
|
||||
if ("admin".equals(tag)) {
|
||||
DeviceAdminInfo dai = findAdmin(
|
||||
ComponentName.unflattenFromString(
|
||||
parser.getAttributeValue(null, "name")));
|
||||
if (dai != null) {
|
||||
ap = new ActiveAdmin(dai);
|
||||
}
|
||||
}
|
||||
} else if ("password-mode".equals(tag)) {
|
||||
ap.passwordMode = Integer.parseInt(
|
||||
parser.getAttributeValue(null, "value"));
|
||||
} else if ("min-password-length".equals(tag)) {
|
||||
ap.minimumPasswordLength = Integer.parseInt(
|
||||
parser.getAttributeValue(null, "value"));
|
||||
} else if ("max-time-to-unlock".equals(tag)) {
|
||||
ap.maximumTimeToUnlock = Long.parseLong(
|
||||
parser.getAttributeValue(null, "value"));
|
||||
}
|
||||
} else if (type == XmlPullParser.END_TAG) {
|
||||
tag = parser.getName();
|
||||
if (ap != null && "admin".equals(tag)) {
|
||||
mActiveAdmin = ap;
|
||||
ap = null;
|
||||
}
|
||||
if (!"policies".equals(tag)) {
|
||||
throw new XmlPullParserException(
|
||||
"Settings do not start with policies tag: found " + tag);
|
||||
}
|
||||
type = parser.next();
|
||||
int outerDepth = parser.getDepth();
|
||||
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
|
||||
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
|
||||
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
|
||||
continue;
|
||||
}
|
||||
tag = parser.getName();
|
||||
if ("admin".equals(tag)) {
|
||||
DeviceAdminInfo dai = findAdmin(
|
||||
ComponentName.unflattenFromString(
|
||||
parser.getAttributeValue(null, "name")));
|
||||
if (dai != null) {
|
||||
ActiveAdmin ap = new ActiveAdmin(dai);
|
||||
ap.readFromXml(parser);
|
||||
mActiveAdmin = ap;
|
||||
}
|
||||
} while (type != XmlPullParser.END_DOCUMENT);
|
||||
success = true;
|
||||
} else if ("failed-password-attempts".equals(tag)) {
|
||||
mFailedPasswordAttempts = Integer.parseInt(
|
||||
parser.getAttributeValue(null, "value"));
|
||||
}
|
||||
}
|
||||
} catch (NullPointerException e) {
|
||||
Log.w(TAG, "failed parsing " + file + " " + e);
|
||||
@@ -285,10 +340,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|
||||
// Ignore
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
Log.w(TAG, "No valid start tag found in policies file");
|
||||
}
|
||||
|
||||
long timeMs = getMaximumTimeToLock();
|
||||
if (timeMs <= 0) {
|
||||
timeMs = Integer.MAX_VALUE;
|
||||
@@ -418,6 +469,27 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
public void setMaximumFailedPasswordsForWipe(ComponentName who, int num) {
|
||||
synchronized (this) {
|
||||
// This API can only be called by an active device admin,
|
||||
// so try to retrieve it to check that the caller is one.
|
||||
getActiveAdminForCallerLocked(who,
|
||||
DeviceAdminInfo.USES_POLICY_WIPE_DATA);
|
||||
ActiveAdmin ap = getActiveAdminForCallerLocked(who,
|
||||
DeviceAdminInfo.USES_POLICY_WATCH_LOGIN);
|
||||
if (ap.maximumFailedPasswordsForWipe != num) {
|
||||
ap.maximumFailedPasswordsForWipe = num;
|
||||
saveSettingsLocked();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int getMaximumFailedPasswordsForWipe() {
|
||||
synchronized (this) {
|
||||
return mActiveAdmin != null ? mActiveAdmin.maximumFailedPasswordsForWipe : 0;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean resetPassword(String password) {
|
||||
int mode;
|
||||
synchronized (this) {
|
||||
@@ -488,20 +560,53 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
void wipeDataLocked(int flags) {
|
||||
try {
|
||||
RecoverySystem.rebootWipeUserData(mContext);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Failed requesting data wipe", e);
|
||||
}
|
||||
}
|
||||
|
||||
public void wipeData(int flags) {
|
||||
synchronized (this) {
|
||||
// This API can only be called by an active device admin,
|
||||
// so try to retrieve it to check that the caller is one.
|
||||
getActiveAdminForCallerLocked(null,
|
||||
DeviceAdminInfo.USES_POLICY_WIPE_DATA);
|
||||
long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
wipeDataLocked(flags);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
RecoverySystem.rebootWipeUserData(mContext);
|
||||
} catch (IOException e) {
|
||||
Log.w(TAG, "Failed requesting data wipe", e);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
|
||||
public void getRemoveWarning(ComponentName comp, final RemoteCallback result) {
|
||||
mContext.enforceCallingOrSelfPermission(
|
||||
android.Manifest.permission.BIND_DEVICE_ADMIN, null);
|
||||
|
||||
synchronized (this) {
|
||||
ActiveAdmin admin = getActiveAdminUncheckedLocked(comp);
|
||||
if (admin == null) {
|
||||
try {
|
||||
result.sendResult(null);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
return;
|
||||
}
|
||||
Intent intent = new Intent(DeviceAdmin.ACTION_DEVICE_ADMIN_DISABLE_REQUESTED);
|
||||
intent.setComponent(admin.info.getComponent());
|
||||
mContext.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
try {
|
||||
result.sendResult(getResultExtras(false));
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
}
|
||||
}, null, Activity.RESULT_OK, null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -516,7 +621,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|
||||
try {
|
||||
mActivePasswordMode = mode;
|
||||
mActivePasswordLength = length;
|
||||
mFailedPasswordAttempts = 0;
|
||||
if (mFailedPasswordAttempts != 0) {
|
||||
mFailedPasswordAttempts = 0;
|
||||
saveSettingsLocked();
|
||||
}
|
||||
sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_CHANGED,
|
||||
DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD);
|
||||
} finally {
|
||||
@@ -534,6 +642,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|
||||
long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mFailedPasswordAttempts++;
|
||||
saveSettingsLocked();
|
||||
int max = getMaximumFailedPasswordsForWipe();
|
||||
if (max > 0 && mFailedPasswordAttempts >= max) {
|
||||
wipeDataLocked(0);
|
||||
}
|
||||
sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_FAILED,
|
||||
DeviceAdminInfo.USES_POLICY_WATCH_LOGIN);
|
||||
} finally {
|
||||
@@ -551,6 +664,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub {
|
||||
long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mFailedPasswordAttempts = 0;
|
||||
saveSettingsLocked();
|
||||
sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_SUCCEEDED,
|
||||
DeviceAdminInfo.USES_POLICY_WATCH_LOGIN);
|
||||
} finally {
|
||||
|
||||
Reference in New Issue
Block a user