summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/ActivityManagerInternal.java7
-rw-r--r--core/java/android/app/AppOpsManager.java2
-rw-r--r--core/java/android/os/IUserManager.aidl2
-rw-r--r--core/java/android/os/UserManager.java18
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java8
-rw-r--r--services/core/java/com/android/server/am/ActivityStackSupervisor.java7
-rw-r--r--services/core/java/com/android/server/audio/AudioService.java61
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java70
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));