1 /*
2  * Copyright (C) 2023 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.settingslib.media;
18 
19 import android.content.Context;
20 import android.media.MediaRoute2Info;
21 import android.media.MediaRouter2Manager;
22 import android.media.RouteListingPreference;
23 import android.media.RoutingSessionInfo;
24 import android.media.session.MediaController;
25 import android.os.UserHandle;
26 import android.text.TextUtils;
27 import android.util.Log;
28 
29 import androidx.annotation.NonNull;
30 import androidx.annotation.Nullable;
31 
32 import com.android.internal.annotations.VisibleForTesting;
33 import com.android.settingslib.bluetooth.LocalBluetoothManager;
34 
35 import java.util.List;
36 import java.util.concurrent.Executor;
37 import java.util.concurrent.Executors;
38 
39 /**
40  * Template implementation of {@link InfoMediaManager} using {@link MediaRouter2Manager}.
41  */
42 public class ManagerInfoMediaManager extends InfoMediaManager {
43 
44     private static final String TAG = "ManagerInfoMediaManager";
45     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
46 
47     @VisibleForTesting
48     /* package */ final RouterManagerCallback mMediaRouterCallback = new RouterManagerCallback();
49     @VisibleForTesting
50     /* package */ MediaRouter2Manager mRouterManager;
51     boolean mIsScanning = false;
52 
53     private final Executor mExecutor = Executors.newSingleThreadExecutor();
54 
ManagerInfoMediaManager( Context context, @NonNull String packageName, @NonNull UserHandle userHandle, LocalBluetoothManager localBluetoothManager, @Nullable MediaController mediaController)55     /* package */ ManagerInfoMediaManager(
56             Context context,
57             @NonNull String packageName,
58             @NonNull UserHandle userHandle,
59             LocalBluetoothManager localBluetoothManager,
60             @Nullable MediaController mediaController) {
61         super(context, packageName, userHandle, localBluetoothManager, mediaController);
62 
63         mRouterManager = MediaRouter2Manager.getInstance(context);
64     }
65 
66     @Override
startScanOnRouter()67     protected void startScanOnRouter() {
68         if (!mIsScanning) {
69             mRouterManager.registerScanRequest();
70             mIsScanning = true;
71         }
72     }
73 
74     @Override
registerRouter()75     protected void registerRouter() {
76         mRouterManager.registerCallback(mExecutor, mMediaRouterCallback);
77     }
78 
79     @Override
stopScanOnRouter()80     protected void stopScanOnRouter() {
81         if (mIsScanning) {
82             mRouterManager.unregisterScanRequest();
83             mIsScanning = false;
84         }
85     }
86 
87     @Override
unregisterRouter()88     protected void unregisterRouter() {
89         mRouterManager.unregisterCallback(mMediaRouterCallback);
90     }
91 
92     @Override
transferToRoute(@onNull MediaRoute2Info route)93     protected void transferToRoute(@NonNull MediaRoute2Info route) {
94         mRouterManager.transfer(mPackageName, route, mUserHandle);
95     }
96 
97     @Override
selectRoute(@onNull MediaRoute2Info route, @NonNull RoutingSessionInfo info)98     protected void selectRoute(@NonNull MediaRoute2Info route, @NonNull RoutingSessionInfo info) {
99         mRouterManager.selectRoute(info, route);
100     }
101 
102     @Override
deselectRoute(@onNull MediaRoute2Info route, @NonNull RoutingSessionInfo info)103     protected void deselectRoute(@NonNull MediaRoute2Info route, @NonNull RoutingSessionInfo info) {
104         mRouterManager.deselectRoute(info, route);
105     }
106 
107     @Override
releaseSession(@onNull RoutingSessionInfo sessionInfo)108     protected void releaseSession(@NonNull RoutingSessionInfo sessionInfo) {
109         mRouterManager.releaseSession(sessionInfo);
110     }
111 
112     @Override
113     @NonNull
getSelectableRoutes(@onNull RoutingSessionInfo info)114     protected List<MediaRoute2Info> getSelectableRoutes(@NonNull RoutingSessionInfo info) {
115         return mRouterManager.getSelectableRoutes(info);
116     }
117 
118     @Override
119     @NonNull
getDeselectableRoutes(@onNull RoutingSessionInfo info)120     protected List<MediaRoute2Info> getDeselectableRoutes(@NonNull RoutingSessionInfo info) {
121         return mRouterManager.getDeselectableRoutes(info);
122     }
123 
124     @Override
125     @NonNull
getSelectedRoutes(@onNull RoutingSessionInfo info)126     protected List<MediaRoute2Info> getSelectedRoutes(@NonNull RoutingSessionInfo info) {
127         return mRouterManager.getSelectedRoutes(info);
128     }
129 
130     @Override
setSessionVolume(@onNull RoutingSessionInfo info, int volume)131     protected void setSessionVolume(@NonNull RoutingSessionInfo info, int volume) {
132         mRouterManager.setSessionVolume(info, volume);
133     }
134 
135     @Override
setRouteVolume(@onNull MediaRoute2Info route, int volume)136     protected void setRouteVolume(@NonNull MediaRoute2Info route, int volume) {
137         mRouterManager.setRouteVolume(route, volume);
138     }
139 
140     @Override
141     @Nullable
getRouteListingPreference()142     protected RouteListingPreference getRouteListingPreference() {
143         return mRouterManager.getRouteListingPreference(mPackageName);
144     }
145 
146     @Override
147     @NonNull
getRoutingSessionsForPackage()148     protected List<RoutingSessionInfo> getRoutingSessionsForPackage() {
149         return mRouterManager.getRoutingSessions(mPackageName);
150     }
151 
152     @Override
153     @NonNull
getRemoteSessions()154     protected List<RoutingSessionInfo> getRemoteSessions() {
155         return mRouterManager.getRemoteSessions();
156     }
157 
158     @Nullable
159     @Override
getRoutingSessionById(@onNull String sessionId)160     protected RoutingSessionInfo getRoutingSessionById(@NonNull String sessionId) {
161         for (RoutingSessionInfo sessionInfo : getRemoteSessions()) {
162             if (TextUtils.equals(sessionInfo.getId(), sessionId)) {
163                 return sessionInfo;
164             }
165         }
166 
167         RoutingSessionInfo systemSession = mRouterManager.getSystemRoutingSession(null);
168 
169         return TextUtils.equals(systemSession.getId(), sessionId) ? systemSession : null;
170     }
171 
172     @Override
173     @NonNull
getAvailableRoutesFromRouter()174     protected List<MediaRoute2Info> getAvailableRoutesFromRouter() {
175         return mRouterManager.getAvailableRoutes(mPackageName);
176     }
177 
178     @Override
179     @NonNull
getTransferableRoutes(@onNull String packageName)180     protected List<MediaRoute2Info> getTransferableRoutes(@NonNull String packageName) {
181         return mRouterManager.getTransferableRoutes(packageName);
182     }
183 
184     @VisibleForTesting
185     /* package */ final class RouterManagerCallback implements MediaRouter2Manager.Callback {
186 
187         @Override
onRoutesUpdated()188         public void onRoutesUpdated() {
189             refreshDevices();
190         }
191 
192         @Override
onPreferredFeaturesChanged(String packageName, List<String> preferredFeatures)193         public void onPreferredFeaturesChanged(String packageName, List<String> preferredFeatures) {
194             if (TextUtils.equals(mPackageName, packageName)) {
195                 refreshDevices();
196             }
197         }
198 
199         @Override
onTransferred(RoutingSessionInfo oldSession, RoutingSessionInfo newSession)200         public void onTransferred(RoutingSessionInfo oldSession, RoutingSessionInfo newSession) {
201             if (DEBUG) {
202                 Log.d(
203                         TAG,
204                         "onTransferred() oldSession : "
205                                 + oldSession.getName()
206                                 + ", newSession : "
207                                 + newSession.getName());
208             }
209             rebuildDeviceList();
210             notifyCurrentConnectedDeviceChanged();
211         }
212 
213         /**
214          * Ignore callback here since we'll also receive{@link
215          * MediaRouter2Manager.Callback#onRequestFailed onRequestFailed} with reason code.
216          */
217         @Override
onTransferFailed(RoutingSessionInfo session, MediaRoute2Info route)218         public void onTransferFailed(RoutingSessionInfo session, MediaRoute2Info route) {}
219 
220         @Override
onRequestFailed(int reason)221         public void onRequestFailed(int reason) {
222             dispatchOnRequestFailed(reason);
223         }
224 
225         @Override
onSessionUpdated(RoutingSessionInfo sessionInfo)226         public void onSessionUpdated(RoutingSessionInfo sessionInfo) {
227             refreshDevices();
228         }
229 
230         @Override
onSessionReleased(@onNull RoutingSessionInfo session)231         public void onSessionReleased(@NonNull RoutingSessionInfo session) {
232             refreshDevices();
233         }
234 
235         @Override
onRouteListingPreferenceUpdated( String packageName, RouteListingPreference routeListingPreference)236         public void onRouteListingPreferenceUpdated(
237                 String packageName, RouteListingPreference routeListingPreference) {
238             if (!TextUtils.equals(mPackageName, packageName)) {
239                 return;
240             }
241             notifyRouteListingPreferenceUpdated(routeListingPreference);
242             refreshDevices();
243         }
244     }
245 }
246