Merge "Import translations. DO NOT MERGE ANYWHERE" into tm-dev
diff --git a/framework/api/system-current.txt b/framework/api/system-current.txt
index 7a57426..db1d7e9 100644
--- a/framework/api/system-current.txt
+++ b/framework/api/system-current.txt
@@ -340,7 +340,7 @@
method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setSsid(@Nullable String);
method @NonNull public android.net.NetworkCapabilities.Builder setSubscriptionIds(@NonNull java.util.Set<java.lang.Integer>);
method @NonNull public android.net.NetworkCapabilities.Builder setTransportInfo(@Nullable android.net.TransportInfo);
- method @NonNull public android.net.NetworkCapabilities.Builder setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>);
+ method @NonNull @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY) public android.net.NetworkCapabilities.Builder setUnderlyingNetworks(@Nullable java.util.List<android.net.Network>);
method @NonNull public static android.net.NetworkCapabilities.Builder withoutDefaultCapabilities();
}
diff --git a/framework/src/android/net/ConnectivityManager.java b/framework/src/android/net/ConnectivityManager.java
index e25a855..a174fe3 100644
--- a/framework/src/android/net/ConnectivityManager.java
+++ b/framework/src/android/net/ConnectivityManager.java
@@ -995,21 +995,25 @@
// LINT.ThenChange(packages/modules/Connectivity/service/native/include/Common.h)
/**
- * Specify default rule which may allow or drop packets depending on existing policy.
+ * A firewall rule which allows or drops packets depending on existing policy.
+ * Used by {@link #setUidFirewallRule(int, int, int)} to follow existing policy to handle
+ * specific uid's packets in specific firewall chain.
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public static final int FIREWALL_RULE_DEFAULT = 0;
/**
- * Specify allow rule which allows packets.
+ * A firewall rule which allows packets. Used by {@link #setUidFirewallRule(int, int, int)} to
+ * allow specific uid's packets in specific firewall chain.
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
public static final int FIREWALL_RULE_ALLOW = 1;
/**
- * Specify deny rule which drops packets.
+ * A firewall rule which drops packets. Used by {@link #setUidFirewallRule(int, int, int)} to
+ * drop specific uid's packets in specific firewall chain.
* @hide
*/
@SystemApi(client = MODULE_LIBRARIES)
diff --git a/framework/src/android/net/NetworkCapabilities.java b/framework/src/android/net/NetworkCapabilities.java
index f7f2f57..97b1f32 100644
--- a/framework/src/android/net/NetworkCapabilities.java
+++ b/framework/src/android/net/NetworkCapabilities.java
@@ -863,8 +863,11 @@
}
/**
- * Get the underlying networks of this network. If the caller is not system privileged, this is
- * always redacted to null and it will be never useful to the caller.
+ * Get the underlying networks of this network. If the caller doesn't have one of
+ * {@link android.Manifest.permission.NETWORK_FACTORY},
+ * {@link android.Manifest.permission.NETWORK_SETTINGS} and
+ * {@link NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}, this is always redacted to null and
+ * it will be never useful to the caller.
*
* @return <li>If the list is null, this network hasn't declared underlying networks.</li>
* <li>If the list is empty, this network has declared that it has no underlying
@@ -2650,7 +2653,7 @@
/**
* Builder class for NetworkCapabilities.
*
- * This class is mainly for for {@link NetworkAgent} instances to use. Many fields in
+ * This class is mainly for {@link NetworkAgent} instances to use. Many fields in
* the built class require holding a signature permission to use - mostly
* {@link android.Manifest.permission.NETWORK_FACTORY}, but refer to the specific
* description of each setter. As this class lives entirely in app space it does not
@@ -3058,9 +3061,20 @@
/**
* Set the underlying networks of this network.
*
+ * <p>This API is mainly for {@link NetworkAgent}s who hold
+ * {@link android.Manifest.permission.NETWORK_FACTORY} to set its underlying networks.
+ *
+ * <p>The underlying networks are only visible for the receiver who has one of
+ * {@link android.Manifest.permission.NETWORK_FACTORY},
+ * {@link android.Manifest.permission.NETWORK_SETTINGS} and
+ * {@link NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}.
+ * If the receiver doesn't have required permissions, the field will be cleared before
+ * sending to the caller.</p>
+ *
* @param networks The underlying networks of this network.
*/
@NonNull
+ @RequiresPermission(android.Manifest.permission.NETWORK_FACTORY)
public Builder setUnderlyingNetworks(@Nullable List<Network> networks) {
mCaps.setUnderlyingNetworks(networks);
return this;
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/src/android/nearby/multidevices/fastpair/provider/FastPairProviderSimulatorSnippet.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/FastPairProviderSimulatorSnippet.kt
index 4a8a772..922e950 100644
--- a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/FastPairProviderSimulatorSnippet.kt
+++ b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/FastPairProviderSimulatorSnippet.kt
@@ -70,4 +70,10 @@
fun getBluetoothLeAddress(): String {
return fastPairProviderSimulatorController.getProviderSimulatorBleAddress()
}
+
+ /** Gets the latest account key received on the Fast Pair provider simulator */
+ @Rpc(description = "Gets the latest account key received on the Fast Pair provider simulator.")
+ fun getLatestReceivedAccountKey(): String? {
+ return fastPairProviderSimulatorController.getLatestReceivedAccountKey()
+ }
}
diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/controller/FastPairProviderSimulatorController.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/controller/FastPairProviderSimulatorController.kt
index 2ab6dbd..a2d2659 100644
--- a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/controller/FastPairProviderSimulatorController.kt
+++ b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/provider/controller/FastPairProviderSimulatorController.kt
@@ -21,7 +21,7 @@
import android.nearby.fastpair.provider.FastPairSimulator
import android.nearby.fastpair.provider.bluetooth.BluetoothController
import com.google.android.mobly.snippet.util.Log
-import com.google.common.io.BaseEncoding
+import com.google.common.io.BaseEncoding.base64
class FastPairProviderSimulatorController(private val context: Context) :
FastPairSimulator.AdvertisingChangedCallback, BluetoothController.EventListener {
@@ -50,7 +50,7 @@
) {
eventListener = listener
- val antiSpoofingKey = BaseEncoding.base64().decode(antiSpoofingKeyString)
+ val antiSpoofingKey = base64().decode(antiSpoofingKeyString)
simulator = FastPairSimulator(
context, FastPairSimulator.Options.builder(modelId)
.setAdvertisingModelId(modelId)
@@ -68,6 +68,9 @@
fun getProviderSimulatorBleAddress() = simulator!!.bleAddress!!
+ fun getLatestReceivedAccountKey() =
+ simulator!!.accountKey?.let { base64().encode(it.toByteArray()) }
+
/**
* Called when we change our BLE advertisement.
*
diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairSeekerSnippet.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairSeekerSnippet.kt
index 65856d8..fd4f4b4 100644
--- a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairSeekerSnippet.kt
+++ b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/FastPairSeekerSnippet.kt
@@ -23,9 +23,11 @@
import android.nearby.ScanRequest
import android.nearby.fastpair.seeker.FAKE_TEST_ACCOUNT_NAME
import android.nearby.multidevices.fastpair.seeker.data.FastPairTestDataManager
+import android.nearby.multidevices.fastpair.seeker.events.PairingCallbackEvents
import android.nearby.multidevices.fastpair.seeker.events.ScanCallbackEvents
import android.nearby.multidevices.fastpair.seeker.ui.CheckNearbyHalfSheetUiTest
import android.nearby.multidevices.fastpair.seeker.ui.DismissNearbyHalfSheetUiTest
+import android.nearby.multidevices.fastpair.seeker.ui.PairByNearbyHalfSheetUiTest
import androidx.test.core.app.ApplicationProvider
import com.google.android.mobly.snippet.Snippet
import com.google.android.mobly.snippet.rpc.AsyncRpc
@@ -114,7 +116,7 @@
@Rpc(description = "Puts an array of FastPairAccountKeyDeviceMetadata into test data cache.")
fun putAccountKeyDeviceMetadata(json: String) {
Log.i("Puts an array of FastPairAccountKeyDeviceMetadata into test data cache.")
- fastPairTestDataManager.sendAccountKeyDeviceMetadata(json)
+ fastPairTestDataManager.sendAccountKeyDeviceMetadataJsonArray(json)
}
/** Dumps all FastPairAccountKeyDeviceMetadata from the test data cache. */
@@ -142,11 +144,25 @@
DismissNearbyHalfSheetUiTest().dismissHalfSheet()
}
+ /** Starts pairing by interacting with half sheet UI.
+ *
+ * @param callbackId the callback ID corresponding to the
+ * {@link FastPairSeekerSnippet#startPairing} call that started the pairing.
+ */
+ @AsyncRpc(description = "Starts pairing by interacting with half sheet UI.")
+ fun startPairing(callbackId: String) {
+ Log.i("Starts pairing by interacting with half sheet UI.")
+
+ PairByNearbyHalfSheetUiTest().clickConnectButton()
+ fastPairTestDataManager.registerDataReceiveListener(PairingCallbackEvents(callbackId))
+ }
+
/** Invokes when the snippet runner shutting down. */
override fun shutdown() {
super.shutdown()
Log.i("Resets the Fast Pair test data cache.")
+ fastPairTestDataManager.unregisterDataReceiveListener()
fastPairTestDataManager.sendResetCache()
}
}
\ No newline at end of file
diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/data/FastPairTestDataManager.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/data/FastPairTestDataManager.kt
index 291aad8..239ac61 100644
--- a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/data/FastPairTestDataManager.kt
+++ b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/data/FastPairTestDataManager.kt
@@ -19,12 +19,20 @@
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
-import android.nearby.fastpair.seeker.*
+import android.content.IntentFilter
+import android.nearby.fastpair.seeker.ACTION_RESET_TEST_DATA_CACHE
+import android.nearby.fastpair.seeker.ACTION_SEND_ACCOUNT_KEY_DEVICE_METADATA
+import android.nearby.fastpair.seeker.ACTION_SEND_ANTISPOOF_KEY_DEVICE_METADATA
+import android.nearby.fastpair.seeker.ACTION_WRITE_ACCOUNT_KEY_DEVICE_METADATA
+import android.nearby.fastpair.seeker.DATA_JSON_STRING_KEY
+import android.nearby.fastpair.seeker.DATA_MODEL_ID_STRING_KEY
+import android.nearby.fastpair.seeker.FastPairTestDataCache
import android.util.Log
/** Manage local FastPairTestDataCache and send to/sync from the remote cache in data provider. */
class FastPairTestDataManager(private val context: Context) : BroadcastReceiver() {
val testDataCache = FastPairTestDataCache()
+ var listener: EventListener? = null
/** Puts a model id to FastPairAntispoofKeyDeviceMetadata pair into local and remote cache.
*
@@ -41,17 +49,17 @@
testDataCache.putAntispoofKeyDeviceMetadata(modelId, json)
}
- /** Puts account key device metadata to local and remote cache.
+ /** Puts account key device metadata array to local and remote cache.
*
* @param json a string of FastPairAccountKeyDeviceMetadata JSON array.
*/
- fun sendAccountKeyDeviceMetadata(json: String) {
+ fun sendAccountKeyDeviceMetadataJsonArray(json: String) {
Intent().also { intent ->
intent.action = ACTION_SEND_ACCOUNT_KEY_DEVICE_METADATA
intent.putExtra(DATA_JSON_STRING_KEY, json)
context.sendBroadcast(intent)
}
- testDataCache.putAccountKeyDeviceMetadata(json)
+ testDataCache.putAccountKeyDeviceMetadataJsonArray(json)
}
/** Clears local and remote cache. */
@@ -73,12 +81,33 @@
ACTION_WRITE_ACCOUNT_KEY_DEVICE_METADATA -> {
Log.d(TAG, "ACTION_WRITE_ACCOUNT_KEY_DEVICE_METADATA received!")
val json = intent.getStringExtra(DATA_JSON_STRING_KEY)!!
- testDataCache.putAccountKeyDeviceMetadata(json)
+ testDataCache.putAccountKeyDeviceMetadataJsonObject(json)
+ listener?.onManageFastPairAccountDevice(json)
}
else -> Log.d(TAG, "Unknown action received!")
}
}
+ fun registerDataReceiveListener(listener: EventListener) {
+ this.listener = listener
+ val bondStateFilter = IntentFilter(ACTION_WRITE_ACCOUNT_KEY_DEVICE_METADATA)
+ context.registerReceiver(this, bondStateFilter)
+ }
+
+ fun unregisterDataReceiveListener() {
+ this.listener = null
+ context.unregisterReceiver(this)
+ }
+
+ /** Interface for listening the data receive from the remote cache in data provider. */
+ interface EventListener {
+ /** Reports a FastPairAccountKeyDeviceMetadata write into the cache.
+ *
+ * @param json the FastPairAccountKeyDeviceMetadata as JSON object string.
+ */
+ fun onManageFastPairAccountDevice(json: String)
+ }
+
companion object {
private const val TAG = "FastPairTestDataManager"
}
diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/events/PairingCallbackEvents.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/events/PairingCallbackEvents.kt
new file mode 100644
index 0000000..19de1d9
--- /dev/null
+++ b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/events/PairingCallbackEvents.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.multidevices.fastpair.seeker.events
+
+import android.nearby.multidevices.fastpair.seeker.data.FastPairTestDataManager
+import com.google.android.mobly.snippet.util.postSnippetEvent
+
+/** The Mobly snippet events to report to the Python side. */
+class PairingCallbackEvents(private val callbackId: String) :
+ FastPairTestDataManager.EventListener {
+
+ /** Reports a FastPairAccountKeyDeviceMetadata write into the cache.
+ *
+ * @param json the FastPairAccountKeyDeviceMetadata as JSON object string.
+ */
+ override fun onManageFastPairAccountDevice(json: String) {
+ postSnippetEvent(callbackId, "onManageAccountDevice") {
+ putString("accountDeviceJsonString", json)
+ }
+ }
+}
\ No newline at end of file
diff --git a/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/ui/PairByNearbyHalfSheetUiTest.kt b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/ui/PairByNearbyHalfSheetUiTest.kt
new file mode 100644
index 0000000..9028668
--- /dev/null
+++ b/nearby/tests/multidevices/clients/src/android/nearby/multidevices/fastpair/seeker/ui/PairByNearbyHalfSheetUiTest.kt
@@ -0,0 +1,47 @@
+/*
+ * 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.multidevices.fastpair.seeker.ui
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.Until
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/** An instrumented test to start pairing by interacting with Nearby half sheet UI.
+ *
+ * To run this test directly:
+ * am instrument -w -r \
+ * -e class android.nearby.multidevices.fastpair.seeker.ui.PairByNearbyHalfSheetUiTest \
+ * android.nearby.multidevices/androidx.test.runner.AndroidJUnitRunner
+ */
+@RunWith(AndroidJUnit4::class)
+class PairByNearbyHalfSheetUiTest {
+ private val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
+
+ @Test
+ fun clickConnectButton() {
+ val connectButton = NearbyHalfSheetUiMap.DevicePairingFragment.connectButton
+ device.findObject(connectButton).click()
+ device.wait(Until.gone(connectButton), CONNECT_BUTTON_TIMEOUT_MILLS)
+ }
+
+ companion object {
+ const val CONNECT_BUTTON_TIMEOUT_MILLS = 3000L
+ }
+}
\ No newline at end of file
diff --git a/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/shared/android/nearby/fastpair/seeker/FastPairTestDataCache.kt b/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/shared/android/nearby/fastpair/seeker/FastPairTestDataCache.kt
index be94031..e08a122 100644
--- a/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/shared/android/nearby/fastpair/seeker/FastPairTestDataCache.kt
+++ b/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/shared/android/nearby/fastpair/seeker/FastPairTestDataCache.kt
@@ -31,12 +31,18 @@
private val antispoofKeyDeviceMetadataDataMap =
mutableMapOf<String, FastPairAntispoofKeyDeviceMetadataData>()
- fun putAccountKeyDeviceMetadata(json: String) {
+ fun putAccountKeyDeviceMetadataJsonArray(json: String) {
accountKeyDeviceMetadataList +=
gson.fromJson(json, Array<FastPairAccountKeyDeviceMetadataData>::class.java)
.map { it.toFastPairAccountKeyDeviceMetadata() }
}
+ fun putAccountKeyDeviceMetadataJsonObject(json: String) {
+ accountKeyDeviceMetadataList +=
+ gson.fromJson(json, FastPairAccountKeyDeviceMetadataData::class.java)
+ .toFastPairAccountKeyDeviceMetadata()
+ }
+
fun putAccountKeyDeviceMetadata(accountKeyDeviceMetadata: FastPairAccountKeyDeviceMetadata) {
accountKeyDeviceMetadataList += accountKeyDeviceMetadata
}
diff --git a/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/src/android/nearby/fastpair/seeker/data/FastPairTestDataManager.kt b/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/src/android/nearby/fastpair/seeker/data/FastPairTestDataManager.kt
index f226789..e924da1 100644
--- a/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/src/android/nearby/fastpair/seeker/data/FastPairTestDataManager.kt
+++ b/nearby/tests/multidevices/clients/test_service/fastpair_seeker_data_provider/src/android/nearby/fastpair/seeker/data/FastPairTestDataManager.kt
@@ -72,7 +72,7 @@
ACTION_SEND_ACCOUNT_KEY_DEVICE_METADATA -> {
Log.d(TAG, "ACTION_SEND_ACCOUNT_KEY_DEVICE_METADATA received!")
val json = intent.getStringExtra(DATA_JSON_STRING_KEY)!!
- testDataCache.putAccountKeyDeviceMetadata(json)
+ testDataCache.putAccountKeyDeviceMetadataJsonArray(json)
}
ACTION_RESET_TEST_DATA_CACHE -> {
Log.d(TAG, "ACTION_RESET_TEST_DATA_CACHE received!")
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);
- }
-}
diff --git a/nearby/tests/multidevices/host/initial_pairing_test.py b/nearby/tests/multidevices/host/initial_pairing_test.py
new file mode 100644
index 0000000..1a49045
--- /dev/null
+++ b/nearby/tests/multidevices/host/initial_pairing_test.py
@@ -0,0 +1,62 @@
+# 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.
+
+"""CTS-V Nearby Mainline Fast Pair end-to-end test case: initial pairing test."""
+
+from test_helper import constants
+from test_helper import fast_pair_base_test
+
+# The model ID to simulate on provider side.
+PROVIDER_SIMULATOR_MODEL_ID = constants.DEFAULT_MODEL_ID
+# The public key to simulate as registered headsets.
+PROVIDER_SIMULATOR_ANTI_SPOOFING_KEY = constants.DEFAULT_ANTI_SPOOFING_KEY
+# The anti-spoof key device metadata JSON file for data provider at seeker side.
+PROVIDER_SIMULATOR_KDM_JSON_FILE = constants.DEFAULT_KDM_JSON_FILE
+
+# Time in seconds for events waiting.
+SETUP_TIMEOUT_SEC = constants.SETUP_TIMEOUT_SEC
+BECOME_DISCOVERABLE_TIMEOUT_SEC = constants.BECOME_DISCOVERABLE_TIMEOUT_SEC
+START_ADVERTISING_TIMEOUT_SEC = constants.START_ADVERTISING_TIMEOUT_SEC
+HALF_SHEET_POPUP_TIMEOUT_SEC = constants.HALF_SHEET_POPUP_TIMEOUT_SEC
+MANAGE_ACCOUNT_DEVICE_TIMEOUT_SEC = constants.AVERAGE_PAIRING_TIMEOUT_SEC * 2
+
+
+class InitialPairingTest(fast_pair_base_test.FastPairBaseTest):
+ """Fast Pair initial pairing test."""
+
+ def setup_test(self) -> None:
+ super().setup_test()
+ self._provider.start_model_id_advertising(PROVIDER_SIMULATOR_MODEL_ID,
+ PROVIDER_SIMULATOR_ANTI_SPOOFING_KEY)
+ self._provider.wait_for_discoverable_mode(BECOME_DISCOVERABLE_TIMEOUT_SEC)
+ self._provider.wait_for_advertising_start(START_ADVERTISING_TIMEOUT_SEC)
+ self._seeker.put_anti_spoof_key_device_metadata(PROVIDER_SIMULATOR_MODEL_ID,
+ PROVIDER_SIMULATOR_KDM_JSON_FILE)
+ self._seeker.set_fast_pair_scan_enabled(True)
+
+ # TODO(b/214015364): Remove Bluetooth bound on both sides ("Forget device").
+ def teardown_test(self) -> None:
+ self._seeker.set_fast_pair_scan_enabled(False)
+ self._provider.teardown_provider_simulator()
+ self._seeker.dismiss_halfsheet()
+ super().teardown_test()
+
+ def test_seeker_initial_pair_provider(self) -> None:
+ self._seeker.wait_and_assert_halfsheet_showed(
+ timeout_seconds=HALF_SHEET_POPUP_TIMEOUT_SEC,
+ expected_model_id=PROVIDER_SIMULATOR_MODEL_ID)
+ self._seeker.start_pairing()
+ self._seeker.wait_and_assert_account_device(
+ get_account_key_from_provider=self._provider.get_latest_received_account_key,
+ timeout_seconds=MANAGE_ACCOUNT_DEVICE_TIMEOUT_SEC)
diff --git a/nearby/tests/multidevices/host/suite_main.py b/nearby/tests/multidevices/host/suite_main.py
index 406a4f0..4f5d48c 100644
--- a/nearby/tests/multidevices/host/suite_main.py
+++ b/nearby/tests/multidevices/host/suite_main.py
@@ -19,6 +19,7 @@
from mobly import suite_runner
+import initial_pairing_test
import seeker_discover_provider_test
import seeker_show_halfsheet_test
@@ -26,6 +27,7 @@
_TEST_CLASSES_LIST = [
seeker_discover_provider_test.SeekerDiscoverProviderTest,
seeker_show_halfsheet_test.SeekerShowHalfSheetTest,
+ initial_pairing_test.InitialPairingTest,
]
diff --git a/nearby/tests/multidevices/host/test_helper/constants.py b/nearby/tests/multidevices/host/test_helper/constants.py
index 646b428..342be8f 100644
--- a/nearby/tests/multidevices/host/test_helper/constants.py
+++ b/nearby/tests/multidevices/host/test_helper/constants.py
@@ -21,12 +21,14 @@
# Default anti-spoof Key Device Metadata JSON file for data provider at seeker side.
DEFAULT_KDM_JSON_FILE = 'simulator_antispoofkey_devicemeta_json.txt'
-# Time in seconds for events waiting.
+# Time in seconds for events waiting according to Fast Pair certification guidelines:
+# https://developers.google.com/nearby/fast-pair/certification-guideline
SETUP_TIMEOUT_SEC = 5
BECOME_DISCOVERABLE_TIMEOUT_SEC = 10
START_ADVERTISING_TIMEOUT_SEC = 5
-SCAN_TIMEOUT_SEC = 30
-HALF_SHEET_POPUP_TIMEOUT_SEC = 30
+SCAN_TIMEOUT_SEC = 5
+HALF_SHEET_POPUP_TIMEOUT_SEC = 5
+AVERAGE_PAIRING_TIMEOUT_SEC = 12
# The phone to simulate Fast Pair provider (like headphone) needs changes in Android system:
# 1. System permission check removal
diff --git a/nearby/tests/multidevices/host/test_helper/fast_pair_provider_simulator.py b/nearby/tests/multidevices/host/test_helper/fast_pair_provider_simulator.py
index 8a98112..d6484fb 100644
--- a/nearby/tests/multidevices/host/test_helper/fast_pair_provider_simulator.py
+++ b/nearby/tests/multidevices/host/test_helper/fast_pair_provider_simulator.py
@@ -18,6 +18,7 @@
from mobly.controllers import android_device
from mobly.controllers.android_device_lib import snippet_event
import retry
+from typing import Optional
from test_helper import event_helper
@@ -179,3 +180,11 @@
on_received=_on_advertising_mode_change_event_received,
on_waiting=_on_advertising_mode_change_event_waiting,
on_missed=_on_advertising_mode_change_event_missed)
+
+ def get_latest_received_account_key(self) -> Optional[str]:
+ """Gets the latest account key received on the provider side.
+
+ Returns:
+ The account key received at provider side.
+ """
+ return self._ad.fp.getLatestReceivedAccountKey()
diff --git a/nearby/tests/multidevices/host/test_helper/fast_pair_seeker.py b/nearby/tests/multidevices/host/test_helper/fast_pair_seeker.py
index cfdb966..64fc2f2 100644
--- a/nearby/tests/multidevices/host/test_helper/fast_pair_seeker.py
+++ b/nearby/tests/multidevices/host/test_helper/fast_pair_seeker.py
@@ -14,6 +14,9 @@
"""Fast Pair seeker role."""
+import json
+from typing import Callable, Optional
+
from mobly import asserts
from mobly.controllers import android_device
from mobly.controllers.android_device_lib import snippet_event
@@ -26,10 +29,12 @@
# Events reported from the seeker snippet.
ON_PROVIDER_FOUND_EVENT = 'onDiscovered'
+ON_MANAGE_ACCOUNT_DEVICE_EVENT = 'onManageAccountDevice'
# Abbreviations for common use type.
AndroidDevice = android_device.AndroidDevice
JsonObject = utils.JsonObject
+ProviderAccountKeyCallable = Callable[[], Optional[str]]
SnippetEvent = snippet_event.SnippetEvent
wait_for_event = event_helper.wait_callback_event
@@ -41,6 +46,7 @@
self._ad = ad
self._ad.debug_tag = 'MainlineFastPairSeeker'
self._scan_result_callback = None
+ self._pairing_result_callback = None
def load_snippet(self) -> None:
"""Starts the seeker snippet and connects.
@@ -58,18 +64,6 @@
"""Stops the Fast Pair seeker scanning."""
self._ad.fp.stopScan()
- def start_pair(self, model_id: str, address: str) -> None:
- """Starts the Fast Pair seeker pairing.
-
- Args:
- model_id: A 3-byte hex string for seeker side to recognize the provider
- device (ex: 0x00000C).
- address: The BLE mac address of the Fast Pair provider.
- """
- self._ad.log.info('Before calling startPairing')
- self._ad.fp.startPairing(model_id, address)
- self._ad.log.info('After calling startPairing')
-
def wait_and_assert_provider_found(self, timeout_seconds: int,
expected_model_id: str,
expected_ble_mac_address: str) -> None:
@@ -137,8 +131,8 @@
Args:
timeout_seconds: The number of seconds to wait before giving up.
- expected_model_id: The expected model ID of the remote Fast Pair provider
- device.
+ expected_model_id: A 3-byte hex string for seeker side to recognize
+ the remote provider device (ex: 0x00000c).
"""
self._ad.log.info('Waits and asserts the half sheet showed for model id "%s".',
expected_model_id)
@@ -147,3 +141,46 @@
def dismiss_halfsheet(self) -> None:
"""Dismisses the half sheet UI if showed."""
self._ad.fp.dismissHalfSheet()
+
+ def start_pairing(self) -> None:
+ """Starts pairing the provider via "Connect" button on half sheet UI."""
+ self._pairing_result_callback = self._ad.fp.startPairing()
+
+ def wait_and_assert_account_device(
+ self, timeout_seconds: int,
+ get_account_key_from_provider: ProviderAccountKeyCallable) -> None:
+ """Waits and asserts the onHalfSheetShowed event from the seeker.
+
+ Args:
+ timeout_seconds: The number of seconds to wait before giving up.
+ get_account_key_from_provider: The callable to get expected account key from the provider
+ side.
+ """
+
+ def _on_manage_account_device_event_received(manage_account_device_event: SnippetEvent,
+ elapsed_time: int) -> bool:
+ account_key_json_str = manage_account_device_event.data['accountDeviceJsonString']
+ account_key_from_seeker = json.loads(account_key_json_str)['account_key']
+ account_key_from_provider = get_account_key_from_provider()
+ self._ad.log.info('Seeker add an account device with account key "%s" in %d seconds.',
+ account_key_from_seeker, elapsed_time)
+ self._ad.log.info('The latest provider side account key is "%s".',
+ account_key_from_provider)
+ return account_key_from_seeker == account_key_from_provider
+
+ def _on_manage_account_device_event_waiting(elapsed_time: int) -> None:
+ self._ad.log.info(
+ 'Still waiting "%s" event callback from seeker side '
+ 'after %d seconds...', ON_MANAGE_ACCOUNT_DEVICE_EVENT, elapsed_time)
+
+ def _on_manage_account_device_event_missed() -> None:
+ asserts.fail(f'Timed out after {timeout_seconds} seconds waiting for '
+ f'the specific "{ON_MANAGE_ACCOUNT_DEVICE_EVENT}" event.')
+
+ wait_for_event(
+ callback_event_handler=self._pairing_result_callback,
+ event_name=ON_MANAGE_ACCOUNT_DEVICE_EVENT,
+ timeout_seconds=timeout_seconds,
+ on_received=_on_manage_account_device_event_received,
+ on_waiting=_on_manage_account_device_event_waiting,
+ on_missed=_on_manage_account_device_event_missed)
diff --git a/service/src/com/android/server/ConnectivityService.java b/service/src/com/android/server/ConnectivityService.java
index b4cc41a..0528f29 100644
--- a/service/src/com/android/server/ConnectivityService.java
+++ b/service/src/com/android/server/ConnectivityService.java
@@ -2238,6 +2238,13 @@
callingAttributionTag);
}
+ private void redactUnderlyingNetworksForCapabilities(NetworkCapabilities nc, int pid, int uid) {
+ if (nc.getUnderlyingNetworks() != null
+ && !checkNetworkFactoryOrSettingsPermission(pid, uid)) {
+ nc.setUnderlyingNetworks(null);
+ }
+ }
+
@VisibleForTesting
NetworkCapabilities networkCapabilitiesRestrictedForCallerPermissions(
NetworkCapabilities nc, int callerPid, int callerUid) {
@@ -2250,8 +2257,6 @@
if (!checkSettingsPermission(callerPid, callerUid)) {
newNc.setUids(null);
newNc.setSSID(null);
- // TODO: Processes holding NETWORK_FACTORY should be able to see the underlying networks
- newNc.setUnderlyingNetworks(null);
}
if (newNc.getNetworkSpecifier() != null) {
newNc.setNetworkSpecifier(newNc.getNetworkSpecifier().redact());
@@ -2265,6 +2270,7 @@
newNc.setAllowedUids(new ArraySet<>());
newNc.setSubscriptionIds(Collections.emptySet());
}
+ redactUnderlyingNetworksForCapabilities(newNc, callerPid, callerUid);
return newNc;
}
@@ -2877,6 +2883,15 @@
NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK);
}
+ private boolean checkNetworkFactoryOrSettingsPermission(int pid, int uid) {
+ return PERMISSION_GRANTED == mContext.checkPermission(
+ android.Manifest.permission.NETWORK_FACTORY, pid, uid)
+ || PERMISSION_GRANTED == mContext.checkPermission(
+ android.Manifest.permission.NETWORK_SETTINGS, pid, uid)
+ || PERMISSION_GRANTED == mContext.checkPermission(
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, pid, uid);
+ }
+
private boolean checkSettingsPermission() {
return checkAnyPermissionOf(
android.Manifest.permission.NETWORK_SETTINGS,
diff --git a/service/src/com/android/server/connectivity/FullScore.java b/service/src/com/android/server/connectivity/FullScore.java
index 799f46b..b13ba93 100644
--- a/service/src/com/android/server/connectivity/FullScore.java
+++ b/service/src/com/android/server/connectivity/FullScore.java
@@ -29,6 +29,7 @@
import android.net.NetworkCapabilities;
import android.net.NetworkScore;
import android.net.NetworkScore.KeepConnectedReason;
+import android.util.Log;
import android.util.SparseArray;
import com.android.internal.annotations.VisibleForTesting;
@@ -46,6 +47,8 @@
* they are handling a score that had the CS-managed bits set.
*/
public class FullScore {
+ private static final String TAG = FullScore.class.getSimpleName();
+
// This will be removed soon. Do *NOT* depend on it for any new code that is not part of
// a migration.
private final int mLegacyInt;
@@ -126,7 +129,15 @@
@VisibleForTesting
static @NonNull String policyNameOf(final int policy) {
final String name = sMessageNames.get(policy);
- if (name == null) throw new IllegalArgumentException("Unknown policy: " + policy);
+ if (name == null) {
+ // Don't throw here because name might be null due to proguard stripping out the
+ // POLICY_* constants, potentially causing a crash only on user builds because proguard
+ // does not run on userdebug builds.
+ // TODO: make MessageUtils safer by not returning the array and instead storing it
+ // internally and providing a getter (that does not throw) for individual values.
+ Log.wtf(TAG, "Unknown policy: " + policy);
+ return Integer.toString(policy);
+ }
return name.substring("POLICY_".length());
}
diff --git a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
index f007b83..0504973 100644
--- a/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
+++ b/tests/cts/net/src/android/net/cts/NetworkAgentTest.kt
@@ -72,6 +72,7 @@
import android.os.HandlerThread
import android.os.Message
import android.os.SystemClock
+import android.platform.test.annotations.AppModeFull
import android.telephony.TelephonyManager
import android.telephony.data.EpsBearerQosSessionAttributes
import android.util.DebugUtils.valueToString
@@ -106,7 +107,6 @@
import com.android.testutils.TestableNetworkCallback
import com.android.testutils.assertThrows
import org.junit.After
-import org.junit.Assume.assumeFalse
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -946,11 +946,9 @@
return Pair(agent, qosTestSocket!!)
}
+ @AppModeFull(reason = "Instant apps don't have permission to bind sockets.")
@Test
fun testQosCallbackRegisterWithUnregister() {
- // Instant apps can't bind sockets to localhost
- // TODO: use @AppModeFull when supported by DevSdkIgnoreRunner
- assumeFalse(realContext.packageManager.isInstantApp())
val (agent, socket) = setupForQosCallbackTesting()
val qosCallback = TestableQosCallback()
@@ -975,11 +973,9 @@
}
}
+ @AppModeFull(reason = "Instant apps don't have permission to bind sockets.")
@Test
fun testQosCallbackOnQosSession() {
- // Instant apps can't bind sockets to localhost
- // TODO: use @AppModeFull when supported by DevSdkIgnoreRunner
- assumeFalse(realContext.packageManager.isInstantApp())
val (agent, socket) = setupForQosCallbackTesting()
val qosCallback = TestableQosCallback()
Executors.newSingleThreadExecutor().let { executor ->
@@ -1023,11 +1019,9 @@
}
}
+ @AppModeFull(reason = "Instant apps don't have permission to bind sockets.")
@Test
fun testQosCallbackOnError() {
- // Instant apps can't bind sockets to localhost
- // TODO: use @AppModeFull when supported by DevSdkIgnoreRunner
- assumeFalse(realContext.packageManager.isInstantApp())
val (agent, socket) = setupForQosCallbackTesting()
val qosCallback = TestableQosCallback()
Executors.newSingleThreadExecutor().let { executor ->
@@ -1064,11 +1058,9 @@
}
}
+ @AppModeFull(reason = "Instant apps don't have permission to bind sockets.")
@Test
fun testQosCallbackIdsAreMappedCorrectly() {
- // Instant apps can't bind sockets to localhost
- // TODO: use @AppModeFull when supported by DevSdkIgnoreRunner
- assumeFalse(realContext.packageManager.isInstantApp())
val (agent, socket) = setupForQosCallbackTesting()
val qosCallback1 = TestableQosCallback()
val qosCallback2 = TestableQosCallback()
@@ -1107,11 +1099,9 @@
}
}
+ @AppModeFull(reason = "Instant apps don't have permission to bind sockets.")
@Test
fun testQosCallbackWhenNetworkReleased() {
- // Instant apps can't bind sockets to localhost
- // TODO: use @AppModeFull when supported by DevSdkIgnoreRunner
- assumeFalse(realContext.packageManager.isInstantApp())
val (agent, socket) = setupForQosCallbackTesting()
Executors.newSingleThreadExecutor().let { executor ->
try {
@@ -1151,6 +1141,7 @@
)
}
+ @AppModeFull(reason = "Instant apps don't have permission to bind sockets.")
@Test
fun testUnregisterAfterReplacement() {
// Keeps an eye on all test networks.
diff --git a/tests/unit/java/com/android/server/connectivity/FullScoreTest.kt b/tests/unit/java/com/android/server/connectivity/FullScoreTest.kt
index e7f6245..c03a9cd 100644
--- a/tests/unit/java/com/android/server/connectivity/FullScoreTest.kt
+++ b/tests/unit/java/com/android/server/connectivity/FullScoreTest.kt
@@ -22,6 +22,7 @@
import android.os.Build
import android.text.TextUtils
import android.util.ArraySet
+import android.util.Log
import androidx.test.filters.SmallTest
import com.android.server.connectivity.FullScore.MAX_CS_MANAGED_POLICY
import com.android.server.connectivity.FullScore.POLICY_ACCEPT_UNVALIDATED
@@ -32,11 +33,12 @@
import com.android.server.connectivity.FullScore.POLICY_IS_VPN
import com.android.testutils.DevSdkIgnoreRule
import com.android.testutils.DevSdkIgnoreRunner
+import org.junit.After
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import kotlin.reflect.full.staticProperties
import kotlin.test.assertEquals
-import kotlin.test.assertFailsWith
import kotlin.test.assertFalse
import kotlin.test.assertTrue
@@ -63,6 +65,23 @@
return mixInScore(nc, nac, validated, false /* yieldToBadWifi */, destroyed)
}
+ private val TAG = this::class.simpleName
+
+ private var wtfHandler: Log.TerribleFailureHandler? = null
+
+ @Before
+ fun setUp() {
+ // policyNameOf will call Log.wtf if passed an invalid policy.
+ wtfHandler = Log.setWtfHandler() { tagString, what, system ->
+ Log.d(TAG, "WTF captured, ignoring: $tagString $what")
+ }
+ }
+
+ @After
+ fun tearDown() {
+ Log.setWtfHandler(wtfHandler)
+ }
+
@Test
fun testGetLegacyInt() {
val ns = FullScore(50, 0L /* policy */, KEEP_CONNECTED_NONE)
@@ -101,10 +120,9 @@
assertFalse(foundNames.contains(name))
foundNames.add(name)
}
- assertFailsWith<IllegalArgumentException> {
- FullScore.policyNameOf(MAX_CS_MANAGED_POLICY + 1)
- }
assertEquals("IS_UNMETERED", FullScore.policyNameOf(POLICY_IS_UNMETERED))
+ val invalidPolicy = MAX_CS_MANAGED_POLICY + 1
+ assertEquals(Integer.toString(invalidPolicy), FullScore.policyNameOf(invalidPolicy))
}
fun getAllPolicies() = Regex("POLICY_.*").let { nameRegex ->