1 /* 2 * Copyright (C) 2020 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.car.media; 18 19 import static android.car.media.CarMediaManager.MEDIA_SOURCE_MODE_BROWSE; 20 import static android.car.media.CarMediaManager.MEDIA_SOURCE_MODE_PLAYBACK; 21 22 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 23 24 import static com.google.common.truth.Truth.assertThat; 25 26 import static org.mockito.ArgumentMatchers.any; 27 import static org.mockito.ArgumentMatchers.anyInt; 28 import static org.mockito.ArgumentMatchers.anyLong; 29 import static org.mockito.ArgumentMatchers.anyString; 30 import static org.mockito.ArgumentMatchers.notNull; 31 import static org.mockito.Mockito.doAnswer; 32 import static org.mockito.Mockito.mock; 33 import static org.mockito.Mockito.never; 34 import static org.mockito.Mockito.times; 35 import static org.mockito.Mockito.verify; 36 import static org.mockito.Mockito.when; 37 38 import android.app.ActivityManager; 39 import android.car.Car; 40 import android.car.media.ICarMediaSourceListener; 41 import android.car.test.mocks.AbstractExtendedMockitoTestCase; 42 import android.car.test.mocks.AndroidMockitoHelper; 43 import android.content.ComponentName; 44 import android.content.Context; 45 import android.content.SharedPreferences; 46 import android.content.pm.PackageManager; 47 import android.content.pm.ResolveInfo; 48 import android.content.pm.ServiceInfo; 49 import android.content.res.Resources; 50 import android.media.session.MediaController; 51 import android.media.session.MediaSession; 52 import android.media.session.MediaSessionManager; 53 import android.media.session.PlaybackState; 54 import android.os.Bundle; 55 import android.os.IBinder; 56 import android.os.UserHandle; 57 import android.os.UserManager; 58 59 import com.android.car.CarLocalServices; 60 import com.android.car.CarLog; 61 import com.android.car.CarMediaService; 62 import com.android.car.R; 63 import com.android.car.power.CarPowerManagementService; 64 import com.android.car.systeminterface.SystemInterface; 65 import com.android.car.user.CarUserService; 66 import com.android.car.user.UserHandleHelper; 67 import com.android.internal.app.IVoiceInteractionManagerService; 68 69 import org.junit.After; 70 import org.junit.Before; 71 import org.junit.Test; 72 import org.mockito.Mock; 73 74 import java.util.ArrayList; 75 import java.util.List; 76 77 public final class CarMediaServiceTest extends AbstractExtendedMockitoTestCase { 78 79 private static final String MEDIA_PACKAGE = "test.package"; 80 private static final String MEDIA_PACKAGE2 = "test.package2"; 81 private static final String MEDIA_CLASS = "test_class"; 82 private static final String MEDIA_CLASS2 = "test_class2"; 83 84 private static final int TEST_USER_ID = 100; 85 86 private static final ComponentName MEDIA_COMPONENT = 87 new ComponentName(MEDIA_PACKAGE, MEDIA_CLASS); 88 private static final ComponentName MEDIA_COMPONENT2 = 89 new ComponentName(MEDIA_PACKAGE2, MEDIA_CLASS2); 90 91 @Mock private Context mContext; 92 @Mock private Resources mResources; 93 @Mock private CarUserService mUserService; 94 @Mock private UserManager mUserManager; 95 @Mock private PackageManager mPackageManager; 96 @Mock private MediaSessionManager mMediaSessionManager; 97 @Mock private SystemInterface mMockSystemInterface; 98 @Mock private IVoiceInteractionManagerService mMockVoiceService; 99 @Mock private CarPowerManagementService mMockCarPowerManagementService; 100 @Mock private UserHandleHelper mUserHandleHelper; 101 @Mock private SharedPreferences mMockSharedPreferences; 102 @Mock private SharedPreferences.Editor mMockSharedPreferencesEditor; 103 104 private CarMediaService mCarMediaService; 105 CarMediaServiceTest()106 public CarMediaServiceTest() { 107 super(CarLog.TAG_MEDIA); 108 } 109 110 @Override onSessionBuilder(CustomMockitoSessionBuilder builder)111 protected void onSessionBuilder(CustomMockitoSessionBuilder builder) { 112 builder.spyStatic(ActivityManager.class); 113 } 114 115 @Before setUp()116 public void setUp() { 117 when(mContext.checkCallingOrSelfPermission(anyString())) 118 .thenReturn(PackageManager.PERMISSION_GRANTED); 119 when(mContext.getPackageManager()).thenReturn(mPackageManager); 120 when(mContext.createContextAsUser(any(), anyInt())).thenReturn(mContext); 121 when(mContext.getSharedPreferences(anyString(), anyInt())) 122 .thenReturn(mMockSharedPreferences); 123 when(mMockSharedPreferences.edit()).thenReturn(mMockSharedPreferencesEditor); 124 when(mMockSharedPreferencesEditor.putInt(anyString(), anyInt())) 125 .thenReturn(mMockSharedPreferencesEditor); 126 when(mMockSharedPreferencesEditor.putLong(anyString(), anyLong())) 127 .thenReturn(mMockSharedPreferencesEditor); 128 when(mMockSharedPreferencesEditor.putString(anyString(), anyString())) 129 .thenReturn(mMockSharedPreferencesEditor); 130 131 doReturn(mResources).when(mContext).getResources(); 132 doReturn(mUserManager).when(mContext).getSystemService(UserManager.class); 133 UserHandle user = UserHandle.of(TEST_USER_ID); 134 AndroidMockitoHelper.mockAmGetCurrentUser(TEST_USER_ID); 135 when(mUserHandleHelper.isEphemeralUser(UserHandle.of(TEST_USER_ID))).thenReturn(false); 136 doReturn(mMediaSessionManager).when(mContext).getSystemService(MediaSessionManager.class); 137 138 mCarMediaService = new CarMediaService(mContext, mUserService, mUserHandleHelper); 139 CarLocalServices.addService(CarPowerManagementService.class, 140 mMockCarPowerManagementService); 141 } 142 143 @After tearDown()144 public void tearDown() { 145 CarLocalServices.removeServiceForTest(CarPowerManagementService.class); 146 } 147 148 @Test testSetMediaSource_ModePlaybackIndependent()149 public void testSetMediaSource_ModePlaybackIndependent() { 150 mCarMediaService.setIndependentPlaybackConfig(true); 151 initMediaService(); 152 153 mCarMediaService.setMediaSource(MEDIA_COMPONENT, MEDIA_SOURCE_MODE_PLAYBACK); 154 155 assertThat(mCarMediaService.getMediaSource(MEDIA_SOURCE_MODE_PLAYBACK)) 156 .isEqualTo(MEDIA_COMPONENT); 157 assertThat(mCarMediaService.getMediaSource(MEDIA_SOURCE_MODE_BROWSE)) 158 .isNotEqualTo(MEDIA_COMPONENT); 159 } 160 161 @Test testSetMediaSource_ModeBrowseIndependent()162 public void testSetMediaSource_ModeBrowseIndependent() { 163 mCarMediaService.setIndependentPlaybackConfig(true); 164 initMediaService(); 165 166 mCarMediaService.setMediaSource(MEDIA_COMPONENT, MEDIA_SOURCE_MODE_BROWSE); 167 168 assertThat(mCarMediaService.getMediaSource(MEDIA_SOURCE_MODE_BROWSE)) 169 .isEqualTo(MEDIA_COMPONENT); 170 assertThat(mCarMediaService.getMediaSource(MEDIA_SOURCE_MODE_PLAYBACK)) 171 .isNotEqualTo(MEDIA_COMPONENT); 172 } 173 174 @Test testSetMediaSource_ModePlaybackAndBrowseIndependent()175 public void testSetMediaSource_ModePlaybackAndBrowseIndependent() { 176 mCarMediaService.setIndependentPlaybackConfig(true); 177 initMediaService(); 178 179 mCarMediaService.setMediaSource(MEDIA_COMPONENT, MEDIA_SOURCE_MODE_BROWSE); 180 mCarMediaService.setMediaSource(MEDIA_COMPONENT2, MEDIA_SOURCE_MODE_PLAYBACK); 181 182 assertThat(mCarMediaService.getMediaSource(MEDIA_SOURCE_MODE_BROWSE)) 183 .isEqualTo(MEDIA_COMPONENT); 184 assertThat(mCarMediaService.getMediaSource(MEDIA_SOURCE_MODE_PLAYBACK)) 185 .isEqualTo(MEDIA_COMPONENT2); 186 } 187 188 @Test testSetMediaSource_Dependent()189 public void testSetMediaSource_Dependent() { 190 mCarMediaService.setIndependentPlaybackConfig(false); 191 initMediaService(); 192 193 mCarMediaService.setMediaSource(MEDIA_COMPONENT, MEDIA_SOURCE_MODE_PLAYBACK); 194 195 assertThat(mCarMediaService.getMediaSource(MEDIA_SOURCE_MODE_BROWSE)) 196 .isEqualTo(MEDIA_COMPONENT); 197 assertThat(mCarMediaService.getMediaSource(MEDIA_SOURCE_MODE_PLAYBACK)) 198 .isEqualTo(MEDIA_COMPONENT); 199 200 mCarMediaService.setMediaSource(MEDIA_COMPONENT2, MEDIA_SOURCE_MODE_BROWSE); 201 202 assertThat(mCarMediaService.getMediaSource(MEDIA_SOURCE_MODE_BROWSE)) 203 .isEqualTo(MEDIA_COMPONENT2); 204 assertThat(mCarMediaService.getMediaSource(MEDIA_SOURCE_MODE_PLAYBACK)) 205 .isEqualTo(MEDIA_COMPONENT2); 206 } 207 208 @Test testMediaSourceListener_Independent()209 public void testMediaSourceListener_Independent() throws Exception { 210 mCarMediaService.setIndependentPlaybackConfig(true); 211 initMediaService(); 212 ICarMediaSourceListener listenerPlayback = mockMediaSourceListener(); 213 ICarMediaSourceListener listenerBrowse = mockMediaSourceListener(); 214 215 mCarMediaService.registerMediaSourceListener(listenerPlayback, MEDIA_SOURCE_MODE_PLAYBACK); 216 mCarMediaService.registerMediaSourceListener(listenerBrowse, MEDIA_SOURCE_MODE_BROWSE); 217 mCarMediaService.setMediaSource(MEDIA_COMPONENT, MEDIA_SOURCE_MODE_PLAYBACK); 218 219 verify(listenerPlayback).onMediaSourceChanged(MEDIA_COMPONENT); 220 verify(listenerBrowse, never()).onMediaSourceChanged(any()); 221 } 222 223 @Test testMediaSourceListener_IndependentBrowse()224 public void testMediaSourceListener_IndependentBrowse() throws Exception { 225 mCarMediaService.setIndependentPlaybackConfig(true); 226 initMediaService(); 227 ICarMediaSourceListener listenerPlayback = mockMediaSourceListener(); 228 ICarMediaSourceListener listenerBrowse = mockMediaSourceListener(); 229 230 mCarMediaService.registerMediaSourceListener(listenerPlayback, MEDIA_SOURCE_MODE_PLAYBACK); 231 mCarMediaService.registerMediaSourceListener(listenerBrowse, MEDIA_SOURCE_MODE_BROWSE); 232 mCarMediaService.setMediaSource(MEDIA_COMPONENT, MEDIA_SOURCE_MODE_BROWSE); 233 234 verify(listenerBrowse).onMediaSourceChanged(MEDIA_COMPONENT); 235 verify(listenerPlayback, never()).onMediaSourceChanged(any()); 236 } 237 238 @Test testMediaSourceListener_Dependent()239 public void testMediaSourceListener_Dependent() throws Exception { 240 mCarMediaService.setIndependentPlaybackConfig(false); 241 initMediaService(); 242 ICarMediaSourceListener listenerPlayback = mockMediaSourceListener(); 243 ICarMediaSourceListener listenerBrowse = mockMediaSourceListener(); 244 245 mCarMediaService.registerMediaSourceListener(listenerPlayback, MEDIA_SOURCE_MODE_PLAYBACK); 246 mCarMediaService.registerMediaSourceListener(listenerBrowse, MEDIA_SOURCE_MODE_BROWSE); 247 mCarMediaService.setMediaSource(MEDIA_COMPONENT, MEDIA_SOURCE_MODE_PLAYBACK); 248 249 verify(listenerPlayback).onMediaSourceChanged(MEDIA_COMPONENT); 250 verify(listenerBrowse).onMediaSourceChanged(MEDIA_COMPONENT); 251 252 mCarMediaService.setMediaSource(MEDIA_COMPONENT, MEDIA_SOURCE_MODE_BROWSE); 253 254 verify(listenerPlayback).onMediaSourceChanged(MEDIA_COMPONENT); 255 verify(listenerBrowse).onMediaSourceChanged(MEDIA_COMPONENT); 256 } 257 258 @Test testMediaSourceListener_Unregister()259 public void testMediaSourceListener_Unregister() throws Exception { 260 initMediaService(); 261 ICarMediaSourceListener listener = mockMediaSourceListener(); 262 263 mCarMediaService.registerMediaSourceListener(listener, MEDIA_SOURCE_MODE_PLAYBACK); 264 mCarMediaService.unregisterMediaSourceListener(listener, MEDIA_SOURCE_MODE_PLAYBACK); 265 mCarMediaService.setMediaSource(MEDIA_COMPONENT, MEDIA_SOURCE_MODE_PLAYBACK); 266 267 verify(listener, never()).onMediaSourceChanged(MEDIA_COMPONENT); 268 } 269 270 @Test testDefaultMediaSource()271 public void testDefaultMediaSource() { 272 initMediaService(MEDIA_CLASS); 273 274 assertThat(mCarMediaService.getMediaSource(MEDIA_SOURCE_MODE_PLAYBACK)) 275 .isEqualTo(MEDIA_COMPONENT); 276 } 277 278 @Test testUnresolvedMediaPackage()279 public void testUnresolvedMediaPackage() { 280 initializeMockPackageManager(); 281 282 assertThat(mCarMediaService.isMediaService(MEDIA_COMPONENT)).isFalse(); 283 } 284 285 /** 286 * Tests that PlaybackState changing to PlaybackState#isActive 287 * will result the media source changing 288 */ 289 @Test testActiveSessionListener_StateActiveChangesSource()290 public void testActiveSessionListener_StateActiveChangesSource() { 291 mockPlaybackStateChange(createPlaybackState(PlaybackState.STATE_BUFFERING)); 292 293 initMediaService(MEDIA_CLASS, MEDIA_CLASS2); 294 295 assertThat(mCarMediaService.getMediaSource(MEDIA_SOURCE_MODE_PLAYBACK)) 296 .isEqualTo(MEDIA_COMPONENT2); 297 verify(mContext, times(2)).startForegroundService(any()); 298 } 299 300 // Tests that PlaybackState changing to STATE_PLAYING will result the media source changing 301 @Test testActiveSessionListener_StatePlayingChangesSource()302 public void testActiveSessionListener_StatePlayingChangesSource() { 303 mockPlaybackStateChange(createPlaybackState(PlaybackState.STATE_PLAYING)); 304 305 initMediaService(MEDIA_CLASS, MEDIA_CLASS2); 306 307 assertThat(mCarMediaService.getMediaSource(MEDIA_SOURCE_MODE_PLAYBACK)) 308 .isEqualTo(MEDIA_COMPONENT2); 309 verify(mContext, times(2)).startForegroundService(any()); 310 } 311 312 @Test testActiveSessionListener_StatePlayingNonMediaAppDoesntChangesSource()313 public void testActiveSessionListener_StatePlayingNonMediaAppDoesntChangesSource() { 314 mockPlaybackStateChange(createPlaybackState(PlaybackState.STATE_PLAYING)); 315 316 // setup media source info only for MEDIA Component 317 // second one will stay null 318 initMediaService(MEDIA_CLASS); 319 320 // New Media source should be null 321 assertThat(mCarMediaService.getMediaSource(MEDIA_SOURCE_MODE_PLAYBACK)).isNull(); 322 // service start should happen on init but not on media source change 323 verify(mContext, times(1)).startForegroundService(any()); 324 } 325 326 @Test testActiveSessionListener_IndependentBrowseUnchanged()327 public void testActiveSessionListener_IndependentBrowseUnchanged() { 328 mCarMediaService.setIndependentPlaybackConfig(true); 329 mockPlaybackStateChange(createPlaybackState(PlaybackState.STATE_PLAYING)); 330 331 initMediaService(MEDIA_CLASS, MEDIA_CLASS2); 332 333 assertThat(mCarMediaService.getMediaSource(MEDIA_SOURCE_MODE_PLAYBACK)) 334 .isEqualTo(MEDIA_COMPONENT2); 335 assertThat(mCarMediaService.getMediaSource(MEDIA_SOURCE_MODE_BROWSE)) 336 .isEqualTo(MEDIA_COMPONENT); 337 verify(mContext, times(2)).startForegroundService(any()); 338 } 339 340 @Test testActiveSessionListener_DependentBrowseChanged()341 public void testActiveSessionListener_DependentBrowseChanged() { 342 mCarMediaService.setIndependentPlaybackConfig(false); 343 mockPlaybackStateChange(createPlaybackState(PlaybackState.STATE_PLAYING)); 344 345 initMediaService(MEDIA_CLASS, MEDIA_CLASS2); 346 347 assertThat(mCarMediaService.getMediaSource(MEDIA_SOURCE_MODE_PLAYBACK)) 348 .isEqualTo(MEDIA_COMPONENT2); 349 assertThat(mCarMediaService.getMediaSource(MEDIA_SOURCE_MODE_BROWSE)) 350 .isEqualTo(MEDIA_COMPONENT2); 351 verify(mContext, times(2)).startForegroundService(any()); 352 } 353 354 @Test testActiveSessionListener_StatePaused()355 public void testActiveSessionListener_StatePaused() { 356 mockPlaybackStateChange(createPlaybackState(PlaybackState.STATE_PAUSED)); 357 358 initMediaService(MEDIA_CLASS, MEDIA_CLASS2); 359 360 assertThat(mCarMediaService.getMediaSource(MEDIA_SOURCE_MODE_PLAYBACK)) 361 .isEqualTo(MEDIA_COMPONENT); 362 verify(mContext, times(1)).startForegroundService(any()); 363 } 364 initMediaService(String... classesToResolve)365 private void initMediaService(String... classesToResolve) { 366 initializeMockPackageManager(classesToResolve); 367 mockUserUnlocked(true); 368 369 mCarMediaService.init(); 370 } 371 mockUserUnlocked(boolean unlocked)372 private void mockUserUnlocked(boolean unlocked) { 373 when(mUserManager.isUserUnlocked(any())).thenReturn(unlocked); 374 } 375 mockMediaSourceListener()376 private ICarMediaSourceListener mockMediaSourceListener() { 377 ICarMediaSourceListener listener = mock(ICarMediaSourceListener.class); 378 when(listener.asBinder()).thenReturn(mock(IBinder.class)); 379 return listener; 380 } 381 382 // This method invokes a playback state changed callback on a mock MediaController mockPlaybackStateChange(PlaybackState newState)383 private void mockPlaybackStateChange(PlaybackState newState) { 384 List<MediaController> controllers = new ArrayList<>(); 385 MediaController mockController = mock(MediaController.class); 386 when(mockController.getPackageName()).thenReturn(MEDIA_PACKAGE2); 387 when(mockController.getSessionToken()).thenReturn(mock(MediaSession.Token.class)); 388 Bundle sessionExtras = new Bundle(); 389 sessionExtras.putString(Car.CAR_EXTRA_BROWSE_SERVICE_FOR_SESSION, MEDIA_CLASS2); 390 when(mockController.getExtras()).thenReturn(sessionExtras); 391 392 doAnswer(invocation -> { 393 MediaController.Callback callback = invocation.getArgument(0); 394 callback.onPlaybackStateChanged(newState); 395 return null; 396 }).when(mockController).registerCallback(notNull()); 397 controllers.add(mockController); 398 399 doAnswer(invocation -> { 400 MediaSessionManager.OnActiveSessionsChangedListener callback = 401 invocation.getArgument(3); 402 callback.onActiveSessionsChanged(controllers); 403 return null; 404 }).when(mMediaSessionManager).addOnActiveSessionsChangedListener(any(), any(), any(), 405 any(MediaSessionManager.OnActiveSessionsChangedListener.class)); 406 } 407 408 // This method sets up PackageManager queries to return mocked media components if specified initializeMockPackageManager(String... classesToResolve)409 private void initializeMockPackageManager(String... classesToResolve) { 410 when(mContext.getString(R.string.config_defaultMediaSource)) 411 .thenReturn(MEDIA_COMPONENT.flattenToShortString()); 412 List<ResolveInfo> packageList = new ArrayList(); 413 for (String className : classesToResolve) { 414 ResolveInfo info = new ResolveInfo(); 415 ServiceInfo serviceInfo = new ServiceInfo(); 416 serviceInfo.name = className; 417 info.serviceInfo = serviceInfo; 418 packageList.add(info); 419 } 420 when(mPackageManager.queryIntentServicesAsUser(any(), anyInt(), any())) 421 .thenReturn(packageList); 422 } 423 createPlaybackState(@laybackState.State int state)424 private PlaybackState createPlaybackState(@PlaybackState.State int state) { 425 return new PlaybackState.Builder().setState(state, 0, 0).build(); 426 } 427 } 428