Replace Bluetooth reflection usages with impl @hide API.
Since we are targeting Android T and above, the reflection limitation
of AdvertisingSet#getOwnAddress() only works when targeting Android O
below. This change use "framework-bluetooth.impl" to access @hide APIs
in "framework-bluetooth" just like SL4A does:
https://cs.android.com/android/platform/superproject/+/master:external/sl4a/Common/Android.bp
This change also replace some constants with @SystemAPI usages.
Test: Maunal launch NearbyFastPairProviderSimulatorApp and pair with mainline FP seeker
Test: http://recall/-/cycSROwIosTzHgYDSIqPXl/fAWLYYQQMVCw0QWuefBU7v
Bug: 214015364
Ignore-AOSP-First: nearby_not_in_aosp_yet
Change-Id: Iedd22c05803e93da529936f03f911c0e647e84ec
diff --git a/nearby/tests/multidevices/clients/proguard.flags b/nearby/tests/multidevices/clients/proguard.flags
index fd494a8..11938cd 100644
--- a/nearby/tests/multidevices/clients/proguard.flags
+++ b/nearby/tests/multidevices/clients/proguard.flags
@@ -3,8 +3,8 @@
*;
}
-# Keep simulator reflection callback.
--keep class android.nearby.fastpair.provider.** {
+# Keep AdvertisingSetCallback#onOwnAddressRead callback.
+-keep class * extends android.bluetooth.le.AdvertisingSetCallback {
*;
}
diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/Android.bp b/nearby/tests/multidevices/clients/test_support/fastpair_provider/Android.bp
index 920834a..e01c436 100644
--- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/Android.bp
+++ b/nearby/tests/multidevices/clients/test_support/fastpair_provider/Android.bp
@@ -22,7 +22,16 @@
"src/**/*.java",
"src/**/*.kt",
],
- sdk_version: "test_current",
+ sdk_version: "core_platform",
+ libs: [
+ // order matters: classes in framework-bluetooth are resolved before framework, meaning
+ // @hide APIs in framework-bluetooth are resolved before @SystemApi stubs in framework
+ "framework-bluetooth.impl",
+ "framework",
+
+ // if sdk_version="" this gets automatically included, but here we need to add manually.
+ "framework-res",
+ ],
static_libs: [
"NearbyFastPairProviderLiteProtos",
"androidx.test.core",
diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/Android.bp b/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/Android.bp
index 79c5007..87d352f 100644
--- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/Android.bp
+++ b/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/Android.bp
@@ -16,9 +16,20 @@
default_applicable_licenses: ["Android-Apache-2.0"],
}
+// Build and install NearbyFastPairProviderSimulatorApp to your phone:
+// m NearbyFastPairProviderSimulatorApp
+// adb root
+// adb remount && adb reboot (make first time remount work)
+//
+// adb root
+// adb remount
+// adb push ${ANDROID_PRODUCT_OUT}/system/app/NearbyFastPairProviderSimulatorApp /system/app/
+// adb reboot
android_app {
name: "NearbyFastPairProviderSimulatorApp",
sdk_version: "test_current",
+ // Sign with "platform" certificate for accessing Bluetooth @SystemAPI
+ certificate: "platform",
static_libs: ["NearbyFastPairProviderSimulatorLib"],
optimize: {
enabled: true,
diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/proguard.flags b/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/proguard.flags
index 28680b3..0827c60 100644
--- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/proguard.flags
+++ b/nearby/tests/multidevices/clients/test_support/fastpair_provider/simulator_app/proguard.flags
@@ -1,5 +1,5 @@
-# Keep simulator reflection callback.
--keep class android.nearby.fastpair.provider.** {
+# Keep AdvertisingSetCallback#onOwnAddressRead callback.
+-keep class * extends android.bluetooth.le.AdvertisingSetCallback {
*;
}
diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/FastPairSimulator.java b/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/FastPairSimulator.java
index 232e84b..0d5563e 100644
--- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/FastPairSimulator.java
+++ b/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/FastPairSimulator.java
@@ -107,8 +107,6 @@
import com.android.server.nearby.common.bluetooth.fastpair.Ltv;
import com.android.server.nearby.common.bluetooth.fastpair.MessageStreamHmacEncoder;
import com.android.server.nearby.common.bluetooth.fastpair.NamingEncoder;
-import com.android.server.nearby.common.bluetooth.fastpair.Reflect;
-import com.android.server.nearby.common.bluetooth.fastpair.ReflectionException;
import com.google.common.base.Ascii;
import com.google.common.primitives.Bytes;
@@ -185,15 +183,6 @@
private static final long ADVERTISING_REFRESH_DELAY_1_MIN = TimeUnit.MINUTES.toMillis(1);
- /** The user will be prompted to accept or deny the incoming pairing request */
- public static final int PAIRING_VARIANT_CONSENT = 3;
-
- /**
- * The user will be prompted to enter the passkey displayed on remote device. This is used for
- * Bluetooth 2.1 pairing.
- */
- public static final int PAIRING_VARIANT_DISPLAY_PASSKEY = 4;
-
/**
* The size of account key filter in bytes is (1.2*n + 3), n represents the size of account key,
* see https://developers.google.com/nearby/fast-pair/spec#advertising_when_not_discoverable.
@@ -299,7 +288,7 @@
// the passkey later over GATT.
mLocalPasskey = key;
checkPasskey();
- } else if (variant == PAIRING_VARIANT_DISPLAY_PASSKEY) {
+ } else if (variant == BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY) {
if (mPasskeyEventCallback != null) {
mPasskeyEventCallback.onPasskeyRequested(
FastPairSimulator.this::enterPassKey);
@@ -307,7 +296,7 @@
mLogger.log("passkeyEventCallback is not set!");
enterPassKey(key);
}
- } else if (variant == PAIRING_VARIANT_CONSENT) {
+ } else if (variant == BluetoothDevice.PAIRING_VARIANT_CONSENT) {
setPasskeyConfirmation(true);
} else if (variant == BluetoothDevice.PAIRING_VARIANT_PIN) {
@@ -1969,14 +1958,7 @@
public void enterPassKey(int passkey) {
mLogger.log("enterPassKey called with passkey %d.", passkey);
- try {
- boolean result =
- (Boolean) Reflect.on(mPairingDevice).withMethod("setPasskey", int.class).get(
- passkey);
- mLogger.log("enterPassKey called with result %b", result);
- } catch (ReflectionException e) {
- mLogger.log("enterPassKey meet Exception %s.", e.getMessage());
- }
+ mPairingDevice.setPairingConfirmation(true);
}
private void checkPasskey() {
@@ -2290,19 +2272,11 @@
}
public void disconnect(BluetoothProfile profile, BluetoothDevice device) {
- try {
- Reflect.on(profile).withMethod("disconnect", BluetoothDevice.class).invoke(device);
- } catch (ReflectionException e) {
- mLogger.log(e, "Error disconnecting device=%s from profile=%s", device, profile);
- }
+ device.disconnect();
}
public void removeBond(BluetoothDevice device) {
- try {
- Reflect.on(device).withMethod("removeBond").invoke();
- } catch (ReflectionException e) {
- mLogger.log(e, "Error removing bond for device=%s", device);
- }
+ device.removeBond();
}
public void resetAccountKeys() {
diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/OreoFastPairAdvertiser.java b/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/OreoFastPairAdvertiser.java
index bb77c11..bc0cdfe 100644
--- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/OreoFastPairAdvertiser.java
+++ b/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/OreoFastPairAdvertiser.java
@@ -18,7 +18,6 @@
import static com.google.common.io.BaseEncoding.base16;
-import android.annotation.TargetApi;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.le.AdvertiseData;
import android.bluetooth.le.AdvertiseSettings;
@@ -27,21 +26,17 @@
import android.bluetooth.le.AdvertisingSetParameters;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.nearby.fastpair.provider.utils.Logger;
-import android.os.Build.VERSION_CODES;
import android.os.ParcelUuid;
import androidx.annotation.Nullable;
import com.android.server.nearby.common.bluetooth.fastpair.Constants.FastPairService;
-import com.android.server.nearby.common.bluetooth.fastpair.Reflect;
-import com.android.server.nearby.common.bluetooth.fastpair.ReflectionException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
/** Fast Pair advertiser taking advantage of new Android Oreo advertising features. */
-@TargetApi(VERSION_CODES.O)
public final class OreoFastPairAdvertiser implements FastPairAdvertiser {
private static final String TAG = "OreoFastPairAdvertiser";
private final Logger mLogger = new Logger(TAG);
@@ -63,13 +58,7 @@
mLogger.log("Advertising succeeded, advertising at %s dBm", txPower);
simulator.setIsAdvertising(true);
mAdvertisingSet = set;
-
- try {
- // Requires custom Android build, see callback below.
- Reflect.on(set).withMethod("getOwnAddress").invoke();
- } catch (ReflectionException e) {
- mLogger.log(e, "Error calling getOwnAddress for AdvertisingSet");
- }
+ mAdvertisingSet.getOwnAddress();
} else {
mLogger.log(
new IllegalStateException(),
@@ -88,7 +77,8 @@
}
}
- // Called via reflection with AdvertisingSet.getOwnAddress().
+ // Callback for AdvertisingSet.getOwnAddress().
+ @Override
public void onOwnAddressRead(
AdvertisingSet set, int addressType, String address) {
if (!address.equals(simulator.getBleAddress())) {
@@ -108,12 +98,7 @@
public void startAdvertising(@Nullable byte[] serviceData) {
// To be informed that BLE address is rotated, we need to polling query it asynchronously.
if (mAdvertisingSet != null) {
- try {
- // Requires custom Android build, see callback: onOwnAddressRead.
- Reflect.on(mAdvertisingSet).withMethod("getOwnAddress").invoke();
- } catch (ReflectionException ignored) {
- // Ignore it due to user already knows it when setting advertisingSet.
- }
+ mAdvertisingSet.getOwnAddress();
}
if (mSimulator.isDestroyed()) {
diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/bluetooth/BluetoothController.kt b/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/bluetooth/BluetoothController.kt
index 6a3e59e..0cc0c92 100644
--- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/bluetooth/BluetoothController.kt
+++ b/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/bluetooth/BluetoothController.kt
@@ -26,15 +26,13 @@
import android.content.IntentFilter
import android.nearby.fastpair.provider.FastPairSimulator
import android.nearby.fastpair.provider.utils.Logger
-import android.nearby.fastpair.provider.utils.Reflect
-import android.nearby.fastpair.provider.utils.ReflectionException
import android.os.SystemClock
import android.provider.Settings
/** Controls the local Bluetooth adapter for Fast Pair testing. */
class BluetoothController(
private val context: Context,
- private val listener: EventListener,
+ private val listener: EventListener
) : BroadcastReceiver() {
private val mLogger = Logger(TAG)
private val bluetoothAdapter: BluetoothAdapter =
@@ -67,23 +65,10 @@
* ```
*/
fun setIoCapability(ioCapabilityClassic: Int, ioCapabilityBLE: Int) {
- try {
- Reflect.on(bluetoothAdapter)
- .withMethod("setIoCapability", Int::class.javaPrimitiveType)[
- ioCapabilityClassic]
- } catch (e: ReflectionException) {
- mLogger.log(e, "Error setIoCapability to %s: %s", ioCapabilityClassic)
- }
- try {
- Reflect.on(bluetoothAdapter)
- .withMethod("setLeIoCapability", Int::class.javaPrimitiveType)[
- ioCapabilityBLE]
- } catch (e: ReflectionException) {
- mLogger.log(e, "Error setLeIoCapability to %s: %s", ioCapabilityBLE)
- }
+ bluetoothAdapter.ioCapability = ioCapabilityClassic
+ bluetoothAdapter.leIoCapability = ioCapabilityBLE
// Toggling airplane mode on/off to restart Bluetooth stack and reset the BLE.
- // Since it also increases reliability, we will do so even if ReflectionException is caught.
try {
Settings.Global.putInt(
context.contentResolver,
@@ -165,7 +150,7 @@
override fun onServiceDisconnected(profile: Int) {}
},
- BLUETOOTH_PROFILE_A2DP_SINK
+ BluetoothProfile.A2DP_SINK
)
}
@@ -204,7 +189,8 @@
else -> remoteDevice
}
mLogger.log(
- "ACTION_BOND_STATE_CHANGED, the bound state of the remote device (%s) change to %s.",
+ "ACTION_BOND_STATE_CHANGED, the bound state of " +
+ "the remote device (%s) change to %s.",
remoteDevice?.remoteDeviceToString(),
bondState.bondStateToString()
)
@@ -284,9 +270,6 @@
companion object {
private const val TAG = "BluetoothController"
- /** Hidden SystemApi field in [BluetoothProfile] interface. */
- private const val BLUETOOTH_PROFILE_A2DP_SINK = 11
-
private const val TURN_AIRPLANE_MODE_OFF = 0
private const val TURN_AIRPLANE_MODE_ON = 1
}
diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/utils/Reflect.java b/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/utils/Reflect.java
deleted file mode 100644
index 5ae5310..0000000
--- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/utils/Reflect.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.nearby.fastpair.provider.utils;
-
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-
-/**
- * Utilities for calling methods using reflection. The main benefit of using this helper is to avoid
- * complications around exception handling when calling methods reflectively. It's not safe to use
- * Java 8's multicatch on such exceptions, because the java compiler converts multicatch into
- * ReflectiveOperationException in some instances, which doesn't work on older sdk versions.
- * Instead, use these utilities and catch ReflectionException.
- *
- * <p>Example usage:
- *
- * <pre>{@code
- * try {
- * Reflect.on(btAdapter)
- * .withMethod("setScanMode", int.class)
- * .invoke(BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE)
- * } catch (ReflectionException e) { }
- * }</pre>
- */
-public final class Reflect {
- private final Object mTargetObject;
-
- private Reflect(Object targetObject) {
- this.mTargetObject = targetObject;
- }
-
- /** Creates an instance of this helper to invoke methods on the given target object. */
- public static Reflect on(Object targetObject) {
- return new Reflect(targetObject);
- }
-
- /** Finds a method with the given name and parameter types. */
- public ReflectionMethod withMethod(String methodName, Class<?>... paramTypes)
- throws ReflectionException {
- try {
- return new ReflectionMethod(mTargetObject.getClass().getMethod(methodName, paramTypes));
- } catch (NoSuchMethodException e) {
- throw new ReflectionException(e);
- }
- }
-
- /** Represents an invokable method found reflectively. */
- public final class ReflectionMethod {
- private final Method mMethod;
-
- private ReflectionMethod(Method method) {
- this.mMethod = method;
- }
-
- /**
- * Invokes this instance method with the given parameters. The called method does not return
- * a value.
- */
- public void invoke(Object... parameters) throws ReflectionException {
- try {
- mMethod.invoke(mTargetObject, parameters);
- } catch (IllegalAccessException e) {
- throw new ReflectionException(e);
- } catch (InvocationTargetException e) {
- throw new ReflectionException(e);
- }
- }
-
- /**
- * Invokes this instance method with the given parameters. The called method returns a non
- * null
- * value.
- */
- public Object get(Object... parameters) throws ReflectionException {
- Object value;
- try {
- value = mMethod.invoke(mTargetObject, parameters);
- } catch (IllegalAccessException e) {
- throw new ReflectionException(e);
- } catch (InvocationTargetException e) {
- throw new ReflectionException(e);
- }
- if (value == null) {
- throw new ReflectionException(new NullPointerException());
- }
- return value;
- }
- }
-}
diff --git a/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/utils/ReflectionException.java b/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/utils/ReflectionException.java
deleted file mode 100644
index 959fd11..0000000
--- a/nearby/tests/multidevices/clients/test_support/fastpair_provider/src/android/nearby/fastpair/provider/utils/ReflectionException.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2022 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.nearby.fastpair.provider.utils;
-
-/**
- * An exception thrown during a reflection operation. Like ReflectiveOperationException, except
- * compatible on older API versions.
- */
-public final class ReflectionException extends Exception {
- public ReflectionException(Throwable cause) {
- super(cause.getMessage(), cause);
- }
-}