1 /* 2 * Copyright (C) 2023 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.server.media; 18 19 import android.Manifest; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.RequiresPermission; 23 import android.bluetooth.BluetoothAdapter; 24 import android.bluetooth.BluetoothManager; 25 import android.content.Context; 26 import android.media.AudioManager; 27 import android.media.IAudioService; 28 import android.media.MediaRoute2Info; 29 import android.media.audiopolicy.AudioProductStrategy; 30 import android.os.Looper; 31 import android.os.ServiceManager; 32 import android.os.UserHandle; 33 34 import com.android.media.flags.Flags; 35 36 import java.util.List; 37 38 /** 39 * Controls device routes. 40 * 41 * <p>A device route is a system wired route, for example, built-in speaker, wired 42 * headsets and headphones, dock, hdmi, or usb devices. 43 * 44 * @see SystemMediaRoute2Provider 45 */ 46 /* package */ interface DeviceRouteController { 47 48 /** Returns a new instance of {@link DeviceRouteController}. */ 49 @RequiresPermission(Manifest.permission.MODIFY_AUDIO_ROUTING) createInstance( @onNull Context context, @NonNull Looper looper, @NonNull OnDeviceRouteChangedListener onDeviceRouteChangedListener)50 /* package */ static DeviceRouteController createInstance( 51 @NonNull Context context, 52 @NonNull Looper looper, 53 @NonNull OnDeviceRouteChangedListener onDeviceRouteChangedListener) { 54 AudioManager audioManager = context.getSystemService(AudioManager.class); 55 AudioProductStrategy strategyForMedia = AudioRoutingUtils.getMediaAudioProductStrategy(); 56 57 BluetoothManager bluetoothManager = context.getSystemService(BluetoothManager.class); 58 BluetoothAdapter btAdapter = 59 bluetoothManager != null ? bluetoothManager.getAdapter() : null; 60 61 // TODO: b/305199571 - Make the audio policies implementation work without the need for a 62 // bluetooth adapter or a strategy for media. If no strategy for media is available we can 63 // disallow media router transfers, and without a bluetooth adapter we can remove support 64 // for transfers to inactive bluetooth routes. 65 if (strategyForMedia != null 66 && btAdapter != null 67 && Flags.enableAudioPoliciesDeviceAndBluetoothController()) { 68 return new AudioManagerRouteController( 69 context, 70 audioManager, 71 looper, 72 strategyForMedia, 73 btAdapter, 74 onDeviceRouteChangedListener); 75 } else { 76 IAudioService audioService = 77 IAudioService.Stub.asInterface( 78 ServiceManager.getService(Context.AUDIO_SERVICE)); 79 return new LegacyDeviceRouteController( 80 context, audioManager, audioService, onDeviceRouteChangedListener); 81 } 82 } 83 84 /** Returns device route availability status. */ 85 @MediaRoute2Info.SuitabilityStatus getBuiltInSpeakerSuitabilityStatus(@onNull Context context)86 static int getBuiltInSpeakerSuitabilityStatus(@NonNull Context context) { 87 if (!Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) { 88 // Route is always suitable if the flag is disabled. 89 return MediaRoute2Info.SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER; 90 } 91 92 int availabilityStatus = 93 context.getResources() 94 .getInteger( 95 com.android.internal.R.integer 96 .config_mediaRouter_builtInSpeakerSuitability); 97 98 switch (availabilityStatus) { 99 case MediaRoute2Info.SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER: 100 case MediaRoute2Info.SUITABILITY_STATUS_SUITABLE_FOR_MANUAL_TRANSFER: 101 case MediaRoute2Info.SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER: 102 return availabilityStatus; 103 default: 104 return MediaRoute2Info.SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER; 105 } 106 } 107 108 /** Returns the currently selected device (built-in or wired) route. */ 109 @NonNull getSelectedRoute()110 MediaRoute2Info getSelectedRoute(); 111 112 /** 113 * Returns all available routes. 114 * 115 * <p>Note that this method returns available routes including the selected route because (a) 116 * this interface doesn't guarantee that the internal state of the controller won't change 117 * between calls to {@link #getSelectedRoute()} and this method and (b) {@link 118 * #getSelectedRoute()} may be treated as a transferable route (not a selected route) if the 119 * selected route is from {@link BluetoothRouteController}. 120 */ getAvailableRoutes()121 List<MediaRoute2Info> getAvailableRoutes(); 122 123 /** 124 * Transfers device output to the given route. 125 * 126 * <p>If the route is {@code null} then active route will be deactivated. 127 * 128 * @param routeId to switch to or {@code null} to unset the active device. 129 */ transferTo(@ullable String routeId)130 void transferTo(@Nullable String routeId); 131 132 /** 133 * Updates device route volume. 134 * 135 * @param volume specifies a volume for the device route or 0 for unknown. 136 * @return {@code true} if updated successfully and {@code false} otherwise. 137 */ updateVolume(int volume)138 boolean updateVolume(int volume); 139 140 /** 141 * Starts listening for changes in the system to keep an up to date view of available and 142 * selected devices. 143 */ start(UserHandle mUser)144 void start(UserHandle mUser); 145 146 /** 147 * Stops keeping the internal state up to date with the system, releasing any resources acquired 148 * in {@link #start} 149 */ stop()150 void stop(); 151 152 /** 153 * Interface for receiving events when device route has changed. 154 */ 155 interface OnDeviceRouteChangedListener { 156 157 /** Called when device route has changed. */ onDeviceRouteChanged()158 void onDeviceRouteChanged(); 159 } 160 161 } 162