blob: 4f64177ad1350b9ee060024ce76826f5b5be5570 [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
19import android.annotation.NonNull;
20import android.content.ComponentName;
21import android.content.Context;
22import android.content.Intent;
23import android.media.AudioManager;
24import android.media.AudioRoutesInfo;
25import android.media.IAudioRoutesObserver;
26import android.media.IAudioService;
27import android.media.MediaRoute2Info;
28import android.media.MediaRoute2ProviderInfo;
29import android.os.Handler;
30import android.os.Looper;
31import android.os.RemoteException;
32import android.os.ServiceManager;
33import android.text.TextUtils;
34import android.util.Log;
35
36import com.android.internal.R;
37
38/**
39 * Provides routes for local playbacks such as phone speaker, wired headset, or Bluetooth speakers.
40 */
41class SystemMediaRoute2Provider extends MediaRoute2Provider {
42 private static final String TAG = "MR2SystemProvider";
43 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
44
Hyundo Moon5736a612019-11-19 15:08:32 +090045 static final String DEFAULT_ROUTE_ID = "DEFAULT_ROUTE";
46 static final String BLUETOOTH_ROUTE_ID = "BLUETOOTH_ROUTE";
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +000047
48 // TODO: Move these to a proper place
49 public static final String CATEGORY_LIVE_AUDIO = "android.media.intent.category.LIVE_AUDIO";
50 public static final String CATEGORY_LIVE_VIDEO = "android.media.intent.category.LIVE_VIDEO";
51
52 private final AudioManager mAudioManager;
53 private final IAudioService mAudioService;
54 private final Handler mHandler;
55 private final Context mContext;
56
57 private static ComponentName sComponentName = new ComponentName(
58 SystemMediaRoute2Provider.class.getPackageName$(),
59 SystemMediaRoute2Provider.class.getName());
60
61 //TODO: Clean up these when audio manager support multiple bt devices
62 MediaRoute2Info mDefaultRoute;
63 MediaRoute2Info mBluetoothA2dpRoute;
64 final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();
65
66 final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() {
67 @Override
68 public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) {
69 mHandler.post(new Runnable() {
70 @Override public void run() {
71 updateAudioRoutes(newRoutes);
72 }
73 });
74 }
75 };
76
77 SystemMediaRoute2Provider(Context context, Callback callback) {
78 super(sComponentName);
79 setCallback(callback);
80
81 mContext = context;
82 mHandler = new Handler(Looper.getMainLooper());
83
84 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
85 mAudioService = IAudioService.Stub.asInterface(
86 ServiceManager.getService(Context.AUDIO_SERVICE));
87
88 initializeRoutes();
89 }
90
91 //TODO: implement method
92 @Override
93 public void requestSelectRoute(@NonNull String packageName, @NonNull String routeId, int seq) {
94 try {
95 mAudioService.setBluetoothA2dpOn(
96 !TextUtils.equals(routeId, mDefaultRoute.getId()));
97 } catch (RemoteException ex) {
98 Log.e(TAG, "Error changing Bluetooth A2DP route");
99 }
100 }
101
102 //TODO: implement method
103 @Override
104 public void unselectRoute(@NonNull String packageName, @NonNull String routeId) {
105 // does nothing..?
106 }
107
108 //TODO: implement method
109 @Override
110 public void sendControlRequest(@NonNull MediaRoute2Info route, @NonNull Intent request) {
111 }
112
113 //TODO: implement method
114 @Override
115 public void requestSetVolume(MediaRoute2Info route, int volume) {
116 }
117
118 //TODO: implement method
119 @Override
120 public void requestUpdateVolume(MediaRoute2Info route, int delta) {
121 }
122
123 void initializeRoutes() {
124 //TODO: adds necessary info
125 mDefaultRoute = new MediaRoute2Info.Builder(
126 DEFAULT_ROUTE_ID,
127 mContext.getResources().getText(R.string.default_audio_route_name).toString())
128 .setVolumeHandling(mAudioManager.isVolumeFixed()
129 ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
130 : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
131 .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
132 .setVolume(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC))
133 .addSupportedCategory(CATEGORY_LIVE_AUDIO)
134 .addSupportedCategory(CATEGORY_LIVE_VIDEO)
135 .build();
136
137 AudioRoutesInfo newAudioRoutes = null;
138 try {
139 newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
140 } catch (RemoteException e) {
141 }
142 if (newAudioRoutes != null) {
143 // This will select the active BT route if there is one and the current
144 // selected route is the default system route, or if there is no selected
145 // route yet.
146 updateAudioRoutes(newAudioRoutes);
147 }
148
149 publishRoutes();
150 }
151
152 void updateAudioRoutes(AudioRoutesInfo newRoutes) {
153 int name = R.string.default_audio_route_name;
154 mCurAudioRoutesInfo.mainType = newRoutes.mainType;
155 if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0
156 || (newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) {
157 name = com.android.internal.R.string.default_audio_route_name_headphones;
158 } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) {
159 name = com.android.internal.R.string.default_audio_route_name_dock_speakers;
160 } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HDMI) != 0) {
161 name = com.android.internal.R.string.default_audio_route_name_hdmi;
162 } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_USB) != 0) {
163 name = com.android.internal.R.string.default_audio_route_name_usb;
164 }
165
166 mDefaultRoute = new MediaRoute2Info.Builder(
167 DEFAULT_ROUTE_ID, mContext.getResources().getText(name).toString())
168 .setVolumeHandling(mAudioManager.isVolumeFixed()
169 ? MediaRoute2Info.PLAYBACK_VOLUME_FIXED
170 : MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
171 .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
172 .setVolume(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC))
173 .addSupportedCategory(CATEGORY_LIVE_AUDIO)
174 .addSupportedCategory(CATEGORY_LIVE_VIDEO)
175 .build();
176
177 if (!TextUtils.equals(newRoutes.bluetoothName, mCurAudioRoutesInfo.bluetoothName)) {
178 mCurAudioRoutesInfo.bluetoothName = newRoutes.bluetoothName;
179 if (mCurAudioRoutesInfo.bluetoothName != null) {
180 //TODO: mark as bluetooth once MediaRoute2Info has device type
181 mBluetoothA2dpRoute = new MediaRoute2Info.Builder(BLUETOOTH_ROUTE_ID,
Kyunglyul Hyund3e53c52019-11-20 12:01:02 +0900182 mCurAudioRoutesInfo.bluetoothName)
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000183 .setDescription(mContext.getResources().getText(
184 R.string.bluetooth_a2dp_audio_route_name).toString())
185 .addSupportedCategory(CATEGORY_LIVE_AUDIO)
186 .build();
Kyunglyul Hyund3e53c52019-11-20 12:01:02 +0900187 } else {
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000188 mBluetoothA2dpRoute = null;
189 }
190 }
191
192 publishRoutes();
193 }
Hyundo Moon5736a612019-11-19 15:08:32 +0900194
195 /**
196 * The first route should be the currently selected system route.
197 * For example, if there are two system routes (BT and device speaker),
198 * BT will be the first route in the list.
199 *
200 * TODO: Support multiple BT devices
201 */
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000202 void publishRoutes() {
Hyundo Moon5736a612019-11-19 15:08:32 +0900203 MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder();
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000204 if (mBluetoothA2dpRoute != null) {
205 builder.addRoute(mBluetoothA2dpRoute);
206 }
Hyundo Moon5736a612019-11-19 15:08:32 +0900207 builder.addRoute(mDefaultRoute);
Kyunglyul Hyun0332e9a22019-11-20 01:39:25 +0000208 setAndNotifyProviderInfo(builder.build());
209 }
210}