summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk1
-rw-r--r--api/current.xml147
-rw-r--r--core/java/android/app/Activity.java172
-rw-r--r--core/java/android/app/DeviceAdmin.java43
-rw-r--r--core/java/android/app/DeviceAdminInfo.java64
-rw-r--r--core/java/android/app/DevicePolicyManager.java52
-rw-r--r--core/java/android/app/IDevicePolicyManager.aidl3
-rw-r--r--core/java/android/os/IRemoteCallback.aidl25
-rw-r--r--core/java/android/os/MailboxNotAvailableException.java37
-rw-r--r--core/java/android/os/Messenger.java2
-rw-r--r--core/java/android/os/RemoteCallback.aidl19
-rw-r--r--core/java/android/os/RemoteCallback.java107
-rw-r--r--core/java/android/provider/Browser.java45
-rw-r--r--core/java/android/webkit/BrowserFrame.java13
-rw-r--r--core/java/android/webkit/WebView.java20
-rw-r--r--core/java/android/webkit/WebViewCore.java15
-rw-r--r--core/res/res/values/strings.xml10
-rw-r--r--services/java/com/android/server/DevicePolicyManagerService.java268
18 files changed, 796 insertions, 247 deletions
diff --git a/Android.mk b/Android.mk
index 5f9bea4..7520afe 100644
--- a/Android.mk
+++ b/Android.mk
@@ -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 \
diff --git a/api/current.xml b/api/current.xml
index e8a8481..1250461 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -16176,11 +16176,26 @@
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"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
deprecated="not deprecated"
visibility="protected"
>
<parameter name="id" type="int">
</parameter>
+<parameter name="args" type="android.os.Bundle">
+</parameter>
</method>
<method name="onCreateOptionsMenu"
return="boolean"
@@ -16476,6 +16491,21 @@
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"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
deprecated="not deprecated"
visibility="protected"
>
@@ -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="&quot;android.app.action.DEVICE_ADMIN_DISABLE_REQUESTED&quot;"
+ 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="&quot;android.app.extra.DISABLE_WARNING&quot;"
+ 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="&quot;android.app.extra.ADD_EXPLANATION&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="EXTRA_DEVICE_ADMIN"
type="java.lang.String"
transient="false"
@@ -119960,6 +120070,28 @@
visibility="public"
>
</field>
+<field name="EXTRA_HEADERS_KEY"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;com.android.browser.headers_key&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="EXTRA_HEADERS_VALUE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;com.android.browser.headers_value&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="HISTORY_PROJECTION"
type="java.lang.String[]"
transient="false"
@@ -189850,6 +189982,21 @@
>
<parameter name="url" type="java.lang.String">
</parameter>
+<parameter name="extraHeaders" type="java.util.Map&lt;java.lang.String, java.lang.String&gt;">
+</parameter>
+</method>
+<method name="loadUrl"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="url" type="java.lang.String">
+</parameter>
</method>
<method name="onChildViewAdded"
return="void"
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index ca15a99..95142e3 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -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
}
/**
+ * @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.
+ * by the activity. The default implementation calls through to
+ * {@link #onCreateDialog(int)} for compatibility.
*
- * If you use {@link #showDialog(int)}, the activity will call through to
+ * <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.
*
- * If you would like the activity to manage the saving and restoring dialogs
+ * <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}.
*
- * If you would like an opportunity to prepare your dialog before it is shown,
- * override {@link #onPrepareDialog(int, Dialog)}.
+ * <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.
- * @return 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)
- * @see #showDialog(int)
+ * @see #onPrepareDialog(int, Dialog, Bundle)
+ * @see #showDialog(int, Bundle)
* @see #dismissDialog(int)
* @see #removeDialog(int)
*/
- protected Dialog onCreateDialog(int id) {
- return null;
+ 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)}Ê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);
}
diff --git a/core/java/android/app/DeviceAdmin.java b/core/java/android/app/DeviceAdmin.java
index 9750d6e..88fdab2 100644
--- a/core/java/android/app/DeviceAdmin.java
+++ b/core/java/android/app/DeviceAdmin.java
@@ -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
@@ -62,6 +63,27 @@ public class DeviceAdmin extends BroadcastReceiver {
= "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
* protected device policy manager APIs. You will generally
@@ -166,6 +188,21 @@ public class DeviceAdmin extends BroadcastReceiver {
}
/**
+ * 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
* can no longer use the protected parts of the {@link DevicePolicyManager}
@@ -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);
}
diff --git a/core/java/android/app/DeviceAdminInfo.java b/core/java/android/app/DeviceAdminInfo.java
index 92fdbc8..e50db89 100644
--- a/core/java/android/app/DeviceAdminInfo.java
+++ b/core/java/android/app/DeviceAdminInfo.java
@@ -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;
diff --git a/core/java/android/app/DevicePolicyManager.java b/core/java/android/app/DevicePolicyManager.java
index 25e3230..9de7336 100644
--- a/core/java/android/app/DevicePolicyManager.java
+++ b/core/java/android/app/DevicePolicyManager.java
@@ -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.
@@ -76,6 +82,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)}
* or {@link #setMinimumPasswordLength(ComponentName, int)} to have the
@@ -285,6 +299,29 @@ public class DevicePolicyManager {
}
/**
+ * 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
* or it will be rejected. The given password will be accepted regardless
@@ -451,6 +488,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
+ */
public void setActivePasswordState(int mode, int length) {
if (mService != null) {
try {
diff --git a/core/java/android/app/IDevicePolicyManager.aidl b/core/java/android/app/IDevicePolicyManager.aidl
index 7e38194..edb8603 100644
--- a/core/java/android/app/IDevicePolicyManager.aidl
+++ b/core/java/android/app/IDevicePolicyManager.aidl
@@ -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);
diff --git a/core/java/android/os/IRemoteCallback.aidl b/core/java/android/os/IRemoteCallback.aidl
new file mode 100644
index 0000000..f0c6c73
--- /dev/null
+++ b/core/java/android/os/IRemoteCallback.aidl
@@ -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);
+}
diff --git a/core/java/android/os/MailboxNotAvailableException.java b/core/java/android/os/MailboxNotAvailableException.java
deleted file mode 100644
index 574adbd..0000000
--- a/core/java/android/os/MailboxNotAvailableException.java
+++ /dev/null
@@ -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);
- }
-}
diff --git a/core/java/android/os/Messenger.java b/core/java/android/os/Messenger.java
index 1bc554e..ad55abdd 100644
--- a/core/java/android/os/Messenger.java
+++ b/core/java/android/os/Messenger.java
@@ -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.
*/
diff --git a/core/java/android/os/RemoteCallback.aidl b/core/java/android/os/RemoteCallback.aidl
new file mode 100644
index 0000000..7ae56f5
--- /dev/null
+++ b/core/java/android/os/RemoteCallback.aidl
@@ -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;
diff --git a/core/java/android/os/RemoteCallback.java b/core/java/android/os/RemoteCallback.java
new file mode 100644
index 0000000..ca95bdf
--- /dev/null
+++ b/core/java/android/os/RemoteCallback.java
@@ -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];
+ }
+ };
+}
diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java
index d67169f..ef43afe 100644
--- a/core/java/android/provider/Browser.java
+++ b/core/java/android/provider/Browser.java
@@ -34,12 +34,6 @@ public class Browser {
Uri.parse("content://browser/bookmarks");
/**
- * The inline scheme to show embedded content in a browser.
- * @hide
- */
- public static final Uri INLINE_URI = Uri.parse("inline:");
-
- /**
* The name of extra data when starting Browser with ACTION_VIEW or
* ACTION_SEARCH intent.
* <p>
@@ -62,24 +56,6 @@ public class Browser {
"com.android.browser.application_id";
/**
- * The content to be rendered when url's scheme is inline.
- * @hide
- */
- public static final String EXTRA_INLINE_CONTENT ="com.android.browser.inline.content";
-
- /**
- * The encoding of the inlined content for inline scheme.
- * @hide
- */
- public static final String EXTRA_INLINE_ENCODING ="com.android.browser.inline.encoding";
-
- /**
- * The url used when the inline content is falied to render.
- * @hide
- */
- public static final String EXTRA_INLINE_FAILURL ="com.android.browser.inline.failurl";
-
- /**
* The name of the extra data in the VIEW intent. The data is in boolean.
* <p>
* If the Browser is handling the intent and the setting for
@@ -102,6 +78,27 @@ public class Browser {
*/
public static final String EXTRA_POST_DATA = "com.android.browser.post_data";
+ /**
+ * The name of the extra data in the VIEW intent. The data is in the format
+ * of String array. This should be paired with EXTRA_HEADERS_VALUE.
+ * <p>
+ * The keys will be combined with the values and sent in the HTTP request
+ * headers for the provided url. The keys can't be the standard HTTP headers
+ * as they are set by the WebView. The url's schema must be http(s).
+ * <p>
+ */
+ public static final String EXTRA_HEADERS_KEY = "com.android.browser.headers_key";
+
+ /**
+ * The name of the extra data in the VIEW intent. The data is in the format
+ * of String array. This should be paired with EXTRA_HEADERS_KEY.
+ * <p>
+ * The values will be combined with the keys and sent in the HTTP request
+ * headers for the provided url. The url's schema must be http(s).
+ * <p>
+ */
+ public static final String EXTRA_HEADERS_VALUE = "com.android.browser.headers_value";
+
/* if you change column order you must also change indices
below */
public static final String[] HISTORY_PROJECTION = new String[] {
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 1337bed..3f1672a 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -177,18 +177,21 @@ class BrowserFrame extends Handler {
/**
* Load a url from the network or the filesystem into the main frame.
- * Following the same behaviour as Safari, javascript: URLs are not
- * passed to the main frame, instead they are evaluated immediately.
+ * Following the same behaviour as Safari, javascript: URLs are not passed
+ * to the main frame, instead they are evaluated immediately.
* @param url The url to load.
+ * @param extraHeaders The extra headers sent with this url. This should not
+ * include the common headers like "user-agent". If it does, it
+ * will be replaced by the intrinsic value of the WebView.
*/
- public void loadUrl(String url) {
+ public void loadUrl(String url, Map<String, String> extraHeaders) {
mLoadInitFromJava = true;
if (URLUtil.isJavaScriptUrl(url)) {
// strip off the scheme and evaluate the string
stringByEvaluatingJavaScriptFromString(
url.substring("javascript:".length()));
} else {
- nativeLoadUrl(url);
+ nativeLoadUrl(url, extraHeaders);
}
mLoadInitFromJava = false;
}
@@ -904,7 +907,7 @@ class BrowserFrame extends Handler {
/**
* Returns false if the url is bad.
*/
- private native void nativeLoadUrl(String url);
+ private native void nativeLoadUrl(String url, Map<String, String> headers);
private native void nativePostUrl(String url, byte[] postData);
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 93e72ff..ecea55f 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -1376,6 +1376,22 @@ public class WebView extends AbsoluteLayout
}
/**
+ * Load the given url with the extra headers.
+ * @param url The url of the resource to load.
+ * @param extraHeaders The extra headers sent with this url. This should not
+ * include the common headers like "user-agent". If it does, it
+ * will be replaced by the intrinsic value of the WebView.
+ */
+ public void loadUrl(String url, Map<String, String> extraHeaders) {
+ switchOutDrawHistory();
+ WebViewCore.GetUrlData arg = new WebViewCore.GetUrlData();
+ arg.mUrl = url;
+ arg.mExtraHeaders = extraHeaders;
+ mWebViewCore.sendMessage(EventHub.LOAD_URL, arg);
+ clearTextEntry();
+ }
+
+ /**
* Load the given url.
* @param url The url of the resource to load.
*/
@@ -1383,9 +1399,7 @@ public class WebView extends AbsoluteLayout
if (url == null) {
return;
}
- switchOutDrawHistory();
- mWebViewCore.sendMessage(EventHub.LOAD_URL, url);
- clearTextEntry();
+ loadUrl(url, null);
}
/**
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 6700d71..1e21cb4 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -686,6 +686,11 @@ final class WebViewCore {
int mY;
}
+ static class GetUrlData {
+ String mUrl;
+ Map<String, String> mExtraHeaders;
+ }
+
static class PostUrlData {
String mUrl;
byte[] mPostData;
@@ -958,9 +963,11 @@ final class WebViewCore {
((Float) msg.obj).floatValue(), msg.arg1);
break;
- case LOAD_URL:
- loadUrl((String) msg.obj);
+ case LOAD_URL: {
+ GetUrlData param = (GetUrlData) msg.obj;
+ loadUrl(param.mUrl, param.mExtraHeaders);
break;
+ }
case POST_URL: {
PostUrlData param = (PostUrlData) msg.obj;
@@ -1545,9 +1552,9 @@ final class WebViewCore {
}
}
- private void loadUrl(String url) {
+ private void loadUrl(String url, Map<String, String> extraHeaders) {
if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, " CORE loadUrl " + url);
- mBrowserFrame.loadUrl(url);
+ mBrowserFrame.loadUrl(url, extraHeaders);
}
private void key(KeyEvent evt, boolean isDown) {
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 31f71d3..259398f 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -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. -->
diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java
index fbd5317..ebd6f3d 100644
--- a/services/java/com/android/server/DevicePolicyManagerService.java
+++ b/services/java/com/android/server/DevicePolicyManagerService.java
@@ -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 {
+ final DeviceAdminInfo info;
+
+ int passwordMode = DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED;
+ int minimumPasswordLength = 0;
+ long maximumTimeToUnlock = 0;
+ int maximumFailedPasswordsForWipe = 0;
+
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;
+ 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 {