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 AudioManagerRouteController controller = 69 AudioManagerRouteController.getInstance( 70 context, audioManager, looper, strategyForMedia, btAdapter); 71 controller.registerRouteChangeListener(onDeviceRouteChangedListener); 72 return controller; 73 } else { 74 IAudioService audioService = 75 IAudioService.Stub.asInterface( 76 ServiceManager.getService(Context.AUDIO_SERVICE)); 77 return new LegacyDeviceRouteController( 78 context, audioManager, audioService, onDeviceRouteChangedListener); 79 } 80 } 81 82 /** Returns device route availability status. */ 83 @MediaRoute2Info.SuitabilityStatus getBuiltInSpeakerSuitabilityStatus(@onNull Context context)84 static int getBuiltInSpeakerSuitabilityStatus(@NonNull Context context) { 85 if (!Flags.enableBuiltInSpeakerRouteSuitabilityStatuses()) { 86 // Route is always suitable if the flag is disabled. 87 return MediaRoute2Info.SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER; 88 } 89 90 int availabilityStatus = 91 context.getResources() 92 .getInteger( 93 com.android.internal.R.integer 94 .config_mediaRouter_builtInSpeakerSuitability); 95 96 switch (availabilityStatus) { 97 case MediaRoute2Info.SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER: 98 case MediaRoute2Info.SUITABILITY_STATUS_SUITABLE_FOR_MANUAL_TRANSFER: 99 case MediaRoute2Info.SUITABILITY_STATUS_NOT_SUITABLE_FOR_TRANSFER: 100 return availabilityStatus; 101 default: 102 return MediaRoute2Info.SUITABILITY_STATUS_SUITABLE_FOR_DEFAULT_TRANSFER; 103 } 104 } 105 106 /** Returns the currently selected device (built-in or wired) route. */ 107 @NonNull getSelectedRoute()108 MediaRoute2Info getSelectedRoute(); 109 110 /** 111 * Returns all available routes. 112 * 113 * <p>Note that this method returns available routes including the selected route because (a) 114 * this interface doesn't guarantee that the internal state of the controller won't change 115 * between calls to {@link #getSelectedRoute()} and this method and (b) {@link 116 * #getSelectedRoute()} may be treated as a transferable route (not a selected route) if the 117 * selected route is from {@link BluetoothRouteController}. 118 */ getAvailableRoutes()119 List<MediaRoute2Info> getAvailableRoutes(); 120 121 /** 122 * Transfers device output to the given route. 123 * 124 * <p>If the route is {@code null} then active route will be deactivated. 125 * 126 * @param routeId to switch to or {@code null} to unset the active device. 127 */ transferTo(@ullable String routeId)128 void transferTo(@Nullable String routeId); 129 130 /** 131 * Updates device route volume. 132 * 133 * @param volume specifies a volume for the device route or 0 for unknown. 134 * @return {@code true} if updated successfully and {@code false} otherwise. 135 */ updateVolume(int volume)136 boolean updateVolume(int volume); 137 138 /** 139 * Starts listening for changes in the system to keep an up to date view of available and 140 * selected devices. 141 */ start(UserHandle mUser)142 void start(UserHandle mUser); 143 144 /** 145 * Stops keeping the internal state up to date with the system, releasing any resources acquired 146 * in {@link #start} 147 */ stop()148 void stop(); 149 150 /** 151 * Interface for receiving events when device route has changed. 152 */ 153 interface OnDeviceRouteChangedListener { 154 155 /** Called when device route has changed. */ onDeviceRouteChanged()156 void onDeviceRouteChanged(); 157 } 158 159 } 160