Home | History | Annotate | Download | only in media
      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.IntDef;
     19 import android.annotation.Nullable;
     20 import android.annotation.SystemApi;
     21 import android.car.CarLibLog;
     22 import android.car.CarNotConnectedException;
     23 import android.content.Context;
     24 import android.media.AudioAttributes;
     25 import android.media.AudioManager;
     26 import android.media.AudioManager.OnAudioFocusChangeListener;
     27 import android.media.IVolumeController;
     28 import android.os.Handler;
     29 import android.os.IBinder;
     30 import android.os.RemoteException;
     31 import android.car.CarManagerBase;
     32 import android.util.Log;
     33 
     34 import java.lang.annotation.Retention;
     35 import java.lang.annotation.RetentionPolicy;
     36 import java.lang.ref.WeakReference;
     37 
     38 /**
     39  * APIs for handling car specific audio stuff.
     40  */
     41 public final class CarAudioManager implements CarManagerBase {
     42 
     43     /**
     44      * Audio usage for unspecified type.
     45      */
     46     public static final int CAR_AUDIO_USAGE_DEFAULT = 0;
     47     /**
     48      * Audio usage for playing music.
     49      */
     50     public static final int CAR_AUDIO_USAGE_MUSIC = 1;
     51     /**
     52      * Audio usage for H/W radio.
     53      */
     54     public static final int CAR_AUDIO_USAGE_RADIO = 2;
     55     /**
     56      * Audio usage for playing navigation guidance.
     57      */
     58     public static final int CAR_AUDIO_USAGE_NAVIGATION_GUIDANCE = 3;
     59     /**
     60      * Audio usage for voice call
     61      */
     62     public static final int CAR_AUDIO_USAGE_VOICE_CALL = 4;
     63     /**
     64      * Audio usage for voice search or voice command.
     65      */
     66     public static final int CAR_AUDIO_USAGE_VOICE_COMMAND = 5;
     67     /**
     68      * Audio usage for playing alarm.
     69      */
     70     public static final int CAR_AUDIO_USAGE_ALARM = 6;
     71     /**
     72      * Audio usage for notification sound.
     73      */
     74     public static final int CAR_AUDIO_USAGE_NOTIFICATION = 7;
     75     /**
     76      * Audio usage for system sound like UI feedback.
     77      */
     78     public static final int CAR_AUDIO_USAGE_SYSTEM_SOUND = 8;
     79     /**
     80      * Audio usage for playing safety alert.
     81      */
     82     public static final int CAR_AUDIO_USAGE_SYSTEM_SAFETY_ALERT = 9;
     83     /**
     84      * Audio usage for the ringing of a phone call.
     85      */
     86     public static final int CAR_AUDIO_USAGE_RINGTONE = 10;
     87     /**
     88      * Audio usage for external audio usage.
     89      * @hide
     90      */
     91     public static final int CAR_AUDIO_USAGE_EXTERNAL_AUDIO_SOURCE = 11;
     92 
     93     /** @hide */
     94     public static final int CAR_AUDIO_USAGE_MAX = CAR_AUDIO_USAGE_EXTERNAL_AUDIO_SOURCE;
     95 
     96     /** @hide */
     97     @IntDef({CAR_AUDIO_USAGE_DEFAULT, CAR_AUDIO_USAGE_MUSIC, CAR_AUDIO_USAGE_RADIO,
     98         CAR_AUDIO_USAGE_NAVIGATION_GUIDANCE, CAR_AUDIO_USAGE_VOICE_CALL,
     99         CAR_AUDIO_USAGE_VOICE_COMMAND, CAR_AUDIO_USAGE_ALARM, CAR_AUDIO_USAGE_NOTIFICATION,
    100         CAR_AUDIO_USAGE_SYSTEM_SOUND, CAR_AUDIO_USAGE_SYSTEM_SAFETY_ALERT,
    101         CAR_AUDIO_USAGE_EXTERNAL_AUDIO_SOURCE})
    102     @Retention(RetentionPolicy.SOURCE)
    103     public @interface CarAudioUsage {}
    104 
    105     /** @hide */
    106     public static final String CAR_RADIO_TYPE_AM_FM = "RADIO_AM_FM";
    107     /** @hide */
    108     public static final String CAR_RADIO_TYPE_AM_FM_HD = "RADIO_AM_FM_HD";
    109     /** @hide */
    110     public static final String CAR_RADIO_TYPE_DAB = "RADIO_DAB";
    111     /** @hide */
    112     public static final String CAR_RADIO_TYPE_SATELLITE = "RADIO_SATELLITE";
    113 
    114     /** @hide */
    115     public static final String CAR_EXTERNAL_SOURCE_TYPE_CD_DVD = "CD_DVD";
    116     /** @hide */
    117     public static final String CAR_EXTERNAL_SOURCE_TYPE_AUX_IN0 = "AUX_IN0";
    118     /** @hide */
    119     public static final String CAR_EXTERNAL_SOURCE_TYPE_AUX_IN1 = "AUX_IN1";
    120     /** @hide */
    121     public static final String CAR_EXTERNAL_SOURCE_TYPE_EXT_NAV_GUIDANCE = "EXT_NAV_GUIDANCE";
    122     /** @hide */
    123     public static final String CAR_EXTERNAL_SOURCE_TYPE_EXT_VOICE_CALL = "EXT_VOICE_CALL";
    124     /** @hide */
    125     public static final String CAR_EXTERNAL_SOURCE_TYPE_EXT_VOICE_COMMAND = "EXT_VOICE_COMMAND";
    126     /** @hide */
    127     public static final String CAR_EXTERNAL_SOURCE_TYPE_EXT_SAFETY_ALERT = "EXT_SAFETY_ALERT";
    128 
    129     private final ICarAudio mService;
    130     private final AudioManager mAudioManager;
    131     private final Handler mHandler;
    132 
    133     private ParameterChangeCallback mParameterChangeCallback;
    134     private OnParameterChangeListener mOnParameterChangeListener;
    135 
    136     /**
    137      * Get {@link AudioAttributes} relevant for the given usage in car.
    138      * @param carUsage
    139      * @return
    140      */
    141     public AudioAttributes getAudioAttributesForCarUsage(@CarAudioUsage int carUsage)
    142             throws CarNotConnectedException {
    143         try {
    144             return mService.getAudioAttributesForCarUsage(carUsage);
    145         } catch (RemoteException e) {
    146             throw new CarNotConnectedException();
    147         }
    148     }
    149 
    150     /**
    151      * Get AudioAttributes for radio. This is necessary when there are multiple types of radio
    152      * in system.
    153      *
    154      * @param radioType String specifying the desired radio type. Should use only what is listed in
    155      *        {@link #getSupportedRadioTypes()}.
    156      * @return
    157      * @throws IllegalArgumentException If not supported type is passed.
    158      *
    159      * @hide
    160      */
    161     public AudioAttributes getAudioAttributesForRadio(String radioType)
    162             throws CarNotConnectedException, IllegalArgumentException {
    163         try {
    164             return mService.getAudioAttributesForRadio(radioType);
    165         } catch (RemoteException e) {
    166             throw new CarNotConnectedException();
    167         }
    168     }
    169 
    170     /**
    171      * Get AudioAttributes for external audio source.
    172      *
    173      * @param externalSourceType String specifying the desired source type. Should use only what is
    174      *        listed in {@link #getSupportedExternalSourceTypes()}.
    175      * @return
    176      * @throws IllegalArgumentException If not supported type is passed.
    177      *
    178      * @hide
    179      */
    180     public AudioAttributes getAudioAttributesForExternalSource(String externalSourceType)
    181             throws CarNotConnectedException, IllegalArgumentException {
    182         try {
    183             return mService.getAudioAttributesForExternalSource(externalSourceType);
    184         } catch (RemoteException e) {
    185             throw new CarNotConnectedException();
    186         }
    187     }
    188 
    189     /**
    190      * List all supported external audio sources.
    191      *
    192      * @return
    193      *
    194      * @hide
    195      */
    196     public String[] getSupportedExternalSourceTypes() throws CarNotConnectedException {
    197         try {
    198             return mService.getSupportedExternalSourceTypes();
    199         } catch (RemoteException e) {
    200             throw new CarNotConnectedException();
    201         }
    202     }
    203 
    204     /**
    205      * List all supported radio sources.
    206      *
    207      * @return
    208      *
    209      * @hide
    210      */
    211     public String[] getSupportedRadioTypes() throws CarNotConnectedException {
    212         try {
    213             return mService.getSupportedRadioTypes();
    214         } catch (RemoteException e) {
    215             throw new CarNotConnectedException();
    216         }
    217     }
    218 
    219     /**
    220      * Request audio focus.
    221      * Send a request to obtain the audio focus.
    222      * @param l
    223      * @param requestAttributes
    224      * @param durationHint
    225      * @param flags
    226      */
    227     public int requestAudioFocus(OnAudioFocusChangeListener l,
    228                                  AudioAttributes requestAttributes,
    229                                  int durationHint,
    230                                  int flags)
    231                                          throws CarNotConnectedException, IllegalArgumentException {
    232         return mAudioManager.requestAudioFocus(l, requestAttributes, durationHint, flags);
    233     }
    234 
    235     /**
    236      * Abandon audio focus. Causes the previous focus owner, if any, to receive focus.
    237      * @param l
    238      * @param aa
    239      */
    240     public void abandonAudioFocus(OnAudioFocusChangeListener l, AudioAttributes aa) {
    241         mAudioManager.abandonAudioFocus(l, aa);
    242     }
    243 
    244     /**
    245      * Sets the volume index for a particular stream.
    246      *
    247      * Requires {@link android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME} permission.
    248      *
    249      * @param streamType The stream whose volume index should be set.
    250      * @param index The volume index to set. See
    251      *            {@link #getStreamMaxVolume(int)} for the largest valid value.
    252      * @param flags One or more flags (e.g., {@link android.media.AudioManager#FLAG_SHOW_UI},
    253      *              {@link android.media.AudioManager#FLAG_PLAY_SOUND})
    254      */
    255     @SystemApi
    256     public void setStreamVolume(int streamType, int index, int flags)
    257             throws CarNotConnectedException {
    258         try {
    259             mService.setStreamVolume(streamType, index, flags);
    260         } catch (RemoteException e) {
    261             Log.e(CarLibLog.TAG_CAR, "setStreamVolume failed", e);
    262             throw new CarNotConnectedException(e);
    263         }
    264     }
    265 
    266     /**
    267      * Registers a global volume controller interface.
    268      *
    269      * Requires {@link android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME} permission.
    270      *
    271      * @hide
    272      */
    273     @SystemApi
    274     public void setVolumeController(IVolumeController controller)
    275             throws CarNotConnectedException {
    276         try {
    277             mService.setVolumeController(controller);
    278         } catch (RemoteException e) {
    279             Log.e(CarLibLog.TAG_CAR, "setVolumeController failed", e);
    280             throw new CarNotConnectedException(e);
    281         }
    282     }
    283 
    284     /**
    285      * Returns the maximum volume index for a particular stream.
    286      *
    287      * Requires {@link android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME} permission.
    288      *
    289      * @param stream The stream type whose maximum volume index is returned.
    290      * @return The maximum valid volume index for the stream.
    291      */
    292     @SystemApi
    293     public int getStreamMaxVolume(int stream) throws CarNotConnectedException {
    294         try {
    295             return mService.getStreamMaxVolume(stream);
    296         } catch (RemoteException e) {
    297             Log.e(CarLibLog.TAG_CAR, "getStreamMaxVolume failed", e);
    298             throw new CarNotConnectedException(e);
    299         }
    300     }
    301 
    302     /**
    303      * Returns the minimum volume index for a particular stream.
    304      *
    305      * Requires {@link android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME} permission.
    306      *
    307      * @param stream The stream type whose maximum volume index is returned.
    308      * @return The maximum valid volume index for the stream.
    309      */
    310     @SystemApi
    311     public int getStreamMinVolume(int stream) throws CarNotConnectedException {
    312         try {
    313             return mService.getStreamMinVolume(stream);
    314         } catch (RemoteException e) {
    315             Log.e(CarLibLog.TAG_CAR, "getStreamMaxVolume failed", e);
    316             throw new CarNotConnectedException(e);
    317         }
    318     }
    319 
    320     /**
    321      * Returns the current volume index for a particular stream.
    322      *
    323      * Requires {@link android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME} permission.
    324      *
    325      * @param stream The stream type whose volume index is returned.
    326      * @return The current volume index for the stream.
    327      *
    328      * @see #getStreamMaxVolume(int)
    329      * @see #setStreamVolume(int, int, int)
    330      */
    331     @SystemApi
    332     public int getStreamVolume(int stream) throws CarNotConnectedException {
    333         try {
    334             return mService.getStreamVolume(stream);
    335         } catch (RemoteException e) {
    336             Log.e(CarLibLog.TAG_CAR, "getStreamVolume failed", e);
    337             throw new CarNotConnectedException(e);
    338         }
    339     }
    340 
    341     /**
    342      * Check if media audio is muted or not. This will include music and radio. Any application
    343      * taking audio focus for media stream will get it out of mute state.
    344      *
    345      * @return true if media is muted.
    346      * @throws CarNotConnectedException if the connection to the car service has been lost.
    347      * @hide
    348      */
    349     @SystemApi
    350     public boolean isMediaMuted() throws CarNotConnectedException {
    351         try {
    352             return mService.isMediaMuted();
    353         } catch (RemoteException e) {
    354             Log.e(CarLibLog.TAG_CAR, "isMediaMuted failed", e);
    355             throw new CarNotConnectedException(e);
    356         }
    357     }
    358 
    359     /**
    360      * Mute or unmute media stream including radio. This can involve audio focus change to stop
    361      * whatever app holding audio focus now. If requester is currently holding audio focus,
    362      * it will get LOSS_TRANSIENT focus loss.
    363      * This API requires {@link android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME} permission.
    364      *
    365      * @param mute
    366      * @return Mute state of system after the request. Note that mute request can fail if there
    367      *         is higher priority audio already being played like phone call.
    368      * @throws CarNotConnectedException if the connection to the car service has been lost.
    369      * @hide
    370      */
    371     @SystemApi
    372     public boolean setMediaMute(boolean mute) throws CarNotConnectedException {
    373         try {
    374             return mService.setMediaMute(mute);
    375         } catch (RemoteException e) {
    376             Log.e(CarLibLog.TAG_CAR, "setMediaMute failed", e);
    377             throw new CarNotConnectedException(e);
    378         }
    379     }
    380 
    381     /**
    382      * Listener to monitor audio parameter changes.
    383      * @hide
    384      */
    385     public interface OnParameterChangeListener {
    386         /**
    387          * Parameter changed.
    388          * @param parameters Have format of key1=value1;key2=value2;...
    389          */
    390         void onParameterChange(String parameters);
    391     }
    392 
    393     /**
    394      * Return array of keys supported in this system.
    395      * Requires {@link android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS} permission.
    396      * The list is static and will not change.
    397      * @return null if there is no audio parameters supported.
    398      * @throws CarNotConnectedException
    399      *
    400      * @hide
    401      */
    402     public @Nullable String[] getParameterKeys() throws CarNotConnectedException {
    403         try {
    404             return mService.getParameterKeys();
    405         } catch (RemoteException e) {
    406             Log.e(CarLibLog.TAG_CAR, "getParameterKeys failed", e);
    407             throw new CarNotConnectedException(e);
    408         }
    409     }
    410 
    411     /**
    412      * Set car specific audio parameters.
    413      * Requires {@link android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS} permission.
    414      * Only keys listed from {@link #getParameterKeys()} should be used.
    415      * @param parameters has format of key1=value1;key2=value2;...
    416      * @throws CarNotConnectedException
    417      *
    418      * @hide
    419      */
    420     public void setParameters(String parameters) throws CarNotConnectedException {
    421         try {
    422             mService.setParameters(parameters);
    423         } catch (RemoteException e) {
    424             Log.e(CarLibLog.TAG_CAR, "setParameters failed", e);
    425             throw new CarNotConnectedException(e);
    426         }
    427     }
    428 
    429     /**
    430      * Get parameters for the key passed.
    431      * Requires {@link android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS} permission.
    432      * Only keys listed from {@link #getParameterKeys()} should be used.
    433      * @param keys Keys to get value. Format is key1;key2;...
    434      * @return Parameters in format of key1=value1;key2=value2;...
    435      * @throws CarNotConnectedException
    436      *
    437      * @hide
    438      */
    439     public String getParameters(String keys) throws CarNotConnectedException {
    440         try {
    441             return mService.getParameters(keys);
    442         } catch (RemoteException e) {
    443             Log.e(CarLibLog.TAG_CAR, "getParameters failed", e);
    444             throw new CarNotConnectedException(e);
    445         }
    446     }
    447 
    448     /**
    449      * Set listener to monitor audio parameter changes.
    450      * Requires {@link android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS} permission.
    451      * @param listener Non-null listener will start monitoring. null listener will stop listening.
    452      * @throws CarNotConnectedException
    453      *
    454      * @hide
    455      */
    456     public void setOnParameterChangeListener(OnParameterChangeListener listener)
    457             throws CarNotConnectedException {
    458         ParameterChangeCallback oldCb = null;
    459         ParameterChangeCallback newCb = null;
    460         synchronized (this) {
    461             if (listener != null) {
    462                 if (mParameterChangeCallback != null) {
    463                     oldCb = mParameterChangeCallback;
    464                 }
    465                 newCb = new ParameterChangeCallback(this);
    466             }
    467             mParameterChangeCallback = newCb;
    468             mOnParameterChangeListener = listener;
    469         }
    470         try {
    471             if (oldCb != null) {
    472                 mService.unregisterOnParameterChangeListener(oldCb);
    473             }
    474             if (newCb != null) {
    475                 mService.registerOnParameterChangeListener(newCb);
    476             }
    477         } catch (RemoteException e) {
    478             Log.e(CarLibLog.TAG_CAR, "setOnParameterChangeListener failed", e);
    479             throw new CarNotConnectedException(e);
    480         }
    481     }
    482 
    483     /** @hide */
    484     @Override
    485     public void onCarDisconnected() {
    486     }
    487 
    488     /** @hide */
    489     public CarAudioManager(IBinder service, Context context, Handler handler) {
    490         mService = ICarAudio.Stub.asInterface(service);
    491         mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
    492         mHandler = handler;
    493     }
    494 
    495     private AudioAttributes createAudioAttributes(int contentType, int usage) {
    496         AudioAttributes.Builder builder = new AudioAttributes.Builder();
    497         return builder.setContentType(contentType).setUsage(usage).build();
    498     }
    499 
    500     private static class ParameterChangeCallback extends ICarAudioCallback.Stub {
    501 
    502         private final WeakReference<CarAudioManager> mManager;
    503 
    504         private ParameterChangeCallback(CarAudioManager manager) {
    505             mManager = new WeakReference<>(manager);
    506         }
    507 
    508         @Override
    509         public void onParameterChange(final String params) {
    510             CarAudioManager manager = mManager.get();
    511             if (manager == null) {
    512                 return;
    513             }
    514             final OnParameterChangeListener listener = manager.mOnParameterChangeListener;
    515             if (listener == null) {
    516                 return;
    517             }
    518             manager.mHandler.post(new Runnable() {
    519                 @Override
    520                 public void run() {
    521                     listener.onParameterChange(params);
    522                 }
    523             });
    524         }
    525     }
    526 }
    527