diff options
-rw-r--r-- | Android.mk | 1 | ||||
-rw-r--r-- | api/current.xml | 147 | ||||
-rw-r--r-- | core/java/android/app/Activity.java | 172 | ||||
-rw-r--r-- | core/java/android/app/DeviceAdmin.java | 43 | ||||
-rw-r--r-- | core/java/android/app/DeviceAdminInfo.java | 64 | ||||
-rw-r--r-- | core/java/android/app/DevicePolicyManager.java | 52 | ||||
-rw-r--r-- | core/java/android/app/IDevicePolicyManager.aidl | 3 | ||||
-rw-r--r-- | core/java/android/os/IRemoteCallback.aidl | 25 | ||||
-rw-r--r-- | core/java/android/os/MailboxNotAvailableException.java | 37 | ||||
-rw-r--r-- | core/java/android/os/Messenger.java | 2 | ||||
-rw-r--r-- | core/java/android/os/RemoteCallback.aidl | 19 | ||||
-rw-r--r-- | core/java/android/os/RemoteCallback.java | 107 | ||||
-rw-r--r-- | core/java/android/provider/Browser.java | 45 | ||||
-rw-r--r-- | core/java/android/webkit/BrowserFrame.java | 13 | ||||
-rw-r--r-- | core/java/android/webkit/WebView.java | 20 | ||||
-rw-r--r-- | core/java/android/webkit/WebViewCore.java | 15 | ||||
-rw-r--r-- | core/res/res/values/strings.xml | 10 | ||||
-rw-r--r-- | services/java/com/android/server/DevicePolicyManagerService.java | 268 |
18 files changed, 796 insertions, 247 deletions
@@ -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=""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" @@ -119960,6 +120070,28 @@ visibility="public" > </field> +<field name="EXTRA_HEADERS_KEY" + type="java.lang.String" + transient="false" + volatile="false" + value=""com.android.browser.headers_key"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_HEADERS_VALUE" + type="java.lang.String" + transient="false" + volatile="false" + value=""com.android.browser.headers_value"" + 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<java.lang.String, java.lang.String>"> +</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 { |