/* * 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 com.android.server; import com.android.internal.os.storage.ExternalStorageFormatter; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.JournaledFile; import com.android.internal.util.XmlUtils; import com.android.internal.widget.LockPatternUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import android.app.Activity; import android.app.ActivityManagerNative; import android.app.AlarmManager; import android.app.AppGlobals; import android.app.PendingIntent; import android.app.admin.DeviceAdminInfo; import android.app.admin.DeviceAdminReceiver; import android.app.admin.DevicePolicyManager; import android.app.admin.IDevicePolicyManager; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.os.Binder; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.IBinder; import android.os.IPowerManager; import android.os.PowerManager; import android.os.Process; import android.os.RecoverySystem; import android.os.RemoteCallback; import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.util.PrintWriterPrinter; import android.util.Printer; import android.util.Slog; import android.util.SparseArray; import android.util.Xml; import android.view.IWindowManager; import android.view.WindowManagerPolicy; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintWriter; import java.text.DateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Set; /** * Implementation of the device policy APIs. */ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { private static final String DEVICE_POLICIES_XML = "device_policies.xml"; private static final String TAG = "DevicePolicyManagerService"; private static final int REQUEST_EXPIRE_PASSWORD = 5571; private static final long MS_PER_DAY = 86400 * 1000; private static final long EXPIRATION_GRACE_PERIOD_MS = 5 * MS_PER_DAY; // 5 days, in ms protected static final String ACTION_EXPIRED_PASSWORD_NOTIFICATION = "com.android.server.ACTION_EXPIRED_PASSWORD_NOTIFICATION"; private static final boolean DBG = false; final Context mContext; final PowerManager.WakeLock mWakeLock; IPowerManager mIPowerManager; IWindowManager mIWindowManager; public static class DevicePolicyData { int mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; int mActivePasswordLength = 0; int mActivePasswordUpperCase = 0; int mActivePasswordLowerCase = 0; int mActivePasswordLetters = 0; int mActivePasswordNumeric = 0; int mActivePasswordSymbols = 0; int mActivePasswordNonLetter = 0; int mFailedPasswordAttempts = 0; int mUserHandle;; int mPasswordOwner = -1; long mLastMaximumTimeToLock = -1; final HashMap mAdminMap = new HashMap(); final ArrayList mAdminList = new ArrayList(); public DevicePolicyData(int userHandle) { mUserHandle = userHandle; } } final SparseArray mUserData = new SparseArray(); Handler mHandler = new Handler(); BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); final int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, getSendingUserId()); if (Intent.ACTION_BOOT_COMPLETED.equals(action) || ACTION_EXPIRED_PASSWORD_NOTIFICATION.equals(action)) { Slog.v(TAG, "Sending password expiration notifications for action " + action + " for user " + userHandle); mHandler.post(new Runnable() { public void run() { handlePasswordExpirationNotification(getUserData(userHandle)); } }); } else if (Intent.ACTION_USER_REMOVED.equals(action)) { removeUserData(userHandle); } else if (Intent.ACTION_USER_STARTED.equals(action) || Intent.ACTION_PACKAGE_CHANGED.equals(action) || Intent.ACTION_PACKAGE_REMOVED.equals(action) || Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) { if (Intent.ACTION_USER_STARTED.equals(action)) { // Reset the policy data synchronized (DevicePolicyManagerService.this) { mUserData.remove(userHandle); } } handlePackagesChanged(userHandle); } } }; static class ActiveAdmin { final DeviceAdminInfo info; int passwordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; static final int DEF_MINIMUM_PASSWORD_LENGTH = 0; int minimumPasswordLength = DEF_MINIMUM_PASSWORD_LENGTH; static final int DEF_PASSWORD_HISTORY_LENGTH = 0; int passwordHistoryLength = DEF_PASSWORD_HISTORY_LENGTH; static final int DEF_MINIMUM_PASSWORD_UPPER_CASE = 0; int minimumPasswordUpperCase = DEF_MINIMUM_PASSWORD_UPPER_CASE; static final int DEF_MINIMUM_PASSWORD_LOWER_CASE = 0; int minimumPasswordLowerCase = DEF_MINIMUM_PASSWORD_LOWER_CASE; static final int DEF_MINIMUM_PASSWORD_LETTERS = 1; int minimumPasswordLetters = DEF_MINIMUM_PASSWORD_LETTERS; static final int DEF_MINIMUM_PASSWORD_NUMERIC = 1; int minimumPasswordNumeric = DEF_MINIMUM_PASSWORD_NUMERIC; static final int DEF_MINIMUM_PASSWORD_SYMBOLS = 1; int minimumPasswordSymbols = DEF_MINIMUM_PASSWORD_SYMBOLS; static final int DEF_MINIMUM_PASSWORD_NON_LETTER = 0; int minimumPasswordNonLetter = DEF_MINIMUM_PASSWORD_NON_LETTER; static final long DEF_MAXIMUM_TIME_TO_UNLOCK = 0; long maximumTimeToUnlock = DEF_MAXIMUM_TIME_TO_UNLOCK; static final int DEF_MAXIMUM_FAILED_PASSWORDS_FOR_WIPE = 0; int maximumFailedPasswordsForWipe = DEF_MAXIMUM_FAILED_PASSWORDS_FOR_WIPE; static final long DEF_PASSWORD_EXPIRATION_TIMEOUT = 0; long passwordExpirationTimeout = DEF_PASSWORD_EXPIRATION_TIMEOUT; static final long DEF_PASSWORD_EXPIRATION_DATE = 0; long passwordExpirationDate = DEF_PASSWORD_EXPIRATION_DATE; static final int DEF_KEYGUARD_FEATURES_DISABLED = 0; // none int disabledKeyguardFeatures = DEF_KEYGUARD_FEATURES_DISABLED; boolean encryptionRequested = false; boolean disableCamera = false; // TODO: review implementation decisions with frameworks team boolean specifiesGlobalProxy = false; String globalProxySpec = null; String globalProxyExclusionList = null; ActiveAdmin(DeviceAdminInfo _info) { info = _info; } int getUid() { return info.getActivityInfo().applicationInfo.uid; } public UserHandle getUserHandle() { return new UserHandle(UserHandle.getUserId(info.getActivityInfo().applicationInfo.uid)); } void writeToXml(XmlSerializer out) throws IllegalArgumentException, IllegalStateException, IOException { out.startTag(null, "policies"); info.writePoliciesToXml(out); out.endTag(null, "policies"); if (passwordQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { out.startTag(null, "password-quality"); out.attribute(null, "value", Integer.toString(passwordQuality)); out.endTag(null, "password-quality"); if (minimumPasswordLength != DEF_MINIMUM_PASSWORD_LENGTH) { out.startTag(null, "min-password-length"); out.attribute(null, "value", Integer.toString(minimumPasswordLength)); out.endTag(null, "min-password-length"); } if(passwordHistoryLength != DEF_PASSWORD_HISTORY_LENGTH) { out.startTag(null, "password-history-length"); out.attribute(null, "value", Integer.toString(passwordHistoryLength)); out.endTag(null, "password-history-length"); } if (minimumPasswordUpperCase != DEF_MINIMUM_PASSWORD_UPPER_CASE) { out.startTag(null, "min-password-uppercase"); out.attribute(null, "value", Integer.toString(minimumPasswordUpperCase)); out.endTag(null, "min-password-uppercase"); } if (minimumPasswordLowerCase != DEF_MINIMUM_PASSWORD_LOWER_CASE) { out.startTag(null, "min-password-lowercase"); out.attribute(null, "value", Integer.toString(minimumPasswordLowerCase)); out.endTag(null, "min-password-lowercase"); } if (minimumPasswordLetters != DEF_MINIMUM_PASSWORD_LETTERS) { out.startTag(null, "min-password-letters"); out.attribute(null, "value", Integer.toString(minimumPasswordLetters)); out.endTag(null, "min-password-letters"); } if (minimumPasswordNumeric != DEF_MINIMUM_PASSWORD_NUMERIC) { out.startTag(null, "min-password-numeric"); out.attribute(null, "value", Integer.toString(minimumPasswordNumeric)); out.endTag(null, "min-password-numeric"); } if (minimumPasswordSymbols != DEF_MINIMUM_PASSWORD_SYMBOLS) { out.startTag(null, "min-password-symbols"); out.attribute(null, "value", Integer.toString(minimumPasswordSymbols)); out.endTag(null, "min-password-symbols"); } if (minimumPasswordNonLetter > DEF_MINIMUM_PASSWORD_NON_LETTER) { out.startTag(null, "min-password-nonletter"); out.attribute(null, "value", Integer.toString(minimumPasswordNonLetter)); out.endTag(null, "min-password-nonletter"); } } if (maximumTimeToUnlock != DEF_MAXIMUM_TIME_TO_UNLOCK) { out.startTag(null, "max-time-to-unlock"); out.attribute(null, "value", Long.toString(maximumTimeToUnlock)); out.endTag(null, "max-time-to-unlock"); } if (maximumFailedPasswordsForWipe != DEF_MAXIMUM_FAILED_PASSWORDS_FOR_WIPE) { out.startTag(null, "max-failed-password-wipe"); out.attribute(null, "value", Integer.toString(maximumFailedPasswordsForWipe)); out.endTag(null, "max-failed-password-wipe"); } if (specifiesGlobalProxy) { out.startTag(null, "specifies-global-proxy"); out.attribute(null, "value", Boolean.toString(specifiesGlobalProxy)); out.endTag(null, "specifies_global_proxy"); if (globalProxySpec != null) { out.startTag(null, "global-proxy-spec"); out.attribute(null, "value", globalProxySpec); out.endTag(null, "global-proxy-spec"); } if (globalProxyExclusionList != null) { out.startTag(null, "global-proxy-exclusion-list"); out.attribute(null, "value", globalProxyExclusionList); out.endTag(null, "global-proxy-exclusion-list"); } } if (passwordExpirationTimeout != DEF_PASSWORD_EXPIRATION_TIMEOUT) { out.startTag(null, "password-expiration-timeout"); out.attribute(null, "value", Long.toString(passwordExpirationTimeout)); out.endTag(null, "password-expiration-timeout"); } if (passwordExpirationDate != DEF_PASSWORD_EXPIRATION_DATE) { out.startTag(null, "password-expiration-date"); out.attribute(null, "value", Long.toString(passwordExpirationDate)); out.endTag(null, "password-expiration-date"); } if (encryptionRequested) { out.startTag(null, "encryption-requested"); out.attribute(null, "value", Boolean.toString(encryptionRequested)); out.endTag(null, "encryption-requested"); } if (disableCamera) { out.startTag(null, "disable-camera"); out.attribute(null, "value", Boolean.toString(disableCamera)); out.endTag(null, "disable-camera"); } if (disabledKeyguardFeatures != DEF_KEYGUARD_FEATURES_DISABLED) { out.startTag(null, "disable-keyguard-features"); out.attribute(null, "value", Integer.toString(disabledKeyguardFeatures)); out.endTag(null, "disable-keyguard-features"); } } 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 ("policies".equals(tag)) { info.readPoliciesFromXml(parser); } else if ("password-quality".equals(tag)) { passwordQuality = Integer.parseInt( parser.getAttributeValue(null, "value")); } else if ("min-password-length".equals(tag)) { minimumPasswordLength = Integer.parseInt( parser.getAttributeValue(null, "value")); } else if ("password-history-length".equals(tag)) { passwordHistoryLength = Integer.parseInt( parser.getAttributeValue(null, "value")); } else if ("min-password-uppercase".equals(tag)) { minimumPasswordUpperCase = Integer.parseInt( parser.getAttributeValue(null, "value")); } else if ("min-password-lowercase".equals(tag)) { minimumPasswordLowerCase = Integer.parseInt( parser.getAttributeValue(null, "value")); } else if ("min-password-letters".equals(tag)) { minimumPasswordLetters = Integer.parseInt( parser.getAttributeValue(null, "value")); } else if ("min-password-numeric".equals(tag)) { minimumPasswordNumeric = Integer.parseInt( parser.getAttributeValue(null, "value")); } else if ("min-password-symbols".equals(tag)) { minimumPasswordSymbols = Integer.parseInt( parser.getAttributeValue(null, "value")); } else if ("min-password-nonletter".equals(tag)) { minimumPasswordNonLetter = 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")); } else if ("specifies-global-proxy".equals(tag)) { specifiesGlobalProxy = Boolean.parseBoolean( parser.getAttributeValue(null, "value")); } else if ("global-proxy-spec".equals(tag)) { globalProxySpec = parser.getAttributeValue(null, "value"); } else if ("global-proxy-exclusion-list".equals(tag)) { globalProxyExclusionList = parser.getAttributeValue(null, "value"); } else if ("password-expiration-timeout".equals(tag)) { passwordExpirationTimeout = Long.parseLong( parser.getAttributeValue(null, "value")); } else if ("password-expiration-date".equals(tag)) { passwordExpirationDate = Long.parseLong( parser.getAttributeValue(null, "value")); } else if ("encryption-requested".equals(tag)) { encryptionRequested = Boolean.parseBoolean( parser.getAttributeValue(null, "value")); } else if ("disable-camera".equals(tag)) { disableCamera = Boolean.parseBoolean( parser.getAttributeValue(null, "value")); } else if ("disable-keyguard-features".equals(tag)) { disabledKeyguardFeatures = Integer.parseInt( parser.getAttributeValue(null, "value")); } else { Slog.w(TAG, "Unknown admin tag: " + tag); } XmlUtils.skipCurrentTag(parser); } } void dump(String prefix, PrintWriter pw) { pw.print(prefix); pw.print("uid="); pw.println(getUid()); pw.print(prefix); pw.println("policies:"); ArrayList pols = info.getUsedPolicies(); if (pols != null) { for (int i=0; i= 0; i--) { ActiveAdmin aa = policy.mAdminList.get(i); try { if (pm.getPackageInfo(aa.info.getPackageName(), 0, userHandle) == null || pm.getReceiverInfo(aa.info.getComponent(), 0, userHandle) == null) { removed = true; policy.mAdminList.remove(i); } } catch (RemoteException re) { // Shouldn't happen } } if (removed) { validatePasswordOwnerLocked(policy); syncDeviceCapabilitiesLocked(policy); saveSettingsLocked(policy.mUserHandle); } } /** * Instantiates the service. */ public DevicePolicyManagerService(Context context) { mContext = context; mWakeLock = ((PowerManager)context.getSystemService(Context.POWER_SERVICE)) .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "DPM"); IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_BOOT_COMPLETED); filter.addAction(ACTION_EXPIRED_PASSWORD_NOTIFICATION); filter.addAction(Intent.ACTION_USER_REMOVED); filter.addAction(Intent.ACTION_USER_STARTED); context.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler); filter = new IntentFilter(); filter.addAction(Intent.ACTION_PACKAGE_CHANGED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE); filter.addDataScheme("package"); context.registerReceiverAsUser(mReceiver, UserHandle.ALL, filter, null, mHandler); } /** * Creates and loads the policy data from xml. * @param userHandle the user for whom to load the policy data * @return */ DevicePolicyData getUserData(int userHandle) { synchronized (this) { DevicePolicyData policy = mUserData.get(userHandle); if (policy == null) { policy = new DevicePolicyData(userHandle); mUserData.append(userHandle, policy); loadSettingsLocked(policy, userHandle); } return policy; } } void removeUserData(int userHandle) { synchronized (this) { if (userHandle == UserHandle.USER_OWNER) { Slog.w(TAG, "Tried to remove device policy file for user 0! Ignoring."); return; } DevicePolicyData policy = mUserData.get(userHandle); if (policy != null) { mUserData.remove(userHandle); } File policyFile = new File(Environment.getUserSystemDirectory(userHandle), DEVICE_POLICIES_XML); policyFile.delete(); Slog.i(TAG, "Removed device policy file " + policyFile.getAbsolutePath()); } } /** * Set an alarm for an upcoming event - expiration warning, expiration, or post-expiration * reminders. Clears alarm if no expirations are configured. */ protected void setExpirationAlarmCheckLocked(Context context, DevicePolicyData policy) { final long expiration = getPasswordExpirationLocked(null, policy.mUserHandle); final long now = System.currentTimeMillis(); final long timeToExpire = expiration - now; final long alarmTime; if (expiration == 0) { // No expirations are currently configured: Cancel alarm. alarmTime = 0; } else if (timeToExpire <= 0) { // The password has already expired: Repeat every 24 hours. alarmTime = now + MS_PER_DAY; } else { // Selecting the next alarm time: Roll forward to the next 24 hour multiple before // the expiration time. long alarmInterval = timeToExpire % MS_PER_DAY; if (alarmInterval == 0) { alarmInterval = MS_PER_DAY; } alarmTime = now + alarmInterval; } long token = Binder.clearCallingIdentity(); try { AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); PendingIntent pi = PendingIntent.getBroadcastAsUser(context, REQUEST_EXPIRE_PASSWORD, new Intent(ACTION_EXPIRED_PASSWORD_NOTIFICATION), PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT, new UserHandle(policy.mUserHandle)); am.cancel(pi); if (alarmTime != 0) { am.set(AlarmManager.RTC, alarmTime, pi); } } finally { Binder.restoreCallingIdentity(token); } } private IPowerManager getIPowerManager() { if (mIPowerManager == null) { IBinder b = ServiceManager.getService(Context.POWER_SERVICE); mIPowerManager = IPowerManager.Stub.asInterface(b); } return mIPowerManager; } private IWindowManager getWindowManager() { if (mIWindowManager == null) { IBinder b = ServiceManager.getService(Context.WINDOW_SERVICE); mIWindowManager = IWindowManager.Stub.asInterface(b); } return mIWindowManager; } ActiveAdmin getActiveAdminUncheckedLocked(ComponentName who, int userHandle) { ActiveAdmin admin = getUserData(userHandle).mAdminMap.get(who); 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, int reqPolicy) throws SecurityException { final int callingUid = Binder.getCallingUid(); final int userHandle = UserHandle.getUserId(callingUid); final DevicePolicyData policy = getUserData(userHandle); if (who != null) { ActiveAdmin admin = policy.mAdminMap.get(who); if (admin == null) { throw new SecurityException("No active admin " + who); } if (admin.getUid() != callingUid) { throw new SecurityException("Admin " + who + " is not owned by uid " + Binder.getCallingUid()); } if (!admin.info.usesPolicy(reqPolicy)) { throw new SecurityException("Admin " + admin.info.getComponent() + " did not specify uses-policy for: " + admin.info.getTagForPolicy(reqPolicy)); } return admin; } else { final int N = policy.mAdminList.size(); for (int i=0; i 0) { for (int i = 0; i < count; i++) { ActiveAdmin admin = policy.mAdminList.get(i); if (admin.info.usesPolicy(reqPolicy)) { sendAdminCommandLocked(admin, action); } } } } void removeActiveAdminLocked(final ComponentName adminReceiver, int userHandle) { final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle); if (admin != null) { sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED, new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { synchronized (DevicePolicyManagerService.this) { int userHandle = admin.getUserHandle().getIdentifier(); DevicePolicyData policy = getUserData(userHandle); boolean doProxyCleanup = admin.info.usesPolicy( DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY); policy.mAdminList.remove(admin); policy.mAdminMap.remove(adminReceiver); validatePasswordOwnerLocked(policy); syncDeviceCapabilitiesLocked(policy); if (doProxyCleanup) { resetGlobalProxyLocked(getUserData(userHandle)); } saveSettingsLocked(userHandle); updateMaximumTimeToLockLocked(policy); } } }); } } public DeviceAdminInfo findAdmin(ComponentName adminName, int userHandle) { enforceCrossUserPermission(userHandle); Intent resolveIntent = new Intent(); resolveIntent.setComponent(adminName); List infos = mContext.getPackageManager().queryBroadcastReceivers( resolveIntent, PackageManager.GET_META_DATA, userHandle); if (infos == null || infos.size() <= 0) { throw new IllegalArgumentException("Unknown admin: " + adminName); } try { return new DeviceAdminInfo(mContext, infos.get(0)); } catch (XmlPullParserException e) { Slog.w(TAG, "Bad device admin requested for user=" + userHandle + ": " + adminName, e); return null; } catch (IOException e) { Slog.w(TAG, "Bad device admin requested for user=" + userHandle + ": " + adminName, e); return null; } } private static JournaledFile makeJournaledFile(int userHandle) { final String base = userHandle == 0 ? "/data/system/" + DEVICE_POLICIES_XML : new File(Environment.getUserSystemDirectory(userHandle), DEVICE_POLICIES_XML) .getAbsolutePath(); return new JournaledFile(new File(base), new File(base + ".tmp")); } private void saveSettingsLocked(int userHandle) { DevicePolicyData policy = getUserData(userHandle); JournaledFile journal = makeJournaledFile(userHandle); FileOutputStream stream = null; try { stream = new FileOutputStream(journal.chooseForWrite(), false); XmlSerializer out = new FastXmlSerializer(); out.setOutput(stream, "utf-8"); out.startDocument(null, true); out.startTag(null, "policies"); final int N = policy.mAdminList.size(); for (int i=0; i= 0) { out.startTag(null, "password-owner"); out.attribute(null, "value", Integer.toString(policy.mPasswordOwner)); out.endTag(null, "password-owner"); } if (policy.mFailedPasswordAttempts != 0) { out.startTag(null, "failed-password-attempts"); out.attribute(null, "value", Integer.toString(policy.mFailedPasswordAttempts)); out.endTag(null, "failed-password-attempts"); } if (policy.mActivePasswordQuality != 0 || policy.mActivePasswordLength != 0 || policy.mActivePasswordUpperCase != 0 || policy.mActivePasswordLowerCase != 0 || policy.mActivePasswordLetters != 0 || policy.mActivePasswordNumeric != 0 || policy.mActivePasswordSymbols != 0 || policy.mActivePasswordNonLetter != 0) { out.startTag(null, "active-password"); out.attribute(null, "quality", Integer.toString(policy.mActivePasswordQuality)); out.attribute(null, "length", Integer.toString(policy.mActivePasswordLength)); out.attribute(null, "uppercase", Integer.toString(policy.mActivePasswordUpperCase)); out.attribute(null, "lowercase", Integer.toString(policy.mActivePasswordLowerCase)); out.attribute(null, "letters", Integer.toString(policy.mActivePasswordLetters)); out.attribute(null, "numeric", Integer .toString(policy.mActivePasswordNumeric)); out.attribute(null, "symbols", Integer.toString(policy.mActivePasswordSymbols)); out.attribute(null, "nonletter", Integer.toString(policy.mActivePasswordNonLetter)); out.endTag(null, "active-password"); } out.endTag(null, "policies"); out.endDocument(); stream.close(); journal.commit(); sendChangedNotification(userHandle); } catch (IOException e) { try { if (stream != null) { stream.close(); } } catch (IOException ex) { // Ignore } journal.rollback(); } } private void sendChangedNotification(int userHandle) { Intent intent = new Intent(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); long ident = Binder.clearCallingIdentity(); try { mContext.sendBroadcastAsUser(intent, new UserHandle(userHandle)); } finally { Binder.restoreCallingIdentity(ident); } } private void loadSettingsLocked(DevicePolicyData policy, int userHandle) { JournaledFile journal = makeJournaledFile(userHandle); FileInputStream stream = null; File file = journal.chooseForRead(); try { stream = new FileInputStream(file); XmlPullParser parser = Xml.newPullParser(); parser.setInput(stream, null); int type; while ((type=parser.next()) != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) { } String tag = parser.getName(); 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)) { String name = parser.getAttributeValue(null, "name"); try { DeviceAdminInfo dai = findAdmin( ComponentName.unflattenFromString(name), userHandle); if (DBG && (UserHandle.getUserId(dai.getActivityInfo().applicationInfo.uid) != userHandle)) { Slog.w(TAG, "findAdmin returned an incorrect uid " + dai.getActivityInfo().applicationInfo.uid + " for user " + userHandle); } if (dai != null) { ActiveAdmin ap = new ActiveAdmin(dai); ap.readFromXml(parser); policy.mAdminMap.put(ap.info.getComponent(), ap); policy.mAdminList.add(ap); } } catch (RuntimeException e) { Slog.w(TAG, "Failed loading admin " + name, e); } } else if ("failed-password-attempts".equals(tag)) { policy.mFailedPasswordAttempts = Integer.parseInt( parser.getAttributeValue(null, "value")); XmlUtils.skipCurrentTag(parser); } else if ("password-owner".equals(tag)) { policy.mPasswordOwner = Integer.parseInt( parser.getAttributeValue(null, "value")); XmlUtils.skipCurrentTag(parser); } else if ("active-password".equals(tag)) { policy.mActivePasswordQuality = Integer.parseInt( parser.getAttributeValue(null, "quality")); policy.mActivePasswordLength = Integer.parseInt( parser.getAttributeValue(null, "length")); policy.mActivePasswordUpperCase = Integer.parseInt( parser.getAttributeValue(null, "uppercase")); policy.mActivePasswordLowerCase = Integer.parseInt( parser.getAttributeValue(null, "lowercase")); policy.mActivePasswordLetters = Integer.parseInt( parser.getAttributeValue(null, "letters")); policy.mActivePasswordNumeric = Integer.parseInt( parser.getAttributeValue(null, "numeric")); policy.mActivePasswordSymbols = Integer.parseInt( parser.getAttributeValue(null, "symbols")); policy.mActivePasswordNonLetter = Integer.parseInt( parser.getAttributeValue(null, "nonletter")); XmlUtils.skipCurrentTag(parser); } else { Slog.w(TAG, "Unknown tag: " + tag); XmlUtils.skipCurrentTag(parser); } } } catch (NullPointerException e) { Slog.w(TAG, "failed parsing " + file + " " + e); } catch (NumberFormatException e) { Slog.w(TAG, "failed parsing " + file + " " + e); } catch (XmlPullParserException e) { Slog.w(TAG, "failed parsing " + file + " " + e); } catch (FileNotFoundException e) { // Don't be noisy, this is normal if we haven't defined any policies. } catch (IOException e) { Slog.w(TAG, "failed parsing " + file + " " + e); } catch (IndexOutOfBoundsException e) { Slog.w(TAG, "failed parsing " + file + " " + e); } try { if (stream != null) { stream.close(); } } catch (IOException e) { // Ignore } // Validate that what we stored for the password quality matches // sufficiently what is currently set. Note that this is only // a sanity check in case the two get out of sync; this should // never normally happen. LockPatternUtils utils = new LockPatternUtils(mContext); if (utils.getActivePasswordQuality() < policy.mActivePasswordQuality) { Slog.w(TAG, "Active password quality 0x" + Integer.toHexString(policy.mActivePasswordQuality) + " does not match actual quality 0x" + Integer.toHexString(utils.getActivePasswordQuality())); policy.mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; policy.mActivePasswordLength = 0; policy.mActivePasswordUpperCase = 0; policy.mActivePasswordLowerCase = 0; policy.mActivePasswordLetters = 0; policy.mActivePasswordNumeric = 0; policy.mActivePasswordSymbols = 0; policy.mActivePasswordNonLetter = 0; } validatePasswordOwnerLocked(policy); syncDeviceCapabilitiesLocked(policy); updateMaximumTimeToLockLocked(policy); } static void validateQualityConstant(int quality) { switch (quality) { case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED: case DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK: case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX: return; } throw new IllegalArgumentException("Invalid quality constant: 0x" + Integer.toHexString(quality)); } void validatePasswordOwnerLocked(DevicePolicyData policy) { if (policy.mPasswordOwner >= 0) { boolean haveOwner = false; for (int i = policy.mAdminList.size() - 1; i >= 0; i--) { if (policy.mAdminList.get(i).getUid() == policy.mPasswordOwner) { haveOwner = true; break; } } if (!haveOwner) { Slog.w(TAG, "Previous password owner " + policy.mPasswordOwner + " no longer active; disabling"); policy.mPasswordOwner = -1; } } } /** * Pushes down policy information to the system for any policies related to general device * capabilities that need to be enforced by lower level services (e.g. Camera services). */ void syncDeviceCapabilitiesLocked(DevicePolicyData policy) { // Ensure the status of the camera is synced down to the system. Interested native services // should monitor this value and act accordingly. boolean systemState = SystemProperties.getBoolean(SYSTEM_PROP_DISABLE_CAMERA, false); boolean cameraDisabled = getCameraDisabled(null, policy.mUserHandle); if (cameraDisabled != systemState) { long token = Binder.clearCallingIdentity(); try { String value = cameraDisabled ? "1" : "0"; Slog.v(TAG, "Change in camera state [" + SYSTEM_PROP_DISABLE_CAMERA + "] = " + value); SystemProperties.set(SYSTEM_PROP_DISABLE_CAMERA, value); } finally { Binder.restoreCallingIdentity(token); } } } public void systemReady() { synchronized (this) { loadSettingsLocked(getUserData(UserHandle.USER_OWNER), UserHandle.USER_OWNER); } } private void handlePasswordExpirationNotification(DevicePolicyData policy) { synchronized (this) { final long now = System.currentTimeMillis(); final int N = policy.mAdminList.size(); if (N <= 0) { return; } for (int i=0; i < N; i++) { ActiveAdmin admin = policy.mAdminList.get(i); if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD) && admin.passwordExpirationTimeout > 0L && admin.passwordExpirationDate > 0L && now >= admin.passwordExpirationDate - EXPIRATION_GRACE_PERIOD_MS) { sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_PASSWORD_EXPIRING); } } setExpirationAlarmCheckLocked(mContext, policy); } } /** * @param adminReceiver The admin to add * @param refreshing true = update an active admin, no error */ public void setActiveAdmin(ComponentName adminReceiver, boolean refreshing, int userHandle) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BIND_DEVICE_ADMIN, null); enforceCrossUserPermission(userHandle); DevicePolicyData policy = getUserData(userHandle); DeviceAdminInfo info = findAdmin(adminReceiver, userHandle); if (info == null) { throw new IllegalArgumentException("Bad admin: " + adminReceiver); } synchronized (this) { long ident = Binder.clearCallingIdentity(); try { if (!refreshing && getActiveAdminUncheckedLocked(adminReceiver, userHandle) != null) { throw new IllegalArgumentException("Admin is already added"); } ActiveAdmin newAdmin = new ActiveAdmin(info); policy.mAdminMap.put(adminReceiver, newAdmin); int replaceIndex = -1; if (refreshing) { final int N = policy.mAdminList.size(); for (int i=0; i < N; i++) { ActiveAdmin oldAdmin = policy.mAdminList.get(i); if (oldAdmin.info.getComponent().equals(adminReceiver)) { replaceIndex = i; break; } } } if (replaceIndex == -1) { policy.mAdminList.add(newAdmin); } else { policy.mAdminList.set(replaceIndex, newAdmin); } saveSettingsLocked(userHandle); sendAdminCommandLocked(newAdmin, DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED); } finally { Binder.restoreCallingIdentity(ident); } } } public boolean isAdminActive(ComponentName adminReceiver, int userHandle) { enforceCrossUserPermission(userHandle); synchronized (this) { return getActiveAdminUncheckedLocked(adminReceiver, userHandle) != null; } } public boolean hasGrantedPolicy(ComponentName adminReceiver, int policyId, int userHandle) { enforceCrossUserPermission(userHandle); synchronized (this) { ActiveAdmin administrator = getActiveAdminUncheckedLocked(adminReceiver, userHandle); if (administrator == null) { throw new SecurityException("No active admin " + adminReceiver); } return administrator.info.usesPolicy(policyId); } } public List getActiveAdmins(int userHandle) { enforceCrossUserPermission(userHandle); synchronized (this) { DevicePolicyData policy = getUserData(userHandle); final int N = policy.mAdminList.size(); if (N <= 0) { return null; } ArrayList res = new ArrayList(N); for (int i=0; i= 0 ms"); } ActiveAdmin ap = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD); // Calling this API automatically bumps the expiration date final long expiration = timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L; ap.passwordExpirationDate = expiration; ap.passwordExpirationTimeout = timeout; if (timeout > 0L) { Slog.w(TAG, "setPasswordExpiration(): password will expire on " + DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT) .format(new Date(expiration))); } saveSettingsLocked(userHandle); // in case this is the first one setExpirationAlarmCheckLocked(mContext, getUserData(userHandle)); } } /** * Return a single admin's expiration cycle time, or the min of all cycle times. * Returns 0 if not configured. */ public long getPasswordExpirationTimeout(ComponentName who, int userHandle) { enforceCrossUserPermission(userHandle); synchronized (this) { if (who != null) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); return admin != null ? admin.passwordExpirationTimeout : 0L; } long timeout = 0L; DevicePolicyData policy = getUserData(userHandle); final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { ActiveAdmin admin = policy.mAdminList.get(i); if (timeout == 0L || (admin.passwordExpirationTimeout != 0L && timeout > admin.passwordExpirationTimeout)) { timeout = admin.passwordExpirationTimeout; } } return timeout; } } /** * Return a single admin's expiration date/time, or the min (soonest) for all admins. * Returns 0 if not configured. */ private long getPasswordExpirationLocked(ComponentName who, int userHandle) { if (who != null) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); return admin != null ? admin.passwordExpirationDate : 0L; } long timeout = 0L; DevicePolicyData policy = getUserData(userHandle); final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { ActiveAdmin admin = policy.mAdminList.get(i); if (timeout == 0L || (admin.passwordExpirationDate != 0 && timeout > admin.passwordExpirationDate)) { timeout = admin.passwordExpirationDate; } } return timeout; } public long getPasswordExpiration(ComponentName who, int userHandle) { enforceCrossUserPermission(userHandle); synchronized (this) { return getPasswordExpirationLocked(who, userHandle); } } public void setPasswordMinimumUpperCase(ComponentName who, int length, int userHandle) { enforceCrossUserPermission(userHandle); synchronized (this) { if (who == null) { throw new NullPointerException("ComponentName is null"); } ActiveAdmin ap = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD); if (ap.minimumPasswordUpperCase != length) { ap.minimumPasswordUpperCase = length; saveSettingsLocked(userHandle); } } } public int getPasswordMinimumUpperCase(ComponentName who, int userHandle) { enforceCrossUserPermission(userHandle); synchronized (this) { int length = 0; if (who != null) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); return admin != null ? admin.minimumPasswordUpperCase : length; } DevicePolicyData policy = getUserData(userHandle); final int N = policy.mAdminList.size(); for (int i=0; i= getPasswordMinimumUpperCase(null, userHandle) && policy.mActivePasswordLowerCase >= getPasswordMinimumLowerCase(null, userHandle) && policy.mActivePasswordLetters >= getPasswordMinimumLetters(null, userHandle) && policy.mActivePasswordNumeric >= getPasswordMinimumNumeric(null, userHandle) && policy.mActivePasswordSymbols >= getPasswordMinimumSymbols(null, userHandle) && policy.mActivePasswordNonLetter >= getPasswordMinimumNonLetter(null, userHandle); } } public int getCurrentFailedPasswordAttempts(int userHandle) { enforceCrossUserPermission(userHandle); 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_WATCH_LOGIN); return getUserData(userHandle).mFailedPasswordAttempts; } } public void setMaximumFailedPasswordsForWipe(ComponentName who, int num, int userHandle) { enforceCrossUserPermission(userHandle); 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(userHandle); } } } public int getMaximumFailedPasswordsForWipe(ComponentName who, int userHandle) { enforceCrossUserPermission(userHandle); synchronized (this) { DevicePolicyData policy = getUserData(userHandle); int count = 0; if (who != null) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); return admin != null ? admin.maximumFailedPasswordsForWipe : count; } final int N = policy.mAdminList.size(); for (int i=0; i admin.maximumFailedPasswordsForWipe) { count = admin.maximumFailedPasswordsForWipe; } } return count; } } public boolean resetPassword(String password, int flags, int userHandle) { enforceCrossUserPermission(userHandle); int quality; 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_RESET_PASSWORD); quality = getPasswordQuality(null, userHandle); if (quality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { int realQuality = LockPatternUtils.computePasswordQuality(password); if (realQuality < quality && quality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) { Slog.w(TAG, "resetPassword: password quality 0x" + Integer.toHexString(quality) + " does not meet required quality 0x" + Integer.toHexString(quality)); return false; } quality = Math.max(realQuality, quality); } int length = getPasswordMinimumLength(null, userHandle); if (password.length() < length) { Slog.w(TAG, "resetPassword: password length " + password.length() + " does not meet required length " + length); return false; } if (quality == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) { int letters = 0; int uppercase = 0; int lowercase = 0; int numbers = 0; int symbols = 0; int nonletter = 0; for (int i = 0; i < password.length(); i++) { char c = password.charAt(i); if (c >= 'A' && c <= 'Z') { letters++; uppercase++; } else if (c >= 'a' && c <= 'z') { letters++; lowercase++; } else if (c >= '0' && c <= '9') { numbers++; nonletter++; } else { symbols++; nonletter++; } } int neededLetters = getPasswordMinimumLetters(null, userHandle); if(letters < neededLetters) { Slog.w(TAG, "resetPassword: number of letters " + letters + " does not meet required number of letters " + neededLetters); return false; } int neededNumbers = getPasswordMinimumNumeric(null, userHandle); if (numbers < neededNumbers) { Slog .w(TAG, "resetPassword: number of numerical digits " + numbers + " does not meet required number of numerical digits " + neededNumbers); return false; } int neededLowerCase = getPasswordMinimumLowerCase(null, userHandle); if (lowercase < neededLowerCase) { Slog.w(TAG, "resetPassword: number of lowercase letters " + lowercase + " does not meet required number of lowercase letters " + neededLowerCase); return false; } int neededUpperCase = getPasswordMinimumUpperCase(null, userHandle); if (uppercase < neededUpperCase) { Slog.w(TAG, "resetPassword: number of uppercase letters " + uppercase + " does not meet required number of uppercase letters " + neededUpperCase); return false; } int neededSymbols = getPasswordMinimumSymbols(null, userHandle); if (symbols < neededSymbols) { Slog.w(TAG, "resetPassword: number of special symbols " + symbols + " does not meet required number of special symbols " + neededSymbols); return false; } int neededNonLetter = getPasswordMinimumNonLetter(null, userHandle); if (nonletter < neededNonLetter) { Slog.w(TAG, "resetPassword: number of non-letter characters " + nonletter + " does not meet required number of non-letter characters " + neededNonLetter); return false; } } } int callingUid = Binder.getCallingUid(); DevicePolicyData policy = getUserData(userHandle); if (policy.mPasswordOwner >= 0 && policy.mPasswordOwner != callingUid) { Slog.w(TAG, "resetPassword: already set by another uid and not entered by user"); 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, quality, false, userHandle); synchronized (this) { int newOwner = (flags&DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) != 0 ? callingUid : -1; if (policy.mPasswordOwner != newOwner) { policy.mPasswordOwner = newOwner; saveSettingsLocked(userHandle); } } } finally { Binder.restoreCallingIdentity(ident); } return true; } public void setMaximumTimeToLock(ComponentName who, long timeMs, int userHandle) { enforceCrossUserPermission(userHandle); synchronized (this) { if (who == null) { throw new NullPointerException("ComponentName is null"); } ActiveAdmin ap = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_FORCE_LOCK); if (ap.maximumTimeToUnlock != timeMs) { ap.maximumTimeToUnlock = timeMs; saveSettingsLocked(userHandle); updateMaximumTimeToLockLocked(getUserData(userHandle)); } } } void updateMaximumTimeToLockLocked(DevicePolicyData policy) { long timeMs = getMaximumTimeToLock(null, policy.mUserHandle); if (policy.mLastMaximumTimeToLock == timeMs) { return; } long ident = Binder.clearCallingIdentity(); try { if (timeMs <= 0) { timeMs = Integer.MAX_VALUE; } else { // Make sure KEEP_SCREEN_ON is disabled, since that // would allow bypassing of the maximum time to lock. Settings.Global.putInt(mContext.getContentResolver(), Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0); } policy.mLastMaximumTimeToLock = timeMs; try { getIPowerManager().setMaximumScreenOffTimeoutFromDeviceAdmin((int)timeMs); } catch (RemoteException e) { Slog.w(TAG, "Failure talking with power manager", e); } } finally { Binder.restoreCallingIdentity(ident); } } public long getMaximumTimeToLock(ComponentName who, int userHandle) { enforceCrossUserPermission(userHandle); synchronized (this) { long time = 0; if (who != null) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); return admin != null ? admin.maximumTimeToUnlock : time; } DevicePolicyData policy = getUserData(userHandle); final int N = policy.mAdminList.size(); for (int i=0; i admin.maximumTimeToUnlock) { time = admin.maximumTimeToUnlock; } } return time; } } 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, DeviceAdminInfo.USES_POLICY_FORCE_LOCK); lockNowUnchecked(); } } private void lockNowUnchecked() { long ident = Binder.clearCallingIdentity(); try { // Power off the display getIPowerManager().goToSleep(SystemClock.uptimeMillis(), PowerManager.GO_TO_SLEEP_REASON_DEVICE_ADMIN); // Ensure the device is locked getWindowManager().lockNow(null); } catch (RemoteException e) { } finally { Binder.restoreCallingIdentity(ident); } } private boolean isExtStorageEncrypted() { String state = SystemProperties.get("vold.decrypt"); return !"".equals(state); } void wipeDataLocked(int flags) { // If the SD card is encrypted and non-removable, we have to force a wipe. boolean forceExtWipe = !Environment.isExternalStorageRemovable() && isExtStorageEncrypted(); boolean wipeExtRequested = (flags&DevicePolicyManager.WIPE_EXTERNAL_STORAGE) != 0; // Note: we can only do the wipe via ExternalStorageFormatter if the volume is not emulated. if ((forceExtWipe || wipeExtRequested) && !Environment.isExternalStorageEmulated()) { Intent intent = new Intent(ExternalStorageFormatter.FORMAT_AND_FACTORY_RESET); intent.putExtra(ExternalStorageFormatter.EXTRA_ALWAYS_RESET, true); intent.setComponent(ExternalStorageFormatter.COMPONENT_NAME); mWakeLock.acquire(10000); mContext.startService(intent); } else { try { RecoverySystem.rebootWipeUserData(mContext); } catch (IOException e) { Slog.w(TAG, "Failed requesting data wipe", e); } } } public void wipeData(int flags, final int userHandle) { enforceCrossUserPermission(userHandle); 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 { if (userHandle == UserHandle.USER_OWNER) { wipeDataLocked(flags); } else { lockNowUnchecked(); mHandler.post(new Runnable() { public void run() { try { ActivityManagerNative.getDefault().switchUser(0); ((UserManager) mContext.getSystemService(Context.USER_SERVICE)) .removeUser(userHandle); } catch (RemoteException re) { // Shouldn't happen } } }); } } finally { Binder.restoreCallingIdentity(ident); } } } public void getRemoveWarning(ComponentName comp, final RemoteCallback result, int userHandle) { enforceCrossUserPermission(userHandle); mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BIND_DEVICE_ADMIN, null); synchronized (this) { ActiveAdmin admin = getActiveAdminUncheckedLocked(comp, userHandle); if (admin == null) { try { result.sendResult(null); } catch (RemoteException e) { } return; } Intent intent = new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLE_REQUESTED); intent.setComponent(admin.info.getComponent()); mContext.sendOrderedBroadcastAsUser(intent, new UserHandle(userHandle), 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); } } public void setActivePasswordState(int quality, int length, int letters, int uppercase, int lowercase, int numbers, int symbols, int nonletter, int userHandle) { enforceCrossUserPermission(userHandle); mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BIND_DEVICE_ADMIN, null); DevicePolicyData p = getUserData(userHandle); validateQualityConstant(quality); synchronized (this) { if (p.mActivePasswordQuality != quality || p.mActivePasswordLength != length || p.mFailedPasswordAttempts != 0 || p.mActivePasswordLetters != letters || p.mActivePasswordUpperCase != uppercase || p.mActivePasswordLowerCase != lowercase || p.mActivePasswordNumeric != numbers || p.mActivePasswordSymbols != symbols || p.mActivePasswordNonLetter != nonletter) { long ident = Binder.clearCallingIdentity(); try { p.mActivePasswordQuality = quality; p.mActivePasswordLength = length; p.mActivePasswordLetters = letters; p.mActivePasswordLowerCase = lowercase; p.mActivePasswordUpperCase = uppercase; p.mActivePasswordNumeric = numbers; p.mActivePasswordSymbols = symbols; p.mActivePasswordNonLetter = nonletter; p.mFailedPasswordAttempts = 0; saveSettingsLocked(userHandle); updatePasswordExpirationsLocked(userHandle); setExpirationAlarmCheckLocked(mContext, p); sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_CHANGED, DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userHandle); } finally { Binder.restoreCallingIdentity(ident); } } } } /** * Called any time the device password is updated. Resets all password expiration clocks. */ private void updatePasswordExpirationsLocked(int userHandle) { DevicePolicyData policy = getUserData(userHandle); final int N = policy.mAdminList.size(); if (N > 0) { for (int i=0; i 0L ? (timeout + System.currentTimeMillis()) : 0L; admin.passwordExpirationDate = expiration; } } saveSettingsLocked(userHandle); } } public void reportFailedPasswordAttempt(int userHandle) { enforceCrossUserPermission(userHandle); mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BIND_DEVICE_ADMIN, null); synchronized (this) { DevicePolicyData policy = getUserData(userHandle); long ident = Binder.clearCallingIdentity(); try { policy.mFailedPasswordAttempts++; saveSettingsLocked(userHandle); int max = getMaximumFailedPasswordsForWipe(null, userHandle); if (max > 0 && policy.mFailedPasswordAttempts >= max) { wipeDataLocked(0); } sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_FAILED, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, userHandle); } finally { Binder.restoreCallingIdentity(ident); } } } public void reportSuccessfulPasswordAttempt(int userHandle) { enforceCrossUserPermission(userHandle); mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BIND_DEVICE_ADMIN, null); synchronized (this) { DevicePolicyData policy = getUserData(userHandle); if (policy.mFailedPasswordAttempts != 0 || policy.mPasswordOwner >= 0) { long ident = Binder.clearCallingIdentity(); try { policy.mFailedPasswordAttempts = 0; policy.mPasswordOwner = -1; saveSettingsLocked(userHandle); sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_SUCCEEDED, DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, userHandle); } finally { Binder.restoreCallingIdentity(ident); } } } } public ComponentName setGlobalProxy(ComponentName who, String proxySpec, String exclusionList, int userHandle) { enforceCrossUserPermission(userHandle); synchronized(this) { if (who == null) { throw new NullPointerException("ComponentName is null"); } // Only check if owner has set global proxy. We don't allow other users to set it. DevicePolicyData policy = getUserData(UserHandle.USER_OWNER); ActiveAdmin admin = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_SETS_GLOBAL_PROXY); // Scan through active admins and find if anyone has already // set the global proxy. Set compSet = policy.mAdminMap.keySet(); for (ComponentName component : compSet) { ActiveAdmin ap = policy.mAdminMap.get(component); if ((ap.specifiesGlobalProxy) && (!component.equals(who))) { // Another admin already sets the global proxy // Return it to the caller. return component; } } // If the user is not the owner, don't set the global proxy. Fail silently. if (UserHandle.getCallingUserId() != UserHandle.USER_OWNER) { Slog.w(TAG, "Only the owner is allowed to set the global proxy. User " + userHandle + " is not permitted."); return null; } if (proxySpec == null) { admin.specifiesGlobalProxy = false; admin.globalProxySpec = null; admin.globalProxyExclusionList = null; } else { admin.specifiesGlobalProxy = true; admin.globalProxySpec = proxySpec; admin.globalProxyExclusionList = exclusionList; } // Reset the global proxy accordingly // Do this using system permissions, as apps cannot write to secure settings long origId = Binder.clearCallingIdentity(); resetGlobalProxyLocked(policy); Binder.restoreCallingIdentity(origId); return null; } } public ComponentName getGlobalProxyAdmin(int userHandle) { enforceCrossUserPermission(userHandle); synchronized(this) { DevicePolicyData policy = getUserData(UserHandle.USER_OWNER); // Scan through active admins and find if anyone has already // set the global proxy. final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { ActiveAdmin ap = policy.mAdminList.get(i); if (ap.specifiesGlobalProxy) { // Device admin sets the global proxy // Return it to the caller. return ap.info.getComponent(); } } } // No device admin sets the global proxy. return null; } private void resetGlobalProxyLocked(DevicePolicyData policy) { final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { ActiveAdmin ap = policy.mAdminList.get(i); if (ap.specifiesGlobalProxy) { saveGlobalProxyLocked(ap.globalProxySpec, ap.globalProxyExclusionList); return; } } // No device admins defining global proxies - reset global proxy settings to none saveGlobalProxyLocked(null, null); } private void saveGlobalProxyLocked(String proxySpec, String exclusionList) { if (exclusionList == null) { exclusionList = ""; } if (proxySpec == null) { proxySpec = ""; } // Remove white spaces proxySpec = proxySpec.trim(); String data[] = proxySpec.split(":"); int proxyPort = 8080; if (data.length > 1) { try { proxyPort = Integer.parseInt(data[1]); } catch (NumberFormatException e) {} } exclusionList = exclusionList.trim(); ContentResolver res = mContext.getContentResolver(); Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_HOST, data[0]); Settings.Global.putInt(res, Settings.Global.GLOBAL_HTTP_PROXY_PORT, proxyPort); Settings.Global.putString(res, Settings.Global.GLOBAL_HTTP_PROXY_EXCLUSION_LIST, exclusionList); } /** * Set the storage encryption request for a single admin. Returns the new total request * status (for all admins). */ public int setStorageEncryption(ComponentName who, boolean encrypt, int userHandle) { enforceCrossUserPermission(userHandle); synchronized (this) { // Check for permissions if (who == null) { throw new NullPointerException("ComponentName is null"); } // Only owner can set storage encryption if (userHandle != UserHandle.USER_OWNER || UserHandle.getCallingUserId() != UserHandle.USER_OWNER) { Slog.w(TAG, "Only owner is allowed to set storage encryption. User " + UserHandle.getCallingUserId() + " is not permitted."); return 0; } ActiveAdmin ap = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_ENCRYPTED_STORAGE); // Quick exit: If the filesystem does not support encryption, we can exit early. if (!isEncryptionSupported()) { return DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED; } // (1) Record the value for the admin so it's sticky if (ap.encryptionRequested != encrypt) { ap.encryptionRequested = encrypt; saveSettingsLocked(userHandle); } DevicePolicyData policy = getUserData(UserHandle.USER_OWNER); // (2) Compute "max" for all admins boolean newRequested = false; final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { newRequested |= policy.mAdminList.get(i).encryptionRequested; } // Notify OS of new request setEncryptionRequested(newRequested); // Return the new global request status return newRequested ? DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE : DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE; } } /** * Get the current storage encryption request status for a given admin, or aggregate of all * active admins. */ public boolean getStorageEncryption(ComponentName who, int userHandle) { enforceCrossUserPermission(userHandle); synchronized (this) { // Check for permissions if a particular caller is specified if (who != null) { // When checking for a single caller, status is based on caller's request ActiveAdmin ap = getActiveAdminUncheckedLocked(who, userHandle); return ap != null ? ap.encryptionRequested : false; } // If no particular caller is specified, return the aggregate set of requests. // This is short circuited by returning true on the first hit. DevicePolicyData policy = getUserData(userHandle); final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { if (policy.mAdminList.get(i).encryptionRequested) { return true; } } return false; } } /** * Get the current encryption status of the device. */ public int getStorageEncryptionStatus(int userHandle) { enforceCrossUserPermission(userHandle); return getEncryptionStatus(); } /** * Hook to low-levels: This should report if the filesystem supports encrypted storage. */ private boolean isEncryptionSupported() { // Note, this can be implemented as // return getEncryptionStatus() != DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED; // But is provided as a separate internal method if there's a faster way to do a // simple check for supported-or-not. return getEncryptionStatus() != DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED; } /** * Hook to low-levels: Reporting the current status of encryption. * @return A value such as {@link DevicePolicyManager#ENCRYPTION_STATUS_UNSUPPORTED} or * {@link DevicePolicyManager#ENCRYPTION_STATUS_INACTIVE} or * {@link DevicePolicyManager#ENCRYPTION_STATUS_ACTIVE}. */ private int getEncryptionStatus() { String status = SystemProperties.get("ro.crypto.state", "unsupported"); if ("encrypted".equalsIgnoreCase(status)) { return DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE; } else if ("unencrypted".equalsIgnoreCase(status)) { return DevicePolicyManager.ENCRYPTION_STATUS_INACTIVE; } else { return DevicePolicyManager.ENCRYPTION_STATUS_UNSUPPORTED; } } /** * Hook to low-levels: If needed, record the new admin setting for encryption. */ private void setEncryptionRequested(boolean encrypt) { } /** * The system property used to share the state of the camera. The native camera service * is expected to read this property and act accordingly. */ public static final String SYSTEM_PROP_DISABLE_CAMERA = "sys.secpolicy.camera.disabled"; /** * Disables all device cameras according to the specified admin. */ public void setCameraDisabled(ComponentName who, boolean disabled, int userHandle) { enforceCrossUserPermission(userHandle); synchronized (this) { if (who == null) { throw new NullPointerException("ComponentName is null"); } ActiveAdmin ap = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DISABLE_CAMERA); if (ap.disableCamera != disabled) { ap.disableCamera = disabled; saveSettingsLocked(userHandle); } syncDeviceCapabilitiesLocked(getUserData(userHandle)); } } /** * Gets whether or not all device cameras are disabled for a given admin, or disabled for any * active admins. */ public boolean getCameraDisabled(ComponentName who, int userHandle) { synchronized (this) { if (who != null) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); return (admin != null) ? admin.disableCamera : false; } DevicePolicyData policy = getUserData(userHandle); // Determine whether or not the device camera is disabled for any active admins. final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { ActiveAdmin admin = policy.mAdminList.get(i); if (admin.disableCamera) { return true; } } return false; } } /** * Selectively disable keyguard features. */ public void setKeyguardDisabledFeatures(ComponentName who, int which, int userHandle) { enforceCrossUserPermission(userHandle); synchronized (this) { if (who == null) { throw new NullPointerException("ComponentName is null"); } ActiveAdmin ap = getActiveAdminForCallerLocked(who, DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_FEATURES); if (ap.disabledKeyguardFeatures != which) { ap.disabledKeyguardFeatures = which; saveSettingsLocked(userHandle); } syncDeviceCapabilitiesLocked(getUserData(userHandle)); } } /** * Gets the disabled state for features in keyguard for the given admin, * or the aggregate of all active admins if who is null. */ public int getKeyguardDisabledFeatures(ComponentName who, int userHandle) { enforceCrossUserPermission(userHandle); synchronized (this) { if (who != null) { ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); return (admin != null) ? admin.disabledKeyguardFeatures : 0; } // Determine which keyguard features are disabled for any active admins. DevicePolicyData policy = getUserData(userHandle); final int N = policy.mAdminList.size(); int which = 0; for (int i = 0; i < N; i++) { ActiveAdmin admin = policy.mAdminList.get(i); which |= admin.disabledKeyguardFeatures; } return which; } } private void enforceCrossUserPermission(int userHandle) { if (userHandle < 0) { throw new IllegalArgumentException("Invalid userId " + userHandle); } final int callingUid = Binder.getCallingUid(); if (userHandle == UserHandle.getUserId(callingUid)) return; if (callingUid != Process.SYSTEM_UID && callingUid != 0) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, "Must be system or have" + " INTERACT_ACROSS_USERS_FULL permission"); } } @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { pw.println("Permission Denial: can't dump DevicePolicyManagerService from from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()); return; } final Printer p = new PrintWriterPrinter(pw); synchronized (this) { p.println("Current Device Policy Manager state:"); int userCount = mUserData.size(); for (int u = 0; u < userCount; u++) { DevicePolicyData policy = getUserData(mUserData.keyAt(u)); p.println(" Enabled Device Admins (User " + policy.mUserHandle + "):"); final int N = policy.mAdminList.size(); for (int i=0; i