1 /* 2 * Copyright (C) 2017 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.connecteddevice; 17 18 import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE; 19 import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE; 20 21 import static com.google.common.truth.Truth.assertThat; 22 23 import static org.mockito.ArgumentMatchers.anyString; 24 import static org.mockito.Mockito.doReturn; 25 import static org.mockito.Mockito.mock; 26 import static org.mockito.Mockito.spy; 27 import static org.mockito.Mockito.verify; 28 import static org.mockito.Mockito.when; 29 30 import android.bluetooth.BluetoothDevice; 31 import android.content.Context; 32 import android.content.pm.PackageManager; 33 import android.hardware.input.InputManager; 34 import android.platform.test.annotations.EnableFlags; 35 import android.platform.test.flag.junit.SetFlagsRule; 36 import android.util.FeatureFlagUtils; 37 import android.view.InputDevice; 38 39 import androidx.preference.Preference; 40 import androidx.preference.PreferenceGroup; 41 import androidx.preference.PreferenceManager; 42 import androidx.preference.PreferenceScreen; 43 44 import com.android.settings.bluetooth.ConnectedBluetoothDeviceUpdater; 45 import com.android.settings.bluetooth.Utils; 46 import com.android.settings.connecteddevice.dock.DockUpdater; 47 import com.android.settings.connecteddevice.stylus.StylusDeviceUpdater; 48 import com.android.settings.connecteddevice.usb.ConnectedUsbDeviceUpdater; 49 import com.android.settings.dashboard.DashboardFragment; 50 import com.android.settings.flags.Flags; 51 import com.android.settings.testutils.shadow.ShadowBluetoothAdapter; 52 import com.android.settings.testutils.shadow.ShadowBluetoothUtils; 53 import com.android.settingslib.bluetooth.CachedBluetoothDevice; 54 import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager; 55 import com.android.settingslib.bluetooth.LocalBluetoothManager; 56 import com.android.settingslib.search.SearchIndexableRaw; 57 58 import com.google.common.collect.ImmutableList; 59 60 import org.junit.Before; 61 import org.junit.Rule; 62 import org.junit.Test; 63 import org.junit.runner.RunWith; 64 import org.mockito.Answers; 65 import org.mockito.Mock; 66 import org.mockito.MockitoAnnotations; 67 import org.robolectric.RobolectricTestRunner; 68 import org.robolectric.RuntimeEnvironment; 69 import org.robolectric.Shadows; 70 import org.robolectric.annotation.Config; 71 import org.robolectric.shadows.ShadowApplicationPackageManager; 72 73 import java.util.ArrayList; 74 import java.util.List; 75 76 @RunWith(RobolectricTestRunner.class) 77 @Config(shadows = {ShadowApplicationPackageManager.class, ShadowBluetoothUtils.class, 78 ShadowBluetoothAdapter.class}) 79 public class ConnectedDeviceGroupControllerTest { 80 81 private static final String PREFERENCE_KEY_1 = "pref_key_1"; 82 private static final String DEVICE_NAME = "device"; 83 84 @Mock 85 private DashboardFragment mDashboardFragment; 86 @Mock 87 private ConnectedBluetoothDeviceUpdater mConnectedBluetoothDeviceUpdater; 88 @Mock 89 private ConnectedUsbDeviceUpdater mConnectedUsbDeviceUpdater; 90 @Mock 91 private DockUpdater mConnectedDockUpdater; 92 @Mock 93 private StylusDeviceUpdater mStylusDeviceUpdater; 94 @Mock 95 private PreferenceScreen mPreferenceScreen; 96 @Mock(answer = Answers.RETURNS_DEEP_STUBS) 97 private PreferenceManager mPreferenceManager; 98 @Mock 99 private InputManager mInputManager; 100 @Mock 101 private CachedBluetoothDeviceManager mCachedDeviceManager; 102 @Mock 103 private LocalBluetoothManager mLocalBluetoothManager; 104 @Mock 105 private CachedBluetoothDevice mCachedDevice; 106 @Mock 107 private BluetoothDevice mDevice; 108 109 private ShadowApplicationPackageManager mPackageManager; 110 private PreferenceGroup mPreferenceGroup; 111 private Context mContext; 112 private Preference mPreference; 113 private ConnectedDeviceGroupController mConnectedDeviceGroupController; 114 115 @Rule 116 public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); 117 118 @Before setUp()119 public void setUp() { 120 MockitoAnnotations.initMocks(this); 121 122 mContext = spy(RuntimeEnvironment.application); 123 mPreference = new Preference(mContext); 124 mPreference.setKey(PREFERENCE_KEY_1); 125 mPackageManager = (ShadowApplicationPackageManager) Shadows.shadowOf( 126 mContext.getPackageManager()); 127 mPreferenceGroup = spy(new PreferenceScreen(mContext, null)); 128 when(mPreferenceGroup.getPreferenceManager()).thenReturn(mPreferenceManager); 129 doReturn(mContext).when(mDashboardFragment).getContext(); 130 mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, true); 131 when(mContext.getSystemService(InputManager.class)).thenReturn(mInputManager); 132 when(mInputManager.getInputDeviceIds()).thenReturn(new int[]{}); 133 134 ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBluetoothManager; 135 mLocalBluetoothManager = Utils.getLocalBtManager(mContext); 136 when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCachedDeviceManager); 137 138 mConnectedDeviceGroupController = new ConnectedDeviceGroupController(mContext); 139 mConnectedDeviceGroupController.init(mConnectedBluetoothDeviceUpdater, 140 mConnectedUsbDeviceUpdater, mConnectedDockUpdater, mStylusDeviceUpdater); 141 mConnectedDeviceGroupController.mPreferenceGroup = mPreferenceGroup; 142 143 when(mCachedDevice.getName()).thenReturn(DEVICE_NAME); 144 when(mCachedDevice.getDevice()).thenReturn(mDevice); 145 when(mCachedDeviceManager.getCachedDevicesCopy()).thenReturn( 146 ImmutableList.of(mCachedDevice)); 147 148 FeatureFlagUtils.setEnabled(mContext, FeatureFlagUtils.SETTINGS_SHOW_STYLUS_PREFERENCES, 149 true); 150 } 151 152 @Test onDeviceAdded_firstAdd_becomeVisibleAndPreferenceAdded()153 public void onDeviceAdded_firstAdd_becomeVisibleAndPreferenceAdded() { 154 mConnectedDeviceGroupController.onDeviceAdded(mPreference); 155 156 assertThat(mPreferenceGroup.isVisible()).isTrue(); 157 assertThat((Preference) mPreferenceGroup.findPreference(PREFERENCE_KEY_1)) 158 .isEqualTo(mPreference); 159 } 160 161 @Test onDeviceRemoved_lastRemove_becomeInvisibleAndPreferenceRemoved()162 public void onDeviceRemoved_lastRemove_becomeInvisibleAndPreferenceRemoved() { 163 mPreferenceGroup.addPreference(mPreference); 164 165 mConnectedDeviceGroupController.onDeviceRemoved(mPreference); 166 167 assertThat(mPreferenceGroup.isVisible()).isFalse(); 168 assertThat(mPreferenceGroup.getPreferenceCount()).isEqualTo(0); 169 } 170 171 @Test onDeviceRemoved_notLastRemove_stillVisible()172 public void onDeviceRemoved_notLastRemove_stillVisible() { 173 mPreferenceGroup.setVisible(true); 174 mPreferenceGroup.addPreference(mPreference); 175 mPreferenceGroup.addPreference(new Preference(mContext)); 176 177 mConnectedDeviceGroupController.onDeviceRemoved(mPreference); 178 179 assertThat(mPreferenceGroup.isVisible()).isTrue(); 180 } 181 182 @Test displayPreference_becomeInvisible()183 public void displayPreference_becomeInvisible() { 184 doReturn(mPreferenceGroup).when(mPreferenceScreen).findPreference(anyString()); 185 186 mConnectedDeviceGroupController.displayPreference(mPreferenceScreen); 187 188 assertThat(mPreferenceGroup.isVisible()).isFalse(); 189 } 190 191 @Test onStart_shouldRegisterUpdaters()192 public void onStart_shouldRegisterUpdaters() { 193 // register the callback in onStart() 194 mConnectedDeviceGroupController.onStart(); 195 196 verify(mConnectedBluetoothDeviceUpdater).registerCallback(); 197 verify(mConnectedUsbDeviceUpdater).registerCallback(); 198 verify(mConnectedDockUpdater).registerCallback(); 199 verify(mConnectedBluetoothDeviceUpdater).refreshPreference(); 200 verify(mStylusDeviceUpdater).registerCallback(); 201 } 202 203 @Test onStop_shouldUnregisterUpdaters()204 public void onStop_shouldUnregisterUpdaters() { 205 // unregister the callback in onStop() 206 mConnectedDeviceGroupController.onStop(); 207 verify(mConnectedBluetoothDeviceUpdater).unregisterCallback(); 208 verify(mConnectedUsbDeviceUpdater).unregisterCallback(); 209 verify(mConnectedDockUpdater).unregisterCallback(); 210 verify(mStylusDeviceUpdater).unregisterCallback(); 211 } 212 213 @Test getAvailabilityStatus_noBluetoothUsbDockFeature_returnUnSupported()214 public void getAvailabilityStatus_noBluetoothUsbDockFeature_returnUnSupported() { 215 mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, false); 216 mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_ACCESSORY, false); 217 mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_HOST, false); 218 mConnectedDeviceGroupController.init(mConnectedBluetoothDeviceUpdater, 219 mConnectedUsbDeviceUpdater, null, null); 220 221 assertThat(mConnectedDeviceGroupController.getAvailabilityStatus()).isEqualTo( 222 UNSUPPORTED_ON_DEVICE); 223 } 224 225 @Test getAvailabilityStatus_BluetoothFeature_returnSupported()226 public void getAvailabilityStatus_BluetoothFeature_returnSupported() { 227 mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, true); 228 mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_ACCESSORY, false); 229 mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_HOST, false); 230 mConnectedDeviceGroupController.init(mConnectedBluetoothDeviceUpdater, 231 mConnectedUsbDeviceUpdater, null, null); 232 233 assertThat(mConnectedDeviceGroupController.getAvailabilityStatus()).isEqualTo( 234 AVAILABLE_UNSEARCHABLE); 235 } 236 237 @Test getAvailabilityStatus_haveUsbFeature_returnSupported()238 public void getAvailabilityStatus_haveUsbFeature_returnSupported() { 239 mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, false); 240 mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_ACCESSORY, false); 241 mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_HOST, true); 242 mConnectedDeviceGroupController.init(mConnectedBluetoothDeviceUpdater, 243 mConnectedUsbDeviceUpdater, null, null); 244 245 assertThat(mConnectedDeviceGroupController.getAvailabilityStatus()).isEqualTo( 246 AVAILABLE_UNSEARCHABLE); 247 } 248 249 @Test getAvailabilityStatus_haveDockFeature_returnSupported()250 public void getAvailabilityStatus_haveDockFeature_returnSupported() { 251 mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, false); 252 mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_ACCESSORY, false); 253 mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_HOST, false); 254 mConnectedDeviceGroupController.init(mConnectedBluetoothDeviceUpdater, 255 mConnectedUsbDeviceUpdater, mConnectedDockUpdater, null); 256 257 assertThat(mConnectedDeviceGroupController.getAvailabilityStatus()).isEqualTo( 258 AVAILABLE_UNSEARCHABLE); 259 } 260 261 262 @Test getAvailabilityStatus_noUsiStylusFeature_returnUnSupported()263 public void getAvailabilityStatus_noUsiStylusFeature_returnUnSupported() { 264 mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, false); 265 mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_ACCESSORY, false); 266 mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_HOST, false); 267 when(mInputManager.getInputDeviceIds()).thenReturn(new int[]{0}); 268 when(mInputManager.getInputDevice(0)).thenReturn(new InputDevice.Builder().setSources( 269 InputDevice.SOURCE_DPAD).setExternal(false).build()); 270 271 mConnectedDeviceGroupController.init(mConnectedBluetoothDeviceUpdater, 272 mConnectedUsbDeviceUpdater, null, mStylusDeviceUpdater); 273 274 assertThat(mConnectedDeviceGroupController.getAvailabilityStatus()).isEqualTo( 275 UNSUPPORTED_ON_DEVICE); 276 } 277 278 @Test getAvailabilityStatus_haveUsiStylusFeature_returnSupported()279 public void getAvailabilityStatus_haveUsiStylusFeature_returnSupported() { 280 mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, false); 281 mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_ACCESSORY, false); 282 mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_HOST, false); 283 when(mInputManager.getInputDeviceIds()).thenReturn(new int[]{0}); 284 when(mInputManager.getInputDevice(0)).thenReturn(new InputDevice.Builder().setSources( 285 InputDevice.SOURCE_STYLUS).setExternal(false).build()); 286 287 mConnectedDeviceGroupController.init(mConnectedBluetoothDeviceUpdater, 288 mConnectedUsbDeviceUpdater, mConnectedDockUpdater, mStylusDeviceUpdater); 289 290 assertThat(mConnectedDeviceGroupController.getAvailabilityStatus()).isEqualTo( 291 AVAILABLE_UNSEARCHABLE); 292 } 293 294 @Test init_noBluetoothAndUsbFeature_doesNotCrash()295 public void init_noBluetoothAndUsbFeature_doesNotCrash() { 296 DashboardFragment fragment = mock(DashboardFragment.class); 297 when(fragment.getContext()).thenReturn(mContext); 298 when(mPreferenceScreen.findPreference(anyString())).thenReturn(mPreferenceGroup); 299 mPackageManager.setSystemFeature(PackageManager.FEATURE_BLUETOOTH, false); 300 mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_ACCESSORY, false); 301 mPackageManager.setSystemFeature(PackageManager.FEATURE_USB_HOST, false); 302 303 mConnectedDeviceGroupController.init(fragment); 304 mConnectedDeviceGroupController.displayPreference(mPreferenceScreen); 305 mConnectedDeviceGroupController.onStart(); 306 mConnectedDeviceGroupController.onStop(); 307 } 308 309 @Test 310 @EnableFlags(Flags.FLAG_ENABLE_BONDED_BLUETOOTH_DEVICE_SEARCHABLE) updateDynamicRawDataToIndex_deviceNotBonded_deviceIsNotSearchable()311 public void updateDynamicRawDataToIndex_deviceNotBonded_deviceIsNotSearchable() { 312 when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_NONE); 313 List<SearchIndexableRaw> searchData = new ArrayList<>(); 314 315 mConnectedDeviceGroupController.updateDynamicRawDataToIndex(searchData); 316 317 assertThat(searchData).isEmpty(); 318 } 319 320 @Test 321 @EnableFlags(Flags.FLAG_ENABLE_BONDED_BLUETOOTH_DEVICE_SEARCHABLE) updateDynamicRawDataToIndex_deviceBonded_deviceIsSearchable()322 public void updateDynamicRawDataToIndex_deviceBonded_deviceIsSearchable() { 323 when(mDevice.getBondState()).thenReturn(BluetoothDevice.BOND_BONDED); 324 List<SearchIndexableRaw> searchData = new ArrayList<>(); 325 326 mConnectedDeviceGroupController.updateDynamicRawDataToIndex(searchData); 327 328 assertThat(searchData).isNotEmpty(); 329 assertThat(searchData.get(0).key).contains(DEVICE_NAME); 330 } 331 } 332