From e33f61319049810ae9cb318e2ba45e8a3449fb43 Mon Sep 17 00:00:00 2001 From: Svetoslav Ganov Date: Wed, 1 Jun 2016 16:25:31 -0700 Subject: [PATCH] Fix permissions update for VrListenerService on user changes. - Fixes a bug where we would not update the overlay restrictions if the VR listener we are bound to changes bug the VR enabled state doesn't. - Also fixed a case where the notification listener access and location permission were not granted/revoked for the correct user. For example, if a vr activity in one user calls a VR activity in another (possible for cross profiles) we end up not revoking the special access for the vr service in the first user. The notification listener setting was also updated for the system user instead of the user for which we grant/revoke specal access. - Properly remove the overlay restrictions for the old user if we transition from a vr actiivty in one user to a vr activity in a profile of this user. Bug: 29273985 Change-Id: Ib1de6f2f5445001ac61edca5c77ea3a066544307 --- core/java/android/app/AppOpsManager.java | 9 +- .../android/server/vr/VrManagerService.java | 154 ++++++++++++------ 2 files changed, 114 insertions(+), 49 deletions(-) diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index e526c17dde9d1..1e4ffbeca33d7 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1340,9 +1340,14 @@ public class AppOpsManager { /** @hide */ public void setUserRestriction(int code, boolean restricted, IBinder token, String[] exceptionPackages) { + setUserRestrictionForUser(code, restricted, token, exceptionPackages, mContext.getUserId()); + } + + /** @hide */ + public void setUserRestrictionForUser(int code, boolean restricted, IBinder token, + String[] exceptionPackages, int userId) { try { - mService.setUserRestriction(code, restricted, token, mContext.getUserId(), - exceptionPackages); + mService.setUserRestriction(code, restricted, token, userId, exceptionPackages); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java index 3700c71e1da74..b4c4bd8daa76c 100644 --- a/services/core/java/com/android/server/vr/VrManagerService.java +++ b/services/core/java/com/android/server/vr/VrManagerService.java @@ -46,11 +46,12 @@ import android.service.vr.IVrListener; import android.service.vr.IVrManager; import android.service.vr.IVrStateCallbacks; import android.service.vr.VrListenerService; +import android.util.ArrayMap; import android.util.ArraySet; import android.util.Slog; import android.util.SparseArray; - import com.android.internal.R; +import com.android.server.LocalServices; import com.android.server.SystemConfig; import com.android.server.SystemService; import com.android.server.utils.ManagedApplicationService.PendingEvent; @@ -67,6 +68,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.List; +import java.util.Map; import java.util.Objects; /** @@ -197,29 +199,44 @@ public class VrManagerService extends SystemService implements EnabledComponentC private final class NotificationAccessManager { private final SparseArray> mAllowedPackages = new SparseArray<>(); + private final ArrayMap mNotificationAccessPackageToUserId = + new ArrayMap<>(); public void update(Collection packageNames) { int currentUserId = ActivityManager.getCurrentUser(); - UserHandle currentUserHandle = new UserHandle(currentUserId); - ArraySet allowed = mAllowedPackages.get(currentUserId); if (allowed == null) { allowed = new ArraySet<>(); } + // Make sure we revoke notification access for listeners in other users + final int listenerCount = mNotificationAccessPackageToUserId.size(); + for (int i = listenerCount - 1; i >= 0; i--) { + final int grantUserId = mNotificationAccessPackageToUserId.valueAt(i); + if (grantUserId != currentUserId) { + String packageName = mNotificationAccessPackageToUserId.keyAt(i); + revokeNotificationListenerAccess(packageName, grantUserId); + revokeNotificationPolicyAccess(packageName); + revokeCoarseLocationPermissionIfNeeded(packageName, grantUserId); + mNotificationAccessPackageToUserId.removeAt(i); + } + } + for (String pkg : allowed) { if (!packageNames.contains(pkg)) { - revokeNotificationListenerAccess(pkg); + revokeNotificationListenerAccess(pkg, currentUserId); revokeNotificationPolicyAccess(pkg); - revokeCoarseLocationAccess(pkg, currentUserHandle); + revokeCoarseLocationPermissionIfNeeded(pkg, currentUserId); + mNotificationAccessPackageToUserId.remove(pkg); } } for (String pkg : packageNames) { if (!allowed.contains(pkg)) { grantNotificationPolicyAccess(pkg); - grantNotificationListenerAccess(pkg, currentUserHandle); - grantCoarseLocationAccess(pkg, currentUserHandle); + grantNotificationListenerAccess(pkg, currentUserId); + grantCoarseLocationPermissionIfNeeded(pkg, currentUserId); + mNotificationAccessPackageToUserId.put(pkg, currentUserId); } } @@ -229,7 +246,6 @@ public class VrManagerService extends SystemService implements EnabledComponentC } } - /** * Called when a user, package, or setting changes that could affect whether or not the * currently bound VrListenerService is changed. @@ -535,17 +551,33 @@ public class VrManagerService extends SystemService implements EnabledComponentC } } - private void updateOverlayStateLocked(ComponentName exemptedComponent) { + private void updateOverlayStateLocked(String exemptedPackage, int newUserId, int oldUserId) { + AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class); + + // If user changed drop restrictions for the old user. + if (oldUserId != newUserId) { + appOpsManager.setUserRestrictionForUser(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, + false, mOverlayToken, null, oldUserId); + } + + // Apply the restrictions for the current user based on vr state + String[] exemptions = (exemptedPackage == null) ? new String[0] : + new String[] { exemptedPackage }; + + appOpsManager.setUserRestrictionForUser(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, + mVrModeEnabled, mOverlayToken, exemptions, newUserId); + } + + private void updateDependentAppOpsLocked(String newVrServicePackage, int newUserId, + String oldVrServicePackage, int oldUserId) { + // If VR state changed and we also have a VR service change. + if (Objects.equals(newVrServicePackage, oldVrServicePackage)) { + return; + } final long identity = Binder.clearCallingIdentity(); try { - AppOpsManager appOpsManager = getContext().getSystemService(AppOpsManager.class); - if (appOpsManager != null) { - String[] exemptions = (exemptedComponent == null) ? new String[0] : - new String[] { exemptedComponent.getPackageName() }; - - appOpsManager.setUserRestriction(AppOpsManager.OP_SYSTEM_ALERT_WINDOW, - mVrModeEnabled, mOverlayToken, exemptions); - } + // Set overlay exception state based on VR enabled and current service + updateOverlayStateLocked(newVrServicePackage, newUserId, oldUserId); } finally { Binder.restoreCallingIdentity(identity); } @@ -578,8 +610,12 @@ public class VrManagerService extends SystemService implements EnabledComponentC return validUserComponent; // Disabled -> Disabled transition does nothing. } + String oldVrServicePackage = mCurrentVrService != null + ? mCurrentVrService.getComponent().getPackageName() : null; + final int oldUserId = mCurrentVrModeUser; + // Always send mode change events. - changeVrModeLocked(enabled, (enabled && validUserComponent) ? component : null); + changeVrModeLocked(enabled); if (!enabled || !validUserComponent) { // Unbind whatever is running @@ -606,12 +642,25 @@ public class VrManagerService extends SystemService implements EnabledComponentC } } - if (calling != null && !Objects.equals(calling, mCurrentVrModeComponent)) { + if (calling != null && !Objects.equals(calling, mCurrentVrModeComponent)) { mCurrentVrModeComponent = calling; + sendUpdatedCaller = true; + } + + if (mCurrentVrModeUser != userId) { mCurrentVrModeUser = userId; sendUpdatedCaller = true; } + String newVrServicePackage = mCurrentVrService != null + ? mCurrentVrService.getComponent().getPackageName() : null; + final int newUserId = mCurrentVrModeUser; + + // Update AppOps settings that change state when entering/exiting VR mode, or changing + // the current VrListenerService. + updateDependentAppOpsLocked(newVrServicePackage, newUserId, + oldVrServicePackage, oldUserId); + if (mCurrentVrService != null && sendUpdatedCaller) { final ComponentName c = mCurrentVrModeComponent; mCurrentVrService.sendEvent(new PendingEvent() { @@ -645,18 +694,6 @@ public class VrManagerService extends SystemService implements EnabledComponentC return true; } - private void grantCoarseLocationAccess(String pkg, UserHandle userId) { - PackageManager pm = mContext.getPackageManager(); - pm.grantRuntimePermission(pkg, android.Manifest.permission.ACCESS_COARSE_LOCATION, - userId); - } - - private void revokeCoarseLocationAccess(String pkg, UserHandle userId) { - PackageManager pm = mContext.getPackageManager(); - pm.revokeRuntimePermission(pkg, - android.Manifest.permission.ACCESS_COARSE_LOCATION, userId); - } - private void grantNotificationPolicyAccess(String pkg) { NotificationManager nm = mContext.getSystemService(NotificationManager.class); nm.setNotificationPolicyAccessGranted(pkg, true); @@ -670,14 +707,14 @@ public class VrManagerService extends SystemService implements EnabledComponentC nm.setNotificationPolicyAccessGranted(pkg, false); } - private void grantNotificationListenerAccess(String pkg, UserHandle userId) { + private void grantNotificationListenerAccess(String pkg, int userId) { PackageManager pm = mContext.getPackageManager(); ArraySet possibleServices = EnabledComponentsObserver.loadComponentNames(pm, - userId.getIdentifier(), NotificationListenerService.SERVICE_INTERFACE, + userId, NotificationListenerService.SERVICE_INTERFACE, android.Manifest.permission.BIND_NOTIFICATION_LISTENER_SERVICE); ContentResolver resolver = mContext.getContentResolver(); - ArraySet current = getCurrentNotifListeners(resolver); + ArraySet current = getNotificationListeners(resolver, userId); for (ComponentName c : possibleServices) { String flatName = c.flattenToString(); @@ -689,14 +726,16 @@ public class VrManagerService extends SystemService implements EnabledComponentC if (current.size() > 0) { String flatSettings = formatSettings(current); - Settings.Secure.putString(resolver, Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, - flatSettings); + Settings.Secure.putStringForUser(resolver, + Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, + flatSettings, userId); } } - private void revokeNotificationListenerAccess(String pkg) { + private void revokeNotificationListenerAccess(String pkg, int userId) { ContentResolver resolver = mContext.getContentResolver(); - ArraySet current = getCurrentNotifListeners(resolver); + + ArraySet current = getNotificationListeners(resolver, userId); ArrayList toRemove = new ArrayList<>(); @@ -710,14 +749,37 @@ public class VrManagerService extends SystemService implements EnabledComponentC current.removeAll(toRemove); String flatSettings = formatSettings(current); - Settings.Secure.putString(resolver, Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, - flatSettings); - + Settings.Secure.putStringForUser(resolver, + Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, + flatSettings, userId); } - private ArraySet getCurrentNotifListeners(ContentResolver resolver) { - String flat = Settings.Secure.getString(resolver, - Settings.Secure.ENABLED_NOTIFICATION_LISTENERS); + private void grantCoarseLocationPermissionIfNeeded(String pkg, int userId) { + // Don't clobber the user if permission set in current state explicitly + if (!isPermissionUserUpdated(Manifest.permission.ACCESS_COARSE_LOCATION, pkg, userId)) { + mContext.getPackageManager().grantRuntimePermission(pkg, + Manifest.permission.ACCESS_COARSE_LOCATION, new UserHandle(userId)); + } + } + + private void revokeCoarseLocationPermissionIfNeeded(String pkg, int userId) { + // Don't clobber the user if permission set in current state explicitly + if (!isPermissionUserUpdated(Manifest.permission.ACCESS_COARSE_LOCATION, pkg, userId)) { + mContext.getPackageManager().revokeRuntimePermission(pkg, + Manifest.permission.ACCESS_COARSE_LOCATION, new UserHandle(userId)); + } + } + + private boolean isPermissionUserUpdated(String permission, String pkg, int userId) { + final int flags = mContext.getPackageManager().getPermissionFlags( + permission, pkg, new UserHandle(userId)); + return (flags & (PackageManager.FLAG_PERMISSION_USER_SET + | PackageManager.FLAG_PERMISSION_USER_FIXED)) != 0; + } + + private ArraySet getNotificationListeners(ContentResolver resolver, int userId) { + String flat = Settings.Secure.getStringForUser(resolver, + Settings.Secure.ENABLED_NOTIFICATION_LISTENERS, userId); ArraySet current = new ArraySet<>(); if (flat != null) { @@ -763,9 +825,8 @@ public class VrManagerService extends SystemService implements EnabledComponentC * Note: Must be called while holding {@code mLock}. * * @param enabled new state of the VR mode. - * @param exemptedComponent a component to exempt from AppOps restrictions for overlays. */ - private void changeVrModeLocked(boolean enabled, ComponentName exemptedComponent) { + private void changeVrModeLocked(boolean enabled) { if (mVrModeEnabled != enabled) { mVrModeEnabled = enabled; @@ -773,7 +834,6 @@ public class VrManagerService extends SystemService implements EnabledComponentC Slog.i(TAG, "VR mode " + ((mVrModeEnabled) ? "enabled" : "disabled")); setVrModeNative(mVrModeEnabled); - updateOverlayStateLocked(exemptedComponent); onVrModeChangedLocked(); } }