1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.nearby.multidevices.fastpair.seeker 18 19 import android.content.Context 20 import android.nearby.FastPairDeviceMetadata 21 import android.nearby.NearbyManager 22 import android.nearby.ScanCallback 23 import android.nearby.ScanRequest 24 import android.nearby.fastpair.seeker.FAKE_TEST_ACCOUNT_NAME 25 import android.nearby.integration.ui.CheckNearbyHalfSheetUiTest 26 import android.nearby.integration.ui.DismissNearbyHalfSheetUiTest 27 import android.nearby.integration.ui.PairByNearbyHalfSheetUiTest 28 import android.nearby.multidevices.fastpair.seeker.data.FastPairTestDataManager 29 import android.nearby.multidevices.fastpair.seeker.events.PairingCallbackEvents 30 import android.nearby.multidevices.fastpair.seeker.events.ScanCallbackEvents 31 import android.provider.Settings 32 import androidx.test.core.app.ApplicationProvider 33 import com.google.android.mobly.snippet.Snippet 34 import com.google.android.mobly.snippet.rpc.AsyncRpc 35 import com.google.android.mobly.snippet.rpc.Rpc 36 import com.google.android.mobly.snippet.util.Log 37 38 /** Expose Mobly RPC methods for Python side to test fast pair seeker role. */ 39 class FastPairSeekerSnippet : Snippet { 40 private val appContext = ApplicationProvider.getApplicationContext<Context>() 41 private val nearbyManager = appContext.getSystemService(Context.NEARBY_SERVICE) as NearbyManager 42 private val fastPairTestDataManager = FastPairTestDataManager(appContext) 43 private lateinit var scanCallback: ScanCallback 44 45 /** 46 * Starts scanning as a Fast Pair seeker to find provider devices. 47 * 48 * @param callbackId the callback ID corresponding to the {@link FastPairSeekerSnippet#startScan} 49 * call that started the scanning. 50 */ 51 @AsyncRpc(description = "Starts scanning as Fast Pair seeker to find provider devices.") startScannull52 fun startScan(callbackId: String) { 53 val scanRequest = ScanRequest.Builder() 54 .setScanMode(ScanRequest.SCAN_MODE_LOW_LATENCY) 55 .setScanType(ScanRequest.SCAN_TYPE_FAST_PAIR) 56 .setBleEnabled(true) 57 .build() 58 scanCallback = ScanCallbackEvents(callbackId) 59 60 Log.i("Start Fast Pair scanning via BLE...") 61 nearbyManager.startScan(scanRequest, /* executor */ { it.run() }, scanCallback) 62 } 63 64 /** Stops the Fast Pair seeker scanning. */ 65 @Rpc(description = "Stops the Fast Pair seeker scanning.") stopScannull66 fun stopScan() { 67 Log.i("Stop Fast Pair scanning.") 68 nearbyManager.stopScan(scanCallback) 69 } 70 71 /** Waits and asserts the HalfSheet showed for Fast Pair pairing. 72 * 73 * @param modelId the expected model id to be associated with the HalfSheet. 74 * @param timeout the number of seconds to wait before giving up. 75 */ 76 @Rpc(description = "Waits the HalfSheet showed for Fast Pair pairing.") waitAndAssertHalfSheetShowednull77 fun waitAndAssertHalfSheetShowed(modelId: String, timeout: Int) { 78 Log.i("Waits and asserts the HalfSheet showed for Fast Pair model $modelId.") 79 80 val deviceMetadata: FastPairDeviceMetadata = 81 fastPairTestDataManager.testDataCache.getFastPairDeviceMetadata(modelId) 82 ?: throw IllegalArgumentException( 83 "Can't find $modelId-FastPairAntispoofKeyDeviceMetadata pair in " + 84 "FastPairTestDataCache." 85 ) 86 val deviceName = deviceMetadata.name!! 87 val initialPairingDescriptionTemplateText = deviceMetadata.initialPairingDescription!! 88 89 CheckNearbyHalfSheetUiTest().apply { 90 updateTestArguments( 91 waitHalfSheetPopupTimeoutSeconds = timeout, 92 halfSheetTitleText = deviceName, 93 halfSheetSubtitleText = initialPairingDescriptionTemplateText.format( 94 deviceName, 95 FAKE_TEST_ACCOUNT_NAME 96 ) 97 ) 98 checkNearbyHalfSheetUi() 99 } 100 } 101 102 /** Puts a model id to FastPairAntispoofKeyDeviceMetadata pair into test data cache. 103 * 104 * @param modelId a string of model id to be associated with. 105 * @param json a string of FastPairAntispoofKeyDeviceMetadata JSON object. 106 */ 107 @Rpc( 108 description = 109 "Puts a model id to FastPairAntispoofKeyDeviceMetadata pair into test data cache." 110 ) putAntispoofKeyDeviceMetadatanull111 fun putAntispoofKeyDeviceMetadata(modelId: String, json: String) { 112 Log.i("Puts a model id to FastPairAntispoofKeyDeviceMetadata pair into test data cache.") 113 fastPairTestDataManager.sendAntispoofKeyDeviceMetadata(modelId, json) 114 } 115 116 /** Puts an array of FastPairAccountKeyDeviceMetadata into test data cache. 117 * 118 * @param json a string of FastPairAccountKeyDeviceMetadata JSON array. 119 */ 120 @Rpc(description = "Puts an array of FastPairAccountKeyDeviceMetadata into test data cache.") putAccountKeyDeviceMetadatanull121 fun putAccountKeyDeviceMetadata(json: String) { 122 Log.i("Puts an array of FastPairAccountKeyDeviceMetadata into test data cache.") 123 fastPairTestDataManager.sendAccountKeyDeviceMetadataJsonArray(json) 124 } 125 126 /** Dumps all FastPairAccountKeyDeviceMetadata from the test data cache. */ 127 @Rpc(description = "Dumps all FastPairAccountKeyDeviceMetadata from the test data cache.") dumpAccountKeyDeviceMetadatanull128 fun dumpAccountKeyDeviceMetadata(): String { 129 Log.i("Dumps all FastPairAccountKeyDeviceMetadata from the test data cache.") 130 return fastPairTestDataManager.testDataCache.dumpAccountKeyDeviceMetadataListAsJson() 131 } 132 133 /** Writes into {@link Settings} whether Fast Pair scan is enabled. 134 * 135 * @param enable whether the Fast Pair scan should be enabled. 136 */ 137 @Rpc(description = "Writes into Settings whether Fast Pair scan is enabled.") setFastPairScanEnablednull138 fun setFastPairScanEnabled(enable: Boolean) { 139 Log.i("Writes into Settings whether Fast Pair scan is enabled.") 140 // TODO(b/228406038): Change back to use NearbyManager.setFastPairScanEnabled once un-hide. 141 val resolver = appContext.contentResolver 142 Settings.Secure.putInt(resolver, "fast_pair_scan_enabled", if (enable) 1 else 0) 143 } 144 145 /** Dismisses the half sheet UI if showed. */ 146 @Rpc(description = "Dismisses the half sheet UI if showed.") dismissHalfSheetnull147 fun dismissHalfSheet() { 148 Log.i("Dismisses the half sheet UI if showed.") 149 150 DismissNearbyHalfSheetUiTest().dismissHalfSheet() 151 } 152 153 /** Starts pairing by interacting with half sheet UI. 154 * 155 * @param callbackId the callback ID corresponding to the 156 * {@link FastPairSeekerSnippet#startPairing} call that started the pairing. 157 */ 158 @AsyncRpc(description = "Starts pairing by interacting with half sheet UI.") startPairingnull159 fun startPairing(callbackId: String) { 160 Log.i("Starts pairing by interacting with half sheet UI.") 161 162 PairByNearbyHalfSheetUiTest().clickConnectButton() 163 fastPairTestDataManager.registerDataReceiveListener(PairingCallbackEvents(callbackId)) 164 } 165 166 /** Invokes when the snippet runner shutting down. */ shutdownnull167 override fun shutdown() { 168 super.shutdown() 169 170 Log.i("Resets the Fast Pair test data cache.") 171 fastPairTestDataManager.unregisterDataReceiveListener() 172 fastPairTestDataManager.sendResetCache() 173 } 174 }