blob: 5b16d686e04c9a1c6312630c78f6b102ecb89cd9 [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";
Sungsoo Limddf140d2020-03-25 08:53:55 +090057 static final String DEVICE_ROUTE_ID = "DEVICE_ROUTE";
Hyundo Moon67c41fd2020-01-17 14:22:42 +090058 static final String SYSTEM_SESSION_ID = "SYSTEM_SESSION";
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +000059
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +000060 private final AudioManager mAudioManager;
61 private final IAudioService mAudioService;
62 private final Handler mHandler;
63 private final Context mContext;
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +090064 private final BluetoothRouteProvider mBtRouteProvider;
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +000065
66 private static ComponentName sComponentName = new ComponentName(
Kyunglyul Hyund9bb4d72020-02-18 15:05:26 +090067 SystemMediaRoute2Provider.class.getPackage().getName(),
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +000068 SystemMediaRoute2Provider.class.getName());
69
Kyunglyul Hyun5161b372020-02-05 18:45:35 +090070 private String mSelectedRouteId;
Sungsoo Limddf140d2020-03-25 08:53:55 +090071 // For apps without MODIFYING_AUDIO_ROUTING permission.
72 // This should be the currently selected route.
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +000073 MediaRoute2Info mDefaultRoute;
Sungsoo Limddf140d2020-03-25 08:53:55 +090074 MediaRoute2Info mDeviceRoute;
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +000075 final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();
76
77 final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() {
78 @Override
79 public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) {
Sungsoo Limea1eaf72020-02-12 11:00:06 +090080 mHandler.post(() -> {
Sungsoo Limddf140d2020-03-25 08:53:55 +090081 updateDeviceRoute(newRoutes);
Sungsoo Limea1eaf72020-02-12 11:00:06 +090082 notifyProviderState();
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +000083 });
84 }
85 };
86
87 SystemMediaRoute2Provider(Context context, Callback callback) {
88 super(sComponentName);
89 setCallback(callback);
90
Kyunglyul Hyun96d36ce2020-01-29 19:59:33 +090091 mIsSystemRouteProvider = true;
92
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +000093 mContext = context;
94 mHandler = new Handler(Looper.getMainLooper());
95
96 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
97 mAudioService = IAudioService.Stub.asInterface(
98 ServiceManager.getService(Context.AUDIO_SERVICE));
Sungsoo Limea1eaf72020-02-12 11:00:06 +090099 AudioRoutesInfo newAudioRoutes = null;
100 try {
101 newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
102 } catch (RemoteException e) {
103 }
Sungsoo Limddf140d2020-03-25 08:53:55 +0900104 updateDeviceRoute(newAudioRoutes);
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000105
Roman Kiryanovfe87df92020-03-27 17:40:27 -0700106 // .getInstance returns null if there is no bt adapter available
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900107 mBtRouteProvider = BluetoothRouteProvider.getInstance(context, (routes) -> {
Sungsoo Limea1eaf72020-02-12 11:00:06 +0900108 publishProviderState();
Hyundo Moon67c41fd2020-01-17 14:22:42 +0900109
110 boolean sessionInfoChanged;
Sungsoo Lim613a77a2020-03-16 15:44:51 +0900111 sessionInfoChanged = updateSessionInfosIfNeeded();
Hyundo Moon67c41fd2020-01-17 14:22:42 +0900112 if (sessionInfoChanged) {
113 notifySessionInfoUpdated();
114 }
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900115 });
Sungsoo Lim613a77a2020-03-16 15:44:51 +0900116 updateSessionInfosIfNeeded();
Hyundo Moon717ef722020-02-06 18:44:13 +0900117 mContext.registerReceiver(new VolumeChangeReceiver(),
118 new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
Sungsoo Lim613a77a2020-03-16 15:44:51 +0900119
Roman Kiryanovfe87df92020-03-27 17:40:27 -0700120 if (mBtRouteProvider != null) {
121 mHandler.post(() -> {
122 mBtRouteProvider.start();
123 notifyProviderState();
124 });
125 }
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000126 }
127
Hyundo Moon63a05402019-12-19 20:13:56 +0900128 @Override
Hyundo Moonf8e49f4b2020-03-06 17:19:42 +0900129 public void requestCreateSession(long requestId, String packageName, String routeId,
Hyundo Moon84e027d2020-01-16 17:39:05 +0900130 Bundle sessionHints) {
Kyunglyul Hyun923ef0d2020-03-13 20:55:11 +0900131
Hyundo Moonf8e49f4b2020-03-06 17:19:42 +0900132 transferToRoute(requestId, SYSTEM_SESSION_ID, routeId);
Kyunglyul Hyun923ef0d2020-03-13 20:55:11 +0900133 mCallback.onSessionCreated(this, requestId, mSessionInfos.get(0));
134 //TODO: We should call after the session info is changed.
Hyundo Moon63a05402019-12-19 20:13:56 +0900135 }
136
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000137 @Override
Hyundo Moonf8e49f4b2020-03-06 17:19:42 +0900138 public void releaseSession(long requestId, String sessionId) {
Kyunglyul Hyuncb8894d2019-12-27 14:24:46 +0900139 // Do nothing
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000140 }
Hyundo Moon0fa60e82020-02-14 11:44:45 +0900141
Kyunglyul Hyun1866d8a2020-01-31 11:56:34 +0900142 @Override
143 public void updateDiscoveryPreference(RouteDiscoveryPreference discoveryPreference) {
144 // Do nothing
145 }
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000146
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000147 @Override
Hyundo Moonf8e49f4b2020-03-06 17:19:42 +0900148 public void selectRoute(long requestId, String sessionId, String routeId) {
Kyunglyul Hyun581fc982020-01-21 16:30:28 +0900149 // Do nothing since we don't support multiple BT yet.
Kyunglyul Hyuncb8894d2019-12-27 14:24:46 +0900150 }
151
152 @Override
Hyundo Moonf8e49f4b2020-03-06 17:19:42 +0900153 public void deselectRoute(long requestId, String sessionId, String routeId) {
Kyunglyul Hyun581fc982020-01-21 16:30:28 +0900154 // Do nothing since we don't support multiple BT yet.
Kyunglyul Hyuncb8894d2019-12-27 14:24:46 +0900155 }
156
157 @Override
Hyundo Moonf8e49f4b2020-03-06 17:19:42 +0900158 public void transferToRoute(long requestId, String sessionId, String routeId) {
Roman Kiryanovfe87df92020-03-27 17:40:27 -0700159 if (mBtRouteProvider != null) {
160 if (TextUtils.equals(routeId, mDeviceRoute.getId())) {
161 mBtRouteProvider.transferTo(null);
162 } else {
163 mBtRouteProvider.transferTo(routeId);
164 }
Kyunglyul Hyun581fc982020-01-21 16:30:28 +0900165 }
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000166 }
167
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000168 @Override
Hyundo Moonf8e49f4b2020-03-06 17:19:42 +0900169 public void setRouteVolume(long requestId, String routeId, int volume) {
Kyunglyul Hyun5161b372020-02-05 18:45:35 +0900170 if (!TextUtils.equals(routeId, mSelectedRouteId)) {
171 return;
172 }
173 mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, volume, 0);
174 }
175
176 @Override
Hyundo Moonf8e49f4b2020-03-06 17:19:42 +0900177 public void setSessionVolume(long requestId, String sessionId, int volume) {
Kyunglyul Hyun5161b372020-02-05 18:45:35 +0900178 // Do nothing since we don't support grouping volume yet.
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000179 }
180
Sungsoo Limddf140d2020-03-25 08:53:55 +0900181 public MediaRoute2Info getDefaultRoute() {
182 return mDefaultRoute;
183 }
184
185 private void updateDeviceRoute(AudioRoutesInfo newRoutes) {
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000186 int name = R.string.default_audio_route_name;
Sungsoo Limea1eaf72020-02-12 11:00:06 +0900187 if (newRoutes != null) {
188 mCurAudioRoutesInfo.mainType = newRoutes.mainType;
189 if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0
190 || (newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) {
191 name = com.android.internal.R.string.default_audio_route_name_headphones;
192 } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) {
193 name = com.android.internal.R.string.default_audio_route_name_dock_speakers;
194 } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HDMI) != 0) {
195 name = com.android.internal.R.string.default_audio_route_name_hdmi;
196 } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_USB) != 0) {
197 name = com.android.internal.R.string.default_audio_route_name_usb;
198 }
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000199 }
Sungsoo Limddf140d2020-03-25 08:53:55 +0900200 mDeviceRoute = new MediaRoute2Info.Builder(
201 DEVICE_ROUTE_ID, mContext.getResources().getText(name).toString())
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000202 .setVolumeHandling(mAudioManager.isVolumeFixed()
203 ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
204 : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
205 .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
206 .setVolume(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC))
Kyunglyul Hyun8828c892020-02-17 20:49:58 +0900207 //TODO: Guess the exact type using AudioDevice
208 .setType(TYPE_BUILTIN_SPEAKER)
Hyundo Moon42bef142020-01-14 14:16:30 +0900209 .addFeature(FEATURE_LIVE_AUDIO)
210 .addFeature(FEATURE_LIVE_VIDEO)
Sungsoo Limea1eaf72020-02-12 11:00:06 +0900211 .setConnectionState(MediaRoute2Info.CONNECTION_STATE_CONNECTED)
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000212 .build();
Sungsoo Limea1eaf72020-02-12 11:00:06 +0900213 updateProviderState();
214 }
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000215
Sungsoo Limea1eaf72020-02-12 11:00:06 +0900216 private void updateProviderState() {
217 MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder();
Sungsoo Limddf140d2020-03-25 08:53:55 +0900218 builder.addRoute(mDeviceRoute);
Sungsoo Limea1eaf72020-02-12 11:00:06 +0900219 if (mBtRouteProvider != null) {
220 for (MediaRoute2Info route : mBtRouteProvider.getAllBluetoothRoutes()) {
221 builder.addRoute(route);
222 }
223 }
224 setProviderState(builder.build());
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000225 }
Hyundo Moon5736a612019-11-19 15:08:32 +0900226
227 /**
Hyundo Moon67c41fd2020-01-17 14:22:42 +0900228 * Updates the mSessionInfo. Returns true if the session info is changed.
229 */
Sungsoo Lim613a77a2020-03-16 15:44:51 +0900230 boolean updateSessionInfosIfNeeded() {
231 synchronized (mLock) {
232 // Prevent to execute this method before mBtRouteProvider is created.
233 if (mBtRouteProvider == null) return false;
234 RoutingSessionInfo oldSessionInfo = mSessionInfos.isEmpty() ? null : mSessionInfos.get(
235 0);
Hyundo Moon67c41fd2020-01-17 14:22:42 +0900236
Sungsoo Lim613a77a2020-03-16 15:44:51 +0900237 RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
238 SYSTEM_SESSION_ID, "" /* clientPackageName */)
239 .setSystemSession(true);
Hyundo Moon67c41fd2020-01-17 14:22:42 +0900240
Sungsoo Lim613a77a2020-03-16 15:44:51 +0900241 MediaRoute2Info selectedRoute = mBtRouteProvider.getSelectedRoute();
242 if (selectedRoute == null) {
Sungsoo Limddf140d2020-03-25 08:53:55 +0900243 selectedRoute = mDeviceRoute;
Sungsoo Lim613a77a2020-03-16 15:44:51 +0900244 } else {
Sungsoo Limddf140d2020-03-25 08:53:55 +0900245 builder.addTransferableRoute(mDeviceRoute.getId());
Sungsoo Lim613a77a2020-03-16 15:44:51 +0900246 }
247 mSelectedRouteId = selectedRoute.getId();
Sungsoo Limddf140d2020-03-25 08:53:55 +0900248 mDefaultRoute = new MediaRoute2Info.Builder(DEFAULT_ROUTE_ID, selectedRoute).build();
Sungsoo Lim613a77a2020-03-16 15:44:51 +0900249 builder.addSelectedRoute(mSelectedRouteId);
Hyundo Moon67c41fd2020-01-17 14:22:42 +0900250
Sungsoo Lim613a77a2020-03-16 15:44:51 +0900251 for (MediaRoute2Info route : mBtRouteProvider.getTransferableRoutes()) {
252 builder.addTransferableRoute(route.getId());
253 }
Kyunglyul Hyun581fc982020-01-21 16:30:28 +0900254
Sungsoo Lim613a77a2020-03-16 15:44:51 +0900255 RoutingSessionInfo newSessionInfo = builder.setProviderId(mUniqueId).build();
256 if (Objects.equals(oldSessionInfo, newSessionInfo)) {
257 return false;
258 } else {
259 mSessionInfos.clear();
260 mSessionInfos.add(newSessionInfo);
261 return true;
262 }
Hyundo Moon67c41fd2020-01-17 14:22:42 +0900263 }
264 }
265
Sungsoo Limea1eaf72020-02-12 11:00:06 +0900266 void publishProviderState() {
267 updateProviderState();
268 notifyProviderState();
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000269 }
Hyundo Moon67c41fd2020-01-17 14:22:42 +0900270
271 void notifySessionInfoUpdated() {
272 RoutingSessionInfo sessionInfo;
273 synchronized (mLock) {
274 sessionInfo = mSessionInfos.get(0);
275 }
Kyunglyul Hyun923ef0d2020-03-13 20:55:11 +0900276
Hyundo Moon67c41fd2020-01-17 14:22:42 +0900277 mCallback.onSessionUpdated(this, sessionInfo);
278 }
Hyundo Moon717ef722020-02-06 18:44:13 +0900279
280 private class VolumeChangeReceiver extends BroadcastReceiver {
281 // This will be called in the main thread.
282 @Override
283 public void onReceive(Context context, Intent intent) {
284 if (!intent.getAction().equals(AudioManager.VOLUME_CHANGED_ACTION)) {
285 return;
286 }
287
288 final int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
289 if (streamType != AudioManager.STREAM_MUSIC) {
290 return;
291 }
292
293 final int newVolume = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
294 final int oldVolume = intent.getIntExtra(
295 AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 0);
296
297 if (newVolume != oldVolume) {
Sungsoo Limddf140d2020-03-25 08:53:55 +0900298 if (TextUtils.equals(mDeviceRoute.getId(), mSelectedRouteId)) {
299 mDeviceRoute = new MediaRoute2Info.Builder(mDeviceRoute)
Hyundo Moon717ef722020-02-06 18:44:13 +0900300 .setVolume(newVolume)
301 .build();
Roman Kiryanovfe87df92020-03-27 17:40:27 -0700302 } else if (mBtRouteProvider != null) {
Sungsoo Limea1eaf72020-02-12 11:00:06 +0900303 mBtRouteProvider.setSelectedRouteVolume(newVolume);
Hyundo Moon717ef722020-02-06 18:44:13 +0900304 }
Sungsoo Limea1eaf72020-02-12 11:00:06 +0900305 publishProviderState();
Hyundo Moon717ef722020-02-06 18:44:13 +0900306 }
307 }
308 }
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000309}