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

Commit 3516a9d7 authored by Daniel Norman's avatar Daniel Norman Committed by Android (Google) Code Review
Browse files

Merge "Allows fallback to matching by name for older kernels without UNIQ." into main

parents cc6f9f11 d4edc1d0
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -305,4 +305,6 @@ public interface BrailleDisplayController {
    @FlaggedApi(Flags.FLAG_BRAILLE_DISPLAY_HID)
    @TestApi
    String TEST_BRAILLE_DISPLAY_UNIQUE_ID = "UNIQUE_ID";
    /** @hide */
    String TEST_BRAILLE_DISPLAY_NAME = "NAME";
}
+17 −2
Original line number Diff line number Diff line
@@ -37,6 +37,8 @@ import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.app.PendingIntent;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -695,6 +697,13 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
            throw new IllegalArgumentException(
                    bluetoothAddress + " is not a valid Bluetooth address");
        }
        final BluetoothManager bluetoothManager =
                mContext.getSystemService(BluetoothManager.class);
        final String bluetoothDeviceName = bluetoothManager == null ? null :
                bluetoothManager.getAdapter().getBondedDevices().stream()
                        .filter(device -> device.getAddress().equalsIgnoreCase(bluetoothAddress))
                        .map(BluetoothDevice::getName)
                        .findFirst().orElse(null);
        synchronized (mLock) {
            checkAccessibilityAccessLocked();
            if (mBrailleDisplayConnection != null) {
@@ -706,7 +715,10 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
                connection.setTestData(mTestBrailleDisplays);
            }
            connection.connectLocked(
                    bluetoothAddress, BrailleDisplayConnection.BUS_BLUETOOTH, controller);
                    bluetoothAddress,
                    bluetoothDeviceName,
                    BrailleDisplayConnection.BUS_BLUETOOTH,
                    controller);
        }
    }

@@ -763,7 +775,10 @@ class AccessibilityServiceConnection extends AbstractAccessibilityServiceConnect
                connection.setTestData(mTestBrailleDisplays);
            }
            connection.connectLocked(
                    usbSerialNumber, BrailleDisplayConnection.BUS_USB, controller);
                    usbSerialNumber,
                    usbDevice.getProductName(),
                    BrailleDisplayConnection.BUS_USB,
                    controller);
        }
    }

+46 −7
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.accessibilityservice.IBrailleDisplayConnection;
import android.accessibilityservice.IBrailleDisplayController;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.PermissionManuallyEnforced;
import android.annotation.RequiresNoPermission;
import android.bluetooth.BluetoothDevice;
@@ -33,6 +34,7 @@ import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Pair;
@@ -141,6 +143,8 @@ class BrailleDisplayConnection extends IBrailleDisplayConnection.Stub {

        @BusType
        int getDeviceBusType(@NonNull Path path);

        String getName(@NonNull Path path);
    }

    /**
@@ -150,14 +154,18 @@ class BrailleDisplayConnection extends IBrailleDisplayConnection.Stub {
     * read from the Braille display.
     *
     * @param expectedUniqueId The expected unique ID of the device to connect, from
     *                          {@link UsbDevice#getSerialNumber()}
     *                          or {@link BluetoothDevice#getAddress()}
     *                         {@link UsbDevice#getSerialNumber()} or
     *                         {@link BluetoothDevice#getAddress()}.
     * @param expectedName     The expected name of the device to connect, from
     *                         {@link BluetoothDevice#getName()} or
     *                         {@link UsbDevice#getProductName()}.
     * @param expectedBusType  The expected bus type from {@link BusType}.
     * @param controller       Interface containing oneway callbacks used to communicate with the
     *                         {@link android.accessibilityservice.BrailleDisplayController}.
     */
    void connectLocked(
            @NonNull String expectedUniqueId,
            @Nullable String expectedName,
            @BusType int expectedBusType,
            @NonNull IBrailleDisplayController controller) {
        Objects.requireNonNull(expectedUniqueId);
@@ -179,10 +187,20 @@ class BrailleDisplayConnection extends IBrailleDisplayConnection.Stub {
                unableToGetDescriptor = true;
                continue;
            }
            final boolean matchesIdentifier;
            final String uniqueId = mScanner.getUniqueId(path);
            if (uniqueId != null) {
                matchesIdentifier = expectedUniqueId.equalsIgnoreCase(uniqueId);
            } else {
                // HIDIOCGRAWUNIQ was added in kernel version 5.7.
                // If the device has an older kernel that does not support that ioctl then as a
                // fallback we can check against the device name (from HIDIOCGRAWNAME).
                final String name = mScanner.getName(path);
                matchesIdentifier = !TextUtils.isEmpty(expectedName) && expectedName.equals(name);
            }
            if (isBrailleDisplay(descriptor)
                    && mScanner.getDeviceBusType(path) == expectedBusType
                    && expectedUniqueId.equalsIgnoreCase(uniqueId)) {
                    && matchesIdentifier) {
                result.add(Pair.create(path.toFile(), descriptor));
            }
        }
@@ -498,6 +516,12 @@ class BrailleDisplayConnection extends IBrailleDisplayConnection.Stub {
                Integer busType = readFromFileDescriptor(path, nativeInterface::getHidrawBusType);
                return busType != null ? busType : BUS_UNKNOWN;
            }

            @Override
            public String getName(@NonNull Path path) {
                Objects.requireNonNull(path);
                return readFromFileDescriptor(path, nativeInterface::getHidrawName);
            }
        };
    }

@@ -542,6 +566,12 @@ class BrailleDisplayConnection extends IBrailleDisplayConnection.Stub {
                            BrailleDisplayController.TEST_BRAILLE_DISPLAY_BUS_BLUETOOTH)
                            ? BUS_BLUETOOTH : BUS_USB;
                }

                @Override
                public String getName(@NonNull Path path) {
                    return brailleDisplayMap.get(path).getString(
                            BrailleDisplayController.TEST_BRAILLE_DISPLAY_NAME);
                }
            };
            return mScanner;
        }
@@ -579,6 +609,8 @@ class BrailleDisplayConnection extends IBrailleDisplayConnection.Stub {
         * @return the result of ioctl(HIDIOCGRAWINFO).bustype, or -1 if the ioctl fails.
         */
        int getHidrawBusType(int fd);

        String getHidrawName(int fd);
    }

    /** Native interface that actually calls native HIDRAW ioctls. */
@@ -602,6 +634,11 @@ class BrailleDisplayConnection extends IBrailleDisplayConnection.Stub {
        public int getHidrawBusType(int fd) {
            return nativeGetHidrawBusType(fd);
        }

        @Override
        public String getHidrawName(int fd) {
            return nativeGetHidrawName(fd);
        }
    }

    private static native int nativeGetHidrawDescSize(int fd);
@@ -611,4 +648,6 @@ class BrailleDisplayConnection extends IBrailleDisplayConnection.Stub {
    private static native String nativeGetHidrawUniq(int fd);

    private static native int nativeGetHidrawBusType(int fd);

    private static native String nativeGetHidrawName(int fd);
}
+16 −4
Original line number Diff line number Diff line
@@ -32,10 +32,10 @@ namespace android {

namespace {

// Max size we allow for the result from HIDIOCGRAWUNIQ (Bluetooth address or USB serial number).
// Copied from linux/hid.h struct hid_device->uniq char array size; the ioctl implementation
// writes at most this many bytes to the provided buffer.
constexpr int UNIQ_SIZE_MAX = 64;
// Max sizes we allow for results from string ioctl calls, copied from UAPI linux/uhid.h.
// The ioctl implementation writes at most this many bytes to the provided buffer:
constexpr int NAME_SIZE_MAX = 128; // HIDIOCGRAWNAME (device name)
constexpr int UNIQ_SIZE_MAX = 64;  // HIDIOCGRAWUNIQ (BT address or USB serial number)

} // anonymous namespace

@@ -82,6 +82,16 @@ static jint com_android_server_accessibility_BrailleDisplayConnection_getHidrawB
    return info.bustype;
}

static jstring com_android_server_accessibility_BrailleDisplayConnection_getHidrawName(
        JNIEnv* env, jclass /*clazz*/, int fd) {
    char buf[NAME_SIZE_MAX];
    if (ioctl(fd, HIDIOCGRAWNAME(NAME_SIZE_MAX), buf) < 0) {
        return nullptr;
    }
    // Local ref is not deleted because it is returned to Java
    return env->NewStringUTF(buf);
}

static const JNINativeMethod gMethods[] = {
        {"nativeGetHidrawDescSize", "(I)I",
         (void*)com_android_server_accessibility_BrailleDisplayConnection_getHidrawDescSize},
@@ -91,6 +101,8 @@ static const JNINativeMethod gMethods[] = {
         (void*)com_android_server_accessibility_BrailleDisplayConnection_getHidrawUniq},
        {"nativeGetHidrawBusType", "(I)I",
         (void*)com_android_server_accessibility_BrailleDisplayConnection_getHidrawBusType},
        {"nativeGetHidrawName", "(I)Ljava/lang/String;",
         (void*)com_android_server_accessibility_BrailleDisplayConnection_getHidrawName},
};

int register_com_android_server_accessibility_BrailleDisplayConnection(JNIEnv* env) {
+49 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.accessibilityservice.BrailleDisplayController;
import android.accessibilityservice.IBrailleDisplayController;
import android.content.Context;
import android.os.Bundle;
import android.os.IBinder;
@@ -173,6 +174,17 @@ public class BrailleDisplayConnectionTest {
                    .isEqualTo(BrailleDisplayConnection.BUS_BLUETOOTH);
        }

        @Test
        public void defaultNativeScanner_getName_returnsName() {
            String name = "My Braille Display";
            when(mNativeInterface.getHidrawName(anyInt())).thenReturn(name);

            BrailleDisplayConnection.BrailleDisplayScanner scanner =
                    BrailleDisplayConnection.getDefaultNativeScanner(mNativeInterface);

            assertThat(scanner.getName(NULL_PATH)).isEqualTo(name);
        }

        @Test
        public void write_bypassesServiceSideCheckWithLargeBuffer_disconnects() {
            Mockito.doNothing().when(mBrailleDisplayConnection).disconnect();
@@ -201,6 +213,38 @@ public class BrailleDisplayConnectionTest {
            verify(mBrailleDisplayConnection).disconnect();
        }

        @Test
        public void connect_unableToGetUniq_usesNameFallback() throws Exception {
            try {
                IBrailleDisplayController controller =
                        Mockito.mock(IBrailleDisplayController.class);
                final Path path = Path.of("/dev/null");
                final String macAddress = "00:11:22:33:AA:BB";
                final String name = "My Braille Display";
                final byte[] descriptor = {0x05, 0x41};
                Bundle bd = new Bundle();
                bd.putString(BrailleDisplayController.TEST_BRAILLE_DISPLAY_HIDRAW_PATH,
                        path.toString());
                bd.putByteArray(BrailleDisplayController.TEST_BRAILLE_DISPLAY_DESCRIPTOR,
                        descriptor);
                bd.putString(BrailleDisplayController.TEST_BRAILLE_DISPLAY_NAME, name);
                bd.putBoolean(BrailleDisplayController.TEST_BRAILLE_DISPLAY_BUS_BLUETOOTH, true);
                bd.putString(BrailleDisplayController.TEST_BRAILLE_DISPLAY_UNIQUE_ID, null);
                BrailleDisplayConnection.BrailleDisplayScanner scanner =
                        mBrailleDisplayConnection.setTestData(List.of(bd));
                // Validate that the test data is set up correctly before attempting connection:
                assertThat(scanner.getUniqueId(path)).isNull();
                assertThat(scanner.getName(path)).isEqualTo(name);

                mBrailleDisplayConnection.connectLocked(
                        macAddress, name, BrailleDisplayConnection.BUS_BLUETOOTH, controller);

                verify(controller).onConnected(eq(mBrailleDisplayConnection), eq(descriptor));
            } finally {
                mBrailleDisplayConnection.disconnect();
            }
        }

        // BrailleDisplayConnection#setTestData() is used to enable CTS testing with
        // test Braille display data, but its own implementation should also be tested
        // so that issues in this helper don't cause confusing failures in CTS.
@@ -220,6 +264,9 @@ public class BrailleDisplayConnectionTest {
            String uniq1 = "uniq1", uniq2 = "uniq2";
            bd1.putString(BrailleDisplayController.TEST_BRAILLE_DISPLAY_UNIQUE_ID, uniq1);
            bd2.putString(BrailleDisplayController.TEST_BRAILLE_DISPLAY_UNIQUE_ID, uniq2);
            String name1 = "name1", name2 = "name2";
            bd1.putString(BrailleDisplayController.TEST_BRAILLE_DISPLAY_NAME, name1);
            bd2.putString(BrailleDisplayController.TEST_BRAILLE_DISPLAY_NAME, name2);
            int bus1 = BrailleDisplayConnection.BUS_USB, bus2 =
                    BrailleDisplayConnection.BUS_BLUETOOTH;
            bd1.putBoolean(BrailleDisplayController.TEST_BRAILLE_DISPLAY_BUS_BLUETOOTH,
@@ -235,6 +282,8 @@ public class BrailleDisplayConnectionTest {
            expect.that(scanner.getDeviceReportDescriptor(path2)).isEqualTo(desc2);
            expect.that(scanner.getUniqueId(path1)).isEqualTo(uniq1);
            expect.that(scanner.getUniqueId(path2)).isEqualTo(uniq2);
            expect.that(scanner.getName(path1)).isEqualTo(name1);
            expect.that(scanner.getName(path2)).isEqualTo(name2);
            expect.that(scanner.getDeviceBusType(path1)).isEqualTo(bus1);
            expect.that(scanner.getDeviceBusType(path2)).isEqualTo(bus2);
        }