Kyunglyul Hyun | d51666d | 2019-04-11 04:08:40 +0000 | [diff] [blame] | 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.mediarouteprovider.example; |
| 18 | |
Kyunglyul Hyun | 3aeabe0 | 2019-12-20 19:42:39 +0900 | [diff] [blame] | 19 | import static android.media.MediaRoute2Info.DEVICE_TYPE_SPEAKER; |
| 20 | import static android.media.MediaRoute2Info.DEVICE_TYPE_TV; |
| 21 | |
Kyunglyul Hyun | d51666d | 2019-04-11 04:08:40 +0000 | [diff] [blame] | 22 | import android.content.Intent; |
Kyunglyul Hyun | 3aedf02 | 2019-04-15 16:38:19 +0900 | [diff] [blame] | 23 | import android.media.MediaRoute2Info; |
| 24 | import android.media.MediaRoute2ProviderInfo; |
Kyunglyul Hyun | d51666d | 2019-04-11 04:08:40 +0000 | [diff] [blame] | 25 | import android.media.MediaRoute2ProviderService; |
Kyunglyul Hyun | 24cc66f | 2019-12-19 19:04:20 +0900 | [diff] [blame] | 26 | import android.media.RouteSessionInfo; |
Kyunglyul Hyun | d51666d | 2019-04-11 04:08:40 +0000 | [diff] [blame] | 27 | import android.os.IBinder; |
Hyundo Moon | 63a0540 | 2019-12-19 20:13:56 +0900 | [diff] [blame] | 28 | import android.text.TextUtils; |
Kyunglyul Hyun | d51666d | 2019-04-11 04:08:40 +0000 | [diff] [blame] | 29 | |
Kyunglyul Hyun | 3aedf02 | 2019-04-15 16:38:19 +0900 | [diff] [blame] | 30 | import java.util.HashMap; |
| 31 | import java.util.Map; |
| 32 | |
Kyunglyul Hyun | d51666d | 2019-04-11 04:08:40 +0000 | [diff] [blame] | 33 | public class SampleMediaRoute2ProviderService extends MediaRoute2ProviderService { |
Hyundo Moon | 7d3ab33 | 2019-10-16 11:09:56 +0900 | [diff] [blame] | 34 | private static final String TAG = "SampleMR2ProviderSvc"; |
Kyunglyul Hyun | 3aedf02 | 2019-04-15 16:38:19 +0900 | [diff] [blame] | 35 | |
Kyunglyul Hyun | caae8dc | 2019-04-29 15:45:23 +0900 | [diff] [blame] | 36 | 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 Moon | 63a0540 | 2019-12-19 20:13:56 +0900 | [diff] [blame] | 40 | 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 Hyun | e23e5ed | 2019-07-15 16:06:26 +0900 | [diff] [blame] | 43 | |
| 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 Hyun | 9b10a64 | 2019-10-08 15:29:23 +0900 | [diff] [blame] | 47 | 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 Hyun | caae8dc | 2019-04-29 15:45:23 +0900 | [diff] [blame] | 53 | public static final String ACTION_REMOVE_ROUTE = |
| 54 | "com.android.mediarouteprovider.action_remove_route"; |
Kyunglyul Hyun | 3aedf02 | 2019-04-15 16:38:19 +0900 | [diff] [blame] | 55 | |
Kyunglyul Hyun | e23e5ed | 2019-07-15 16:06:26 +0900 | [diff] [blame] | 56 | 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 Hyun | 3aedf02 | 2019-04-15 16:38:19 +0900 | [diff] [blame] | 61 | Map<String, MediaRoute2Info> mRoutes = new HashMap<>(); |
Kyunglyul Hyun | cb8894d | 2019-12-27 14:24:46 +0900 | [diff] [blame] | 62 | Map<String, Integer> mRouteSessionMap = new HashMap<>(); |
Hyundo Moon | a64ca9e | 2019-12-27 17:41:17 +0900 | [diff] [blame] | 63 | private int mNextSessionId = 1000; |
Kyunglyul Hyun | 3aedf02 | 2019-04-15 16:38:19 +0900 | [diff] [blame] | 64 | |
| 65 | private void initializeRoutes() { |
| 66 | MediaRoute2Info route1 = new MediaRoute2Info.Builder(ROUTE_ID1, ROUTE_NAME1) |
Kyunglyul Hyun | e23e5ed | 2019-07-15 16:06:26 +0900 | [diff] [blame] | 67 | .addSupportedCategory(CATEGORY_SAMPLE) |
Kyunglyul Hyun | 3aeabe0 | 2019-12-20 19:42:39 +0900 | [diff] [blame] | 68 | .setDeviceType(DEVICE_TYPE_TV) |
Kyunglyul Hyun | 3aedf02 | 2019-04-15 16:38:19 +0900 | [diff] [blame] | 69 | .build(); |
Kyunglyul Hyun | caae8dc | 2019-04-29 15:45:23 +0900 | [diff] [blame] | 70 | MediaRoute2Info route2 = new MediaRoute2Info.Builder(ROUTE_ID2, ROUTE_NAME2) |
Kyunglyul Hyun | e23e5ed | 2019-07-15 16:06:26 +0900 | [diff] [blame] | 71 | .addSupportedCategory(CATEGORY_SAMPLE) |
Kyunglyul Hyun | 3aeabe0 | 2019-12-20 19:42:39 +0900 | [diff] [blame] | 72 | .setDeviceType(DEVICE_TYPE_SPEAKER) |
Kyunglyul Hyun | caae8dc | 2019-04-29 15:45:23 +0900 | [diff] [blame] | 73 | .build(); |
Hyundo Moon | 63a0540 | 2019-12-19 20:13:56 +0900 | [diff] [blame] | 74 | MediaRoute2Info route3 = new MediaRoute2Info.Builder( |
| 75 | ROUTE_ID3_SESSION_CREATION_FAILED, ROUTE_NAME3) |
| 76 | .addSupportedCategory(CATEGORY_SAMPLE) |
| 77 | .build(); |
Kyunglyul Hyun | e23e5ed | 2019-07-15 16:06:26 +0900 | [diff] [blame] | 78 | 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 Hyun | 9b10a64 | 2019-10-08 15:29:23 +0900 | [diff] [blame] | 83 | 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 Hyun | 3aedf02 | 2019-04-15 16:38:19 +0900 | [diff] [blame] | 95 | mRoutes.put(route1.getId(), route1); |
Kyunglyul Hyun | caae8dc | 2019-04-29 15:45:23 +0900 | [diff] [blame] | 96 | mRoutes.put(route2.getId(), route2); |
Hyundo Moon | 63a0540 | 2019-12-19 20:13:56 +0900 | [diff] [blame] | 97 | mRoutes.put(route3.getId(), route3); |
Kyunglyul Hyun | e23e5ed | 2019-07-15 16:06:26 +0900 | [diff] [blame] | 98 | mRoutes.put(routeSpecial.getId(), routeSpecial); |
Kyunglyul Hyun | 9b10a64 | 2019-10-08 15:29:23 +0900 | [diff] [blame] | 99 | mRoutes.put(fixedVolumeRoute.getId(), fixedVolumeRoute); |
| 100 | mRoutes.put(variableVolumeRoute.getId(), variableVolumeRoute); |
Kyunglyul Hyun | 3aedf02 | 2019-04-15 16:38:19 +0900 | [diff] [blame] | 101 | } |
| 102 | |
| 103 | @Override |
| 104 | public void onCreate() { |
| 105 | initializeRoutes(); |
| 106 | } |
| 107 | |
Kyunglyul Hyun | d51666d | 2019-04-11 04:08:40 +0000 | [diff] [blame] | 108 | @Override |
| 109 | public IBinder onBind(Intent intent) { |
Kyunglyul Hyun | 3aedf02 | 2019-04-15 16:38:19 +0900 | [diff] [blame] | 110 | publishRoutes(); |
Kyunglyul Hyun | d51666d | 2019-04-11 04:08:40 +0000 | [diff] [blame] | 111 | return super.onBind(intent); |
| 112 | } |
| 113 | |
| 114 | @Override |
Kyunglyul Hyun | caae8dc | 2019-04-29 15:45:23 +0900 | [diff] [blame] | 115 | public void onControlRequest(String routeId, Intent request) { |
Kyunglyul Hyun | 3aeabe0 | 2019-12-20 19:42:39 +0900 | [diff] [blame] | 116 | String action = request.getAction(); |
| 117 | if (ACTION_REMOVE_ROUTE.equals(action)) { |
Kyunglyul Hyun | caae8dc | 2019-04-29 15:45:23 +0900 | [diff] [blame] | 118 | 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 Hyun | 5a8dedc | 2019-10-10 14:09:44 +0900 | [diff] [blame] | 127 | @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 Hyun | 24cc66f | 2019-12-19 19:04:20 +0900 | [diff] [blame] | 154 | @Override |
| 155 | public void onCreateSession(String packageName, String routeId, String controlCategory, |
Kyunglyul Hyun | c784724 | 2019-12-30 17:46:04 +0900 | [diff] [blame] | 156 | long requestId) { |
Kyunglyul Hyun | cb8894d | 2019-12-27 14:24:46 +0900 | [diff] [blame] | 157 | MediaRoute2Info route = mRoutes.get(routeId); |
| 158 | if (route == null || TextUtils.equals(ROUTE_ID3_SESSION_CREATION_FAILED, routeId)) { |
Hyundo Moon | 63a0540 | 2019-12-19 20:13:56 +0900 | [diff] [blame] | 159 | // Tell the router that session cannot be created by passing null as sessionInfo. |
Kyunglyul Hyun | e6b6238 | 2019-12-24 11:17:24 +0900 | [diff] [blame] | 160 | notifySessionCreated(/* sessionInfo= */ null, requestId); |
Hyundo Moon | 63a0540 | 2019-12-19 20:13:56 +0900 | [diff] [blame] | 161 | return; |
| 162 | } |
Hyundo Moon | 9be3548 | 2019-12-30 17:33:13 +0900 | [diff] [blame] | 163 | maybeDeselectRoute(routeId); |
Kyunglyul Hyun | cb8894d | 2019-12-27 14:24:46 +0900 | [diff] [blame] | 164 | |
Hyundo Moon | a64ca9e | 2019-12-27 17:41:17 +0900 | [diff] [blame] | 165 | final int sessionId = mNextSessionId; |
| 166 | mNextSessionId++; |
Kyunglyul Hyun | cb8894d | 2019-12-27 14:24:46 +0900 | [diff] [blame] | 167 | |
| 168 | mRoutes.put(routeId, new MediaRoute2Info.Builder(route) |
| 169 | .setClientPackageName(packageName) |
| 170 | .build()); |
| 171 | mRouteSessionMap.put(routeId, sessionId); |
Hyundo Moon | 63a0540 | 2019-12-19 20:13:56 +0900 | [diff] [blame] | 172 | |
Kyunglyul Hyun | 24cc66f | 2019-12-19 19:04:20 +0900 | [diff] [blame] | 173 | RouteSessionInfo sessionInfo = new RouteSessionInfo.Builder( |
Kyunglyul Hyun | cb8894d | 2019-12-27 14:24:46 +0900 | [diff] [blame] | 174 | sessionId, packageName, controlCategory) |
Kyunglyul Hyun | 24cc66f | 2019-12-19 19:04:20 +0900 | [diff] [blame] | 175 | .addSelectedRoute(routeId) |
| 176 | .build(); |
Kyunglyul Hyun | cb8894d | 2019-12-27 14:24:46 +0900 | [diff] [blame] | 177 | notifySessionCreated(sessionInfo, requestId); |
| 178 | publishRoutes(); |
Kyunglyul Hyun | 24cc66f | 2019-12-19 19:04:20 +0900 | [diff] [blame] | 179 | } |
| 180 | |
| 181 | @Override |
Kyunglyul Hyun | cb8894d | 2019-12-27 14:24:46 +0900 | [diff] [blame] | 182 | 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 Hyun | 24cc66f | 2019-12-19 19:04:20 +0900 | [diff] [blame] | 193 | |
| 194 | @Override |
Hyundo Moon | 9be3548 | 2019-12-30 17:33:13 +0900 | [diff] [blame] | 195 | public void onSelectRoute(int sessionId, String routeId) { |
Kyunglyul Hyun | 24cc66f | 2019-12-19 19:04:20 +0900 | [diff] [blame] | 196 | RouteSessionInfo sessionInfo = getSessionInfo(sessionId); |
Kyunglyul Hyun | cb8894d | 2019-12-27 14:24:46 +0900 | [diff] [blame] | 197 | MediaRoute2Info route = mRoutes.get(routeId); |
| 198 | if (route == null || sessionInfo == null) { |
| 199 | return; |
| 200 | } |
Hyundo Moon | 9be3548 | 2019-12-30 17:33:13 +0900 | [diff] [blame] | 201 | maybeDeselectRoute(routeId); |
Kyunglyul Hyun | cb8894d | 2019-12-27 14:24:46 +0900 | [diff] [blame] | 202 | |
| 203 | mRoutes.put(routeId, new MediaRoute2Info.Builder(route) |
| 204 | .setClientPackageName(sessionInfo.getPackageName()) |
| 205 | .build()); |
| 206 | mRouteSessionMap.put(routeId, sessionId); |
| 207 | |
Kyunglyul Hyun | 24cc66f | 2019-12-19 19:04:20 +0900 | [diff] [blame] | 208 | RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo) |
| 209 | .addSelectedRoute(routeId) |
| 210 | .build(); |
Kyunglyul Hyun | e6b6238 | 2019-12-24 11:17:24 +0900 | [diff] [blame] | 211 | updateSessionInfo(newSessionInfo); |
Kyunglyul Hyun | 24cc66f | 2019-12-19 19:04:20 +0900 | [diff] [blame] | 212 | publishRoutes(); |
| 213 | } |
| 214 | |
| 215 | @Override |
Hyundo Moon | 9be3548 | 2019-12-30 17:33:13 +0900 | [diff] [blame] | 216 | public void onDeselectRoute(int sessionId, String routeId) { |
Kyunglyul Hyun | 24cc66f | 2019-12-19 19:04:20 +0900 | [diff] [blame] | 217 | RouteSessionInfo sessionInfo = getSessionInfo(sessionId); |
Kyunglyul Hyun | cb8894d | 2019-12-27 14:24:46 +0900 | [diff] [blame] | 218 | 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 Hyun | 24cc66f | 2019-12-19 19:04:20 +0900 | [diff] [blame] | 228 | RouteSessionInfo newSessionInfo = new RouteSessionInfo.Builder(sessionInfo) |
| 229 | .removeSelectedRoute(routeId) |
| 230 | .build(); |
Kyunglyul Hyun | e6b6238 | 2019-12-24 11:17:24 +0900 | [diff] [blame] | 231 | updateSessionInfo(newSessionInfo); |
Kyunglyul Hyun | 24cc66f | 2019-12-19 19:04:20 +0900 | [diff] [blame] | 232 | 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 Hyun | e6b6238 | 2019-12-24 11:17:24 +0900 | [diff] [blame] | 242 | updateSessionInfo(newSessionInfo); |
Kyunglyul Hyun | 24cc66f | 2019-12-19 19:04:20 +0900 | [diff] [blame] | 243 | publishRoutes(); |
| 244 | } |
| 245 | |
Hyundo Moon | 9be3548 | 2019-12-30 17:33:13 +0900 | [diff] [blame] | 246 | void maybeDeselectRoute(String routeId) { |
Kyunglyul Hyun | cb8894d | 2019-12-27 14:24:46 +0900 | [diff] [blame] | 247 | if (!mRouteSessionMap.containsKey(routeId)) { |
| 248 | return; |
| 249 | } |
| 250 | |
| 251 | int sessionId = mRouteSessionMap.get(routeId); |
Hyundo Moon | 9be3548 | 2019-12-30 17:33:13 +0900 | [diff] [blame] | 252 | onDeselectRoute(sessionId, routeId); |
Kyunglyul Hyun | cb8894d | 2019-12-27 14:24:46 +0900 | [diff] [blame] | 253 | } |
| 254 | |
Kyunglyul Hyun | 3aedf02 | 2019-04-15 16:38:19 +0900 | [diff] [blame] | 255 | void publishRoutes() { |
| 256 | MediaRoute2ProviderInfo info = new MediaRoute2ProviderInfo.Builder() |
| 257 | .addRoutes(mRoutes.values()) |
| 258 | .build(); |
Kyunglyul Hyun | 24cc66f | 2019-12-19 19:04:20 +0900 | [diff] [blame] | 259 | updateProviderInfo(info); |
Kyunglyul Hyun | d51666d | 2019-04-11 04:08:40 +0000 | [diff] [blame] | 260 | } |
| 261 | } |