Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 09e96759 authored by Treehugger Robot's avatar Treehugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Improve user handling when querying for resumable media" into udc-dev am: c43c0104

parents 3c550eea c43c0104
Loading
Loading
Loading
Loading
+27 −9
Original line number Diff line number Diff line
@@ -122,9 +122,9 @@ constructor(
                    Log.e(TAG, "Error getting package information", e)
                }

                Log.d(TAG, "Adding resume controls $desc")
                Log.d(TAG, "Adding resume controls for ${browser.userId}: $desc")
                mediaDataManager.addResumptionControls(
                    currentUserId,
                    browser.userId,
                    desc,
                    resumeAction,
                    token,
@@ -196,7 +196,11 @@ 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
@@ -210,11 +214,21 @@ 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)
                // 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}")
                }
            }
        }
    }
@@ -244,7 +258,7 @@ 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 }
                if (inf != null && inf.size > 0) {
@@ -280,13 +294,17 @@ 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()
    }
@@ -326,7 +344,7 @@ constructor(
    /** Get a runnable which will resume media playback */
    private fun getResumeAction(componentName: ComponentName): Runnable {
        return Runnable {
            mediaBrowser = mediaBrowserFactory.create(null, componentName)
            mediaBrowser = mediaBrowserFactory.create(null, componentName, currentUserId)
            mediaBrowser?.restart()
        }
    }
+14 −1
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.media.controls.resume;

import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -53,6 +54,7 @@ public class ResumeMediaBrowser {
    private final ResumeMediaBrowserLogger mLogger;
    private final ComponentName mComponentName;
    private final MediaController.Callback mMediaControllerCallback = new SessionDestroyCallback();
    @UserIdInt private final int mUserId;

    private MediaBrowser mMediaBrowser;
    @Nullable private MediaController mMediaController;
@@ -62,18 +64,21 @@ 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,
            ResumeMediaBrowserLogger logger) {
            ResumeMediaBrowserLogger logger,
            @UserIdInt int userId) {
        mContext = context;
        mCallback = callback;
        mComponentName = componentName;
        mBrowserFactory = browserFactory;
        mLogger = logger;
        mUserId = userId;
    }

    /**
@@ -284,6 +289,14 @@ public class ResumeMediaBrowser {
        return new MediaController(mContext, token);
    }

    /**
     * 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
+5 −2
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.media.controls.resume;

import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.Context;

@@ -42,10 +43,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, mLogger);
            ComponentName componentName, @UserIdInt int userId) {
        return new ResumeMediaBrowser(mContext, callback, componentName, mBrowserFactory, mLogger,
            userId);
    }
}
+69 −3
Original line number Diff line number Diff line
@@ -98,6 +98,8 @@ 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>
    @Captor lateinit var userCallbackCaptor: ArgumentCaptor<UserTracker.Callback>

    private lateinit var executor: FakeExecutor
    private lateinit var data: MediaData
@@ -124,7 +126,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
@@ -334,6 +336,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)
@@ -417,6 +420,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)
@@ -600,7 +604,7 @@ class MediaResumeListenerTest : SysuiTestCase() {

        // Set up our factory to return a new browser so we can verify we disconnected the old one
        val newResumeBrowser = mock(ResumeMediaBrowser::class.java)
        whenever(resumeBrowserFactory.create(capture(callbackCaptor), any()))
        whenever(resumeBrowserFactory.create(capture(callbackCaptor), any(), anyInt()))
            .thenReturn(newResumeBrowser)

        // When the resume action is run
@@ -610,6 +614,66 @@ class MediaResumeListenerTest : SysuiTestCase() {
        verify(resumeBrowser).disconnect()
    }

    @Test
    fun testUserUnlocked_userChangeWhileQuerying() {
        val firstUserId = 1
        val secondUserId = 2
        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)
            }
        verify(userTracker).addCallback(capture(userCallbackCaptor), any())

        // When the first user unlocks and we query their recent media
        userCallbackCaptor.value.onUserChanged(firstUserId, context)
        resumeListener.userUnlockReceiver.onReceive(context, unlockIntent)
        whenever(resumeBrowser.userId).thenReturn(userIdCaptor.value)
        verify(resumeBrowser, times(3)).findRecentMedia()

        // And the user changes before the MBS response is received
        userCallbackCaptor.value.onUserChanged(secondUserId, context)
        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.userUnlockReceiver.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)
@@ -620,6 +684,8 @@ class MediaResumeListenerTest : SysuiTestCase() {
        resolveInfo.serviceInfo = serviceInfo
        resolveInfo.serviceInfo.name = CLASS_NAME
        val resumeInfo = listOf(resolveInfo)
        whenever(pm.queryIntentServices(any(), anyInt())).thenReturn(resumeInfo)
        whenever(pm.queryIntentServicesAsUser(any(), anyInt(), anyInt())).thenReturn(resumeInfo)
        whenever(pm.resolveServiceAsUser(any(), anyInt(), anyInt())).thenReturn(resolveInfo)
        whenever(pm.getApplicationLabel(any())).thenReturn(PACKAGE_NAME)
    }
}
+5 −3
Original line number Diff line number Diff line
@@ -93,7 +93,8 @@ public class ResumeMediaBrowserTest : SysuiTestCase() {
                component,
                browserFactory,
                logger,
                mediaController
                mediaController,
                context.userId,
            )
    }

@@ -381,8 +382,9 @@ public class ResumeMediaBrowserTest : SysuiTestCase() {
        componentName: ComponentName,
        browserFactory: MediaBrowserFactory,
        logger: ResumeMediaBrowserLogger,
        private val fakeController: MediaController
    ) : ResumeMediaBrowser(context, callback, componentName, browserFactory, logger) {
        private val fakeController: MediaController,
        userId: Int,
    ) : ResumeMediaBrowser(context, callback, componentName, browserFactory, logger, userId) {

        override fun createMediaController(token: MediaSession.Token): MediaController {
            return fakeController