1 /* 2 * Copyright (C) 2023 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.systemui.media.dialog; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.mockito.Mockito.any; 22 import static org.mockito.Mockito.anyBoolean; 23 import static org.mockito.Mockito.mock; 24 import static org.mockito.Mockito.never; 25 import static org.mockito.Mockito.times; 26 import static org.mockito.Mockito.verify; 27 import static org.mockito.Mockito.when; 28 29 import android.app.KeyguardManager; 30 import android.bluetooth.BluetoothDevice; 31 import android.bluetooth.BluetoothLeBroadcastMetadata; 32 import android.bluetooth.BluetoothLeBroadcastReceiveState; 33 import android.media.AudioManager; 34 import android.media.session.MediaSessionManager; 35 import android.os.PowerExemptionManager; 36 import android.testing.TestableLooper; 37 import android.view.View; 38 import android.widget.EditText; 39 import android.widget.ImageView; 40 import android.widget.TextView; 41 42 import androidx.test.ext.junit.runners.AndroidJUnit4; 43 import androidx.test.filters.SmallTest; 44 45 import com.android.settingslib.bluetooth.CachedBluetoothDevice; 46 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast; 47 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant; 48 import com.android.settingslib.bluetooth.LocalBluetoothManager; 49 import com.android.settingslib.bluetooth.LocalBluetoothProfileManager; 50 import com.android.settingslib.media.BluetoothMediaDevice; 51 import com.android.settingslib.media.LocalMediaManager; 52 import com.android.settingslib.media.MediaDevice; 53 import com.android.systemui.SysuiTestCase; 54 import com.android.systemui.animation.DialogTransitionAnimator; 55 import com.android.systemui.broadcast.BroadcastSender; 56 import com.android.systemui.flags.FeatureFlags; 57 import com.android.systemui.media.nearby.NearbyMediaDevicesManager; 58 import com.android.systemui.plugins.ActivityStarter; 59 import com.android.systemui.res.R; 60 import com.android.systemui.settings.UserTracker; 61 import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection; 62 63 import com.google.common.base.Strings; 64 65 import org.junit.After; 66 import org.junit.Before; 67 import org.junit.Test; 68 import org.junit.runner.RunWith; 69 70 import java.nio.charset.StandardCharsets; 71 import java.util.ArrayList; 72 import java.util.List; 73 74 @SmallTest 75 @RunWith(AndroidJUnit4.class) 76 @TestableLooper.RunWithLooper 77 public class MediaOutputBroadcastDialogTest extends SysuiTestCase { 78 79 private static final String TEST_PACKAGE = "test_package"; 80 private static final String BROADCAST_NAME_TEST = "Broadcast_name_test"; 81 private static final String BROADCAST_CODE_TEST = "112233"; 82 private static final String BROADCAST_CODE_UPDATE_TEST = "11223344"; 83 84 // Mock 85 private final MediaSessionManager mMediaSessionManager = mock(MediaSessionManager.class); 86 private final LocalBluetoothManager mLocalBluetoothManager = mock(LocalBluetoothManager.class); 87 private final LocalBluetoothProfileManager mLocalBluetoothProfileManager = mock( 88 LocalBluetoothProfileManager.class); 89 private final LocalBluetoothLeBroadcast mLocalBluetoothLeBroadcast = mock( 90 LocalBluetoothLeBroadcast.class); 91 private final LocalBluetoothLeBroadcastAssistant mLocalBluetoothLeBroadcastAssistant = mock( 92 LocalBluetoothLeBroadcastAssistant.class); 93 private final BluetoothLeBroadcastMetadata mBluetoothLeBroadcastMetadata = mock( 94 BluetoothLeBroadcastMetadata.class); 95 private final BluetoothLeBroadcastReceiveState mBluetoothLeBroadcastReceiveState = mock( 96 BluetoothLeBroadcastReceiveState.class); 97 private final ActivityStarter mStarter = mock(ActivityStarter.class); 98 private final BroadcastSender mBroadcastSender = mock(BroadcastSender.class); 99 private final LocalMediaManager mLocalMediaManager = mock(LocalMediaManager.class); 100 private final MediaDevice mBluetoothMediaDevice = mock(BluetoothMediaDevice.class); 101 private final BluetoothDevice mBluetoothFirstDevice = mock(BluetoothDevice.class); 102 private final BluetoothDevice mBluetoothSecondDevice = mock(BluetoothDevice.class); 103 private final CachedBluetoothDevice mCachedBluetoothDevice = mock(CachedBluetoothDevice.class); 104 private final CommonNotifCollection mNotifCollection = mock(CommonNotifCollection.class); 105 private final DialogTransitionAnimator mDialogTransitionAnimator = mock( 106 DialogTransitionAnimator.class); 107 private final NearbyMediaDevicesManager mNearbyMediaDevicesManager = mock( 108 NearbyMediaDevicesManager.class); 109 private final AudioManager mAudioManager = mock(AudioManager.class); 110 private PowerExemptionManager mPowerExemptionManager = mock(PowerExemptionManager.class); 111 private KeyguardManager mKeyguardManager = mock(KeyguardManager.class); 112 private FeatureFlags mFlags = mock(FeatureFlags.class); 113 private UserTracker mUserTracker = mock(UserTracker.class); 114 115 private MediaOutputBroadcastDialog mMediaOutputBroadcastDialog; 116 private MediaOutputController mMediaOutputController; 117 118 @Before setUp()119 public void setUp() { 120 when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBluetoothProfileManager); 121 when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(null); 122 when(mLocalBluetoothProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(null); 123 when(mLocalBluetoothLeBroadcast.getProgramInfo()).thenReturn(BROADCAST_NAME_TEST); 124 when(mLocalBluetoothLeBroadcast.getBroadcastCode()).thenReturn( 125 BROADCAST_CODE_TEST.getBytes(StandardCharsets.UTF_8)); 126 127 mMediaOutputController = 128 new MediaOutputController( 129 mContext, 130 TEST_PACKAGE, 131 mContext.getUser(), 132 /* token */ null, 133 mMediaSessionManager, 134 mLocalBluetoothManager, 135 mStarter, 136 mNotifCollection, 137 mDialogTransitionAnimator, 138 mNearbyMediaDevicesManager, 139 mAudioManager, 140 mPowerExemptionManager, 141 mKeyguardManager, 142 mFlags, 143 mUserTracker); 144 mMediaOutputController.mLocalMediaManager = mLocalMediaManager; 145 mMediaOutputBroadcastDialog = new MediaOutputBroadcastDialog(mContext, false, 146 mBroadcastSender, mMediaOutputController); 147 mMediaOutputBroadcastDialog.show(); 148 } 149 150 @After tearDown()151 public void tearDown() { 152 if (mMediaOutputBroadcastDialog.mAlertDialog != null){ 153 mMediaOutputBroadcastDialog.mAlertDialog.dismiss(); 154 } 155 mMediaOutputBroadcastDialog.dismiss(); 156 } 157 158 @Test startBroadcastWithConnectedDevices_noBroadcastMetadata_failToAddSource()159 public void startBroadcastWithConnectedDevices_noBroadcastMetadata_failToAddSource() { 160 when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( 161 mLocalBluetoothLeBroadcast); 162 when(mLocalBluetoothLeBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn(null); 163 when(mLocalBluetoothProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn( 164 mLocalBluetoothLeBroadcastAssistant); 165 166 mMediaOutputBroadcastDialog.startBroadcastWithConnectedDevices(); 167 168 verify(mLocalBluetoothLeBroadcastAssistant, never()).addSource(any(), any(), anyBoolean()); 169 } 170 171 @Test startBroadcastWithConnectedDevices_noConnectedMediaDevice_failToAddSource()172 public void startBroadcastWithConnectedDevices_noConnectedMediaDevice_failToAddSource() { 173 when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( 174 mLocalBluetoothLeBroadcast); 175 when(mLocalBluetoothLeBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn( 176 mBluetoothLeBroadcastMetadata); 177 when(mLocalBluetoothProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn( 178 mLocalBluetoothLeBroadcastAssistant); 179 when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(null); 180 181 mMediaOutputBroadcastDialog.startBroadcastWithConnectedDevices(); 182 183 verify(mLocalBluetoothLeBroadcastAssistant, never()).addSource(any(), any(), anyBoolean()); 184 } 185 186 @Test startBroadcastWithConnectedDevices_hasBroadcastSource_failToAddSource()187 public void startBroadcastWithConnectedDevices_hasBroadcastSource_failToAddSource() { 188 when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( 189 mLocalBluetoothLeBroadcast); 190 when(mLocalBluetoothLeBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn( 191 mBluetoothLeBroadcastMetadata); 192 when(mLocalBluetoothProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn( 193 mLocalBluetoothLeBroadcastAssistant); 194 when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mBluetoothMediaDevice); 195 when(((BluetoothMediaDevice) mBluetoothMediaDevice).getCachedDevice()) 196 .thenReturn(mCachedBluetoothDevice); 197 when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothFirstDevice); 198 List<BluetoothLeBroadcastReceiveState> sourceList = new ArrayList<>(); 199 sourceList.add(mBluetoothLeBroadcastReceiveState); 200 when(mLocalBluetoothLeBroadcastAssistant.getAllSources(mBluetoothFirstDevice)).thenReturn( 201 sourceList); 202 203 mMediaOutputBroadcastDialog.startBroadcastWithConnectedDevices(); 204 205 verify(mLocalBluetoothLeBroadcastAssistant, never()).addSource(any(), any(), anyBoolean()); 206 } 207 208 @Test startBroadcastWithConnectedDevices_noBroadcastSource_failToAddSource()209 public void startBroadcastWithConnectedDevices_noBroadcastSource_failToAddSource() { 210 when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( 211 mLocalBluetoothLeBroadcast); 212 when(mLocalBluetoothProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn( 213 mLocalBluetoothLeBroadcastAssistant); 214 when(mLocalBluetoothLeBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn( 215 mBluetoothLeBroadcastMetadata); 216 when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mBluetoothMediaDevice); 217 when(mBluetoothMediaDevice.isBLEDevice()).thenReturn(true); 218 when(((BluetoothMediaDevice) mBluetoothMediaDevice).getCachedDevice()).thenReturn( 219 mCachedBluetoothDevice); 220 when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothFirstDevice); 221 List<BluetoothLeBroadcastReceiveState> sourceList = new ArrayList<>(); 222 when(mLocalBluetoothLeBroadcastAssistant.getAllSources(mBluetoothFirstDevice)).thenReturn( 223 sourceList); 224 List<BluetoothDevice> connectedDevicesList = new ArrayList<>(); 225 connectedDevicesList.add(mBluetoothFirstDevice); 226 when(mLocalBluetoothLeBroadcastAssistant.getConnectedDevices()).thenReturn( 227 connectedDevicesList); 228 229 mMediaOutputBroadcastDialog.startBroadcastWithConnectedDevices(); 230 231 verify(mLocalBluetoothLeBroadcastAssistant, times(1)).addSource(any(), any(), anyBoolean()); 232 } 233 234 @Test handleLeBroadcastMetadataChanged_checkBroadcastName()235 public void handleLeBroadcastMetadataChanged_checkBroadcastName() { 236 when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( 237 mLocalBluetoothLeBroadcast); 238 final TextView broadcastName = mMediaOutputBroadcastDialog.mDialogView 239 .requireViewById(R.id.broadcast_name_summary); 240 241 mMediaOutputBroadcastDialog.handleLeBroadcastMetadataChanged(); 242 243 assertThat(broadcastName.getText().toString()).isEqualTo(BROADCAST_NAME_TEST); 244 } 245 246 @Test handleLeBroadcastMetadataChanged_checkBroadcastCode()247 public void handleLeBroadcastMetadataChanged_checkBroadcastCode() { 248 when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( 249 mLocalBluetoothLeBroadcast); 250 251 final TextView broadcastCode = mMediaOutputBroadcastDialog.mDialogView 252 .requireViewById(R.id.broadcast_code_summary); 253 254 mMediaOutputBroadcastDialog.handleLeBroadcastMetadataChanged(); 255 256 assertThat(broadcastCode.getText().toString()).isEqualTo(BROADCAST_CODE_TEST); 257 } 258 259 @Test updateBroadcastInfo_stopBroadcastFailed_handleFailedUi()260 public void updateBroadcastInfo_stopBroadcastFailed_handleFailedUi() { 261 ImageView broadcastCodeEdit = mMediaOutputBroadcastDialog.mDialogView 262 .requireViewById(R.id.broadcast_code_edit); 263 TextView broadcastCode = mMediaOutputBroadcastDialog.mDialogView.requireViewById( 264 R.id.broadcast_code_summary); 265 broadcastCode.setText(BROADCAST_CODE_UPDATE_TEST); 266 when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn(null); 267 broadcastCodeEdit.callOnClick(); 268 269 mMediaOutputBroadcastDialog.updateBroadcastInfo(true, BROADCAST_CODE_UPDATE_TEST); 270 assertThat(mMediaOutputBroadcastDialog.getRetryCount()).isEqualTo(1); 271 272 mMediaOutputBroadcastDialog.updateBroadcastInfo(true, BROADCAST_CODE_UPDATE_TEST); 273 assertThat(mMediaOutputBroadcastDialog.getRetryCount()).isEqualTo(2); 274 275 // It will be the MAX Retry Count = 3 276 mMediaOutputBroadcastDialog.updateBroadcastInfo(true, BROADCAST_CODE_UPDATE_TEST); 277 assertThat(mMediaOutputBroadcastDialog.getRetryCount()).isEqualTo(0); 278 } 279 280 @Test afterTextChanged_nameLengthMoreThanMax_showErrorMessage()281 public void afterTextChanged_nameLengthMoreThanMax_showErrorMessage() { 282 ImageView broadcastNameEdit = mMediaOutputBroadcastDialog.mDialogView 283 .requireViewById(R.id.broadcast_name_edit); 284 TextView broadcastName = mMediaOutputBroadcastDialog.mDialogView.requireViewById( 285 R.id.broadcast_name_summary); 286 broadcastName.setText(BROADCAST_NAME_TEST); 287 when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( 288 mLocalBluetoothLeBroadcast); 289 broadcastNameEdit.callOnClick(); 290 EditText editText = mMediaOutputBroadcastDialog.mAlertDialog.findViewById( 291 R.id.broadcast_edit_text); 292 TextView broadcastErrorMessage = mMediaOutputBroadcastDialog.mAlertDialog.findViewById( 293 R.id.broadcast_error_message); 294 assertThat(broadcastErrorMessage.getVisibility()).isEqualTo(View.INVISIBLE); 295 296 // input the invalid text 297 String moreThanMax = Strings.repeat("a", 298 MediaOutputBroadcastDialog.BROADCAST_NAME_MAX_LENGTH + 3); 299 editText.setText(moreThanMax); 300 301 assertThat(broadcastErrorMessage.getVisibility()).isEqualTo(View.VISIBLE); 302 } 303 304 @Test afterTextChanged_enterValidNameAfterLengthMoreThanMax_noErrorMessage()305 public void afterTextChanged_enterValidNameAfterLengthMoreThanMax_noErrorMessage() { 306 ImageView broadcastNameEdit = mMediaOutputBroadcastDialog.mDialogView 307 .requireViewById(R.id.broadcast_name_edit); 308 TextView broadcastName = mMediaOutputBroadcastDialog.mDialogView.requireViewById( 309 R.id.broadcast_name_summary); 310 broadcastName.setText(BROADCAST_NAME_TEST); 311 when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( 312 mLocalBluetoothLeBroadcast); 313 broadcastNameEdit.callOnClick(); 314 EditText editText = mMediaOutputBroadcastDialog.mAlertDialog.findViewById( 315 R.id.broadcast_edit_text); 316 TextView broadcastErrorMessage = mMediaOutputBroadcastDialog.mAlertDialog.findViewById( 317 R.id.broadcast_error_message); 318 assertThat(broadcastErrorMessage.getVisibility()).isEqualTo(View.INVISIBLE); 319 320 // input the invalid text 321 String testString = Strings.repeat("a", 322 MediaOutputBroadcastDialog.BROADCAST_NAME_MAX_LENGTH + 2); 323 editText.setText(testString); 324 assertThat(broadcastErrorMessage.getVisibility()).isEqualTo(View.VISIBLE); 325 326 // input the valid text 327 testString = Strings.repeat("b", 328 MediaOutputBroadcastDialog.BROADCAST_NAME_MAX_LENGTH - 100); 329 editText.setText(testString); 330 331 assertThat(broadcastErrorMessage.getVisibility()).isEqualTo(View.INVISIBLE); 332 } 333 334 @Test afterTextChanged_codeLengthMoreThanMax_showErrorMessage()335 public void afterTextChanged_codeLengthMoreThanMax_showErrorMessage() { 336 ImageView broadcastCodeEdit = mMediaOutputBroadcastDialog.mDialogView 337 .requireViewById(R.id.broadcast_code_edit); 338 TextView broadcastCode = mMediaOutputBroadcastDialog.mDialogView.requireViewById( 339 R.id.broadcast_code_summary); 340 broadcastCode.setText(BROADCAST_CODE_UPDATE_TEST); 341 when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( 342 mLocalBluetoothLeBroadcast); 343 broadcastCodeEdit.callOnClick(); 344 EditText editText = mMediaOutputBroadcastDialog.mAlertDialog.findViewById( 345 R.id.broadcast_edit_text); 346 TextView broadcastErrorMessage = mMediaOutputBroadcastDialog.mAlertDialog.findViewById( 347 R.id.broadcast_error_message); 348 assertThat(broadcastErrorMessage.getVisibility()).isEqualTo(View.INVISIBLE); 349 350 // input the invalid text 351 String moreThanMax = Strings.repeat("a", 352 MediaOutputBroadcastDialog.BROADCAST_CODE_MAX_LENGTH + 1); 353 editText.setText(moreThanMax); 354 355 assertThat(broadcastErrorMessage.getVisibility()).isEqualTo(View.VISIBLE); 356 } 357 358 @Test afterTextChanged_codeLengthLessThanMin_showErrorMessage()359 public void afterTextChanged_codeLengthLessThanMin_showErrorMessage() { 360 ImageView broadcastCodeEdit = mMediaOutputBroadcastDialog.mDialogView 361 .requireViewById(R.id.broadcast_code_edit); 362 TextView broadcastCode = mMediaOutputBroadcastDialog.mDialogView.requireViewById( 363 R.id.broadcast_code_summary); 364 broadcastCode.setText(BROADCAST_CODE_UPDATE_TEST); 365 when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( 366 mLocalBluetoothLeBroadcast); 367 broadcastCodeEdit.callOnClick(); 368 EditText editText = mMediaOutputBroadcastDialog.mAlertDialog.findViewById( 369 R.id.broadcast_edit_text); 370 TextView broadcastErrorMessage = mMediaOutputBroadcastDialog.mAlertDialog.findViewById( 371 R.id.broadcast_error_message); 372 assertThat(broadcastErrorMessage.getVisibility()).isEqualTo(View.INVISIBLE); 373 374 // input the invalid text 375 String moreThanMax = Strings.repeat("a", 376 MediaOutputBroadcastDialog.BROADCAST_CODE_MIN_LENGTH - 1); 377 editText.setText(moreThanMax); 378 379 assertThat(broadcastErrorMessage.getVisibility()).isEqualTo(View.VISIBLE); 380 } 381 382 @Test addSourceToAllConnectedDevices()383 public void addSourceToAllConnectedDevices() { 384 when(mLocalBluetoothProfileManager.getLeAudioBroadcastProfile()).thenReturn( 385 mLocalBluetoothLeBroadcast); 386 when(mLocalBluetoothProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn( 387 mLocalBluetoothLeBroadcastAssistant); 388 when(mLocalBluetoothLeBroadcast.getLatestBluetoothLeBroadcastMetadata()).thenReturn( 389 mBluetoothLeBroadcastMetadata); 390 when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mBluetoothMediaDevice); 391 when(mBluetoothMediaDevice.isBLEDevice()).thenReturn(true); 392 when(((BluetoothMediaDevice) mBluetoothMediaDevice).getCachedDevice()) 393 .thenReturn(mCachedBluetoothDevice); 394 when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothFirstDevice); 395 List<BluetoothLeBroadcastReceiveState> sourceList = new ArrayList<>(); 396 when(mLocalBluetoothLeBroadcastAssistant.getAllSources(mBluetoothFirstDevice)).thenReturn( 397 sourceList); 398 List<BluetoothDevice> connectedDevicesList = new ArrayList<>(); 399 connectedDevicesList.add(mBluetoothFirstDevice); 400 connectedDevicesList.add(mBluetoothSecondDevice); 401 when(mLocalBluetoothLeBroadcastAssistant.getConnectedDevices()).thenReturn( 402 connectedDevicesList); 403 404 mMediaOutputBroadcastDialog.startBroadcastWithConnectedDevices(); 405 406 verify(mLocalBluetoothLeBroadcastAssistant, times(2)).addSource(any(), any(), anyBoolean()); 407 } 408 409 } 410