1c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown/*
2c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Copyright (C) 2013 The Android Open Source Project
3c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown *
4c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Licensed under the Apache License, Version 2.0 (the "License");
5c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * you may not use this file except in compliance with the License.
6c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * You may obtain a copy of the License at
7c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown *
8c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown *      http://www.apache.org/licenses/LICENSE-2.0
9c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown *
10c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Unless required by applicable law or agreed to in writing, software
11c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * distributed under the License is distributed on an "AS IS" BASIS,
12c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * See the License for the specific language governing permissions and
14c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * limitations under the License.
15c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */
16c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
17b507e525a61ed761eecfc2eaaf19af7e8db5dca5Jeff Brownpackage android.support.v7.media;
18c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
19c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.Context;
20c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.Intent;
21c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Handler;
22c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Message;
23b507e525a61ed761eecfc2eaaf19af7e8db5dca5Jeff Brownimport android.support.v7.media.MediaRouter.ControlRequestCallback;
24c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.text.TextUtils;
25c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
26c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown/**
27c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Media route providers are used to publish additional media routes for
28c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * use within an application.  Media route providers may also be declared
29c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * as a service to publish additional media routes to all applications
30c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * in the system.
31c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p>
3211417b1cfde8f1749905f2d735623af9214148afJeff Brown * The purpose of a media route provider is to discover media routes that satisfy
3311417b1cfde8f1749905f2d735623af9214148afJeff Brown * the criteria specified by the current {@link MediaRouteDiscoveryRequest} and publish a
3411417b1cfde8f1749905f2d735623af9214148afJeff Brown * {@link MediaRouteProviderDescriptor} with information about each route by calling
3511417b1cfde8f1749905f2d735623af9214148afJeff Brown * {@link #setDescriptor} to notify the currently registered {@link Callback}.
3611417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p><p>
3711417b1cfde8f1749905f2d735623af9214148afJeff Brown * The provider should watch for changes to the discovery request by implementing
3811417b1cfde8f1749905f2d735623af9214148afJeff Brown * {@link #onDiscoveryRequestChanged} and updating the set of routes that it is
3911417b1cfde8f1749905f2d735623af9214148afJeff Brown * attempting to discover.  It should also handle route control requests such
4011417b1cfde8f1749905f2d735623af9214148afJeff Brown * as volume changes or {@link MediaControlIntent media control intents}
4111417b1cfde8f1749905f2d735623af9214148afJeff Brown * by implementing {@link #onCreateRouteController} to return a {@link RouteController}
4211417b1cfde8f1749905f2d735623af9214148afJeff Brown * for a particular route.
4311417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p><p>
4411417b1cfde8f1749905f2d735623af9214148afJeff Brown * A media route provider may be used privately within the scope of a single
4511417b1cfde8f1749905f2d735623af9214148afJeff Brown * application process by calling {@link MediaRouter#addProvider MediaRouter.addProvider}
4611417b1cfde8f1749905f2d735623af9214148afJeff Brown * to add it to the local {@link MediaRouter}.  A media route provider may also be made
4711417b1cfde8f1749905f2d735623af9214148afJeff Brown * available globally to all applications by registering a {@link MediaRouteProviderService}
4811417b1cfde8f1749905f2d735623af9214148afJeff Brown * in the provider's manifest.  When the media route provider is registered
4911417b1cfde8f1749905f2d735623af9214148afJeff Brown * as a service, all applications that use the media router API will be able to
5011417b1cfde8f1749905f2d735623af9214148afJeff Brown * discover and used the provider's routes without having to install anything else.
51c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p>
52c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This object must only be accessed on the main thread.
53c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p>
54c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */
55c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownpublic abstract class MediaRouteProvider {
56c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    private static final int MSG_DELIVER_DESCRIPTOR_CHANGED = 1;
5711417b1cfde8f1749905f2d735623af9214148afJeff Brown    private static final int MSG_DELIVER_DISCOVERY_REQUEST_CHANGED = 2;
58c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
59c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    private final Context mContext;
60fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    private final ProviderMetadata mMetadata;
61c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    private final ProviderHandler mHandler = new ProviderHandler();
62c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
6311417b1cfde8f1749905f2d735623af9214148afJeff Brown    private Callback mCallback;
6411417b1cfde8f1749905f2d735623af9214148afJeff Brown
6511417b1cfde8f1749905f2d735623af9214148afJeff Brown    private MediaRouteDiscoveryRequest mDiscoveryRequest;
6611417b1cfde8f1749905f2d735623af9214148afJeff Brown    private boolean mPendingDiscoveryRequestChange;
6711417b1cfde8f1749905f2d735623af9214148afJeff Brown
6811417b1cfde8f1749905f2d735623af9214148afJeff Brown    private MediaRouteProviderDescriptor mDescriptor;
69c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    private boolean mPendingDescriptorChange;
70c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
71c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
72c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Creates a media route provider.
73c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
74c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @param context The context.
75c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
76c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public MediaRouteProvider(Context context) {
77fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        this(context, null);
78fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
79fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
80fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    MediaRouteProvider(Context context, ProviderMetadata metadata) {
81c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        if (context == null) {
82c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            throw new IllegalArgumentException("context must not be null");
83c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
84c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
85c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        mContext = context;
86fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        if (metadata == null) {
87fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mMetadata = new ProviderMetadata(context.getPackageName());
88fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        } else {
89fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mMetadata = metadata;
90fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
91c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
92c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
93c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
94c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Gets the context of the media route provider.
95c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
96c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public final Context getContext() {
97c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        return mContext;
98c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
99c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
10011417b1cfde8f1749905f2d735623af9214148afJeff Brown    /**
10111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Gets the provider's handler which is associated with the main thread.
10211417b1cfde8f1749905f2d735623af9214148afJeff Brown     */
10311417b1cfde8f1749905f2d735623af9214148afJeff Brown    public final Handler getHandler() {
10411417b1cfde8f1749905f2d735623af9214148afJeff Brown        return mHandler;
10511417b1cfde8f1749905f2d735623af9214148afJeff Brown    }
10611417b1cfde8f1749905f2d735623af9214148afJeff Brown
10711417b1cfde8f1749905f2d735623af9214148afJeff Brown    /**
10811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Gets some metadata about the provider's implementation.
10911417b1cfde8f1749905f2d735623af9214148afJeff Brown     */
11011417b1cfde8f1749905f2d735623af9214148afJeff Brown    public final ProviderMetadata getMetadata() {
111fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        return mMetadata;
112fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    }
113fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
114c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
11511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Sets a callback to invoke when the provider's descriptor changes.
11611417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
11711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param callback The callback to use, or null if none.
11811417b1cfde8f1749905f2d735623af9214148afJeff Brown     */
11911417b1cfde8f1749905f2d735623af9214148afJeff Brown    public final void setCallback(Callback callback) {
12011417b1cfde8f1749905f2d735623af9214148afJeff Brown        MediaRouter.checkCallingThread();
12111417b1cfde8f1749905f2d735623af9214148afJeff Brown        mCallback = callback;
12211417b1cfde8f1749905f2d735623af9214148afJeff Brown    }
12311417b1cfde8f1749905f2d735623af9214148afJeff Brown
12411417b1cfde8f1749905f2d735623af9214148afJeff Brown    /**
12511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Gets the current discovery request which informs the provider about the
12611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * kinds of routes to discover and whether to perform active scanning.
12711417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
12811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @return The current discovery request, or null if no discovery is needed at this time.
12911417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
13011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @see #onDiscoveryRequestChanged
13111417b1cfde8f1749905f2d735623af9214148afJeff Brown     */
13211417b1cfde8f1749905f2d735623af9214148afJeff Brown    public final MediaRouteDiscoveryRequest getDiscoveryRequest() {
13311417b1cfde8f1749905f2d735623af9214148afJeff Brown        return mDiscoveryRequest;
13411417b1cfde8f1749905f2d735623af9214148afJeff Brown    }
13511417b1cfde8f1749905f2d735623af9214148afJeff Brown
13611417b1cfde8f1749905f2d735623af9214148afJeff Brown    /**
13711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Sets a discovery request to inform the provider about the kinds of
13811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * routes that its clients would like to discover and whether to perform active scanning.
13911417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
14011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param request The discovery request, or null if no discovery is needed at this time.
14111417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
14211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @see #onDiscoveryRequestChanged
14311417b1cfde8f1749905f2d735623af9214148afJeff Brown     */
14411417b1cfde8f1749905f2d735623af9214148afJeff Brown    public final void setDiscoveryRequest(MediaRouteDiscoveryRequest request) {
14511417b1cfde8f1749905f2d735623af9214148afJeff Brown        MediaRouter.checkCallingThread();
14611417b1cfde8f1749905f2d735623af9214148afJeff Brown
14711417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (mDiscoveryRequest == request
14811417b1cfde8f1749905f2d735623af9214148afJeff Brown                || (mDiscoveryRequest != null && mDiscoveryRequest.equals(request))) {
14911417b1cfde8f1749905f2d735623af9214148afJeff Brown            return;
15011417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
15111417b1cfde8f1749905f2d735623af9214148afJeff Brown
15211417b1cfde8f1749905f2d735623af9214148afJeff Brown        mDiscoveryRequest = request;
15311417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (!mPendingDiscoveryRequestChange) {
15411417b1cfde8f1749905f2d735623af9214148afJeff Brown            mPendingDiscoveryRequestChange = true;
15511417b1cfde8f1749905f2d735623af9214148afJeff Brown            mHandler.sendEmptyMessage(MSG_DELIVER_DISCOVERY_REQUEST_CHANGED);
15611417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
15711417b1cfde8f1749905f2d735623af9214148afJeff Brown    }
15811417b1cfde8f1749905f2d735623af9214148afJeff Brown
15911417b1cfde8f1749905f2d735623af9214148afJeff Brown    private void deliverDiscoveryRequestChanged() {
16011417b1cfde8f1749905f2d735623af9214148afJeff Brown        mPendingDiscoveryRequestChange = false;
16111417b1cfde8f1749905f2d735623af9214148afJeff Brown        onDiscoveryRequestChanged(mDiscoveryRequest);
16211417b1cfde8f1749905f2d735623af9214148afJeff Brown    }
16311417b1cfde8f1749905f2d735623af9214148afJeff Brown
16411417b1cfde8f1749905f2d735623af9214148afJeff Brown    /**
16511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Called by the media router when the {@link MediaRouteDiscoveryRequest discovery request}
16611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * has changed.
16711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * <p>
16811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Whenever an applications calls {@link MediaRouter#addCallback} to register
16911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * a callback, it also provides a selector to specify the kinds of routes that
17011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * it is interested in.  The media router combines all of these selectors together
17111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * to generate a {@link MediaRouteDiscoveryRequest} and notifies each provider when a change
17211417b1cfde8f1749905f2d735623af9214148afJeff Brown     * occurs by calling {@link #setDiscoveryRequest} which posts a message to invoke
17311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * this method asynchronously.
17411417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p><p>
17511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * The provider should examine the {@link MediaControlIntent media control categories}
17611417b1cfde8f1749905f2d735623af9214148afJeff Brown     * in the discovery request's {@link MediaRouteSelector selector} to determine what
17711417b1cfde8f1749905f2d735623af9214148afJeff Brown     * kinds of routes it should try to discover and whether it should perform active
17811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * or passive scans.  In many cases, the provider may be able to save power by
17911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * determining that the selector does not contain any categories that it supports
18011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * and it can therefore avoid performing any scans at all.
18111417b1cfde8f1749905f2d735623af9214148afJeff Brown     * </p>
18211417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
18311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @param request The new discovery request, or null if no discovery is needed at this time.
18411417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
18511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @see MediaRouter#addCallback
18611417b1cfde8f1749905f2d735623af9214148afJeff Brown     */
18711417b1cfde8f1749905f2d735623af9214148afJeff Brown    public void onDiscoveryRequestChanged(MediaRouteDiscoveryRequest request) {
18811417b1cfde8f1749905f2d735623af9214148afJeff Brown    }
18911417b1cfde8f1749905f2d735623af9214148afJeff Brown
19011417b1cfde8f1749905f2d735623af9214148afJeff Brown    /**
191fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * Gets the provider's descriptor.
192fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * <p>
193fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * The descriptor describes the state of the media route provider and
194fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * the routes that it publishes.  Watch for changes to the descriptor
19511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * by registering a {@link Callback callback} with {@link #setCallback}.
196fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * </p>
197c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
19811417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @return The media route provider descriptor, or null if none.
19911417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
20011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @see Callback#onDescriptorChanged
201c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
20211417b1cfde8f1749905f2d735623af9214148afJeff Brown    public final MediaRouteProviderDescriptor getDescriptor() {
203c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        return mDescriptor;
204c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
205c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
206c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
207c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Sets the provider's descriptor.
208c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <p>
20911417b1cfde8f1749905f2d735623af9214148afJeff Brown     * The provider must call this method to notify the currently registered
21011417b1cfde8f1749905f2d735623af9214148afJeff Brown     * {@link Callback callback} about the change to the provider's descriptor.
211c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </p>
212c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
213fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * @param descriptor The updated route provider descriptor, or null if none.
21411417b1cfde8f1749905f2d735623af9214148afJeff Brown     *
21511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * @see Callback#onDescriptorChanged
216c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
21711417b1cfde8f1749905f2d735623af9214148afJeff Brown    public final void setDescriptor(MediaRouteProviderDescriptor descriptor) {
218c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        MediaRouter.checkCallingThread();
219c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
220c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        if (mDescriptor != descriptor) {
221c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mDescriptor = descriptor;
222c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (!mPendingDescriptorChange) {
223c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                mPendingDescriptorChange = true;
224c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                mHandler.sendEmptyMessage(MSG_DELIVER_DESCRIPTOR_CHANGED);
225c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
226c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
227c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
228c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
229c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    private void deliverDescriptorChanged() {
230c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        mPendingDescriptorChange = false;
231c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
23211417b1cfde8f1749905f2d735623af9214148afJeff Brown        if (mCallback != null) {
23311417b1cfde8f1749905f2d735623af9214148afJeff Brown            mCallback.onDescriptorChanged(this, mDescriptor);
234c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
235c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
236c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
237c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
238c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Called by the media router to obtain a route controller for a particular route.
239c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <p>
2403b11cd2027d3249ec1d56939d3d3236cbd7ec91aJeff Brown     * The media router will invoke the {@link RouteController#onRelease} method of the route
241fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * controller when it is no longer needed to allow it to free its resources.
242c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </p>
243c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     *
244c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @param routeId The unique id of the route.
245c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * @return The route controller.  Returns null if there is no such route or if the route
246c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * cannot be controlled using the route controller interface.
247c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
248c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public RouteController onCreateRouteController(String routeId) {
249c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        return null;
250c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
251c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
252c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
25311417b1cfde8f1749905f2d735623af9214148afJeff Brown     * Describes properties of the route provider's implementation.
25428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * <p>
25511417b1cfde8f1749905f2d735623af9214148afJeff Brown     * This object is immutable once created.
25628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     * </p>
25728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown     */
25811417b1cfde8f1749905f2d735623af9214148afJeff Brown    public static final class ProviderMetadata {
259fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        private final String mPackageName;
260fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
26111417b1cfde8f1749905f2d735623af9214148afJeff Brown        /**
26211417b1cfde8f1749905f2d735623af9214148afJeff Brown         * Creates a provider metadata object.
26311417b1cfde8f1749905f2d735623af9214148afJeff Brown         *
26411417b1cfde8f1749905f2d735623af9214148afJeff Brown         * @param packageName The provider application's package name.
26511417b1cfde8f1749905f2d735623af9214148afJeff Brown         */
266fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        public ProviderMetadata(String packageName) {
267fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (TextUtils.isEmpty(packageName)) {
268fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                throw new IllegalArgumentException("packageName must not be null or empty");
269fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
270fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            mPackageName = packageName;
271fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
272fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
273c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
27411417b1cfde8f1749905f2d735623af9214148afJeff Brown         * Gets the provider application's package name.
275c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
27611417b1cfde8f1749905f2d735623af9214148afJeff Brown        public String getPackageName() {
27711417b1cfde8f1749905f2d735623af9214148afJeff Brown            return mPackageName;
278c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
279c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
280c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
281c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public String toString() {
28211417b1cfde8f1749905f2d735623af9214148afJeff Brown            return "ProviderMetadata{ packageName=" + mPackageName + " }";
283c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
284c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
285c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
286c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
287c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Provides control over a particular route.
288c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * <p>
289c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * The media router obtains a route controller for a route whenever it needs
290c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * to control a route.  When a route is selected, the media router invokes
2913b11cd2027d3249ec1d56939d3d3236cbd7ec91aJeff Brown     * the {@link #onSelect} method of its route controller.  While selected,
292c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * the media router may call other methods of the route controller to
293c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * request that it perform certain actions to the route.  When a route is
2943b11cd2027d3249ec1d56939d3d3236cbd7ec91aJeff Brown     * unselected, the media router invokes the {@link #onUnselect} method of its
295c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * route controller.  When the media route no longer needs the route controller
2963b11cd2027d3249ec1d56939d3d3236cbd7ec91aJeff Brown     * it will invoke the {@link #onRelease} method to allow the route controller
297c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * to free its resources.
298c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </p><p>
299fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * There may be multiple route controllers simultaneously active for the
300fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * same route.  Each route controller will be released separately.
301fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown     * </p><p>
302c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * All operations on the route controller are asynchronous and
303c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * results are communicated via callbacks.
304c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * </p>
305c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
306c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public static abstract class RouteController {
307c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
308c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Releases the route controller, allowing it to free its resources.
309c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
310129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown        public void onRelease() {
311c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
312c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
313c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
314c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Selects the route.
315c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
316129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown        public void onSelect() {
317c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
318c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
319c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
320c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Unselects the route.
321c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
322129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown        public void onUnselect() {
323c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
324c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
325c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
326c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Requests to set the volume of the route.
327c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
32811417b1cfde8f1749905f2d735623af9214148afJeff Brown         * @param volume The new volume value between 0 and {@link MediaRouteDescriptor#getVolumeMax}.
329c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
330129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown        public void onSetVolume(int volume) {
331c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
332c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
333c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
334c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Requests an incremental volume update for the route.
335c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
336c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param delta The delta to add to the current volume.
337c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
338129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown        public void onUpdateVolume(int delta) {
339c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
340c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
341c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
342129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown         * Performs a {@link MediaControlIntent media control} request
343129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown         * asynchronously on behalf of the route.
344c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
345c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param intent A {@link MediaControlIntent media control intent}.
346c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @param callback A {@link ControlRequestCallback} to invoke with the result
347c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * of the request, or null if no result is required.
348fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * @return True if the controller intends to handle the request and will
34911417b1cfde8f1749905f2d735623af9214148afJeff Brown         * invoke the callback when finished.  False if the controller will not
350fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown         * handle the request and will not invoke the callback.
351c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
352c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * @see MediaControlIntent
353c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
354129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown        public boolean onControlRequest(Intent intent, ControlRequestCallback callback) {
355c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return false;
356c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
357c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
358c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
359c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
360c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Callback which is invoked when route information becomes available or changes.
361c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
362c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public static abstract class Callback {
363c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
364c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Called when information about a route provider and its routes changes.
365c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         *
36611417b1cfde8f1749905f2d735623af9214148afJeff Brown         * @param provider The media route provider that changed, never null.
36711417b1cfde8f1749905f2d735623af9214148afJeff Brown         * @param descriptor The new media route provider descriptor, or null if none.
368c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
369c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onDescriptorChanged(MediaRouteProvider provider,
37011417b1cfde8f1749905f2d735623af9214148afJeff Brown                MediaRouteProviderDescriptor descriptor) {
371c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
372c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
373c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
374c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    private final class ProviderHandler extends Handler {
375c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
376c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void handleMessage(Message msg) {
377c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            switch (msg.what) {
378c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                case MSG_DELIVER_DESCRIPTOR_CHANGED:
379c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    deliverDescriptorChanged();
380c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    break;
38111417b1cfde8f1749905f2d735623af9214148afJeff Brown                case MSG_DELIVER_DISCOVERY_REQUEST_CHANGED:
38211417b1cfde8f1749905f2d735623af9214148afJeff Brown                    deliverDiscoveryRequestChanged();
38311417b1cfde8f1749905f2d735623af9214148afJeff Brown                    break;
384c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
385c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
386c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
387c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown}
388