1 /*
2  * Copyright (C) 2015 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 package android.car.media;
17 
18 import android.annotation.NonNull;
19 import android.annotation.RequiresPermission;
20 import android.annotation.SystemApi;
21 import android.annotation.TestApi;
22 import android.car.Car;
23 import android.car.CarLibLog;
24 import android.car.CarManagerBase;
25 import android.media.AudioAttributes;
26 import android.os.Bundle;
27 import android.os.IBinder;
28 import android.os.RemoteException;
29 import android.util.Log;
30 import android.view.Display;
31 import android.view.DisplayAddress;
32 
33 import java.util.ArrayList;
34 import java.util.List;
35 
36 /**
37  * APIs for handling audio in a car.
38  *
39  * In a car environment, we introduced the support to turn audio dynamic routing on /off by
40  * setting the "audioUseDynamicRouting" attribute in config.xml
41  *
42  * When audio dynamic routing is enabled:
43  * - Audio devices are grouped into zones
44  * - There is at least one primary zone, and extra secondary zones such as RSE
45  *   (Reat Seat Entertainment)
46  * - Within each zone, audio devices are grouped into volume groups for volume control
47  * - Audio is assigned to an audio device based on its AudioAttributes usage
48  *
49  * When audio dynamic routing is disabled:
50  * - There is exactly one audio zone, which is the primary zone
51  * - Each volume group represents a controllable STREAM_TYPE, same as AudioManager
52  */
53 public final class CarAudioManager extends CarManagerBase {
54 
55     /**
56      * Zone id of the primary audio zone.
57      * @hide
58      */
59     @SystemApi
60     public static final int PRIMARY_AUDIO_ZONE = 0x0;
61 
62     /**
63      * Extra for {@link android.media.AudioAttributes.Builder#addBundle(Bundle)}: when used in an
64      * {@link android.media.AudioFocusRequest}, the requester should receive all audio focus events,
65      * including {@link android.media.AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}.
66      * The requester must hold {@link Car#PERMISSION_RECEIVE_CAR_AUDIO_DUCKING_EVENTS}; otherwise,
67      * this extra is ignored.
68      *
69      * @hide
70      */
71     @SystemApi
72     public static final String AUDIOFOCUS_EXTRA_RECEIVE_DUCKING_EVENTS =
73             "android.car.media.AUDIOFOCUS_EXTRA_RECEIVE_DUCKING_EVENTS";
74 
75     /**
76      * Extra for {@link android.media.AudioAttributes.Builder#addBundle(Bundle)}: when used in an
77      * {@link android.media.AudioFocusRequest}, the requester should receive all audio focus for the
78      * the zone. If the zone id is not defined: the audio focus request will default to the
79      * currently mapped zone for the requesting uid or {@link CarAudioManager.PRIMARY_AUDIO_ZONE}
80      * if no uid mapping currently exist.
81      *
82      * @hide
83      */
84     public static final String AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID =
85             "android.car.media.AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID";
86 
87     private final ICarAudio mService;
88     private final List<CarVolumeCallback> mCarVolumeCallbacks;
89 
90     private final ICarVolumeCallback mCarVolumeCallbackImpl = new ICarVolumeCallback.Stub() {
91         @Override
92         public void onGroupVolumeChanged(int zoneId, int groupId, int flags) {
93             for (CarVolumeCallback callback : mCarVolumeCallbacks) {
94                 callback.onGroupVolumeChanged(zoneId, groupId, flags);
95             }
96         }
97 
98         @Override
99         public void onMasterMuteChanged(int zoneId, int flags) {
100             for (CarVolumeCallback callback : mCarVolumeCallbacks) {
101                 callback.onMasterMuteChanged(zoneId, flags);
102             }
103         }
104     };
105 
106     /**
107      * @return Whether dynamic routing is enabled or not.
108      * @hide
109      */
110     @TestApi
isDynamicRoutingEnabled()111     public boolean isDynamicRoutingEnabled() {
112         try {
113             return mService.isDynamicRoutingEnabled();
114         } catch (RemoteException e) {
115             return handleRemoteExceptionFromCarService(e, false);
116         }
117     }
118 
119     /**
120      * Sets the volume index for a volume group in primary zone.
121      *
122      * @see {@link #setGroupVolume(int, int, int, int)}
123      * @hide
124      */
125     @SystemApi
126     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
setGroupVolume(int groupId, int index, int flags)127     public void setGroupVolume(int groupId, int index, int flags) {
128         setGroupVolume(PRIMARY_AUDIO_ZONE, groupId, index, flags);
129     }
130 
131     /**
132      * Sets the volume index for a volume group.
133      *
134      * @param zoneId The zone id whose volume group is affected.
135      * @param groupId The volume group id whose volume index should be set.
136      * @param index The volume index to set. See
137      *            {@link #getGroupMaxVolume(int, int)} for the largest valid value.
138      * @param flags One or more flags (e.g., {@link android.media.AudioManager#FLAG_SHOW_UI},
139      *              {@link android.media.AudioManager#FLAG_PLAY_SOUND})
140      * @hide
141      */
142     @SystemApi
143     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
setGroupVolume(int zoneId, int groupId, int index, int flags)144     public void setGroupVolume(int zoneId, int groupId, int index, int flags) {
145         try {
146             mService.setGroupVolume(zoneId, groupId, index, flags);
147         } catch (RemoteException e) {
148             handleRemoteExceptionFromCarService(e);
149         }
150     }
151 
152     /**
153      * Returns the maximum volume index for a volume group in primary zone.
154      *
155      * @see {@link #getGroupMaxVolume(int, int)}
156      * @hide
157      */
158     @SystemApi
159     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
getGroupMaxVolume(int groupId)160     public int getGroupMaxVolume(int groupId) {
161         return getGroupMaxVolume(PRIMARY_AUDIO_ZONE, groupId);
162     }
163 
164     /**
165      * Returns the maximum volume index for a volume group.
166      *
167      * @param zoneId The zone id whose volume group is queried.
168      * @param groupId The volume group id whose maximum volume index is returned.
169      * @return The maximum valid volume index for the given group.
170      * @hide
171      */
172     @SystemApi
173     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
getGroupMaxVolume(int zoneId, int groupId)174     public int getGroupMaxVolume(int zoneId, int groupId) {
175         try {
176             return mService.getGroupMaxVolume(zoneId, groupId);
177         } catch (RemoteException e) {
178             return handleRemoteExceptionFromCarService(e, 0);
179         }
180     }
181 
182     /**
183      * Returns the minimum volume index for a volume group in primary zone.
184      *
185      * @see {@link #getGroupMinVolume(int, int)}
186      * @hide
187      */
188     @SystemApi
189     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
getGroupMinVolume(int groupId)190     public int getGroupMinVolume(int groupId) {
191         return getGroupMinVolume(PRIMARY_AUDIO_ZONE, groupId);
192     }
193 
194     /**
195      * Returns the minimum volume index for a volume group.
196      *
197      * @param zoneId The zone id whose volume group is queried.
198      * @param groupId The volume group id whose minimum volume index is returned.
199      * @return The minimum valid volume index for the given group, non-negative
200      * @hide
201      */
202     @SystemApi
203     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
getGroupMinVolume(int zoneId, int groupId)204     public int getGroupMinVolume(int zoneId, int groupId) {
205         try {
206             return mService.getGroupMinVolume(zoneId, groupId);
207         } catch (RemoteException e) {
208             return handleRemoteExceptionFromCarService(e, 0);
209         }
210     }
211 
212     /**
213      * Returns the current volume index for a volume group in primary zone.
214      *
215      * @see {@link #getGroupVolume(int, int)}
216      * @hide
217      */
218     @SystemApi
219     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
getGroupVolume(int groupId)220     public int getGroupVolume(int groupId) {
221         return getGroupVolume(PRIMARY_AUDIO_ZONE, groupId);
222     }
223 
224     /**
225      * Returns the current volume index for a volume group.
226      *
227      * @param zoneId The zone id whose volume groups is queried.
228      * @param groupId The volume group id whose volume index is returned.
229      * @return The current volume index for the given group.
230      *
231      * @see #getGroupMaxVolume(int, int)
232      * @see #setGroupVolume(int, int, int, int)
233      * @hide
234      */
235     @SystemApi
236     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
getGroupVolume(int zoneId, int groupId)237     public int getGroupVolume(int zoneId, int groupId) {
238         try {
239             return mService.getGroupVolume(zoneId, groupId);
240         } catch (RemoteException e) {
241             return handleRemoteExceptionFromCarService(e, 0);
242         }
243     }
244 
245     /**
246      * Adjust the relative volume in the front vs back of the vehicle cabin.
247      *
248      * @param value in the range -1.0 to 1.0 for fully toward the back through
249      *              fully toward the front.  0.0 means evenly balanced.
250      *
251      * @see #setBalanceTowardRight(float)
252      * @hide
253      */
254     @SystemApi
255     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
setFadeTowardFront(float value)256     public void setFadeTowardFront(float value) {
257         try {
258             mService.setFadeTowardFront(value);
259         } catch (RemoteException e) {
260             handleRemoteExceptionFromCarService(e);
261         }
262     }
263 
264     /**
265      * Adjust the relative volume on the left vs right side of the vehicle cabin.
266      *
267      * @param value in the range -1.0 to 1.0 for fully toward the left through
268      *              fully toward the right.  0.0 means evenly balanced.
269      *
270      * @see #setFadeTowardFront(float)
271      * @hide
272      */
273     @SystemApi
274     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
setBalanceTowardRight(float value)275     public void setBalanceTowardRight(float value) {
276         try {
277             mService.setBalanceTowardRight(value);
278         } catch (RemoteException e) {
279             handleRemoteExceptionFromCarService(e);
280         }
281     }
282 
283     /**
284      * Queries the system configuration in order to report the available, non-microphone audio
285      * input devices.
286      *
287      * @return An array of strings representing the available input ports.
288      * Each port is identified by it's "address" tag in the audioPolicyConfiguration xml file.
289      * Empty array if we find nothing.
290      *
291      * @see #createAudioPatch(String, int, int)
292      * @see #releaseAudioPatch(CarAudioPatchHandle)
293      * @hide
294      */
295     @SystemApi
296     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
getExternalSources()297     public @NonNull String[] getExternalSources() {
298         try {
299             return mService.getExternalSources();
300         } catch (RemoteException e) {
301             handleRemoteExceptionFromCarService(e);
302             return new String[0];
303         }
304     }
305 
306     /**
307      * Given an input port identified by getExternalSources(), request that it's audio signal
308      * be routed below the HAL to the output port associated with the given usage.  For example,
309      * The output of a tuner might be routed directly to the output buss associated with
310      * AudioAttributes.USAGE_MEDIA while the tuner is playing.
311      *
312      * @param sourceAddress the input port name obtained from getExternalSources().
313      * @param usage the type of audio represented by this source (usually USAGE_MEDIA).
314      * @param gainInMillibels How many steps above the minimum value defined for the source port to
315      *                       set the gain when creating the patch.
316      *                       This may be used for source balancing without affecting the user
317      *                       controlled volumes applied to the destination ports.  A value of
318      *                       0 indicates no gain change is requested.
319      * @return A handle for the created patch which can be used to later remove it.
320      *
321      * @see #getExternalSources()
322      * @see #releaseAudioPatch(CarAudioPatchHandle)
323      * @hide
324      */
325     @SystemApi
326     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
createAudioPatch(String sourceAddress, @AudioAttributes.AttributeUsage int usage, int gainInMillibels)327     public CarAudioPatchHandle createAudioPatch(String sourceAddress,
328             @AudioAttributes.AttributeUsage int usage, int gainInMillibels) {
329         try {
330             return mService.createAudioPatch(sourceAddress, usage, gainInMillibels);
331         } catch (RemoteException e) {
332             return handleRemoteExceptionFromCarService(e, null);
333         }
334     }
335 
336     /**
337      * Removes the association between an input port and an output port identified by the provided
338      * handle.
339      *
340      * @param patch CarAudioPatchHandle returned from createAudioPatch().
341      *
342      * @see #getExternalSources()
343      * @see #createAudioPatch(String, int, int)
344      * @hide
345      */
346     @SystemApi
347     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
releaseAudioPatch(CarAudioPatchHandle patch)348     public void releaseAudioPatch(CarAudioPatchHandle patch) {
349         try {
350             mService.releaseAudioPatch(patch);
351         } catch (RemoteException e) {
352             handleRemoteExceptionFromCarService(e);
353         }
354     }
355 
356     /**
357      * Gets the count of available volume groups in primary zone.
358      *
359      * @see {@link #getVolumeGroupCount(int)}
360      * @hide
361      */
362     @SystemApi
363     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
getVolumeGroupCount()364     public int getVolumeGroupCount() {
365         return getVolumeGroupCount(PRIMARY_AUDIO_ZONE);
366     }
367 
368     /**
369      * Gets the count of available volume groups in the system.
370      *
371      * @param zoneId The zone id whois count of volume groups is queried.
372      * @return Count of volume groups
373      * @hide
374      */
375     @SystemApi
376     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
getVolumeGroupCount(int zoneId)377     public int getVolumeGroupCount(int zoneId) {
378         try {
379             return mService.getVolumeGroupCount(zoneId);
380         } catch (RemoteException e) {
381             return handleRemoteExceptionFromCarService(e, 0);
382         }
383     }
384 
385     /**
386      * Gets the volume group id for a given {@link AudioAttributes} usage in primary zone.
387      *
388      * @see {@link #getVolumeGroupIdForUsage(int, int)}
389      * @hide
390      */
391     @SystemApi
392     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
getVolumeGroupIdForUsage(@udioAttributes.AttributeUsage int usage)393     public int getVolumeGroupIdForUsage(@AudioAttributes.AttributeUsage int usage) {
394         return getVolumeGroupIdForUsage(PRIMARY_AUDIO_ZONE, usage);
395     }
396 
397     /**
398      * Gets the volume group id for a given {@link AudioAttributes} usage.
399      *
400      * @param zoneId The zone id whose volume group is queried.
401      * @param usage The {@link AudioAttributes} usage to get a volume group from.
402      * @return The volume group id where the usage belongs to
403      * @hide
404      */
405     @SystemApi
406     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
getVolumeGroupIdForUsage(int zoneId, @AudioAttributes.AttributeUsage int usage)407     public int getVolumeGroupIdForUsage(int zoneId, @AudioAttributes.AttributeUsage int usage) {
408         try {
409             return mService.getVolumeGroupIdForUsage(zoneId, usage);
410         } catch (RemoteException e) {
411             return handleRemoteExceptionFromCarService(e, 0);
412         }
413     }
414 
415     /**
416      * Gets array of {@link AudioAttributes} usages for a volume group in primary zone.
417      *
418      * @see {@link #getUsagesForVolumeGroupId(int, int)}
419      * @hide
420      */
421     @SystemApi
422     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
getUsagesForVolumeGroupId(int groupId)423     public @NonNull int[] getUsagesForVolumeGroupId(int groupId) {
424         return getUsagesForVolumeGroupId(PRIMARY_AUDIO_ZONE, groupId);
425     }
426 
427     /**
428      * Gets the audio zones currently available
429      *
430      * @return audio zone ids
431      * @hide
432      */
433     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
getAudioZoneIds()434     public @NonNull int[] getAudioZoneIds() {
435         try {
436             return mService.getAudioZoneIds();
437         } catch (RemoteException e) {
438             return handleRemoteExceptionFromCarService(e, new int[0]);
439         }
440     }
441 
442     /**
443      * Gets the audio zone id currently mapped to uId,
444      * defaults to PRIMARY_AUDIO_ZONE if no mapping exist
445      *
446      * @param uid The uid to map
447      * @return zone id mapped to uid
448      * @hide
449      */
450     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
getZoneIdForUid(int uid)451     public int getZoneIdForUid(int uid) {
452         try {
453             return mService.getZoneIdForUid(uid);
454         } catch (RemoteException e) {
455             return handleRemoteExceptionFromCarService(e, 0);
456         }
457     }
458 
459     /**
460      * Maps the audio zone id to uid
461      *
462      * @param zoneId The audio zone id
463      * @param uid The uid to map
464      * @return true if the uid is successfully mapped
465      * @hide
466      */
467     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
setZoneIdForUid(int zoneId, int uid)468     public boolean setZoneIdForUid(int zoneId, int uid) {
469         try {
470             return mService.setZoneIdForUid(zoneId, uid);
471         } catch (RemoteException e) {
472             return handleRemoteExceptionFromCarService(e, false);
473         }
474     }
475 
476     /**
477      * Clears the current zone mapping of the uid
478      *
479      * @param uid The uid to clear
480      * @return true if the zone was successfully cleared
481      * @hide
482      */
483     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
clearZoneIdForUid(int uid)484     public boolean clearZoneIdForUid(int uid) {
485         try {
486             return mService.clearZoneIdForUid(uid);
487         } catch (RemoteException e) {
488             return handleRemoteExceptionFromCarService(e, false);
489         }
490     }
491 
492     /**
493      * Get the zone id for the display
494      *
495      * @param  display display to query
496      * @return zone id for display or
497      * CarAudioManager.PRIMARY_AUDIO_ZONE if no match is found.
498      * @hide
499      */
500     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
getZoneIdForDisplay(Display display)501     public int getZoneIdForDisplay(Display display) {
502         DisplayAddress address = display.getAddress();
503         if (address instanceof DisplayAddress.Physical) {
504             DisplayAddress.Physical physicalAddress = (DisplayAddress.Physical) address;
505             if (physicalAddress != null) {
506                 return getZoneIdForDisplayPortId(physicalAddress.getPort());
507             }
508         }
509         return PRIMARY_AUDIO_ZONE;
510     }
511 
512     /**
513      * Get the zone id for the display port id passed in
514      *
515      * @param  displayPortId display port id to query
516      * @return zone id for display port id or
517      * CarAudioManager.PRIMARY_AUDIO_ZONE if no match is found.
518      * @hide
519      */
520     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
getZoneIdForDisplayPortId(byte displayPortId)521     public int getZoneIdForDisplayPortId(byte displayPortId) {
522         try {
523             return mService.getZoneIdForDisplayPortId(displayPortId);
524         } catch (RemoteException e) {
525             return handleRemoteExceptionFromCarService(e, 0);
526         }
527     }
528 
529     /**
530      * Gets array of {@link AudioAttributes} usages for a volume group in a zone.
531      *
532      * @param zoneId The zone id whose volume group is queried.
533      * @param groupId The volume group id whose associated audio usages is returned.
534      * @return Array of {@link AudioAttributes} usages for a given volume group id
535      * @hide
536      */
537     @SystemApi
538     @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
getUsagesForVolumeGroupId(int zoneId, int groupId)539     public @NonNull int[] getUsagesForVolumeGroupId(int zoneId, int groupId) {
540         try {
541             return mService.getUsagesForVolumeGroupId(zoneId, groupId);
542         } catch (RemoteException e) {
543             return handleRemoteExceptionFromCarService(e, new int[0]);
544         }
545     }
546 
547     /** @hide */
548     @Override
onCarDisconnected()549     public void onCarDisconnected() {
550         if (mService != null) {
551             try {
552                 mService.unregisterVolumeCallback(mCarVolumeCallbackImpl.asBinder());
553             } catch (RemoteException e) {
554                 handleRemoteExceptionFromCarService(e);
555             }
556         }
557     }
558 
559     /** @hide */
CarAudioManager(Car car, IBinder service)560     public CarAudioManager(Car car, IBinder service) {
561         super(car);
562         mService = ICarAudio.Stub.asInterface(service);
563         mCarVolumeCallbacks = new ArrayList<>();
564         try {
565             mService.registerVolumeCallback(mCarVolumeCallbackImpl.asBinder());
566         } catch (RemoteException e) {
567             Log.e(CarLibLog.TAG_CAR, "registerVolumeCallback failed", e);
568         }
569     }
570 
571     /**
572      * Registers a {@link CarVolumeCallback} to receive volume change callbacks
573      * @param callback {@link CarVolumeCallback} instance, can not be null
574      */
registerCarVolumeCallback(@onNull CarVolumeCallback callback)575     public void registerCarVolumeCallback(@NonNull CarVolumeCallback callback) {
576         mCarVolumeCallbacks.add(callback);
577     }
578 
579     /**
580      * Unregisters a {@link CarVolumeCallback} from receiving volume change callbacks
581      * @param callback {@link CarVolumeCallback} instance previously registered, can not be null
582      */
unregisterCarVolumeCallback(@onNull CarVolumeCallback callback)583     public void unregisterCarVolumeCallback(@NonNull CarVolumeCallback callback) {
584         mCarVolumeCallbacks.remove(callback);
585     }
586 
587     /**
588      * Callback interface to receive volume change events in a car.
589      * Extend this class and register it with {@link #registerCarVolumeCallback(CarVolumeCallback)}
590      * and unregister it via {@link #unregisterCarVolumeCallback(CarVolumeCallback)}
591      */
592     public abstract static class CarVolumeCallback {
593         /**
594          * This is called whenever a group volume is changed.
595          * The changed-to volume index is not included, the caller is encouraged to
596          * get the current group volume index via CarAudioManager.
597          *
598          * @param zoneId Id of the audio zone that volume change happens
599          * @param groupId Id of the volume group that volume is changed
600          * @param flags see {@link android.media.AudioManager} for flag definitions
601          */
onGroupVolumeChanged(int zoneId, int groupId, int flags)602         public void onGroupVolumeChanged(int zoneId, int groupId, int flags) {}
603 
604         /**
605          * This is called whenever the global mute state is changed.
606          * The changed-to global mute state is not included, the caller is encouraged to
607          * get the current global mute state via AudioManager.
608          *
609          * @param zoneId Id of the audio zone that global mute state change happens
610          * @param flags see {@link android.media.AudioManager} for flag definitions
611          */
onMasterMuteChanged(int zoneId, int flags)612         public void onMasterMuteChanged(int zoneId, int flags) {}
613     }
614 }
615