diff options
Diffstat (limited to 'services/java/com/android')
29 files changed, 2206 insertions, 727 deletions
diff --git a/services/java/com/android/server/AlarmManagerService.java b/services/java/com/android/server/AlarmManagerService.java index f4ad756..f960833 100644 --- a/services/java/com/android/server/AlarmManagerService.java +++ b/services/java/com/android/server/AlarmManagerService.java @@ -353,7 +353,7 @@ class AlarmManagerService extends IAlarmManager.Stub { while (it.hasNext()) { Alarm alarm = it.next(); - if (UserHandle.getUserId(alarm.operation.getTargetUid()) == userHandle) { + if (UserHandle.getUserId(alarm.operation.getCreatorUid()) == userHandle) { it.remove(); } } diff --git a/services/java/com/android/server/DevicePolicyManagerService.java b/services/java/com/android/server/DevicePolicyManagerService.java index 28a4310..aec5d6e 100644 --- a/services/java/com/android/server/DevicePolicyManagerService.java +++ b/services/java/com/android/server/DevicePolicyManagerService.java @@ -16,7 +16,6 @@ package com.android.server; -import com.android.internal.content.PackageMonitor; import com.android.internal.os.storage.ExternalStorageFormatter; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.JournaledFile; @@ -28,7 +27,9 @@ 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; @@ -40,6 +41,7 @@ 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; @@ -49,6 +51,7 @@ 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; @@ -56,10 +59,12 @@ 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; @@ -82,56 +87,86 @@ 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 EXPIRATION_GRACE_PERIOD_MS = 5 * 86400 * 1000; // 5 days, in ms + 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 long MS_PER_DAY = 86400 * 1000; + private static final boolean DBG = false; final Context mContext; - final MyPackageMonitor mMonitor; final PowerManager.WakeLock mWakeLock; IPowerManager mIPowerManager; IWindowManager mIWindowManager; - 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 mPasswordOwner = -1; - Handler mHandler = new Handler(); + 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<ComponentName, ActiveAdmin> mAdminMap + = new HashMap<ComponentName, ActiveAdmin>(); + final ArrayList<ActiveAdmin> mAdminList + = new ArrayList<ActiveAdmin>(); + + public DevicePolicyData(int userHandle) { + mUserHandle = userHandle; + } + } - long mLastMaximumTimeToLock = -1; + final SparseArray<DevicePolicyData> mUserData = new SparseArray<DevicePolicyData>(); - final HashMap<ComponentName, ActiveAdmin> mAdminMap - = new HashMap<ComponentName, ActiveAdmin>(); - final ArrayList<ActiveAdmin> mAdminList - = new ArrayList<ActiveAdmin>(); + Handler mHandler = new Handler(); BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); + 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); + Slog.v(TAG, "Sending password expiration notifications for action " + action + + " for user " + userHandle); mHandler.post(new Runnable() { public void run() { - handlePasswordExpirationNotification(); + 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); } } }; @@ -194,6 +229,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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"); @@ -421,39 +460,28 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - class MyPackageMonitor extends PackageMonitor { - @Override - public void onSomePackagesChanged() { - synchronized (DevicePolicyManagerService.this) { - boolean removed = false; - for (int i=mAdminList.size()-1; i>=0; i--) { - ActiveAdmin aa = mAdminList.get(i); - int change = isPackageDisappearing(aa.info.getPackageName()); - if (change == PACKAGE_PERMANENT_CHANGE - || change == PACKAGE_TEMPORARY_CHANGE) { - Slog.w(TAG, "Admin unexpectedly uninstalled: " - + aa.info.getComponent()); - removed = true; - mAdminList.remove(i); - } else if (isPackageModified(aa.info.getPackageName())) { - try { - mContext.getPackageManager().getReceiverInfo( - aa.info.getComponent(), 0); - } catch (NameNotFoundException e) { - Slog.w(TAG, "Admin package change removed component: " - + aa.info.getComponent()); - removed = true; - mAdminList.remove(i); - } - } - } - if (removed) { - validatePasswordOwnerLocked(); - syncDeviceCapabilitiesLocked(); - saveSettingsLocked(); + private void handlePackagesChanged(int userHandle) { + boolean removed = false; + Slog.d(TAG, "Handling package changes for user " + userHandle); + DevicePolicyData policy = getUserData(userHandle); + IPackageManager pm = AppGlobals.getPackageManager(); + for (int i = policy.mAdminList.size() - 1; 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); + } } /** @@ -461,22 +489,62 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { */ public DevicePolicyManagerService(Context context) { mContext = context; - mMonitor = new MyPackageMonitor(); - mMonitor.register(context, null, true); 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); - context.registerReceiver(mReceiver, filter); + 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) { - final long expiration = getPasswordExpirationLocked(null); + 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; @@ -499,9 +567,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { long token = Binder.clearCallingIdentity(); try { AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); - PendingIntent pi = PendingIntent.getBroadcast(context, REQUEST_EXPIRE_PASSWORD, + PendingIntent pi = PendingIntent.getBroadcastAsUser(context, REQUEST_EXPIRE_PASSWORD, new Intent(ACTION_EXPIRED_PASSWORD_NOTIFICATION), - PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT); + PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT, + new UserHandle(policy.mUserHandle)); am.cancel(pi); if (alarmTime != 0) { am.set(AlarmManager.RTC, alarmTime, pi); @@ -527,8 +596,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return mIWindowManager; } - ActiveAdmin getActiveAdminUncheckedLocked(ComponentName who) { - ActiveAdmin admin = mAdminMap.get(who); + 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)) { @@ -540,8 +609,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 = mAdminMap.get(who); + ActiveAdmin admin = policy.mAdminMap.get(who); if (admin == null) { throw new SecurityException("No active admin " + who); } @@ -556,9 +627,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } return admin; } else { - final int N = mAdminList.size(); + final int N = policy.mAdminList.size(); for (int i=0; i<N; i++) { - ActiveAdmin admin = mAdminList.get(i); + ActiveAdmin admin = policy.mAdminList.get(i); if (admin.getUid() == callingUid && admin.info.usesPolicy(reqPolicy)) { return admin; } @@ -579,18 +650,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { intent.putExtra("expiration", admin.passwordExpirationDate); } if (result != null) { - mContext.sendOrderedBroadcastAsUser(intent, UserHandle.OWNER, + mContext.sendOrderedBroadcastAsUser(intent, admin.getUserHandle(), null, result, mHandler, Activity.RESULT_OK, null, null); } else { mContext.sendBroadcastAsUser(intent, UserHandle.OWNER); } } - void sendAdminCommandLocked(String action, int reqPolicy) { - final int N = mAdminList.size(); - if (N > 0) { - for (int i=0; i<N; i++) { - ActiveAdmin admin = mAdminList.get(i); + void sendAdminCommandLocked(String action, int reqPolicy, int userHandle) { + final DevicePolicyData policy = getUserData(userHandle); + final int count = policy.mAdminList.size(); + if (count > 0) { + for (int i = 0; i < count; i++) { + ActiveAdmin admin = policy.mAdminList.get(i); if (admin.info.usesPolicy(reqPolicy)) { sendAdminCommandLocked(admin, action); } @@ -598,8 +670,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - void removeActiveAdminLocked(final ComponentName adminReceiver) { - final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver); + void removeActiveAdminLocked(final ComponentName adminReceiver, int userHandle) { + final ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle); if (admin != null) { sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLED, @@ -607,28 +679,31 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { @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); - mAdminList.remove(admin); - mAdminMap.remove(adminReceiver); - validatePasswordOwnerLocked(); - syncDeviceCapabilitiesLocked(); + policy.mAdminList.remove(admin); + policy.mAdminMap.remove(adminReceiver); + validatePasswordOwnerLocked(policy); + syncDeviceCapabilitiesLocked(policy); if (doProxyCleanup) { - resetGlobalProxyLocked(); + resetGlobalProxyLocked(getUserData(userHandle)); } - saveSettingsLocked(); - updateMaximumTimeToLockLocked(); + saveSettingsLocked(userHandle); + updateMaximumTimeToLockLocked(policy); } } }); } } - public DeviceAdminInfo findAdmin(ComponentName adminName) { + public DeviceAdminInfo findAdmin(ComponentName adminName, int userHandle) { + enforceCrossUserPermission(userHandle); Intent resolveIntent = new Intent(); resolveIntent.setComponent(adminName); List<ResolveInfo> infos = mContext.getPackageManager().queryBroadcastReceivers( - resolveIntent, PackageManager.GET_META_DATA); + resolveIntent, PackageManager.GET_META_DATA, userHandle); if (infos == null || infos.size() <= 0) { throw new IllegalArgumentException("Unknown admin: " + adminName); } @@ -636,21 +711,25 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { try { return new DeviceAdminInfo(mContext, infos.get(0)); } catch (XmlPullParserException e) { - Slog.w(TAG, "Bad device admin requested: " + adminName, 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: " + adminName, e); + Slog.w(TAG, "Bad device admin requested for user=" + userHandle + ": " + adminName, e); return null; } } - private static JournaledFile makeJournaledFile() { - final String base = "/data/system/device_policies.xml"; + 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() { - JournaledFile journal = makeJournaledFile(); + private void saveSettingsLocked(int userHandle) { + DevicePolicyData policy = getUserData(userHandle); + JournaledFile journal = makeJournaledFile(userHandle); FileOutputStream stream = null; try { stream = new FileOutputStream(journal.chooseForWrite(), false); @@ -660,9 +739,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { out.startTag(null, "policies"); - final int N = mAdminList.size(); + final int N = policy.mAdminList.size(); for (int i=0; i<N; i++) { - ActiveAdmin ap = mAdminList.get(i); + ActiveAdmin ap = policy.mAdminList.get(i); if (ap != null) { out.startTag(null, "admin"); out.attribute(null, "name", ap.info.getComponent().flattenToString()); @@ -671,32 +750,32 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - if (mPasswordOwner >= 0) { + if (policy.mPasswordOwner >= 0) { out.startTag(null, "password-owner"); - out.attribute(null, "value", Integer.toString(mPasswordOwner)); + out.attribute(null, "value", Integer.toString(policy.mPasswordOwner)); out.endTag(null, "password-owner"); } - if (mFailedPasswordAttempts != 0) { + if (policy.mFailedPasswordAttempts != 0) { out.startTag(null, "failed-password-attempts"); - out.attribute(null, "value", Integer.toString(mFailedPasswordAttempts)); + out.attribute(null, "value", Integer.toString(policy.mFailedPasswordAttempts)); out.endTag(null, "failed-password-attempts"); } - if (mActivePasswordQuality != 0 || mActivePasswordLength != 0 - || mActivePasswordUpperCase != 0 || mActivePasswordLowerCase != 0 - || mActivePasswordLetters != 0 || mActivePasswordNumeric != 0 - || mActivePasswordSymbols != 0 || mActivePasswordNonLetter != 0) { + 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(mActivePasswordQuality)); - out.attribute(null, "length", Integer.toString(mActivePasswordLength)); - out.attribute(null, "uppercase", Integer.toString(mActivePasswordUpperCase)); - out.attribute(null, "lowercase", Integer.toString(mActivePasswordLowerCase)); - out.attribute(null, "letters", Integer.toString(mActivePasswordLetters)); + 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(mActivePasswordNumeric)); - out.attribute(null, "symbols", Integer.toString(mActivePasswordSymbols)); - out.attribute(null, "nonletter", Integer.toString(mActivePasswordNonLetter)); + .toString(policy.mActivePasswordNumeric)); + out.attribute(null, "symbols", Integer.toString(policy.mActivePasswordSymbols)); + out.attribute(null, "nonletter", Integer.toString(policy.mActivePasswordNonLetter)); out.endTag(null, "active-password"); } @@ -705,7 +784,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { out.endDocument(); stream.close(); journal.commit(); - sendChangedNotification(); + sendChangedNotification(userHandle); } catch (IOException e) { try { if (stream != null) { @@ -718,20 +797,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - private void sendChangedNotification() { + 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 { - // TODO: This shouldn't be sent to all users, if DPM is per user. - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); + mContext.sendBroadcastAsUser(intent, new UserHandle(userHandle)); } finally { Binder.restoreCallingIdentity(ident); } } - private void loadSettingsLocked() { - JournaledFile journal = makeJournaledFile(); + private void loadSettingsLocked(DevicePolicyData policy, int userHandle) { + JournaledFile journal = makeJournaledFile(userHandle); FileInputStream stream = null; File file = journal.chooseForRead(); try { @@ -760,40 +838,46 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { String name = parser.getAttributeValue(null, "name"); try { DeviceAdminInfo dai = findAdmin( - ComponentName.unflattenFromString(name)); + 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); - mAdminMap.put(ap.info.getComponent(), ap); - mAdminList.add(ap); + 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)) { - mFailedPasswordAttempts = Integer.parseInt( + policy.mFailedPasswordAttempts = Integer.parseInt( parser.getAttributeValue(null, "value")); XmlUtils.skipCurrentTag(parser); } else if ("password-owner".equals(tag)) { - mPasswordOwner = Integer.parseInt( + policy.mPasswordOwner = Integer.parseInt( parser.getAttributeValue(null, "value")); XmlUtils.skipCurrentTag(parser); } else if ("active-password".equals(tag)) { - mActivePasswordQuality = Integer.parseInt( + policy.mActivePasswordQuality = Integer.parseInt( parser.getAttributeValue(null, "quality")); - mActivePasswordLength = Integer.parseInt( + policy.mActivePasswordLength = Integer.parseInt( parser.getAttributeValue(null, "length")); - mActivePasswordUpperCase = Integer.parseInt( + policy.mActivePasswordUpperCase = Integer.parseInt( parser.getAttributeValue(null, "uppercase")); - mActivePasswordLowerCase = Integer.parseInt( + policy.mActivePasswordLowerCase = Integer.parseInt( parser.getAttributeValue(null, "lowercase")); - mActivePasswordLetters = Integer.parseInt( + policy.mActivePasswordLetters = Integer.parseInt( parser.getAttributeValue(null, "letters")); - mActivePasswordNumeric = Integer.parseInt( + policy.mActivePasswordNumeric = Integer.parseInt( parser.getAttributeValue(null, "numeric")); - mActivePasswordSymbols = Integer.parseInt( + policy.mActivePasswordSymbols = Integer.parseInt( parser.getAttributeValue(null, "symbols")); - mActivePasswordNonLetter = Integer.parseInt( + policy.mActivePasswordNonLetter = Integer.parseInt( parser.getAttributeValue(null, "nonletter")); XmlUtils.skipCurrentTag(parser); } else { @@ -827,24 +911,24 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // a sanity check in case the two get out of sync; this should // never normally happen. LockPatternUtils utils = new LockPatternUtils(mContext); - if (utils.getActivePasswordQuality() < mActivePasswordQuality) { + if (utils.getActivePasswordQuality() < policy.mActivePasswordQuality) { Slog.w(TAG, "Active password quality 0x" - + Integer.toHexString(mActivePasswordQuality) + + Integer.toHexString(policy.mActivePasswordQuality) + " does not match actual quality 0x" + Integer.toHexString(utils.getActivePasswordQuality())); - mActivePasswordQuality = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; - mActivePasswordLength = 0; - mActivePasswordUpperCase = 0; - mActivePasswordLowerCase = 0; - mActivePasswordLetters = 0; - mActivePasswordNumeric = 0; - mActivePasswordSymbols = 0; - mActivePasswordNonLetter = 0; + 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(); - syncDeviceCapabilitiesLocked(); - updateMaximumTimeToLockLocked(); + validatePasswordOwnerLocked(policy); + syncDeviceCapabilitiesLocked(policy); + updateMaximumTimeToLockLocked(policy); } static void validateQualityConstant(int quality) { @@ -862,19 +946,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { + Integer.toHexString(quality)); } - void validatePasswordOwnerLocked() { - if (mPasswordOwner >= 0) { + void validatePasswordOwnerLocked(DevicePolicyData policy) { + if (policy.mPasswordOwner >= 0) { boolean haveOwner = false; - for (int i=mAdminList.size()-1; i>=0; i--) { - if (mAdminList.get(i).getUid() == mPasswordOwner) { + 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 " + mPasswordOwner + Slog.w(TAG, "Previous password owner " + policy.mPasswordOwner + " no longer active; disabling"); - mPasswordOwner = -1; + policy.mPasswordOwner = -1; } } } @@ -883,11 +967,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { * 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() { + 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); + boolean cameraDisabled = getCameraDisabled(null, policy.mUserHandle); if (cameraDisabled != systemState) { long token = Binder.clearCallingIdentity(); try { @@ -903,19 +987,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { public void systemReady() { synchronized (this) { - loadSettingsLocked(); + loadSettingsLocked(getUserData(UserHandle.USER_OWNER), UserHandle.USER_OWNER); } } - private void handlePasswordExpirationNotification() { + private void handlePasswordExpirationNotification(DevicePolicyData policy) { synchronized (this) { final long now = System.currentTimeMillis(); - final int N = mAdminList.size(); + final int N = policy.mAdminList.size(); if (N <= 0) { return; } for (int i=0; i < N; i++) { - ActiveAdmin admin = mAdminList.get(i); + ActiveAdmin admin = policy.mAdminList.get(i); if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD) && admin.passwordExpirationTimeout > 0L && admin.passwordExpirationDate > 0L @@ -923,7 +1007,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { sendAdminCommandLocked(admin, DeviceAdminReceiver.ACTION_PASSWORD_EXPIRING); } } - setExpirationAlarmCheckLocked(mContext); + setExpirationAlarmCheckLocked(mContext, policy); } } @@ -931,27 +1015,29 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { * @param adminReceiver The admin to add * @param refreshing true = update an active admin, no error */ - public void setActiveAdmin(ComponentName adminReceiver, boolean refreshing) { + public void setActiveAdmin(ComponentName adminReceiver, boolean refreshing, int userHandle) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BIND_DEVICE_ADMIN, null); + enforceCrossUserPermission(userHandle); - DeviceAdminInfo info = findAdmin(adminReceiver); + 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) != null) { + if (!refreshing && getActiveAdminUncheckedLocked(adminReceiver, userHandle) != null) { throw new IllegalArgumentException("Admin is already added"); } ActiveAdmin newAdmin = new ActiveAdmin(info); - mAdminMap.put(adminReceiver, newAdmin); + policy.mAdminMap.put(adminReceiver, newAdmin); int replaceIndex = -1; if (refreshing) { - final int N = mAdminList.size(); + final int N = policy.mAdminList.size(); for (int i=0; i < N; i++) { - ActiveAdmin oldAdmin = mAdminList.get(i); + ActiveAdmin oldAdmin = policy.mAdminList.get(i); if (oldAdmin.info.getComponent().equals(adminReceiver)) { replaceIndex = i; break; @@ -959,11 +1045,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } if (replaceIndex == -1) { - mAdminList.add(newAdmin); + policy.mAdminList.add(newAdmin); } else { - mAdminList.set(replaceIndex, newAdmin); + policy.mAdminList.set(replaceIndex, newAdmin); } - saveSettingsLocked(); + saveSettingsLocked(userHandle); sendAdminCommandLocked(newAdmin, DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED); } finally { Binder.restoreCallingIdentity(ident); @@ -971,15 +1057,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - public boolean isAdminActive(ComponentName adminReceiver) { + public boolean isAdminActive(ComponentName adminReceiver, int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { - return getActiveAdminUncheckedLocked(adminReceiver) != null; + return getActiveAdminUncheckedLocked(adminReceiver, userHandle) != null; } } - public boolean hasGrantedPolicy(ComponentName adminReceiver, int policyId) { + public boolean hasGrantedPolicy(ComponentName adminReceiver, int policyId, int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { - ActiveAdmin administrator = getActiveAdminUncheckedLocked(adminReceiver); + ActiveAdmin administrator = getActiveAdminUncheckedLocked(adminReceiver, userHandle); if (administrator == null) { throw new SecurityException("No active admin " + adminReceiver); } @@ -987,25 +1075,29 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - public List<ComponentName> getActiveAdmins() { + public List<ComponentName> getActiveAdmins(int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { - final int N = mAdminList.size(); + DevicePolicyData policy = getUserData(userHandle); + final int N = policy.mAdminList.size(); if (N <= 0) { return null; } ArrayList<ComponentName> res = new ArrayList<ComponentName>(N); for (int i=0; i<N; i++) { - res.add(mAdminList.get(i).info.getComponent()); + res.add(policy.mAdminList.get(i).info.getComponent()); } return res; } } - public boolean packageHasActiveAdmins(String packageName) { + public boolean packageHasActiveAdmins(String packageName, int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { - final int N = mAdminList.size(); + DevicePolicyData policy = getUserData(userHandle); + final int N = policy.mAdminList.size(); for (int i=0; i<N; i++) { - if (mAdminList.get(i).info.getPackageName().equals(packageName)) { + if (policy.mAdminList.get(i).info.getPackageName().equals(packageName)) { return true; } } @@ -1013,9 +1105,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - public void removeActiveAdmin(ComponentName adminReceiver) { + public void removeActiveAdmin(ComponentName adminReceiver, int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { - ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver); + ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle); if (admin == null) { return; } @@ -1025,15 +1118,16 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } long ident = Binder.clearCallingIdentity(); try { - removeActiveAdminLocked(adminReceiver); + removeActiveAdminLocked(adminReceiver, userHandle); } finally { Binder.restoreCallingIdentity(ident); } } } - public void setPasswordQuality(ComponentName who, int quality) { + public void setPasswordQuality(ComponentName who, int quality, int userHandle) { validateQualityConstant(quality); + enforceCrossUserPermission(userHandle); synchronized (this) { if (who == null) { @@ -1043,23 +1137,25 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD); if (ap.passwordQuality != quality) { ap.passwordQuality = quality; - saveSettingsLocked(); + saveSettingsLocked(userHandle); } } } - public int getPasswordQuality(ComponentName who) { + public int getPasswordQuality(ComponentName who, int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { int mode = DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED; + DevicePolicyData policy = getUserData(userHandle); if (who != null) { - ActiveAdmin admin = getActiveAdminUncheckedLocked(who); + ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); return admin != null ? admin.passwordQuality : mode; } - final int N = mAdminList.size(); + final int N = policy.mAdminList.size(); for (int i=0; i<N; i++) { - ActiveAdmin admin = mAdminList.get(i); + ActiveAdmin admin = policy.mAdminList.get(i); if (mode < admin.passwordQuality) { mode = admin.passwordQuality; } @@ -1068,7 +1164,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - public void setPasswordMinimumLength(ComponentName who, int length) { + public void setPasswordMinimumLength(ComponentName who, int length, int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { if (who == null) { throw new NullPointerException("ComponentName is null"); @@ -1077,23 +1174,25 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD); if (ap.minimumPasswordLength != length) { ap.minimumPasswordLength = length; - saveSettingsLocked(); + saveSettingsLocked(userHandle); } } } - public int getPasswordMinimumLength(ComponentName who) { + public int getPasswordMinimumLength(ComponentName who, int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { + DevicePolicyData policy = getUserData(userHandle); int length = 0; if (who != null) { - ActiveAdmin admin = getActiveAdminUncheckedLocked(who); + ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); return admin != null ? admin.minimumPasswordLength : length; } - final int N = mAdminList.size(); + final int N = policy.mAdminList.size(); for (int i=0; i<N; i++) { - ActiveAdmin admin = mAdminList.get(i); + ActiveAdmin admin = policy.mAdminList.get(i); if (length < admin.minimumPasswordLength) { length = admin.minimumPasswordLength; } @@ -1102,7 +1201,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - public void setPasswordHistoryLength(ComponentName who, int length) { + public void setPasswordHistoryLength(ComponentName who, int length, int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { if (who == null) { throw new NullPointerException("ComponentName is null"); @@ -1111,23 +1211,25 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD); if (ap.passwordHistoryLength != length) { ap.passwordHistoryLength = length; - saveSettingsLocked(); + saveSettingsLocked(userHandle); } } } - public int getPasswordHistoryLength(ComponentName who) { + public int getPasswordHistoryLength(ComponentName who, int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { + DevicePolicyData policy = getUserData(userHandle); int length = 0; if (who != null) { - ActiveAdmin admin = getActiveAdminUncheckedLocked(who); + ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); return admin != null ? admin.passwordHistoryLength : length; } - final int N = mAdminList.size(); + final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { - ActiveAdmin admin = mAdminList.get(i); + ActiveAdmin admin = policy.mAdminList.get(i); if (length < admin.passwordHistoryLength) { length = admin.passwordHistoryLength; } @@ -1136,7 +1238,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - public void setPasswordExpirationTimeout(ComponentName who, long timeout) { + public void setPasswordExpirationTimeout(ComponentName who, long timeout, int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { if (who == null) { throw new NullPointerException("ComponentName is null"); @@ -1155,8 +1258,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { + DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT) .format(new Date(expiration))); } - saveSettingsLocked(); - setExpirationAlarmCheckLocked(mContext); // in case this is the first one + saveSettingsLocked(userHandle); + // in case this is the first one + setExpirationAlarmCheckLocked(mContext, getUserData(userHandle)); } } @@ -1164,17 +1268,19 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { * 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) { + public long getPasswordExpirationTimeout(ComponentName who, int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { if (who != null) { - ActiveAdmin admin = getActiveAdminUncheckedLocked(who); + ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); return admin != null ? admin.passwordExpirationTimeout : 0L; } long timeout = 0L; - final int N = mAdminList.size(); + DevicePolicyData policy = getUserData(userHandle); + final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { - ActiveAdmin admin = mAdminList.get(i); + ActiveAdmin admin = policy.mAdminList.get(i); if (timeout == 0L || (admin.passwordExpirationTimeout != 0L && timeout > admin.passwordExpirationTimeout)) { timeout = admin.passwordExpirationTimeout; @@ -1188,16 +1294,17 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { * 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) { + private long getPasswordExpirationLocked(ComponentName who, int userHandle) { if (who != null) { - ActiveAdmin admin = getActiveAdminUncheckedLocked(who); + ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); return admin != null ? admin.passwordExpirationDate : 0L; } long timeout = 0L; - final int N = mAdminList.size(); + DevicePolicyData policy = getUserData(userHandle); + final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { - ActiveAdmin admin = mAdminList.get(i); + ActiveAdmin admin = policy.mAdminList.get(i); if (timeout == 0L || (admin.passwordExpirationDate != 0 && timeout > admin.passwordExpirationDate)) { timeout = admin.passwordExpirationDate; @@ -1206,13 +1313,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return timeout; } - public long getPasswordExpiration(ComponentName who) { + public long getPasswordExpiration(ComponentName who, int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { - return getPasswordExpirationLocked(who); + return getPasswordExpirationLocked(who, userHandle); } } - public void setPasswordMinimumUpperCase(ComponentName who, int length) { + public void setPasswordMinimumUpperCase(ComponentName who, int length, int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { if (who == null) { throw new NullPointerException("ComponentName is null"); @@ -1221,23 +1330,25 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD); if (ap.minimumPasswordUpperCase != length) { ap.minimumPasswordUpperCase = length; - saveSettingsLocked(); + saveSettingsLocked(userHandle); } } } - public int getPasswordMinimumUpperCase(ComponentName who) { + public int getPasswordMinimumUpperCase(ComponentName who, int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { int length = 0; if (who != null) { - ActiveAdmin admin = getActiveAdminUncheckedLocked(who); + ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); return admin != null ? admin.minimumPasswordUpperCase : length; } - final int N = mAdminList.size(); + DevicePolicyData policy = getUserData(userHandle); + final int N = policy.mAdminList.size(); for (int i=0; i<N; i++) { - ActiveAdmin admin = mAdminList.get(i); + ActiveAdmin admin = policy.mAdminList.get(i); if (length < admin.minimumPasswordUpperCase) { length = admin.minimumPasswordUpperCase; } @@ -1246,7 +1357,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - public void setPasswordMinimumLowerCase(ComponentName who, int length) { + public void setPasswordMinimumLowerCase(ComponentName who, int length, int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { if (who == null) { throw new NullPointerException("ComponentName is null"); @@ -1255,23 +1367,25 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD); if (ap.minimumPasswordLowerCase != length) { ap.minimumPasswordLowerCase = length; - saveSettingsLocked(); + saveSettingsLocked(userHandle); } } } - public int getPasswordMinimumLowerCase(ComponentName who) { + public int getPasswordMinimumLowerCase(ComponentName who, int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { int length = 0; if (who != null) { - ActiveAdmin admin = getActiveAdminUncheckedLocked(who); + ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); return admin != null ? admin.minimumPasswordLowerCase : length; } - final int N = mAdminList.size(); + DevicePolicyData policy = getUserData(userHandle); + final int N = policy.mAdminList.size(); for (int i=0; i<N; i++) { - ActiveAdmin admin = mAdminList.get(i); + ActiveAdmin admin = policy.mAdminList.get(i); if (length < admin.minimumPasswordLowerCase) { length = admin.minimumPasswordLowerCase; } @@ -1280,7 +1394,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - public void setPasswordMinimumLetters(ComponentName who, int length) { + public void setPasswordMinimumLetters(ComponentName who, int length, int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { if (who == null) { throw new NullPointerException("ComponentName is null"); @@ -1289,23 +1404,25 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD); if (ap.minimumPasswordLetters != length) { ap.minimumPasswordLetters = length; - saveSettingsLocked(); + saveSettingsLocked(userHandle); } } } - public int getPasswordMinimumLetters(ComponentName who) { + public int getPasswordMinimumLetters(ComponentName who, int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { int length = 0; if (who != null) { - ActiveAdmin admin = getActiveAdminUncheckedLocked(who); + ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); return admin != null ? admin.minimumPasswordLetters : length; } - final int N = mAdminList.size(); + DevicePolicyData policy = getUserData(userHandle); + final int N = policy.mAdminList.size(); for (int i=0; i<N; i++) { - ActiveAdmin admin = mAdminList.get(i); + ActiveAdmin admin = policy.mAdminList.get(i); if (length < admin.minimumPasswordLetters) { length = admin.minimumPasswordLetters; } @@ -1314,7 +1431,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - public void setPasswordMinimumNumeric(ComponentName who, int length) { + public void setPasswordMinimumNumeric(ComponentName who, int length, int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { if (who == null) { throw new NullPointerException("ComponentName is null"); @@ -1323,23 +1441,25 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD); if (ap.minimumPasswordNumeric != length) { ap.minimumPasswordNumeric = length; - saveSettingsLocked(); + saveSettingsLocked(userHandle); } } } - public int getPasswordMinimumNumeric(ComponentName who) { + public int getPasswordMinimumNumeric(ComponentName who, int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { int length = 0; if (who != null) { - ActiveAdmin admin = getActiveAdminUncheckedLocked(who); + ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); return admin != null ? admin.minimumPasswordNumeric : length; } - final int N = mAdminList.size(); + DevicePolicyData policy = getUserData(userHandle); + final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { - ActiveAdmin admin = mAdminList.get(i); + ActiveAdmin admin = policy.mAdminList.get(i); if (length < admin.minimumPasswordNumeric) { length = admin.minimumPasswordNumeric; } @@ -1348,7 +1468,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - public void setPasswordMinimumSymbols(ComponentName who, int length) { + public void setPasswordMinimumSymbols(ComponentName who, int length, int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { if (who == null) { throw new NullPointerException("ComponentName is null"); @@ -1357,23 +1478,25 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD); if (ap.minimumPasswordSymbols != length) { ap.minimumPasswordSymbols = length; - saveSettingsLocked(); + saveSettingsLocked(userHandle); } } } - public int getPasswordMinimumSymbols(ComponentName who) { + public int getPasswordMinimumSymbols(ComponentName who, int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { int length = 0; if (who != null) { - ActiveAdmin admin = getActiveAdminUncheckedLocked(who); + ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); return admin != null ? admin.minimumPasswordSymbols : length; } - final int N = mAdminList.size(); + DevicePolicyData policy = getUserData(userHandle); + final int N = policy.mAdminList.size(); for (int i=0; i<N; i++) { - ActiveAdmin admin = mAdminList.get(i); + ActiveAdmin admin = policy.mAdminList.get(i); if (length < admin.minimumPasswordSymbols) { length = admin.minimumPasswordSymbols; } @@ -1382,7 +1505,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - public void setPasswordMinimumNonLetter(ComponentName who, int length) { + public void setPasswordMinimumNonLetter(ComponentName who, int length, int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { if (who == null) { throw new NullPointerException("ComponentName is null"); @@ -1391,23 +1515,25 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD); if (ap.minimumPasswordNonLetter != length) { ap.minimumPasswordNonLetter = length; - saveSettingsLocked(); + saveSettingsLocked(userHandle); } } } - public int getPasswordMinimumNonLetter(ComponentName who) { + public int getPasswordMinimumNonLetter(ComponentName who, int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { int length = 0; if (who != null) { - ActiveAdmin admin = getActiveAdminUncheckedLocked(who); + ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); return admin != null ? admin.minimumPasswordNonLetter : length; } - final int N = mAdminList.size(); + DevicePolicyData policy = getUserData(userHandle); + final int N = policy.mAdminList.size(); for (int i=0; i<N; i++) { - ActiveAdmin admin = mAdminList.get(i); + ActiveAdmin admin = policy.mAdminList.get(i); if (length < admin.minimumPasswordNonLetter) { length = admin.minimumPasswordNonLetter; } @@ -1416,39 +1542,43 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - public boolean isActivePasswordSufficient() { + public boolean isActivePasswordSufficient(int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { + DevicePolicyData policy = getUserData(userHandle); // 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_LIMIT_PASSWORD); - if (mActivePasswordQuality < getPasswordQuality(null) - || mActivePasswordLength < getPasswordMinimumLength(null)) { + if (policy.mActivePasswordQuality < getPasswordQuality(null, userHandle) + || policy.mActivePasswordLength < getPasswordMinimumLength(null, userHandle)) { return false; } - if(mActivePasswordQuality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) { + if (policy.mActivePasswordQuality != DevicePolicyManager.PASSWORD_QUALITY_COMPLEX) { return true; } - return mActivePasswordUpperCase >= getPasswordMinimumUpperCase(null) - && mActivePasswordLowerCase >= getPasswordMinimumLowerCase(null) - && mActivePasswordLetters >= getPasswordMinimumLetters(null) - && mActivePasswordNumeric >= getPasswordMinimumNumeric(null) - && mActivePasswordSymbols >= getPasswordMinimumSymbols(null) - && mActivePasswordNonLetter >= getPasswordMinimumNonLetter(null); + return policy.mActivePasswordUpperCase >= 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() { + 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 mFailedPasswordAttempts; + return getUserData(userHandle).mFailedPasswordAttempts; } } - public void setMaximumFailedPasswordsForWipe(ComponentName who, int num) { + 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. @@ -1458,23 +1588,25 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { DeviceAdminInfo.USES_POLICY_WATCH_LOGIN); if (ap.maximumFailedPasswordsForWipe != num) { ap.maximumFailedPasswordsForWipe = num; - saveSettingsLocked(); + saveSettingsLocked(userHandle); } } } - public int getMaximumFailedPasswordsForWipe(ComponentName who) { + 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); + ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); return admin != null ? admin.maximumFailedPasswordsForWipe : count; } - final int N = mAdminList.size(); + final int N = policy.mAdminList.size(); for (int i=0; i<N; i++) { - ActiveAdmin admin = mAdminList.get(i); + ActiveAdmin admin = policy.mAdminList.get(i); if (count == 0) { count = admin.maximumFailedPasswordsForWipe; } else if (admin.maximumFailedPasswordsForWipe != 0 @@ -1486,14 +1618,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - public boolean resetPassword(String password, int flags) { + 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); + quality = getPasswordQuality(null, userHandle); if (quality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { int realQuality = LockPatternUtils.computePasswordQuality(password); if (realQuality < quality @@ -1506,7 +1639,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } quality = Math.max(realQuality, quality); } - int length = getPasswordMinimumLength(null); + int length = getPasswordMinimumLength(null, userHandle); if (password.length() < length) { Slog.w(TAG, "resetPassword: password length " + password.length() + " does not meet required length " + length); @@ -1535,13 +1668,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { nonletter++; } } - int neededLetters = getPasswordMinimumLetters(null); + 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); + int neededNumbers = getPasswordMinimumNumeric(null, userHandle); if (numbers < neededNumbers) { Slog .w(TAG, "resetPassword: number of numerical digits " + numbers @@ -1549,27 +1682,27 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { + neededNumbers); return false; } - int neededLowerCase = getPasswordMinimumLowerCase(null); + 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); + 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); + 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); + 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 " @@ -1580,7 +1713,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } int callingUid = Binder.getCallingUid(); - if (mPasswordOwner >= 0 && mPasswordOwner != callingUid) { + 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; } @@ -1590,13 +1724,13 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { long ident = Binder.clearCallingIdentity(); try { LockPatternUtils utils = new LockPatternUtils(mContext); - utils.saveLockPassword(password, quality); + utils.saveLockPassword(password, quality, false, userHandle); synchronized (this) { int newOwner = (flags&DevicePolicyManager.RESET_PASSWORD_REQUIRE_ENTRY) != 0 ? callingUid : -1; - if (mPasswordOwner != newOwner) { - mPasswordOwner = newOwner; - saveSettingsLocked(); + if (policy.mPasswordOwner != newOwner) { + policy.mPasswordOwner = newOwner; + saveSettingsLocked(userHandle); } } } finally { @@ -1606,7 +1740,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return true; } - public void setMaximumTimeToLock(ComponentName who, long timeMs) { + public void setMaximumTimeToLock(ComponentName who, long timeMs, int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { if (who == null) { throw new NullPointerException("ComponentName is null"); @@ -1615,15 +1750,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { DeviceAdminInfo.USES_POLICY_FORCE_LOCK); if (ap.maximumTimeToUnlock != timeMs) { ap.maximumTimeToUnlock = timeMs; - saveSettingsLocked(); - updateMaximumTimeToLockLocked(); + saveSettingsLocked(userHandle); + updateMaximumTimeToLockLocked(getUserData(userHandle)); } } } - void updateMaximumTimeToLockLocked() { - long timeMs = getMaximumTimeToLock(null); - if (mLastMaximumTimeToLock == timeMs) { + void updateMaximumTimeToLockLocked(DevicePolicyData policy) { + long timeMs = getMaximumTimeToLock(null, policy.mUserHandle); + if (policy.mLastMaximumTimeToLock == timeMs) { return; } @@ -1638,7 +1773,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0); } - mLastMaximumTimeToLock = timeMs; + policy.mLastMaximumTimeToLock = timeMs; try { getIPowerManager().setMaximumScreenOffTimeoutFromDeviceAdmin((int)timeMs); @@ -1650,18 +1785,20 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - public long getMaximumTimeToLock(ComponentName who) { + public long getMaximumTimeToLock(ComponentName who, int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { long time = 0; if (who != null) { - ActiveAdmin admin = getActiveAdminUncheckedLocked(who); + ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); return admin != null ? admin.maximumTimeToUnlock : time; } - final int N = mAdminList.size(); + DevicePolicyData policy = getUserData(userHandle); + final int N = policy.mAdminList.size(); for (int i=0; i<N; i++) { - ActiveAdmin admin = mAdminList.get(i); + ActiveAdmin admin = policy.mAdminList.get(i); if (time == 0) { time = admin.maximumTimeToUnlock; } else if (admin.maximumTimeToUnlock != 0 @@ -1679,17 +1816,21 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // so try to retrieve it to check that the caller is one. getActiveAdminForCallerLocked(null, DeviceAdminInfo.USES_POLICY_FORCE_LOCK); - 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(); - } catch (RemoteException e) { - } finally { - Binder.restoreCallingIdentity(ident); - } + 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(); + } catch (RemoteException e) { + } finally { + Binder.restoreCallingIdentity(ident); } } @@ -1719,7 +1860,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } } - public void wipeData(int flags) { + 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. @@ -1727,19 +1869,35 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { DeviceAdminInfo.USES_POLICY_WIPE_DATA); long ident = Binder.clearCallingIdentity(); try { - wipeDataLocked(flags); + 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) { + 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); + ActiveAdmin admin = getActiveAdminUncheckedLocked(comp, userHandle); if (admin == null) { try { result.sendResult(null); @@ -1749,7 +1907,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } Intent intent = new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_DISABLE_REQUESTED); intent.setComponent(admin.info.getComponent()); - mContext.sendOrderedBroadcastAsUser(intent, UserHandle.OWNER, + mContext.sendOrderedBroadcastAsUser(intent, new UserHandle(userHandle), null, new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -1763,34 +1921,36 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } public void setActivePasswordState(int quality, int length, int letters, int uppercase, - int lowercase, int numbers, int symbols, int nonletter) { + 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 (mActivePasswordQuality != quality || mActivePasswordLength != length - || mFailedPasswordAttempts != 0 || mActivePasswordLetters != letters - || mActivePasswordUpperCase != uppercase - || mActivePasswordLowerCase != lowercase || mActivePasswordNumeric != numbers - || mActivePasswordSymbols != symbols || mActivePasswordNonLetter != nonletter) { + 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 { - mActivePasswordQuality = quality; - mActivePasswordLength = length; - mActivePasswordLetters = letters; - mActivePasswordLowerCase = lowercase; - mActivePasswordUpperCase = uppercase; - mActivePasswordNumeric = numbers; - mActivePasswordSymbols = symbols; - mActivePasswordNonLetter = nonletter; - mFailedPasswordAttempts = 0; - saveSettingsLocked(); - updatePasswordExpirationsLocked(); - setExpirationAlarmCheckLocked(mContext); + 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); + DeviceAdminInfo.USES_POLICY_LIMIT_PASSWORD, userHandle); } finally { Binder.restoreCallingIdentity(ident); } @@ -1801,55 +1961,60 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { /** * Called any time the device password is updated. Resets all password expiration clocks. */ - private void updatePasswordExpirationsLocked() { - final int N = mAdminList.size(); + private void updatePasswordExpirationsLocked(int userHandle) { + DevicePolicyData policy = getUserData(userHandle); + final int N = policy.mAdminList.size(); if (N > 0) { for (int i=0; i<N; i++) { - ActiveAdmin admin = mAdminList.get(i); + ActiveAdmin admin = policy.mAdminList.get(i); if (admin.info.usesPolicy(DeviceAdminInfo.USES_POLICY_EXPIRE_PASSWORD)) { long timeout = admin.passwordExpirationTimeout; long expiration = timeout > 0L ? (timeout + System.currentTimeMillis()) : 0L; admin.passwordExpirationDate = expiration; } } - saveSettingsLocked(); + saveSettingsLocked(userHandle); } } - public void reportFailedPasswordAttempt() { + 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 { - mFailedPasswordAttempts++; - saveSettingsLocked(); - int max = getMaximumFailedPasswordsForWipe(null); - if (max > 0 && mFailedPasswordAttempts >= max) { + 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); + DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, userHandle); } finally { Binder.restoreCallingIdentity(ident); } } } - public void reportSuccessfulPasswordAttempt() { + public void reportSuccessfulPasswordAttempt(int userHandle) { + enforceCrossUserPermission(userHandle); mContext.enforceCallingOrSelfPermission( android.Manifest.permission.BIND_DEVICE_ADMIN, null); synchronized (this) { - if (mFailedPasswordAttempts != 0 || mPasswordOwner >= 0) { + DevicePolicyData policy = getUserData(userHandle); + if (policy.mFailedPasswordAttempts != 0 || policy.mPasswordOwner >= 0) { long ident = Binder.clearCallingIdentity(); try { - mFailedPasswordAttempts = 0; - mPasswordOwner = -1; - saveSettingsLocked(); + policy.mFailedPasswordAttempts = 0; + policy.mPasswordOwner = -1; + saveSettingsLocked(userHandle); sendAdminCommandLocked(DeviceAdminReceiver.ACTION_PASSWORD_SUCCEEDED, - DeviceAdminInfo.USES_POLICY_WATCH_LOGIN); + DeviceAdminInfo.USES_POLICY_WATCH_LOGIN, userHandle); } finally { Binder.restoreCallingIdentity(ident); } @@ -1858,26 +2023,36 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } public ComponentName setGlobalProxy(ComponentName who, String proxySpec, - String exclusionList) { + 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<ComponentName> compSet = mAdminMap.keySet(); + Set<ComponentName> compSet = policy.mAdminMap.keySet(); for (ComponentName component : compSet) { - ActiveAdmin ap = mAdminMap.get(component); + 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; @@ -1892,19 +2067,21 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // Reset the global proxy accordingly // Do this using system permissions, as apps cannot write to secure settings long origId = Binder.clearCallingIdentity(); - resetGlobalProxyLocked(); + resetGlobalProxyLocked(policy); Binder.restoreCallingIdentity(origId); return null; } } - public ComponentName getGlobalProxyAdmin() { + 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 = mAdminList.size(); + final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { - ActiveAdmin ap = mAdminList.get(i); + ActiveAdmin ap = policy.mAdminList.get(i); if (ap.specifiesGlobalProxy) { // Device admin sets the global proxy // Return it to the caller. @@ -1916,10 +2093,10 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { return null; } - private void resetGlobalProxyLocked() { - final int N = mAdminList.size(); + private void resetGlobalProxyLocked(DevicePolicyData policy) { + final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { - ActiveAdmin ap = mAdminList.get(i); + ActiveAdmin ap = policy.mAdminList.get(i); if (ap.specifiesGlobalProxy) { saveGlobalProxyLocked(ap.globalProxySpec, ap.globalProxyExclusionList); return; @@ -1957,12 +2134,21 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { * 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) { + 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); @@ -1974,14 +2160,15 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // (1) Record the value for the admin so it's sticky if (ap.encryptionRequested != encrypt) { ap.encryptionRequested = encrypt; - saveSettingsLocked(); + saveSettingsLocked(userHandle); } + DevicePolicyData policy = getUserData(UserHandle.USER_OWNER); // (2) Compute "max" for all admins boolean newRequested = false; - final int N = mAdminList.size(); + final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { - newRequested |= mAdminList.get(i).encryptionRequested; + newRequested |= policy.mAdminList.get(i).encryptionRequested; } // Notify OS of new request @@ -1998,20 +2185,22 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { * Get the current storage encryption request status for a given admin, or aggregate of all * active admins. */ - public boolean getStorageEncryption(ComponentName who) { + 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); + 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. - final int N = mAdminList.size(); + DevicePolicyData policy = getUserData(userHandle); + final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { - if (mAdminList.get(i).encryptionRequested) { + if (policy.mAdminList.get(i).encryptionRequested) { return true; } } @@ -2022,7 +2211,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { /** * Get the current encryption status of the device. */ - public int getStorageEncryptionStatus() { + public int getStorageEncryptionStatus(int userHandle) { + enforceCrossUserPermission(userHandle); return getEncryptionStatus(); } @@ -2069,7 +2259,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { /** * Disables all device cameras according to the specified admin. */ - public void setCameraDisabled(ComponentName who, boolean disabled) { + public void setCameraDisabled(ComponentName who, boolean disabled, int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { if (who == null) { throw new NullPointerException("ComponentName is null"); @@ -2078,9 +2269,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { DeviceAdminInfo.USES_POLICY_DISABLE_CAMERA); if (ap.disableCamera != disabled) { ap.disableCamera = disabled; - saveSettingsLocked(); + saveSettingsLocked(userHandle); } - syncDeviceCapabilitiesLocked(); + syncDeviceCapabilitiesLocked(getUserData(userHandle)); } } @@ -2088,17 +2279,18 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { * Gets whether or not all device cameras are disabled for a given admin, or disabled for any * active admins. */ - public boolean getCameraDisabled(ComponentName who) { + public boolean getCameraDisabled(ComponentName who, int userHandle) { synchronized (this) { if (who != null) { - ActiveAdmin admin = getActiveAdminUncheckedLocked(who); + 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 = mAdminList.size(); + final int N = policy.mAdminList.size(); for (int i = 0; i < N; i++) { - ActiveAdmin admin = mAdminList.get(i); + ActiveAdmin admin = policy.mAdminList.get(i); if (admin.disableCamera) { return true; } @@ -2110,7 +2302,8 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { /** * Selectively disable keyguard widgets. */ - public void setKeyguardWidgetsDisabled(ComponentName who, int which) { + public void setKeyguardWidgetsDisabled(ComponentName who, int which, int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { if (who == null) { throw new NullPointerException("ComponentName is null"); @@ -2119,9 +2312,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { DeviceAdminInfo.USES_POLICY_DISABLE_KEYGUARD_WIDGETS); if ((ap.disableKeyguardWidgets & which) != which) { ap.disableKeyguardWidgets |= which; - saveSettingsLocked(); + saveSettingsLocked(userHandle); } - syncDeviceCapabilitiesLocked(); + syncDeviceCapabilitiesLocked(getUserData(userHandle)); } } @@ -2129,24 +2322,39 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { * Gets the disabled state for widgets in keyguard for the given admin, * or the aggregate of all active admins if who is null. */ - public int getKeyguardWidgetsDisabled(ComponentName who) { + public int getKeyguardWidgetsDisabled(ComponentName who, int userHandle) { + enforceCrossUserPermission(userHandle); synchronized (this) { if (who != null) { - ActiveAdmin admin = getActiveAdminUncheckedLocked(who); + ActiveAdmin admin = getActiveAdminUncheckedLocked(who, userHandle); return (admin != null) ? admin.disableKeyguardWidgets : 0; } // Determine whether or not keyguard widgets are disabled for any active admins. - final int N = mAdminList.size(); + DevicePolicyData policy = getUserData(userHandle); + final int N = policy.mAdminList.size(); int which = 0; for (int i = 0; i < N; i++) { - ActiveAdmin admin = mAdminList.get(i); + ActiveAdmin admin = policy.mAdminList.get(i); which |= admin.disableKeyguardWidgets; } 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) @@ -2163,19 +2371,23 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { synchronized (this) { p.println("Current Device Policy Manager state:"); - p.println(" Enabled Device Admins:"); - final int N = mAdminList.size(); - for (int i=0; i<N; i++) { - ActiveAdmin ap = mAdminList.get(i); - if (ap != null) { - pw.print(" "); pw.print(ap.info.getComponent().flattenToShortString()); - pw.println(":"); - ap.dump(" ", pw); + 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<N; i++) { + ActiveAdmin ap = policy.mAdminList.get(i); + if (ap != null) { + pw.print(" "); pw.print(ap.info.getComponent().flattenToShortString()); + pw.println(":"); + ap.dump(" ", pw); + } } - } - pw.println(" "); - pw.print(" mPasswordOwner="); pw.println(mPasswordOwner); + pw.println(" "); + pw.print(" mPasswordOwner="); pw.println(policy.mPasswordOwner); + } } } } diff --git a/services/java/com/android/server/DreamController.java b/services/java/com/android/server/DreamController.java new file mode 100644 index 0000000..498e581 --- /dev/null +++ b/services/java/com/android/server/DreamController.java @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2012 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 android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Binder; +import android.os.Handler; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.IBinder.DeathRecipient; +import android.service.dreams.IDreamService; +import android.util.Slog; +import android.view.IWindowManager; +import android.view.WindowManager; +import android.view.WindowManagerGlobal; + +import com.android.internal.util.DumpUtils; + +import java.io.PrintWriter; +import java.util.NoSuchElementException; + +/** + * Internal controller for starting and stopping the current dream and managing related state. + * + * Assumes all operations (except {@link #dump}) are called from a single thread. + */ +final class DreamController { + private static final boolean DEBUG = true; + private static final String TAG = DreamController.class.getSimpleName(); + + public interface Listener { + void onDreamStopped(boolean wasTest); + } + + private final Context mContext; + private final IWindowManager mIWindowManager; + private final DeathRecipient mDeathRecipient; + private final ServiceConnection mServiceConnection; + private final Listener mListener; + + private Handler mHandler; + + private ComponentName mCurrentDreamComponent; + private IDreamService mCurrentDream; + private Binder mCurrentDreamToken; + private boolean mCurrentDreamIsTest; + + public DreamController(Context context, DeathRecipient deathRecipient, + ServiceConnection serviceConnection, Listener listener) { + mContext = context; + mDeathRecipient = deathRecipient; + mServiceConnection = serviceConnection; + mListener = listener; + mIWindowManager = WindowManagerGlobal.getWindowManagerService(); + } + + public void setHandler(Handler handler) { + mHandler = handler; + } + + public void dump(PrintWriter pw) { + if (mHandler== null || pw == null) { + return; + } + DumpUtils.dumpAsync(mHandler, new DumpUtils.Dump() { + @Override + public void dump(PrintWriter pw) { + pw.print(" component="); pw.println(mCurrentDreamComponent); + pw.print(" token="); pw.println(mCurrentDreamToken); + pw.print(" dream="); pw.println(mCurrentDream); + } + }, pw, 200); + } + + public void start(ComponentName dream, boolean isTest) { + if (DEBUG) Slog.v(TAG, String.format("start(%s,%s)", dream, isTest)); + + if (mCurrentDreamComponent != null ) { + if (dream.equals(mCurrentDreamComponent) && isTest == mCurrentDreamIsTest) { + if (DEBUG) Slog.v(TAG, "Dream is already started: " + dream); + return; + } + // stop the current dream before starting a new one + stop(); + } + + mCurrentDreamComponent = dream; + mCurrentDreamIsTest = isTest; + mCurrentDreamToken = new Binder(); + + try { + if (DEBUG) Slog.v(TAG, "Adding window token: " + mCurrentDreamToken + + " for window type: " + WindowManager.LayoutParams.TYPE_DREAM); + mIWindowManager.addWindowToken(mCurrentDreamToken, + WindowManager.LayoutParams.TYPE_DREAM); + } catch (RemoteException e) { + Slog.w(TAG, "Unable to add window token."); + stop(); + return; + } + + Intent intent = new Intent(Intent.ACTION_MAIN) + .setComponent(mCurrentDreamComponent) + .addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) + .putExtra("android.dreams.TEST", mCurrentDreamIsTest); + + if (!mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE)) { + Slog.w(TAG, "Unable to bind service"); + stop(); + return; + } + if (DEBUG) Slog.v(TAG, "Bound service"); + } + + public void attach(ComponentName name, IBinder dream) { + if (DEBUG) Slog.v(TAG, String.format("attach(%s,%s)", name, dream)); + mCurrentDream = IDreamService.Stub.asInterface(dream); + + boolean linked = linkDeathRecipient(dream); + if (!linked) { + stop(); + return; + } + + try { + if (DEBUG) Slog.v(TAG, "Attaching with token:" + mCurrentDreamToken); + mCurrentDream.attach(mCurrentDreamToken); + } catch (Throwable ex) { + Slog.w(TAG, "Unable to send window token to dream:" + ex); + stop(); + } + } + + public void stop() { + if (DEBUG) Slog.v(TAG, "stop()"); + + if (mCurrentDream != null) { + unlinkDeathRecipient(mCurrentDream.asBinder()); + + if (DEBUG) Slog.v(TAG, "Unbinding: " + mCurrentDreamComponent + " service: " + mCurrentDream); + mContext.unbindService(mServiceConnection); + } + if (mCurrentDreamToken != null) { + removeWindowToken(mCurrentDreamToken); + } + + final boolean wasTest = mCurrentDreamIsTest; + mCurrentDream = null; + mCurrentDreamToken = null; + mCurrentDreamComponent = null; + mCurrentDreamIsTest = false; + + if (mListener != null && mHandler != null) { + mHandler.post(new Runnable(){ + @Override + public void run() { + mListener.onDreamStopped(wasTest); + }}); + } + } + + public void stopSelf(IBinder token) { + if (DEBUG) Slog.v(TAG, String.format("stopSelf(%s)", token)); + if (token == null || token != mCurrentDreamToken) { + Slog.w(TAG, "Stop requested for non-current dream token: " + token); + } else { + stop(); + } + } + + private void removeWindowToken(IBinder token) { + if (DEBUG) Slog.v(TAG, "Removing window token: " + token); + try { + mIWindowManager.removeWindowToken(token); + } catch (Throwable e) { + Slog.w(TAG, "Error removing window token", e); + } + } + + private boolean linkDeathRecipient(IBinder dream) { + if (DEBUG) Slog.v(TAG, "Linking death recipient"); + try { + dream.linkToDeath(mDeathRecipient, 0); + return true; + } catch (RemoteException e) { + Slog.w(TAG, "Unable to link death recipient", e); + return false; + } + } + + private void unlinkDeathRecipient(IBinder dream) { + if (DEBUG) Slog.v(TAG, "Unlinking death recipient"); + try { + dream.unlinkToDeath(mDeathRecipient, 0); + } catch (NoSuchElementException e) { + // we tried + } + } + +}
\ No newline at end of file diff --git a/services/java/com/android/server/DreamManagerService.java b/services/java/com/android/server/DreamManagerService.java new file mode 100644 index 0000000..b02ea7f --- /dev/null +++ b/services/java/com/android/server/DreamManagerService.java @@ -0,0 +1,387 @@ +/* + * Copyright (C) 2012 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 static android.provider.Settings.Secure.SCREENSAVER_COMPONENTS; +import static android.provider.Settings.Secure.SCREENSAVER_DEFAULT_COMPONENT; + +import android.app.ActivityManagerNative; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.ServiceConnection; +import android.content.pm.PackageManager; +import android.os.Binder; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.RemoteException; +import android.os.UserHandle; +import android.provider.Settings; +import android.service.dreams.Dream; +import android.service.dreams.IDreamManager; +import android.util.Slog; +import android.util.SparseArray; + +import java.io.FileDescriptor; +import java.io.PrintWriter; + +/** + * Service api for managing dreams. + * + * @hide + */ +public final class DreamManagerService + extends IDreamManager.Stub + implements ServiceConnection { + private static final boolean DEBUG = true; + private static final String TAG = DreamManagerService.class.getSimpleName(); + + private static final Intent mDreamingStartedIntent = new Intent(Dream.ACTION_DREAMING_STARTED) + .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + private static final Intent mDreamingStoppedIntent = new Intent(Dream.ACTION_DREAMING_STOPPED) + .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + + private final Object mLock = new Object(); + private final DreamController mController; + private final DreamControllerHandler mHandler; + private final Context mContext; + + private final CurrentUserManager mCurrentUserManager = new CurrentUserManager(); + + private final DeathRecipient mAwakenOnBinderDeath = new DeathRecipient() { + @Override + public void binderDied() { + if (DEBUG) Slog.v(TAG, "binderDied()"); + awaken(); + } + }; + + private final DreamController.Listener mControllerListener = new DreamController.Listener() { + @Override + public void onDreamStopped(boolean wasTest) { + synchronized(mLock) { + setDreamingLocked(false, wasTest); + } + }}; + + private boolean mIsDreaming; + + public DreamManagerService(Context context) { + if (DEBUG) Slog.v(TAG, "DreamManagerService startup"); + mContext = context; + mController = new DreamController(context, mAwakenOnBinderDeath, this, mControllerListener); + mHandler = new DreamControllerHandler(mController); + mController.setHandler(mHandler); + } + + public void systemReady() { + mCurrentUserManager.init(mContext); + + if (DEBUG) Slog.v(TAG, "Ready to dream!"); + } + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); + + pw.println("Dreamland:"); + mController.dump(pw); + mCurrentUserManager.dump(pw); + } + + // begin IDreamManager api + @Override + public ComponentName[] getDreamComponents() { + checkPermission(android.Manifest.permission.READ_DREAM_STATE); + int userId = UserHandle.getCallingUserId(); + + final long ident = Binder.clearCallingIdentity(); + try { + return getDreamComponentsForUser(userId); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public void setDreamComponents(ComponentName[] componentNames) { + checkPermission(android.Manifest.permission.WRITE_DREAM_STATE); + int userId = UserHandle.getCallingUserId(); + + final long ident = Binder.clearCallingIdentity(); + try { + Settings.Secure.putStringForUser(mContext.getContentResolver(), + SCREENSAVER_COMPONENTS, + componentsToString(componentNames), + userId); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public ComponentName getDefaultDreamComponent() { + checkPermission(android.Manifest.permission.READ_DREAM_STATE); + int userId = UserHandle.getCallingUserId(); + + final long ident = Binder.clearCallingIdentity(); + try { + String name = Settings.Secure.getStringForUser(mContext.getContentResolver(), + SCREENSAVER_DEFAULT_COMPONENT, + userId); + return name == null ? null : ComponentName.unflattenFromString(name); + } finally { + Binder.restoreCallingIdentity(ident); + } + + } + + @Override + public boolean isDreaming() { + checkPermission(android.Manifest.permission.READ_DREAM_STATE); + + return mIsDreaming; + } + + @Override + public void dream() { + checkPermission(android.Manifest.permission.WRITE_DREAM_STATE); + + final long ident = Binder.clearCallingIdentity(); + try { + if (DEBUG) Slog.v(TAG, "Dream now"); + ComponentName[] dreams = getDreamComponentsForUser(mCurrentUserManager.getCurrentUserId()); + ComponentName firstDream = dreams != null && dreams.length > 0 ? dreams[0] : null; + if (firstDream != null) { + mHandler.requestStart(firstDream, false /*isTest*/); + synchronized (mLock) { + setDreamingLocked(true, false /*isTest*/); + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public void testDream(ComponentName dream) { + checkPermission(android.Manifest.permission.WRITE_DREAM_STATE); + + final long ident = Binder.clearCallingIdentity(); + try { + if (DEBUG) Slog.v(TAG, "Test dream name=" + dream); + if (dream != null) { + mHandler.requestStart(dream, true /*isTest*/); + synchronized (mLock) { + setDreamingLocked(true, true /*isTest*/); + } + } + } finally { + Binder.restoreCallingIdentity(ident); + } + + } + + @Override + public void awaken() { + checkPermission(android.Manifest.permission.WRITE_DREAM_STATE); + + final long ident = Binder.clearCallingIdentity(); + try { + if (DEBUG) Slog.v(TAG, "Wake up"); + mHandler.requestStop(); + } finally { + Binder.restoreCallingIdentity(ident); + } + } + + @Override + public void awakenSelf(IBinder token) { + // requires no permission, called by Dream from an arbitrary process + + final long ident = Binder.clearCallingIdentity(); + try { + if (DEBUG) Slog.v(TAG, "Wake up from dream: " + token); + if (token != null) { + mHandler.requestStopSelf(token); + } + } finally { + Binder.restoreCallingIdentity(ident); + } + } + // end IDreamManager api + + // begin ServiceConnection + @Override + public void onServiceConnected(ComponentName name, IBinder dream) { + if (DEBUG) Slog.v(TAG, "Service connected: " + name + " binder=" + + dream + " thread=" + Thread.currentThread().getId()); + mHandler.requestAttach(name, dream); + } + + @Override + public void onServiceDisconnected(ComponentName name) { + if (DEBUG) Slog.v(TAG, "Service disconnected: " + name); + // Only happens in exceptional circumstances, awaken just to be safe + awaken(); + } + // end ServiceConnection + + private void checkPermission(String permission) { + if (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(permission)) { + throw new SecurityException("Access denied to process: " + Binder.getCallingPid() + + ", must have permission " + permission); + } + } + + private void setDreamingLocked(boolean isDreaming, boolean isTest) { + boolean wasDreaming = mIsDreaming; + if (!isTest) { + if (!wasDreaming && isDreaming) { + if (DEBUG) Slog.v(TAG, "Firing ACTION_DREAMING_STARTED"); + mContext.sendBroadcast(mDreamingStartedIntent); + } else if (wasDreaming && !isDreaming) { + if (DEBUG) Slog.v(TAG, "Firing ACTION_DREAMING_STOPPED"); + mContext.sendBroadcast(mDreamingStoppedIntent); + } + } + mIsDreaming = isDreaming; + } + + private ComponentName[] getDreamComponentsForUser(int userId) { + String names = Settings.Secure.getStringForUser(mContext.getContentResolver(), + SCREENSAVER_COMPONENTS, + userId); + return names == null ? null : componentsFromString(names); + } + + private static String componentsToString(ComponentName[] componentNames) { + StringBuilder names = new StringBuilder(); + if (componentNames != null) { + for (ComponentName componentName : componentNames) { + if (names.length() > 0) { + names.append(','); + } + names.append(componentName.flattenToString()); + } + } + return names.toString(); + } + + private static ComponentName[] componentsFromString(String names) { + String[] namesArray = names.split(","); + ComponentName[] componentNames = new ComponentName[namesArray.length]; + for (int i = 0; i < namesArray.length; i++) { + componentNames[i] = ComponentName.unflattenFromString(namesArray[i]); + } + return componentNames; + } + + /** + * Keeps track of the current user, since dream() uses the current user's configuration. + */ + private static class CurrentUserManager { + private final Object mLock = new Object(); + private int mCurrentUserId; + + public void init(Context context) { + IntentFilter filter = new IntentFilter(); + filter.addAction(Intent.ACTION_USER_SWITCHED); + context.registerReceiver(new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (Intent.ACTION_USER_SWITCHED.equals(action)) { + synchronized(mLock) { + mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); + if (DEBUG) Slog.v(TAG, "userId " + mCurrentUserId + " is in the house"); + } + } + }}, filter); + try { + synchronized (mLock) { + mCurrentUserId = ActivityManagerNative.getDefault().getCurrentUser().id; + } + } catch (RemoteException e) { + Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e); + } + } + + public void dump(PrintWriter pw) { + pw.print(" user="); pw.println(getCurrentUserId()); + } + + public int getCurrentUserId() { + synchronized(mLock) { + return mCurrentUserId; + } + } + } + + /** + * Handler for asynchronous operations performed by the dream manager. + * + * Ensures operations to {@link DreamController} are single-threaded. + */ + private static final class DreamControllerHandler extends Handler { + private final DreamController mController; + private final Runnable mStopRunnable = new Runnable() { + @Override + public void run() { + mController.stop(); + }}; + + public DreamControllerHandler(DreamController controller) { + super(true /*async*/); + mController = controller; + } + + public void requestStart(final ComponentName name, final boolean isTest) { + post(new Runnable(){ + @Override + public void run() { + mController.start(name, isTest); + }}); + } + + public void requestAttach(final ComponentName name, final IBinder dream) { + post(new Runnable(){ + @Override + public void run() { + mController.attach(name, dream); + }}); + } + + public void requestStopSelf(final IBinder token) { + post(new Runnable(){ + @Override + public void run() { + mController.stopSelf(token); + }}); + } + + public void requestStop() { + post(mStopRunnable); + } + + } + +} diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index 32ab154..ba758e5 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -549,34 +549,6 @@ class MountService extends IMountService.Stub } } - private final BroadcastReceiver mBootReceiver = new BroadcastReceiver() { - @Override - public void onReceive(Context context, Intent intent) { - final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); - if (userId == -1) return; - final UserHandle user = new UserHandle(userId); - - Slog.d(TAG, "BOOT_COMPLETED for " + user); - - // Broadcast mounted volumes to newly booted user. This kicks off - // media scanner when a user becomes active. - synchronized (mVolumesLock) { - for (StorageVolume volume : mVolumes) { - final UserHandle owner = volume.getOwner(); - final boolean ownerMatch = owner == null - || owner.getIdentifier() == user.getIdentifier(); - - final String state = mVolumeStates.get(volume.getPath()); - - if (ownerMatch && (Environment.MEDIA_MOUNTED.equals(state) - || Environment.MEDIA_MOUNTED_READ_ONLY.equals(state))) { - sendStorageIntent(Intent.ACTION_MEDIA_MOUNTED, volume, user); - } - } - } - } - }; - private final BroadcastReceiver mUserReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -1309,10 +1281,6 @@ class MountService extends IMountService.Stub mHandlerThread.start(); mHandler = new MountServiceHandler(mHandlerThread.getLooper()); - // Watch for user boot completion - mContext.registerReceiverAsUser(mBootReceiver, UserHandle.ALL, - new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, mHandler); - // Watch for user changes final IntentFilter userFilter = new IntentFilter(); userFilter.addAction(Intent.ACTION_USER_ADDED); diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 90783b7..2792704 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -38,7 +38,6 @@ import android.os.StrictMode; import android.os.SystemClock; import android.os.SystemProperties; import android.server.search.SearchManagerService; -import android.service.dreams.DreamManagerService; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; @@ -189,7 +188,7 @@ class ServerThread extends Thread { // For debug builds, log event loop stalls to dropbox for analysis. if (StrictMode.conditionallyEnableDebugLogging()) { - Slog.i(TAG, "Enabled StrictMode logging for UI Looper"); + Slog.i(TAG, "Enabled StrictMode logging for WM Looper"); } } }); diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 989477f..1f5fa4d 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -168,6 +168,7 @@ public final class ActivityManagerService extends ActivityManagerNative static final boolean localLOGV = DEBUG; static final boolean DEBUG_SWITCH = localLOGV || false; static final boolean DEBUG_TASKS = localLOGV || false; + static final boolean DEBUG_THUMBNAILS = localLOGV || false; static final boolean DEBUG_PAUSE = localLOGV || false; static final boolean DEBUG_OOM_ADJ = localLOGV || false; static final boolean DEBUG_TRANSITION = localLOGV || false; @@ -253,6 +254,9 @@ public final class ActivityManagerService extends ActivityManagerNative // giving up on them and unfreezing the screen. static final int USER_SWITCH_TIMEOUT = 2*1000; + // Maximum number of users we allow to be running at a time. + static final int MAX_RUNNING_USERS = 3; + static final int MY_PID = Process.myPid(); static final String[] EMPTY_STRING_ARRAY = new String[0]; @@ -1794,7 +1798,7 @@ public final class ActivityManagerService extends ActivityManagerNative } private final void updateLruProcessInternalLocked(ProcessRecord app, - boolean oomAdj, boolean updateActivityTime, int bestPos) { + boolean updateActivityTime, int bestPos) { // put it on the LRU to keep track of when it should be exited. int lrui = mLruProcesses.indexOf(app); if (lrui >= 0) mLruProcesses.remove(lrui); @@ -1851,7 +1855,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (cr.binding != null && cr.binding.service != null && cr.binding.service.app != null && cr.binding.service.app.lruSeq != mLruSeq) { - updateLruProcessInternalLocked(cr.binding.service.app, false, + updateLruProcessInternalLocked(cr.binding.service.app, updateActivityTime, i+1); } } @@ -1859,21 +1863,21 @@ public final class ActivityManagerService extends ActivityManagerNative for (int j=app.conProviders.size()-1; j>=0; j--) { ContentProviderRecord cpr = app.conProviders.get(j).provider; if (cpr.proc != null && cpr.proc.lruSeq != mLruSeq) { - updateLruProcessInternalLocked(cpr.proc, false, + updateLruProcessInternalLocked(cpr.proc, updateActivityTime, i+1); } } - - //Slog.i(TAG, "Putting proc to front: " + app.processName); - if (oomAdj) { - updateOomAdjLocked(); - } } final void updateLruProcessLocked(ProcessRecord app, boolean oomAdj, boolean updateActivityTime) { mLruSeq++; - updateLruProcessInternalLocked(app, oomAdj, updateActivityTime, 0); + updateLruProcessInternalLocked(app, updateActivityTime, 0); + + //Slog.i(TAG, "Putting proc to front: " + app.processName); + if (oomAdj) { + updateOomAdjLocked(); + } } final ProcessRecord getProcessRecordLocked( @@ -4455,7 +4459,13 @@ public final class ActivityManagerService extends ActivityManagerNative enableScreenAfterBoot(); } } - + + public final void activityResumed(IBinder token) { + final long origId = Binder.clearCallingIdentity(); + mMainStack.activityResumed(token); + Binder.restoreCallingIdentity(origId); + } + public final void activityPaused(IBinder token) { final long origId = Binder.clearCallingIdentity(); mMainStack.activityPaused(token, false); @@ -5840,6 +5850,18 @@ public final class ActivityManagerService extends ActivityManagerNative return null; } + public Bitmap getTaskTopThumbnail(int id) { + synchronized (this) { + enforceCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER, + "getTaskTopThumbnail()"); + TaskRecord tr = taskForIdLocked(id); + if (tr != null) { + return mMainStack.getTaskTopThumbnailLocked(tr); + } + } + return null; + } + public boolean removeSubTask(int taskId, int subTaskIndex) { synchronized (this) { enforceCallingPermission(android.Manifest.permission.REMOVE_TASKS, @@ -11432,7 +11454,7 @@ public final class ActivityManagerService extends ActivityManagerNative // Add the new results to the existing results, tracking // and de-dupping single user receivers. for (int i=0; i<newReceivers.size(); i++) { - ResolveInfo ri = receivers.get(i); + ResolveInfo ri = newReceivers.get(i); if ((ri.activityInfo.flags&ActivityInfo.FLAG_SINGLE_USER) != 0) { ComponentName cn = new ComponentName( ri.activityInfo.packageName, ri.activityInfo.name); @@ -13960,6 +13982,8 @@ public final class ActivityManagerService extends ActivityManagerNative mUserLru.remove(userIdInt); mUserLru.add(userIdInt); + mWindowManager.setCurrentUser(userId); + final UserStartedState uss = mStartedUsers.get(userId); mHandler.removeMessages(REPORT_USER_SWITCH_MSG); @@ -13998,7 +14022,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (!haveActivities) { startHomeActivityLocked(userId); } - + sendUserSwitchBroadcastsLocked(oldUserId, userId); } } finally { @@ -14117,6 +14141,33 @@ public final class ActivityManagerService extends ActivityManagerNative android.Manifest.permission.RECEIVE_BOOT_COMPLETED, false, false, MY_PID, Process.SYSTEM_UID, userId); } + int num = mUserLru.size(); + int i = 0; + while (num > MAX_RUNNING_USERS && i < mUserLru.size()) { + Integer oldUserId = mUserLru.get(i); + UserStartedState oldUss = mStartedUsers.get(oldUserId); + if (oldUss == null) { + // Shouldn't happen, but be sane if it does. + mUserLru.remove(i); + num--; + continue; + } + if (oldUss.mState == UserStartedState.STATE_STOPPING) { + // This user is already stopping, doesn't count. + num--; + i++; + continue; + } + if (oldUserId == UserHandle.USER_OWNER || oldUserId == mCurrentUserId) { + // Owner and current can't be stopped, but count as running. + i++; + continue; + } + // This is a user to be stopped. + stopUserLocked(oldUserId, null); + num--; + i++; + } } } @@ -14135,52 +14186,56 @@ public final class ActivityManagerService extends ActivityManagerNative throw new IllegalArgumentException("Can't stop primary user " + userId); } synchronized (this) { - if (mCurrentUserId == userId) { - return ActivityManager.USER_OP_IS_CURRENT; - } - - final UserStartedState uss = mStartedUsers.get(userId); - if (uss == null) { - // User is not started, nothing to do... but we do need to - // callback if requested. - if (callback != null) { - mHandler.post(new Runnable() { - @Override - public void run() { - try { - callback.userStopped(userId); - } catch (RemoteException e) { - } - } - }); - } - return ActivityManager.USER_OP_SUCCESS; - } + return stopUserLocked(userId, callback); + } + } + private int stopUserLocked(final int userId, final IStopUserCallback callback) { + if (mCurrentUserId == userId) { + return ActivityManager.USER_OP_IS_CURRENT; + } + + final UserStartedState uss = mStartedUsers.get(userId); + if (uss == null) { + // User is not started, nothing to do... but we do need to + // callback if requested. if (callback != null) { - uss.mStopCallbacks.add(callback); + mHandler.post(new Runnable() { + @Override + public void run() { + try { + callback.userStopped(userId); + } catch (RemoteException e) { + } + } + }); } + return ActivityManager.USER_OP_SUCCESS; + } - if (uss.mState != UserStartedState.STATE_STOPPING) { - uss.mState = UserStartedState.STATE_STOPPING; + if (callback != null) { + uss.mStopCallbacks.add(callback); + } - long ident = Binder.clearCallingIdentity(); - try { - // Inform of user switch - Intent intent = new Intent(Intent.ACTION_SHUTDOWN); - final IIntentReceiver resultReceiver = new IIntentReceiver.Stub() { - @Override - public void performReceive(Intent intent, int resultCode, String data, - Bundle extras, boolean ordered, boolean sticky, int sendingUser) { - finishUserStop(uss); - } - }; - broadcastIntentLocked(null, null, intent, - null, resultReceiver, 0, null, null, null, - true, false, MY_PID, Process.SYSTEM_UID, userId); - } finally { - Binder.restoreCallingIdentity(ident); - } + if (uss.mState != UserStartedState.STATE_STOPPING) { + uss.mState = UserStartedState.STATE_STOPPING; + + long ident = Binder.clearCallingIdentity(); + try { + // Inform of user switch + Intent intent = new Intent(Intent.ACTION_SHUTDOWN); + final IIntentReceiver resultReceiver = new IIntentReceiver.Stub() { + @Override + public void performReceive(Intent intent, int resultCode, String data, + Bundle extras, boolean ordered, boolean sticky, int sendingUser) { + finishUserStop(uss); + } + }; + broadcastIntentLocked(null, null, intent, + null, resultReceiver, 0, null, null, null, + true, false, MY_PID, Process.SYSTEM_UID, userId); + } finally { + Binder.restoreCallingIdentity(ident); } } @@ -14200,6 +14255,7 @@ public final class ActivityManagerService extends ActivityManagerNative stopped = true; // User can no longer run. mStartedUsers.remove(userId); + mUserLru.remove(Integer.valueOf(userId)); // Clean up all state and processes associated with the user. // Kill all the processes for the user. @@ -14218,12 +14274,14 @@ public final class ActivityManagerService extends ActivityManagerNative @Override public UserInfo getCurrentUser() { - if (checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) - != PackageManager.PERMISSION_GRANTED) { + if ((checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) + != PackageManager.PERMISSION_GRANTED) && ( + checkCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) + != PackageManager.PERMISSION_GRANTED)) { String msg = "Permission Denial: getCurrentUser() from pid=" + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() - + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; + + " requires " + android.Manifest.permission.INTERACT_ACROSS_USERS; Slog.w(TAG, msg); throw new SecurityException(msg); } diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java index 009fb5d..7ff5748 100644 --- a/services/java/com/android/server/am/ActivityRecord.java +++ b/services/java/com/android/server/am/ActivityRecord.java @@ -219,7 +219,13 @@ final class ActivityRecord { pw.print(prefix); pw.print("frozenBeforeDestroy="); pw.print(frozenBeforeDestroy); pw.print(" thumbnailNeeded="); pw.print(thumbnailNeeded); pw.print(" forceNewConfig="); pw.println(forceNewConfig); - pw.print(prefix); pw.print("thumbHolder="); pw.println(thumbHolder); + pw.print(prefix); pw.print("thumbHolder: "); + pw.print(Integer.toHexString(System.identityHashCode(thumbHolder))); + if (thumbHolder != null) { + pw.print(" bm="); pw.print(thumbHolder.lastThumbnail); + pw.print(" desc="); pw.print(thumbHolder.lastDescription); + } + pw.println(); if (launchTime != 0 || startTime != 0) { pw.print(prefix); pw.print("launchTime="); if (launchTime == 0) pw.print("0"); @@ -674,19 +680,15 @@ final class ActivityRecord { } if (thumbHolder != null) { if (newThumbnail != null) { + if (ActivityManagerService.DEBUG_THUMBNAILS) Slog.i(ActivityManagerService.TAG, + "Setting thumbnail of " + this + " holder " + thumbHolder + + " to " + newThumbnail); thumbHolder.lastThumbnail = newThumbnail; } thumbHolder.lastDescription = description; } } - void clearThumbnail() { - if (thumbHolder != null) { - thumbHolder.lastThumbnail = null; - thumbHolder.lastDescription = null; - } - } - void startLaunchTickingLocked() { if (ActivityManagerService.IS_USER_BUILD) { return; diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index f72d318..df50d89 100755 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -52,7 +52,6 @@ import android.os.IBinder; import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.PowerManager; -import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; @@ -755,8 +754,6 @@ final class ActivityStack { completeResumeLocked(r); checkReadyForSleepLocked(); if (DEBUG_SAVED_STATE) Slog.i(TAG, "Launch completed; removing icicle of " + r.icicle); - r.icicle = null; - r.haveState = false; } else { // This activity is not starting in the resumed state... which // should look like we asked it to pause+stop (but remain visible), @@ -1010,7 +1007,21 @@ final class ActivityStack { resumeTopActivityLocked(null); } } - + + final void activityResumed(IBinder token) { + ActivityRecord r = null; + + synchronized (mService) { + int index = indexOfTokenLocked(token); + if (index >= 0) { + r = mHistory.get(index); + if (DEBUG_SAVED_STATE) Slog.i(TAG, "Resumed activity; dropping state of: " + r); + r.icicle = null; + r.haveState = false; + } + } + } + final void activityPaused(IBinder token, boolean timeout) { if (DEBUG_PAUSE) Slog.v( TAG, "Activity paused: token=" + token + ", timeout=" + timeout); @@ -1192,8 +1203,7 @@ final class ActivityStack { if (mMainStack) { mService.reportResumedActivityLocked(next); } - - next.clearThumbnail(); + if (mMainStack) { mService.setFocusedActivityLocked(next); } @@ -1488,6 +1498,15 @@ final class ActivityStack { // can be resumed... if (mResumedActivity != null) { if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: need to start pausing"); + // At this point we want to put the upcoming activity's process + // at the top of the LRU list, since we know we will be needing it + // very soon and it would be a waste to let it get killed if it + // happens to be sitting towards the end. + if (next.app != null && next.app.thread != null) { + // No reason to do full oom adj update here; we'll let that + // happen whenever it needs to later. + mService.updateLruProcessLocked(next.app, false, true); + } startPausingLocked(userLeaving, false); return true; } @@ -1728,11 +1747,6 @@ final class ActivityStack { "resume-exception", true); return true; } - - // Didn't need to use the icicle, and it is now out of date. - if (DEBUG_SAVED_STATE) Slog.i(TAG, "Resumed activity; didn't need icicle of: " + next); - next.icicle = null; - next.haveState = false; next.stopped = false; } else { @@ -2578,7 +2592,6 @@ final class ActivityStack { mDismissKeyguardOnNextActivity = false; mService.mWindowManager.dismissKeyguard(); } - Slog.i(TAG, "DONE STARTING!"); return err; } @@ -4314,18 +4327,33 @@ final class ActivityStack { finishTaskMoveLocked(task); return true; } - + public ActivityManager.TaskThumbnails getTaskThumbnailsLocked(TaskRecord tr) { TaskAccessInfo info = getTaskAccessInfoLocked(tr.taskId, true); ActivityRecord resumed = mResumedActivity; if (resumed != null && resumed.thumbHolder == tr) { info.mainThumbnail = resumed.stack.screenshotActivities(resumed); - } else { - info.mainThumbnail = tr.lastThumbnail; } return info; } + public Bitmap getTaskTopThumbnailLocked(TaskRecord tr) { + ActivityRecord resumed = mResumedActivity; + if (resumed != null && resumed.task == tr) { + // This task is the current resumed task, we just need to take + // a screenshot of it and return that. + return resumed.stack.screenshotActivities(resumed); + } + // Return the information about the task, to figure out the top + // thumbnail to return. + TaskAccessInfo info = getTaskAccessInfoLocked(tr.taskId, true); + if (info.numSubThumbbails <= 0) { + return info.mainThumbnail; + } else { + return info.subtasks.get(info.numSubThumbbails-1).holder.lastThumbnail; + } + } + public ActivityRecord removeTaskActivitiesLocked(int taskId, int subTaskIndex, boolean taskRequired) { TaskAccessInfo info = getTaskAccessInfoLocked(taskId, false); @@ -4356,7 +4384,6 @@ final class ActivityStack { } public TaskAccessInfo getTaskAccessInfoLocked(int taskId, boolean inclThumbs) { - ActivityRecord resumed = mResumedActivity; final TaskAccessInfo thumbs = new TaskAccessInfo(); // How many different sub-thumbnails? final int NA = mHistory.size(); @@ -4366,6 +4393,10 @@ final class ActivityStack { ActivityRecord ar = mHistory.get(j); if (!ar.finishing && ar.task.taskId == taskId) { holder = ar.thumbHolder; + if (holder != null) { + thumbs.mainThumbnail = holder.lastThumbnail; + } + j++; break; } j++; @@ -4380,7 +4411,6 @@ final class ActivityStack { ArrayList<TaskAccessInfo.SubTask> subtasks = new ArrayList<TaskAccessInfo.SubTask>(); thumbs.subtasks = subtasks; - ActivityRecord lastActivity = null; while (j < NA) { ActivityRecord ar = mHistory.get(j); j++; @@ -4390,30 +4420,28 @@ final class ActivityStack { if (ar.task.taskId != taskId) { break; } - lastActivity = ar; if (ar.thumbHolder != holder && holder != null) { thumbs.numSubThumbbails++; holder = ar.thumbHolder; TaskAccessInfo.SubTask sub = new TaskAccessInfo.SubTask(); - sub.thumbnail = holder.lastThumbnail; + sub.holder = holder; sub.activity = ar; sub.index = j-1; subtasks.add(sub); } } - if (lastActivity != null && subtasks.size() > 0) { - if (resumed == lastActivity) { - TaskAccessInfo.SubTask sub = subtasks.get(subtasks.size()-1); - sub.thumbnail = lastActivity.stack.screenshotActivities(lastActivity); - } - } if (thumbs.numSubThumbbails > 0) { thumbs.retriever = new IThumbnailRetriever.Stub() { public Bitmap getThumbnail(int index) { if (index < 0 || index >= thumbs.subtasks.size()) { return null; } - return thumbs.subtasks.get(index).thumbnail; + TaskAccessInfo.SubTask sub = thumbs.subtasks.get(index); + ActivityRecord resumed = mResumedActivity; + if (resumed != null && resumed.thumbHolder == sub.holder) { + return resumed.stack.screenshotActivities(resumed); + } + return sub.holder.lastThumbnail; } }; } diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java index 1b6ff7a..b0af081 100644 --- a/services/java/com/android/server/am/BroadcastQueue.java +++ b/services/java/com/android/server/am/BroadcastQueue.java @@ -965,12 +965,12 @@ public class BroadcastQueue { if (!printed) { if (needSep) { pw.println(); - needSep = false; } + needSep = true; printed = true; pw.println(" Active broadcasts [" + mQueueName + "]:"); } - pw.println(" Broadcast #" + i + ":"); + pw.println(" Active Broadcast " + mQueueName + " #" + i + ":"); br.dump(pw, " "); } printed = false; @@ -985,9 +985,10 @@ public class BroadcastQueue { pw.println(); } needSep = true; + printed = true; pw.println(" Active ordered broadcasts [" + mQueueName + "]:"); } - pw.println(" Ordered Broadcast #" + i + ":"); + pw.println(" Active Ordered Broadcast " + mQueueName + " #" + i + ":"); mOrderedBroadcasts.get(i).dump(pw, " "); } if (dumpPackage == null || (mPendingBroadcast != null @@ -1023,7 +1024,8 @@ public class BroadcastQueue { printed = true; } if (dumpAll) { - pw.print(" Historical Broadcast #"); pw.print(i); pw.println(":"); + pw.print(" Historical Broadcast " + mQueueName + " #"); + pw.print(i); pw.println(":"); r.dump(pw, " "); } else { if (i >= 50) { diff --git a/services/java/com/android/server/am/TaskAccessInfo.java b/services/java/com/android/server/am/TaskAccessInfo.java index 5618c1a..50aeec1 100644 --- a/services/java/com/android/server/am/TaskAccessInfo.java +++ b/services/java/com/android/server/am/TaskAccessInfo.java @@ -19,11 +19,10 @@ package com.android.server.am; import java.util.ArrayList; import android.app.ActivityManager.TaskThumbnails; -import android.graphics.Bitmap; final class TaskAccessInfo extends TaskThumbnails { final static class SubTask { - Bitmap thumbnail; + ThumbnailHolder holder; ActivityRecord activity; int index; } diff --git a/services/java/com/android/server/display/DisplayManagerService.java b/services/java/com/android/server/display/DisplayManagerService.java index 39f2418..02fc6b1 100644 --- a/services/java/com/android/server/display/DisplayManagerService.java +++ b/services/java/com/android/server/display/DisplayManagerService.java @@ -148,6 +148,9 @@ public final class DisplayManagerService extends IDisplayManager.Stub { private final DisplayViewport mDefaultViewport = new DisplayViewport(); private final DisplayViewport mExternalTouchViewport = new DisplayViewport(); + // Persistent data store for all internal settings maintained by the display manager service. + private final PersistentDataStore mPersistentDataStore = new PersistentDataStore(); + // Temporary callback list, used when sending display events to applications. // May be used outside of the lock but only on the handler thread. private final ArrayList<CallbackRecord> mTempCallbacks = new ArrayList<CallbackRecord>(); @@ -403,6 +406,50 @@ public final class DisplayManagerService extends IDisplayManager.Stub { } @Override // Binder call + public void renameWifiDisplay(String address, String alias) { + if (mContext.checkCallingPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires CONFIGURE_WIFI_DISPLAY permission"); + } + if (address == null) { + throw new IllegalArgumentException("address must not be null"); + } + + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mSyncRoot) { + if (mWifiDisplayAdapter != null) { + mWifiDisplayAdapter.requestRenameLocked(address, alias); + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override // Binder call + public void forgetWifiDisplay(String address) { + if (mContext.checkCallingPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Requires CONFIGURE_WIFI_DISPLAY permission"); + } + if (address == null) { + throw new IllegalArgumentException("address must not be null"); + } + + final long token = Binder.clearCallingIdentity(); + try { + synchronized (mSyncRoot) { + if (mWifiDisplayAdapter != null) { + mWifiDisplayAdapter.requestForgetLocked(address); + } + } + } finally { + Binder.restoreCallingIdentity(token); + } + } + + @Override // Binder call public WifiDisplayStatus getWifiDisplayStatus() { if (mContext.checkCallingPermission(android.Manifest.permission.CONFIGURE_WIFI_DISPLAY) != PackageManager.PERMISSION_GRANTED) { @@ -439,15 +486,27 @@ public final class DisplayManagerService extends IDisplayManager.Stub { private void registerAdditionalDisplayAdapters() { synchronized (mSyncRoot) { if (shouldRegisterNonEssentialDisplayAdaptersLocked()) { - registerDisplayAdapterLocked(new OverlayDisplayAdapter( - mSyncRoot, mContext, mHandler, mDisplayAdapterListener, mUiHandler)); - mWifiDisplayAdapter = new WifiDisplayAdapter( - mSyncRoot, mContext, mHandler, mDisplayAdapterListener); - registerDisplayAdapterLocked(mWifiDisplayAdapter); + registerOverlayDisplayAdapterLocked(); + registerWifiDisplayAdapterLocked(); } } } + private void registerOverlayDisplayAdapterLocked() { + registerDisplayAdapterLocked(new OverlayDisplayAdapter( + mSyncRoot, mContext, mHandler, mDisplayAdapterListener, mUiHandler)); + } + + private void registerWifiDisplayAdapterLocked() { + if (mContext.getResources().getBoolean( + com.android.internal.R.bool.config_enableWifiDisplay)) { + mWifiDisplayAdapter = new WifiDisplayAdapter( + mSyncRoot, mContext, mHandler, mDisplayAdapterListener, + mPersistentDataStore); + registerDisplayAdapterLocked(mWifiDisplayAdapter); + } + } + private boolean shouldRegisterNonEssentialDisplayAdaptersLocked() { // In safe mode, we disable non-essential display adapters to give the user // an opportunity to fix broken settings or other problems that might affect diff --git a/services/java/com/android/server/display/PersistentDataStore.java b/services/java/com/android/server/display/PersistentDataStore.java new file mode 100644 index 0000000..3a6e1a6 --- /dev/null +++ b/services/java/com/android/server/display/PersistentDataStore.java @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2012 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.display; + +import com.android.internal.util.FastXmlSerializer; +import com.android.internal.util.XmlUtils; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +import android.hardware.display.WifiDisplay; +import android.util.AtomicFile; +import android.util.Slog; +import android.util.Xml; + +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; + +import libcore.io.IoUtils; +import libcore.util.Objects; + +/** + * Manages persistent state recorded by the display manager service as an XML file. + * Caller must acquire lock on the data store before accessing it. + * + * File format: + * <code> + * <display-manager-state> + * <remembered-wifi-displays> + * <wifi-display deviceAddress="00:00:00:00:00:00" deviceName="XXXX" deviceAlias="YYYY" /> + * >remembered-wifi-displays> + * >/display-manager-state> + * </code> + * + * TODO: refactor this to extract common code shared with the input manager's data store + */ +final class PersistentDataStore { + static final String TAG = "DisplayManager"; + + // Remembered Wifi display devices. + private ArrayList<WifiDisplay> mRememberedWifiDisplays = new ArrayList<WifiDisplay>(); + + // The atomic file used to safely read or write the file. + private final AtomicFile mAtomicFile; + + // True if the data has been loaded. + private boolean mLoaded; + + // True if there are changes to be saved. + private boolean mDirty; + + public PersistentDataStore() { + mAtomicFile = new AtomicFile(new File("/data/system/display-manager-state.xml")); + } + + public void saveIfNeeded() { + if (mDirty) { + save(); + mDirty = false; + } + } + + public WifiDisplay[] getRememberedWifiDisplays() { + loadIfNeeded(); + return mRememberedWifiDisplays.toArray(new WifiDisplay[mRememberedWifiDisplays.size()]); + } + + public WifiDisplay applyWifiDisplayAlias(WifiDisplay display) { + if (display != null) { + loadIfNeeded(); + + String alias = null; + int index = findRememberedWifiDisplay(display.getDeviceAddress()); + if (index >= 0) { + alias = mRememberedWifiDisplays.get(index).getDeviceAlias(); + } + if (!Objects.equal(display.getDeviceAlias(), alias)) { + return new WifiDisplay(display.getDeviceAddress(), display.getDeviceName(), alias); + } + } + return display; + } + + public WifiDisplay[] applyWifiDisplayAliases(WifiDisplay[] displays) { + WifiDisplay[] results = displays; + if (results != null) { + int count = displays.length; + for (int i = 0; i < count; i++) { + WifiDisplay result = applyWifiDisplayAlias(displays[i]); + if (result != displays[i]) { + if (results == displays) { + results = new WifiDisplay[count]; + System.arraycopy(displays, 0, results, 0, count); + } + results[i] = result; + } + } + } + return results; + } + + public boolean rememberWifiDisplay(WifiDisplay display) { + loadIfNeeded(); + + int index = findRememberedWifiDisplay(display.getDeviceAddress()); + if (index >= 0) { + WifiDisplay other = mRememberedWifiDisplays.get(index); + if (other.equals(display)) { + return false; // already remembered without change + } + mRememberedWifiDisplays.set(index, display); + } else { + mRememberedWifiDisplays.add(display); + } + setDirty(); + return true; + } + + public boolean renameWifiDisplay(String deviceAddress, String alias) { + int index = findRememberedWifiDisplay(deviceAddress); + if (index >= 0) { + WifiDisplay display = mRememberedWifiDisplays.get(index); + if (Objects.equal(display.getDeviceAlias(), alias)) { + return false; // already has this alias + } + WifiDisplay renamedDisplay = new WifiDisplay(deviceAddress, + display.getDeviceName(), alias); + mRememberedWifiDisplays.set(index, renamedDisplay); + setDirty(); + return true; + } + return false; + } + + public boolean forgetWifiDisplay(String deviceAddress) { + int index = findRememberedWifiDisplay(deviceAddress); + if (index >= 0) { + mRememberedWifiDisplays.remove(index); + setDirty(); + return true; + } + return false; + } + + private int findRememberedWifiDisplay(String deviceAddress) { + int count = mRememberedWifiDisplays.size(); + for (int i = 0; i < count; i++) { + if (mRememberedWifiDisplays.get(i).getDeviceAddress().equals(deviceAddress)) { + return i; + } + } + return -1; + } + + private void loadIfNeeded() { + if (!mLoaded) { + load(); + mLoaded = true; + } + } + + private void setDirty() { + mDirty = true; + } + + private void clearState() { + mRememberedWifiDisplays.clear(); + } + + private void load() { + clearState(); + + final InputStream is; + try { + is = mAtomicFile.openRead(); + } catch (FileNotFoundException ex) { + return; + } + + XmlPullParser parser; + try { + parser = Xml.newPullParser(); + parser.setInput(new BufferedInputStream(is), null); + loadFromXml(parser); + } catch (IOException ex) { + Slog.w(TAG, "Failed to load display manager persistent store data.", ex); + clearState(); + } catch (XmlPullParserException ex) { + Slog.w(TAG, "Failed to load display manager persistent store data.", ex); + clearState(); + } finally { + IoUtils.closeQuietly(is); + } + } + + private void save() { + final FileOutputStream os; + try { + os = mAtomicFile.startWrite(); + boolean success = false; + try { + XmlSerializer serializer = new FastXmlSerializer(); + serializer.setOutput(new BufferedOutputStream(os), "utf-8"); + saveToXml(serializer); + serializer.flush(); + success = true; + } finally { + if (success) { + mAtomicFile.finishWrite(os); + } else { + mAtomicFile.failWrite(os); + } + } + } catch (IOException ex) { + Slog.w(TAG, "Failed to save display manager persistent store data.", ex); + } + } + + private void loadFromXml(XmlPullParser parser) + throws IOException, XmlPullParserException { + XmlUtils.beginDocument(parser, "display-manager-state"); + final int outerDepth = parser.getDepth(); + while (XmlUtils.nextElementWithin(parser, outerDepth)) { + if (parser.getName().equals("remembered-wifi-displays")) { + loadRememberedWifiDisplaysFromXml(parser); + } + } + } + + private void loadRememberedWifiDisplaysFromXml(XmlPullParser parser) + throws IOException, XmlPullParserException { + final int outerDepth = parser.getDepth(); + while (XmlUtils.nextElementWithin(parser, outerDepth)) { + if (parser.getName().equals("wifi-display")) { + String deviceAddress = parser.getAttributeValue(null, "deviceAddress"); + String deviceName = parser.getAttributeValue(null, "deviceName"); + String deviceAlias = parser.getAttributeValue(null, "deviceAlias"); + if (deviceAddress == null || deviceName == null) { + throw new XmlPullParserException( + "Missing deviceAddress or deviceName attribute on wifi-display."); + } + if (findRememberedWifiDisplay(deviceAddress) >= 0) { + throw new XmlPullParserException( + "Found duplicate wifi display device address."); + } + + mRememberedWifiDisplays.add( + new WifiDisplay(deviceAddress, deviceName, deviceAlias)); + } + } + } + + private void saveToXml(XmlSerializer serializer) throws IOException { + serializer.startDocument(null, true); + serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + serializer.startTag(null, "display-manager-state"); + serializer.startTag(null, "remembered-wifi-displays"); + for (WifiDisplay display : mRememberedWifiDisplays) { + serializer.startTag(null, "wifi-display"); + serializer.attribute(null, "deviceAddress", display.getDeviceAddress()); + serializer.attribute(null, "deviceName", display.getDeviceName()); + if (display.getDeviceAlias() != null) { + serializer.attribute(null, "deviceAlias", display.getDeviceAlias()); + } + serializer.endTag(null, "wifi-display"); + } + serializer.endTag(null, "remembered-wifi-displays"); + serializer.endTag(null, "display-manager-state"); + serializer.endDocument(); + } +}
\ No newline at end of file diff --git a/services/java/com/android/server/display/WifiDisplayAdapter.java b/services/java/com/android/server/display/WifiDisplayAdapter.java index b57d3dc..1d50ded 100644 --- a/services/java/com/android/server/display/WifiDisplayAdapter.java +++ b/services/java/com/android/server/display/WifiDisplayAdapter.java @@ -49,21 +49,26 @@ import java.util.Arrays; final class WifiDisplayAdapter extends DisplayAdapter { private static final String TAG = "WifiDisplayAdapter"; + private PersistentDataStore mPersistentDataStore; + private WifiDisplayController mDisplayController; private WifiDisplayDevice mDisplayDevice; private WifiDisplayStatus mCurrentStatus; - private boolean mEnabled; + private int mFeatureState; private int mScanState; private int mActiveDisplayState; private WifiDisplay mActiveDisplay; - private WifiDisplay[] mKnownDisplays = WifiDisplay.EMPTY_ARRAY; + private WifiDisplay[] mAvailableDisplays = WifiDisplay.EMPTY_ARRAY; + private WifiDisplay[] mRememberedDisplays = WifiDisplay.EMPTY_ARRAY; private boolean mPendingStatusChangeBroadcast; public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, - Context context, Handler handler, Listener listener) { + Context context, Handler handler, Listener listener, + PersistentDataStore persistentDataStore) { super(syncRoot, context, handler, listener, TAG); + mPersistentDataStore = persistentDataStore; } @Override @@ -71,11 +76,12 @@ final class WifiDisplayAdapter extends DisplayAdapter { super.dumpLocked(pw); pw.println("mCurrentStatus=" + getWifiDisplayStatusLocked()); - pw.println("mEnabled=" + mEnabled); + pw.println("mFeatureState=" + mFeatureState); pw.println("mScanState=" + mScanState); pw.println("mActiveDisplayState=" + mActiveDisplayState); pw.println("mActiveDisplay=" + mActiveDisplay); - pw.println("mKnownDisplays=" + Arrays.toString(mKnownDisplays)); + pw.println("mAvailableDisplays=" + Arrays.toString(mAvailableDisplays)); + pw.println("mRememberedDisplays=" + Arrays.toString(mRememberedDisplays)); pw.println("mPendingStatusChangeBroadcast=" + mPendingStatusChangeBroadcast); // Try to dump the controller state. @@ -93,6 +99,8 @@ final class WifiDisplayAdapter extends DisplayAdapter { public void registerLocked() { super.registerLocked(); + updateRememberedDisplaysLocked(); + getHandler().post(new Runnable() { @Override public void run() { @@ -135,18 +143,58 @@ final class WifiDisplayAdapter extends DisplayAdapter { }); } + public void requestRenameLocked(String address, String alias) { + if (alias != null) { + alias = alias.trim(); + if (alias.isEmpty()) { + alias = null; + } + } + + if (mPersistentDataStore.renameWifiDisplay(address, alias)) { + mPersistentDataStore.saveIfNeeded(); + updateRememberedDisplaysLocked(); + scheduleStatusChangedBroadcastLocked(); + } + } + + public void requestForgetLocked(String address) { + if (mPersistentDataStore.forgetWifiDisplay(address)) { + mPersistentDataStore.saveIfNeeded(); + updateRememberedDisplaysLocked(); + scheduleStatusChangedBroadcastLocked(); + } + + if (mActiveDisplay != null && mActiveDisplay.getDeviceAddress().equals(address)) { + requestDisconnectLocked(); + } + } + public WifiDisplayStatus getWifiDisplayStatusLocked() { if (mCurrentStatus == null) { - mCurrentStatus = new WifiDisplayStatus(mEnabled, mScanState, mActiveDisplayState, - mActiveDisplay, mKnownDisplays); + mCurrentStatus = new WifiDisplayStatus( + mFeatureState, mScanState, mActiveDisplayState, + mActiveDisplay, mAvailableDisplays, mRememberedDisplays); } return mCurrentStatus; } + private void updateRememberedDisplaysLocked() { + mRememberedDisplays = mPersistentDataStore.getRememberedWifiDisplays(); + mActiveDisplay = mPersistentDataStore.applyWifiDisplayAlias(mActiveDisplay); + mAvailableDisplays = mPersistentDataStore.applyWifiDisplayAliases(mAvailableDisplays); + } + private void handleConnectLocked(WifiDisplay display, Surface surface, int width, int height, int flags) { handleDisconnectLocked(); + if (mPersistentDataStore.rememberWifiDisplay(display)) { + mPersistentDataStore.saveIfNeeded(); + updateRememberedDisplaysLocked(); + scheduleStatusChangedBroadcastLocked(); + } + int deviceFlags = 0; if ((flags & RemoteDisplay.DISPLAY_FLAG_SECURE) != 0) { deviceFlags |= DisplayDeviceInfo.FLAG_SUPPORTS_SECURE_VIDEO_OUTPUT; @@ -154,7 +202,7 @@ final class WifiDisplayAdapter extends DisplayAdapter { float refreshRate = 60.0f; // TODO: get this for real - String name = display.getDeviceName(); + String name = display.getFriendlyDisplayName(); IBinder displayToken = Surface.createDisplay(name); mDisplayDevice = new WifiDisplayDevice(displayToken, name, width, height, refreshRate, deviceFlags, surface); @@ -170,6 +218,7 @@ final class WifiDisplayAdapter extends DisplayAdapter { } private void scheduleStatusChangedBroadcastLocked() { + mCurrentStatus = null; if (!mPendingStatusChangeBroadcast) { mPendingStatusChangeBroadcast = true; getHandler().post(mStatusChangeBroadcast); @@ -202,11 +251,10 @@ final class WifiDisplayAdapter extends DisplayAdapter { private final WifiDisplayController.Listener mWifiDisplayListener = new WifiDisplayController.Listener() { @Override - public void onEnablementChanged(boolean enabled) { + public void onFeatureStateChanged(int featureState) { synchronized (getSyncRoot()) { - if (mEnabled != enabled) { - mCurrentStatus = null; - mEnabled = enabled; + if (mFeatureState != featureState) { + mFeatureState = featureState; scheduleStatusChangedBroadcastLocked(); } } @@ -216,20 +264,21 @@ final class WifiDisplayAdapter extends DisplayAdapter { public void onScanStarted() { synchronized (getSyncRoot()) { if (mScanState != WifiDisplayStatus.SCAN_STATE_SCANNING) { - mCurrentStatus = null; mScanState = WifiDisplayStatus.SCAN_STATE_SCANNING; scheduleStatusChangedBroadcastLocked(); } } } - public void onScanFinished(WifiDisplay[] knownDisplays) { + public void onScanFinished(WifiDisplay[] availableDisplays) { synchronized (getSyncRoot()) { + availableDisplays = mPersistentDataStore.applyWifiDisplayAliases( + availableDisplays); + if (mScanState != WifiDisplayStatus.SCAN_STATE_NOT_SCANNING - || !Arrays.equals(mKnownDisplays, knownDisplays)) { - mCurrentStatus = null; + || !Arrays.equals(mAvailableDisplays, availableDisplays)) { mScanState = WifiDisplayStatus.SCAN_STATE_NOT_SCANNING; - mKnownDisplays = knownDisplays; + mAvailableDisplays = availableDisplays; scheduleStatusChangedBroadcastLocked(); } } @@ -238,10 +287,11 @@ final class WifiDisplayAdapter extends DisplayAdapter { @Override public void onDisplayConnecting(WifiDisplay display) { synchronized (getSyncRoot()) { + display = mPersistentDataStore.applyWifiDisplayAlias(display); + if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTING || mActiveDisplay == null || !mActiveDisplay.equals(display)) { - mCurrentStatus = null; mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTING; mActiveDisplay = display; scheduleStatusChangedBroadcastLocked(); @@ -254,7 +304,6 @@ final class WifiDisplayAdapter extends DisplayAdapter { synchronized (getSyncRoot()) { if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED || mActiveDisplay != null) { - mCurrentStatus = null; mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED; mActiveDisplay = null; scheduleStatusChangedBroadcastLocked(); @@ -266,12 +315,12 @@ final class WifiDisplayAdapter extends DisplayAdapter { public void onDisplayConnected(WifiDisplay display, Surface surface, int width, int height, int flags) { synchronized (getSyncRoot()) { + display = mPersistentDataStore.applyWifiDisplayAlias(display); handleConnectLocked(display, surface, width, height, flags); if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTED || mActiveDisplay == null || !mActiveDisplay.equals(display)) { - mCurrentStatus = null; mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTED; mActiveDisplay = display; scheduleStatusChangedBroadcastLocked(); @@ -287,7 +336,6 @@ final class WifiDisplayAdapter extends DisplayAdapter { if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED || mActiveDisplay != null) { - mCurrentStatus = null; mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED; mActiveDisplay = null; scheduleStatusChangedBroadcastLocked(); diff --git a/services/java/com/android/server/display/WifiDisplayController.java b/services/java/com/android/server/display/WifiDisplayController.java index 87e11e6..84f4e83 100644 --- a/services/java/com/android/server/display/WifiDisplayController.java +++ b/services/java/com/android/server/display/WifiDisplayController.java @@ -19,13 +19,17 @@ package com.android.server.display; import com.android.internal.util.DumpUtils; import android.content.BroadcastReceiver; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.database.ContentObserver; import android.hardware.display.WifiDisplay; +import android.hardware.display.WifiDisplayStatus; import android.media.AudioManager; import android.media.RemoteDisplay; import android.net.NetworkInfo; +import android.net.Uri; import android.net.wifi.p2p.WifiP2pConfig; import android.net.wifi.p2p.WifiP2pDevice; import android.net.wifi.p2p.WifiP2pDeviceList; @@ -37,6 +41,7 @@ import android.net.wifi.p2p.WifiP2pManager.Channel; import android.net.wifi.p2p.WifiP2pManager.GroupInfoListener; import android.net.wifi.p2p.WifiP2pManager.PeerListListener; import android.os.Handler; +import android.provider.Settings; import android.util.Slog; import android.view.Surface; @@ -48,6 +53,8 @@ import java.net.SocketException; import java.util.ArrayList; import java.util.Enumeration; +import libcore.util.Objects; + /** * Manages all of the various asynchronous interactions with the {@link WifiP2pManager} * on behalf of {@link WifiDisplayAdapter}. @@ -94,9 +101,12 @@ final class WifiDisplayController implements DumpUtils.Dump { private boolean mWfdEnabling; private NetworkInfo mNetworkInfo; - private final ArrayList<WifiP2pDevice> mKnownWifiDisplayPeers = + private final ArrayList<WifiP2pDevice> mAvailableWifiDisplayPeers = new ArrayList<WifiP2pDevice>(); + // True if Wifi display is enabled by the user. + private boolean mWifiDisplayOnSetting; + // True if there is a call to discoverPeers in progress. private boolean mDiscoverPeersInProgress; @@ -132,6 +142,13 @@ final class WifiDisplayController implements DumpUtils.Dump { // True if the remote submix is enabled. private boolean mRemoteSubmixOn; + // The information we have most recently told WifiDisplayAdapter about. + private WifiDisplay mAdvertisedDisplay; + private Surface mAdvertisedDisplaySurface; + private int mAdvertisedDisplayWidth; + private int mAdvertisedDisplayHeight; + private int mAdvertisedDisplayFlags; + public WifiDisplayController(Context context, Handler handler, Listener listener) { mContext = context; mHandler = handler; @@ -146,10 +163,31 @@ final class WifiDisplayController implements DumpUtils.Dump { intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION); intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION); intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION); - context.registerReceiver(mWifiP2pReceiver, intentFilter); + context.registerReceiver(mWifiP2pReceiver, intentFilter, null, mHandler); + + ContentObserver settingsObserver = new ContentObserver(mHandler) { + @Override + public void onChange(boolean selfChange, Uri uri) { + updateSettings(); + } + }; + + final ContentResolver resolver = mContext.getContentResolver(); + resolver.registerContentObserver(Settings.Global.getUriFor( + Settings.Global.WIFI_DISPLAY_ON), false, settingsObserver); + updateSettings(); + } + + private void updateSettings() { + final ContentResolver resolver = mContext.getContentResolver(); + mWifiDisplayOnSetting = Settings.Global.getInt(resolver, + Settings.Global.WIFI_DISPLAY_ON, 0) != 0; + + updateWfdEnableState(); } public void dump(PrintWriter pw) { + pw.println("mWifiDisplayOnSetting=" + mWifiDisplayOnSetting); pw.println("mWifiP2pEnabled=" + mWifiP2pEnabled); pw.println("mWfdEnabled=" + mWfdEnabled); pw.println("mWfdEnabling=" + mWfdEnabling); @@ -164,9 +202,14 @@ final class WifiDisplayController implements DumpUtils.Dump { pw.println("mRemoteDisplayInterface=" + mRemoteDisplayInterface); pw.println("mRemoteDisplayConnected=" + mRemoteDisplayConnected); pw.println("mRemoteSubmixOn=" + mRemoteSubmixOn); - - pw.println("mKnownWifiDisplayPeers: size=" + mKnownWifiDisplayPeers.size()); - for (WifiP2pDevice device : mKnownWifiDisplayPeers) { + pw.println("mAdvertisedDisplay=" + mAdvertisedDisplay); + pw.println("mAdvertisedDisplaySurface=" + mAdvertisedDisplaySurface); + pw.println("mAdvertisedDisplayWidth=" + mAdvertisedDisplayWidth); + pw.println("mAdvertisedDisplayHeight=" + mAdvertisedDisplayHeight); + pw.println("mAdvertisedDisplayFlags=" + mAdvertisedDisplayFlags); + + pw.println("mAvailableWifiDisplayPeers: size=" + mAvailableWifiDisplayPeers.size()); + for (WifiP2pDevice device : mAvailableWifiDisplayPeers) { pw.println(" " + describeWifiP2pDevice(device)); } } @@ -176,7 +219,7 @@ final class WifiDisplayController implements DumpUtils.Dump { } public void requestConnect(String address) { - for (WifiP2pDevice device : mKnownWifiDisplayPeers) { + for (WifiP2pDevice device : mAvailableWifiDisplayPeers) { if (device.deviceAddress.equals(address)) { connect(device); } @@ -187,49 +230,65 @@ final class WifiDisplayController implements DumpUtils.Dump { disconnect(); } - private void enableWfd() { - if (!mWfdEnabled && !mWfdEnabling) { - mWfdEnabling = true; - - WifiP2pWfdInfo wfdInfo = new WifiP2pWfdInfo(); - wfdInfo.setWfdEnabled(true); - wfdInfo.setDeviceType(WifiP2pWfdInfo.WFD_SOURCE); - wfdInfo.setSessionAvailable(true); - wfdInfo.setControlPort(DEFAULT_CONTROL_PORT); - wfdInfo.setMaxThroughput(MAX_THROUGHPUT); - mWifiP2pManager.setWFDInfo(mWifiP2pChannel, wfdInfo, new ActionListener() { - @Override - public void onSuccess() { - if (DEBUG) { - Slog.d(TAG, "Successfully set WFD info."); - } - if (mWfdEnabling) { - mWfdEnabling = false; - setWfdEnabled(true); + private void updateWfdEnableState() { + if (mWifiDisplayOnSetting && mWifiP2pEnabled) { + // WFD should be enabled. + if (!mWfdEnabled && !mWfdEnabling) { + mWfdEnabling = true; + + WifiP2pWfdInfo wfdInfo = new WifiP2pWfdInfo(); + wfdInfo.setWfdEnabled(true); + wfdInfo.setDeviceType(WifiP2pWfdInfo.WFD_SOURCE); + wfdInfo.setSessionAvailable(true); + wfdInfo.setControlPort(DEFAULT_CONTROL_PORT); + wfdInfo.setMaxThroughput(MAX_THROUGHPUT); + mWifiP2pManager.setWFDInfo(mWifiP2pChannel, wfdInfo, new ActionListener() { + @Override + public void onSuccess() { + if (DEBUG) { + Slog.d(TAG, "Successfully set WFD info."); + } + if (mWfdEnabling) { + mWfdEnabling = false; + mWfdEnabled = true; + reportFeatureState(); + } } - } - @Override - public void onFailure(int reason) { - if (DEBUG) { - Slog.d(TAG, "Failed to set WFD info with reason " + reason + "."); + @Override + public void onFailure(int reason) { + if (DEBUG) { + Slog.d(TAG, "Failed to set WFD info with reason " + reason + "."); + } + mWfdEnabling = false; } - mWfdEnabling = false; - } - }); + }); + } + } else { + // WFD should be disabled. + mWfdEnabling = false; + mWfdEnabled = false; + reportFeatureState(); + disconnect(); } } - private void setWfdEnabled(final boolean enabled) { - if (mWfdEnabled != enabled) { - mWfdEnabled = enabled; - mHandler.post(new Runnable() { - @Override - public void run() { - mListener.onEnablementChanged(enabled); - } - }); + private void reportFeatureState() { + final int featureState = computeFeatureState(); + mHandler.post(new Runnable() { + @Override + public void run() { + mListener.onFeatureStateChanged(featureState); + } + }); + } + + private int computeFeatureState() { + if (!mWifiP2pEnabled) { + return WifiDisplayStatus.FEATURE_STATE_DISABLED; } + return mWifiDisplayOnSetting ? WifiDisplayStatus.FEATURE_STATE_ON : + WifiDisplayStatus.FEATURE_STATE_OFF; } private void discoverPeers() { @@ -296,14 +355,14 @@ final class WifiDisplayController implements DumpUtils.Dump { Slog.d(TAG, "Received list of peers."); } - mKnownWifiDisplayPeers.clear(); + mAvailableWifiDisplayPeers.clear(); for (WifiP2pDevice device : peers.getDeviceList()) { if (DEBUG) { Slog.d(TAG, " " + describeWifiP2pDevice(device)); } if (isWifiDisplay(device)) { - mKnownWifiDisplayPeers.add(device); + mAvailableWifiDisplayPeers.add(device); } } @@ -322,10 +381,10 @@ final class WifiDisplayController implements DumpUtils.Dump { } private void handleScanFinished() { - final int count = mKnownWifiDisplayPeers.size(); + final int count = mAvailableWifiDisplayPeers.size(); final WifiDisplay[] displays = WifiDisplay.CREATOR.newArray(count); for (int i = 0; i < count; i++) { - displays[i] = createWifiDisplay(mKnownWifiDisplayPeers.get(i)); + displays[i] = createWifiDisplay(mAvailableWifiDisplayPeers.get(i)); } mHandler.post(new Runnable() { @@ -368,18 +427,11 @@ final class WifiDisplayController implements DumpUtils.Dump { } private void retryConnection() { - if (mDesiredDevice != null && mConnectedDevice != mDesiredDevice - && mConnectionRetriesLeft > 0) { - mConnectionRetriesLeft -= 1; - Slog.i(TAG, "Retrying Wifi display connection. Retries left: " - + mConnectionRetriesLeft); - - // Cheap hack. Make a new instance of the device object so that we - // can distinguish it from the previous connection attempt. - // This will cause us to tear everything down before we try again. - mDesiredDevice = new WifiP2pDevice(mDesiredDevice); - updateConnection(); - } + // Cheap hack. Make a new instance of the device object so that we + // can distinguish it from the previous connection attempt. + // This will cause us to tear everything down before we try again. + mDesiredDevice = new WifiP2pDevice(mDesiredDevice); + updateConnection(); } /** @@ -401,13 +453,7 @@ final class WifiDisplayController implements DumpUtils.Dump { mHandler.removeCallbacks(mRtspTimeout); setRemoteSubmixOn(false); - - mHandler.post(new Runnable() { - @Override - public void run() { - mListener.onDisplayDisconnected(); - } - }); + unadvertiseDisplay(); // continue to next step } @@ -416,6 +462,8 @@ final class WifiDisplayController implements DumpUtils.Dump { if (mConnectedDevice != null && mConnectedDevice != mDesiredDevice) { Slog.i(TAG, "Disconnecting from Wifi display: " + mConnectedDevice.deviceName); + unadvertiseDisplay(); + final WifiP2pDevice oldDevice = mConnectedDevice; mWifiP2pManager.removeGroup(mWifiP2pChannel, new ActionListener() { @Override @@ -446,6 +494,7 @@ final class WifiDisplayController implements DumpUtils.Dump { if (mConnectingDevice != null && mConnectingDevice != mDesiredDevice) { Slog.i(TAG, "Canceling connection to Wifi display: " + mConnectingDevice.deviceName); + unadvertiseDisplay(); mHandler.removeCallbacks(mConnectionTimeout); final WifiP2pDevice oldDevice = mConnectingDevice; @@ -475,6 +524,7 @@ final class WifiDisplayController implements DumpUtils.Dump { // Step 4. If we wanted to disconnect, then mission accomplished. if (mDesiredDevice == null) { + unadvertiseDisplay(); return; // done } @@ -485,14 +535,11 @@ final class WifiDisplayController implements DumpUtils.Dump { mConnectingDevice = mDesiredDevice; WifiP2pConfig config = new WifiP2pConfig(); config.deviceAddress = mConnectingDevice.deviceAddress; + // Helps with STA & P2P concurrency + config.groupOwnerIntent = WifiP2pConfig.MAX_GROUP_OWNER_INTENT; - final WifiDisplay display = createWifiDisplay(mConnectingDevice); - mHandler.post(new Runnable() { - @Override - public void run() { - mListener.onDisplayConnecting(display); - } - }); + WifiDisplay display = createWifiDisplay(mConnectingDevice); + advertiseDisplay(display, null, 0, 0, 0); final WifiP2pDevice newDevice = mDesiredDevice; mWifiP2pManager.connect(mWifiP2pChannel, config, new ActionListener() { @@ -541,8 +588,8 @@ final class WifiDisplayController implements DumpUtils.Dump { mRemoteDisplay = RemoteDisplay.listen(iface, new RemoteDisplay.Listener() { @Override - public void onDisplayConnected(final Surface surface, - final int width, final int height, final int flags) { + public void onDisplayConnected(Surface surface, + int width, int height, int flags) { if (mConnectedDevice == oldDevice && !mRemoteDisplayConnected) { Slog.i(TAG, "Opened RTSP connection with Wifi display: " + mConnectedDevice.deviceName); @@ -550,13 +597,7 @@ final class WifiDisplayController implements DumpUtils.Dump { mHandler.removeCallbacks(mRtspTimeout); final WifiDisplay display = createWifiDisplay(mConnectedDevice); - mHandler.post(new Runnable() { - @Override - public void run() { - mListener.onDisplayConnected(display, - surface, width, height, flags); - } - }); + advertiseDisplay(display, surface, width, height, flags); } } @@ -593,26 +634,13 @@ final class WifiDisplayController implements DumpUtils.Dump { } private void handleStateChanged(boolean enabled) { - if (mWifiP2pEnabled != enabled) { - mWifiP2pEnabled = enabled; - if (enabled) { - if (!mWfdEnabled) { - enableWfd(); - } - } else { - setWfdEnabled(false); - disconnect(); - } - } + mWifiP2pEnabled = enabled; + updateWfdEnableState(); } private void handlePeersChanged() { - if (mWifiP2pEnabled) { - if (mWfdEnabled) { - requestPeers(); - } else { - enableWfd(); - } + if (mWfdEnabled) { + requestPeers(); } } @@ -630,7 +658,8 @@ final class WifiDisplayController implements DumpUtils.Dump { if (mConnectingDevice != null && !info.contains(mConnectingDevice)) { Slog.i(TAG, "Aborting connection to Wifi display because " + "the current P2P group does not contain the device " - + "we expected to find: " + mConnectingDevice.deviceName); + + "we expected to find: " + mConnectingDevice.deviceName + + ", group info was: " + describeWifiP2pGroup(info)); handleConnectionFailure(false); return; } @@ -693,19 +722,18 @@ final class WifiDisplayController implements DumpUtils.Dump { private void handleConnectionFailure(boolean timeoutOccurred) { Slog.i(TAG, "Wifi display connection failed!"); - mHandler.post(new Runnable() { - @Override - public void run() { - mListener.onDisplayConnectionFailed(); - } - }); - if (mDesiredDevice != null) { if (mConnectionRetriesLeft > 0) { + final WifiP2pDevice oldDevice = mDesiredDevice; mHandler.postDelayed(new Runnable() { @Override public void run() { - retryConnection(); + if (mDesiredDevice == oldDevice && mConnectionRetriesLeft > 0) { + mConnectionRetriesLeft -= 1; + Slog.i(TAG, "Retrying Wifi display connection. Retries left: " + + mConnectionRetriesLeft); + retryConnection(); + } } }, timeoutOccurred ? 0 : CONNECT_RETRY_DELAY_MILLIS); } else { @@ -714,6 +742,48 @@ final class WifiDisplayController implements DumpUtils.Dump { } } + private void advertiseDisplay(final WifiDisplay display, + final Surface surface, final int width, final int height, final int flags) { + if (!Objects.equal(mAdvertisedDisplay, display) + || mAdvertisedDisplaySurface != surface + || mAdvertisedDisplayWidth != width + || mAdvertisedDisplayHeight != height + || mAdvertisedDisplayFlags != flags) { + final WifiDisplay oldDisplay = mAdvertisedDisplay; + final Surface oldSurface = mAdvertisedDisplaySurface; + + mAdvertisedDisplay = display; + mAdvertisedDisplaySurface = surface; + mAdvertisedDisplayWidth = width; + mAdvertisedDisplayHeight = height; + mAdvertisedDisplayFlags = flags; + + mHandler.post(new Runnable() { + @Override + public void run() { + if (oldSurface != null && surface != oldSurface) { + mListener.onDisplayDisconnected(); + } else if (oldDisplay != null && !Objects.equal(display, oldDisplay)) { + mListener.onDisplayConnectionFailed(); + } + + if (display != null) { + if (!Objects.equal(display, oldDisplay)) { + mListener.onDisplayConnecting(display); + } + if (surface != null && surface != oldSurface) { + mListener.onDisplayConnected(display, surface, width, height, flags); + } + } + } + }); + } + } + + private void unadvertiseDisplay() { + advertiseDisplay(null, null, 0, 0, 0); + } + private static Inet4Address getInterfaceAddress(WifiP2pGroup info) { NetworkInterface iface; try { @@ -766,7 +836,7 @@ final class WifiDisplayController implements DumpUtils.Dump { } private static WifiDisplay createWifiDisplay(WifiP2pDevice device) { - return new WifiDisplay(device.deviceAddress, device.deviceName); + return new WifiDisplay(device.deviceAddress, device.deviceName, null); } private final BroadcastReceiver mWifiP2pReceiver = new BroadcastReceiver() { @@ -774,6 +844,8 @@ final class WifiDisplayController implements DumpUtils.Dump { public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (action.equals(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION)) { + // This broadcast is sticky so we'll always get the initial Wifi P2P state + // on startup. boolean enabled = (intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, WifiP2pManager.WIFI_P2P_STATE_DISABLED)) == WifiP2pManager.WIFI_P2P_STATE_ENABLED; @@ -806,10 +878,10 @@ final class WifiDisplayController implements DumpUtils.Dump { * Called on the handler thread when displays are connected or disconnected. */ public interface Listener { - void onEnablementChanged(boolean enabled); + void onFeatureStateChanged(int featureState); void onScanStarted(); - void onScanFinished(WifiDisplay[] knownDisplays); + void onScanFinished(WifiDisplay[] availableDisplays); void onDisplayConnecting(WifiDisplay display); void onDisplayConnectionFailed(); diff --git a/services/java/com/android/server/net/LockdownVpnTracker.java b/services/java/com/android/server/net/LockdownVpnTracker.java index f2d6745..f32dd09 100644 --- a/services/java/com/android/server/net/LockdownVpnTracker.java +++ b/services/java/com/android/server/net/LockdownVpnTracker.java @@ -268,9 +268,11 @@ public class LockdownVpnTracker { } public NetworkInfo augmentNetworkInfo(NetworkInfo info) { - final NetworkInfo vpnInfo = mVpn.getNetworkInfo(); - info = new NetworkInfo(info); - info.setDetailedState(vpnInfo.getDetailedState(), vpnInfo.getReason(), null); + if (info.isConnected()) { + final NetworkInfo vpnInfo = mVpn.getNetworkInfo(); + info = new NetworkInfo(info); + info.setDetailedState(vpnInfo.getDetailedState(), vpnInfo.getReason(), null); + } return info; } diff --git a/services/java/com/android/server/net/NetworkStatsCollection.java b/services/java/com/android/server/net/NetworkStatsCollection.java index 60666b4..3169035 100644 --- a/services/java/com/android/server/net/NetworkStatsCollection.java +++ b/services/java/com/android/server/net/NetworkStatsCollection.java @@ -31,6 +31,7 @@ import android.net.TrafficStats; import android.text.format.DateUtils; import android.util.AtomicFile; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.FileRotator; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.Objects; @@ -431,13 +432,13 @@ public class NetworkStatsCollection implements FileRotator.Reader { * moving any {@link NetworkStats#TAG_NONE} series to * {@link TrafficStats#UID_REMOVED}. */ - public void removeUid(int uid) { + public void removeUids(int[] uids) { final ArrayList<Key> knownKeys = Lists.newArrayList(); knownKeys.addAll(mStats.keySet()); // migrate all UID stats into special "removed" bucket for (Key key : knownKeys) { - if (key.uid == uid) { + if (ArrayUtils.contains(uids, key.uid)) { // only migrate combined TAG_NONE history if (key.tag == TAG_NONE) { final NetworkStatsHistory uidHistory = mStats.get(key); diff --git a/services/java/com/android/server/net/NetworkStatsRecorder.java b/services/java/com/android/server/net/NetworkStatsRecorder.java index c3ecf54..2b32b41 100644 --- a/services/java/com/android/server/net/NetworkStatsRecorder.java +++ b/services/java/com/android/server/net/NetworkStatsRecorder.java @@ -42,6 +42,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.lang.ref.WeakReference; +import java.util.Arrays; import java.util.HashSet; import java.util.Map; @@ -233,23 +234,27 @@ public class NetworkStatsRecorder { * Remove the given UID from all {@link FileRotator} history, migrating it * to {@link TrafficStats#UID_REMOVED}. */ - public void removeUidLocked(int uid) { + public void removeUidsLocked(int[] uids) { try { - // process all existing data to migrate uid - mRotator.rewriteAll(new RemoveUidRewriter(mBucketDuration, uid)); + // Rewrite all persisted data to migrate UID stats + mRotator.rewriteAll(new RemoveUidRewriter(mBucketDuration, uids)); } catch (IOException e) { - Log.wtf(TAG, "problem removing UID " + uid, e); + Log.wtf(TAG, "problem removing UIDs " + Arrays.toString(uids), e); recoverFromWtf(); } - // clear UID from current stats snapshot + // Remove any pending stats + mPending.removeUids(uids); + mSinceBoot.removeUids(uids); + + // Clear UID from current stats snapshot if (mLastSnapshot != null) { - mLastSnapshot = mLastSnapshot.withoutUid(uid); + mLastSnapshot = mLastSnapshot.withoutUids(uids); } final NetworkStatsCollection complete = mComplete != null ? mComplete.get() : null; if (complete != null) { - complete.removeUid(uid); + complete.removeUids(uids); } } @@ -293,11 +298,11 @@ public class NetworkStatsRecorder { */ public static class RemoveUidRewriter implements FileRotator.Rewriter { private final NetworkStatsCollection mTemp; - private final int mUid; + private final int[] mUids; - public RemoveUidRewriter(long bucketDuration, int uid) { + public RemoveUidRewriter(long bucketDuration, int[] uids) { mTemp = new NetworkStatsCollection(bucketDuration); - mUid = uid; + mUids = uids; } @Override @@ -309,7 +314,7 @@ public class NetworkStatsRecorder { public void read(InputStream in) throws IOException { mTemp.read(in); mTemp.clearDirty(); - mTemp.removeUid(mUid); + mTemp.removeUids(mUids); } @Override diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java index 3a593e4..f2748a3 100644 --- a/services/java/com/android/server/net/NetworkStatsService.java +++ b/services/java/com/android/server/net/NetworkStatsService.java @@ -23,6 +23,7 @@ import static android.Manifest.permission.MODIFY_NETWORK_ACCOUNTING; import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY; import static android.content.Intent.ACTION_SHUTDOWN; import static android.content.Intent.ACTION_UID_REMOVED; +import static android.content.Intent.ACTION_USER_REMOVED; import static android.content.Intent.EXTRA_UID; import static android.net.ConnectivityManager.ACTION_TETHER_STATE_CHANGED; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION_IMMEDIATE; @@ -76,6 +77,8 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.net.IConnectivityManager; import android.net.INetworkManagementEventObserver; import android.net.INetworkStatsService; @@ -112,6 +115,7 @@ import android.util.Slog; import android.util.SparseIntArray; import android.util.TrustedTime; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.FileRotator; import com.android.internal.util.IndentingPrintWriter; import com.android.server.EventLogTags; @@ -122,8 +126,10 @@ import java.io.File; import java.io.FileDescriptor; import java.io.IOException; import java.io.PrintWriter; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.List; /** * Collect and persist detailed network statistics, and provide this data to @@ -322,6 +328,10 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final IntentFilter removedFilter = new IntentFilter(ACTION_UID_REMOVED); mContext.registerReceiver(mRemovedReceiver, removedFilter, null, mHandler); + // listen for user changes to clean stats + final IntentFilter userFilter = new IntentFilter(ACTION_USER_REMOVED); + mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler); + // persist stats during clean shutdown final IntentFilter shutdownFilter = new IntentFilter(ACTION_SHUTDOWN); mContext.registerReceiver(mShutdownReceiver, shutdownFilter); @@ -739,11 +749,34 @@ public class NetworkStatsService extends INetworkStatsService.Stub { public void onReceive(Context context, Intent intent) { // on background handler thread, and UID_REMOVED is protected // broadcast. - final int uid = intent.getIntExtra(EXTRA_UID, 0); + + final int uid = intent.getIntExtra(EXTRA_UID, -1); + if (uid == -1) return; + + synchronized (mStatsLock) { + mWakeLock.acquire(); + try { + removeUidsLocked(uid); + } finally { + mWakeLock.release(); + } + } + } + }; + + private BroadcastReceiver mUserReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + // On background handler thread, and USER_REMOVED is protected + // broadcast. + + final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); + if (userId == -1) return; + synchronized (mStatsLock) { mWakeLock.acquire(); try { - removeUidLocked(uid); + removeUserLocked(userId); } finally { mWakeLock.release(); } @@ -1034,15 +1067,37 @@ public class NetworkStatsService extends INetworkStatsService.Stub { /** * Clean up {@link #mUidRecorder} after UID is removed. */ - private void removeUidLocked(int uid) { - // perform one last poll before removing + private void removeUidsLocked(int... uids) { + if (LOGV) Slog.v(TAG, "removeUidsLocked() for UIDs " + Arrays.toString(uids)); + + // Perform one last poll before removing performPollLocked(FLAG_PERSIST_ALL); - mUidRecorder.removeUidLocked(uid); - mUidTagRecorder.removeUidLocked(uid); + mUidRecorder.removeUidsLocked(uids); + mUidTagRecorder.removeUidsLocked(uids); + + // Clear kernel stats associated with UID + for (int uid : uids) { + resetKernelUidStats(uid); + } + } + + /** + * Clean up {@link #mUidRecorder} after user is removed. + */ + private void removeUserLocked(int userId) { + if (LOGV) Slog.v(TAG, "removeUserLocked() for userId=" + userId); + + // Build list of UIDs that we should clean up + int[] uids = new int[0]; + final List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications( + PackageManager.GET_UNINSTALLED_PACKAGES | PackageManager.GET_DISABLED_COMPONENTS); + for (ApplicationInfo app : apps) { + final int uid = UserHandle.getUid(userId, app.uid); + uids = ArrayUtils.appendInt(uids, uid); + } - // clear kernel stats associated with UID - resetKernelUidStats(uid); + removeUidsLocked(uids); } @Override diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index 536c612..f0cc083 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -8044,13 +8044,13 @@ public class PackageManagerService extends IPackageManager.Stub { IDevicePolicyManager dpm = IDevicePolicyManager.Stub.asInterface( ServiceManager.getService(Context.DEVICE_POLICY_SERVICE)); try { - if (dpm != null && dpm.packageHasActiveAdmins(packageName)) { + if (dpm != null && dpm.packageHasActiveAdmins(packageName, UserHandle.getUserId(uid))) { Slog.w(TAG, "Not removing package " + packageName + ": has active device admin"); return PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER; } } catch (RemoteException e) { } - + synchronized (mInstallLock) { res = deletePackageLI(packageName, (flags & PackageManager.DELETE_ALL_USERS) != 0 diff --git a/services/java/com/android/server/pm/UserManagerService.java b/services/java/com/android/server/pm/UserManagerService.java index 690d7c9..2dc9a6a 100644 --- a/services/java/com/android/server/pm/UserManagerService.java +++ b/services/java/com/android/server/pm/UserManagerService.java @@ -29,6 +29,8 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.UserInfo; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.os.Binder; import android.os.Environment; import android.os.FileUtils; @@ -37,6 +39,7 @@ import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; +import android.os.UserManager; import android.util.AtomicFile; import android.util.Slog; import android.util.SparseArray; @@ -83,8 +86,6 @@ public class UserManagerService extends IUserManager.Stub { private SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>(); - private final int mUserLimit; - private int[] mUserIds; private boolean mGuestEnabled; private int mNextSerialNumber; @@ -127,8 +128,6 @@ public class UserManagerService extends IUserManager.Stub { mPm = pm; mInstallLock = installLock; mPackagesLock = packagesLock; - mUserLimit = mContext.getResources().getInteger( - com.android.internal.R.integer.config_multiuserMaximumUsers); mUsersDir = new File(dataDir, USER_INFO_DIR); mUsersDir.mkdirs(); // Make zeroth user directory, for services to migrate their files to that location @@ -188,30 +187,35 @@ public class UserManagerService extends IUserManager.Stub { writeUserLocked(info); } } + sendUserInfoChangedBroadcast(userId); } @Override - public ParcelFileDescriptor setUserIcon(int userId) { + public void setUserIcon(int userId, Bitmap bitmap) { checkManageUsersPermission("update users"); synchronized (mPackagesLock) { UserInfo info = mUsers.get(userId); - if (info == null) return null; - ParcelFileDescriptor fd = openIconBitmapLocked(info, true /* write */); - if (fd != null) { - writeUserLocked(info); - } - return fd; + if (info == null) return; + writeBitmapLocked(info, bitmap); + writeUserLocked(info); } + sendUserInfoChangedBroadcast(userId); + } + + private void sendUserInfoChangedBroadcast(int userId) { + Intent changedIntent = new Intent(Intent.ACTION_USER_INFO_CHANGED); + changedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId); + changedIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + mContext.sendBroadcastAsUser(changedIntent, new UserHandle(userId)); } @Override - public ParcelFileDescriptor getUserIcon(int userId) { + public Bitmap getUserIcon(int userId) { checkManageUsersPermission("read users"); synchronized (mPackagesLock) { UserInfo info = mUsers.get(userId); if (info == null || info.iconPath == null) return null; - ParcelFileDescriptor fd = openIconBitmapLocked(info, false /* read */); - return fd; + return BitmapFactory.decodeFile(info.iconPath); } } @@ -268,7 +272,7 @@ public class UserManagerService extends IUserManager.Stub { */ private boolean isUserLimitReachedLocked() { int nUsers = mUsers.size(); - return nUsers >= mUserLimit; + return nUsers >= UserManager.getMaxSupportedUsers(); } /** @@ -289,7 +293,7 @@ public class UserManagerService extends IUserManager.Stub { } } - private ParcelFileDescriptor openIconBitmapLocked(UserInfo info, boolean toWrite) { + private void writeBitmapLocked(UserInfo info, Bitmap bitmap) { try { File dir = new File(mUsersDir, Integer.toString(info.id)); File file = new File(dir, USER_PHOTO_FILENAME); @@ -300,16 +304,18 @@ public class UserManagerService extends IUserManager.Stub { FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH, -1, -1); } - ParcelFileDescriptor fd = ParcelFileDescriptor.open(file, - toWrite ? MODE_CREATE|MODE_READ_WRITE : MODE_READ_WRITE); - if (toWrite) { + FileOutputStream os; + if (bitmap.compress(Bitmap.CompressFormat.PNG, 100, os = new FileOutputStream(file))) { info.iconPath = file.getAbsolutePath(); } - return fd; + try { + os.close(); + } catch (IOException ioe) { + // What the ... ! + } } catch (FileNotFoundException e) { Slog.w(LOG_TAG, "Error setting photo for user ", e); } - return null; } /** diff --git a/services/java/com/android/server/power/PowerManagerService.java b/services/java/com/android/server/power/PowerManagerService.java index fda619c..7052ed5 100644 --- a/services/java/com/android/server/power/PowerManagerService.java +++ b/services/java/com/android/server/power/PowerManagerService.java @@ -50,6 +50,7 @@ import android.os.ServiceManager; import android.os.SystemClock; import android.os.WorkSource; import android.provider.Settings; +import android.service.dreams.Dream; import android.service.dreams.IDreamManager; import android.util.EventLog; import android.util.Log; @@ -98,6 +99,8 @@ public final class PowerManagerService extends IPowerManager.Stub private static final int DIRTY_STAY_ON = 1 << 7; // Dirty bit: battery state changed private static final int DIRTY_BATTERY_STATE = 1 << 8; + // Dirty bit: dream ended + private static final int DIRTY_DREAM_ENDED = 1 << 9; // Wakefulness: The device is asleep and can only be awoken by a call to wakeUp(). // The screen should be off or in the process of being turned off by the display controller. @@ -364,6 +367,10 @@ public final class PowerManagerService extends IPowerManager.Stub filter.addAction(Intent.ACTION_DOCK_EVENT); mContext.registerReceiver(new DockReceiver(), filter); + filter = new IntentFilter(); + filter.addAction(Dream.ACTION_DREAMING_STOPPED); + mContext.registerReceiver(new DreamReceiver(), filter); + // Register for settings changes. final ContentResolver resolver = mContext.getContentResolver(); resolver.registerContentObserver(Settings.Secure.getUriFor( @@ -1146,8 +1153,12 @@ public final class PowerManagerService extends IPowerManager.Stub * Determines whether to post a message to the sandman to update the dream state. */ private void updateDreamLocked(int dirty) { - if ((dirty & (DIRTY_WAKEFULNESS | DIRTY_SETTINGS - | DIRTY_IS_POWERED | DIRTY_STAY_ON | DIRTY_BATTERY_STATE)) != 0) { + if ((dirty & (DIRTY_WAKEFULNESS + | DIRTY_SETTINGS + | DIRTY_IS_POWERED + | DIRTY_STAY_ON + | DIRTY_BATTERY_STATE + | DIRTY_DREAM_ENDED)) != 0) { scheduleSandmanLocked(); } } @@ -1230,15 +1241,15 @@ public final class PowerManagerService extends IPowerManager.Stub handleDreamFinishedLocked(); } - // Allow the sandman to detect when the dream has ended. - // FIXME: The DreamManagerService should tell us explicitly. + // In addition to listening for the intent, poll the sandman periodically to detect + // when the dream has ended (as a watchdog only, ensuring our state is always correct). if (mWakefulness == WAKEFULNESS_DREAMING || mWakefulness == WAKEFULNESS_NAPPING) { if (!mSandmanScheduled) { mSandmanScheduled = true; Message msg = mHandler.obtainMessage(MSG_SANDMAN); msg.setAsynchronous(true); - mHandler.sendMessageDelayed(msg, 1000); + mHandler.sendMessageDelayed(msg, 5000); } } } @@ -1472,6 +1483,11 @@ public final class PowerManagerService extends IPowerManager.Stub // TODO } + private void handleDreamEndedLocked() { + mDirty |= DIRTY_DREAM_ENDED; + updatePowerStateLocked(); + } + /** * Reboot the device immediately, passing 'reason' (may be null) * to the underlying __reboot system call. Should not return. @@ -1937,6 +1953,15 @@ public final class PowerManagerService extends IPowerManager.Stub } } + private final class DreamReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + synchronized (mLock) { + handleDreamEndedLocked(); + } + } + } + private final class SettingsObserver extends ContentObserver { public SettingsObserver(Handler handler) { super(handler); diff --git a/services/java/com/android/server/updatable/CertPinInstallReceiver.java b/services/java/com/android/server/updates/CertPinInstallReceiver.java index c03fbc3..c03fbc3 100644 --- a/services/java/com/android/server/updatable/CertPinInstallReceiver.java +++ b/services/java/com/android/server/updates/CertPinInstallReceiver.java diff --git a/services/java/com/android/server/updatable/ConfigUpdateInstallReceiver.java b/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java index a74a648..5da612a 100644 --- a/services/java/com/android/server/updatable/ConfigUpdateInstallReceiver.java +++ b/services/java/com/android/server/updates/ConfigUpdateInstallReceiver.java @@ -89,8 +89,7 @@ public class ConfigUpdateInstallReceiver extends BroadcastReceiver { // get the hash of the currently used value String currentHash = getCurrentHash(getCurrentContent()); if (!verifyVersion(currentVersion, altVersion)) { - EventLog.writeEvent(EventLogTags.CONFIG_INSTALL_FAILED, - "New version is not greater than current version"); + Slog.i(TAG, "Not installing, new version is <= current version"); } else if (!verifyPreviousHash(currentHash, altRequiredHash)) { EventLog.writeEvent(EventLogTags.CONFIG_INSTALL_FAILED, "Current hash did not match required value"); diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java index 3ef6d4c..392d5e7 100644 --- a/services/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/java/com/android/server/usb/UsbDeviceManager.java @@ -91,6 +91,7 @@ public class UsbDeviceManager { private static final int MSG_SET_CURRENT_FUNCTIONS = 2; private static final int MSG_SYSTEM_READY = 3; private static final int MSG_BOOT_COMPLETED = 4; + private static final int MSG_USER_SWITCHED = 5; private static final int AUDIO_MODE_NONE = 0; private static final int AUDIO_MODE_SOURCE = 1; @@ -295,14 +296,24 @@ public class UsbDeviceManager { private UsbAccessory mCurrentAccessory; private int mUsbNotificationId; private boolean mAdbNotificationShown; + private int mCurrentUser = UserHandle.USER_NULL; private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() { + @Override public void onReceive(Context context, Intent intent) { if (DEBUG) Slog.d(TAG, "boot completed"); mHandler.sendEmptyMessage(MSG_BOOT_COMPLETED); } }; + private final BroadcastReceiver mUserSwitchedReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); + mHandler.obtainMessage(MSG_USER_SWITCHED, userId, 0).sendToTarget(); + } + }; + public UsbHandler(Looper looper) { super(looper); try { @@ -347,8 +358,10 @@ public class UsbDeviceManager { mUEventObserver.startObserving(USB_STATE_MATCH); mUEventObserver.startObserving(ACCESSORY_START_MATCH); - mContext.registerReceiver(mBootCompletedReceiver, - new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); + mContext.registerReceiver( + mBootCompletedReceiver, new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); + mContext.registerReceiver( + mUserSwitchedReceiver, new IntentFilter(Intent.ACTION_USER_SWITCHED)); } catch (Exception e) { Slog.e(TAG, "Error initializing UsbHandler", e); } @@ -611,6 +624,18 @@ public class UsbDeviceManager { mDebuggingManager.setAdbEnabled(mAdbEnabled); } break; + case MSG_USER_SWITCHED: { + final boolean mtpActive = + containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MTP) + || containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_PTP); + if (mtpActive && mCurrentUser != UserHandle.USER_NULL) { + Slog.v(TAG, "Current user switched; resetting USB host stack for MTP"); + setUsbConfig("none"); + setUsbConfig(mCurrentFunctions); + } + mCurrentUser = msg.arg1; + break; + } } } diff --git a/services/java/com/android/server/wm/KeyguardDisableHandler.java b/services/java/com/android/server/wm/KeyguardDisableHandler.java index d935b8b..859df51 100644 --- a/services/java/com/android/server/wm/KeyguardDisableHandler.java +++ b/services/java/com/android/server/wm/KeyguardDisableHandler.java @@ -16,12 +16,15 @@ package com.android.server.wm; +import android.app.ActivityManagerNative; import android.app.admin.DevicePolicyManager; import android.content.Context; import android.os.Handler; import android.os.IBinder; import android.os.Message; +import android.os.RemoteException; import android.os.TokenWatcher; +import android.os.UserHandle; import android.util.Log; import android.util.Pair; import android.view.WindowManagerPolicy; @@ -87,9 +90,14 @@ public class KeyguardDisableHandler extends Handler { DevicePolicyManager dpm = (DevicePolicyManager) mContext.getSystemService( Context.DEVICE_POLICY_SERVICE); if (dpm != null) { - mAllowDisableKeyguard = dpm.getPasswordQuality(null) - == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED ? - ALLOW_DISABLE_YES : ALLOW_DISABLE_NO; + try { + mAllowDisableKeyguard = dpm.getPasswordQuality(null, + ActivityManagerNative.getDefault().getCurrentUser().id) + == DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED ? + ALLOW_DISABLE_YES : ALLOW_DISABLE_NO; + } catch (RemoteException re) { + // Nothing much we can do + } } } if (mAllowDisableKeyguard == ALLOW_DISABLE_YES) { diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 55a7c46..73cc7ed 100755 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -292,9 +292,6 @@ public class WindowManagerService extends IWindowManager.Stub if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action)) { mKeyguardDisableHandler.sendEmptyMessage( KeyguardDisableHandler.KEYGUARD_POLICY_CHANGED); - } else if (Intent.ACTION_USER_SWITCHED.equals(action)) { - final int newUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0); - mCurrentUserId = newUserId; } } }; @@ -811,8 +808,6 @@ public class WindowManagerService extends IWindowManager.Stub // Track changes to DevicePolicyManager state so we can enable/disable keyguard. IntentFilter filter = new IntentFilter(); filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED); - // Track user switching. - filter.addAction(Intent.ACTION_USER_SWITCHED); mContext.registerReceiver(mBroadcastReceiver, filter); mHoldingScreenWakeLock = pmc.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK @@ -1648,7 +1643,8 @@ public class WindowManagerService extends IWindowManager.Stub } } - if (mWallpaperTarget != foundW) { + if (mWallpaperTarget != foundW + && (mLowerWallpaperTarget == null || mLowerWallpaperTarget != foundW)) { if (DEBUG_WALLPAPER) { Slog.v(TAG, "New wallpaper target: " + foundW + " oldTarget: " + mWallpaperTarget); @@ -3391,7 +3387,7 @@ public class WindowManagerService extends IWindowManager.Stub // Exiting app if (scaleUp) { // noop animation - a = new AlphaAnimation(1, 1); + a = new AlphaAnimation(1, 0); a.setDuration(duration); } else { float scaleW = thumbWidth / displayInfo.appWidth; @@ -3440,7 +3436,7 @@ public class WindowManagerService extends IWindowManager.Stub "applyAnimation: atoken=" + atoken + " anim=" + a + " nextAppTransition=ANIM_CUSTOM" + " transit=" + transit + " isEntrance=" + enter - + " Callers " + Debug.getCallers(3)); + + " Callers=" + Debug.getCallers(3)); } else if (mNextAppTransitionType == ActivityOptions.ANIM_SCALE_UP) { a = createScaleUpAnimationLocked(transit, enter); initialized = true; @@ -3448,7 +3444,7 @@ public class WindowManagerService extends IWindowManager.Stub "applyAnimation: atoken=" + atoken + " anim=" + a + " nextAppTransition=ANIM_SCALE_UP" + " transit=" + transit + " isEntrance=" + enter - + " Callers " + Debug.getCallers(3)); + + " Callers=" + Debug.getCallers(3)); } else if (mNextAppTransitionType == ActivityOptions.ANIM_THUMBNAIL_SCALE_UP || mNextAppTransitionType == ActivityOptions.ANIM_THUMBNAIL_SCALE_DOWN) { boolean scaleUp = (mNextAppTransitionType == ActivityOptions.ANIM_THUMBNAIL_SCALE_UP); @@ -3459,7 +3455,7 @@ public class WindowManagerService extends IWindowManager.Stub Slog.v(TAG, "applyAnimation: atoken=" + atoken + " anim=" + a + " nextAppTransition=" + animName + " transit=" + transit + " isEntrance=" + enter - + " Callers " + Debug.getCallers(3)); + + " Callers=" + Debug.getCallers(3)); } } else { int animAttr = 0; @@ -3521,7 +3517,7 @@ public class WindowManagerService extends IWindowManager.Stub + " anim=" + a + " animAttr=0x" + Integer.toHexString(animAttr) + " transit=" + transit + " isEntrance=" + enter - + " Callers " + Debug.getCallers(3)); + + " Callers=" + Debug.getCallers(3)); } if (a != null) { if (DEBUG_ANIM) { @@ -4193,7 +4189,7 @@ public class WindowManagerService extends IWindowManager.Stub synchronized(mWindowMap) { if (DEBUG_STARTING_WINDOW) Slog.v( - TAG, "setAppStartingIcon: token=" + token + " pkg=" + pkg + TAG, "setAppStartingWindow: token=" + token + " pkg=" + pkg + " transferFrom=" + transferFrom); AppWindowToken wtoken = findAppWindowToken(token); @@ -4225,7 +4221,7 @@ public class WindowManagerService extends IWindowManager.Stub mSkipAppTransitionAnimation = true; } if (DEBUG_STARTING_WINDOW) Slog.v(TAG, - "Moving existing starting from " + ttoken + "Moving existing starting " + startingWindow + " from " + ttoken + " to " + wtoken); final long origId = Binder.clearCallingIdentity(); @@ -4234,6 +4230,7 @@ public class WindowManagerService extends IWindowManager.Stub wtoken.startingData = ttoken.startingData; wtoken.startingView = ttoken.startingView; wtoken.startingDisplayed = ttoken.startingDisplayed; + ttoken.startingDisplayed = false; wtoken.startingWindow = startingWindow; wtoken.reportedVisible = ttoken.reportedVisible; ttoken.startingData = null; @@ -4243,6 +4240,8 @@ public class WindowManagerService extends IWindowManager.Stub startingWindow.mToken = wtoken; startingWindow.mRootToken = wtoken; startingWindow.mAppToken = wtoken; + startingWindow.mWinAnimator.mAppAnimator = wtoken.mAppAnimator; + if (DEBUG_WINDOW_MOVEMENT || DEBUG_ADD_REMOVE || DEBUG_STARTING_WINDOW) { Slog.v(TAG, "Removing starting window: " + startingWindow); } @@ -4550,9 +4549,9 @@ public class WindowManagerService extends IWindowManager.Stub } wtoken.hiddenRequested = !visible; - if (DEBUG_APP_TRANSITIONS) Slog.v( - TAG, "Setting dummy animation on: " + wtoken); if (!wtoken.startingDisplayed) { + if (DEBUG_APP_TRANSITIONS) Slog.v( + TAG, "Setting dummy animation on: " + wtoken); wtoken.mAppAnimator.setDummyAnimation(); } mOpeningApps.remove(wtoken); @@ -5396,6 +5395,13 @@ public class WindowManagerService extends IWindowManager.Stub mInputManager.setInputFilter(filter); } + public void setCurrentUser(final int newUserId) { + synchronized (mWindowMap) { + mCurrentUserId = newUserId; + mPolicy.setCurrentUserLw(newUserId); + } + } + public void enableScreenAfterBoot() { synchronized(mWindowMap) { if (DEBUG_BOOT) { @@ -8147,7 +8153,11 @@ public class WindowManagerService extends IWindowManager.Stub updateLayoutToAnimationLocked(); } if (DEBUG_LAYERS) Slog.v(TAG, "Assign layer " + w + ": " - + winAnimator.mAnimLayer); + + "mBase=" + w.mBaseLayer + + " mLayer=" + w.mLayer + + (w.mAppToken == null ? + "" : " mAppLayer=" + w.mAppToken.mAppAnimator.animLayerAdjustment) + + " =mAnimLayer=" + winAnimator.mAnimLayer); //System.out.println( // "Assigned layer " + curLayer + " to " + w.mClient.asBinder()); } @@ -8539,7 +8549,7 @@ public class WindowManagerService extends IWindowManager.Stub transit = WindowManagerPolicy.TRANSIT_WALLPAPER_CLOSE; if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit away from wallpaper: " + transit); - } else if (mWallpaperTarget != null) { + } else if (mWallpaperTarget != null && mWallpaperTarget.isVisibleLw()) { // We are transitioning from an activity without // a wallpaper to now showing the wallpaper transit = WindowManagerPolicy.TRANSIT_WALLPAPER_OPEN; @@ -8596,7 +8606,7 @@ public class WindowManagerService extends IWindowManager.Stub for (i=0; i<NN; i++) { AppWindowToken wtoken = mClosingApps.get(i); if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, - "Now closing app" + wtoken); + "Now closing app " + wtoken); wtoken.mAppAnimator.clearThumbnail(); wtoken.inPendingTransaction = false; wtoken.mAppAnimator.animation = null; diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java index b62028e..ac958b8 100644 --- a/services/java/com/android/server/wm/WindowState.java +++ b/services/java/com/android/server/wm/WindowState.java @@ -518,11 +518,11 @@ final class WindowState implements WindowManagerPolicy.WindowState { MagnificationSpec spec = mDisplayContent.mMagnificationSpec; if (spec != null && !spec.isNop()) { if (mAttachedWindow != null) { - if (!mPolicy.canMagnifyWindow(mAttachedWindow.mAttrs)) { + if (!mPolicy.canMagnifyWindowLw(mAttachedWindow.mAttrs)) { return null; } } - if (!mPolicy.canMagnifyWindow(mAttrs)) { + if (!mPolicy.canMagnifyWindowLw(mAttrs)) { return null; } } diff --git a/services/java/com/android/server/wm/WindowStateAnimator.java b/services/java/com/android/server/wm/WindowStateAnimator.java index 000a191..5f40709 100644 --- a/services/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/java/com/android/server/wm/WindowStateAnimator.java @@ -62,7 +62,7 @@ class WindowStateAnimator { final WindowState mWin; final WindowStateAnimator mAttachedWinAnimator; final WindowAnimator mAnimator; - final AppWindowAnimator mAppAnimator; + AppWindowAnimator mAppAnimator; final Session mSession; final WindowManagerPolicy mPolicy; final Context mContext; @@ -1520,7 +1520,7 @@ class WindowStateAnimator { "applyAnimation: win=" + this + " anim=" + anim + " attr=0x" + Integer.toHexString(attr) + " a=" + a - + " mAnimation=" + mAnimation + + " transit=" + transit + " isEntrance=" + isEntrance + " Callers " + Debug.getCallers(3)); if (a != null) { if (WindowManagerService.DEBUG_ANIM) { |
