1 /*
2  * Copyright (C) 2018 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 package com.android.settings.bluetooth;
17 
18 import static android.bluetooth.BluetoothDevice.PAIRING_VARIANT_CONSENT;
19 
20 import static com.google.common.truth.Truth.assertThat;
21 
22 import static org.mockito.Mockito.verify;
23 import static org.mockito.Mockito.when;
24 
25 import android.bluetooth.BluetoothAdapter;
26 import android.bluetooth.BluetoothClass;
27 import android.bluetooth.BluetoothDevice;
28 import android.bluetooth.BluetoothProfile;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.os.Parcel;
32 
33 import com.android.settings.core.SettingsUIDeviceConfig;
34 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
35 import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
36 import com.android.settings.testutils.shadow.ShadowDeviceConfig;
37 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
38 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
39 import com.android.settingslib.bluetooth.LocalBluetoothManager;
40 import com.android.settingslib.bluetooth.LocalBluetoothProfile;
41 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
42 
43 import org.junit.Before;
44 import org.junit.Test;
45 import org.junit.runner.RunWith;
46 import org.mockito.Mock;
47 import org.mockito.MockitoAnnotations;
48 import org.robolectric.RobolectricTestRunner;
49 import org.robolectric.RuntimeEnvironment;
50 import org.robolectric.annotation.Config;
51 import org.robolectric.shadow.api.Shadow;
52 
53 import java.util.ArrayList;
54 import java.util.List;
55 
56 @RunWith(RobolectricTestRunner.class)
57 @Config(shadows = {ShadowBluetoothAdapter.class, ShadowBluetoothUtils.class,
58         ShadowDeviceConfig.class})
59 public class BluetoothPairingControllerTest {
60     private final BluetoothClass mBluetoothClass =
61             createBtClass(BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE);
62 
63     @Mock
64     private CachedBluetoothDeviceManager mCachedDeviceManager;
65     @Mock
66     private LocalBluetoothManager mLocalBluetoothManager;
67     @Mock
68     private LocalBluetoothProfileManager mLocalBluetoothProfileManager;
69     @Mock
70     private BluetoothDevice mBluetoothDevice;
71     @Mock
72     private CachedBluetoothDevice mCachedDevice;
73     @Mock
74     private LocalBluetoothProfile mLocalBluetoothProfile;
75     @Mock
76     private LocalBluetoothProfile mPbapLocalBluetoothProfile;
77 
78     private Context mContext;
79     private BluetoothPairingController mBluetoothPairingController;
80     private ShadowBluetoothAdapter mShadowBluetoothAdapter;
81 
createBtClass(int deviceClass)82     private BluetoothClass createBtClass(int deviceClass) {
83         Parcel p = Parcel.obtain();
84         p.writeInt(deviceClass);
85         p.setDataPosition(0); // reset position of parcel before passing to constructor
86 
87         BluetoothClass bluetoothClass = BluetoothClass.CREATOR.createFromParcel(p);
88         p.recycle();
89         return bluetoothClass;
90     }
91 
createBluetoothPairingController()92     private BluetoothPairingController createBluetoothPairingController() {
93         final Intent intent = new Intent();
94         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mBluetoothDevice);
95         return new BluetoothPairingController(intent, mContext);
96     }
97 
98     @Before
setUp()99     public void setUp() {
100         MockitoAnnotations.initMocks(this);
101 
102         mContext = RuntimeEnvironment.application;
103         mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
104         mShadowBluetoothAdapter.setEnabled(true);
105         ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
106         mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
107         when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
108         when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
109         when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedDevice);
110         List<LocalBluetoothProfile> localBluetoothProfiles = new ArrayList<>();
111         mockIsLeAudio(false);
112         localBluetoothProfiles.add(mLocalBluetoothProfile);
113         when(mCachedDevice.getProfiles()).thenReturn(localBluetoothProfiles);
114         when(mCachedDevice.getDevice()).thenReturn(mBluetoothDevice);
115 
116         mBluetoothPairingController = createBluetoothPairingController();
117         mBluetoothPairingController.mockPbapClientProfile(mPbapLocalBluetoothProfile);
118     }
119 
120     @Test
onDialogPositiveClick_confirmationDialog_setPBAP()121     public void onDialogPositiveClick_confirmationDialog_setPBAP() {
122         mBluetoothPairingController.mType = PAIRING_VARIANT_CONSENT;
123         mBluetoothPairingController.onCheckedChanged(null, true);
124 
125         mBluetoothPairingController.onDialogPositiveClick(null);
126 
127         verify(mBluetoothDevice).setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
128     }
129 
130     @Test
onSetContactSharingState_permissionAllowed_setPBAPAllowed()131     public void onSetContactSharingState_permissionAllowed_setPBAPAllowed() {
132         when(mBluetoothDevice.getPhonebookAccessPermission()).thenReturn(
133                 BluetoothDevice.ACCESS_ALLOWED);
134         mBluetoothPairingController.setContactSharingState();
135         mBluetoothPairingController.onDialogPositiveClick(null);
136 
137         verify(mBluetoothDevice).setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
138     }
139 
140     @Test
onSetContactSharingState_permissionUnknown_audioVideoHandsfree_setPBAPAllowed()141     public void onSetContactSharingState_permissionUnknown_audioVideoHandsfree_setPBAPAllowed() {
142         when(mBluetoothDevice.getPhonebookAccessPermission()).thenReturn(
143                 BluetoothDevice.ACCESS_UNKNOWN);
144         when(mBluetoothDevice.getBluetoothClass()).thenReturn(mBluetoothClass);
145         mBluetoothPairingController.setContactSharingState();
146         mBluetoothPairingController.onDialogPositiveClick(null);
147 
148         verify(mBluetoothDevice).setPhonebookAccessPermission(BluetoothDevice.ACCESS_ALLOWED);
149     }
150 
151     @Test
onSetContactSharingState_permissionRejected_setPBAPRejected()152     public void onSetContactSharingState_permissionRejected_setPBAPRejected() {
153         when(mBluetoothDevice.getPhonebookAccessPermission()).thenReturn(
154                 BluetoothDevice.ACCESS_REJECTED);
155         when(mBluetoothDevice.getBluetoothClass()).thenReturn(mBluetoothClass);
156         mBluetoothPairingController.setContactSharingState();
157         mBluetoothPairingController.onDialogPositiveClick(null);
158 
159         verify(mBluetoothDevice).setPhonebookAccessPermission(BluetoothDevice.ACCESS_REJECTED);
160     }
161 
162     @Test
isLeAudio_noLeProfile_returnsFalse()163     public void isLeAudio_noLeProfile_returnsFalse() {
164         mockIsLeAudio(false);
165 
166         mBluetoothPairingController = createBluetoothPairingController();
167 
168         assertThat(mBluetoothPairingController.isLeAudio()).isFalse();
169     }
170 
171     @Test
isLeAudio_isLeProfile_returnsTrue()172     public void isLeAudio_isLeProfile_returnsTrue() {
173         mockIsLeAudio(true);
174 
175         mBluetoothPairingController = createBluetoothPairingController();
176 
177         assertThat(mBluetoothPairingController.isLeAudio()).isTrue();
178     }
179 
180     @Test
isLeContactSharingEnabled_configIsFalse_returnsFalse()181     public void isLeContactSharingEnabled_configIsFalse_returnsFalse() {
182         mockIsLeContactSharingEnabled(false);
183 
184         mBluetoothPairingController = createBluetoothPairingController();
185 
186         assertThat(mBluetoothPairingController.isLeContactSharingEnabled()).isFalse();
187     }
188 
189     @Test
isLeContactSharingEnabled_configIsTrue_returnsTrue()190     public void isLeContactSharingEnabled_configIsTrue_returnsTrue() {
191         mockIsLeContactSharingEnabled(true);
192 
193         mBluetoothPairingController = createBluetoothPairingController();
194 
195         assertThat(mBluetoothPairingController.isLeContactSharingEnabled()).isTrue();
196     }
197 
198     @Test
isContactSharingVisible_profileIsNotReady_returnsTrue()199     public void isContactSharingVisible_profileIsNotReady_returnsTrue() {
200         // isProfileReady=false, isLeAudio=false, isLeContactSharingEnabled=true
201         mockIsProfileReady(false);
202         mockIsLeAudio(false);
203         mockIsLeContactSharingEnabled(true);
204 
205         mBluetoothPairingController = createBluetoothPairingController();
206         mBluetoothPairingController.mockPbapClientProfile(mPbapLocalBluetoothProfile);
207 
208         assertThat(mBluetoothPairingController.isContactSharingVisible()).isTrue();
209     }
210 
211     @Test
isContactSharingVisible_profileIsReady_returnsFalse()212     public void isContactSharingVisible_profileIsReady_returnsFalse() {
213         // isProfileReady=true, isLeAudio=false, isLeContactSharingEnabled=true
214         mockIsProfileReady(true);
215         mockIsLeAudio(false);
216         mockIsLeContactSharingEnabled(true);
217 
218         mBluetoothPairingController = createBluetoothPairingController();
219         mBluetoothPairingController.mockPbapClientProfile(mPbapLocalBluetoothProfile);
220 
221         assertThat(mBluetoothPairingController.isContactSharingVisible()).isFalse();
222     }
223 
224     @Test
isContactSharingVisible_DeviceIsLeAudioAndProfileIsReady_returnsFalse()225     public void isContactSharingVisible_DeviceIsLeAudioAndProfileIsReady_returnsFalse() {
226         // isProfileReady=true, isLeAudio=true, isLeContactSharingEnabled=true
227         mockIsProfileReady(true);
228         mockIsLeAudio(true);
229         mockIsLeContactSharingEnabled(true);
230 
231         mBluetoothPairingController = createBluetoothPairingController();
232         mBluetoothPairingController.mockPbapClientProfile(mPbapLocalBluetoothProfile);
233 
234         assertThat(mBluetoothPairingController.isContactSharingVisible()).isFalse();
235     }
236 
237     @Test
isContactSharingVisible_DeviceIsLeAudioAndProfileIsNotReady_returnsTrue()238     public void isContactSharingVisible_DeviceIsLeAudioAndProfileIsNotReady_returnsTrue() {
239         // isProfileReady=false, isLeAudio=true, isLeContactSharingEnabled=true
240         mockIsProfileReady(false);
241         mockIsLeAudio(true);
242         mockIsLeContactSharingEnabled(true);
243 
244         mBluetoothPairingController = createBluetoothPairingController();
245         mBluetoothPairingController.mockPbapClientProfile(mPbapLocalBluetoothProfile);
246 
247         assertThat(mBluetoothPairingController.isContactSharingVisible()).isTrue();
248     }
249 
250     @Test
isContactSharingVisible_DeviceIsLeAndContactSharingIsNotEnabled_returnsFalse()251     public void isContactSharingVisible_DeviceIsLeAndContactSharingIsNotEnabled_returnsFalse() {
252         // isProfileReady=false, isLeAudio=true, isLeContactSharingEnabled=false
253         mockIsProfileReady(false);
254         mockIsLeAudio(true);
255         mockIsLeContactSharingEnabled(false);
256 
257         mBluetoothPairingController = createBluetoothPairingController();
258         mBluetoothPairingController.mockPbapClientProfile(mPbapLocalBluetoothProfile);
259 
260         assertThat(mBluetoothPairingController.isContactSharingVisible()).isFalse();
261     }
262 
mockIsProfileReady(boolean mockValue)263     private void mockIsProfileReady(boolean mockValue) {
264         when(mPbapLocalBluetoothProfile.isProfileReady()).thenReturn(mockValue);
265     }
266 
mockIsLeAudio(boolean mockValue)267     private void mockIsLeAudio(boolean mockValue) {
268         int profileId = BluetoothProfile.HEADSET;
269         if (mockValue) {
270             profileId = BluetoothProfile.LE_AUDIO;
271         }
272         when(mLocalBluetoothProfile.getProfileId()).thenReturn(profileId);
273     }
274 
mockIsLeContactSharingEnabled(boolean mockValue)275     private void mockIsLeContactSharingEnabled(boolean mockValue) {
276         android.provider.DeviceConfig.setProperty(
277                 android.provider.DeviceConfig.NAMESPACE_SETTINGS_UI,
278                 SettingsUIDeviceConfig.BT_LE_AUDIO_CONTACT_SHARING_ENABLED,
279                 /* value= */ mockValue ? "true" : "false", true);
280     }
281 }
282