Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 4acc16d1 authored by Phil Weaver's avatar Phil Weaver
Browse files

Add flag and listener for a11y volume requests.

We're adding a dedicated volume level for accessibility. Services
can use the new flag to request that this volume be activated for
accessibility usages.

To let AudioManager know when the request state changes, adding a
hidden convenience method to check if any active service requests
the a11y volume stream. This method can be used to enable the
stream and to decide when to show the UI to change its volume.

AudioManager wanted a listener for changes to this flag, so rather
than add yet another special-purpose listener, I've added one that
gets called back whenever there are state changes in a11y services.
These changes happen infrequently enough that we shouldn't need
more targeted methods.

Bug: 30448020
Bug: 27899567

Test: Adding CTS in linked CL.
Change-Id: Ifc53314dc7d9a6ee3d50b04ebcc1a87280cafa5e
parent 3eca29e1
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -2783,6 +2783,7 @@ package android.accessibilityservice {
    field public static final int FEEDBACK_HAPTIC = 2; // 0x2
    field public static final int FEEDBACK_SPOKEN = 1; // 0x1
    field public static final int FEEDBACK_VISUAL = 8; // 0x8
    field public static final int FLAG_ENABLE_ACCESSIBILITY_VOLUME = 128; // 0x80
    field public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 2; // 0x2
    field public static final int FLAG_REPORT_VIEW_IDS = 16; // 0x10
    field public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 8; // 0x8
@@ -44854,6 +44855,7 @@ package android.view.accessibility {
  }
  public final class AccessibilityManager {
    method public boolean addAccessibilityServicesStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener);
    method public boolean addAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener);
    method public boolean addTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener);
    method public deprecated java.util.List<android.content.pm.ServiceInfo> getAccessibilityServiceList();
@@ -44862,11 +44864,16 @@ package android.view.accessibility {
    method public void interrupt();
    method public boolean isEnabled();
    method public boolean isTouchExplorationEnabled();
    method public boolean removeAccessibilityServicesStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener);
    method public boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener);
    method public boolean removeTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener);
    method public void sendAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
  }
  public static abstract interface AccessibilityManager.AccessibilityServicesStateChangeListener {
    method public abstract void onAccessibilityServicesStateChanged();
  }
  public static abstract interface AccessibilityManager.AccessibilityStateChangeListener {
    method public abstract void onAccessibilityStateChanged(boolean);
  }
+7 −0
Original line number Diff line number Diff line
@@ -2898,6 +2898,7 @@ package android.accessibilityservice {
    field public static final int FEEDBACK_HAPTIC = 2; // 0x2
    field public static final int FEEDBACK_SPOKEN = 1; // 0x1
    field public static final int FEEDBACK_VISUAL = 8; // 0x8
    field public static final int FLAG_ENABLE_ACCESSIBILITY_VOLUME = 128; // 0x80
    field public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 2; // 0x2
    field public static final int FLAG_REPORT_VIEW_IDS = 16; // 0x10
    field public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 8; // 0x8
@@ -48066,6 +48067,7 @@ package android.view.accessibility {
  }
  public final class AccessibilityManager {
    method public boolean addAccessibilityServicesStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener);
    method public boolean addAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener);
    method public boolean addTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener);
    method public deprecated java.util.List<android.content.pm.ServiceInfo> getAccessibilityServiceList();
@@ -48074,11 +48076,16 @@ package android.view.accessibility {
    method public void interrupt();
    method public boolean isEnabled();
    method public boolean isTouchExplorationEnabled();
    method public boolean removeAccessibilityServicesStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener);
    method public boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener);
    method public boolean removeTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener);
    method public void sendAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
  }
  public static abstract interface AccessibilityManager.AccessibilityServicesStateChangeListener {
    method public abstract void onAccessibilityServicesStateChanged();
  }
  public static abstract interface AccessibilityManager.AccessibilityStateChangeListener {
    method public abstract void onAccessibilityStateChanged(boolean);
  }
+7 −0
Original line number Diff line number Diff line
@@ -2783,6 +2783,7 @@ package android.accessibilityservice {
    field public static final int FEEDBACK_HAPTIC = 2; // 0x2
    field public static final int FEEDBACK_SPOKEN = 1; // 0x1
    field public static final int FEEDBACK_VISUAL = 8; // 0x8
    field public static final int FLAG_ENABLE_ACCESSIBILITY_VOLUME = 128; // 0x80
    field public static final int FLAG_INCLUDE_NOT_IMPORTANT_VIEWS = 2; // 0x2
    field public static final int FLAG_REPORT_VIEW_IDS = 16; // 0x10
    field public static final int FLAG_REQUEST_ENHANCED_WEB_ACCESSIBILITY = 8; // 0x8
@@ -45148,6 +45149,7 @@ package android.view.accessibility {
  }
  public final class AccessibilityManager {
    method public boolean addAccessibilityServicesStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener);
    method public boolean addAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener);
    method public boolean addTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener);
    method public deprecated java.util.List<android.content.pm.ServiceInfo> getAccessibilityServiceList();
@@ -45156,11 +45158,16 @@ package android.view.accessibility {
    method public void interrupt();
    method public boolean isEnabled();
    method public boolean isTouchExplorationEnabled();
    method public boolean removeAccessibilityServicesStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener);
    method public boolean removeAccessibilityStateChangeListener(android.view.accessibility.AccessibilityManager.AccessibilityStateChangeListener);
    method public boolean removeTouchExplorationStateChangeListener(android.view.accessibility.AccessibilityManager.TouchExplorationStateChangeListener);
    method public void sendAccessibilityEvent(android.view.accessibility.AccessibilityEvent);
  }
  public static abstract interface AccessibilityManager.AccessibilityServicesStateChangeListener {
    method public abstract void onAccessibilityServicesStateChanged();
  }
  public static abstract interface AccessibilityManager.AccessibilityStateChangeListener {
    method public abstract void onAccessibilityStateChanged(boolean);
  }
+9 −0
Original line number Diff line number Diff line
@@ -319,6 +319,13 @@ public class AccessibilityServiceInfo implements Parcelable {
     */
    public static final int FLAG_RETRIEVE_INTERACTIVE_WINDOWS = 0x00000040;

    /**
     * This flag requests that all audio tracks system-wide with
     * {@link android.media.AudioAttributes#USAGE_ASSISTANCE_ACCESSIBILITY} be controlled by the
     * {@link android.media.AudioManager#STREAM_ACCESSIBILITY} volume.
     */
    public static final int FLAG_ENABLE_ACCESSIBILITY_VOLUME = 0x00000080;

    /** {@hide} */
    public static final int FLAG_FORCE_DIRECT_BOOT_AWARE = 0x00010000;

@@ -930,6 +937,8 @@ public class AccessibilityServiceInfo implements Parcelable {
                return "FLAG_REQUEST_FILTER_KEY_EVENTS";
            case FLAG_RETRIEVE_INTERACTIVE_WINDOWS:
                return "FLAG_RETRIEVE_INTERACTIVE_WINDOWS";
            case FLAG_ENABLE_ACCESSIBILITY_VOLUME:
                return "FLAG_ENABLE_ACCESSIBILITY_VOLUME";
            default:
                return null;
        }
+84 −4
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package android.view.accessibility;

import static android.accessibilityservice.AccessibilityServiceInfo.FLAG_ENABLE_ACCESSIBILITY_VOLUME;

import android.Manifest;
import android.accessibilityservice.AccessibilityServiceInfo;
import android.annotation.NonNull;
@@ -118,6 +120,8 @@ public final class AccessibilityManager {
    private final CopyOnWriteArrayList<HighTextContrastChangeListener>
            mHighTextContrastStateChangeListeners = new CopyOnWriteArrayList<>();

    private final CopyOnWriteArrayList<AccessibilityServicesStateChangeListener>
            mServicesStateChangeListeners = new CopyOnWriteArrayList<>();
    /**
     * Listener for the system accessibility state. To listen for changes to the
     * accessibility state on the device, implement this interface and register
@@ -130,7 +134,7 @@ public final class AccessibilityManager {
         *
         * @param enabled Whether accessibility is enabled.
         */
        public void onAccessibilityStateChanged(boolean enabled);
        void onAccessibilityStateChanged(boolean enabled);
    }

    /**
@@ -146,7 +150,20 @@ public final class AccessibilityManager {
         *
         * @param enabled Whether touch exploration is enabled.
         */
        public void onTouchExplorationStateChanged(boolean enabled);
        void onTouchExplorationStateChanged(boolean enabled);
    }

    /**
     * Listener for changes to the state of accessibility services. Changes include services being
     * enabled or disabled, or changes to the {@link AccessibilityServiceInfo} of a running service.
     * {@see #addAccessibilityServicesStateChangeListener}.
     */
    public interface AccessibilityServicesStateChangeListener {

        /**
         * Called when the state of accessibility services changes.
         */
        void onAccessibilityServicesStateChanged();
    }

    /**
@@ -164,11 +181,12 @@ public final class AccessibilityManager {
         *
         * @param enabled Whether high text contrast is enabled.
         */
        public void onHighTextContrastStateChanged(boolean enabled);
        void onHighTextContrastStateChanged(boolean enabled);
    }

    private final IAccessibilityManagerClient.Stub mClient =
            new IAccessibilityManagerClient.Stub() {
        @Override
        public void setState(int state) {
            // We do not want to change this immediately as the application may
            // have already checked that accessibility is on and fired an event,
@@ -178,6 +196,11 @@ public final class AccessibilityManager {
            // events when accessibility is off.
            mHandler.obtainMessage(MyHandler.MSG_SET_STATE, state, 0).sendToTarget();
        }

        @Override
        public void notifyServicesStateChanged() {
            mHandler.obtainMessage(MyHandler.MSG_NOTIFY_SERVICES_STATE_CHANGED).sendToTarget();
        }
    };

    /**
@@ -518,6 +541,30 @@ public final class AccessibilityManager {
        return mTouchExplorationStateChangeListeners.remove(listener);
    }

    /**
     * Registers a {@link AccessibilityServicesStateChangeListener}.
     *
     * @param listener The listener.
     * @return True if successfully registered.
     */
    public boolean addAccessibilityServicesStateChangeListener(
            @NonNull AccessibilityServicesStateChangeListener listener) {
        // Final CopyOnWriteArrayList - no lock needed.
        return mServicesStateChangeListeners.add(listener);
    }

    /**
     * Unregisters a {@link AccessibilityServicesStateChangeListener}.
     *
     * @param listener The listener.
     * @return True if successfully unregistered.
     */
    public boolean removeAccessibilityServicesStateChangeListener(
            @NonNull AccessibilityServicesStateChangeListener listener) {
        // Final CopyOnWriteArrayList - no lock needed.
        return mServicesStateChangeListeners.remove(listener);
    }

    /**
     * Registers a {@link HighTextContrastChangeListener} for changes in
     * the global high text contrast state of the system.
@@ -547,6 +594,24 @@ public final class AccessibilityManager {
        return mHighTextContrastStateChangeListeners.remove(listener);
    }

    /**
     * Check if the accessibility volume stream is active.
     *
     * @return True if accessibility volume is active (i.e. some service has requested it). False
     * otherwise.
     * @hide
     */
    public boolean isAccessibilityVolumeStreamActive() {
        List<AccessibilityServiceInfo> serviceInfos =
                getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_ALL_MASK);
        for (int i = 0; i < serviceInfos.size(); i++) {
            if ((serviceInfos.get(i).flags & FLAG_ENABLE_ACCESSIBILITY_VOLUME) != 0) {
                return true;
            }
        }
        return false;
    }

    /**
     * Sets the current state and notifies listeners, if necessary.
     *
@@ -695,11 +760,22 @@ public final class AccessibilityManager {
        }
    }

    /**
     * Notifies the registered {@link AccessibilityServicesStateChangeListener}s.
     */
    private void handleNotifyServicesStateChanged() {
        // Listeners are a final CopyOnWriteArrayList, hence no lock needed.
        for (AccessibilityServicesStateChangeListener listener : mServicesStateChangeListeners) {
            listener.onAccessibilityServicesStateChanged();
        }
    }

    private final class MyHandler extends Handler {
        public static final int MSG_NOTIFY_ACCESSIBILITY_STATE_CHANGED = 1;
        public static final int MSG_NOTIFY_EXPLORATION_STATE_CHANGED = 2;
        public static final int MSG_NOTIFY_HIGH_TEXT_CONTRAST_STATE_CHANGED = 3;
        public static final int MSG_SET_STATE = 4;
        public static final int MSG_NOTIFY_SERVICES_STATE_CHANGED = 5;

        public MyHandler(Looper looper) {
            super(looper, null, false);
@@ -720,6 +796,10 @@ public final class AccessibilityManager {
                    handleNotifyHighTextContrastStateChanged();
                } break;

                case MSG_NOTIFY_SERVICES_STATE_CHANGED: {
                    handleNotifyServicesStateChanged();
                } break;

                case MSG_SET_STATE: {
                    // See comment at mClient
                    final int state = message.arg1;
Loading