From d8163ff782fa46c6b1447ca8ef53355f4e59a4d1 Mon Sep 17 00:00:00 2001 From: Daniel Norman <danielnorman@google.com> Date: Thu, 15 Feb 2024 01:59:47 +0000 Subject: [PATCH] Use a sysprop so connect APIs fail fast if HIDRAW is not supported. Bug: 311783331 Test: atest BrailleDisplayControllerImplTest Test: Manually set sysprop on my device, observe API returns failure Change-Id: I12106e620fbf84d48673e3d113343c2a0954e744 --- .../BrailleDisplayControllerImpl.java | 31 ++++++++++++++++++- .../BrailleDisplayControllerImplTest.java | 24 +++++++++++--- 2 files changed, 50 insertions(+), 5 deletions(-) diff --git a/core/java/android/accessibilityservice/BrailleDisplayControllerImpl.java b/core/java/android/accessibilityservice/BrailleDisplayControllerImpl.java index cac1dc4e04a9..f1df33657279 100644 --- a/core/java/android/accessibilityservice/BrailleDisplayControllerImpl.java +++ b/core/java/android/accessibilityservice/BrailleDisplayControllerImpl.java @@ -25,9 +25,11 @@ import android.hardware.usb.UsbDevice; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; +import android.os.SystemProperties; import android.view.accessibility.AccessibilityInteractionClient; import android.view.accessibility.Flags; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.FunctionalUtils; import java.io.IOException; @@ -36,24 +38,46 @@ import java.util.concurrent.Executor; /** * Default implementation of {@link BrailleDisplayController}. + * + * @hide */ // BrailleDisplayControllerImpl is not an API, but it implements BrailleDisplayController APIs. // This @FlaggedApi annotation tells the linter that this method delegates API checks to its // callers. @FlaggedApi(Flags.FLAG_BRAILLE_DISPLAY_HID) -final class BrailleDisplayControllerImpl implements BrailleDisplayController { +@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) +public final class BrailleDisplayControllerImpl implements BrailleDisplayController { private final AccessibilityService mAccessibilityService; private final Object mLock; + private final boolean mIsHidrawSupported; private IBrailleDisplayConnection mBrailleDisplayConnection; private Executor mCallbackExecutor; private BrailleDisplayCallback mCallback; + /** + * Read-only property that returns whether HIDRAW access is supported on this device. + * + * <p>Defaults to true. + * + * <p>Device manufacturers without HIDRAW kernel support can set this to false in + * the device's product makefile. + */ + private static final boolean IS_HIDRAW_SUPPORTED = SystemProperties.getBoolean( + "ro.accessibility.support_hidraw", true); + BrailleDisplayControllerImpl(AccessibilityService accessibilityService, Object lock) { + this(accessibilityService, lock, IS_HIDRAW_SUPPORTED); + } + + @VisibleForTesting + public BrailleDisplayControllerImpl(AccessibilityService accessibilityService, + Object lock, boolean isHidrawSupported) { mAccessibilityService = accessibilityService; mLock = lock; + mIsHidrawSupported = isHidrawSupported; } @Override @@ -113,6 +137,11 @@ final class BrailleDisplayControllerImpl implements BrailleDisplayController { createConnection, @NonNull Executor callbackExecutor, @NonNull BrailleDisplayCallback callback) { BrailleDisplayController.checkApiFlagIsEnabled(); + if (!mIsHidrawSupported) { + callbackExecutor.execute(() -> callback.onConnectionFailed( + BrailleDisplayCallback.FLAG_ERROR_CANNOT_ACCESS)); + return; + } if (isConnected()) { throw new IllegalStateException( "This service already has a connected Braille display"); diff --git a/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java b/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java index aaa199d4c814..e8b295bd5fdb 100644 --- a/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java +++ b/core/tests/coretests/src/android/accessibilityservice/BrailleDisplayControllerImplTest.java @@ -22,6 +22,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyZeroInteractions; import android.hardware.usb.UsbDevice; import android.platform.test.annotations.RequiresFlagsEnabled; @@ -58,6 +59,7 @@ public class BrailleDisplayControllerImplTest { @Rule public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); + private AccessibilityService mAccessibilityService; private BrailleDisplayController mBrailleDisplayController; @Mock @@ -76,12 +78,13 @@ public class BrailleDisplayControllerImplTest { @Before public void test() { MockitoAnnotations.initMocks(this); - AccessibilityService accessibilityService = spy(new TestAccessibilityService()); - doReturn((Executor) Runnable::run).when(accessibilityService).getMainExecutor(); - doReturn(TEST_SERVICE_CONNECTION_ID).when(accessibilityService).getConnectionId(); + mAccessibilityService = spy(new TestAccessibilityService()); + doReturn((Executor) Runnable::run).when(mAccessibilityService).getMainExecutor(); + doReturn(TEST_SERVICE_CONNECTION_ID).when(mAccessibilityService).getConnectionId(); AccessibilityInteractionClient.addConnection(TEST_SERVICE_CONNECTION_ID, mAccessibilityServiceConnection, /*initializeCache=*/false); - mBrailleDisplayController = accessibilityService.getBrailleDisplayController(); + mBrailleDisplayController = new BrailleDisplayControllerImpl( + mAccessibilityService, new Object(), /*isHidrawSupported=*/true); } // Automated CTS tests only use the BluetoothDevice version of BrailleDisplayController#connect @@ -104,4 +107,17 @@ public class BrailleDisplayControllerImplTest { assertThrows(IllegalStateException.class, () -> mBrailleDisplayController.connect(usbDevice, mBrailleDisplayCallback)); } + + @Test + public void connect_HidrawNotSupported_callsOnConnectionFailed() { + BrailleDisplayController controller = new BrailleDisplayControllerImpl( + mAccessibilityService, new Object(), /*isHidrawSupported=*/false); + UsbDevice usbDevice = Mockito.mock(UsbDevice.class); + + controller.connect(usbDevice, mBrailleDisplayCallback); + + verify(mBrailleDisplayCallback).onConnectionFailed( + BrailleDisplayController.BrailleDisplayCallback.FLAG_ERROR_CANNOT_ACCESS); + verifyZeroInteractions(mAccessibilityServiceConnection); + } } -- GitLab