| Index: content/public/android/java/src/org/chromium/content/browser/ChromeMediaSession.java
 | 
| diff --git a/content/public/android/java/src/org/chromium/content/browser/ChromeMediaSession.java b/content/public/android/java/src/org/chromium/content/browser/ChromeMediaSession.java
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..7630212af767b5c666d579b5982a6c591cce9778
 | 
| --- /dev/null
 | 
| +++ b/content/public/android/java/src/org/chromium/content/browser/ChromeMediaSession.java
 | 
| @@ -0,0 +1,128 @@
 | 
| +// Copyright 2016 The Chromium Authors. All rights reserved.
 | 
| +// Use of this source code is governed by a BSD-style license that can be
 | 
| +// found in the LICENSE file.
 | 
| +
 | 
| +package org.chromium.content.browser;
 | 
| +
 | 
| +import org.chromium.base.ObserverList;
 | 
| +import org.chromium.base.ObserverList.RewindableIterator;
 | 
| +import org.chromium.base.VisibleForTesting;
 | 
| +import org.chromium.base.annotations.CalledByNative;
 | 
| +import org.chromium.base.annotations.JNINamespace;
 | 
| +import org.chromium.content_public.browser.WebContents;
 | 
| +import org.chromium.content_public.common.MediaMetadata;
 | 
| +
 | 
| +/**
 | 
| + * The ChromeMediaSession Java wrapper to allow communicating with the native MediaSession object.
 | 
| + * ChromeMediaSession is created and owned by native MediaSessionAndroid.
 | 
| + */
 | 
| +@JNINamespace("content")
 | 
| +public class ChromeMediaSession {
 | 
| +    private long mNativeMediaSessionAndroid;
 | 
| +    // The observers are stored only Java and are proxied via ChromeMediaSession to observe native
 | 
| +    // MediaSession.
 | 
| +    private ObserverList<MediaSessionObserver> mObservers;
 | 
| +    private RewindableIterator<MediaSessionObserver> mObserversIterator;
 | 
| +
 | 
| +    /**
 | 
| +     * @returns The {@link ChromeMediaSession} for |contents|.
 | 
| +     */
 | 
| +    public static ChromeMediaSession fromWebContents(WebContents contents) {
 | 
| +        return nativeGetMediaSessionFromWebContents(contents);
 | 
| +    }
 | 
| +
 | 
| +    /**
 | 
| +     * Add an observer. This method can only be called from {@link MediaSessionObserver}
 | 
| +     * constructor.
 | 
| +     */
 | 
| +    protected void addObserver(MediaSessionObserver observer) {
 | 
| +        mObservers.addObserver(observer);
 | 
| +    }
 | 
| +
 | 
| +    /**
 | 
| +     * Remove an observer.
 | 
| +     */
 | 
| +    public void removeObserver(MediaSessionObserver observer) {
 | 
| +        mObservers.removeObserver(observer);
 | 
| +    }
 | 
| +
 | 
| +    /**
 | 
| +     * Wrapper for native MediaSession::Resume().
 | 
| +     */
 | 
| +    public void resume() {
 | 
| +        nativeResume(mNativeMediaSessionAndroid);
 | 
| +    }
 | 
| +
 | 
| +    /**
 | 
| +     * Wrapper for native MediaSession::Suspend();
 | 
| +     */
 | 
| +    public void suspend() {
 | 
| +        nativeSuspend(mNativeMediaSessionAndroid);
 | 
| +    }
 | 
| +
 | 
| +    /**
 | 
| +     * Wrapper for native MediaSession::Stop();
 | 
| +     */
 | 
| +    public void stop() {
 | 
| +        nativeStop(mNativeMediaSessionAndroid);
 | 
| +    }
 | 
| +
 | 
| +    @CalledByNative
 | 
| +    private boolean hasObservers() {
 | 
| +        return !mObservers.isEmpty();
 | 
| +    }
 | 
| +
 | 
| +    @CalledByNative
 | 
| +    private void mediaSessionDestroyed() {
 | 
| +        for (mObserversIterator.rewind(); mObserversIterator.hasNext();) {
 | 
| +            mObserversIterator.next().mediaSessionDestroyed();
 | 
| +        }
 | 
| +    }
 | 
| +
 | 
| +    @VisibleForTesting
 | 
| +    @CalledByNative
 | 
| +    public void mediaSessionStateChanged(boolean isControllable, boolean isSuspended) {
 | 
| +        for (mObserversIterator.rewind(); mObserversIterator.hasNext();) {
 | 
| +            mObserversIterator.next().mediaSessionStateChanged(isControllable, isSuspended);
 | 
| +        }
 | 
| +    }
 | 
| +
 | 
| +    @VisibleForTesting
 | 
| +    @CalledByNative
 | 
| +    public void mediaSessionMetadataChanged(MediaMetadata metadata) {
 | 
| +        for (mObserversIterator.rewind(); mObserversIterator.hasNext();) {
 | 
| +            mObserversIterator.next().mediaSessionMetadataChanged(metadata);
 | 
| +        }
 | 
| +    }
 | 
| +
 | 
| +    /**
 | 
| +     * Create a ChromeMediaSession object for a native MediaSession.
 | 
| +     */
 | 
| +    @CalledByNative
 | 
| +    private static ChromeMediaSession create(long nativeChromeMediaSession) {
 | 
| +        return new ChromeMediaSession(nativeChromeMediaSession);
 | 
| +    }
 | 
| +
 | 
| +    /**
 | 
| +     * Called when the native MediaSessionAndroid (the owner) is destroyed.
 | 
| +     */
 | 
| +    @CalledByNative
 | 
| +    private void tearDown() {
 | 
| +        mNativeMediaSessionAndroid = 0;
 | 
| +        // All observers should stop observing when receiving MediaSessionDestroyed.
 | 
| +        assert mObservers.isEmpty();
 | 
| +        mObservers.clear();
 | 
| +    }
 | 
| +
 | 
| +    private ChromeMediaSession(long nativeChromeMediaSession) {
 | 
| +        mNativeMediaSessionAndroid = nativeChromeMediaSession;
 | 
| +        mObservers = new ObserverList<MediaSessionObserver>();
 | 
| +        mObserversIterator = mObservers.rewindableIterator();
 | 
| +    }
 | 
| +
 | 
| +    private static native ChromeMediaSession nativeGetMediaSessionFromWebContents(
 | 
| +            WebContents contents);
 | 
| +    private native void nativeResume(long nativeMediaSessionAndroid);
 | 
| +    private native void nativeSuspend(long nativeMediaSessionAndroid);
 | 
| +    private native void nativeStop(long nativeMediaSessionAndroid);
 | 
| +}
 | 
| 
 |