From df83afaf299666e99c519aa86e7e082b7c116e95 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Wed, 20 Jan 2010 13:37:26 -0800 Subject: [PATCH] More device policy manager / admin work. Update API with some new features, re-arrange how you check for valid passwords, and start hooking up the back-end implementation. --- api/current.xml | 125 +++++++++++------- .../java/android/app/DevicePolicyManager.java | 116 ++++++++++------ .../android/app/IDevicePolicyManager.aidl | 7 +- core/java/android/os/IPowerManager.aidl | 1 + .../internal/widget/LockPatternUtils.java | 58 +++++--- .../providers/settings/DatabaseHelper.java | 17 ++- .../server/DevicePolicyManagerService.java | 110 ++++++++++++--- .../android/server/PowerManagerService.java | 23 +++- 8 files changed, 322 insertions(+), 135 deletions(-) diff --git a/api/current.xml b/api/current.xml index 01ff582aac77d..689f5ddd8e079 100644 --- a/api/current.xml +++ b/api/current.xml @@ -20208,28 +20208,6 @@ deprecated="not deprecated" visibility="public" > - - - - + + + + + + + + + + - - - - + + + + + + Mode constants are ordered so that higher values are more restrictive; + * thus the highest requested mode constant (between the policy set here, + * the user's preference, and any other considerations) is the one that + * is in effect. + * * @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_NUMERIC}, - * or {@link #PASSWORD_MODE_ALPHANUMERIC}. + * {@link #PASSWORD_MODE_UNSPECIFIED}, {@link #PASSWORD_MODE_SOMETHING}, + * {@link #PASSWORD_MODE_NUMERIC}, or {@link #PASSWORD_MODE_ALPHANUMERIC}. */ public void setPasswordMode(ComponentName admin, int mode) { if (mService != null) { @@ -177,21 +193,6 @@ public class DevicePolicyManager { return PASSWORD_MODE_UNSPECIFIED; } - /** - * Retrieve the password mode associated with the last password the - * user selected. - */ - public int getActivePasswordMode() { - if (mService != null) { - try { - return mService.getActivePasswordMode(); - } catch (RemoteException e) { - Log.w(TAG, "Failed talking with device policy service", e); - } - } - return PASSWORD_MODE_UNSPECIFIED; - } - /** * Called by an application that is administering the device to set the * minimum allowed password length. After setting this, the user @@ -234,18 +235,22 @@ public class DevicePolicyManager { } /** - * Retrieve the password length associated with the last password the - * user selected. + * Determine whether the current password the user has set is sufficient + * to meet the policy requirements (mode, minimum length) that have been + * requested. + * + * @return Returns true if the password meets the current requirements, + * else false. */ - public int getActiveMinimumPasswordLength() { + public boolean isActivePasswordSufficient() { if (mService != null) { try { - return mService.getActiveMinimumPasswordLength(); + return mService.isActivePasswordSufficient(); } catch (RemoteException e) { Log.w(TAG, "Failed talking with device policy service", e); } } - return 0; + return false; } /** @@ -262,6 +267,30 @@ public class DevicePolicyManager { } return -1; } + + /** + * 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 + * of the current password mode, automatically adjusting the password mode + * higher if needed. (The string you give here is acceptable for any mode; + * if it contains only digits, that is still an acceptable alphanumeric + * password.) + * + * @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. + */ + public boolean resetPassword(String password) { + if (mService != null) { + try { + return mService.resetPassword(password); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + return false; + } /** * Called by an application that is administering the device to set the @@ -298,22 +327,25 @@ public class DevicePolicyManager { } /** - * Constant for {@link #wipeData}: perform a low-level format of data - * storage. + * Make the device lock immediately, as if the lock screen timeout has + * expired at the point of this call. */ - public static final int WIPE_LOW_LEVEL_FORMAT = 0x0001; - - /** - * Constant for {@link #wipeData}: also wipe any external storage. - */ - public static final int WIPE_EXTERNAL_STORAGE = 0x0002; + public void lockNow() { + if (mService != null) { + try { + mService.lockNow(); + } catch (RemoteException e) { + Log.w(TAG, "Failed talking with device policy service", e); + } + } + } /** * Ask the user date be wiped. This will cause the device to reboot, - * erasing all user data while next booting up. + * erasing all user data while next booting up. External storage such + * as SD cards will not be erased. * - * @param flags Bit mask of additional options: currently - * {@link #WIPE_LOW_LEVEL_FORMAT} and {@link #WIPE_EXTERNAL_STORAGE}. + * @param flags Bit mask of additional options: currently must be 0. */ public void wipeData(int flags) { if (mService != null) { diff --git a/core/java/android/app/IDevicePolicyManager.aidl b/core/java/android/app/IDevicePolicyManager.aidl index f62647fe48b62..7e38194807bd2 100644 --- a/core/java/android/app/IDevicePolicyManager.aidl +++ b/core/java/android/app/IDevicePolicyManager.aidl @@ -26,17 +26,20 @@ import android.content.ComponentName; interface IDevicePolicyManager { void setPasswordMode(in ComponentName who, int mode); int getPasswordMode(); - int getActivePasswordMode(); void setMinimumPasswordLength(in ComponentName who, int length); int getMinimumPasswordLength(); - int getActiveMinimumPasswordLength(); + boolean isActivePasswordSufficient(); int getCurrentFailedPasswordAttempts(); + boolean resetPassword(String password); + void setMaximumTimeToLock(in ComponentName who, long timeMs); long getMaximumTimeToLock(); + void lockNow(); + void wipeData(int flags); void setActiveAdmin(in ComponentName policyReceiver); diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index 23762caa8d5dc..b5408ae98b2a1 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -28,6 +28,7 @@ interface IPowerManager void setPokeLock(int pokey, IBinder lock, String tag); int getSupportedWakeLockFlags(); void setStayOnSetting(int val); + void setMaximumScreenOffTimeount(int timeMs); void preventScreenOn(boolean prevent); boolean isScreenOn(); void reboot(String reason); diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 9f7a3708759d5..66c0719aee46f 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -79,9 +79,9 @@ public class LockPatternUtils { * pin = digit-only password * password = alphanumeric password */ - public static final int MODE_PATTERN = 0; - public static final int MODE_PIN = 1; - public static final int MODE_PASSWORD = 2; + public static final int MODE_PATTERN = DevicePolicyManager.PASSWORD_MODE_SOMETHING; + public static final int MODE_PIN = DevicePolicyManager.PASSWORD_MODE_NUMERIC; + public static final int MODE_PASSWORD = DevicePolicyManager.PASSWORD_MODE_ALPHANUMERIC; /** * The minimum number of dots the user must include in a wrong pattern @@ -95,6 +95,7 @@ public class LockPatternUtils { private final static String PATTERN_EVER_CHOSEN_KEY = "lockscreen.patterneverchosen"; public final static String PASSWORD_TYPE_KEY = "lockscreen.password_type"; + private final Context mContext; private final ContentResolver mContentResolver; private DevicePolicyManager mDevicePolicyManager; private static String sLockPatternFilename; @@ -104,6 +105,7 @@ public class LockPatternUtils { * @param contentResolver Used to look up and save settings. */ public LockPatternUtils(Context context) { + mContext = context; mContentResolver = context.getContentResolver(); mDevicePolicyManager = (DevicePolicyManager)context.getSystemService(Context.DEVICE_POLICY_SERVICE); @@ -126,10 +128,6 @@ public class LockPatternUtils { return mDevicePolicyManager.getMinimumPasswordLength(); } - public int getActiveMinimumPasswordLength() { - return mDevicePolicyManager.getActiveMinimumPasswordLength(); - } - /** * Gets the device policy password mode. If the mode is non-specific, returns * MODE_PATTERN which allows the user to choose anything. @@ -154,10 +152,6 @@ public class LockPatternUtils { * * @return */ - public int getActivePasswordMode() { - return mDevicePolicyManager.getActivePasswordMode(); - } - public void reportFailedPasswordAttempt() { mDevicePolicyManager.reportFailedPasswordAttempt(); } @@ -278,6 +272,16 @@ public class LockPatternUtils { return getBoolean(PATTERN_EVER_CHOSEN_KEY); } + /** + * Clear any lock pattern or password. + */ + public void clearLock() { + saveLockPassword(null, LockPatternUtils.MODE_PATTERN); + setLockPatternEnabled(false); + saveLockPattern(null); + setLong(PASSWORD_TYPE_KEY, MODE_PATTERN); + } + /** * Save a lock pattern. * @param pattern The new pattern to save. @@ -295,11 +299,13 @@ public class LockPatternUtils { raf.write(hash, 0, hash.length); } raf.close(); - setBoolean(PATTERN_EVER_CHOSEN_KEY, true); - setLong(PASSWORD_TYPE_KEY, MODE_PATTERN); - if (pattern != null && isDevicePolicyActive()) { - setActivePasswordState(DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED, - pattern.size()); + if (pattern != null) { + setBoolean(PATTERN_EVER_CHOSEN_KEY, true); + setLong(PASSWORD_TYPE_KEY, MODE_PATTERN); + DevicePolicyManager dpm = (DevicePolicyManager)mContext.getSystemService( + Context.DEVICE_POLICY_SERVICE); + dpm.setActivePasswordState( + DevicePolicyManager.PASSWORD_MODE_SOMETHING, pattern.size()); } } catch (FileNotFoundException fnfe) { // Cant do much, unless we want to fail over to using the settings provider @@ -314,9 +320,8 @@ public class LockPatternUtils { * Save a lock password. * @param password The password to save */ - public void saveLockPassword(String password) { + public void saveLockPassword(String password, int mode) { // Compute the hash - boolean numericHint = password != null ? TextUtils.isDigitsOnly(password) : false; final byte[] hash = LockPatternUtils.passwordToHash(password); try { // Write the hash to file @@ -328,10 +333,15 @@ public class LockPatternUtils { raf.write(hash, 0, hash.length); } raf.close(); - setLong(PASSWORD_TYPE_KEY, numericHint ? MODE_PIN : MODE_PASSWORD); - if (password != null && isDevicePolicyActive()) { - setActivePasswordState(numericHint ? DevicePolicyManager.PASSWORD_MODE_NUMERIC - : DevicePolicyManager.PASSWORD_MODE_ALPHANUMERIC, password.length()); + if (password != null) { + int textMode = TextUtils.isDigitsOnly(password) ? MODE_PIN : MODE_PASSWORD; + if (textMode > mode) { + mode = textMode; + } + setLong(PASSWORD_TYPE_KEY, mode); + DevicePolicyManager dpm = (DevicePolicyManager)mContext.getSystemService( + Context.DEVICE_POLICY_SERVICE); + dpm.setActivePasswordState(mode, password.length()); } } catch (FileNotFoundException fnfe) { // Cant do much, unless we want to fail over to using the settings provider @@ -554,6 +564,7 @@ public class LockPatternUtils { } private boolean getBoolean(String systemSettingKey) { + // STOPSHIP: these need to be moved to secure settings! return 1 == android.provider.Settings.System.getInt( mContentResolver, @@ -561,6 +572,7 @@ public class LockPatternUtils { } private void setBoolean(String systemSettingKey, boolean enabled) { + // STOPSHIP: these need to be moved to secure settings! android.provider.Settings.System.putInt( mContentResolver, systemSettingKey, @@ -568,10 +580,12 @@ public class LockPatternUtils { } private long getLong(String systemSettingKey, long def) { + // STOPSHIP: these need to be moved to secure settings! return android.provider.Settings.System.getLong(mContentResolver, systemSettingKey, def); } private void setLong(String systemSettingKey, long value) { + // STOPSHIP: these need to be moved to secure settings! android.provider.Settings.System.putLong(mContentResolver, systemSettingKey, value); } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index 89d75b7ef5662..979955c2d941c 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -71,7 +71,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion' // is properly propagated through your change. Not doing so will result in a loss of user // settings. - private static final int DATABASE_VERSION = 46; + private static final int DATABASE_VERSION = 47; private Context mContext; @@ -580,6 +580,21 @@ public class DatabaseHelper extends SQLiteOpenHelper { upgradeVersion = 46; } + if (upgradeVersion == 46) { + /* + * The password mode constants have changed; reset back to no + * password. + */ + db.beginTransaction(); + try { + db.execSQL("DELETE FROM system WHERE name='lockscreen.password_type';"); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + upgradeVersion = 47; + } + if (upgradeVersion != currentVersion) { Log.w(TAG, "Got stuck trying to upgrade from version " + upgradeVersion diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java index e13ddc8174dbb..fd425381d413a 100644 --- a/services/java/com/android/server/DevicePolicyManagerService.java +++ b/services/java/com/android/server/DevicePolicyManagerService.java @@ -17,6 +17,7 @@ package com.android.server; import com.android.common.FastXmlSerializer; +import com.android.internal.widget.LockPatternUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -27,11 +28,19 @@ import android.app.DeviceAdminInfo; import android.app.DevicePolicyManager; import android.app.IDevicePolicyManager; import android.content.ComponentName; +import android.content.ContentResolver; 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.IBinder; +import android.os.IPowerManager; +import android.os.PowerManager; +import android.os.RecoverySystem; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.provider.Settings; import android.util.Log; import android.util.Xml; @@ -49,6 +58,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private final Context mContext; + IPowerManager mIPowerManager; + int mActivePasswordMode = DevicePolicyManager.PASSWORD_MODE_UNSPECIFIED; int mActivePasswordLength = 0; int mFailedPasswordAttempts = 0; @@ -75,8 +86,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mContext = context; } + private IPowerManager getIPowerManager() { + if (mIPowerManager == null) { + IBinder b = ServiceManager.getService(Context.POWER_SERVICE); + mIPowerManager = IPowerManager.Stub.asInterface(b); + } + return mIPowerManager; + } + ActiveAdmin getActiveAdminForCallerLocked(ComponentName who) throws SecurityException { - if (mActiveAdmin != null && mActiveAdmin.getUid() == Binder.getCallingPid()) { + if (mActiveAdmin != null && mActiveAdmin.getUid() == Binder.getCallingUid()) { if (who != null) { if (!who.getPackageName().equals(mActiveAdmin.info.getActivityInfo().packageName) || !who.getClassName().equals(mActiveAdmin.info.getActivityInfo().name)) { @@ -258,6 +277,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { if (!success) { Log.w(TAG, "No valid start tag found in policies file"); } + + long timeMs = getMaximumTimeToLock(); + if (timeMs <= 0) { + timeMs = Integer.MAX_VALUE; + } + try { + getIPowerManager().setMaximumScreenOffTimeount((int)timeMs); + } catch (RemoteException e) { + Log.w(TAG, "Failure talking with power manager", e); + } + } public void systemReady() { @@ -335,15 +365,6 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - public int getActivePasswordMode() { - 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); - return mActivePasswordMode; - } - } - public void setMinimumPasswordLength(ComponentName who, int length) { synchronized (this) { if (who == null) { @@ -363,12 +384,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - public int getActiveMinimumPasswordLength() { + public boolean isActivePasswordSufficient() { 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); - return mActivePasswordLength; + return mActivePasswordMode >= getPasswordMode() + && mActivePasswordLength >= getMinimumPasswordLength(); } } @@ -381,6 +403,31 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + public boolean resetPassword(String password) { + int mode; + 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); + mode = getPasswordMode(); + if (password.length() < getMinimumPasswordLength()) { + return false; + } + } + + // Don't do this with the lock held, because it is going to call + // back in to the service. + long ident = Binder.clearCallingIdentity(); + try { + LockPatternUtils utils = new LockPatternUtils(mContext); + utils.saveLockPassword(password, mode); + } finally { + Binder.restoreCallingIdentity(ident); + } + + return true; + } + public void setMaximumTimeToLock(ComponentName who, long timeMs) { synchronized (this) { if (who == null) { @@ -389,7 +436,21 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { ActiveAdmin ap = getActiveAdminForCallerLocked(who); if (ap.maximumTimeToUnlock != timeMs) { ap.maximumTimeToUnlock = timeMs; - saveSettingsLocked(); + + long ident = Binder.clearCallingIdentity(); + try { + saveSettingsLocked(); + if (timeMs <= 0) { + timeMs = Integer.MAX_VALUE; + } + try { + getIPowerManager().setMaximumScreenOffTimeount((int)timeMs); + } catch (RemoteException e) { + Log.w(TAG, "Failure talking with power manager", e); + } + } finally { + Binder.restoreCallingIdentity(ident); + } } } } @@ -400,17 +461,28 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } + public void lockNow() { + 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); + // STOPSHIP need to implement. + } + } + 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); - long ident = Binder.clearCallingIdentity(); - try { - Log.w(TAG, "*************** WIPE DATA HERE"); - } 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); } } diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index bf6996c794327..f106fc360f09e 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -169,7 +169,8 @@ class PowerManagerService extends IPowerManager.Stub private boolean mProximitySensorActive = false; private int mProximityPendingValue = -1; // -1 == nothing, 0 == inactive, 1 == active private long mLastProximityEventTime; - private int mTotalDelaySetting; + private int mScreenOffTimeoutSetting; + private int mMaximumScreenOffTimeout = Integer.MAX_VALUE; private int mKeylightDelay; private int mDimDelay; private int mScreenOffDelay; @@ -378,6 +379,16 @@ class PowerManagerService extends IPowerManager.Stub Settings.System.STAY_ON_WHILE_PLUGGED_IN, val); } + public void setMaximumScreenOffTimeount(int timeMs) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.WRITE_SECURE_SETTINGS, null); + synchronized (mLocks) { + mMaximumScreenOffTimeout = timeMs; + // recalculate everything + setScreenOffTimeoutsLocked(); + } + } + private class SettingsObserver implements Observer { private int getInt(String name) { return mSettings.getValues(name).getAsInteger(Settings.System.VALUE); @@ -390,7 +401,7 @@ class PowerManagerService extends IPowerManager.Stub updateWakeLockLocked(); // SCREEN_OFF_TIMEOUT - mTotalDelaySetting = getInt(SCREEN_OFF_TIMEOUT); + mScreenOffTimeoutSetting = getInt(SCREEN_OFF_TIMEOUT); // DIM_SCREEN //mDimScreen = getInt(DIM_SCREEN) != 0; @@ -935,7 +946,8 @@ class PowerManagerService extends IPowerManager.Stub pw.println(" mPreventScreenOn=" + mPreventScreenOn + " mScreenBrightnessOverride=" + mScreenBrightnessOverride + " mButtonBrightnessOverride=" + mButtonBrightnessOverride); - pw.println(" mTotalDelaySetting=" + mTotalDelaySetting); + pw.println(" mScreenOffTimeoutSetting=" + mScreenOffTimeoutSetting + + " mMaximumScreenOffTimeout=" + mMaximumScreenOffTimeout); pw.println(" mLastScreenOnTime=" + mLastScreenOnTime); pw.println(" mBroadcastWakeLock=" + mBroadcastWakeLock); pw.println(" mStayOnWhilePluggedInScreenDimLock=" + mStayOnWhilePluggedInScreenDimLock); @@ -2285,7 +2297,10 @@ class PowerManagerService extends IPowerManager.Stub mDimDelay = -1; mScreenOffDelay = 0; } else { - int totalDelay = mTotalDelaySetting; + int totalDelay = mScreenOffTimeoutSetting; + if (totalDelay > mMaximumScreenOffTimeout) { + totalDelay = mMaximumScreenOffTimeout; + } mKeylightDelay = LONG_KEYLIGHT_DELAY; if (totalDelay < 0) { mScreenOffDelay = Integer.MAX_VALUE;