diff options
8 files changed, 158 insertions, 17 deletions
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index bde8f39..40eb799 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -17,6 +17,7 @@ package android.app; import android.annotation.NonNull; +import android.content.ComponentName; /** * Activity manager local system service interface. @@ -48,4 +49,10 @@ public abstract class ActivityManagerInternal { */ public abstract void release(); } + + /** + * Returns home activity for the specified user. + * @param userId ID of the user or {@link android.os.UserHandle#USER_ALL} + */ + public abstract ComponentName getHomeActivityForUser(int userId); } diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 5aa399b..7104185 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -606,7 +606,7 @@ public class AppOpsManager { UserManager.DISALLOW_CREATE_WINDOWS, //SYSTEM_ALERT_WINDOW null, //ACCESS_NOTIFICATIONS null, //CAMERA - null, //RECORD_AUDIO + UserManager.DISALLOW_RECORD_AUDIO, //RECORD_AUDIO null, //PLAY_AUDIO null, //READ_CLIPBOARD null, //WRITE_CLIPBOARD diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index 811418b..f212daf 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -45,6 +45,8 @@ interface IUserManager { Bundle getUserRestrictions(int userHandle); boolean hasUserRestriction(in String restrictionKey, int userHandle); void setUserRestrictions(in Bundle restrictions, int userHandle); + void setUserRestriction(String key, boolean value, int userId); + void setSystemControlledUserRestriction(String key, boolean value, int userId); void setApplicationRestrictions(in String packageName, in Bundle restrictions, int userHandle); Bundle getApplicationRestrictions(in String packageName); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index c7ac7ce..cc37d5e 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -412,6 +412,16 @@ public class UserManager { public static final String DISALLOW_SAFE_BOOT = "no_safe_boot"; /** + * Specifies if a user is not allowed to record audio. This restriction is always enabled for + * background users. The default value is <code>false</code>. + * + * @see #setUserRestrictions(Bundle) + * @see #getUserRestrictions() + * @hide + */ + public static final String DISALLOW_RECORD_AUDIO = "no_record_audio"; + + /** * Application restriction key that is used to indicate the pending arrival * of real restrictions for the app. * @@ -688,9 +698,11 @@ public class UserManager { */ @Deprecated public void setUserRestriction(String key, boolean value, UserHandle userHandle) { - Bundle bundle = getUserRestrictions(userHandle); - bundle.putBoolean(key, value); - setUserRestrictions(bundle, userHandle); + try { + mService.setUserRestriction(key, value, userHandle.getIdentifier()); + } catch (RemoteException re) { + Log.w(TAG, "Could not set user restriction", re); + } } /** 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)); |