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.cts; 18 19 import static android.Manifest.permission.BLUETOOTH_PRIVILEGED; 20 import static android.Manifest.permission.READ_DEVICE_CONFIG; 21 import static android.Manifest.permission.WRITE_DEVICE_CONFIG; 22 import static android.nearby.PresenceCredential.IDENTITY_TYPE_PRIVATE; 23 import static android.nearby.ScanCallback.ERROR_UNSUPPORTED; 24 25 import static com.google.common.truth.Truth.assertThat; 26 27 import static org.junit.Assert.assertThrows; 28 29 import android.app.UiAutomation; 30 import android.bluetooth.BluetoothAdapter; 31 import android.bluetooth.BluetoothManager; 32 import android.bluetooth.cts.BTAdapterUtils; 33 import android.content.Context; 34 import android.nearby.BroadcastCallback; 35 import android.nearby.BroadcastRequest; 36 import android.nearby.NearbyDevice; 37 import android.nearby.NearbyManager; 38 import android.nearby.OffloadCapability; 39 import android.nearby.PresenceBroadcastRequest; 40 import android.nearby.PresenceDevice; 41 import android.nearby.PrivateCredential; 42 import android.nearby.ScanCallback; 43 import android.nearby.ScanRequest; 44 import android.os.Build; 45 import android.provider.DeviceConfig; 46 47 import androidx.annotation.NonNull; 48 import androidx.annotation.RequiresApi; 49 import androidx.test.InstrumentationRegistry; 50 import androidx.test.ext.junit.runners.AndroidJUnit4; 51 import androidx.test.filters.SdkSuppress; 52 53 import com.android.modules.utils.build.SdkLevel; 54 55 import org.junit.Before; 56 import org.junit.Test; 57 import org.junit.runner.RunWith; 58 59 import java.util.Collections; 60 import java.util.concurrent.CountDownLatch; 61 import java.util.concurrent.Executor; 62 import java.util.concurrent.Executors; 63 import java.util.concurrent.TimeUnit; 64 import java.util.function.Consumer; 65 66 /** 67 * TODO(b/215435939) This class doesn't include any logic yet. Because SELinux denies access to 68 * NearbyManager. 69 */ 70 @RunWith(AndroidJUnit4.class) 71 @RequiresApi(Build.VERSION_CODES.TIRAMISU) 72 public class NearbyManagerTest { 73 private static final byte[] SALT = new byte[]{1, 2}; 74 private static final byte[] SECRET_ID = new byte[]{1, 2, 3, 4}; 75 private static final byte[] META_DATA_ENCRYPTION_KEY = new byte[14]; 76 private static final byte[] AUTHENTICITY_KEY = new byte[]{0, 1, 1, 1}; 77 private static final String DEVICE_NAME = "test_device"; 78 private static final int BLE_MEDIUM = 1; 79 80 private Context mContext; 81 private NearbyManager mNearbyManager; 82 private UiAutomation mUiAutomation = 83 InstrumentationRegistry.getInstrumentation().getUiAutomation(); 84 85 private ScanRequest mScanRequest = new ScanRequest.Builder() 86 .setScanType(ScanRequest.SCAN_TYPE_FAST_PAIR) 87 .setScanMode(ScanRequest.SCAN_MODE_LOW_LATENCY) 88 .setBleEnabled(true) 89 .build(); 90 private PresenceDevice.Builder mBuilder = 91 new PresenceDevice.Builder("deviceId", SALT, SECRET_ID, META_DATA_ENCRYPTION_KEY); 92 93 private ScanCallback mScanCallback = new ScanCallback() { 94 @Override 95 public void onDiscovered(@NonNull NearbyDevice device) { 96 } 97 98 @Override 99 public void onUpdated(@NonNull NearbyDevice device) { 100 } 101 102 @Override 103 public void onLost(@NonNull NearbyDevice device) { 104 } 105 106 @Override 107 public void onError(int errorCode) { 108 } 109 }; 110 111 private static final Executor EXECUTOR = Executors.newSingleThreadExecutor(); 112 113 @Before setUp()114 public void setUp() { 115 mUiAutomation.adoptShellPermissionIdentity(READ_DEVICE_CONFIG, WRITE_DEVICE_CONFIG, 116 BLUETOOTH_PRIVILEGED); 117 String nameSpace = SdkLevel.isAtLeastU() ? DeviceConfig.NAMESPACE_NEARBY 118 : DeviceConfig.NAMESPACE_TETHERING; 119 DeviceConfig.setProperty(nameSpace, 120 "nearby_enable_presence_broadcast_legacy", 121 "true", false); 122 123 mContext = InstrumentationRegistry.getContext(); 124 mNearbyManager = mContext.getSystemService(NearbyManager.class); 125 126 enableBluetooth(); 127 } 128 129 @Test 130 @SdkSuppress(minSdkVersion = 32, codeName = "T") test_startAndStopScan()131 public void test_startAndStopScan() { 132 mNearbyManager.startScan(mScanRequest, EXECUTOR, mScanCallback); 133 mNearbyManager.stopScan(mScanCallback); 134 } 135 136 @Test 137 @SdkSuppress(minSdkVersion = 32, codeName = "T") test_startScan_noPrivilegedPermission()138 public void test_startScan_noPrivilegedPermission() { 139 mUiAutomation.dropShellPermissionIdentity(); 140 assertThrows(SecurityException.class, () -> mNearbyManager 141 .startScan(mScanRequest, EXECUTOR, mScanCallback)); 142 } 143 144 @Test 145 @SdkSuppress(minSdkVersion = 32, codeName = "T") test_stopScan_noPrivilegedPermission()146 public void test_stopScan_noPrivilegedPermission() { 147 mNearbyManager.startScan(mScanRequest, EXECUTOR, mScanCallback); 148 mUiAutomation.dropShellPermissionIdentity(); 149 assertThrows(SecurityException.class, () -> mNearbyManager.stopScan(mScanCallback)); 150 } 151 152 @Test 153 @SdkSuppress(minSdkVersion = 32, codeName = "T") testStartStopBroadcast()154 public void testStartStopBroadcast() throws InterruptedException { 155 PrivateCredential credential = new PrivateCredential.Builder(SECRET_ID, AUTHENTICITY_KEY, 156 META_DATA_ENCRYPTION_KEY, DEVICE_NAME) 157 .setIdentityType(IDENTITY_TYPE_PRIVATE) 158 .build(); 159 BroadcastRequest broadcastRequest = 160 new PresenceBroadcastRequest.Builder( 161 Collections.singletonList(BLE_MEDIUM), SALT, credential) 162 .addAction(123) 163 .build(); 164 165 CountDownLatch latch = new CountDownLatch(1); 166 BroadcastCallback callback = status -> { 167 latch.countDown(); 168 assertThat(status).isEqualTo(BroadcastCallback.STATUS_OK); 169 }; 170 mNearbyManager.startBroadcast(broadcastRequest, Executors.newSingleThreadExecutor(), 171 callback); 172 latch.await(10, TimeUnit.SECONDS); 173 mNearbyManager.stopBroadcast(callback); 174 } 175 176 @Test 177 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) queryOffloadScanSupport()178 public void queryOffloadScanSupport() { 179 OffloadCallback callback = new OffloadCallback(); 180 mNearbyManager.queryOffloadCapability(EXECUTOR, callback); 181 } 182 183 @Test 184 @SdkSuppress(minSdkVersion = Build.VERSION_CODES.UPSIDE_DOWN_CAKE) testAllCallbackMethodsExits()185 public void testAllCallbackMethodsExits() { 186 mScanCallback.onDiscovered(mBuilder.setRssi(-10).build()); 187 mScanCallback.onUpdated(mBuilder.setRssi(-5).build()); 188 mScanCallback.onLost(mBuilder.setRssi(-8).build()); 189 mScanCallback.onError(ERROR_UNSUPPORTED); 190 } 191 enableBluetooth()192 private void enableBluetooth() { 193 BluetoothManager manager = mContext.getSystemService(BluetoothManager.class); 194 BluetoothAdapter bluetoothAdapter = manager.getAdapter(); 195 if (!bluetoothAdapter.isEnabled()) { 196 assertThat(BTAdapterUtils.enableAdapter(bluetoothAdapter, mContext)).isTrue(); 197 } 198 } 199 200 private static class OffloadCallback implements Consumer<OffloadCapability> { 201 @Override accept(OffloadCapability aBoolean)202 public void accept(OffloadCapability aBoolean) { 203 // no-op for now 204 } 205 } 206 } 207