Fix permission checking for a11y volume.
Adding new permission for changing accessibility volume
to allow system UI to change it. Also providing audio
manager with a whitelist of bound accessibility service
uids so it can allow servics to change the volume.
Bug: 36569297
Test: Adding CTS test in linked CL.
Change-Id: I4f327e3a251fc3780c5957f41217c2ef5bb8b16e
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 8721f34..1cd0afe 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2312,6 +2312,10 @@
<permission android:name="android.permission.MODIFY_ACCESSIBILITY_DATA"
android:protectionLevel="signature" />
+ <!-- @hide Allows an application to change the accessibility volume. -->
+ <permission android:name="android.permission.CHANGE_ACCESSIBILITY_VOLUME"
+ android:protectionLevel="signature" />
+
<!-- @hide Allows an application to collect frame statistics -->
<permission android:name="android.permission.FRAME_STATS"
android:protectionLevel="signature" />
diff --git a/media/java/android/media/AudioManagerInternal.java b/media/java/android/media/AudioManagerInternal.java
index b60dbd5..0a1de33 100644
--- a/media/java/android/media/AudioManagerInternal.java
+++ b/media/java/android/media/AudioManagerInternal.java
@@ -15,6 +15,7 @@
*/
package android.media;
+import android.util.IntArray;
import com.android.server.LocalServices;
/**
@@ -43,6 +44,8 @@
public abstract void updateRingerModeAffectedStreamsInternal();
+ public abstract void setAccessibilityServiceUids(IntArray uids);
+
public interface RingerModeDelegate {
/** Called when external ringer mode is evaluated, returns the new internal ringer mode */
int onSetRingerModeExternal(int ringerModeOld, int ringerModeNew, String caller,
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index e4d71b6..1147f16 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -181,7 +181,7 @@
<uses-permission android:name="android.permission.MODIFY_ACCESSIBILITY_DATA" />
<!-- to control accessibility volume -->
- <uses-permission android:name="android.permission.BIND_ACCESSIBILITY_SERVICE" />
+ <uses-permission android:name="android.permission.CHANGE_ACCESSIBILITY_VOLUME" />
<application
android:name=".SystemUIApplication"
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 05c6592..acaae7b 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -52,6 +52,7 @@
import android.hardware.display.DisplayManager;
import android.hardware.fingerprint.IFingerprintService;
import android.hardware.input.InputManager;
+import android.media.AudioManagerInternal;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -75,6 +76,7 @@
import android.provider.SettingsStringUtil.SettingStringHelper;
import android.text.TextUtils;
import android.text.TextUtils.SimpleStringSplitter;
+import android.util.IntArray;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
@@ -220,6 +222,8 @@
private final List<AccessibilityServiceInfo> mTempAccessibilityServiceInfoList =
new ArrayList<>();
+ private final IntArray mTempIntArray = new IntArray(0);
+
private final RemoteCallbackList<IAccessibilityManagerClient> mGlobalClients =
new RemoteCallbackList<>();
@@ -1558,6 +1562,21 @@
}
}
+ final int count = userState.mBoundServices.size();
+ mTempIntArray.clear();
+ for (int i = 0; i < count; i++) {
+ final ResolveInfo resolveInfo =
+ userState.mBoundServices.get(i).mAccessibilityServiceInfo.getResolveInfo();
+ if (resolveInfo != null) {
+ mTempIntArray.add(resolveInfo.serviceInfo.applicationInfo.uid);
+ }
+ }
+ // Calling out with lock held, but to a lower-level service
+ final AudioManagerInternal audioManager =
+ LocalServices.getService(AudioManagerInternal.class);
+ if (audioManager != null) {
+ audioManager.setAccessibilityServiceUids(mTempIntArray);
+ }
updateAccessibilityEnabledSetting(userState);
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 333d27b..e08ab60 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -106,6 +106,7 @@
import android.util.AndroidRuntimeException;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.IntArray;
import android.util.Log;
import android.util.MathUtils;
import android.util.Slog;
@@ -126,6 +127,7 @@
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -575,6 +577,10 @@
private VolumePolicy mVolumePolicy = VolumePolicy.DEFAULT;
private long mLoweredFromNormalToVibrateTime;
+ // Array of Uids of valid accessibility services to check if caller is one of them
+ private int[] mAccessibilityServiceUids;
+ private final Object mAccessibilityServiceUidsLock = new Object();
+
// Intent "extra" data keys.
public static final String CONNECT_INTENT_KEY_PORT_NAME = "portName";
public static final String CONNECT_INTENT_KEY_STATE = "state";
@@ -1241,11 +1247,9 @@
/** @see AudioManager#adjustStreamVolume(int, int, int) */
public void adjustStreamVolume(int streamType, int direction, int flags,
String callingPackage) {
- if ( streamType == AudioManager.STREAM_ACCESSIBILITY
- && (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE))) {
+ if ((streamType == AudioManager.STREAM_ACCESSIBILITY) && !canChangeAccessibilityVolume()) {
Log.w(TAG, "Trying to call adjustStreamVolume() for a11y without"
- + "BIND_ACCESSIBILITY_SERVICE / callingPackage=" + callingPackage);
+ + "CHANGE_ACCESSIBILITY_VOLUME / callingPackage=" + callingPackage);
return;
}
adjustStreamVolume(streamType, direction, flags, callingPackage, callingPackage,
@@ -1559,17 +1563,33 @@
/** @see AudioManager#setStreamVolume(int, int, int) */
public void setStreamVolume(int streamType, int index, int flags, String callingPackage) {
- if ( streamType == AudioManager.STREAM_ACCESSIBILITY
- && (PackageManager.PERMISSION_GRANTED != mContext.checkCallingOrSelfPermission(
- android.Manifest.permission.BIND_ACCESSIBILITY_SERVICE))) {
+ if ((streamType == AudioManager.STREAM_ACCESSIBILITY) && !canChangeAccessibilityVolume()) {
Log.w(TAG, "Trying to call setStreamVolume() for a11y without"
- + " BIND_ACCESSIBILITY_SERVICE callingPackage=" + callingPackage);
+ + " CHANGE_ACCESSIBILITY_VOLUME callingPackage=" + callingPackage);
return;
}
setStreamVolume(streamType, index, flags, callingPackage, callingPackage,
Binder.getCallingUid());
}
+ private boolean canChangeAccessibilityVolume() {
+ synchronized (mAccessibilityServiceUidsLock) {
+ if (PackageManager.PERMISSION_GRANTED == mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_ACCESSIBILITY_VOLUME)) {
+ return true;
+ }
+ if (mAccessibilityServiceUids != null) {
+ int callingUid = Binder.getCallingUid();
+ for (int i = 0; i < mAccessibilityServiceUids.length; i++) {
+ if (mAccessibilityServiceUids[i] == callingUid) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ }
+
private void setStreamVolume(int streamType, int index, int flags, String callingPackage,
String caller, int uid) {
if (DEBUG_VOL) {
@@ -6380,6 +6400,29 @@
}
}
}
+
+ @Override
+ public void setAccessibilityServiceUids(IntArray uids) {
+ synchronized (mAccessibilityServiceUidsLock) {
+ if (uids.size() == 0) {
+ mAccessibilityServiceUids = null;
+ } else {
+ boolean changed = (mAccessibilityServiceUids == null)
+ || (mAccessibilityServiceUids.length != uids.size());
+ if (!changed) {
+ for (int i = 0; i < mAccessibilityServiceUids.length; i++) {
+ if (uids.get(i) != mAccessibilityServiceUids[i]) {
+ changed = true;
+ break;
+ }
+ }
+ }
+ if (changed) {
+ mAccessibilityServiceUids = uids.toArray();
+ }
+ }
+ }
+ }
}
//==========================================================================================