• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.media;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.content.ComponentName;
22 import android.media.MediaRoute2Info;
23 import android.media.MediaRoute2ProviderInfo;
24 import android.media.MediaRoute2ProviderService.Reason;
25 import android.media.MediaRouter2;
26 import android.media.MediaRouter2Utils;
27 import android.media.RouteDiscoveryPreference;
28 import android.media.RoutingSessionInfo;
29 import android.os.Bundle;
30 import android.os.UserHandle;
31 
32 import com.android.internal.annotations.GuardedBy;
33 
34 import java.io.PrintWriter;
35 import java.util.ArrayList;
36 import java.util.List;
37 import java.util.Objects;
38 import java.util.Set;
39 
40 abstract class MediaRoute2Provider {
41     final ComponentName mComponentName;
42     final String mUniqueId;
43     final Object mLock = new Object();
44 
45     Callback mCallback;
46     public final boolean mIsSystemRouteProvider;
47     private volatile MediaRoute2ProviderInfo mProviderInfo;
48 
49     @GuardedBy("mLock")
50     final List<RoutingSessionInfo> mSessionInfos = new ArrayList<>();
51 
MediaRoute2Provider(@onNull ComponentName componentName, boolean isSystemRouteProvider)52     MediaRoute2Provider(@NonNull ComponentName componentName, boolean isSystemRouteProvider) {
53         mComponentName = Objects.requireNonNull(componentName, "Component name must not be null.");
54         mUniqueId = componentName.flattenToShortString();
55         mIsSystemRouteProvider = isSystemRouteProvider;
56     }
57 
setCallback(Callback callback)58     public void setCallback(Callback callback) {
59         mCallback = callback;
60     }
61 
requestCreateSession( long requestId, String packageName, String routeOriginalId, @Nullable Bundle sessionHints, @RoutingSessionInfo.TransferReason int transferReason, @NonNull UserHandle transferInitiatorUserHandle, @NonNull String transferInitiatorPackageName)62     public abstract void requestCreateSession(
63             long requestId,
64             String packageName,
65             String routeOriginalId,
66             @Nullable Bundle sessionHints,
67             @RoutingSessionInfo.TransferReason int transferReason,
68             @NonNull UserHandle transferInitiatorUserHandle,
69             @NonNull String transferInitiatorPackageName);
70 
releaseSession(long requestId, String sessionId)71     public abstract void releaseSession(long requestId, String sessionId);
72 
updateDiscoveryPreference( Set<String> activelyScanningPackages, RouteDiscoveryPreference discoveryPreference)73     public abstract void updateDiscoveryPreference(
74             Set<String> activelyScanningPackages, RouteDiscoveryPreference discoveryPreference);
75 
selectRoute(long requestId, String sessionId, String routeId)76     public abstract void selectRoute(long requestId, String sessionId, String routeId);
deselectRoute(long requestId, String sessionId, String routeId)77     public abstract void deselectRoute(long requestId, String sessionId, String routeId);
78 
transferToRoute( long requestId, @NonNull UserHandle transferInitiatorUserHandle, @NonNull String transferInitiatorPackageName, String sessionOriginalId, String routeOriginalId, @RoutingSessionInfo.TransferReason int transferReason)79     public abstract void transferToRoute(
80             long requestId,
81             @NonNull UserHandle transferInitiatorUserHandle,
82             @NonNull String transferInitiatorPackageName,
83             String sessionOriginalId,
84             String routeOriginalId,
85             @RoutingSessionInfo.TransferReason int transferReason);
86 
setRouteVolume(long requestId, String routeOriginalId, int volume)87     public abstract void setRouteVolume(long requestId, String routeOriginalId, int volume);
88 
setSessionVolume(long requestId, String sessionOriginalId, int volume)89     public abstract void setSessionVolume(long requestId, String sessionOriginalId, int volume);
90 
prepareReleaseSession(@onNull String sessionUniqueId)91     public abstract void prepareReleaseSession(@NonNull String sessionUniqueId);
92 
93     @NonNull
getUniqueId()94     public String getUniqueId() {
95         return mUniqueId;
96     }
97 
98     @Nullable
getProviderInfo()99     public MediaRoute2ProviderInfo getProviderInfo() {
100         return mProviderInfo;
101     }
102 
103     @NonNull
getSessionInfos()104     public List<RoutingSessionInfo> getSessionInfos() {
105         synchronized (mLock) {
106             return new ArrayList<>(mSessionInfos);
107         }
108     }
109 
setProviderState(MediaRoute2ProviderInfo providerInfo)110     void setProviderState(MediaRoute2ProviderInfo providerInfo) {
111         if (providerInfo == null) {
112             mProviderInfo = null;
113         } else {
114             mProviderInfo = new MediaRoute2ProviderInfo.Builder(providerInfo)
115                     .setUniqueId(mComponentName.getPackageName(), mUniqueId)
116                     .setSystemRouteProvider(mIsSystemRouteProvider)
117                     .build();
118         }
119     }
120 
notifyProviderState()121     void notifyProviderState() {
122         if (mCallback != null) {
123             mCallback.onProviderStateChanged(this);
124         }
125     }
126 
127     /** Calls {@link Callback#onRequestFailed} with the given id and reason. */
notifyRequestFailed(long requestId, @Reason int reason)128     protected void notifyRequestFailed(long requestId, @Reason int reason) {
129         if (mCallback != null) {
130             mCallback.onRequestFailed(/* provider= */ this, requestId, reason);
131         }
132     }
133 
setAndNotifyProviderState(MediaRoute2ProviderInfo providerInfo)134     void setAndNotifyProviderState(MediaRoute2ProviderInfo providerInfo) {
135         setProviderState(providerInfo);
136         notifyProviderState();
137     }
138 
hasComponentName(String packageName, String className)139     public boolean hasComponentName(String packageName, String className) {
140         return mComponentName.getPackageName().equals(packageName)
141                 && mComponentName.getClassName().equals(className);
142     }
143 
dump(PrintWriter pw, String prefix)144     public void dump(PrintWriter pw, String prefix) {
145         pw.println(prefix + getDebugString());
146         prefix += "  ";
147 
148         if (mProviderInfo == null) {
149             pw.println(prefix + "<provider info not received, yet>");
150         } else if (mProviderInfo.getRoutes().isEmpty()) {
151             pw.println(prefix + "<provider info has no routes>");
152         } else {
153             for (MediaRoute2Info route : mProviderInfo.getRoutes()) {
154                 pw.printf("%s%s | %s\n", prefix, route.getId(), route.getName());
155             }
156         }
157 
158         pw.println(prefix + "Active routing sessions:");
159         synchronized (mLock) {
160             if (mSessionInfos.isEmpty()) {
161                 pw.println(prefix + "  <no active routing sessions>");
162             } else {
163                 for (RoutingSessionInfo routingSessionInfo : mSessionInfos) {
164                     routingSessionInfo.dump(pw, prefix + "  ");
165                 }
166             }
167         }
168     }
169 
170     @Override
toString()171     public String toString() {
172         return getDebugString();
173     }
174 
175     /** Returns a human-readable string describing the instance, for debugging purposes. */
getDebugString()176     protected abstract String getDebugString();
177 
178     public interface Callback {
onProviderStateChanged(@ullable MediaRoute2Provider provider)179         void onProviderStateChanged(@Nullable MediaRoute2Provider provider);
onSessionCreated(@onNull MediaRoute2Provider provider, long requestId, @Nullable RoutingSessionInfo sessionInfo)180         void onSessionCreated(@NonNull MediaRoute2Provider provider,
181                 long requestId, @Nullable RoutingSessionInfo sessionInfo);
182 
183         /**
184          * Called when there's a session info change.
185          *
186          * <p>If the provided {@code sessionInfo} has a null {@link
187          * RoutingSessionInfo#getClientPackageName()}, that means that it's applicable to all
188          * packages. We call this type of routing session "global". This is typically used for
189          * system provided {@link RoutingSessionInfo}. However, some applications may be exempted
190          * from the global routing sessions, because their media is being routed using a session
191          * different from the global routing session.
192          *
193          * @param provider The provider that owns the session that changed.
194          * @param sessionInfo The new {@link RoutingSessionInfo}.
195          * @param packageNamesWithRoutingSessionOverrides The names of packages that are not
196          *     affected by global session changes. This set may only be non-empty when the {@code
197          *     sessionInfo} is for the global session, and therefore has no {@link
198          *     RoutingSessionInfo#getClientPackageName()}.
199          */
onSessionUpdated( @onNull MediaRoute2Provider provider, @NonNull RoutingSessionInfo sessionInfo, Set<String> packageNamesWithRoutingSessionOverrides)200         void onSessionUpdated(
201                 @NonNull MediaRoute2Provider provider,
202                 @NonNull RoutingSessionInfo sessionInfo,
203                 Set<String> packageNamesWithRoutingSessionOverrides);
204 
onSessionReleased(@onNull MediaRoute2Provider provider, @NonNull RoutingSessionInfo sessionInfo)205         void onSessionReleased(@NonNull MediaRoute2Provider provider,
206                 @NonNull RoutingSessionInfo sessionInfo);
207 
onRequestFailed( @onNull MediaRoute2Provider provider, long requestId, @Reason int reason)208         void onRequestFailed(
209                 @NonNull MediaRoute2Provider provider, long requestId, @Reason int reason);
210     }
211 
212     /**
213      * Holds session creation or transfer initiation information for a transfer in flight.
214      *
215      * <p>The initiator app is typically also the {@link RoutingSessionInfo#getClientPackageName()
216      * client app}, with the exception of the {@link MediaRouter2#getSystemController() system
217      * routing session} which is exceptional in that it's shared among all apps.
218      *
219      * <p>For the system routing session, the initiator app is the one that programmatically
220      * triggered the transfer (for example, via {@link MediaRouter2#transferTo}), or the target app
221      * of the proxy router that did the transfer.
222      *
223      * @see MediaRouter2.RoutingController#wasTransferInitiatedBySelf()
224      * @see RoutingSessionInfo#getTransferInitiatorPackageName()
225      * @see RoutingSessionInfo#getTransferInitiatorUserHandle()
226      */
227     protected static class SessionCreationOrTransferRequest {
228 
229         /**
230          * The id of the request, or {@link
231          * android.media.MediaRoute2ProviderService#REQUEST_ID_NONE} if unknown.
232          */
233         public final long mRequestId;
234 
235         /** The {@link MediaRoute2Info#getOriginalId()} original id} of the target route. */
236         @NonNull public final String mTargetOriginalRouteId;
237 
238         @RoutingSessionInfo.TransferReason public final int mTransferReason;
239 
240         /** The {@link android.os.UserHandle} on which the initiator app is running. */
241         @NonNull public final UserHandle mTransferInitiatorUserHandle;
242 
243         @NonNull public final String mTransferInitiatorPackageName;
244 
SessionCreationOrTransferRequest( long requestId, @NonNull String targetOriginalRouteId, @RoutingSessionInfo.TransferReason int transferReason, @NonNull UserHandle transferInitiatorUserHandle, @NonNull String transferInitiatorPackageName)245         SessionCreationOrTransferRequest(
246                 long requestId,
247                 @NonNull String targetOriginalRouteId,
248                 @RoutingSessionInfo.TransferReason int transferReason,
249                 @NonNull UserHandle transferInitiatorUserHandle,
250                 @NonNull String transferInitiatorPackageName) {
251             mRequestId = requestId;
252             mTargetOriginalRouteId = targetOriginalRouteId;
253             mTransferReason = transferReason;
254             mTransferInitiatorUserHandle = transferInitiatorUserHandle;
255             mTransferInitiatorPackageName = transferInitiatorPackageName;
256         }
257 
isTargetRoute(@ullable MediaRoute2Info route2Info)258         public boolean isTargetRoute(@Nullable MediaRoute2Info route2Info) {
259             return route2Info != null && mTargetOriginalRouteId.equals(route2Info.getOriginalId());
260         }
261 
262         /**
263          * Returns whether the given list of {@link MediaRoute2Info#getOriginalId() original ids}
264          * contains the {@link #mTargetOriginalRouteId target route id}.
265          */
isTargetRouteIdInRouteOriginalIdList( @onNull List<String> originalRouteIdList)266         public boolean isTargetRouteIdInRouteOriginalIdList(
267                 @NonNull List<String> originalRouteIdList) {
268             return originalRouteIdList.stream().anyMatch(mTargetOriginalRouteId::equals);
269         }
270 
271         /**
272          * Returns whether the given list of {@link MediaRoute2Info#getId() unique ids} contains the
273          * {@link #mTargetOriginalRouteId target route id}.
274          */
isTargetRouteIdInRouteUniqueIdList(@onNull List<String> uniqueRouteIdList)275         public boolean isTargetRouteIdInRouteUniqueIdList(@NonNull List<String> uniqueRouteIdList) {
276             return uniqueRouteIdList.stream()
277                     .map(MediaRouter2Utils::getOriginalId)
278                     .anyMatch(mTargetOriginalRouteId::equals);
279         }
280     }
281 }
282