summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt46
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserFactory.java7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt103
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt15
5 files changed, 155 insertions, 37 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
index 61d0b41e9bb6..f2deebf46771 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaResumeListener.kt
@@ -97,9 +97,16 @@ class MediaResumeListener @Inject constructor(
Log.e(TAG, "Error getting package information", e)
}
- Log.d(TAG, "Adding resume controls $desc")
- mediaDataManager.addResumptionControls(currentUserId, desc, resumeAction, token,
- appName.toString(), appIntent, component.packageName)
+ Log.d(TAG, "Adding resume controls for ${browser.userId}: $desc")
+ mediaDataManager.addResumptionControls(
+ browser.userId,
+ desc,
+ resumeAction,
+ token,
+ appName.toString(),
+ appIntent,
+ component.packageName
+ )
}
}
@@ -154,7 +161,11 @@ class MediaResumeListener @Inject constructor(
}
resumeComponents.add(component to lastPlayed)
}
- Log.d(TAG, "loaded resume components ${resumeComponents.toArray().contentToString()}")
+ Log.d(
+ TAG,
+ "loaded resume components for $currentUserId: " +
+ "${resumeComponents.toArray().contentToString()}"
+ )
if (needsUpdate) {
// Save any missing times that we had to fill in
@@ -170,11 +181,21 @@ class MediaResumeListener @Inject constructor(
return
}
+ val pm = context.packageManager
val now = systemClock.currentTimeMillis()
resumeComponents.forEach {
if (now.minus(it.second) <= RESUME_MEDIA_TIMEOUT) {
- val browser = mediaBrowserFactory.create(mediaBrowserCallback, it.first)
- browser.findRecentMedia()
+ // Verify that the service exists for this user
+ val intent = Intent(MediaBrowserService.SERVICE_INTERFACE)
+ intent.component = it.first
+ val inf = pm.resolveServiceAsUser(intent, 0, currentUserId)
+ if (inf != null) {
+ val browser =
+ mediaBrowserFactory.create(mediaBrowserCallback, it.first, currentUserId)
+ browser.findRecentMedia()
+ } else {
+ Log.d(TAG, "User $currentUserId does not have component ${it.first}")
+ }
}
}
}
@@ -199,7 +220,7 @@ class MediaResumeListener @Inject constructor(
Log.d(TAG, "Checking for service component for " + data.packageName)
val pm = context.packageManager
val serviceIntent = Intent(MediaBrowserService.SERVICE_INTERFACE)
- val resumeInfo = pm.queryIntentServices(serviceIntent, 0)
+ val resumeInfo = pm.queryIntentServicesAsUser(serviceIntent, 0, currentUserId)
val inf = resumeInfo?.filter {
it.serviceInfo.packageName == data.packageName
@@ -242,13 +263,18 @@ class MediaResumeListener @Inject constructor(
browser: ResumeMediaBrowser
) {
// Since this is a test, just save the component for later
- Log.d(TAG, "Can get resumable media from $componentName")
+ Log.d(
+ TAG,
+ "Can get resumable media for ${browser.userId} from $componentName"
+ )
mediaDataManager.setResumeAction(key, getResumeAction(componentName))
updateResumptionList(componentName)
mediaBrowser = null
}
},
- componentName)
+ componentName,
+ currentUserId
+ )
mediaBrowser?.testConnection()
}
@@ -288,7 +314,7 @@ class MediaResumeListener @Inject constructor(
*/
private fun getResumeAction(componentName: ComponentName): Runnable {
return Runnable {
- mediaBrowser = mediaBrowserFactory.create(null, componentName)
+ mediaBrowser = mediaBrowserFactory.create(null, componentName, currentUserId)
mediaBrowser?.restart()
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java
index fecc903326f5..bcd597814354 100644
--- a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java
+++ b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowser.java
@@ -17,6 +17,7 @@
package com.android.systemui.media;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -50,6 +51,8 @@ public class ResumeMediaBrowser {
private final Context mContext;
@Nullable private final Callback mCallback;
private MediaBrowserFactory mBrowserFactory;
+ @UserIdInt private final int mUserId;
+
private MediaBrowser mMediaBrowser;
private ComponentName mComponentName;
@@ -58,13 +61,19 @@ public class ResumeMediaBrowser {
* @param context the context
* @param callback used to report media items found
* @param componentName Component name of the MediaBrowserService this browser will connect to
+ * @param userId ID of the current user
*/
- public ResumeMediaBrowser(Context context, @Nullable Callback callback,
- ComponentName componentName, MediaBrowserFactory browserFactory) {
+ public ResumeMediaBrowser(
+ Context context,
+ @Nullable Callback callback,
+ ComponentName componentName,
+ MediaBrowserFactory browserFactory,
+ @UserIdInt int userId) {
mContext = context;
mCallback = callback;
mComponentName = componentName;
mBrowserFactory = browserFactory;
+ mUserId = userId;
}
/**
@@ -260,6 +269,14 @@ public class ResumeMediaBrowser {
}
/**
+ * Get the ID of the user associated with this broswer
+ * @return the user ID
+ */
+ public @UserIdInt int getUserId() {
+ return mUserId;
+ }
+
+ /**
* Get the media session token
* @return the token, or null if the MediaBrowser is null or disconnected
*/
diff --git a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserFactory.java b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserFactory.java
index 2261aa5ac265..3f4104906281 100644
--- a/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/media/ResumeMediaBrowserFactory.java
@@ -16,6 +16,7 @@
package com.android.systemui.media;
+import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.Context;
@@ -39,10 +40,12 @@ public class ResumeMediaBrowserFactory {
*
* @param callback will be called on connection or error, and addTrack when media item found
* @param componentName component to browse
+ * @param userId ID of the current user
* @return
*/
public ResumeMediaBrowser create(ResumeMediaBrowser.Callback callback,
- ComponentName componentName) {
- return new ResumeMediaBrowser(mContext, callback, componentName, mBrowserFactory);
+ ComponentName componentName, @UserIdInt int userId) {
+ return new ResumeMediaBrowser(mContext, callback, componentName, mBrowserFactory,
+ userId);
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
index 54da1a4b5f65..7e3a5a6edc1d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaResumeListenerTest.kt
@@ -92,6 +92,7 @@ class MediaResumeListenerTest : SysuiTestCase() {
@Captor lateinit var callbackCaptor: ArgumentCaptor<ResumeMediaBrowser.Callback>
@Captor lateinit var actionCaptor: ArgumentCaptor<Runnable>
@Captor lateinit var componentCaptor: ArgumentCaptor<String>
+ @Captor lateinit var userIdCaptor: ArgumentCaptor<Int>
private lateinit var executor: FakeExecutor
private lateinit var data: MediaData
@@ -112,7 +113,7 @@ class MediaResumeListenerTest : SysuiTestCase() {
Settings.Secure.putInt(context.contentResolver,
Settings.Secure.MEDIA_CONTROLS_RESUME, 1)
- whenever(resumeBrowserFactory.create(capture(callbackCaptor), any()))
+ whenever(resumeBrowserFactory.create(capture(callbackCaptor), any(), capture(userIdCaptor)))
.thenReturn(resumeBrowser)
// resume components are stored in sharedpreferences
@@ -123,6 +124,7 @@ class MediaResumeListenerTest : SysuiTestCase() {
whenever(sharedPrefsEditor.putString(any(), any())).thenReturn(sharedPrefsEditor)
whenever(mockContext.packageManager).thenReturn(context.packageManager)
whenever(mockContext.contentResolver).thenReturn(context.contentResolver)
+ whenever(mockContext.userId).thenReturn(context.userId)
executor = FakeExecutor(clock)
resumeListener = MediaResumeListener(mockContext, broadcastDispatcher, executor,
@@ -233,15 +235,7 @@ class MediaResumeListenerTest : SysuiTestCase() {
@Test
fun testOnLoad_checksForResume_hasService() {
// Set up mocks to successfully find a MBS that returns valid media
- val pm = mock(PackageManager::class.java)
- whenever(mockContext.packageManager).thenReturn(pm)
- val resolveInfo = ResolveInfo()
- val serviceInfo = ServiceInfo()
- serviceInfo.packageName = PACKAGE_NAME
- resolveInfo.serviceInfo = serviceInfo
- resolveInfo.serviceInfo.name = CLASS_NAME
- val resumeInfo = listOf(resolveInfo)
- whenever(pm.queryIntentServices(any(), anyInt())).thenReturn(resumeInfo)
+ setUpMbsWithValidResolveInfo()
val description = MediaDescription.Builder().setTitle(TITLE).build()
val component = ComponentName(PACKAGE_NAME, CLASS_NAME)
@@ -280,6 +274,7 @@ class MediaResumeListenerTest : SysuiTestCase() {
@Test
fun testOnUserUnlock_loadsTracks() {
// Set up mock service to successfully find valid media
+ setUpMbsWithValidResolveInfo()
val description = MediaDescription.Builder().setTitle(TITLE).build()
val component = ComponentName(PACKAGE_NAME, CLASS_NAME)
whenever(resumeBrowser.token).thenReturn(token)
@@ -308,15 +303,7 @@ class MediaResumeListenerTest : SysuiTestCase() {
@Test
fun testGetResumeAction_restarts() {
// Set up mocks to successfully find a MBS that returns valid media
- val pm = mock(PackageManager::class.java)
- whenever(mockContext.packageManager).thenReturn(pm)
- val resolveInfo = ResolveInfo()
- val serviceInfo = ServiceInfo()
- serviceInfo.packageName = PACKAGE_NAME
- resolveInfo.serviceInfo = serviceInfo
- resolveInfo.serviceInfo.name = CLASS_NAME
- val resumeInfo = listOf(resolveInfo)
- whenever(pm.queryIntentServices(any(), anyInt())).thenReturn(resumeInfo)
+ setUpMbsWithValidResolveInfo()
val description = MediaDescription.Builder().setTitle(TITLE).build()
val component = ComponentName(PACKAGE_NAME, CLASS_NAME)
@@ -362,6 +349,7 @@ class MediaResumeListenerTest : SysuiTestCase() {
@Test
fun testLoadComponents_recentlyPlayed_adds() {
// Set up browser to return successfully
+ setUpMbsWithValidResolveInfo()
val description = MediaDescription.Builder().setTitle(TITLE).build()
val component = ComponentName(PACKAGE_NAME, CLASS_NAME)
whenever(resumeBrowser.token).thenReturn(token)
@@ -445,4 +433,81 @@ class MediaResumeListenerTest : SysuiTestCase() {
}
verify(sharedPrefsEditor, times(1)).apply()
}
+
+ @Test
+ fun testUserUnlocked_userChangeWhileQuerying() {
+ val firstUserId = context.userId
+ val secondUserId = firstUserId + 1
+ val description = MediaDescription.Builder().setTitle(TITLE).build()
+ val component = ComponentName(PACKAGE_NAME, CLASS_NAME)
+
+ setUpMbsWithValidResolveInfo()
+ whenever(resumeBrowser.token).thenReturn(token)
+ whenever(resumeBrowser.appIntent).thenReturn(pendingIntent)
+
+ val unlockIntent =
+ Intent(Intent.ACTION_USER_UNLOCKED).apply {
+ putExtra(Intent.EXTRA_USER_HANDLE, firstUserId)
+ }
+
+ // When the first user unlocks and we query their recent media
+ resumeListener.userChangeReceiver.onReceive(context, unlockIntent)
+ whenever(resumeBrowser.userId).thenReturn(userIdCaptor.value)
+ verify(resumeBrowser, times(3)).findRecentMedia()
+
+ // And the user changes before the MBS response is received
+ val changeIntent =
+ Intent(Intent.ACTION_USER_SWITCHED).apply {
+ putExtra(Intent.EXTRA_USER_HANDLE, secondUserId)
+ }
+ resumeListener.userChangeReceiver.onReceive(context, changeIntent)
+ callbackCaptor.value.addTrack(description, component, resumeBrowser)
+
+ // Then the loaded media is correctly associated with the first user
+ verify(mediaDataManager)
+ .addResumptionControls(
+ eq(firstUserId),
+ eq(description),
+ any(),
+ eq(token),
+ eq(PACKAGE_NAME),
+ eq(pendingIntent),
+ eq(PACKAGE_NAME)
+ )
+ }
+
+ @Test
+ fun testUserUnlocked_noComponent_doesNotQuery() {
+ // Set up a valid MBS, but user does not have the service available
+ setUpMbsWithValidResolveInfo()
+ val pm = mock(PackageManager::class.java)
+ whenever(mockContext.packageManager).thenReturn(pm)
+ whenever(pm.resolveServiceAsUser(any(), anyInt(), anyInt())).thenReturn(null)
+
+ val unlockIntent =
+ Intent(Intent.ACTION_USER_UNLOCKED).apply {
+ putExtra(Intent.EXTRA_USER_HANDLE, context.userId)
+ }
+
+ // When the user is unlocked, but does not have the component installed
+ resumeListener.userChangeReceiver.onReceive(context, unlockIntent)
+
+ // Then we never attempt to connect to it
+ verify(resumeBrowser, never()).findRecentMedia()
+ }
+
+ /** Sets up mocks to successfully find a MBS that returns valid media. */
+ private fun setUpMbsWithValidResolveInfo() {
+ val pm = mock(PackageManager::class.java)
+ whenever(mockContext.packageManager).thenReturn(pm)
+ val resolveInfo = ResolveInfo()
+ val serviceInfo = ServiceInfo()
+ serviceInfo.packageName = PACKAGE_NAME
+ resolveInfo.serviceInfo = serviceInfo
+ resolveInfo.serviceInfo.name = CLASS_NAME
+ val resumeInfo = listOf(resolveInfo)
+ whenever(pm.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(resumeInfo)
+ whenever(pm.resolveServiceAsUser(any(), anyInt(), anyInt())).thenReturn(resolveInfo)
+ whenever(pm.getApplicationLabel(any())).thenReturn(PACKAGE_NAME)
+ }
} \ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt
index dfa7c66b38f9..5620467b2959 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/ResumeMediaBrowserTest.kt
@@ -81,8 +81,14 @@ public class ResumeMediaBrowserTest : SysuiTestCase() {
whenever(mediaController.transportControls).thenReturn(transportControls)
- resumeBrowser = TestableResumeMediaBrowser(context, callback, component, browserFactory,
- mediaController)
+ resumeBrowser = TestableResumeMediaBrowser(
+ context,
+ callback,
+ component,
+ browserFactory,
+ mediaController,
+ context.userId
+ )
}
@Test
@@ -282,8 +288,9 @@ public class ResumeMediaBrowserTest : SysuiTestCase() {
callback: Callback,
componentName: ComponentName,
browserFactory: MediaBrowserFactory,
- private val fakeController: MediaController
- ) : ResumeMediaBrowser(context, callback, componentName, browserFactory) {
+ private val fakeController: MediaController,
+ userId: Int
+ ) : ResumeMediaBrowser(context, callback, componentName, browserFactory, userId) {
override fun createMediaController(token: MediaSession.Token): MediaController {
return fakeController