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

Commit 735f3430 authored by Jaewan Kim's avatar Jaewan Kim
Browse files

MediaSession2: Initial commit of MediaBrowser2

Test: Run all MediaComponents tests once
Change-Id: I395125017d01cd68d66bad5f7ec770a9174f08da
parent 3bad8ce2
Loading
Loading
Loading
Loading
+58 −0
Original line number Diff line number Diff line
/*
 * Copyright 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.media;

import android.content.Context;
import android.media.IMediaSession2;
import android.media.MediaBrowser2;
import android.media.MediaBrowser2.BrowserCallback;
import android.media.SessionToken;
import android.media.update.MediaBrowser2Provider;
import android.os.Bundle;
import android.os.RemoteException;
import android.util.Log;

import java.util.concurrent.Executor;

public class MediaBrowser2Impl extends MediaController2Impl implements MediaBrowser2Provider {
    private final String TAG = "MediaBrowser2";
    private final boolean DEBUG = true; // TODO(jaewan): change.

    private final MediaBrowser2 mInstance;
    private final MediaBrowser2.BrowserCallback mCallback;

    public MediaBrowser2Impl(MediaBrowser2 instance, Context context, SessionToken token,
            BrowserCallback callback, Executor executor) {
        super(instance, context, token, callback, executor);
        mInstance = instance;
        mCallback = callback;
    }

    @Override
    public void getBrowserRoot_impl(Bundle rootHints) {
        final IMediaSession2 binder = getSessionBinder();
        if (binder != null) {
            try {
                binder.getBrowserRoot(getControllerStub(), rootHints);
            } catch (RemoteException e) {
                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
            }
        } else {
            Log.w(TAG, "Session isn't active", new IllegalStateException());
        }
    }
}
+10 −1
Original line number Diff line number Diff line
@@ -188,6 +188,14 @@ public class MediaController2Impl implements MediaController2Provider {
        });
    }

    IMediaSession2 getSessionBinder() {
        return mSessionBinder;
    }

    MediaSession2CallbackStub getControllerStub() {
        return mSessionCallbackStub;
    }

    @Override
    public SessionToken getSessionToken_impl() {
        return mToken;
@@ -382,7 +390,8 @@ public class MediaController2Impl implements MediaController2Provider {
        }
    }

    private static class MediaSession2CallbackStub extends IMediaSession2Callback.Stub {
    // TODO(jaewan): Pull out this from the controller2, and rename it to the MediaBrowserStub
    static class MediaSession2CallbackStub extends IMediaSession2Callback.Stub {
        private final WeakReference<MediaController2Impl> mController;

        private MediaSession2CallbackStub(MediaController2Impl controller) {
+6 −0
Original line number Diff line number Diff line
@@ -124,6 +124,12 @@ public class MediaSession2Stub extends IMediaSession2.Stub {
        mCommandHandler.postCommand(controller, Command.fromBundle(command), args);
    }

    @Override
    public void getBrowserRoot(IMediaSession2Callback caller, Bundle rootHints)
            throws RuntimeException {
        // TODO(jaewan): Implement this.
    }

    @Deprecated
    @Override
    public PlaybackState getPlaybackState() throws RemoteException {
+10 −0
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ package com.android.media.update;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
import android.media.MediaBrowser2;
import android.media.MediaBrowser2.BrowserCallback;
import android.media.MediaController2;
import android.media.MediaPlayerBase;
import android.media.MediaSession2;
@@ -27,6 +29,7 @@ import android.media.MediaSession2.SessionCallback;
import android.media.MediaSessionService2;
import android.media.IMediaSession2Callback;
import android.media.SessionToken;
import android.media.update.MediaBrowser2Provider;
import android.media.update.MediaControlView2Provider;
import android.media.update.MediaController2Provider;
import android.media.update.MediaSession2Provider;
@@ -39,6 +42,7 @@ import android.util.AttributeSet;
import android.widget.MediaControlView2;
import android.widget.VideoView2;

import com.android.media.MediaBrowser2Impl;
import com.android.media.MediaController2Impl;
import com.android.media.MediaSession2Impl;
import com.android.media.MediaSessionService2Impl;
@@ -61,6 +65,12 @@ public class ApiFactory implements StaticProvider {
        return new MediaController2Impl(instance, context, token, callback, executor);
    }

    @Override
    public MediaBrowser2Provider createMediaBrowser2(MediaBrowser2 instance, Context context,
            SessionToken token, BrowserCallback callback, Executor executor) {
        return new MediaBrowser2Impl(instance, context, token, callback, executor);
    }

    @Override
    public MediaSession2Provider createMediaSession2(MediaSession2 instance, Context context,
            MediaPlayerBase player, String id, SessionCallback callback) {
+116 −0
Original line number Diff line number Diff line
/*
 * Copyright 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.media;

import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;

import android.content.Context;
import android.media.MediaBrowser2.BrowserCallback;
import android.media.MediaSession2.CommandGroup;
import android.os.Bundle;
import android.support.annotation.CallSuper;
import android.support.annotation.NonNull;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;

import org.junit.runner.RunWith;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

/**
 * Tests {@link MediaBrowser2}.
 * <p>
 * This test inherits {@link MediaController2Test} to ensure that inherited APIs from
 * {@link MediaController2} works cleanly.
 */
// TODO(jaewan): Implement host-side test so browser and service can run in different processes.
@RunWith(AndroidJUnit4.class)
@SmallTest
public class MediaBrowser2Test extends MediaController2Test {
    private static final String TAG = "MediaPlayerBrowserTest";

    @Override
    TestControllerInterface onCreateController(@NonNull SessionToken token,
            @NonNull TestControllerCallbackInterface callback) {
        return new TestMediaBrowser(mContext, token, new TestBrowserCallback(callback));
    }

    public static class TestBrowserCallback extends BrowserCallback
            implements WaitForConnectionInterface {
        private final TestControllerCallbackInterface mCallbackProxy;
        public final CountDownLatch connectLatch = new CountDownLatch(1);
        public final CountDownLatch disconnectLatch = new CountDownLatch(1);

        TestBrowserCallback(TestControllerCallbackInterface callbackProxy) {
            mCallbackProxy = callbackProxy;
        }

        @CallSuper
        @Override
        public void onConnected(CommandGroup commands) {
            super.onConnected(commands);
            connectLatch.countDown();
        }

        @CallSuper
        @Override
        public void onDisconnected() {
            super.onDisconnected();
            disconnectLatch.countDown();
        }

        @Override
        public void onGetRootResult(Bundle rootHints, String rootMediaId, Bundle rootExtra) {
            // No-op here.
        }

        @Override
        public void waitForConnect(boolean expect) throws InterruptedException {
            if (expect) {
                assertTrue(connectLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
            } else {
                assertFalse(connectLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
            }
        }

        @Override
        public void waitForDisconnect(boolean expect) throws InterruptedException {
            if (expect) {
                assertTrue(disconnectLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
            } else {
                assertFalse(disconnectLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
            }
        }
    }

    public class TestMediaBrowser extends MediaBrowser2 implements TestControllerInterface {
        private final BrowserCallback mCallback;

        public TestMediaBrowser(@NonNull Context context, @NonNull SessionToken token,
                @NonNull ControllerCallback callback) {
            super(context, token, (BrowserCallback) callback, sHandlerExecutor);
            mCallback = (BrowserCallback) callback;
        }

        @Override
        public BrowserCallback getCallback() {
            return mCallback;
        }
    }
}
 No newline at end of file
Loading