1 /*
2  * Copyright 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 
17 package com.android.settings.accessibility;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 
21 import static org.mockito.Mockito.doReturn;
22 import static org.mockito.Mockito.spy;
23 import static org.mockito.Mockito.when;
24 import static org.robolectric.Shadows.shadowOf;
25 
26 import android.app.Application;
27 import android.bluetooth.BluetoothAdapter;
28 import android.bluetooth.BluetoothDevice;
29 import android.bluetooth.BluetoothHapClient;
30 import android.bluetooth.BluetoothHearingAid;
31 import android.bluetooth.BluetoothProfile;
32 import android.content.BroadcastReceiver;
33 import android.content.Context;
34 import android.content.Intent;
35 
36 import androidx.preference.Preference;
37 import androidx.test.core.app.ApplicationProvider;
38 
39 import com.android.settings.R;
40 import com.android.settings.bluetooth.Utils;
41 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
42 import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
43 import com.android.settingslib.bluetooth.BluetoothEventManager;
44 import com.android.settingslib.bluetooth.CachedBluetoothDevice;
45 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
46 import com.android.settingslib.bluetooth.HapClientProfile;
47 import com.android.settingslib.bluetooth.HearingAidInfo;
48 import com.android.settingslib.bluetooth.HearingAidProfile;
49 import com.android.settingslib.bluetooth.LocalBluetoothManager;
50 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
51 
52 import org.junit.After;
53 import org.junit.Before;
54 import org.junit.Rule;
55 import org.junit.Test;
56 import org.junit.runner.RunWith;
57 import org.mockito.Mock;
58 import org.mockito.junit.MockitoJUnit;
59 import org.mockito.junit.MockitoRule;
60 import org.robolectric.RobolectricTestRunner;
61 import org.robolectric.annotation.Config;
62 import org.robolectric.shadow.api.Shadow;
63 import org.robolectric.shadows.ShadowApplication;
64 import org.robolectric.shadows.ShadowLooper;
65 
66 import java.util.ArrayList;
67 import java.util.HashSet;
68 import java.util.List;
69 import java.util.Set;
70 
71 /** Tests for {@link AccessibilityHearingAidPreferenceController}. */
72 @RunWith(RobolectricTestRunner.class)
73 @Config(shadows = {ShadowBluetoothAdapter.class, ShadowBluetoothUtils.class})
74 public class AccessibilityHearingAidPreferenceControllerTest {
75     @Rule
76     public final MockitoRule mockito = MockitoJUnit.rule();
77 
78     private static final String TEST_DEVICE_ADDRESS = "00:A1:A1:A1:A1:A1";
79     private static final String TEST_DEVICE_ADDRESS_2 = "00:A2:A2:A2:A2:A2";
80     private static final String TEST_DEVICE_NAME = "TEST_HEARING_AID_BT_DEVICE_NAME";
81     private static final String HEARING_AID_PREFERENCE = "hearing_aid_preference";
82 
83     private BluetoothAdapter mBluetoothAdapter;
84     private ShadowBluetoothAdapter mShadowBluetoothAdapter;
85     private BluetoothDevice mBluetoothDevice;
86 
87     private BluetoothDevice mSubBluetoothDevice;
88     private final Context mContext = ApplicationProvider.getApplicationContext();
89 
90     private Preference mHearingAidPreference;
91     private AccessibilityHearingAidPreferenceController mPreferenceController;
92     private ShadowApplication mShadowApplication;
93 
94     @Mock
95     private CachedBluetoothDevice mCachedBluetoothDevice;
96     @Mock
97     private CachedBluetoothDevice mCachedSubBluetoothDevice;
98     @Mock
99     private CachedBluetoothDeviceManager mCachedDeviceManager;
100     @Mock
101     private LocalBluetoothManager mLocalBluetoothManager;
102     @Mock
103     private BluetoothEventManager mEventManager;
104     @Mock
105     private LocalBluetoothProfileManager mLocalBluetoothProfileManager;
106     @Mock
107     private HearingAidProfile mHearingAidProfile;
108     @Mock
109     private HapClientProfile mHapClientProfile;
110 
111     @Before
setUp()112     public void setUp() {
113         mShadowApplication = shadowOf((Application) ApplicationProvider.getApplicationContext());
114         setupEnvironment();
115 
116         mHearingAidPreference = new Preference(mContext);
117         mHearingAidPreference.setKey(HEARING_AID_PREFERENCE);
118         mPreferenceController = new AccessibilityHearingAidPreferenceController(mContext,
119                 HEARING_AID_PREFERENCE);
120         mPreferenceController.setPreference(mHearingAidPreference);
121         mHearingAidPreference.setSummary("");
122     }
123 
124     @After
tearDown()125     public void tearDown() {
126         ShadowBluetoothUtils.reset();
127     }
128 
129     @Test
getSummary_connectedAshaHearingAidRightSide_connectedRightSideSummary()130     public void getSummary_connectedAshaHearingAidRightSide_connectedRightSideSummary() {
131         when(mCachedBluetoothDevice.getDeviceSide()).thenReturn(
132                 HearingAidInfo.DeviceSide.SIDE_RIGHT);
133         when(mHearingAidProfile.getConnectedDevices()).thenReturn(generateHearingAidDeviceList());
134 
135         mPreferenceController.onStart();
136         Intent intent = new Intent(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
137         intent.putExtra(BluetoothHearingAid.EXTRA_STATE, BluetoothHearingAid.STATE_CONNECTED);
138         sendIntent(intent);
139         ShadowLooper.idleMainLooper();
140 
141         assertThat(mHearingAidPreference.getSummary().toString().contentEquals(
142                 "TEST_HEARING_AID_BT_DEVICE_NAME / Right only")).isTrue();
143     }
144 
145     @Test
getSummary_connectedAshaHearingAidBothSide_connectedBothSideSummary()146     public void getSummary_connectedAshaHearingAidBothSide_connectedBothSideSummary() {
147         when(mCachedBluetoothDevice.getDeviceSide()).thenReturn(
148                 HearingAidInfo.DeviceSide.SIDE_LEFT);
149         when(mCachedBluetoothDevice.getSubDevice()).thenReturn(mCachedSubBluetoothDevice);
150         when(mSubBluetoothDevice.isConnected()).thenReturn(true);
151         when(mHearingAidProfile.getConnectedDevices()).thenReturn(generateHearingAidDeviceList());
152 
153         mPreferenceController.onStart();
154         Intent intent = new Intent(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
155         intent.putExtra(BluetoothHearingAid.EXTRA_STATE, BluetoothHearingAid.STATE_CONNECTED);
156         sendIntent(intent);
157         ShadowLooper.idleMainLooper();
158 
159         assertThat(mHearingAidPreference.getSummary().toString().contentEquals(
160                 "TEST_HEARING_AID_BT_DEVICE_NAME / Left and right")).isTrue();
161     }
162 
163     @Test
getSummary_connectedLeAudioHearingAidLeftSide_connectedLeftSideSummary()164     public void getSummary_connectedLeAudioHearingAidLeftSide_connectedLeftSideSummary() {
165         when(mCachedBluetoothDevice.getDeviceSide()).thenReturn(
166                 HearingAidInfo.DeviceSide.SIDE_LEFT);
167         when(mCachedBluetoothDevice.getMemberDevice()).thenReturn(new HashSet<>());
168         when(mHapClientProfile.getConnectedDevices()).thenReturn(generateHearingAidDeviceList());
169 
170         mPreferenceController.onStart();
171         Intent intent = new Intent(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED);
172         intent.putExtra(BluetoothHearingAid.EXTRA_STATE, BluetoothHapClient.STATE_CONNECTED);
173         sendIntent(intent);
174         ShadowLooper.idleMainLooper();
175 
176         assertThat(mHearingAidPreference.getSummary().toString().contentEquals(
177                 "TEST_HEARING_AID_BT_DEVICE_NAME / Left only")).isTrue();
178     }
179 
180     @Test
getSummary_connectedLeAudioHearingAidRightSide_connectedRightSideSummary()181     public void getSummary_connectedLeAudioHearingAidRightSide_connectedRightSideSummary() {
182         when(mCachedBluetoothDevice.getDeviceSide()).thenReturn(
183                 HearingAidInfo.DeviceSide.SIDE_RIGHT);
184         when(mCachedBluetoothDevice.getMemberDevice()).thenReturn(new HashSet<>());
185         when(mHapClientProfile.getConnectedDevices()).thenReturn(generateHearingAidDeviceList());
186 
187         mPreferenceController.onStart();
188         Intent intent = new Intent(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED);
189         intent.putExtra(BluetoothHearingAid.EXTRA_STATE, BluetoothHapClient.STATE_CONNECTED);
190         sendIntent(intent);
191         ShadowLooper.idleMainLooper();
192 
193         assertThat(mHearingAidPreference.getSummary().toString().contentEquals(
194                 "TEST_HEARING_AID_BT_DEVICE_NAME / Right only")).isTrue();
195     }
196 
197     @Test
getSummary_connectedLeAudioHearingAidLeftAndRightSide_connectedSummary()198     public void getSummary_connectedLeAudioHearingAidLeftAndRightSide_connectedSummary() {
199         when(mCachedBluetoothDevice.getDeviceSide()).thenReturn(
200                 HearingAidInfo.DeviceSide.SIDE_LEFT_AND_RIGHT);
201         when(mCachedBluetoothDevice.getMemberDevice()).thenReturn(new HashSet<>());
202         when(mHapClientProfile.getConnectedDevices()).thenReturn(generateHearingAidDeviceList());
203 
204         mPreferenceController.onStart();
205         Intent intent = new Intent(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED);
206         intent.putExtra(BluetoothHearingAid.EXTRA_STATE, BluetoothHapClient.STATE_CONNECTED);
207         sendIntent(intent);
208         ShadowLooper.idleMainLooper();
209 
210         assertThat(mHearingAidPreference.getSummary().toString().contentEquals(
211                 "TEST_HEARING_AID_BT_DEVICE_NAME / Left and right")).isTrue();
212     }
213 
214     @Test
getSummary_connectedLeAudioHearingAidBothSide_connectedBothSideSummary()215     public void getSummary_connectedLeAudioHearingAidBothSide_connectedBothSideSummary() {
216         when(mCachedBluetoothDevice.getMemberDevice()).thenReturn(generateMemberDevices());
217         when(mHapClientProfile.getConnectedDevices()).thenReturn(generateHearingAidDeviceList());
218         when(mSubBluetoothDevice.isConnected()).thenReturn(true);
219 
220         mPreferenceController.onStart();
221         Intent intent = new Intent(BluetoothHapClient.ACTION_HAP_CONNECTION_STATE_CHANGED);
222         intent.putExtra(BluetoothHearingAid.EXTRA_STATE, BluetoothHapClient.STATE_CONNECTED);
223         sendIntent(intent);
224         ShadowLooper.idleMainLooper();
225 
226         assertThat(mHearingAidPreference.getSummary().toString()).isEqualTo(
227                 "TEST_HEARING_AID_BT_DEVICE_NAME / Left and right");
228     }
229 
230     @Test
getSummary_connectedMultipleHearingAids_connectedMultipleDevicesSummary()231     public void getSummary_connectedMultipleHearingAids_connectedMultipleDevicesSummary() {
232         when(mCachedBluetoothDevice.getDeviceSide()).thenReturn(
233                 HearingAidInfo.DeviceSide.SIDE_LEFT);
234         when(mHearingAidProfile.getConnectedDevices()).thenReturn(
235                 generateMultipleHearingAidDeviceList());
236 
237         mPreferenceController.onStart();
238         Intent intent = new Intent(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
239         intent.putExtra(BluetoothHearingAid.EXTRA_STATE, BluetoothHearingAid.STATE_CONNECTED);
240         sendIntent(intent);
241         ShadowLooper.idleMainLooper();
242 
243         assertThat(mHearingAidPreference.getSummary().toString().contentEquals(
244                 "TEST_HEARING_AID_BT_DEVICE_NAME +1 more")).isTrue();
245     }
246 
247     @Test
getSummary_bluetoothOff_disconnectedSummary()248     public void getSummary_bluetoothOff_disconnectedSummary() {
249         mPreferenceController.onStart();
250         Intent intent = new Intent(BluetoothAdapter.ACTION_STATE_CHANGED);
251         intent.putExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF);
252         sendIntent(intent);
253         ShadowLooper.idleMainLooper();
254 
255         assertThat(mHearingAidPreference.getSummary()).isEqualTo(
256                 mContext.getText(R.string.accessibility_hearingaid_not_connected_summary));
257     }
258 
259     @Test
onServiceConnected_onHearingAidProfileConnected_updateSummary()260     public void onServiceConnected_onHearingAidProfileConnected_updateSummary() {
261         when(mCachedBluetoothDevice.getDeviceSide()).thenReturn(
262                 HearingAidInfo.DeviceSide.SIDE_LEFT);
263         when(mHearingAidProfile.getConnectedDevices()).thenReturn(generateHearingAidDeviceList());
264 
265         mPreferenceController.onStart();
266         mPreferenceController.onServiceConnected();
267         ShadowLooper.idleMainLooper();
268 
269         assertThat(mHearingAidPreference.getSummary().toString()).isEqualTo(
270                 "TEST_HEARING_AID_BT_DEVICE_NAME / Left only");
271     }
272 
273     @Test
onServiceConnected_onHapClientProfileConnected_updateSummary()274     public void onServiceConnected_onHapClientProfileConnected_updateSummary() {
275         when(mCachedBluetoothDevice.getDeviceSide()).thenReturn(
276                 HearingAidInfo.DeviceSide.SIDE_RIGHT);
277         when(mHapClientProfile.getConnectedDevices()).thenReturn(generateHearingAidDeviceList());
278 
279         mPreferenceController.onStart();
280         mPreferenceController.onServiceConnected();
281         ShadowLooper.idleMainLooper();
282 
283         assertThat(mHearingAidPreference.getSummary().toString()).isEqualTo(
284                 "TEST_HEARING_AID_BT_DEVICE_NAME / Right only");
285     }
286 
setupEnvironment()287     private void setupEnvironment() {
288         ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager;
289         mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
290         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
291         mShadowBluetoothAdapter = Shadow.extract(mBluetoothAdapter);
292         mShadowBluetoothAdapter.addSupportedProfiles(BluetoothProfile.HEARING_AID);
293         mShadowBluetoothAdapter.addSupportedProfiles(BluetoothProfile.HAP_CLIENT);
294         mBluetoothDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS));
295         mSubBluetoothDevice = spy(mBluetoothAdapter.getRemoteDevice(TEST_DEVICE_ADDRESS_2));
296         mBluetoothAdapter.enable();
297 
298         doReturn(mEventManager).when(mLocalBluetoothManager).getEventManager();
299         when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager);
300         when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager);
301         when(mLocalBluetoothProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile);
302         when(mLocalBluetoothProfileManager.getHapClientProfile()).thenReturn(mHapClientProfile);
303         when(mHearingAidProfile.isProfileReady()).thenReturn(true);
304         when(mHapClientProfile.isProfileReady()).thenReturn(true);
305         when(mCachedDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice);
306         when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice);
307         when(mCachedBluetoothDevice.getAddress()).thenReturn(TEST_DEVICE_ADDRESS);
308         when(mCachedBluetoothDevice.getName()).thenReturn(TEST_DEVICE_NAME);
309         when(mCachedDeviceManager.findDevice(mSubBluetoothDevice)).thenReturn(
310                 mCachedSubBluetoothDevice);
311         when(mCachedSubBluetoothDevice.getDevice()).thenReturn(mSubBluetoothDevice);
312     }
313 
sendIntent(Intent intent)314     private void sendIntent(Intent intent) {
315         for (BroadcastReceiver receiver : mShadowApplication.getReceiversForIntent(intent)) {
316             receiver.onReceive(mContext, intent);
317         }
318     }
319 
generateHearingAidDeviceList()320     private List<BluetoothDevice> generateHearingAidDeviceList() {
321         final List<BluetoothDevice> deviceList = new ArrayList<>(1);
322         deviceList.add(mBluetoothDevice);
323         return deviceList;
324     }
325 
generateMultipleHearingAidDeviceList()326     private List<BluetoothDevice> generateMultipleHearingAidDeviceList() {
327         // Generates different Bluetooth devices for testing multiple devices
328         final List<BluetoothDevice> deviceList = new ArrayList<>(2);
329         deviceList.add(mBluetoothDevice);
330         deviceList.add(mSubBluetoothDevice);
331         return deviceList;
332     }
333 
generateMemberDevices()334     private Set<CachedBluetoothDevice> generateMemberDevices() {
335         final Set<CachedBluetoothDevice> memberDevices = new HashSet<>();
336         memberDevices.add(mCachedSubBluetoothDevice);
337         return memberDevices;
338     }
339 }
340