diff options
author | Fyodor Kupolov <fkupolov@google.com> | 2015-04-17 17:59:14 -0700 |
---|---|---|
committer | Fyodor Kupolov <fkupolov@google.com> | 2015-05-08 17:17:16 -0700 |
commit | b501330a1b6ef14ff512a5727f7a01bc423d6fbb (patch) | |
tree | 59796487de2723dc92a0520def552124fb3aa556 /services | |
parent | 4f434a04708e7a254afe2e0d362f715229dc15d1 (diff) | |
download | frameworks_base-b501330a1b6ef14ff512a5727f7a01bc423d6fbb.zip frameworks_base-b501330a1b6ef14ff512a5727f7a01bc423d6fbb.tar.gz frameworks_base-b501330a1b6ef14ff512a5727f7a01bc423d6fbb.tar.bz2 |
Disable multi-user background recording
On user switch, kill existing processes of the background user with
android.permission.RECORD_AUDIO permission. Home activity should not be
killed to avoid an expensive restart of the home launcher, when the
user switches back.
Introduced DISALLOW_RECORD_AUDIO user restriction, which is enabled for the
background user, and removed for the foreground user.
Introduced a concept of system controlled user restriction, which can only
be set by the system, rather than device administrator.
Bug: 20346194
Change-Id: Ic942fd565e80d14424230dae612965a8e229c4ef
Diffstat (limited to 'services')
4 files changed, 133 insertions, 13 deletions
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index ab7fba5..10855e2 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -19951,6 +19951,14 @@ public final class ActivityManagerService extends ActivityManagerNative return token; } } + + @Override + public ComponentName getHomeActivityForUser(int userId) { + synchronized (ActivityManagerService.this) { + ActivityRecord homeActivity = mStackSupervisor.getHomeActivityForUser(userId); + return homeActivity.realActivity; + } + } } private final class SleepTokenImpl extends SleepToken { diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 8c0d6b8..cb5ba8e 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -2656,6 +2656,10 @@ public final class ActivityStackSupervisor implements DisplayListener { } ActivityRecord getHomeActivity() { + return getHomeActivityForUser(UserHandle.USER_ALL); + } + + ActivityRecord getHomeActivityForUser(int userId) { final ArrayList<TaskRecord> tasks = mHomeStack.getAllTasks(); for (int taskNdx = tasks.size() - 1; taskNdx >= 0; --taskNdx) { final TaskRecord task = tasks.get(taskNdx); @@ -2663,7 +2667,8 @@ public final class ActivityStackSupervisor implements DisplayListener { final ArrayList<ActivityRecord> activities = task.mActivities; for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) { final ActivityRecord r = activities.get(activityNdx); - if (r.isHomeActivity()) { + if (r.isHomeActivity() + && ((userId == UserHandle.USER_ALL) || (r.userId == userId))) { return r; } } diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 06fba34..aa365ea 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -21,8 +21,11 @@ import static android.media.AudioManager.RINGER_MODE_NORMAL; import static android.media.AudioManager.RINGER_MODE_SILENT; import static android.media.AudioManager.RINGER_MODE_VIBRATE; +import android.Manifest; import android.app.ActivityManager; +import android.app.ActivityManagerInternal; import android.app.ActivityManagerNative; +import android.app.AppGlobals; import android.app.AppOpsManager; import android.app.KeyguardManager; import android.bluetooth.BluetoothA2dp; @@ -37,7 +40,10 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; +import android.content.pm.UserInfo; import android.content.res.Configuration; import android.content.res.Resources; import android.content.res.XmlResourceParser; @@ -82,11 +88,13 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; +import android.os.UserManager; import android.os.Vibrator; import android.provider.Settings; import android.provider.Settings.System; import android.telecom.TelecomManager; import android.text.TextUtils; +import android.util.AndroidRuntimeException; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; @@ -102,6 +110,7 @@ import android.view.accessibility.AccessibilityManager; import com.android.internal.util.XmlUtils; import com.android.server.EventLogTags; import com.android.server.LocalServices; +import com.android.server.pm.UserManagerService; import org.xmlpull.v1.XmlPullParserException; @@ -645,6 +654,8 @@ public class AudioService extends IAudioService.Stub { intentFilter.addAction(Intent.ACTION_SCREEN_ON); intentFilter.addAction(Intent.ACTION_SCREEN_OFF); intentFilter.addAction(Intent.ACTION_USER_SWITCHED); + intentFilter.addAction(Intent.ACTION_USER_BACKGROUND); + intentFilter.addAction(Intent.ACTION_USER_FOREGROUND); intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED); intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); @@ -668,7 +679,7 @@ public class AudioService extends IAudioService.Stub { setRotationForAudioSystem(); } - context.registerReceiver(mReceiver, intentFilter); + context.registerReceiverAsUser(mReceiver, UserHandle.ALL, intentFilter, null, null); LocalServices.addService(AudioManagerInternal.class, new AudioServiceInternal()); } @@ -4975,10 +4986,58 @@ public class AudioService extends IAudioService.Stub { 0, 0, mStreamStates[AudioSystem.STREAM_MUSIC], 0); + } else if (action.equals(Intent.ACTION_USER_BACKGROUND)) { + // Disable audio recording for the background user/profile + int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); + if (userId >= 0) { + // TODO Kill recording streams instead of killing processes holding permission + UserInfo userInfo = UserManagerService.getInstance().getUserInfo(userId); + killBackgroundUserProcessesWithRecordAudioPermission(userInfo); + } + UserManagerService.getInstance().setSystemControlledUserRestriction( + UserManager.DISALLOW_RECORD_AUDIO, true, userId); + } else if (action.equals(Intent.ACTION_USER_FOREGROUND)) { + // Enable audio recording for foreground user/profile + int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); + UserManagerService.getInstance().setSystemControlledUserRestriction( + UserManager.DISALLOW_RECORD_AUDIO, false, userId); } } } // end class AudioServiceBroadcastReceiver + private void killBackgroundUserProcessesWithRecordAudioPermission(UserInfo oldUser) { + PackageManager pm = mContext.getPackageManager(); + // Find the home activity of the user. It should not be killed to avoid expensive restart, + // when the user switches back. For managed profiles, we should kill all recording apps + ComponentName homeActivityName = null; + if (!oldUser.isManagedProfile()) { + homeActivityName = LocalServices.getService(ActivityManagerInternal.class) + .getHomeActivityForUser(oldUser.id); + } + final String[] permissions = { Manifest.permission.RECORD_AUDIO }; + List<PackageInfo> packages; + try { + packages = AppGlobals.getPackageManager() + .getPackagesHoldingPermissions(permissions, 0, oldUser.id).getList(); + } catch (RemoteException e) { + throw new AndroidRuntimeException(e); + } + for (int j = packages.size() - 1; j >= 0; j--) { + PackageInfo pkg = packages.get(j); + if (homeActivityName != null + && pkg.packageName.equals(homeActivityName.getPackageName()) + && pkg.applicationInfo.isSystemApp()) { + continue; + } + try { + ActivityManagerNative.getDefault().killUid(pkg.applicationInfo.uid, + "killBackgroundUserProcessesWithAudioRecordPermission"); + } catch (RemoteException e) { + Log.w(TAG, "Error calling killUid", e); + } + } + } + //========================================================================================== // RemoteControlDisplay / RemoteControlClient / Remote info //========================================================================================== diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 51b26a8..8dccfdf 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -50,6 +50,8 @@ import android.util.SparseBooleanArray; import android.util.TimeUtils; import android.util.Xml; +import com.google.android.collect.Sets; + import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IAppOpsService; import com.android.internal.util.ArrayUtils; @@ -70,6 +72,7 @@ import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; +import java.util.Set; import libcore.io.IoUtils; @@ -126,6 +129,10 @@ public class UserManagerService extends IUserManager.Stub { // without first making sure that the rest of the framework is prepared for it. private static final int MAX_MANAGED_PROFILES = 1; + // Set of user restrictions, which can only be enforced by the system + private static final Set<String> SYSTEM_CONTROLLED_RESTRICTIONS = Sets.newArraySet( + UserManager.DISALLOW_RECORD_AUDIO); + static final int WRITE_USER_MSG = 1; static final int WRITE_USER_DELAY = 2*1000; // 2 seconds @@ -500,7 +507,7 @@ public class UserManagerService extends IUserManager.Stub { public boolean hasUserRestriction(String restrictionKey, int userId) { synchronized (mPackagesLock) { Bundle restrictions = mUserRestrictions.get(userId); - return restrictions != null ? restrictions.getBoolean(restrictionKey) : false; + return restrictions != null && restrictions.getBoolean(restrictionKey); } } @@ -515,25 +522,59 @@ public class UserManagerService extends IUserManager.Stub { } @Override + public void setUserRestriction(String key, boolean value, int userId) { + synchronized (mPackagesLock) { + if (!SYSTEM_CONTROLLED_RESTRICTIONS.contains(key)) { + Bundle restrictions = getUserRestrictions(userId); + restrictions.putBoolean(key, value); + setUserRestrictionsInternalLocked(restrictions, userId); + } + } + } + + @Override + public void setSystemControlledUserRestriction(String key, boolean value, int userId) { + checkSystemOrRoot("setSystemControlledUserRestriction"); + synchronized (mPackagesLock) { + Bundle restrictions = getUserRestrictions(userId); + restrictions.putBoolean(key, value); + setUserRestrictionsInternalLocked(restrictions, userId); + } + } + + @Override public void setUserRestrictions(Bundle restrictions, int userId) { checkManageUsersPermission("setUserRestrictions"); if (restrictions == null) return; synchronized (mPackagesLock) { - mUserRestrictions.get(userId).clear(); - mUserRestrictions.get(userId).putAll(restrictions); - long token = Binder.clearCallingIdentity(); - try { - mAppOpsService.setUserRestrictions(mUserRestrictions.get(userId), userId); - } catch (RemoteException e) { - Log.w(LOG_TAG, "Unable to notify AppOpsService of UserRestrictions"); - } finally { - Binder.restoreCallingIdentity(token); + final Bundle oldUserRestrictions = mUserRestrictions.get(userId); + // Restore the original state of system controlled restrictions from oldUserRestrictions + for (String key : SYSTEM_CONTROLLED_RESTRICTIONS) { + restrictions.remove(key); + if (oldUserRestrictions.containsKey(key)) { + restrictions.putBoolean(key, oldUserRestrictions.getBoolean(key)); + } } - scheduleWriteUserLocked(mUsers.get(userId)); + setUserRestrictionsInternalLocked(restrictions, userId); } } + private void setUserRestrictionsInternalLocked(Bundle restrictions, int userId) { + final Bundle userRestrictions = mUserRestrictions.get(userId); + userRestrictions.clear(); + userRestrictions.putAll(restrictions); + long token = Binder.clearCallingIdentity(); + try { + mAppOpsService.setUserRestrictions(userRestrictions, userId); + } catch (RemoteException e) { + Log.w(LOG_TAG, "Unable to notify AppOpsService of UserRestrictions"); + } finally { + Binder.restoreCallingIdentity(token); + } + scheduleWriteUserLocked(mUsers.get(userId)); + } + /** * Check if we've hit the limit of how many users can be created. */ @@ -569,6 +610,13 @@ public class UserManagerService extends IUserManager.Stub { } } + private static void checkSystemOrRoot(String message) { + final int uid = Binder.getCallingUid(); + if (uid != Process.SYSTEM_UID && uid != 0) { + throw new SecurityException("Only system may call: " + message); + } + } + private void writeBitmapLocked(UserInfo info, Bitmap bitmap) { try { File dir = new File(mUsersDir, Integer.toString(info.id)); |