blob: afca6cfbd99c392a3969b005a830093cfb89e786 [file] [log] [blame]
Kyunglyul Hyund51666d2019-04-11 04:08:40 +00001/*
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
17package com.android.mediarouteprovider.example;
18
Kyunglyul Hyun3aeabe02019-12-20 19:42:39 +090019import static android.media.MediaRoute2Info.DEVICE_TYPE_SPEAKER;
20import static android.media.MediaRoute2Info.DEVICE_TYPE_TV;
21
Kyunglyul Hyund51666d2019-04-11 04:08:40 +000022import android.content.Intent;
Kyunglyul Hyun3aedf022019-04-15 16:38:19 +090023import android.media.MediaRoute2Info;
24import android.media.MediaRoute2ProviderInfo;
Kyunglyul Hyund51666d2019-04-11 04:08:40 +000025import android.media.MediaRoute2ProviderService;
Kyunglyul Hyun24cc66f2019-12-19 19:04:20 +090026import android.media.RouteSessionInfo;
Kyunglyul Hyund51666d2019-04-11 04:08:40 +000027import android.os.IBinder;
Hyundo Moon63a05402019-12-19 20:13:56 +090028import android.text.TextUtils;
Kyunglyul Hyund51666d2019-04-11 04:08:40 +000029
Kyunglyul Hyun3aedf022019-04-15 16:38:19 +090030import java.util.HashMap;
31import java.util.Map;
32
Kyunglyul Hyund51666d2019-04-11 04:08:40 +000033public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService {
Hyundo Moon7d3ab332019-10-16 11:09:56 +090034 private static final String TAG = "SampleMR2ProviderSvc";
Kyunglyul Hyun3aedf022019-04-15 16:38:19 +090035
Kyunglyul Hyuncaae8dc2019-04-29 15:45:23 +090036 public static final String ROUTE_ID1 = "route_id1";
37 public static final String ROUTE_NAME1 = "Sample Route 1";
38 public static final String ROUTE_ID2 = "route_id2";
39 public static final String ROUTE_NAME2 = "Sample Route 2";
Hyundo Moon63a05402019-12-19 20:13:56 +090040 public static final String ROUTE_ID3_SESSION_CREATION_FAILED =
41 "route_id3_session_creation_failed";
42 public static final String ROUTE_NAME3 = "Sample Route 3 - Session creation failed";
Kyunglyul Hyune23e5ed2019-07-15 16:06:26 +090043
44 public static final String ROUTE_ID_SPECIAL_CATEGORY = "route_special_category";
45 public static final String ROUTE_NAME_SPECIAL_CATEGORY = "Special Category Route";
46
Kyunglyul Hyun9b10a642019-10-08 15:29:23 +090047 public static final int VOLUME_MAX = 100;
48 public static final String ROUTE_ID_FIXED_VOLUME = "route_fixed_volume";
49 public static final String ROUTE_NAME_FIXED_VOLUME = "Fixed Volume Route";
50 public static final String ROUTE_ID_VARIABLE_VOLUME = "route_variable_volume";
51 public static final String ROUTE_NAME_VARIABLE_VOLUME = "Variable Volume Route";
52
Kyunglyul Hyuncaae8dc2019-04-29 15:45:23 +090053 public static final String ACTION_REMOVE_ROUTE =
54 "com.android.mediarouteprovider.action_remove_route";
Kyunglyul Hyun3aedf022019-04-15 16:38:19 +090055
Kyunglyul Hyune23e5ed2019-07-15 16:06:26 +090056 public static final String CATEGORY_SAMPLE =
57 "com.android.mediarouteprovider.CATEGORY_SAMPLE";
58 public static final String CATEGORY_SPECIAL =
59 "com.android.mediarouteprovider.CATEGORY_SPECIAL";
60
Kyunglyul Hyun3aedf022019-04-15 16:38:19 +090061 Map<String, MediaRoute2Info> mRoutes = new HashMap<>();
Kyunglyul Hyuncb8894d2019-12-27 14:24:46 +090062 Map<String, Integer> mRouteSessionMap = new HashMap<>();
Hyundo Moona64ca9e2019-12-27 17:41:17 +090063 private int mNextSessionId = 1000;
Kyunglyul Hyun3aedf022019-04-15 16:38:19 +090064
65 private void initializeRoutes() {
66 MediaRoute2Info route1 = new MediaRoute2Info.Builder(ROUTE_ID1, ROUTE_NAME1)
Kyunglyul Hyune23e5ed2019-07-15 16:06:26 +090067 .addSupportedCategory(CATEGORY_SAMPLE)
Kyunglyul Hyun3aeabe02019-12-20 19:42:39 +090068 .setDeviceType(DEVICE_TYPE_TV)
Kyunglyul Hyun3aedf022019-04-15 16:38:19 +090069 .build();
Kyunglyul Hyuncaae8dc2019-04-29 15:45:23 +090070 MediaRoute2Info route2 = new MediaRoute2Info.Builder(ROUTE_ID2, ROUTE_NAME2)
Kyunglyul Hyune23e5ed2019-07-15 16:06:26 +090071 .addSupportedCategory(CATEGORY_SAMPLE)
Kyunglyul Hyun3aeabe02019-12-20 19:42:39 +090072 .setDeviceType(DEVICE_TYPE_SPEAKER)
Kyunglyul Hyuncaae8dc2019-04-29 15:45:23 +090073 .build();
Hyundo Moon63a05402019-12-19 20:13:56 +090074 MediaRoute2Info route3 = new MediaRoute2Info.Builder(
75 ROUTE_ID3_SESSION_CREATION_FAILED, ROUTE_NAME3)
76 .addSupportedCategory(CATEGORY_SAMPLE)
77 .build();
Kyunglyul Hyune23e5ed2019-07-15 16:06:26 +090078 MediaRoute2Info routeSpecial =
79 new MediaRoute2Info.Builder(ROUTE_ID_SPECIAL_CATEGORY, ROUTE_NAME_SPECIAL_CATEGORY)
80 .addSupportedCategory(CATEGORY_SAMPLE)
81 .addSupportedCategory(CATEGORY_SPECIAL)
82 .build();
Kyunglyul Hyun9b10a642019-10-08 15:29:23 +090083 MediaRoute2Info fixedVolumeRoute =
84 new MediaRoute2Info.Builder(ROUTE_ID_FIXED_VOLUME, ROUTE_NAME_FIXED_VOLUME)
85 .addSupportedCategory(CATEGORY_SAMPLE)
86 .setVolumeHandling(MediaRoute2Info.PLAYBACK_VOLUME_FIXED)
87 .build();
88 MediaRoute2Info variableVolumeRoute =
89 new MediaRoute2Info.Builder(ROUTE_ID_VARIABLE_VOLUME, ROUTE_NAME_VARIABLE_VOLUME)
90 .addSupportedCategory(CATEGORY_SAMPLE)
91 .setVolumeHandling(MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
92 .setVolumeMax(VOLUME_MAX)
93 .build();
94
Kyunglyul Hyun3aedf022019-04-15 16:38:19 +090095 mRoutes.put(route1.getId(), route1);
Kyunglyul Hyuncaae8dc2019-04-29 15:45:23 +090096 mRoutes.put(route2.getId(), route2);
Hyundo Moon63a05402019-12-19 20:13:56 +090097 mRoutes.put(route3.getId(), route3);
Kyunglyul Hyune23e5ed2019-07-15 16:06:26 +090098 mRoutes.put(routeSpecial.getId(), routeSpecial);
Kyunglyul Hyun9b10a642019-10-08 15:29:23 +090099 mRoutes.put(fixedVolumeRoute.getId(), fixedVolumeRoute);
100 mRoutes.put(variableVolumeRoute.getId(), variableVolumeRoute);
Kyunglyul Hyun3aedf022019-04-15 16:38:19 +0900101 }
102
103 @Override
104 public void onCreate() {
105 initializeRoutes();
106 }
107
Kyunglyul Hyund51666d2019-04-11 04:08:40 +0000108 @Override
109 public IBinder onBind(Intent intent) {
Kyunglyul Hyun3aedf022019-04-15 16:38:19 +0900110 publishRoutes();
Kyunglyul Hyund51666d2019-04-11 04:08:40 +0000111 return super.onBind(intent);
112 }
113
114 @Override
Kyunglyul Hyuncaae8dc2019-04-29 15:45:23 +0900115 public void onControlRequest(String routeId, Intent request) {
Kyunglyul Hyun3aeabe02019-12-20 19:42:39 +0900116 String action = request.getAction();
117 if (ACTION_REMOVE_ROUTE.equals(action)) {
Kyunglyul Hyuncaae8dc2019-04-29 15:45:23 +0900118 MediaRoute2Info route = mRoutes.get(routeId);
119 if (route != null) {
120 mRoutes.remove(routeId);
121 publishRoutes();
122 mRoutes.put(routeId, route);
123 }
124 }
125 }
126
Kyunglyul Hyun5a8dedc2019-10-10 14:09:44 +0900127 @Override
128 public void onSetVolume(String routeId, int volume) {
129 MediaRoute2Info route = mRoutes.get(routeId);
130 if (route == null) {
131 return;
132 }
133 volume = Math.min(volume, Math.max(0, route.getVolumeMax()));
134 mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
135 .setVolume(volume)
136 .build());
137 publishRoutes();
138 }
139
140 @Override
141 public void onUpdateVolume(String routeId, int delta) {
142 MediaRoute2Info route = mRoutes.get(routeId);
143 if (route == null) {
144 return;
145 }
146 int volume = route.getVolume() + delta;
147 volume = Math.min(volume, Math.max(0, route.getVolumeMax()));
148 mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
149 .setVolume(volume)
150 .build());
151 publishRoutes();
152 }
153
Kyunglyul Hyun24cc66f2019-12-19 19:04:20 +0900154 @Override
155 public void onCreateSession(String packageName, String routeId, String controlCategory,
Kyunglyul Hyunc7847242019-12-30 17:46:04 +0900156 long requestId) {
Kyunglyul Hyuncb8894d2019-12-27 14:24:46 +0900157 MediaRoute2Info route = mRoutes.get(routeId);
158 if (route == null || TextUtils.equals(ROUTE_ID3_SESSION_CREATION_FAILED, routeId)) {
Hyundo Moon63a05402019-12-19 20:13:56 +0900159 // Tell the router that session cannot be created by passing null as sessionInfo.
Kyunglyul Hyune6b62382019-12-24 11:17:24 +0900160 notifySessionCreated(/* sessionInfo= */ null, requestId);
Hyundo Moon63a05402019-12-19 20:13:56 +0900161 return;
162 }
Hyundo Moon9be35482019-12-30 17:33:13 +0900163 maybeDeselectRoute(routeId);
Kyunglyul Hyuncb8894d2019-12-27 14:24:46 +0900164
Hyundo Moona64ca9e2019-12-27 17:41:17 +0900165 final int sessionId = mNextSessionId;
166 mNextSessionId++;
Kyunglyul Hyuncb8894d2019-12-27 14:24:46 +0900167
168 mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
169 .setClientPackageName(packageName)
170 .build());
171 mRouteSessionMap.put(routeId, sessionId);
Hyundo Moon63a05402019-12-19 20:13:56 +0900172
Kyunglyul Hyun24cc66f2019-12-19 19:04:20 +0900173 RouteSessionInfo sessionInfo = new RouteSessionInfo.Builder(
Kyunglyul Hyuncb8894d2019-12-27 14:24:46 +0900174 sessionId, packageName, controlCategory)
Kyunglyul Hyun24cc66f2019-12-19 19:04:20 +0900175 .addSelectedRoute(routeId)
176 .build();
Kyunglyul Hyuncb8894d2019-12-27 14:24:46 +0900177 notifySessionCreated(sessionInfo, requestId);
178 publishRoutes();
Kyunglyul Hyun24cc66f2019-12-19 19:04:20 +0900179 }
180
181 @Override
Kyunglyul Hyuncb8894d2019-12-27 14:24:46 +0900182 public void onDestroySession(int sessionId, RouteSessionInfo lastSessionInfo) {
183 for (String routeId : lastSessionInfo.getSelectedRoutes()) {
184 mRouteSessionMap.remove(routeId);
185 MediaRoute2Info route = mRoutes.get(routeId);
186 if (route != null) {
187 mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
188 .setClientPackageName(null)
189 .build());
190 }
191 }
192 }
Kyunglyul Hyun24cc66f2019-12-19 19:04:20 +0900193
194 @Override
Hyundo Moon9be35482019-12-30 17:33:13 +0900195 public void onSelectRoute(int sessionId, String routeId) {
Kyunglyul Hyun24cc66f2019-12-19 19:04:20 +0900196 RouteSessionInfo sessionInfo = getSessionInfo(sessionId);
Kyunglyul Hyuncb8894d2019-12-27 14:24:46 +0900197 MediaRoute2Info route = mRoutes.get(routeId);
198 if (route == null || sessionInfo == null) {
199 return;
200 }
Hyundo Moon9be35482019-12-30 17:33:13 +0900201 maybeDeselectRoute(routeId);
Kyunglyul Hyuncb8894d2019-12-27 14:24:46 +0900202
203 mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
204 .setClientPackageName(sessionInfo.getPackageName())
205 .build());
206 mRouteSessionMap.put(routeId, sessionId);
207
Kyunglyul Hyun24cc66f2019-12-19 19:04:20 +0900208 RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo)
209 .addSelectedRoute(routeId)
210 .build();
Kyunglyul Hyune6b62382019-12-24 11:17:24 +0900211 updateSessionInfo(newSessionInfo);
Kyunglyul Hyun24cc66f2019-12-19 19:04:20 +0900212 publishRoutes();
213 }
214
215 @Override
Hyundo Moon9be35482019-12-30 17:33:13 +0900216 public void onDeselectRoute(int sessionId, String routeId) {
Kyunglyul Hyun24cc66f2019-12-19 19:04:20 +0900217 RouteSessionInfo sessionInfo = getSessionInfo(sessionId);
Kyunglyul Hyuncb8894d2019-12-27 14:24:46 +0900218 MediaRoute2Info route = mRoutes.get(routeId);
219
220 mRouteSessionMap.remove(routeId);
221 if (sessionInfo == null || route == null) {
222 return;
223 }
224 mRoutes.put(routeId, new MediaRoute2Info.Builder(route)
225 .setClientPackageName(null)
226 .build());
227
Kyunglyul Hyun24cc66f2019-12-19 19:04:20 +0900228 RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo)
229 .removeSelectedRoute(routeId)
230 .build();
Kyunglyul Hyune6b62382019-12-24 11:17:24 +0900231 updateSessionInfo(newSessionInfo);
Kyunglyul Hyun24cc66f2019-12-19 19:04:20 +0900232 publishRoutes();
233 }
234
235 @Override
236 public void onTransferRoute(int sessionId, String routeId) {
237 RouteSessionInfo sessionInfo = getSessionInfo(sessionId);
238 RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo)
239 .clearSelectedRoutes()
240 .addSelectedRoute(routeId)
241 .build();
Kyunglyul Hyune6b62382019-12-24 11:17:24 +0900242 updateSessionInfo(newSessionInfo);
Kyunglyul Hyun24cc66f2019-12-19 19:04:20 +0900243 publishRoutes();
244 }
245
Hyundo Moon9be35482019-12-30 17:33:13 +0900246 void maybeDeselectRoute(String routeId) {
Kyunglyul Hyuncb8894d2019-12-27 14:24:46 +0900247 if (!mRouteSessionMap.containsKey(routeId)) {
248 return;
249 }
250
251 int sessionId = mRouteSessionMap.get(routeId);
Hyundo Moon9be35482019-12-30 17:33:13 +0900252 onDeselectRoute(sessionId, routeId);
Kyunglyul Hyuncb8894d2019-12-27 14:24:46 +0900253 }
254
Kyunglyul Hyun3aedf022019-04-15 16:38:19 +0900255 void publishRoutes() {
256 MediaRoute2ProviderInfo info = new MediaRoute2ProviderInfo.Builder()
257 .addRoutes(mRoutes.values())
258 .build();
Kyunglyul Hyun24cc66f2019-12-19 19:04:20 +0900259 updateProviderInfo(info);
Kyunglyul Hyund51666d2019-04-11 04:08:40 +0000260 }
261}