1 /*
2  * Copyright (C) 2021 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.bluetooth.cts;
18 
19 import static android.Manifest.permission.BLUETOOTH_CONNECT;
20 import static android.Manifest.permission.BLUETOOTH_PRIVILEGED;
21 import static android.bluetooth.BluetoothDevice.ACCESS_ALLOWED;
22 import static android.bluetooth.BluetoothDevice.ACCESS_REJECTED;
23 import static android.bluetooth.BluetoothDevice.ACCESS_UNKNOWN;
24 import static android.bluetooth.BluetoothDevice.TRANSPORT_AUTO;
25 import static android.bluetooth.BluetoothDevice.TRANSPORT_BREDR;
26 import static android.bluetooth.BluetoothDevice.TRANSPORT_LE;
27 
28 import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
29 
30 import static org.junit.Assert.assertEquals;
31 import static org.junit.Assert.assertFalse;
32 import static org.junit.Assert.assertNull;
33 import static org.junit.Assert.assertThrows;
34 import static org.junit.Assert.assertTrue;
35 import static org.junit.Assume.assumeTrue;
36 
37 import android.app.UiAutomation;
38 import android.bluetooth.BluetoothAdapter;
39 import android.bluetooth.BluetoothDevice;
40 import android.bluetooth.BluetoothManager;
41 import android.bluetooth.BluetoothProfile;
42 import android.bluetooth.BluetoothSinkAudioPolicy;
43 import android.bluetooth.BluetoothSocket;
44 import android.bluetooth.BluetoothSocketException;
45 import android.bluetooth.BluetoothStatusCodes;
46 import android.bluetooth.OobData;
47 import android.content.AttributionSource;
48 import android.content.Context;
49 import android.content.pm.PackageManager;
50 import android.platform.test.annotations.RequiresFlagsEnabled;
51 import android.platform.test.flag.junit.CheckFlagsRule;
52 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
53 
54 import androidx.test.ext.junit.runners.AndroidJUnit4;
55 import androidx.test.filters.LargeTest;
56 import androidx.test.platform.app.InstrumentationRegistry;
57 
58 import com.android.bluetooth.flags.Flags;
59 
60 import org.junit.After;
61 import org.junit.Before;
62 import org.junit.Rule;
63 import org.junit.Test;
64 import org.junit.runner.RunWith;
65 
66 import java.io.IOException;
67 import java.io.UnsupportedEncodingException;
68 import java.util.UUID;
69 
70 @RunWith(AndroidJUnit4.class)
71 @LargeTest
72 public class BluetoothDeviceTest {
73 
74     private Context mContext;
75     private boolean mHasBluetooth;
76     private boolean mHasCompanionDevice;
77     private BluetoothAdapter mAdapter;
78     private UiAutomation mUiAutomation;;
79 
80     private final String mFakeDeviceAddress = "00:11:22:AA:BB:CC";
81     private BluetoothDevice mFakeDevice;
82     private int mFakePsm = 100;
83     private UUID mFakeUuid = UUID.fromString("0000111E-0000-1000-8000-00805F9B34FB");
84 
85     @Rule
86     public final CheckFlagsRule mCheckFlagsRule =
87             DeviceFlagsValueProvider.createCheckFlagsRule();
88 
89     @Before
setUp()90     public void setUp() throws Exception {
91         mContext = InstrumentationRegistry.getInstrumentation().getContext();
92 
93         mHasBluetooth = mContext.getPackageManager().hasSystemFeature(
94                 PackageManager.FEATURE_BLUETOOTH);
95 
96         mHasCompanionDevice = mContext.getPackageManager().hasSystemFeature(
97                 PackageManager.FEATURE_COMPANION_DEVICE_SETUP);
98 
99         if (mHasBluetooth && mHasCompanionDevice) {
100             BluetoothManager manager = mContext.getSystemService(BluetoothManager.class);
101             mAdapter = manager.getAdapter();
102             mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
103             mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
104             assertTrue(BTAdapterUtils.enableAdapter(mAdapter, mContext));
105             mFakeDevice = mAdapter.getRemoteDevice(mFakeDeviceAddress);
106         }
107     }
108 
109     @After
tearDown()110     public void tearDown() throws Exception {
111         if (mHasBluetooth && mHasCompanionDevice) {
112             mAdapter = null;
113             mUiAutomation.dropShellPermissionIdentity();
114         }
115     }
116 
117     @Test
setAlias_getAlias()118     public void setAlias_getAlias() {
119         // Skip the test if bluetooth or companion device are not present.
120         assumeTrue(mHasBluetooth && mHasCompanionDevice);
121 
122         int userId = mContext.getUser().getIdentifier();
123         String packageName = mContext.getOpPackageName();
124 
125         AttributionSource source = AttributionSource.myAttributionSource();
126         assertEquals("android.bluetooth.cts", source.getPackageName());
127 
128         // Verifies that when there is no alias, we return the device name
129         assertNull(mFakeDevice.getAlias());
130 
131         assertThrows(IllegalArgumentException.class, () -> mFakeDevice.setAlias(""));
132 
133         String testDeviceAlias = "Test Device Alias";
134 
135         // This should throw a SecurityException because there is no CDM association
136         assertThrows("BluetoothDevice.setAlias without"
137                 + " a CDM association or BLUETOOTH_PRIVILEGED permission",
138                 SecurityException.class, () -> mFakeDevice.setAlias(testDeviceAlias));
139 
140         runShellCommand(String.format(
141                 "cmd companiondevice associate %d %s %s", userId, packageName, mFakeDeviceAddress));
142         String output = runShellCommand("dumpsys companiondevice");
143         assertTrue("Package name missing from output", output.contains(packageName));
144         assertTrue("Device address missing from output",
145                 output.toLowerCase().contains(mFakeDeviceAddress.toLowerCase()));
146 
147         // Takes time to update the CDM cache, so sleep to ensure the association is cached
148         try {
149             Thread.sleep(1000);
150         } catch (Exception e) {
151             e.printStackTrace();
152         }
153 
154         /*
155          * Device properties don't exist for non-existent BluetoothDevice, so calling setAlias with
156          * permissions should return false
157          */
158         assertEquals(BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED, mFakeDevice
159                 .setAlias(testDeviceAlias));
160         runShellCommand(String.format("cmd companiondevice disassociate %d %s %s", userId,
161                     packageName, mFakeDeviceAddress));
162 
163         assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
164         assertNull(mFakeDevice.getAlias());
165         assertEquals(BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
166                 mFakeDevice.setAlias(testDeviceAlias));
167     }
168 
169     @Test
170     @RequiresFlagsEnabled(Flags.FLAG_GET_ADDRESS_TYPE_API)
getAddressType()171     public void getAddressType() {
172         // Skip the test if bluetooth or companion device are not present.
173         assumeTrue(mHasBluetooth && mHasCompanionDevice);
174 
175         assertEquals(BluetoothDevice.ADDRESS_TYPE_PUBLIC, mFakeDevice.getAddressType());
176     }
177 
178     @Test
getIdentityAddress()179     public void getIdentityAddress() {
180         // Skip the test if bluetooth or companion device are not present.
181         assumeTrue(mHasBluetooth && mHasCompanionDevice);
182 
183         // This should throw a SecurityException because no BLUETOOTH_PRIVILEGED permission
184         assertThrows("No BLUETOOTH_PRIVILEGED permission", SecurityException.class,
185                 () -> mFakeDevice.getIdentityAddress());
186     }
187 
188     @Test
getConnectionHandle()189     public void getConnectionHandle() {
190         // Skip the test if bluetooth or companion device are not present.
191         assumeTrue(mHasBluetooth && mHasCompanionDevice);
192 
193         // This should throw a SecurityException because no BLUETOOTH_PRIVILEGED permission
194         assertThrows("No BLUETOOTH_PRIVILEGED permission", SecurityException.class,
195                 () -> mFakeDevice.getConnectionHandle(TRANSPORT_LE));
196 
197         // but it should work after we get the permission
198         mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED);
199         var handle = mFakeDevice.getConnectionHandle(TRANSPORT_LE);
200         assertEquals(handle, BluetoothDevice.ERROR);
201     }
202 
203     @Test
getAnonymizedAddress()204     public void getAnonymizedAddress() {
205         // Skip the test if bluetooth or companion device are not present.
206         assumeTrue(mHasBluetooth && mHasCompanionDevice);
207 
208         assertEquals("XX:XX:XX:XX:BB:CC", mFakeDevice.getAnonymizedAddress());
209     }
210 
211     @Test
getBatteryLevel()212     public void getBatteryLevel() {
213         // Skip the test if bluetooth or companion device are not present.
214         assumeTrue(mHasBluetooth && mHasCompanionDevice);
215 
216         assertEquals(BluetoothDevice.BATTERY_LEVEL_UNKNOWN, mFakeDevice.getBatteryLevel());
217 
218         mUiAutomation.dropShellPermissionIdentity();
219         assertThrows(SecurityException.class, () -> mFakeDevice.getBatteryLevel());
220         mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
221 
222         assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
223         assertEquals(BluetoothDevice.BATTERY_LEVEL_BLUETOOTH_OFF, mFakeDevice.getBatteryLevel());
224     }
225 
226     @Test
isBondingInitiatedLocally()227     public void isBondingInitiatedLocally() {
228         // Skip the test if bluetooth or companion device are not present.
229         assumeTrue(mHasBluetooth && mHasCompanionDevice);
230 
231         assertFalse(mFakeDevice.isBondingInitiatedLocally());
232 
233         mUiAutomation.dropShellPermissionIdentity();
234         assertThrows(SecurityException.class, () -> mFakeDevice.isBondingInitiatedLocally());
235         mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
236 
237         assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
238         assertFalse(mFakeDevice.isBondingInitiatedLocally());
239     }
240 
241     @Test
prepareToEnterProcess()242     public void prepareToEnterProcess() {
243         // Skip the test if bluetooth or companion device are not present.
244         assumeTrue(mHasBluetooth && mHasCompanionDevice);
245 
246         mFakeDevice.prepareToEnterProcess(null);
247     }
248 
249     @Test
setPin()250     public void setPin() {
251         // Skip the test if bluetooth or companion device are not present.
252         assumeTrue(mHasBluetooth && mHasCompanionDevice);
253 
254         assertFalse(mFakeDevice.setPin((String) null));
255         assertFalse(mFakeDevice.setPin("12345678901234567")); // check PIN too big
256 
257         assertFalse(mFakeDevice.setPin("123456")); //device is not bonding
258 
259         mUiAutomation.dropShellPermissionIdentity();
260         assertThrows(SecurityException.class, () -> mFakeDevice.setPin("123456"));
261         mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
262 
263         assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
264         assertFalse(mFakeDevice.setPin("123456"));
265     }
266 
267     @Test
connect_disconnect()268     public void connect_disconnect() {
269         // Skip the test if bluetooth or companion device are not present.
270         assumeTrue(mHasBluetooth && mHasCompanionDevice);
271 
272         // This should throw a SecurityException because no BLUETOOTH_PRIVILEGED permission
273         assertThrows(SecurityException.class, () -> mFakeDevice.connect());
274         assertThrows(SecurityException.class, () -> mFakeDevice.disconnect());
275     }
276 
277     @Test
cancelBondProcess()278     public void cancelBondProcess() {
279         // Skip the test if bluetooth or companion device are not present.
280         assumeTrue(mHasBluetooth && mHasCompanionDevice);
281 
282         mUiAutomation.dropShellPermissionIdentity();
283         assertThrows(SecurityException.class, () -> mFakeDevice.cancelBondProcess());
284         mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
285 
286         assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
287         assertFalse(mFakeDevice.cancelBondProcess());
288     }
289 
290     @Test
createBond()291     public void createBond() {
292         // Skip the test if bluetooth or companion device are not present.
293         assumeTrue(mHasBluetooth && mHasCompanionDevice);
294 
295         mUiAutomation.dropShellPermissionIdentity();
296         assertThrows(SecurityException.class, () -> mFakeDevice.createBond(TRANSPORT_AUTO));
297         mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
298 
299         assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
300         assertFalse(mFakeDevice.createBond(TRANSPORT_AUTO));
301     }
302 
303     @Test
createBondOutOfBand()304     public void createBondOutOfBand() {
305         // Skip the test if bluetooth or companion device are not present.
306         assumeTrue(mHasBluetooth && mHasCompanionDevice);
307 
308         OobData data = new OobData.ClassicBuilder(
309                 new byte[16], new byte[2], new byte[7]).build();
310 
311         assertThrows(IllegalArgumentException.class, () -> mFakeDevice.createBondOutOfBand(
312                 TRANSPORT_AUTO, null, null));
313 
314         mUiAutomation.dropShellPermissionIdentity();
315         assertThrows(SecurityException.class, () -> mFakeDevice
316                 .createBondOutOfBand(TRANSPORT_AUTO, data, null));
317         mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
318     }
319 
320     @Test
getUuids()321     public void getUuids() {
322         // Skip the test if bluetooth or companion device are not present.
323         assumeTrue(mHasBluetooth && mHasCompanionDevice);
324 
325         assertNull(mFakeDevice.getUuids());
326         mUiAutomation.dropShellPermissionIdentity();
327         assertThrows(SecurityException.class, () -> mFakeDevice.getUuids());
328         mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
329 
330         assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
331         assertNull(mFakeDevice.getUuids());
332     }
333 
334     @Test
isEncrypted()335     public void isEncrypted() {
336         // Skip the test if bluetooth or companion device are not present.
337         assumeTrue(mHasBluetooth && mHasCompanionDevice);
338 
339         //Device is not connected
340         assertFalse(mFakeDevice.isEncrypted());
341 
342         mUiAutomation.dropShellPermissionIdentity();
343         assertThrows(SecurityException.class, () -> mFakeDevice.isEncrypted());
344         mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
345 
346         assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
347         assertFalse(mFakeDevice.isEncrypted());
348     }
349 
350     @Test
removeBond()351     public void removeBond() {
352         // Skip the test if bluetooth or companion device are not present.
353         assumeTrue(mHasBluetooth && mHasCompanionDevice);
354 
355         //Device is not bonded
356         assertFalse(mFakeDevice.removeBond());
357 
358         mUiAutomation.dropShellPermissionIdentity();
359         assertThrows(SecurityException.class, () -> mFakeDevice.removeBond());
360         mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
361 
362         assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
363         assertFalse(mFakeDevice.removeBond());
364     }
365 
366     @Test
setPinByteArray()367     public void setPinByteArray() {
368         // Skip the test if bluetooth or companion device are not present.
369         assumeTrue(mHasBluetooth && mHasCompanionDevice);
370 
371         assertThrows(NullPointerException.class, () -> mFakeDevice.setPin((byte[]) null));
372 
373         // check PIN too big
374         assertFalse(mFakeDevice.setPin(convertPinToBytes("12345678901234567")));
375         assertFalse(mFakeDevice.setPin(convertPinToBytes("123456"))); // device is not bonding
376 
377         mUiAutomation.dropShellPermissionIdentity();
378         assertThrows(SecurityException.class, () -> mFakeDevice
379                 .setPin(convertPinToBytes("123456")));
380         mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
381 
382         assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
383         assertFalse(mFakeDevice.setPin(convertPinToBytes("123456")));
384     }
385 
386     @Test
connectGatt()387     public void connectGatt() {
388         // Skip the test if bluetooth or companion device are not present.
389         assumeTrue(mHasBluetooth && mHasCompanionDevice);
390 
391         assertThrows(NullPointerException.class, () -> mFakeDevice
392                 .connectGatt(mContext, false, null,
393                 TRANSPORT_AUTO, BluetoothDevice.PHY_LE_1M_MASK));
394 
395         assertThrows(NullPointerException.class, () ->
396                 mFakeDevice.connectGatt(mContext, false, null,
397                 TRANSPORT_AUTO, BluetoothDevice.PHY_LE_1M_MASK, null));
398     }
399 
400     @Test
fetchUuidsWithSdp()401     public void fetchUuidsWithSdp() {
402         // Skip the test if bluetooth or companion device are not present.
403         assumeTrue(mHasBluetooth && mHasCompanionDevice);
404 
405         // TRANSPORT_AUTO doesn't need BLUETOOTH_PRIVILEGED permission
406         assertTrue(mFakeDevice.fetchUuidsWithSdp(TRANSPORT_AUTO));
407 
408         // This should throw a SecurityException because no BLUETOOTH_PRIVILEGED permission
409         assertThrows(SecurityException.class, () -> mFakeDevice.fetchUuidsWithSdp(TRANSPORT_BREDR));
410         assertThrows(SecurityException.class, () -> mFakeDevice.fetchUuidsWithSdp(TRANSPORT_LE));
411 
412         assertTrue(BTAdapterUtils.disableAdapter(mAdapter, mContext));
413         assertFalse(mFakeDevice.fetchUuidsWithSdp(TRANSPORT_AUTO));
414     }
415 
416     @Test
messageAccessPermission()417     public void messageAccessPermission() {
418         // Skip the test if bluetooth or companion device are not present
419         // or if MAP is not enabled.
420         assumeTrue(mHasBluetooth && mHasCompanionDevice
421                    && TestUtils.isProfileEnabled(BluetoothProfile.MAP));
422 
423         // This should throw a SecurityException because no BLUETOOTH_PRIVILEGED permission
424         assertThrows(SecurityException.class, () -> mFakeDevice
425                 .setMessageAccessPermission(ACCESS_ALLOWED));
426         assertThrows(SecurityException.class, () -> mFakeDevice
427                 .setMessageAccessPermission(ACCESS_UNKNOWN));
428         assertThrows(SecurityException.class, () -> mFakeDevice
429                 .setMessageAccessPermission(ACCESS_REJECTED));
430 
431         TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED);
432 
433         // Should be able to set permissions after adopting the BLUETOOTH_PRIVILEGED permission
434         assertTrue(mFakeDevice.setMessageAccessPermission(ACCESS_UNKNOWN));
435         assertEquals(ACCESS_UNKNOWN, mFakeDevice.getMessageAccessPermission());
436         assertTrue(mFakeDevice.setMessageAccessPermission(ACCESS_ALLOWED));
437         assertEquals(ACCESS_ALLOWED, mFakeDevice.getMessageAccessPermission());
438         assertTrue(mFakeDevice.setMessageAccessPermission(ACCESS_REJECTED));
439         assertEquals(ACCESS_REJECTED, mFakeDevice.getMessageAccessPermission());
440     }
441 
442     @Test
phonebookAccessPermission()443     public void phonebookAccessPermission() {
444         // Skip the test if bluetooth or companion device are not present
445         // or if PBAP is not enabled.
446         assumeTrue(mHasBluetooth && mHasCompanionDevice
447                    && TestUtils.isProfileEnabled(BluetoothProfile.PBAP));
448 
449         // This should throw a SecurityException because no BLUETOOTH_PRIVILEGED permission
450         assertThrows(SecurityException.class, () -> mFakeDevice
451                 .setPhonebookAccessPermission(ACCESS_ALLOWED));
452         assertThrows(SecurityException.class, () -> mFakeDevice
453                 .setPhonebookAccessPermission(ACCESS_UNKNOWN));
454         assertThrows(SecurityException.class, () -> mFakeDevice
455                 .setPhonebookAccessPermission(ACCESS_REJECTED));
456 
457         TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED);
458 
459         // Should be able to set permissions after adopting the BLUETOOTH_PRIVILEGED permission
460         assertTrue(mFakeDevice.setPhonebookAccessPermission(ACCESS_UNKNOWN));
461         assertEquals(ACCESS_UNKNOWN, mFakeDevice.getPhonebookAccessPermission());
462         assertTrue(mFakeDevice.setPhonebookAccessPermission(ACCESS_ALLOWED));
463         assertEquals(ACCESS_ALLOWED, mFakeDevice.getPhonebookAccessPermission());
464         assertTrue(mFakeDevice.setPhonebookAccessPermission(ACCESS_REJECTED));
465         assertEquals(ACCESS_REJECTED, mFakeDevice.getPhonebookAccessPermission());
466     }
467 
468     @Test
simAccessPermission()469     public void simAccessPermission() {
470         // Skip the test if bluetooth or companion device are not present
471         // or if SAP is not enabled.
472         assumeTrue(mHasBluetooth && mHasCompanionDevice
473                    && TestUtils.isProfileEnabled(BluetoothProfile.SAP));
474 
475         // This should throw a SecurityException because no BLUETOOTH_PRIVILEGED permission
476         assertThrows(SecurityException.class, () -> mFakeDevice
477                 .setSimAccessPermission(ACCESS_ALLOWED));
478         assertThrows(SecurityException.class, () -> mFakeDevice
479                 .setSimAccessPermission(ACCESS_UNKNOWN));
480         assertThrows(SecurityException.class, () -> mFakeDevice
481                 .setSimAccessPermission(ACCESS_REJECTED));
482 
483         TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED);
484 
485         // Should be able to set permissions after adopting the BLUETOOTH_PRIVILEGED permission
486         assertTrue(mFakeDevice.setSimAccessPermission(ACCESS_UNKNOWN));
487         assertEquals(ACCESS_UNKNOWN, mFakeDevice.getSimAccessPermission());
488         assertTrue(mFakeDevice.setSimAccessPermission(ACCESS_ALLOWED));
489         assertEquals(ACCESS_ALLOWED, mFakeDevice.getSimAccessPermission());
490         assertTrue(mFakeDevice.setSimAccessPermission(ACCESS_REJECTED));
491         assertEquals(ACCESS_REJECTED, mFakeDevice.getSimAccessPermission());
492     }
493 
494     @Test
isRequestAudioPolicyAsSinkSupported()495     public void isRequestAudioPolicyAsSinkSupported() {
496         // Skip the test if bluetooth or companion device are not present.
497         assumeTrue(mHasBluetooth && mHasCompanionDevice);
498 
499         assertThrows(SecurityException.class,
500                 () -> mFakeDevice.isRequestAudioPolicyAsSinkSupported());
501 
502         TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED);
503 
504         assertEquals(BluetoothStatusCodes.FEATURE_NOT_CONFIGURED,
505                 mFakeDevice.isRequestAudioPolicyAsSinkSupported());
506     }
507 
508     @Test
setGetAudioPolicy()509     public void setGetAudioPolicy() {
510         // Skip the test if bluetooth or companion device are not present.
511         assumeTrue(mHasBluetooth && mHasCompanionDevice);
512 
513         BluetoothSinkAudioPolicy demoAudioPolicy = new BluetoothSinkAudioPolicy.Builder().build();
514 
515         // This should throw a SecurityException because no BLUETOOTH_PRIVILEGED permission
516         assertThrows(SecurityException.class,
517                 () -> mFakeDevice.requestAudioPolicyAsSink(demoAudioPolicy));
518         assertThrows(SecurityException.class, () -> mFakeDevice.getRequestedAudioPolicyAsSink());
519 
520         TestUtils.adoptPermissionAsShellUid(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED);
521 
522         assertEquals(BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED,
523                 mFakeDevice.requestAudioPolicyAsSink(demoAudioPolicy));
524         assertNull(mFakeDevice.getRequestedAudioPolicyAsSink());
525 
526         BluetoothSinkAudioPolicy newPolicy = new BluetoothSinkAudioPolicy.Builder(demoAudioPolicy)
527                 .setCallEstablishPolicy(BluetoothSinkAudioPolicy.POLICY_ALLOWED)
528                 .setActiveDevicePolicyAfterConnection(BluetoothSinkAudioPolicy.POLICY_NOT_ALLOWED)
529                 .setInBandRingtonePolicy(BluetoothSinkAudioPolicy.POLICY_ALLOWED)
530                 .build();
531 
532         assertEquals(BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED,
533                 mFakeDevice.requestAudioPolicyAsSink(newPolicy));
534         assertNull(mFakeDevice.getRequestedAudioPolicyAsSink());
535 
536         assertEquals(BluetoothSinkAudioPolicy.POLICY_ALLOWED, newPolicy.getCallEstablishPolicy());
537         assertEquals(BluetoothSinkAudioPolicy.POLICY_NOT_ALLOWED,
538                 newPolicy.getActiveDevicePolicyAfterConnection());
539         assertEquals(BluetoothSinkAudioPolicy.POLICY_ALLOWED, newPolicy.getInBandRingtonePolicy());
540     }
541 
convertPinToBytes(String pin)542     private byte[] convertPinToBytes(String pin) {
543         if (pin == null) {
544             return null;
545         }
546         byte[] pinBytes;
547         try {
548             pinBytes = pin.getBytes("UTF-8");
549         } catch (UnsupportedEncodingException uee) {
550             return null;
551         }
552         return pinBytes;
553     }
554 
555     @Test
getPackageNameOfBondingApplication()556     public void getPackageNameOfBondingApplication() {
557         // Skip the test if bluetooth or companion device are not present.
558         assumeTrue(mHasBluetooth && mHasCompanionDevice);
559 
560         mUiAutomation.dropShellPermissionIdentity();
561         assertThrows(SecurityException.class,
562                 () -> mFakeDevice.getPackageNameOfBondingApplication());
563         mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
564         assertThrows(SecurityException.class,
565                 () -> mFakeDevice.getPackageNameOfBondingApplication());
566 
567         mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_PRIVILEGED, BLUETOOTH_CONNECT);
568         // Since no application actually start bonding with this device, this should return null
569         assertNull(mFakeDevice.getPackageNameOfBondingApplication());
570 
571         mFakeDevice.createBond();
572         assertEquals(mContext.getPackageName(),
573                 mFakeDevice.getPackageNameOfBondingApplication());
574 
575         // Clean up create bond
576         // Either cancel the bonding process or remove bond
577         mFakeDevice.cancelBondProcess();
578         mFakeDevice.removeBond();
579     }
580 
581     @RequiresFlagsEnabled(Flags.FLAG_METADATA_API_INACTIVE_AUDIO_DEVICE_UPON_CONNECTION)
582     @Test
setActiveAudioDevicePolicy_getActiveAudioDevicePolicy()583     public void setActiveAudioDevicePolicy_getActiveAudioDevicePolicy() {
584         if (!mHasBluetooth || !mHasCompanionDevice) {
585             // Skip the test if bluetooth or companion device are not present.
586             return;
587         }
588         String deviceAddress = "00:11:22:AA:AA:AA";
589         BluetoothDevice device = mAdapter.getRemoteDevice(deviceAddress);
590 
591         // This should throw a SecurityException because no BLUETOOTH_CONNECT permission
592         mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_PRIVILEGED);
593         assertThrows(
594                 SecurityException.class,
595                 () ->
596                         device.setActiveAudioDevicePolicy(
597                                 BluetoothDevice
598                                         .ACTIVE_AUDIO_DEVICE_POLICY_ALL_PROFILES_INACTIVE_UPON_CONNECTION));
599         assertThrows(SecurityException.class, () -> device.getActiveAudioDevicePolicy());
600 
601         // This should throw a SecurityException because no BLUETOOTH_PRIVILEGED permission
602         mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT);
603         assertThrows(
604                 SecurityException.class,
605                 () ->
606                         device.setActiveAudioDevicePolicy(
607                                 BluetoothDevice
608                                         .ACTIVE_AUDIO_DEVICE_POLICY_ALL_PROFILES_INACTIVE_UPON_CONNECTION));
609         assertThrows(SecurityException.class, () -> device.getActiveAudioDevicePolicy());
610 
611         mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED);
612 
613         assertEquals(
614                 BluetoothDevice.ACTIVE_AUDIO_DEVICE_POLICY_DEFAULT,
615                 device.getActiveAudioDevicePolicy());
616         assertEquals(
617                 BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED,
618                 device.setActiveAudioDevicePolicy(
619                         BluetoothDevice
620                                 .ACTIVE_AUDIO_DEVICE_POLICY_ALL_PROFILES_INACTIVE_UPON_CONNECTION));
621     }
622     @RequiresFlagsEnabled(Flags.FLAG_BT_SOCKET_API_L2CAP_CID)
623     @Test
getL2capChannel()624     public void getL2capChannel() throws IOException {
625         // Skip the test if bluetooth or companion device are not present.
626         assumeTrue(mHasBluetooth && mHasCompanionDevice);
627 
628         BluetoothSocket l2capSocket = mFakeDevice.createInsecureL2capChannel(mFakePsm);
629         BluetoothSocket rfcommSocket = mFakeDevice
630                         .createInsecureRfcommSocketToServiceRecord(mFakeUuid);
631 
632         mUiAutomation.adoptShellPermissionIdentity(BLUETOOTH_CONNECT, BLUETOOTH_PRIVILEGED);
633 
634         // This should throw a BluetoothSocketException because it is not L2CAP socket
635         assertThrows("Unknown L2CAP socket", BluetoothSocketException.class,
636                 () -> rfcommSocket.getL2capLocalChannelId());
637         assertThrows("Unknown L2CAP socket", BluetoothSocketException.class,
638                 () -> rfcommSocket.getL2capRemoteChannelId());
639 
640         // This should throw a BluetoothSocketException because L2CAP socket is not connected
641         assertThrows("Socket closed", BluetoothSocketException.class,
642                 () -> l2capSocket.getL2capLocalChannelId());
643         assertThrows("Socket closed", BluetoothSocketException.class,
644                 () -> l2capSocket.getL2capRemoteChannelId());
645     }
646 }
647