blob: c7d14e0afe7b0229e1452453f9a0ad2ca0726276 [file] [log] [blame]
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +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.server.media;
18
Hyundo Moon42bef142020-01-14 14:16:30 +090019import static android.media.MediaRoute2Info.FEATURE_LIVE_AUDIO;
20import static android.media.MediaRoute2Info.FEATURE_LIVE_VIDEO;
Kyunglyul Hyun8828c892020-02-17 20:49:58 +090021import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
Hyundo Moon42bef142020-01-14 14:16:30 +090022
Hyundo Moon717ef722020-02-06 18:44:13 +090023import android.content.BroadcastReceiver;
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +000024import android.content.ComponentName;
25import android.content.Context;
26import android.content.Intent;
Hyundo Moon717ef722020-02-06 18:44:13 +090027import android.content.IntentFilter;
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +000028import android.media.AudioManager;
29import android.media.AudioRoutesInfo;
30import android.media.IAudioRoutesObserver;
31import android.media.IAudioService;
32import android.media.MediaRoute2Info;
33import android.media.MediaRoute2ProviderInfo;
Kyunglyul Hyun1866d8a2020-01-31 11:56:34 +090034import android.media.RouteDiscoveryPreference;
Hyundo Moon67c41fd2020-01-17 14:22:42 +090035import android.media.RoutingSessionInfo;
Hyundo Moon84e027d2020-01-16 17:39:05 +090036import android.os.Bundle;
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +000037import android.os.Handler;
38import android.os.Looper;
39import android.os.RemoteException;
40import android.os.ServiceManager;
Hyundo Moon67c41fd2020-01-17 14:22:42 +090041import android.text.TextUtils;
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +000042import android.util.Log;
43
44import com.android.internal.R;
45
Hyundo Moon67c41fd2020-01-17 14:22:42 +090046import java.util.Objects;
Kyunglyul Hyunefe43742019-12-31 18:32:28 +090047
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +000048/**
49 * Provides routes for local playbacks such as phone speaker, wired headset, or Bluetooth speakers.
50 */
Sungsoo Limea1eaf72020-02-12 11:00:06 +090051// TODO: check thread safety. We may need to use lock to protect variables.
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +000052class SystemMediaRoute2Provider extends MediaRoute2Provider {
53 private static final String TAG = "MR2SystemProvider";
54 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
55
Hyundo Moon5736a612019-11-19 15:08:32 +090056 static final String DEFAULT_ROUTE_ID = "DEFAULT_ROUTE";
Hyundo Moon67c41fd2020-01-17 14:22:42 +090057 static final String SYSTEM_SESSION_ID = "SYSTEM_SESSION";
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +000058
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +000059 private final AudioManager mAudioManager;
60 private final IAudioService mAudioService;
61 private final Handler mHandler;
62 private final Context mContext;
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +090063 private final BluetoothRouteProvider mBtRouteProvider;
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +000064
65 private static ComponentName sComponentName = new ComponentName(
Kyunglyul Hyund9bb4d72020-02-18 15:05:26 +090066 SystemMediaRoute2Provider.class.getPackage().getName(),
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +000067 SystemMediaRoute2Provider.class.getName());
68
Kyunglyul Hyun5161b372020-02-05 18:45:35 +090069 private String mSelectedRouteId;
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +000070 MediaRoute2Info mDefaultRoute;
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +000071 final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();
72
73 final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() {
74 @Override
75 public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) {
Sungsoo Limea1eaf72020-02-12 11:00:06 +090076 mHandler.post(() -> {
77 updateDefaultRoute(newRoutes);
78 notifyProviderState();
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +000079 });
80 }
81 };
82
83 SystemMediaRoute2Provider(Context context, Callback callback) {
84 super(sComponentName);
85 setCallback(callback);
86
Kyunglyul Hyun96d36ce2020-01-29 19:59:33 +090087 mIsSystemRouteProvider = true;
88
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +000089 mContext = context;
90 mHandler = new Handler(Looper.getMainLooper());
91
92 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
93 mAudioService = IAudioService.Stub.asInterface(
94 ServiceManager.getService(Context.AUDIO_SERVICE));
Sungsoo Limea1eaf72020-02-12 11:00:06 +090095 AudioRoutesInfo newAudioRoutes = null;
96 try {
97 newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
98 } catch (RemoteException e) {
99 }
100 updateDefaultRoute(newAudioRoutes);
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000101
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900102 mBtRouteProvider = BluetoothRouteProvider.getInstance(context, (routes) -> {
Sungsoo Limea1eaf72020-02-12 11:00:06 +0900103 publishProviderState();
Hyundo Moon67c41fd2020-01-17 14:22:42 +0900104
105 boolean sessionInfoChanged;
Sungsoo Lim613a77a2020-03-16 15:44:51 +0900106 sessionInfoChanged = updateSessionInfosIfNeeded();
Hyundo Moon67c41fd2020-01-17 14:22:42 +0900107 if (sessionInfoChanged) {
108 notifySessionInfoUpdated();
109 }
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900110 });
Sungsoo Lim613a77a2020-03-16 15:44:51 +0900111 updateSessionInfosIfNeeded();
Hyundo Moon717ef722020-02-06 18:44:13 +0900112
113 mContext.registerReceiver(new VolumeChangeReceiver(),
114 new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
Sungsoo Lim613a77a2020-03-16 15:44:51 +0900115
116 mHandler.post(() -> {
117 mBtRouteProvider.start();
118 notifyProviderState();
119 });
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000120 }
121
Hyundo Moon63a05402019-12-19 20:13:56 +0900122 @Override
Hyundo Moonf8e49f4b2020-03-06 17:19:42 +0900123 public void requestCreateSession(long requestId, String packageName, String routeId,
Hyundo Moon84e027d2020-01-16 17:39:05 +0900124 Bundle sessionHints) {
Kyunglyul Hyun923ef0d2020-03-13 20:55:11 +0900125
Hyundo Moonf8e49f4b2020-03-06 17:19:42 +0900126 transferToRoute(requestId, SYSTEM_SESSION_ID, routeId);
Kyunglyul Hyun923ef0d2020-03-13 20:55:11 +0900127 mCallback.onSessionCreated(this, requestId, mSessionInfos.get(0));
128 //TODO: We should call after the session info is changed.
Hyundo Moon63a05402019-12-19 20:13:56 +0900129 }
130
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000131 @Override
Hyundo Moonf8e49f4b2020-03-06 17:19:42 +0900132 public void releaseSession(long requestId, String sessionId) {
Kyunglyul Hyuncb8894d2019-12-27 14:24:46 +0900133 // Do nothing
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000134 }
Hyundo Moon0fa60e82020-02-14 11:44:45 +0900135
Kyunglyul Hyun1866d8a2020-01-31 11:56:34 +0900136 @Override
137 public void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference) {
138 // Do nothing
139 }
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000140
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000141 @Override
Hyundo Moonf8e49f4b2020-03-06 17:19:42 +0900142 public void selectRoute(long requestId, String sessionId, String routeId) {
Kyunglyul Hyun581fc982020-01-21 16:30:28 +0900143 // Do nothing since we don't support multiple BT yet.
Kyunglyul Hyuncb8894d2019-12-27 14:24:46 +0900144 }
145
146 @Override
Hyundo Moonf8e49f4b2020-03-06 17:19:42 +0900147 public void deselectRoute(long requestId, String sessionId, String routeId) {
Kyunglyul Hyun581fc982020-01-21 16:30:28 +0900148 // Do nothing since we don't support multiple BT yet.
Kyunglyul Hyuncb8894d2019-12-27 14:24:46 +0900149 }
150
151 @Override
Hyundo Moonf8e49f4b2020-03-06 17:19:42 +0900152 public void transferToRoute(long requestId, String sessionId, String routeId) {
Kyunglyul Hyun581fc982020-01-21 16:30:28 +0900153 if (TextUtils.equals(routeId, mDefaultRoute.getId())) {
Sungsoo Limcfa7abc2020-02-12 09:32:05 +0900154 mBtRouteProvider.transferTo(null);
Kyunglyul Hyun581fc982020-01-21 16:30:28 +0900155 } else {
Sungsoo Limcfa7abc2020-02-12 09:32:05 +0900156 mBtRouteProvider.transferTo(routeId);
Kyunglyul Hyun581fc982020-01-21 16:30:28 +0900157 }
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000158 }
159
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000160 @Override
Hyundo Moonf8e49f4b2020-03-06 17:19:42 +0900161 public void setRouteVolume(long requestId, String routeId, int volume) {
Kyunglyul Hyun5161b372020-02-05 18:45:35 +0900162 if (!TextUtils.equals(routeId, mSelectedRouteId)) {
163 return;
164 }
165 mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
166 }
167
168 @Override
Hyundo Moonf8e49f4b2020-03-06 17:19:42 +0900169 public void setSessionVolume(long requestId, String sessionId, int volume) {
Kyunglyul Hyun5161b372020-02-05 18:45:35 +0900170 // Do nothing since we don't support grouping volume yet.
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000171 }
172
Sungsoo Limea1eaf72020-02-12 11:00:06 +0900173 private void updateDefaultRoute(AudioRoutesInfo newRoutes) {
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000174 int name = R.string.default_audio_route_name;
Sungsoo Limea1eaf72020-02-12 11:00:06 +0900175 if (newRoutes != null) {
176 mCurAudioRoutesInfo.mainType = newRoutes.mainType;
177 if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0
178 || (newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) {
179 name = com.android.internal.R.string.default_audio_route_name_headphones;
180 } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) {
181 name = com.android.internal.R.string.default_audio_route_name_dock_speakers;
182 } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HDMI) != 0) {
183 name = com.android.internal.R.string.default_audio_route_name_hdmi;
184 } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_USB) != 0) {
185 name = com.android.internal.R.string.default_audio_route_name_usb;
186 }
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000187 }
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000188 mDefaultRoute = new MediaRoute2Info.Builder(
189 DEFAULT_ROUTE_ID, mContext.getResources().getText(name).toString())
190 .setVolumeHandling(mAudioManager.isVolumeFixed()
191 ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
192 : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
193 .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
194 .setVolume(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC))
Kyunglyul Hyun8828c892020-02-17 20:49:58 +0900195 //TODO: Guess the exact type using AudioDevice
196 .setType(TYPE_BUILTIN_SPEAKER)
Hyundo Moon42bef142020-01-14 14:16:30 +0900197 .addFeature(FEATURE_LIVE_AUDIO)
198 .addFeature(FEATURE_LIVE_VIDEO)
Sungsoo Limea1eaf72020-02-12 11:00:06 +0900199 .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED)
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000200 .build();
Sungsoo Limea1eaf72020-02-12 11:00:06 +0900201 updateProviderState();
202 }
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000203
Sungsoo Limea1eaf72020-02-12 11:00:06 +0900204 private void updateProviderState() {
205 MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder();
206 builder.addRoute(mDefaultRoute);
207 if (mBtRouteProvider != null) {
208 for (MediaRoute2Info route : mBtRouteProvider.getAllBluetoothRoutes()) {
209 builder.addRoute(route);
210 }
211 }
212 setProviderState(builder.build());
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000213 }
Hyundo Moon5736a612019-11-19 15:08:32 +0900214
215 /**
Hyundo Moon67c41fd2020-01-17 14:22:42 +0900216 * Updates the mSessionInfo. Returns true if the session info is changed.
217 */
Sungsoo Lim613a77a2020-03-16 15:44:51 +0900218 boolean updateSessionInfosIfNeeded() {
219 synchronized (mLock) {
220 // Prevent to execute this method before mBtRouteProvider is created.
221 if (mBtRouteProvider == null) return false;
222 RoutingSessionInfo oldSessionInfo = mSessionInfos.isEmpty() ? null : mSessionInfos.get(
223 0);
Hyundo Moon67c41fd2020-01-17 14:22:42 +0900224
Sungsoo Lim613a77a2020-03-16 15:44:51 +0900225 RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
226 SYSTEM_SESSION_ID, "" /* clientPackageName */)
227 .setSystemSession(true);
Hyundo Moon67c41fd2020-01-17 14:22:42 +0900228
Sungsoo Lim613a77a2020-03-16 15:44:51 +0900229 MediaRoute2Info selectedRoute = mBtRouteProvider.getSelectedRoute();
230 if (selectedRoute == null) {
231 selectedRoute = mDefaultRoute;
232 } else {
233 builder.addTransferableRoute(mDefaultRoute.getId());
234 }
235 mSelectedRouteId = selectedRoute.getId();
236 builder.addSelectedRoute(mSelectedRouteId);
Hyundo Moon67c41fd2020-01-17 14:22:42 +0900237
Sungsoo Lim613a77a2020-03-16 15:44:51 +0900238 for (MediaRoute2Info route : mBtRouteProvider.getTransferableRoutes()) {
239 builder.addTransferableRoute(route.getId());
240 }
Kyunglyul Hyun581fc982020-01-21 16:30:28 +0900241
Sungsoo Lim613a77a2020-03-16 15:44:51 +0900242 RoutingSessionInfo newSessionInfo = builder.setProviderId(mUniqueId).build();
243 if (Objects.equals(oldSessionInfo, newSessionInfo)) {
244 return false;
245 } else {
246 mSessionInfos.clear();
247 mSessionInfos.add(newSessionInfo);
248 return true;
249 }
Hyundo Moon67c41fd2020-01-17 14:22:42 +0900250 }
251 }
252
Sungsoo Limea1eaf72020-02-12 11:00:06 +0900253 void publishProviderState() {
254 updateProviderState();
255 notifyProviderState();
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000256 }
Hyundo Moon67c41fd2020-01-17 14:22:42 +0900257
258 void notifySessionInfoUpdated() {
259 RoutingSessionInfo sessionInfo;
260 synchronized (mLock) {
261 sessionInfo = mSessionInfos.get(0);
262 }
Kyunglyul Hyun923ef0d2020-03-13 20:55:11 +0900263
Hyundo Moon67c41fd2020-01-17 14:22:42 +0900264 mCallback.onSessionUpdated(this, sessionInfo);
265 }
Hyundo Moon717ef722020-02-06 18:44:13 +0900266
267 private class VolumeChangeReceiver extends BroadcastReceiver {
268 // This will be called in the main thread.
269 @Override
270 public void onReceive(Context context, Intent intent) {
271 if (!intent.getAction().equals(AudioManager.VOLUME_CHANGED_ACTION)) {
272 return;
273 }
274
275 final int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
276 if (streamType != AudioManager.STREAM_MUSIC) {
277 return;
278 }
279
280 final int newVolume = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
281 final int oldVolume = intent.getIntExtra(
282 AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 0);
283
284 if (newVolume != oldVolume) {
Sungsoo Limea1eaf72020-02-12 11:00:06 +0900285 if (TextUtils.equals(mDefaultRoute.getId(), mSelectedRouteId)) {
Hyundo Moon717ef722020-02-06 18:44:13 +0900286 mDefaultRoute = new MediaRoute2Info.Builder(mDefaultRoute)
287 .setVolume(newVolume)
288 .build();
Sungsoo Limea1eaf72020-02-12 11:00:06 +0900289 } else {
290 mBtRouteProvider.setSelectedRouteVolume(newVolume);
Hyundo Moon717ef722020-02-06 18:44:13 +0900291 }
Sungsoo Limea1eaf72020-02-12 11:00:06 +0900292 publishProviderState();
Hyundo Moon717ef722020-02-06 18:44:13 +0900293 }
294 }
295 }
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000296}