From 8aa2e8939c61d788cbc192098465e79f584e173a Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Fri, 22 Jan 2010 11:31:30 -0800 Subject: [PATCH] More device admin work: description, policy control. There is now a description attribute associated with all components, that can supply user-visible information about what the component does. We use this to show such information about device admins, and wallpapers are also updated to be able to show this in addition to the existing description in their meta-data. This also defines security control for admins, requiring that they declare the policies they will touch, and enforcing that they do so to be able to use various APIs. --- api/current.xml | 117 ++++++++++ core/java/android/app/DeviceAdmin.java | 12 ++ core/java/android/app/DeviceAdminInfo.java | 199 ++++++++++++++++++ .../java/android/app/DevicePolicyManager.java | 32 +++ core/java/android/app/WallpaperInfo.java | 35 +-- .../android/content/pm/ComponentInfo.java | 29 +++ .../android/content/pm/PackageParser.java | 17 +- .../widget/AppSecurityPermissions.java | 26 ++- core/res/res/values/attrs_manifest.xml | 5 + core/res/res/values/strings.xml | 34 +++ .../server/DevicePolicyManagerService.java | 51 +++-- 11 files changed, 523 insertions(+), 34 deletions(-) diff --git a/api/current.xml b/api/current.xml index 8cd368f3519fa..cd200c143d681 100644 --- a/api/current.xml +++ b/api/current.xml @@ -20148,6 +20148,34 @@ visibility="public" > + + + + + + + + + + + + + + + + + + + + + + + + + + + + The calling device admin must have requested + * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to receive + * this broadcast. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PASSWORD_CHANGED @@ -94,6 +98,10 @@ public class DeviceAdmin extends BroadcastReceiver { * {@link DevicePolicyManager#getCurrentFailedPasswordAttempts() * DevicePolicyManager.getCurrentFailedPasswordAttempts()}. You will generally * handle this in {@link DeviceAdmin#onPasswordFailed(Context, Intent)}. + * + *

The calling device admin must have requested + * {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN} to receive + * this broadcast. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PASSWORD_FAILED @@ -102,6 +110,10 @@ public class DeviceAdmin extends BroadcastReceiver { /** * Action sent to a device administrator when the user has successfully * entered their password, after failing one or more times. + * + *

The calling device admin must have requested + * {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN} to receive + * this broadcast. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_PASSWORD_SUCCEEDED diff --git a/core/java/android/app/DeviceAdminInfo.java b/core/java/android/app/DeviceAdminInfo.java index eac6e46a809b4..92fdbc80908f0 100644 --- a/core/java/android/app/DeviceAdminInfo.java +++ b/core/java/android/app/DeviceAdminInfo.java @@ -19,21 +19,28 @@ 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; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; +import android.content.res.Resources.NotFoundException; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; +import android.util.Log; import android.util.Printer; +import android.util.SparseArray; import android.util.Xml; import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; /** * This class is used to specify meta information of a device administrator @@ -42,11 +49,123 @@ import java.io.IOException; public final class DeviceAdminInfo implements Parcelable { static final String TAG = "DeviceAdminInfo"; + /** + * A type of policy that this device admin can use: limit the passwords + * that the user can select, via {@link DevicePolicyManager#setPasswordMode} + * and {@link DevicePolicyManager#setMinimumPasswordLength}. + * + *

To control this policy, the device admin must have a "limit-password" + * tag in the "uses-policies" section of its meta-data. + */ + public static final int USES_POLICY_LIMIT_PASSWORD = 0; + + /** + * A type of policy that this device admin can use: able to watch login + * attempts from the user, via {@link DeviceAdmin#ACTION_PASSWORD_FAILED}, + * {@link DeviceAdmin#ACTION_PASSWORD_SUCCEEDED}, and + * {@link DevicePolicyManager#getCurrentFailedPasswordAttempts}. + * + *

To control this policy, the device admin must have a "watch-login" + * tag in the "uses-policies" section of its meta-data. + */ + public static final int USES_POLICY_WATCH_LOGIN = 1; + + /** + * A type of policy that this device admin can use: able to reset the + * user's password via + * {@link DevicePolicyManager#resetPassword}. + * + *

To control this policy, the device admin must have a "reset-password" + * tag in the "uses-policies" section of its meta-data. + */ + public static final int USES_POLICY_RESET_PASSWORD = 2; + + /** + * A type of policy that this device admin can use: able to limit the + * maximum lock timeout for the device via + * {@link DevicePolicyManager#setMaximumTimeToLock}. + * + *

To control this policy, the device admin must have a "limit-unlock" + * tag in the "uses-policies" section of its meta-data. + */ + public static final int USES_POLICY_LIMIT_UNLOCK = 3; + + /** + * A type of policy that this device admin can use: able to force the device + * to lock via{@link DevicePolicyManager#lockNow}. + * + *

To control this policy, the device admin must have a "force-lock" + * tag in the "uses-policies" section of its meta-data. + */ + public static final int USES_POLICY_FORCE_LOCK = 4; + + /** + * A type of policy that this device admin can use: able to factory + * reset the device, erasing all of the user's data, via + * {@link DevicePolicyManager#wipeData}. + * + *

To control this policy, the device admin must have a "wipe-data" + * tag in the "uses-policies" section of its meta-data. + */ + public static final int USES_POLICY_WIPE_DATA = 5; + + /** @hide */ + public static class PolicyInfo { + final public String tag; + final public int label; + final public int description; + + public PolicyInfo(String tagIn, int labelIn, int descriptionIn) { + tag = tagIn; + label = labelIn; + description = descriptionIn; + } + } + + static HashMap sKnownPolicies = new HashMap(); + static SparseArray sRevKnownPolicies = new SparseArray(); + + 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 outerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + String tagName = parser.getName(); + if (tagName.equals("uses-policies")) { + int innerDepth = parser.getDepth(); + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + String policyName = parser.getName(); + Integer val = sKnownPolicies.get(policyName); + if (val != null) { + mUsesPolicies |= 1 << val.intValue(); + } else { + Log.w(TAG, "Unknown tag under uses-policies of " + + getComponent() + ": " + policyName); + } + } + } + } } finally { if (parser != null) parser.close(); } @@ -93,6 +238,7 @@ public final class DeviceAdminInfo implements Parcelable { DeviceAdminInfo(Parcel source) { mReceiver = ResolveInfo.CREATOR.createFromParcel(source); + mUsesPolicies = source.readInt(); } /** @@ -136,6 +282,26 @@ public final class DeviceAdminInfo implements Parcelable { return mReceiver.loadLabel(pm); } + /** + * Load user-visible description associated with this device admin. + * + * @param pm Supply a PackageManager used to load the device admin's + * resources. + */ + public CharSequence loadDescription(PackageManager pm) throws NotFoundException { + if (mReceiver.activityInfo.descriptionRes != 0) { + String packageName = mReceiver.resolvePackageName; + ApplicationInfo applicationInfo = null; + if (packageName == null) { + packageName = mReceiver.activityInfo.packageName; + applicationInfo = mReceiver.activityInfo.applicationInfo; + } + return pm.getText(packageName, + mReceiver.activityInfo.descriptionRes, applicationInfo); + } + throw new NotFoundException(); + } + /** * Load the user-displayed icon for this device admin. * @@ -146,6 +312,38 @@ public final class DeviceAdminInfo implements Parcelable { return mReceiver.loadIcon(pm); } + /** + * Return true if the device admin has requested that it be able to use + * the given policy control. The possible policy identifier inputs are: + * {@link #USES_POLICY_LIMIT_PASSWORD}, {@link #USES_POLICY_WATCH_LOGIN}, + * {@link #USES_POLICY_RESET_PASSWORD}, {@link #USES_POLICY_LIMIT_UNLOCK}, + * {@link #USES_POLICY_FORCE_LOCK}, {@link #USES_POLICY_WIPE_DATA}. + */ + public boolean usesPolicy(int policyIdent) { + return (mUsesPolicies & (1< getUsedPolicies() { + ArrayList res = new ArrayList(); + for (int i=0; iThe calling device admin must have requested + * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call + * this method; if it has not, a security exception will be thrown. + * * @param admin Which {@link DeviceAdmin} this request is associated with. * @param mode The new desired mode. One of * {@link #PASSWORD_MODE_UNSPECIFIED}, {@link #PASSWORD_MODE_SOMETHING}, @@ -205,6 +209,10 @@ public class DevicePolicyManager { * {@link #PASSWORD_MODE_NUMERIC} or {@link #PASSWORD_MODE_ALPHANUMERIC} * with {@link #setPasswordMode}. * + *

The calling device admin must have requested + * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call + * this method; if it has not, a security exception will be thrown. + * * @param admin Which {@link DeviceAdmin} this request is associated with. * @param length The new desired minimum password length. A value of 0 * means there is no restriction. @@ -239,6 +247,10 @@ public class DevicePolicyManager { * to meet the policy requirements (mode, minimum length) that have been * requested. * + *

The calling device admin must have requested + * {@link DeviceAdminInfo#USES_POLICY_LIMIT_PASSWORD} to be able to call + * this method; if it has not, a security exception will be thrown. + * * @return Returns true if the password meets the current requirements, * else false. */ @@ -256,6 +268,10 @@ public class DevicePolicyManager { /** * Retrieve the number of times the user has failed at entering a * password since that last successful password entry. + * + *

The calling device admin must have requested + * {@link DeviceAdminInfo#USES_POLICY_WATCH_LOGIN} to be able to call + * this method; if it has not, a security exception will be thrown. */ public int getCurrentFailedPasswordAttempts() { if (mService != null) { @@ -277,6 +293,10 @@ public class DevicePolicyManager { * if it contains only digits, that is still an acceptable alphanumeric * password.) * + *

The calling device admin must have requested + * {@link DeviceAdminInfo#USES_POLICY_RESET_PASSWORD} to be able to call + * this method; if it has not, a security exception will be thrown. + * * @param password The new password for the user. * @return Returns true if the password was applied, or false if it is * not acceptable for the current constraints. @@ -297,6 +317,10 @@ public class DevicePolicyManager { * maximum time for user activity until the device will lock. This limits * the length that the user can set. It takes effect immediately. * + *

The calling device admin must have requested + * {@link DeviceAdminInfo#USES_POLICY_LIMIT_UNLOCK} to be able to call + * this method; if it has not, a security exception will be thrown. + * * @param admin Which {@link DeviceAdmin} this request is associated with. * @param timeMs The new desired maximum time to lock in milliseconds. * A value of 0 means there is no restriction. @@ -329,6 +353,10 @@ public class DevicePolicyManager { /** * Make the device lock immediately, as if the lock screen timeout has * expired at the point of this call. + * + *

The calling device admin must have requested + * {@link DeviceAdminInfo#USES_POLICY_FORCE_LOCK} to be able to call + * this method; if it has not, a security exception will be thrown. */ public void lockNow() { if (mService != null) { @@ -345,6 +373,10 @@ public class DevicePolicyManager { * erasing all user data while next booting up. External storage such * as SD cards will not be erased. * + *

The calling device admin must have requested + * {@link DeviceAdminInfo#USES_POLICY_WIPE_DATA} to be able to call + * this method; if it has not, a security exception will be thrown. + * * @param flags Bit mask of additional options: currently must be 0. */ public void wipeData(int flags) { diff --git a/core/java/android/app/WallpaperInfo.java b/core/java/android/app/WallpaperInfo.java index 1612ac9491798..5ca3fb54e2fc6 100644 --- a/core/java/android/app/WallpaperInfo.java +++ b/core/java/android/app/WallpaperInfo.java @@ -21,6 +21,7 @@ import org.xmlpull.v1.XmlPullParserException; import android.content.ComponentName; import android.content.Context; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; @@ -204,7 +205,7 @@ public final class WallpaperInfo implements Parcelable { return pm.getDrawable(mService.serviceInfo.packageName, mThumbnailResource, - null); + mService.serviceInfo.applicationInfo); } /** @@ -212,25 +213,33 @@ public final class WallpaperInfo implements Parcelable { */ public CharSequence loadAuthor(PackageManager pm) throws NotFoundException { if (mAuthorResource <= 0) throw new NotFoundException(); - return pm.getText( - (mService.resolvePackageName != null) - ? mService.resolvePackageName - : getPackageName(), - mAuthorResource, - null); + String packageName = mService.resolvePackageName; + ApplicationInfo applicationInfo = null; + if (packageName == null) { + packageName = mService.serviceInfo.packageName; + applicationInfo = mService.serviceInfo.applicationInfo; + } + return pm.getText(packageName, mAuthorResource, applicationInfo); } /** * Return a brief summary of this wallpaper's behavior. */ public CharSequence loadDescription(PackageManager pm) throws NotFoundException { + String packageName = mService.resolvePackageName; + ApplicationInfo applicationInfo = null; + if (packageName == null) { + packageName = mService.serviceInfo.packageName; + applicationInfo = mService.serviceInfo.applicationInfo; + } + if (mService.serviceInfo.descriptionRes != 0) { + return pm.getText(packageName, mService.serviceInfo.descriptionRes, + applicationInfo); + + } if (mDescriptionResource <= 0) throw new NotFoundException(); - return pm.getText( - (mService.resolvePackageName != null) - ? mService.resolvePackageName - : getPackageName(), - mDescriptionResource, - null); + return pm.getText(packageName, mDescriptionResource, + mService.serviceInfo.applicationInfo); } /** diff --git a/core/java/android/content/pm/ComponentInfo.java b/core/java/android/content/pm/ComponentInfo.java index 73c924482b0ac..338c62b6c8207 100644 --- a/core/java/android/content/pm/ComponentInfo.java +++ b/core/java/android/content/pm/ComponentInfo.java @@ -1,3 +1,19 @@ +/* + * Copyright (C) 2008 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.content.pm; import android.graphics.drawable.Drawable; @@ -26,6 +42,13 @@ public class ComponentInfo extends PackageItemInfo { */ public String processName; + /** + * A string resource identifier (in the package's resources) containing + * a user-readable description of the component. From the "description" + * attribute or, if not set, 0. + */ + public int descriptionRes; + /** * Indicates whether or not this component may be instantiated. Note that this value can be * overriden by the one in its parent {@link ApplicationInfo}. @@ -47,6 +70,7 @@ public class ComponentInfo extends PackageItemInfo { super(orig); applicationInfo = orig.applicationInfo; processName = orig.processName; + descriptionRes = orig.descriptionRes; enabled = orig.enabled; exported = orig.exported; } @@ -108,6 +132,9 @@ public class ComponentInfo extends PackageItemInfo { super.dumpFront(pw, prefix); pw.println(prefix + "enabled=" + enabled + " exported=" + exported + " processName=" + processName); + if (descriptionRes != 0) { + pw.println(prefix + "description=" + descriptionRes); + } } protected void dumpBack(Printer pw, String prefix) { @@ -124,6 +151,7 @@ public class ComponentInfo extends PackageItemInfo { super.writeToParcel(dest, parcelableFlags); applicationInfo.writeToParcel(dest, parcelableFlags); dest.writeString(processName); + dest.writeInt(descriptionRes); dest.writeInt(enabled ? 1 : 0); dest.writeInt(exported ? 1 : 0); } @@ -132,6 +160,7 @@ public class ComponentInfo extends PackageItemInfo { super(source); applicationInfo = ApplicationInfo.CREATOR.createFromParcel(source); processName = source.readString(); + descriptionRes = source.readInt(); enabled = (source.readInt() != 0); exported = (source.readInt() != 0); } diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 8a5df324e60de..bbde0a6035bad 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -119,15 +119,18 @@ public class PackageParser { static class ParseComponentArgs extends ParsePackageItemArgs { final String[] sepProcesses; final int processRes; + final int descriptionRes; final int enabledRes; int flags; ParseComponentArgs(Package _owner, String[] _outError, int _nameRes, int _labelRes, int _iconRes, - String[] _sepProcesses, int _processRes,int _enabledRes) { + String[] _sepProcesses, int _processRes, + int _descriptionRes, int _enabledRes) { super(_owner, _outError, _nameRes, _labelRes, _iconRes); sepProcesses = _sepProcesses; processRes = _processRes; + descriptionRes = _descriptionRes; enabledRes = _enabledRes; } } @@ -1602,6 +1605,7 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestActivity_icon, mSeparateProcesses, com.android.internal.R.styleable.AndroidManifestActivity_process, + com.android.internal.R.styleable.AndroidManifestActivity_description, com.android.internal.R.styleable.AndroidManifestActivity_enabled); } @@ -1803,6 +1807,7 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestActivityAlias_icon, mSeparateProcesses, 0, + com.android.internal.R.styleable.AndroidManifestActivityAlias_description, com.android.internal.R.styleable.AndroidManifestActivityAlias_enabled); mParseActivityAliasArgs.tag = ""; } @@ -1837,6 +1842,9 @@ public class PackageParser { info.nonLocalizedLabel = target.info.nonLocalizedLabel; info.launchMode = target.info.launchMode; info.processName = target.info.processName; + if (info.descriptionRes == 0) { + info.descriptionRes = target.info.descriptionRes; + } info.screenOrientation = target.info.screenOrientation; info.taskAffinity = target.info.taskAffinity; info.theme = target.info.theme; @@ -1926,6 +1934,7 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestProvider_icon, mSeparateProcesses, com.android.internal.R.styleable.AndroidManifestProvider_process, + com.android.internal.R.styleable.AndroidManifestProvider_description, com.android.internal.R.styleable.AndroidManifestProvider_enabled); mParseProviderArgs.tag = ""; } @@ -2188,6 +2197,7 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestService_icon, mSeparateProcesses, com.android.internal.R.styleable.AndroidManifestService_process, + com.android.internal.R.styleable.AndroidManifestService_description, com.android.internal.R.styleable.AndroidManifestService_enabled); mParseServiceArgs.tag = ""; } @@ -2640,6 +2650,11 @@ public class PackageParser { owner.applicationInfo.processName, args.sa.getNonResourceString(args.processRes), args.flags, args.sepProcesses, args.outError); } + + if (args.descriptionRes != 0) { + outInfo.descriptionRes = args.sa.getResourceId(args.descriptionRes, 0); + } + outInfo.enabled = args.sa.getBoolean(args.enabledRes, true); } diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java index a09f23cf9a9d7..aa14c81fb2507 100755 --- a/core/java/android/widget/AppSecurityPermissions.java +++ b/core/java/android/widget/AppSecurityPermissions.java @@ -146,6 +146,19 @@ public class AppSecurityPermissions implements View.OnClickListener { } } + /** + * Utility to retrieve a view displaying a single permission. + */ + public static View getPermissionItemView(Context context, + CharSequence grpName, CharSequence description, boolean dangerous) { + LayoutInflater inflater = (LayoutInflater)context.getSystemService( + Context.LAYOUT_INFLATER_SERVICE); + Drawable icon = context.getResources().getDrawable(dangerous + ? R.drawable.ic_bullet_key_permission : R.drawable.ic_text_dot); + return getPermissionItemView(context, inflater, grpName, + description, dangerous, icon); + } + private void getAllUsedPermissions(int sharedUid, Set permSet) { String sharedPkgList[] = mPm.getPackagesForUid(sharedUid); if(sharedPkgList == null || (sharedPkgList.length == 0)) { @@ -304,15 +317,20 @@ public class AppSecurityPermissions implements View.OnClickListener { mNoPermsView.setVisibility(View.VISIBLE); } - private View getPermissionItemView(CharSequence grpName, String permList, + private View getPermissionItemView(CharSequence grpName, CharSequence permList, boolean dangerous) { - View permView = mInflater.inflate(R.layout.app_permission_item, null); - Drawable icon = dangerous ? mDangerousIcon : mNormalIcon; + return getPermissionItemView(mContext, mInflater, grpName, permList, + dangerous, dangerous ? mDangerousIcon : mNormalIcon); + } + + private static View getPermissionItemView(Context context, LayoutInflater inflater, + CharSequence grpName, CharSequence permList, boolean dangerous, Drawable icon) { + View permView = inflater.inflate(R.layout.app_permission_item, null); TextView permGrpView = (TextView) permView.findViewById(R.id.permission_group); TextView permDescView = (TextView) permView.findViewById(R.id.permission_list); if (dangerous) { - final Resources resources = mContext.getResources(); + final Resources resources = context.getResources(); permGrpView.setTextColor(resources.getColor(R.color.perms_dangerous_grp_color)); permDescView.setTextColor(resources.getColor(R.color.perms_dangerous_perm_color)); } diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index c6ef3a0c12fe8..7728c501a6a6b 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -938,6 +938,7 @@ is a period then it is appended to your package name. --> + @@ -1016,6 +1017,7 @@ is a period then it is appended to your package name. --> + @@ -1047,6 +1049,7 @@ is a period then it is appended to your package name. --> + @@ -1078,6 +1081,7 @@ + @@ -1130,6 +1134,7 @@ "com.mycompany.MyName". --> + Allows an application to read and write the cache filesystem. + + + + Limit password + + Restrict the types of passwords you + are allowed to use. + + Watch login attempts + + Monitor attempts to login to + the device, in particular to respond to failed login attempts. + + Reset your password + + Force your password + to a new value, requiring the administrator give it to you + before you can log in. + + Limit lock timeout + + Restrict the unlock timeout + durations you can select. + + Force lock + + Force the device to immediately lock, + requiring that its password is re-entered. + + Erase all data + + Perform a factory reset, deleting + all of your data without any confirmation from you. + diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java index 36da4eb3d65c4..fbd53173fd0be 100644 --- a/services/java/com/android/server/DevicePolicyManagerService.java +++ b/services/java/com/android/server/DevicePolicyManagerService.java @@ -91,7 +91,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return mIPowerManager; } - ActiveAdmin getActiveAdminForCallerLocked(ComponentName who) throws SecurityException { + ActiveAdmin getActiveAdminForCallerLocked(ComponentName who, int reqPolicy) + throws SecurityException { if (mActiveAdmin != null && mActiveAdmin.getUid() == Binder.getCallingUid()) { if (who != null) { if (!who.getPackageName().equals(mActiveAdmin.info.getActivityInfo().packageName) @@ -99,20 +100,27 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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()); } - - void sendAdminCommandLocked(ActiveAdmin policy, String action) { + void sendAdminCommandLocked(ActiveAdmin admin, String action) { Intent intent = new Intent(action); - intent.setComponent(policy.info.getComponent()); + intent.setComponent(admin.info.getComponent()); mContext.sendBroadcast(intent); } - void sendAdminCommandLocked(String action) { + void sendAdminCommandLocked(String action, int reqPolicy) { if (mActiveAdmin != null) { + if (mActiveAdmin.info.usesPolicy(reqPolicy)) { + return; + } sendAdminCommandLocked(mActiveAdmin, action); } } @@ -353,7 +361,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (who == null) { throw new NullPointerException("ComponentName is null"); } - ActiveAdmin ap = getActiveAdminForCallerLocked(who); + ActiveAdmin ap = getActiveAdminForCallerLocked(who, + DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD); if (ap.passwordMode != mode) { ap.passwordMode = mode; saveSettingsLocked(); @@ -373,7 +382,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (who == null) { throw new NullPointerException("ComponentName is null"); } - ActiveAdmin ap = getActiveAdminForCallerLocked(who); + ActiveAdmin ap = getActiveAdminForCallerLocked(who, + DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD); if (ap.minimumPasswordLength != length) { ap.minimumPasswordLength = length; saveSettingsLocked(); @@ -391,7 +401,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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); + getActiveAdminForCallerLocked(null, + DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD); return mActivePasswordMode >= getPasswordMode() && mActivePasswordLength >= getMinimumPasswordLength(); } @@ -401,7 +412,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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); + getActiveAdminForCallerLocked(null, + DeviceAdminInfo.USES_POLICY_WATCH_LOGIN); return mFailedPasswordAttempts; } } @@ -411,7 +423,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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); + getActiveAdminForCallerLocked(null, + DeviceAdminInfo.USES_POLICY_RESET_PASSWORD); mode = getPasswordMode(); if (password.length() < getMinimumPasswordLength()) { return false; @@ -436,7 +449,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (who == null) { throw new NullPointerException("ComponentName is null"); } - ActiveAdmin ap = getActiveAdminForCallerLocked(who); + ActiveAdmin ap = getActiveAdminForCallerLocked(who, + DeviceAdminInfo.USES_POLICY_LIMIT_UNLOCK); if (ap.maximumTimeToUnlock != timeMs) { ap.maximumTimeToUnlock = timeMs; @@ -468,7 +482,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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); + getActiveAdminForCallerLocked(null, + DeviceAdminInfo.USES_POLICY_FORCE_LOCK); // STOPSHIP need to implement. } } @@ -477,7 +492,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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); + getActiveAdminForCallerLocked(null, + DeviceAdminInfo.USES_POLICY_WIPE_DATA); } long ident = Binder.clearCallingIdentity(); try { @@ -501,7 +517,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mActivePasswordMode = mode; mActivePasswordLength = length; mFailedPasswordAttempts = 0; - sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_CHANGED); + sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_CHANGED, + DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD); } finally { Binder.restoreCallingIdentity(ident); } @@ -517,7 +534,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { long ident = Binder.clearCallingIdentity(); try { mFailedPasswordAttempts++; - sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_FAILED); + sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_FAILED, + DeviceAdminInfo.USES_POLICY_WATCH_LOGIN); } finally { Binder.restoreCallingIdentity(ident); } @@ -533,7 +551,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { long ident = Binder.clearCallingIdentity(); try { mFailedPasswordAttempts = 0; - sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_SUCCEEDED); + sendAdminCommandLocked(DeviceAdmin.ACTION_PASSWORD_SUCCEEDED, + DeviceAdminInfo.USES_POLICY_WATCH_LOGIN); } finally { Binder.restoreCallingIdentity(ident); }