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();
+                    }
+                }
+            }
+        }
     }
 
     //==========================================================================================