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