Home | History | Annotate | Download | only in media
      1 /*
      2  * Copyright (C) 2006 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 android.media;
     18 
     19 import static android.Manifest.permission.REMOTE_AUDIO_PLAYBACK;
     20 import static android.media.AudioManager.RINGER_MODE_NORMAL;
     21 import static android.media.AudioManager.RINGER_MODE_SILENT;
     22 import static android.media.AudioManager.RINGER_MODE_VIBRATE;
     23 
     24 import android.app.Activity;
     25 import android.app.ActivityManagerNative;
     26 import android.app.KeyguardManager;
     27 import android.app.PendingIntent;
     28 import android.app.PendingIntent.CanceledException;
     29 import android.app.PendingIntent.OnFinished;
     30 import android.bluetooth.BluetoothA2dp;
     31 import android.bluetooth.BluetoothAdapter;
     32 import android.bluetooth.BluetoothClass;
     33 import android.bluetooth.BluetoothDevice;
     34 import android.bluetooth.BluetoothHeadset;
     35 import android.bluetooth.BluetoothProfile;
     36 import android.content.ActivityNotFoundException;
     37 import android.content.BroadcastReceiver;
     38 import android.content.ComponentName;
     39 import android.content.ContentResolver;
     40 import android.content.Context;
     41 import android.content.Intent;
     42 import android.content.IntentFilter;
     43 import android.content.pm.PackageManager;
     44 import android.content.res.Configuration;
     45 import android.database.ContentObserver;
     46 import android.media.MediaPlayer.OnCompletionListener;
     47 import android.media.MediaPlayer.OnErrorListener;
     48 import android.os.Binder;
     49 import android.os.Bundle;
     50 import android.os.Environment;
     51 import android.os.Handler;
     52 import android.os.IBinder;
     53 import android.os.Looper;
     54 import android.os.Message;
     55 import android.os.PowerManager;
     56 import android.os.RemoteCallbackList;
     57 import android.os.RemoteException;
     58 import android.os.ServiceManager;
     59 import android.os.SystemProperties;
     60 import android.os.Vibrator;
     61 import android.provider.Settings;
     62 import android.provider.Settings.System;
     63 import android.speech.RecognizerIntent;
     64 import android.telephony.PhoneStateListener;
     65 import android.telephony.TelephonyManager;
     66 import android.text.TextUtils;
     67 import android.util.Log;
     68 import android.view.KeyEvent;
     69 import android.view.VolumePanel;
     70 
     71 import com.android.internal.telephony.ITelephony;
     72 
     73 import java.io.FileDescriptor;
     74 import java.io.IOException;
     75 import java.io.PrintWriter;
     76 import java.util.ArrayList;
     77 import java.util.concurrent.ConcurrentHashMap;
     78 import java.util.HashMap;
     79 import java.util.Iterator;
     80 import java.util.List;
     81 import java.util.Map;
     82 import java.util.NoSuchElementException;
     83 import java.util.Set;
     84 import java.util.Stack;
     85 
     86 /**
     87  * The implementation of the volume manager service.
     88  * <p>
     89  * This implementation focuses on delivering a responsive UI. Most methods are
     90  * asynchronous to external calls. For example, the task of setting a volume
     91  * will update our internal state, but in a separate thread will set the system
     92  * volume and later persist to the database. Similarly, setting the ringer mode
     93  * will update the state and broadcast a change and in a separate thread later
     94  * persist the ringer mode.
     95  *
     96  * @hide
     97  */
     98 public class AudioService extends IAudioService.Stub implements OnFinished {
     99 
    100     private static final String TAG = "AudioService";
    101 
    102     /** Debug remote control client/display feature */
    103     protected static final boolean DEBUG_RC = false;
    104     /** Debug volumes */
    105     protected static final boolean DEBUG_VOL = false;
    106 
    107     /** How long to delay before persisting a change in volume/ringer mode. */
    108     private static final int PERSIST_DELAY = 500;
    109 
    110     private Context mContext;
    111     private ContentResolver mContentResolver;
    112     private boolean mVoiceCapable;
    113 
    114     /** The UI */
    115     private VolumePanel mVolumePanel;
    116 
    117     // sendMsg() flags
    118     /** If the msg is already queued, replace it with this one. */
    119     private static final int SENDMSG_REPLACE = 0;
    120     /** If the msg is already queued, ignore this one and leave the old. */
    121     private static final int SENDMSG_NOOP = 1;
    122     /** If the msg is already queued, queue this one and leave the old. */
    123     private static final int SENDMSG_QUEUE = 2;
    124 
    125     // AudioHandler messages
    126     private static final int MSG_SET_DEVICE_VOLUME = 0;
    127     private static final int MSG_PERSIST_VOLUME = 1;
    128     private static final int MSG_PERSIST_MASTER_VOLUME = 2;
    129     private static final int MSG_PERSIST_RINGER_MODE = 3;
    130     private static final int MSG_MEDIA_SERVER_DIED = 4;
    131     private static final int MSG_MEDIA_SERVER_STARTED = 5;
    132     private static final int MSG_PLAY_SOUND_EFFECT = 6;
    133     private static final int MSG_BTA2DP_DOCK_TIMEOUT = 7;
    134     private static final int MSG_LOAD_SOUND_EFFECTS = 8;
    135     private static final int MSG_SET_FORCE_USE = 9;
    136     private static final int MSG_PERSIST_MEDIABUTTONRECEIVER = 10;
    137     private static final int MSG_BT_HEADSET_CNCT_FAILED = 11;
    138     private static final int MSG_RCDISPLAY_CLEAR = 12;
    139     private static final int MSG_RCDISPLAY_UPDATE = 13;
    140     private static final int MSG_SET_ALL_VOLUMES = 14;
    141     private static final int MSG_PERSIST_MASTER_VOLUME_MUTE = 15;
    142     private static final int MSG_REPORT_NEW_ROUTES = 16;
    143     private static final int MSG_REEVALUATE_REMOTE = 17;
    144     private static final int MSG_RCC_NEW_PLAYBACK_INFO = 18;
    145     private static final int MSG_RCC_NEW_VOLUME_OBS = 19;
    146     // start of messages handled under wakelock
    147     //   these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
    148     //   and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
    149     private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 20;
    150     private static final int MSG_SET_A2DP_CONNECTION_STATE = 21;
    151     // end of messages handled under wakelock
    152 
    153     // flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be
    154     // persisted
    155     private static final int PERSIST_CURRENT = 0x1;
    156     private static final int PERSIST_LAST_AUDIBLE = 0x2;
    157 
    158     private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
    159     // Timeout for connection to bluetooth headset service
    160     private static final int BT_HEADSET_CNCT_TIMEOUT_MS = 3000;
    161 
    162     /** @see AudioSystemThread */
    163     private AudioSystemThread mAudioSystemThread;
    164     /** @see AudioHandler */
    165     private AudioHandler mAudioHandler;
    166     /** @see VolumeStreamState */
    167     private VolumeStreamState[] mStreamStates;
    168     private SettingsObserver mSettingsObserver;
    169 
    170     private int mMode;
    171     // protects mRingerMode
    172     private final Object mSettingsLock = new Object();
    173 
    174     private boolean mMediaServerOk;
    175 
    176     private SoundPool mSoundPool;
    177     private final Object mSoundEffectsLock = new Object();
    178     private static final int NUM_SOUNDPOOL_CHANNELS = 4;
    179 
    180     // Internally master volume is a float in the 0.0 - 1.0 range,
    181     // but to support integer based AudioManager API we translate it to 0 - 100
    182     private static final int MAX_MASTER_VOLUME = 100;
    183 
    184     // Maximum volume adjust steps allowed in a single batch call.
    185     private static final int MAX_BATCH_VOLUME_ADJUST_STEPS = 4;
    186 
    187     /* Sound effect file names  */
    188     private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/";
    189     private static final String[] SOUND_EFFECT_FILES = new String[] {
    190         "Effect_Tick.ogg",
    191         "KeypressStandard.ogg",
    192         "KeypressSpacebar.ogg",
    193         "KeypressDelete.ogg",
    194         "KeypressReturn.ogg"
    195     };
    196 
    197     /* Sound effect file name mapping sound effect id (AudioManager.FX_xxx) to
    198      * file index in SOUND_EFFECT_FILES[] (first column) and indicating if effect
    199      * uses soundpool (second column) */
    200     private final int[][] SOUND_EFFECT_FILES_MAP = new int[][] {
    201         {0, -1},  // FX_KEY_CLICK
    202         {0, -1},  // FX_FOCUS_NAVIGATION_UP
    203         {0, -1},  // FX_FOCUS_NAVIGATION_DOWN
    204         {0, -1},  // FX_FOCUS_NAVIGATION_LEFT
    205         {0, -1},  // FX_FOCUS_NAVIGATION_RIGHT
    206         {1, -1},  // FX_KEYPRESS_STANDARD
    207         {2, -1},  // FX_KEYPRESS_SPACEBAR
    208         {3, -1},  // FX_FOCUS_DELETE
    209         {4, -1}   // FX_FOCUS_RETURN
    210     };
    211 
    212    /** @hide Maximum volume index values for audio streams */
    213     private final int[] MAX_STREAM_VOLUME = new int[] {
    214         5,  // STREAM_VOICE_CALL
    215         7,  // STREAM_SYSTEM
    216         7,  // STREAM_RING
    217         15, // STREAM_MUSIC
    218         7,  // STREAM_ALARM
    219         7,  // STREAM_NOTIFICATION
    220         15, // STREAM_BLUETOOTH_SCO
    221         7,  // STREAM_SYSTEM_ENFORCED
    222         15, // STREAM_DTMF
    223         15  // STREAM_TTS
    224     };
    225     /* mStreamVolumeAlias[] indicates for each stream if it uses the volume settings
    226      * of another stream: This avoids multiplying the volume settings for hidden
    227      * stream types that follow other stream behavior for volume settings
    228      * NOTE: do not create loops in aliases!
    229      * Some streams alias to different streams according to device category (phone or tablet) or
    230      * use case (in call s off call...).See updateStreamVolumeAlias() for more details
    231      *  mStreamVolumeAlias contains the default aliases for a voice capable device (phone) and
    232      *  STREAM_VOLUME_ALIAS_NON_VOICE for a non voice capable device (tablet).*/
    233     private final int[] STREAM_VOLUME_ALIAS = new int[] {
    234         AudioSystem.STREAM_VOICE_CALL,      // STREAM_VOICE_CALL
    235         AudioSystem.STREAM_RING,            // STREAM_SYSTEM
    236         AudioSystem.STREAM_RING,            // STREAM_RING
    237         AudioSystem.STREAM_MUSIC,           // STREAM_MUSIC
    238         AudioSystem.STREAM_ALARM,           // STREAM_ALARM
    239         AudioSystem.STREAM_RING,            // STREAM_NOTIFICATION
    240         AudioSystem.STREAM_BLUETOOTH_SCO,   // STREAM_BLUETOOTH_SCO
    241         AudioSystem.STREAM_RING,            // STREAM_SYSTEM_ENFORCED
    242         AudioSystem.STREAM_RING,            // STREAM_DTMF
    243         AudioSystem.STREAM_MUSIC            // STREAM_TTS
    244     };
    245     private final int[] STREAM_VOLUME_ALIAS_NON_VOICE = new int[] {
    246         AudioSystem.STREAM_VOICE_CALL,      // STREAM_VOICE_CALL
    247         AudioSystem.STREAM_MUSIC,           // STREAM_SYSTEM
    248         AudioSystem.STREAM_RING,            // STREAM_RING
    249         AudioSystem.STREAM_MUSIC,           // STREAM_MUSIC
    250         AudioSystem.STREAM_ALARM,           // STREAM_ALARM
    251         AudioSystem.STREAM_RING,            // STREAM_NOTIFICATION
    252         AudioSystem.STREAM_BLUETOOTH_SCO,   // STREAM_BLUETOOTH_SCO
    253         AudioSystem.STREAM_MUSIC,           // STREAM_SYSTEM_ENFORCED
    254         AudioSystem.STREAM_MUSIC,           // STREAM_DTMF
    255         AudioSystem.STREAM_MUSIC            // STREAM_TTS
    256     };
    257     private int[] mStreamVolumeAlias;
    258 
    259     // stream names used by dumpStreamStates()
    260     private final String[] STREAM_NAMES = new String[] {
    261             "STREAM_VOICE_CALL",
    262             "STREAM_SYSTEM",
    263             "STREAM_RING",
    264             "STREAM_MUSIC",
    265             "STREAM_ALARM",
    266             "STREAM_NOTIFICATION",
    267             "STREAM_BLUETOOTH_SCO",
    268             "STREAM_SYSTEM_ENFORCED",
    269             "STREAM_DTMF",
    270             "STREAM_TTS"
    271     };
    272 
    273     private final AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() {
    274         public void onError(int error) {
    275             switch (error) {
    276             case AudioSystem.AUDIO_STATUS_SERVER_DIED:
    277                 if (mMediaServerOk) {
    278                     sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SENDMSG_NOOP, 0, 0,
    279                             null, 1500);
    280                     mMediaServerOk = false;
    281                 }
    282                 break;
    283             case AudioSystem.AUDIO_STATUS_OK:
    284                 if (!mMediaServerOk) {
    285                     sendMsg(mAudioHandler, MSG_MEDIA_SERVER_STARTED, SENDMSG_NOOP, 0, 0,
    286                             null, 0);
    287                     mMediaServerOk = true;
    288                 }
    289                 break;
    290             default:
    291                 break;
    292             }
    293        }
    294     };
    295 
    296     /**
    297      * Current ringer mode from one of {@link AudioManager#RINGER_MODE_NORMAL},
    298      * {@link AudioManager#RINGER_MODE_SILENT}, or
    299      * {@link AudioManager#RINGER_MODE_VIBRATE}.
    300      */
    301     // protected by mSettingsLock
    302     private int mRingerMode;
    303 
    304     /** @see System#MODE_RINGER_STREAMS_AFFECTED */
    305     private int mRingerModeAffectedStreams;
    306 
    307     // Streams currently muted by ringer mode
    308     private int mRingerModeMutedStreams;
    309 
    310     /** @see System#MUTE_STREAMS_AFFECTED */
    311     private int mMuteAffectedStreams;
    312 
    313     /**
    314      * NOTE: setVibrateSetting(), getVibrateSetting(), shouldVibrate() are deprecated.
    315      * mVibrateSetting is just maintained during deprecation period but vibration policy is
    316      * now only controlled by mHasVibrator and mRingerMode
    317      */
    318     private int mVibrateSetting;
    319 
    320     // Is there a vibrator
    321     private final boolean mHasVibrator;
    322 
    323     // Broadcast receiver for device connections intent broadcasts
    324     private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
    325 
    326     // Used to alter media button redirection when the phone is ringing.
    327     private boolean mIsRinging = false;
    328 
    329     // Devices currently connected
    330     private final HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>();
    331 
    332     // Forced device usage for communications
    333     private int mForcedUseForComm;
    334 
    335     // True if we have master volume support
    336     private final boolean mUseMasterVolume;
    337 
    338     private final int[] mMasterVolumeRamp;
    339 
    340     // List of binder death handlers for setMode() client processes.
    341     // The last process to have called setMode() is at the top of the list.
    342     private final ArrayList <SetModeDeathHandler> mSetModeDeathHandlers = new ArrayList <SetModeDeathHandler>();
    343 
    344     // List of clients having issued a SCO start request
    345     private final ArrayList <ScoClient> mScoClients = new ArrayList <ScoClient>();
    346 
    347     // BluetoothHeadset API to control SCO connection
    348     private BluetoothHeadset mBluetoothHeadset;
    349 
    350     // Bluetooth headset device
    351     private BluetoothDevice mBluetoothHeadsetDevice;
    352 
    353     // Indicate if SCO audio connection is currently active and if the initiator is
    354     // audio service (internal) or bluetooth headset (external)
    355     private int mScoAudioState;
    356     // SCO audio state is not active
    357     private static final int SCO_STATE_INACTIVE = 0;
    358     // SCO audio activation request waiting for headset service to connect
    359     private static final int SCO_STATE_ACTIVATE_REQ = 1;
    360     // SCO audio state is active or starting due to a local request to start a virtual call
    361     private static final int SCO_STATE_ACTIVE_INTERNAL = 3;
    362     // SCO audio deactivation request waiting for headset service to connect
    363     private static final int SCO_STATE_DEACTIVATE_REQ = 5;
    364 
    365     // SCO audio state is active due to an action in BT handsfree (either voice recognition or
    366     // in call audio)
    367     private static final int SCO_STATE_ACTIVE_EXTERNAL = 2;
    368     // Deactivation request for all SCO connections (initiated by audio mode change)
    369     // waiting for headset service to connect
    370     private static final int SCO_STATE_DEACTIVATE_EXT_REQ = 4;
    371 
    372     // Current connection state indicated by bluetooth headset
    373     private int mScoConnectionState;
    374 
    375     // true if boot sequence has been completed
    376     private boolean mBootCompleted;
    377     // listener for SoundPool sample load completion indication
    378     private SoundPoolCallback mSoundPoolCallBack;
    379     // thread for SoundPool listener
    380     private SoundPoolListenerThread mSoundPoolListenerThread;
    381     // message looper for SoundPool listener
    382     private Looper mSoundPoolLooper = null;
    383     // volume applied to sound played with playSoundEffect()
    384     private static int SOUND_EFFECT_VOLUME_DB;
    385     // getActiveStreamType() will return STREAM_NOTIFICATION during this period after a notification
    386     // stopped
    387     private static final int NOTIFICATION_VOLUME_DELAY_MS = 5000;
    388     // previous volume adjustment direction received by checkForRingerModeChange()
    389     private int mPrevVolDirection = AudioManager.ADJUST_SAME;
    390     // Keyguard manager proxy
    391     private KeyguardManager mKeyguardManager;
    392     // mVolumeControlStream is set by VolumePanel to temporarily force the stream type which volume
    393     // is controlled by Vol keys.
    394     private int  mVolumeControlStream = -1;
    395     private final Object mForceControlStreamLock = new Object();
    396     // VolumePanel is currently the only client of forceVolumeControlStream() and runs in system
    397     // server process so in theory it is not necessary to monitor the client death.
    398     // However it is good to be ready for future evolutions.
    399     private ForceControlStreamClient mForceControlStreamClient = null;
    400     // Used to play ringtones outside system_server
    401     private volatile IRingtonePlayer mRingtonePlayer;
    402 
    403     private int mDeviceOrientation = Configuration.ORIENTATION_UNDEFINED;
    404 
    405     // Request to override default use of A2DP for media.
    406     private boolean mBluetoothA2dpEnabled;
    407     private final Object mBluetoothA2dpEnabledLock = new Object();
    408 
    409     // Monitoring of audio routes.  Protected by mCurAudioRoutes.
    410     final AudioRoutesInfo mCurAudioRoutes = new AudioRoutesInfo();
    411     final RemoteCallbackList<IAudioRoutesObserver> mRoutesObservers
    412             = new RemoteCallbackList<IAudioRoutesObserver>();
    413 
    414     /**
    415      * A fake stream type to match the notion of remote media playback
    416      */
    417     public final static int STREAM_REMOTE_MUSIC = -200;
    418 
    419     ///////////////////////////////////////////////////////////////////////////
    420     // Construction
    421     ///////////////////////////////////////////////////////////////////////////
    422 
    423     /** @hide */
    424     public AudioService(Context context) {
    425         mContext = context;
    426         mContentResolver = context.getContentResolver();
    427         mVoiceCapable = mContext.getResources().getBoolean(
    428                 com.android.internal.R.bool.config_voice_capable);
    429 
    430         PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
    431         mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
    432 
    433         Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
    434         mHasVibrator = vibrator == null ? false : vibrator.hasVibrator();
    435 
    436        // Intialized volume
    437         MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = SystemProperties.getInt(
    438             "ro.config.vc_call_vol_steps",
    439            MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL]);
    440 
    441         SOUND_EFFECT_VOLUME_DB = context.getResources().getInteger(
    442                 com.android.internal.R.integer.config_soundEffectVolumeDb);
    443 
    444         mVolumePanel = new VolumePanel(context, this);
    445         mMode = AudioSystem.MODE_NORMAL;
    446         mForcedUseForComm = AudioSystem.FORCE_NONE;
    447         createAudioSystemThread();
    448         readPersistedSettings();
    449         mSettingsObserver = new SettingsObserver();
    450         updateStreamVolumeAlias(false /*updateVolumes*/);
    451         createStreamStates();
    452 
    453         mMediaServerOk = true;
    454 
    455         // Call setRingerModeInt() to apply correct mute
    456         // state on streams affected by ringer mode.
    457         mRingerModeMutedStreams = 0;
    458         setRingerModeInt(getRingerMode(), false);
    459 
    460         AudioSystem.setErrorCallback(mAudioSystemCallback);
    461 
    462         // Register for device connection intent broadcasts.
    463         IntentFilter intentFilter =
    464                 new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
    465         intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
    466         intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
    467         intentFilter.addAction(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG);
    468         intentFilter.addAction(Intent.ACTION_USB_AUDIO_DEVICE_PLUG);
    469         intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
    470         intentFilter.addAction(Intent.ACTION_SCREEN_ON);
    471         intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
    472 
    473         // Register a configuration change listener only if requested by system properties
    474         // to monitor orientation changes (off by default)
    475         if (SystemProperties.getBoolean("ro.audio.monitorOrientation", false)) {
    476             Log.v(TAG, "monitoring device orientation");
    477             intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
    478             // initialize orientation in AudioSystem
    479             setOrientationForAudioSystem();
    480         }
    481 
    482         context.registerReceiver(mReceiver, intentFilter);
    483 
    484         // Register for package removal intent broadcasts for media button receiver persistence
    485         IntentFilter pkgFilter = new IntentFilter();
    486         pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
    487         pkgFilter.addDataScheme("package");
    488         context.registerReceiver(mReceiver, pkgFilter);
    489 
    490         // Register for phone state monitoring
    491         TelephonyManager tmgr = (TelephonyManager)
    492                 context.getSystemService(Context.TELEPHONY_SERVICE);
    493         tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
    494 
    495         mUseMasterVolume = context.getResources().getBoolean(
    496                 com.android.internal.R.bool.config_useMasterVolume);
    497         restoreMasterVolume();
    498 
    499         mMasterVolumeRamp = context.getResources().getIntArray(
    500                 com.android.internal.R.array.config_masterVolumeRamp);
    501 
    502         mMainRemote = new RemotePlaybackState(-1, MAX_STREAM_VOLUME[AudioManager.STREAM_MUSIC],
    503                 MAX_STREAM_VOLUME[AudioManager.STREAM_MUSIC]);
    504         mHasRemotePlayback = false;
    505         mMainRemoteIsActive = false;
    506         postReevaluateRemote();
    507     }
    508 
    509     private void createAudioSystemThread() {
    510         mAudioSystemThread = new AudioSystemThread();
    511         mAudioSystemThread.start();
    512         waitForAudioHandlerCreation();
    513     }
    514 
    515     /** Waits for the volume handler to be created by the other thread. */
    516     private void waitForAudioHandlerCreation() {
    517         synchronized(this) {
    518             while (mAudioHandler == null) {
    519                 try {
    520                     // Wait for mAudioHandler to be set by the other thread
    521                     wait();
    522                 } catch (InterruptedException e) {
    523                     Log.e(TAG, "Interrupted while waiting on volume handler.");
    524                 }
    525             }
    526         }
    527     }
    528 
    529     private void checkAllAliasStreamVolumes() {
    530         int numStreamTypes = AudioSystem.getNumStreamTypes();
    531         for (int streamType = 0; streamType < numStreamTypes; streamType++) {
    532             if (streamType != mStreamVolumeAlias[streamType]) {
    533                 mStreamStates[streamType].
    534                                     setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]],
    535                                                   false /*lastAudible*/);
    536                 mStreamStates[streamType].
    537                                     setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]],
    538                                                   true /*lastAudible*/);
    539             }
    540             // apply stream volume
    541             if (mStreamStates[streamType].muteCount() == 0) {
    542                 mStreamStates[streamType].applyAllVolumes();
    543             }
    544         }
    545     }
    546 
    547     private void createStreamStates() {
    548         int numStreamTypes = AudioSystem.getNumStreamTypes();
    549         VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
    550 
    551         for (int i = 0; i < numStreamTypes; i++) {
    552             streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[mStreamVolumeAlias[i]], i);
    553         }
    554 
    555         checkAllAliasStreamVolumes();
    556     }
    557 
    558     private void dumpStreamStates(PrintWriter pw) {
    559         pw.println("\nStream volumes (device: index)");
    560         int numStreamTypes = AudioSystem.getNumStreamTypes();
    561         for (int i = 0; i < numStreamTypes; i++) {
    562             pw.println("- "+STREAM_NAMES[i]+":");
    563             mStreamStates[i].dump(pw);
    564             pw.println("");
    565         }
    566     }
    567 
    568 
    569     private void updateStreamVolumeAlias(boolean updateVolumes) {
    570         int dtmfStreamAlias;
    571         if (mVoiceCapable) {
    572             mStreamVolumeAlias = STREAM_VOLUME_ALIAS;
    573             dtmfStreamAlias = AudioSystem.STREAM_RING;
    574         } else {
    575             mStreamVolumeAlias = STREAM_VOLUME_ALIAS_NON_VOICE;
    576             dtmfStreamAlias = AudioSystem.STREAM_MUSIC;
    577         }
    578         if (isInCommunication()) {
    579             dtmfStreamAlias = AudioSystem.STREAM_VOICE_CALL;
    580         }
    581         mStreamVolumeAlias[AudioSystem.STREAM_DTMF] = dtmfStreamAlias;
    582         if (updateVolumes) {
    583             mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias],
    584                                                                  false /*lastAudible*/);
    585             mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias],
    586                                                                  true /*lastAudible*/);
    587             sendMsg(mAudioHandler,
    588                     MSG_SET_ALL_VOLUMES,
    589                     SENDMSG_QUEUE,
    590                     0,
    591                     0,
    592                     mStreamStates[AudioSystem.STREAM_DTMF], 0);
    593         }
    594     }
    595 
    596     private void readPersistedSettings() {
    597         final ContentResolver cr = mContentResolver;
    598 
    599         int ringerModeFromSettings =
    600                 System.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);
    601         int ringerMode = ringerModeFromSettings;
    602         // sanity check in case the settings are restored from a device with incompatible
    603         // ringer modes
    604         if (!AudioManager.isValidRingerMode(ringerMode)) {
    605             ringerMode = AudioManager.RINGER_MODE_NORMAL;
    606         }
    607         if ((ringerMode == AudioManager.RINGER_MODE_VIBRATE) && !mHasVibrator) {
    608             ringerMode = AudioManager.RINGER_MODE_SILENT;
    609         }
    610         if (ringerMode != ringerModeFromSettings) {
    611             System.putInt(cr, System.MODE_RINGER, ringerMode);
    612         }
    613         synchronized(mSettingsLock) {
    614             mRingerMode = ringerMode;
    615         }
    616 
    617         // System.VIBRATE_ON is not used any more but defaults for mVibrateSetting
    618         // are still needed while setVibrateSetting() and getVibrateSetting() are being deprecated.
    619         mVibrateSetting = getValueForVibrateSetting(0,
    620                                         AudioManager.VIBRATE_TYPE_NOTIFICATION,
    621                                         mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT
    622                                                         : AudioManager.VIBRATE_SETTING_OFF);
    623         mVibrateSetting = getValueForVibrateSetting(mVibrateSetting,
    624                                         AudioManager.VIBRATE_TYPE_RINGER,
    625                                         mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT
    626                                                         : AudioManager.VIBRATE_SETTING_OFF);
    627 
    628         // make sure settings for ringer mode are consistent with device type: non voice capable
    629         // devices (tablets) include media stream in silent mode whereas phones don't.
    630         mRingerModeAffectedStreams = Settings.System.getInt(cr,
    631                 Settings.System.MODE_RINGER_STREAMS_AFFECTED,
    632                 ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
    633                  (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)));
    634         if (mVoiceCapable) {
    635             mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
    636         } else {
    637             mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
    638         }
    639         Settings.System.putInt(cr,
    640                 Settings.System.MODE_RINGER_STREAMS_AFFECTED, mRingerModeAffectedStreams);
    641 
    642         mMuteAffectedStreams = System.getInt(cr,
    643                 System.MUTE_STREAMS_AFFECTED,
    644                 ((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM)));
    645 
    646         boolean masterMute = System.getInt(cr, System.VOLUME_MASTER_MUTE, 0) == 1;
    647         AudioSystem.setMasterMute(masterMute);
    648         broadcastMasterMuteStatus(masterMute);
    649 
    650         // Each stream will read its own persisted settings
    651 
    652         // Broadcast the sticky intent
    653         broadcastRingerMode(ringerMode);
    654 
    655         // Broadcast vibrate settings
    656         broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
    657         broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION);
    658 
    659         // Restore the default media button receiver from the system settings
    660         restoreMediaButtonReceiver();
    661     }
    662 
    663     private int rescaleIndex(int index, int srcStream, int dstStream) {
    664         return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex();
    665     }
    666 
    667     ///////////////////////////////////////////////////////////////////////////
    668     // IPC methods
    669     ///////////////////////////////////////////////////////////////////////////
    670 
    671     /** @see AudioManager#adjustVolume(int, int) */
    672     public void adjustVolume(int direction, int flags) {
    673         adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags);
    674     }
    675 
    676     /** @see AudioManager#adjustLocalOrRemoteStreamVolume(int, int) with current assumption
    677      *  on streamType: fixed to STREAM_MUSIC */
    678     public void adjustLocalOrRemoteStreamVolume(int streamType, int direction) {
    679         if (DEBUG_VOL) Log.d(TAG, "adjustLocalOrRemoteStreamVolume(dir="+direction+")");
    680         if (checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
    681             adjustRemoteVolume(AudioSystem.STREAM_MUSIC, direction, 0);
    682         } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
    683             adjustStreamVolume(AudioSystem.STREAM_MUSIC, direction, 0);
    684         }
    685     }
    686 
    687     /** @see AudioManager#adjustVolume(int, int, int) */
    688     public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
    689         if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream="+suggestedStreamType);
    690         int streamType;
    691         if (mVolumeControlStream != -1) {
    692             streamType = mVolumeControlStream;
    693         } else {
    694             streamType = getActiveStreamType(suggestedStreamType);
    695         }
    696 
    697         // Play sounds on STREAM_RING only and if lock screen is not on.
    698         if ((streamType != STREAM_REMOTE_MUSIC) &&
    699                 (flags & AudioManager.FLAG_PLAY_SOUND) != 0 &&
    700                 ((mStreamVolumeAlias[streamType] != AudioSystem.STREAM_RING)
    701                  || (mKeyguardManager != null && mKeyguardManager.isKeyguardLocked()))) {
    702             flags &= ~AudioManager.FLAG_PLAY_SOUND;
    703         }
    704 
    705         if (streamType == STREAM_REMOTE_MUSIC) {
    706             // don't play sounds for remote
    707             flags &= ~AudioManager.FLAG_PLAY_SOUND;
    708             //if (DEBUG_VOL) Log.i(TAG, "Need to adjust remote volume: calling adjustRemoteVolume()");
    709             adjustRemoteVolume(AudioSystem.STREAM_MUSIC, direction, flags);
    710         } else {
    711             adjustStreamVolume(streamType, direction, flags);
    712         }
    713     }
    714 
    715     /** @see AudioManager#adjustStreamVolume(int, int, int) */
    716     public void adjustStreamVolume(int streamType, int direction, int flags) {
    717         if (DEBUG_VOL) Log.d(TAG, "adjustStreamVolume() stream="+streamType+", dir="+direction);
    718 
    719         ensureValidDirection(direction);
    720         ensureValidStreamType(streamType);
    721 
    722         // use stream type alias here so that streams with same alias have the same behavior,
    723         // including with regard to silent mode control (e.g the use of STREAM_RING below and in
    724         // checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION)
    725         int streamTypeAlias = mStreamVolumeAlias[streamType];
    726         VolumeStreamState streamState = mStreamStates[streamTypeAlias];
    727 
    728         final int device = getDeviceForStream(streamTypeAlias);
    729         // get last audible index if stream is muted, current index otherwise
    730         final int aliasIndex = streamState.getIndex(device,
    731                                                   (streamState.muteCount() != 0) /* lastAudible */);
    732         boolean adjustVolume = true;
    733 
    734         // convert one UI step (+/-1) into a number of internal units on the stream alias
    735         int step = rescaleIndex(10, streamType, streamTypeAlias);
    736 
    737         // If either the client forces allowing ringer modes for this adjustment,
    738         // or the stream type is one that is affected by ringer modes
    739         if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
    740                 (streamTypeAlias == getMasterStreamType())) {
    741             int ringerMode = getRingerMode();
    742             // do not vibrate if already in vibrate mode
    743             if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
    744                 flags &= ~AudioManager.FLAG_VIBRATE;
    745             }
    746             // Check if the ringer mode changes with this volume adjustment. If
    747             // it does, it will handle adjusting the volume, so we won't below
    748             adjustVolume = checkForRingerModeChange(aliasIndex, direction, step);
    749             if ((streamTypeAlias == getMasterStreamType()) &&
    750                     (mRingerMode == AudioManager.RINGER_MODE_SILENT)) {
    751                 streamState.setLastAudibleIndex(0, device);
    752             }
    753         }
    754 
    755         // If stream is muted, adjust last audible index only
    756         int index;
    757         final int oldIndex = mStreamStates[streamType].getIndex(device,
    758                 (mStreamStates[streamType].muteCount() != 0) /* lastAudible */);
    759 
    760         if (streamState.muteCount() != 0) {
    761             if (adjustVolume) {
    762                 // Post a persist volume msg
    763                 // no need to persist volume on all streams sharing the same alias
    764                 streamState.adjustLastAudibleIndex(direction * step, device);
    765                 sendMsg(mAudioHandler,
    766                         MSG_PERSIST_VOLUME,
    767                         SENDMSG_QUEUE,
    768                         PERSIST_LAST_AUDIBLE,
    769                         device,
    770                         streamState,
    771                         PERSIST_DELAY);
    772             }
    773             index = mStreamStates[streamType].getIndex(device, true  /* lastAudible */);
    774         } else {
    775             if (adjustVolume && streamState.adjustIndex(direction * step, device)) {
    776                 // Post message to set system volume (it in turn will post a message
    777                 // to persist). Do not change volume if stream is muted.
    778                 sendMsg(mAudioHandler,
    779                         MSG_SET_DEVICE_VOLUME,
    780                         SENDMSG_QUEUE,
    781                         device,
    782                         0,
    783                         streamState,
    784                         0);
    785             }
    786             index = mStreamStates[streamType].getIndex(device, false  /* lastAudible */);
    787         }
    788 
    789         sendVolumeUpdate(streamType, oldIndex, index, flags);
    790     }
    791 
    792     /** @see AudioManager#adjustMasterVolume(int) */
    793     public void adjustMasterVolume(int steps, int flags) {
    794         ensureValidSteps(steps);
    795         int volume = Math.round(AudioSystem.getMasterVolume() * MAX_MASTER_VOLUME);
    796         int delta = 0;
    797         int numSteps = Math.abs(steps);
    798         int direction = steps > 0 ? AudioManager.ADJUST_RAISE : AudioManager.ADJUST_LOWER;
    799         for (int i = 0; i < numSteps; ++i) {
    800             delta = findVolumeDelta(direction, volume);
    801             volume += delta;
    802         }
    803 
    804         //Log.d(TAG, "adjustMasterVolume volume: " + volume + " steps: " + steps);
    805         setMasterVolume(volume, flags);
    806     }
    807 
    808     /** @see AudioManager#setStreamVolume(int, int, int) */
    809     public void setStreamVolume(int streamType, int index, int flags) {
    810         ensureValidStreamType(streamType);
    811         VolumeStreamState streamState = mStreamStates[mStreamVolumeAlias[streamType]];
    812 
    813         final int device = getDeviceForStream(streamType);
    814         // get last audible index if stream is muted, current index otherwise
    815         final int oldIndex = streamState.getIndex(device,
    816                                                   (streamState.muteCount() != 0) /* lastAudible */);
    817 
    818         index = rescaleIndex(index * 10, streamType, mStreamVolumeAlias[streamType]);
    819 
    820         // setting volume on master stream type also controls silent mode
    821         if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
    822                 (mStreamVolumeAlias[streamType] == getMasterStreamType())) {
    823             int newRingerMode;
    824             if (index == 0) {
    825                 newRingerMode = mHasVibrator ? AudioManager.RINGER_MODE_VIBRATE
    826                                               : AudioManager.RINGER_MODE_SILENT;
    827                 setStreamVolumeInt(mStreamVolumeAlias[streamType],
    828                                    index,
    829                                    device,
    830                                    false,
    831                                    true);
    832             } else {
    833                 newRingerMode = AudioManager.RINGER_MODE_NORMAL;
    834             }
    835             setRingerMode(newRingerMode);
    836         }
    837 
    838         setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, false, true);
    839         // get last audible index if stream is muted, current index otherwise
    840         index = mStreamStates[streamType].getIndex(device,
    841                                  (mStreamStates[streamType].muteCount() != 0) /* lastAudible */);
    842 
    843         sendVolumeUpdate(streamType, oldIndex, index, flags);
    844     }
    845 
    846     /** @see AudioManager#forceVolumeControlStream(int) */
    847     public void forceVolumeControlStream(int streamType, IBinder cb) {
    848         synchronized(mForceControlStreamLock) {
    849             mVolumeControlStream = streamType;
    850             if (mVolumeControlStream == -1) {
    851                 if (mForceControlStreamClient != null) {
    852                     mForceControlStreamClient.release();
    853                     mForceControlStreamClient = null;
    854                 }
    855             } else {
    856                 mForceControlStreamClient = new ForceControlStreamClient(cb);
    857             }
    858         }
    859     }
    860 
    861     private class ForceControlStreamClient implements IBinder.DeathRecipient {
    862         private IBinder mCb; // To be notified of client's death
    863 
    864         ForceControlStreamClient(IBinder cb) {
    865             if (cb != null) {
    866                 try {
    867                     cb.linkToDeath(this, 0);
    868                 } catch (RemoteException e) {
    869                     // Client has died!
    870                     Log.w(TAG, "ForceControlStreamClient() could not link to "+cb+" binder death");
    871                     cb = null;
    872                 }
    873             }
    874             mCb = cb;
    875         }
    876 
    877         public void binderDied() {
    878             synchronized(mForceControlStreamLock) {
    879                 Log.w(TAG, "SCO client died");
    880                 if (mForceControlStreamClient != this) {
    881                     Log.w(TAG, "unregistered control stream client died");
    882                 } else {
    883                     mForceControlStreamClient = null;
    884                     mVolumeControlStream = -1;
    885                 }
    886             }
    887         }
    888 
    889         public void release() {
    890             if (mCb != null) {
    891                 mCb.unlinkToDeath(this, 0);
    892                 mCb = null;
    893             }
    894         }
    895     }
    896 
    897     private int findVolumeDelta(int direction, int volume) {
    898         int delta = 0;
    899         if (direction == AudioManager.ADJUST_RAISE) {
    900             if (volume == MAX_MASTER_VOLUME) {
    901                 return 0;
    902             }
    903             // This is the default value if we make it to the end
    904             delta = mMasterVolumeRamp[1];
    905             // If we're raising the volume move down the ramp array until we
    906             // find the volume we're above and use that groups delta.
    907             for (int i = mMasterVolumeRamp.length - 1; i > 1; i -= 2) {
    908                 if (volume >= mMasterVolumeRamp[i - 1]) {
    909                     delta = mMasterVolumeRamp[i];
    910                     break;
    911                 }
    912             }
    913         } else if (direction == AudioManager.ADJUST_LOWER){
    914             if (volume == 0) {
    915                 return 0;
    916             }
    917             int length = mMasterVolumeRamp.length;
    918             // This is the default value if we make it to the end
    919             delta = -mMasterVolumeRamp[length - 1];
    920             // If we're lowering the volume move up the ramp array until we
    921             // find the volume we're below and use the group below it's delta
    922             for (int i = 2; i < length; i += 2) {
    923                 if (volume <= mMasterVolumeRamp[i]) {
    924                     delta = -mMasterVolumeRamp[i - 1];
    925                     break;
    926                 }
    927             }
    928         }
    929         return delta;
    930     }
    931 
    932     // UI update and Broadcast Intent
    933     private void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags) {
    934         if (!mVoiceCapable && (streamType == AudioSystem.STREAM_RING)) {
    935             streamType = AudioSystem.STREAM_NOTIFICATION;
    936         }
    937 
    938         mVolumePanel.postVolumeChanged(streamType, flags);
    939 
    940         oldIndex = (oldIndex + 5) / 10;
    941         index = (index + 5) / 10;
    942         Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
    943         intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
    944         intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index);
    945         intent.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex);
    946         mContext.sendBroadcast(intent);
    947     }
    948 
    949     // UI update and Broadcast Intent
    950     private void sendMasterVolumeUpdate(int flags, int oldVolume, int newVolume) {
    951         mVolumePanel.postMasterVolumeChanged(flags);
    952 
    953         Intent intent = new Intent(AudioManager.MASTER_VOLUME_CHANGED_ACTION);
    954         intent.putExtra(AudioManager.EXTRA_PREV_MASTER_VOLUME_VALUE, oldVolume);
    955         intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_VALUE, newVolume);
    956         mContext.sendBroadcast(intent);
    957     }
    958 
    959     // UI update and Broadcast Intent
    960     private void sendMasterMuteUpdate(boolean muted, int flags) {
    961         mVolumePanel.postMasterMuteChanged(flags);
    962         broadcastMasterMuteStatus(muted);
    963     }
    964 
    965     private void broadcastMasterMuteStatus(boolean muted) {
    966         Intent intent = new Intent(AudioManager.MASTER_MUTE_CHANGED_ACTION);
    967         intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_MUTED, muted);
    968         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
    969                 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
    970         long origCallerIdentityToken = Binder.clearCallingIdentity();
    971         mContext.sendStickyBroadcast(intent);
    972         Binder.restoreCallingIdentity(origCallerIdentityToken);
    973     }
    974 
    975     /**
    976      * Sets the stream state's index, and posts a message to set system volume.
    977      * This will not call out to the UI. Assumes a valid stream type.
    978      *
    979      * @param streamType Type of the stream
    980      * @param index Desired volume index of the stream
    981      * @param device the device whose volume must be changed
    982      * @param force If true, set the volume even if the desired volume is same
    983      * as the current volume.
    984      * @param lastAudible If true, stores new index as last audible one
    985      */
    986     private void setStreamVolumeInt(int streamType,
    987                                     int index,
    988                                     int device,
    989                                     boolean force,
    990                                     boolean lastAudible) {
    991         VolumeStreamState streamState = mStreamStates[streamType];
    992 
    993         // If stream is muted, set last audible index only
    994         if (streamState.muteCount() != 0) {
    995             // Do not allow last audible index to be 0
    996             if (index != 0) {
    997                 streamState.setLastAudibleIndex(index, device);
    998                 // Post a persist volume msg
    999                 sendMsg(mAudioHandler,
   1000                         MSG_PERSIST_VOLUME,
   1001                         SENDMSG_QUEUE,
   1002                         PERSIST_LAST_AUDIBLE,
   1003                         device,
   1004                         streamState,
   1005                         PERSIST_DELAY);
   1006             }
   1007         } else {
   1008             if (streamState.setIndex(index, device, lastAudible) || force) {
   1009                 // Post message to set system volume (it in turn will post a message
   1010                 // to persist).
   1011                 sendMsg(mAudioHandler,
   1012                         MSG_SET_DEVICE_VOLUME,
   1013                         SENDMSG_QUEUE,
   1014                         device,
   1015                         0,
   1016                         streamState,
   1017                         0);
   1018             }
   1019         }
   1020     }
   1021 
   1022     /** @see AudioManager#setStreamSolo(int, boolean) */
   1023     public void setStreamSolo(int streamType, boolean state, IBinder cb) {
   1024         for (int stream = 0; stream < mStreamStates.length; stream++) {
   1025             if (!isStreamAffectedByMute(stream) || stream == streamType) continue;
   1026             // Bring back last audible volume
   1027             mStreamStates[stream].mute(cb, state);
   1028          }
   1029     }
   1030 
   1031     /** @see AudioManager#setStreamMute(int, boolean) */
   1032     public void setStreamMute(int streamType, boolean state, IBinder cb) {
   1033         if (isStreamAffectedByMute(streamType)) {
   1034             mStreamStates[streamType].mute(cb, state);
   1035         }
   1036     }
   1037 
   1038     /** get stream mute state. */
   1039     public boolean isStreamMute(int streamType) {
   1040         return (mStreamStates[streamType].muteCount() != 0);
   1041     }
   1042 
   1043     /** @see AudioManager#setMasterMute(boolean, IBinder) */
   1044     public void setMasterMute(boolean state, int flags, IBinder cb) {
   1045         if (state != AudioSystem.getMasterMute()) {
   1046             AudioSystem.setMasterMute(state);
   1047             // Post a persist master volume msg
   1048             sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME_MUTE, SENDMSG_REPLACE, state ? 1
   1049                     : 0, 0, null, PERSIST_DELAY);
   1050             sendMasterMuteUpdate(state, flags);
   1051         }
   1052     }
   1053 
   1054     /** get master mute state. */
   1055     public boolean isMasterMute() {
   1056         return AudioSystem.getMasterMute();
   1057     }
   1058 
   1059     /** @see AudioManager#getStreamVolume(int) */
   1060     public int getStreamVolume(int streamType) {
   1061         ensureValidStreamType(streamType);
   1062         int device = getDeviceForStream(streamType);
   1063         return (mStreamStates[streamType].getIndex(device, false  /* lastAudible */) + 5) / 10;
   1064     }
   1065 
   1066     public int getMasterVolume() {
   1067         if (isMasterMute()) return 0;
   1068         return getLastAudibleMasterVolume();
   1069     }
   1070 
   1071     public void setMasterVolume(int volume, int flags) {
   1072         if (volume < 0) {
   1073             volume = 0;
   1074         } else if (volume > MAX_MASTER_VOLUME) {
   1075             volume = MAX_MASTER_VOLUME;
   1076         }
   1077         doSetMasterVolume((float)volume / MAX_MASTER_VOLUME, flags);
   1078     }
   1079 
   1080     private void doSetMasterVolume(float volume, int flags) {
   1081         // don't allow changing master volume when muted
   1082         if (!AudioSystem.getMasterMute()) {
   1083             int oldVolume = getMasterVolume();
   1084             AudioSystem.setMasterVolume(volume);
   1085 
   1086             int newVolume = getMasterVolume();
   1087             if (newVolume != oldVolume) {
   1088                 // Post a persist master volume msg
   1089                 sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME, SENDMSG_REPLACE,
   1090                         Math.round(volume * (float)1000.0), 0, null, PERSIST_DELAY);
   1091             }
   1092             // Send the volume update regardless whether there was a change.
   1093             sendMasterVolumeUpdate(flags, oldVolume, newVolume);
   1094         }
   1095     }
   1096 
   1097     /** @see AudioManager#getStreamMaxVolume(int) */
   1098     public int getStreamMaxVolume(int streamType) {
   1099         ensureValidStreamType(streamType);
   1100         return (mStreamStates[streamType].getMaxIndex() + 5) / 10;
   1101     }
   1102 
   1103     public int getMasterMaxVolume() {
   1104         return MAX_MASTER_VOLUME;
   1105     }
   1106 
   1107     /** Get last audible volume before stream was muted. */
   1108     public int getLastAudibleStreamVolume(int streamType) {
   1109         ensureValidStreamType(streamType);
   1110         int device = getDeviceForStream(streamType);
   1111         return (mStreamStates[streamType].getIndex(device, true  /* lastAudible */) + 5) / 10;
   1112     }
   1113 
   1114     /** Get last audible master volume before it was muted. */
   1115     public int getLastAudibleMasterVolume() {
   1116         return Math.round(AudioSystem.getMasterVolume() * MAX_MASTER_VOLUME);
   1117     }
   1118 
   1119     /** @see AudioManager#getMasterStreamType(int) */
   1120     public int getMasterStreamType() {
   1121         if (mVoiceCapable) {
   1122             return AudioSystem.STREAM_RING;
   1123         } else {
   1124             return AudioSystem.STREAM_MUSIC;
   1125         }
   1126     }
   1127 
   1128     /** @see AudioManager#getRingerMode() */
   1129     public int getRingerMode() {
   1130         synchronized(mSettingsLock) {
   1131             return mRingerMode;
   1132         }
   1133     }
   1134 
   1135     private void ensureValidRingerMode(int ringerMode) {
   1136         if (!AudioManager.isValidRingerMode(ringerMode)) {
   1137             throw new IllegalArgumentException("Bad ringer mode " + ringerMode);
   1138         }
   1139     }
   1140 
   1141     /** @see AudioManager#setRingerMode(int) */
   1142     public void setRingerMode(int ringerMode) {
   1143         if ((ringerMode == AudioManager.RINGER_MODE_VIBRATE) && !mHasVibrator) {
   1144             ringerMode = AudioManager.RINGER_MODE_SILENT;
   1145         }
   1146         if (ringerMode != getRingerMode()) {
   1147             setRingerModeInt(ringerMode, true);
   1148             // Send sticky broadcast
   1149             broadcastRingerMode(ringerMode);
   1150         }
   1151     }
   1152 
   1153     private void setRingerModeInt(int ringerMode, boolean persist) {
   1154         synchronized(mSettingsLock) {
   1155             mRingerMode = ringerMode;
   1156         }
   1157 
   1158         // Mute stream if not previously muted by ringer mode and ringer mode
   1159         // is not RINGER_MODE_NORMAL and stream is affected by ringer mode.
   1160         // Unmute stream if previously muted by ringer mode and ringer mode
   1161         // is RINGER_MODE_NORMAL or stream is not affected by ringer mode.
   1162         int numStreamTypes = AudioSystem.getNumStreamTypes();
   1163         for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
   1164             if (isStreamMutedByRingerMode(streamType)) {
   1165                 if (!isStreamAffectedByRingerMode(streamType) ||
   1166                     ringerMode == AudioManager.RINGER_MODE_NORMAL) {
   1167                     // ring and notifications volume should never be 0 when not silenced
   1168                     // on voice capable devices
   1169                     if (mVoiceCapable &&
   1170                             mStreamVolumeAlias[streamType] == AudioSystem.STREAM_RING) {
   1171                         synchronized (mStreamStates[streamType]) {
   1172                             Set set = mStreamStates[streamType].mLastAudibleIndex.entrySet();
   1173                             Iterator i = set.iterator();
   1174                             while (i.hasNext()) {
   1175                                 Map.Entry entry = (Map.Entry)i.next();
   1176                                 if ((Integer)entry.getValue() == 0) {
   1177                                     entry.setValue(10);
   1178                                 }
   1179                             }
   1180                         }
   1181                     }
   1182                     mStreamStates[streamType].mute(null, false);
   1183                     mRingerModeMutedStreams &= ~(1 << streamType);
   1184                 }
   1185             } else {
   1186                 if (isStreamAffectedByRingerMode(streamType) &&
   1187                     ringerMode != AudioManager.RINGER_MODE_NORMAL) {
   1188                    mStreamStates[streamType].mute(null, true);
   1189                    mRingerModeMutedStreams |= (1 << streamType);
   1190                }
   1191             }
   1192         }
   1193 
   1194         // Post a persist ringer mode msg
   1195         if (persist) {
   1196             sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE,
   1197                     SENDMSG_REPLACE, 0, 0, null, PERSIST_DELAY);
   1198         }
   1199     }
   1200 
   1201     private void restoreMasterVolume() {
   1202         if (mUseMasterVolume) {
   1203             float volume = Settings.System.getFloat(mContentResolver,
   1204                     Settings.System.VOLUME_MASTER, -1.0f);
   1205             if (volume >= 0.0f) {
   1206                 AudioSystem.setMasterVolume(volume);
   1207             }
   1208         }
   1209     }
   1210 
   1211     /** @see AudioManager#shouldVibrate(int) */
   1212     public boolean shouldVibrate(int vibrateType) {
   1213         if (!mHasVibrator) return false;
   1214 
   1215         switch (getVibrateSetting(vibrateType)) {
   1216 
   1217             case AudioManager.VIBRATE_SETTING_ON:
   1218                 return getRingerMode() != AudioManager.RINGER_MODE_SILENT;
   1219 
   1220             case AudioManager.VIBRATE_SETTING_ONLY_SILENT:
   1221                 return getRingerMode() == AudioManager.RINGER_MODE_VIBRATE;
   1222 
   1223             case AudioManager.VIBRATE_SETTING_OFF:
   1224                 // return false, even for incoming calls
   1225                 return false;
   1226 
   1227             default:
   1228                 return false;
   1229         }
   1230     }
   1231 
   1232     /** @see AudioManager#getVibrateSetting(int) */
   1233     public int getVibrateSetting(int vibrateType) {
   1234         if (!mHasVibrator) return AudioManager.VIBRATE_SETTING_OFF;
   1235         return (mVibrateSetting >> (vibrateType * 2)) & 3;
   1236     }
   1237 
   1238     /** @see AudioManager#setVibrateSetting(int, int) */
   1239     public void setVibrateSetting(int vibrateType, int vibrateSetting) {
   1240 
   1241         if (!mHasVibrator) return;
   1242 
   1243         mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, vibrateType, vibrateSetting);
   1244 
   1245         // Broadcast change
   1246         broadcastVibrateSetting(vibrateType);
   1247 
   1248     }
   1249 
   1250     /**
   1251      * @see #setVibrateSetting(int, int)
   1252      */
   1253     public static int getValueForVibrateSetting(int existingValue, int vibrateType,
   1254             int vibrateSetting) {
   1255 
   1256         // First clear the existing setting. Each vibrate type has two bits in
   1257         // the value. Note '3' is '11' in binary.
   1258         existingValue &= ~(3 << (vibrateType * 2));
   1259 
   1260         // Set into the old value
   1261         existingValue |= (vibrateSetting & 3) << (vibrateType * 2);
   1262 
   1263         return existingValue;
   1264     }
   1265 
   1266     private class SetModeDeathHandler implements IBinder.DeathRecipient {
   1267         private IBinder mCb; // To be notified of client's death
   1268         private int mPid;
   1269         private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client
   1270 
   1271         SetModeDeathHandler(IBinder cb, int pid) {
   1272             mCb = cb;
   1273             mPid = pid;
   1274         }
   1275 
   1276         public void binderDied() {
   1277             int newModeOwnerPid = 0;
   1278             synchronized(mSetModeDeathHandlers) {
   1279                 Log.w(TAG, "setMode() client died");
   1280                 int index = mSetModeDeathHandlers.indexOf(this);
   1281                 if (index < 0) {
   1282                     Log.w(TAG, "unregistered setMode() client died");
   1283                 } else {
   1284                     newModeOwnerPid = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid);
   1285                 }
   1286             }
   1287             // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
   1288             // SCO connections not started by the application changing the mode
   1289             if (newModeOwnerPid != 0) {
   1290                  disconnectBluetoothSco(newModeOwnerPid);
   1291             }
   1292         }
   1293 
   1294         public int getPid() {
   1295             return mPid;
   1296         }
   1297 
   1298         public void setMode(int mode) {
   1299             mMode = mode;
   1300         }
   1301 
   1302         public int getMode() {
   1303             return mMode;
   1304         }
   1305 
   1306         public IBinder getBinder() {
   1307             return mCb;
   1308         }
   1309     }
   1310 
   1311     /** @see AudioManager#setMode(int) */
   1312     public void setMode(int mode, IBinder cb) {
   1313         if (!checkAudioSettingsPermission("setMode()")) {
   1314             return;
   1315         }
   1316 
   1317         if (mode < AudioSystem.MODE_CURRENT || mode >= AudioSystem.NUM_MODES) {
   1318             return;
   1319         }
   1320 
   1321         int newModeOwnerPid = 0;
   1322         synchronized(mSetModeDeathHandlers) {
   1323             if (mode == AudioSystem.MODE_CURRENT) {
   1324                 mode = mMode;
   1325             }
   1326             newModeOwnerPid = setModeInt(mode, cb, Binder.getCallingPid());
   1327         }
   1328         // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
   1329         // SCO connections not started by the application changing the mode
   1330         if (newModeOwnerPid != 0) {
   1331              disconnectBluetoothSco(newModeOwnerPid);
   1332         }
   1333     }
   1334 
   1335     // must be called synchronized on mSetModeDeathHandlers
   1336     // setModeInt() returns a valid PID if the audio mode was successfully set to
   1337     // any mode other than NORMAL.
   1338     int setModeInt(int mode, IBinder cb, int pid) {
   1339         int newModeOwnerPid = 0;
   1340         if (cb == null) {
   1341             Log.e(TAG, "setModeInt() called with null binder");
   1342             return newModeOwnerPid;
   1343         }
   1344 
   1345         SetModeDeathHandler hdlr = null;
   1346         Iterator iter = mSetModeDeathHandlers.iterator();
   1347         while (iter.hasNext()) {
   1348             SetModeDeathHandler h = (SetModeDeathHandler)iter.next();
   1349             if (h.getPid() == pid) {
   1350                 hdlr = h;
   1351                 // Remove from client list so that it is re-inserted at top of list
   1352                 iter.remove();
   1353                 hdlr.getBinder().unlinkToDeath(hdlr, 0);
   1354                 break;
   1355             }
   1356         }
   1357         int status = AudioSystem.AUDIO_STATUS_OK;
   1358         do {
   1359             if (mode == AudioSystem.MODE_NORMAL) {
   1360                 // get new mode from client at top the list if any
   1361                 if (!mSetModeDeathHandlers.isEmpty()) {
   1362                     hdlr = mSetModeDeathHandlers.get(0);
   1363                     cb = hdlr.getBinder();
   1364                     mode = hdlr.getMode();
   1365                 }
   1366             } else {
   1367                 if (hdlr == null) {
   1368                     hdlr = new SetModeDeathHandler(cb, pid);
   1369                 }
   1370                 // Register for client death notification
   1371                 try {
   1372                     cb.linkToDeath(hdlr, 0);
   1373                 } catch (RemoteException e) {
   1374                     // Client has died!
   1375                     Log.w(TAG, "setMode() could not link to "+cb+" binder death");
   1376                 }
   1377 
   1378                 // Last client to call setMode() is always at top of client list
   1379                 // as required by SetModeDeathHandler.binderDied()
   1380                 mSetModeDeathHandlers.add(0, hdlr);
   1381                 hdlr.setMode(mode);
   1382             }
   1383 
   1384             if (mode != mMode) {
   1385                 status = AudioSystem.setPhoneState(mode);
   1386                 if (status == AudioSystem.AUDIO_STATUS_OK) {
   1387                     mMode = mode;
   1388                 } else {
   1389                     if (hdlr != null) {
   1390                         mSetModeDeathHandlers.remove(hdlr);
   1391                         cb.unlinkToDeath(hdlr, 0);
   1392                     }
   1393                     // force reading new top of mSetModeDeathHandlers stack
   1394                     mode = AudioSystem.MODE_NORMAL;
   1395                 }
   1396             } else {
   1397                 status = AudioSystem.AUDIO_STATUS_OK;
   1398             }
   1399         } while (status != AudioSystem.AUDIO_STATUS_OK && !mSetModeDeathHandlers.isEmpty());
   1400 
   1401         if (status == AudioSystem.AUDIO_STATUS_OK) {
   1402             if (mode != AudioSystem.MODE_NORMAL) {
   1403                 if (mSetModeDeathHandlers.isEmpty()) {
   1404                     Log.e(TAG, "setMode() different from MODE_NORMAL with empty mode client stack");
   1405                 } else {
   1406                     newModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
   1407                 }
   1408             }
   1409             int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
   1410             if (streamType == STREAM_REMOTE_MUSIC) {
   1411                 // here handle remote media playback the same way as local playback
   1412                 streamType = AudioManager.STREAM_MUSIC;
   1413             }
   1414             int device = getDeviceForStream(streamType);
   1415             int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device, false);
   1416             setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true, false);
   1417 
   1418             updateStreamVolumeAlias(true /*updateVolumes*/);
   1419         }
   1420         return newModeOwnerPid;
   1421     }
   1422 
   1423     /** @see AudioManager#getMode() */
   1424     public int getMode() {
   1425         return mMode;
   1426     }
   1427 
   1428     /** @see AudioManager#playSoundEffect(int) */
   1429     public void playSoundEffect(int effectType) {
   1430         sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_NOOP,
   1431                 effectType, -1, null, 0);
   1432     }
   1433 
   1434     /** @see AudioManager#playSoundEffect(int, float) */
   1435     public void playSoundEffectVolume(int effectType, float volume) {
   1436         loadSoundEffects();
   1437         sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_NOOP,
   1438                 effectType, (int) (volume * 1000), null, 0);
   1439     }
   1440 
   1441     /**
   1442      * Loads samples into the soundpool.
   1443      * This method must be called at first when sound effects are enabled
   1444      */
   1445     public boolean loadSoundEffects() {
   1446         int status;
   1447 
   1448         synchronized (mSoundEffectsLock) {
   1449             if (!mBootCompleted) {
   1450                 Log.w(TAG, "loadSoundEffects() called before boot complete");
   1451                 return false;
   1452             }
   1453 
   1454             if (mSoundPool != null) {
   1455                 return true;
   1456             }
   1457             mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
   1458 
   1459             try {
   1460                 mSoundPoolCallBack = null;
   1461                 mSoundPoolListenerThread = new SoundPoolListenerThread();
   1462                 mSoundPoolListenerThread.start();
   1463                 // Wait for mSoundPoolCallBack to be set by the other thread
   1464                 mSoundEffectsLock.wait();
   1465             } catch (InterruptedException e) {
   1466                 Log.w(TAG, "Interrupted while waiting sound pool listener thread.");
   1467             }
   1468 
   1469             if (mSoundPoolCallBack == null) {
   1470                 Log.w(TAG, "loadSoundEffects() could not create SoundPool listener or thread");
   1471                 if (mSoundPoolLooper != null) {
   1472                     mSoundPoolLooper.quit();
   1473                     mSoundPoolLooper = null;
   1474                 }
   1475                 mSoundPoolListenerThread = null;
   1476                 mSoundPool.release();
   1477                 mSoundPool = null;
   1478                 return false;
   1479             }
   1480             /*
   1481              * poolId table: The value -1 in this table indicates that corresponding
   1482              * file (same index in SOUND_EFFECT_FILES[] has not been loaded.
   1483              * Once loaded, the value in poolId is the sample ID and the same
   1484              * sample can be reused for another effect using the same file.
   1485              */
   1486             int[] poolId = new int[SOUND_EFFECT_FILES.length];
   1487             for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
   1488                 poolId[fileIdx] = -1;
   1489             }
   1490             /*
   1491              * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded.
   1492              * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0:
   1493              * this indicates we have a valid sample loaded for this effect.
   1494              */
   1495 
   1496             int lastSample = 0;
   1497             for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
   1498                 // Do not load sample if this effect uses the MediaPlayer
   1499                 if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) {
   1500                     continue;
   1501                 }
   1502                 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
   1503                     String filePath = Environment.getRootDirectory()
   1504                             + SOUND_EFFECTS_PATH
   1505                             + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effect][0]];
   1506                     int sampleId = mSoundPool.load(filePath, 0);
   1507                     if (sampleId <= 0) {
   1508                         Log.w(TAG, "Soundpool could not load file: "+filePath);
   1509                     } else {
   1510                         SOUND_EFFECT_FILES_MAP[effect][1] = sampleId;
   1511                         poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId;
   1512                         lastSample = sampleId;
   1513                     }
   1514                 } else {
   1515                     SOUND_EFFECT_FILES_MAP[effect][1] = poolId[SOUND_EFFECT_FILES_MAP[effect][0]];
   1516                 }
   1517             }
   1518             // wait for all samples to be loaded
   1519             if (lastSample != 0) {
   1520                 mSoundPoolCallBack.setLastSample(lastSample);
   1521 
   1522                 try {
   1523                     mSoundEffectsLock.wait();
   1524                     status = mSoundPoolCallBack.status();
   1525                 } catch (java.lang.InterruptedException e) {
   1526                     Log.w(TAG, "Interrupted while waiting sound pool callback.");
   1527                     status = -1;
   1528                 }
   1529             } else {
   1530                 status = -1;
   1531             }
   1532 
   1533             if (mSoundPoolLooper != null) {
   1534                 mSoundPoolLooper.quit();
   1535                 mSoundPoolLooper = null;
   1536             }
   1537             mSoundPoolListenerThread = null;
   1538             if (status != 0) {
   1539                 Log.w(TAG,
   1540                         "loadSoundEffects(), Error "
   1541                                 + ((lastSample != 0) ? mSoundPoolCallBack.status() : -1)
   1542                                 + " while loading samples");
   1543                 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
   1544                     if (SOUND_EFFECT_FILES_MAP[effect][1] > 0) {
   1545                         SOUND_EFFECT_FILES_MAP[effect][1] = -1;
   1546                     }
   1547                 }
   1548 
   1549                 mSoundPool.release();
   1550                 mSoundPool = null;
   1551             }
   1552         }
   1553         return (status == 0);
   1554     }
   1555 
   1556     /**
   1557      *  Unloads samples from the sound pool.
   1558      *  This method can be called to free some memory when
   1559      *  sound effects are disabled.
   1560      */
   1561     public void unloadSoundEffects() {
   1562         synchronized (mSoundEffectsLock) {
   1563             if (mSoundPool == null) {
   1564                 return;
   1565             }
   1566 
   1567             mAudioHandler.removeMessages(MSG_LOAD_SOUND_EFFECTS);
   1568             mAudioHandler.removeMessages(MSG_PLAY_SOUND_EFFECT);
   1569 
   1570             int[] poolId = new int[SOUND_EFFECT_FILES.length];
   1571             for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
   1572                 poolId[fileIdx] = 0;
   1573             }
   1574 
   1575             for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
   1576                 if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) {
   1577                     continue;
   1578                 }
   1579                 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) {
   1580                     mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]);
   1581                     SOUND_EFFECT_FILES_MAP[effect][1] = -1;
   1582                     poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1;
   1583                 }
   1584             }
   1585             mSoundPool.release();
   1586             mSoundPool = null;
   1587         }
   1588     }
   1589 
   1590     class SoundPoolListenerThread extends Thread {
   1591         public SoundPoolListenerThread() {
   1592             super("SoundPoolListenerThread");
   1593         }
   1594 
   1595         @Override
   1596         public void run() {
   1597 
   1598             Looper.prepare();
   1599             mSoundPoolLooper = Looper.myLooper();
   1600 
   1601             synchronized (mSoundEffectsLock) {
   1602                 if (mSoundPool != null) {
   1603                     mSoundPoolCallBack = new SoundPoolCallback();
   1604                     mSoundPool.setOnLoadCompleteListener(mSoundPoolCallBack);
   1605                 }
   1606                 mSoundEffectsLock.notify();
   1607             }
   1608             Looper.loop();
   1609         }
   1610     }
   1611 
   1612     private final class SoundPoolCallback implements
   1613             android.media.SoundPool.OnLoadCompleteListener {
   1614 
   1615         int mStatus;
   1616         int mLastSample;
   1617 
   1618         public int status() {
   1619             return mStatus;
   1620         }
   1621 
   1622         public void setLastSample(int sample) {
   1623             mLastSample = sample;
   1624         }
   1625 
   1626         public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
   1627             synchronized (mSoundEffectsLock) {
   1628                 if (status != 0) {
   1629                     mStatus = status;
   1630                 }
   1631                 if (sampleId == mLastSample) {
   1632                     mSoundEffectsLock.notify();
   1633                 }
   1634             }
   1635         }
   1636     }
   1637 
   1638     /** @see AudioManager#reloadAudioSettings() */
   1639     public void reloadAudioSettings() {
   1640         // restore ringer mode, ringer mode affected streams, mute affected streams and vibrate settings
   1641         readPersistedSettings();
   1642 
   1643         // restore volume settings
   1644         int numStreamTypes = AudioSystem.getNumStreamTypes();
   1645         for (int streamType = 0; streamType < numStreamTypes; streamType++) {
   1646             VolumeStreamState streamState = mStreamStates[streamType];
   1647 
   1648             synchronized (streamState) {
   1649                 streamState.readSettings();
   1650 
   1651                 // unmute stream that was muted but is not affect by mute anymore
   1652                 if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType)) {
   1653                     int size = streamState.mDeathHandlers.size();
   1654                     for (int i = 0; i < size; i++) {
   1655                         streamState.mDeathHandlers.get(i).mMuteCount = 1;
   1656                         streamState.mDeathHandlers.get(i).mute(false);
   1657                     }
   1658                 }
   1659             }
   1660         }
   1661 
   1662         checkAllAliasStreamVolumes();
   1663 
   1664         // apply new ringer mode
   1665         setRingerModeInt(getRingerMode(), false);
   1666     }
   1667 
   1668     /** @see AudioManager#setSpeakerphoneOn() */
   1669     public void setSpeakerphoneOn(boolean on){
   1670         if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
   1671             return;
   1672         }
   1673         mForcedUseForComm = on ? AudioSystem.FORCE_SPEAKER : AudioSystem.FORCE_NONE;
   1674 
   1675         sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
   1676                 AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0);
   1677     }
   1678 
   1679     /** @see AudioManager#isSpeakerphoneOn() */
   1680     public boolean isSpeakerphoneOn() {
   1681         return (mForcedUseForComm == AudioSystem.FORCE_SPEAKER);
   1682     }
   1683 
   1684     /** @see AudioManager#setBluetoothScoOn() */
   1685     public void setBluetoothScoOn(boolean on){
   1686         if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
   1687             return;
   1688         }
   1689         mForcedUseForComm = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE;
   1690 
   1691         sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
   1692                 AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0);
   1693         sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
   1694                 AudioSystem.FOR_RECORD, mForcedUseForComm, null, 0);
   1695     }
   1696 
   1697     /** @see AudioManager#isBluetoothScoOn() */
   1698     public boolean isBluetoothScoOn() {
   1699         return (mForcedUseForComm == AudioSystem.FORCE_BT_SCO);
   1700     }
   1701 
   1702     /** @see AudioManager#setBluetoothA2dpOn() */
   1703     public void setBluetoothA2dpOn(boolean on) {
   1704         setBluetoothA2dpOnInt(on);
   1705     }
   1706 
   1707     /** @see AudioManager#isBluetoothA2dpOn() */
   1708     public boolean isBluetoothA2dpOn() {
   1709         synchronized (mBluetoothA2dpEnabledLock) {
   1710             return mBluetoothA2dpEnabled;
   1711         }
   1712     }
   1713 
   1714     /** @see AudioManager#startBluetoothSco() */
   1715     public void startBluetoothSco(IBinder cb){
   1716         if (!checkAudioSettingsPermission("startBluetoothSco()") ||
   1717                 !mBootCompleted) {
   1718             return;
   1719         }
   1720         ScoClient client = getScoClient(cb, true);
   1721         client.incCount();
   1722     }
   1723 
   1724     /** @see AudioManager#stopBluetoothSco() */
   1725     public void stopBluetoothSco(IBinder cb){
   1726         if (!checkAudioSettingsPermission("stopBluetoothSco()") ||
   1727                 !mBootCompleted) {
   1728             return;
   1729         }
   1730         ScoClient client = getScoClient(cb, false);
   1731         if (client != null) {
   1732             client.decCount();
   1733         }
   1734     }
   1735 
   1736 
   1737     private class ScoClient implements IBinder.DeathRecipient {
   1738         private IBinder mCb; // To be notified of client's death
   1739         private int mCreatorPid;
   1740         private int mStartcount; // number of SCO connections started by this client
   1741 
   1742         ScoClient(IBinder cb) {
   1743             mCb = cb;
   1744             mCreatorPid = Binder.getCallingPid();
   1745             mStartcount = 0;
   1746         }
   1747 
   1748         public void binderDied() {
   1749             synchronized(mScoClients) {
   1750                 Log.w(TAG, "SCO client died");
   1751                 int index = mScoClients.indexOf(this);
   1752                 if (index < 0) {
   1753                     Log.w(TAG, "unregistered SCO client died");
   1754                 } else {
   1755                     clearCount(true);
   1756                     mScoClients.remove(this);
   1757                 }
   1758             }
   1759         }
   1760 
   1761         public void incCount() {
   1762             synchronized(mScoClients) {
   1763                 requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED);
   1764                 if (mStartcount == 0) {
   1765                     try {
   1766                         mCb.linkToDeath(this, 0);
   1767                     } catch (RemoteException e) {
   1768                         // client has already died!
   1769                         Log.w(TAG, "ScoClient  incCount() could not link to "+mCb+" binder death");
   1770                     }
   1771                 }
   1772                 mStartcount++;
   1773             }
   1774         }
   1775 
   1776         public void decCount() {
   1777             synchronized(mScoClients) {
   1778                 if (mStartcount == 0) {
   1779                     Log.w(TAG, "ScoClient.decCount() already 0");
   1780                 } else {
   1781                     mStartcount--;
   1782                     if (mStartcount == 0) {
   1783                         try {
   1784                             mCb.unlinkToDeath(this, 0);
   1785                         } catch (NoSuchElementException e) {
   1786                             Log.w(TAG, "decCount() going to 0 but not registered to binder");
   1787                         }
   1788                     }
   1789                     requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
   1790                 }
   1791             }
   1792         }
   1793 
   1794         public void clearCount(boolean stopSco) {
   1795             synchronized(mScoClients) {
   1796                 if (mStartcount != 0) {
   1797                     try {
   1798                         mCb.unlinkToDeath(this, 0);
   1799                     } catch (NoSuchElementException e) {
   1800                         Log.w(TAG, "clearCount() mStartcount: "+mStartcount+" != 0 but not registered to binder");
   1801                     }
   1802                 }
   1803                 mStartcount = 0;
   1804                 if (stopSco) {
   1805                     requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
   1806                 }
   1807             }
   1808         }
   1809 
   1810         public int getCount() {
   1811             return mStartcount;
   1812         }
   1813 
   1814         public IBinder getBinder() {
   1815             return mCb;
   1816         }
   1817 
   1818         public int getPid() {
   1819             return mCreatorPid;
   1820         }
   1821 
   1822         public int totalCount() {
   1823             synchronized(mScoClients) {
   1824                 int count = 0;
   1825                 int size = mScoClients.size();
   1826                 for (int i = 0; i < size; i++) {
   1827                     count += mScoClients.get(i).getCount();
   1828                 }
   1829                 return count;
   1830             }
   1831         }
   1832 
   1833         private void requestScoState(int state) {
   1834             checkScoAudioState();
   1835             if (totalCount() == 0) {
   1836                 if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
   1837                     // Make sure that the state transitions to CONNECTING even if we cannot initiate
   1838                     // the connection.
   1839                     broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
   1840                     // Accept SCO audio activation only in NORMAL audio mode or if the mode is
   1841                     // currently controlled by the same client process.
   1842                     synchronized(mSetModeDeathHandlers) {
   1843                         if ((mSetModeDeathHandlers.isEmpty() ||
   1844                                 mSetModeDeathHandlers.get(0).getPid() == mCreatorPid) &&
   1845                                 (mScoAudioState == SCO_STATE_INACTIVE ||
   1846                                  mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
   1847                             if (mScoAudioState == SCO_STATE_INACTIVE) {
   1848                                 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
   1849                                     if (mBluetoothHeadset.startScoUsingVirtualVoiceCall(
   1850                                             mBluetoothHeadsetDevice)) {
   1851                                         mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
   1852                                     } else {
   1853                                         broadcastScoConnectionState(
   1854                                                 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
   1855                                     }
   1856                                 } else if (getBluetoothHeadset()) {
   1857                                     mScoAudioState = SCO_STATE_ACTIVATE_REQ;
   1858                                 }
   1859                             } else {
   1860                                 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
   1861                                 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
   1862                             }
   1863                         } else {
   1864                             broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
   1865                         }
   1866                     }
   1867                 } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED &&
   1868                               (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
   1869                                mScoAudioState == SCO_STATE_ACTIVATE_REQ)) {
   1870                     if (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) {
   1871                         if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
   1872                             if (!mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
   1873                                     mBluetoothHeadsetDevice)) {
   1874                                 mScoAudioState = SCO_STATE_INACTIVE;
   1875                                 broadcastScoConnectionState(
   1876                                         AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
   1877                             }
   1878                         } else if (getBluetoothHeadset()) {
   1879                             mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
   1880                         }
   1881                     } else {
   1882                         mScoAudioState = SCO_STATE_INACTIVE;
   1883                         broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
   1884                     }
   1885                 }
   1886             }
   1887         }
   1888     }
   1889 
   1890     private void checkScoAudioState() {
   1891         if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null &&
   1892                 mScoAudioState == SCO_STATE_INACTIVE &&
   1893                 mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
   1894                 != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
   1895             mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
   1896         }
   1897     }
   1898 
   1899     private ScoClient getScoClient(IBinder cb, boolean create) {
   1900         synchronized(mScoClients) {
   1901             ScoClient client = null;
   1902             int size = mScoClients.size();
   1903             for (int i = 0; i < size; i++) {
   1904                 client = mScoClients.get(i);
   1905                 if (client.getBinder() == cb)
   1906                     return client;
   1907             }
   1908             if (create) {
   1909                 client = new ScoClient(cb);
   1910                 mScoClients.add(client);
   1911             }
   1912             return client;
   1913         }
   1914     }
   1915 
   1916     public void clearAllScoClients(int exceptPid, boolean stopSco) {
   1917         synchronized(mScoClients) {
   1918             ScoClient savedClient = null;
   1919             int size = mScoClients.size();
   1920             for (int i = 0; i < size; i++) {
   1921                 ScoClient cl = mScoClients.get(i);
   1922                 if (cl.getPid() != exceptPid) {
   1923                     cl.clearCount(stopSco);
   1924                 } else {
   1925                     savedClient = cl;
   1926                 }
   1927             }
   1928             mScoClients.clear();
   1929             if (savedClient != null) {
   1930                 mScoClients.add(savedClient);
   1931             }
   1932         }
   1933     }
   1934 
   1935     private boolean getBluetoothHeadset() {
   1936         boolean result = false;
   1937         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
   1938         if (adapter != null) {
   1939             result = adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
   1940                                     BluetoothProfile.HEADSET);
   1941         }
   1942         // If we could not get a bluetooth headset proxy, send a failure message
   1943         // without delay to reset the SCO audio state and clear SCO clients.
   1944         // If we could get a proxy, send a delayed failure message that will reset our state
   1945         // in case we don't receive onServiceConnected().
   1946         sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED,
   1947                 SENDMSG_REPLACE, 0, 0, null, result ? BT_HEADSET_CNCT_TIMEOUT_MS : 0);
   1948         return result;
   1949     }
   1950 
   1951     private void disconnectBluetoothSco(int exceptPid) {
   1952         synchronized(mScoClients) {
   1953             checkScoAudioState();
   1954             if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL ||
   1955                     mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
   1956                 if (mBluetoothHeadsetDevice != null) {
   1957                     if (mBluetoothHeadset != null) {
   1958                         if (!mBluetoothHeadset.stopVoiceRecognition(
   1959                                 mBluetoothHeadsetDevice)) {
   1960                             sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED,
   1961                                     SENDMSG_REPLACE, 0, 0, null, 0);
   1962                         }
   1963                     } else if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL &&
   1964                             getBluetoothHeadset()) {
   1965                         mScoAudioState = SCO_STATE_DEACTIVATE_EXT_REQ;
   1966                     }
   1967                 }
   1968             } else {
   1969                 clearAllScoClients(exceptPid, true);
   1970             }
   1971         }
   1972     }
   1973 
   1974     private void resetBluetoothSco() {
   1975         synchronized(mScoClients) {
   1976             clearAllScoClients(0, false);
   1977             mScoAudioState = SCO_STATE_INACTIVE;
   1978             broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
   1979         }
   1980     }
   1981 
   1982     private void broadcastScoConnectionState(int state) {
   1983         if (state != mScoConnectionState) {
   1984             Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
   1985             newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
   1986             newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE,
   1987                     mScoConnectionState);
   1988             mContext.sendStickyBroadcast(newIntent);
   1989             mScoConnectionState = state;
   1990         }
   1991     }
   1992 
   1993     private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
   1994         new BluetoothProfile.ServiceListener() {
   1995         public void onServiceConnected(int profile, BluetoothProfile proxy) {
   1996             BluetoothDevice btDevice;
   1997             List<BluetoothDevice> deviceList;
   1998             switch(profile) {
   1999             case BluetoothProfile.A2DP:
   2000                 BluetoothA2dp a2dp = (BluetoothA2dp) proxy;
   2001                 deviceList = a2dp.getConnectedDevices();
   2002                 if (deviceList.size() > 0) {
   2003                     btDevice = deviceList.get(0);
   2004                     synchronized (mConnectedDevices) {
   2005                         int state = a2dp.getConnectionState(btDevice);
   2006                         int delay = checkSendBecomingNoisyIntent(
   2007                                                 AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
   2008                                                 (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0);
   2009                         queueMsgUnderWakeLock(mAudioHandler,
   2010                                 MSG_SET_A2DP_CONNECTION_STATE,
   2011                                 state,
   2012                                 0,
   2013                                 btDevice,
   2014                                 delay);
   2015                     }
   2016                 }
   2017                 break;
   2018 
   2019             case BluetoothProfile.HEADSET:
   2020                 synchronized (mScoClients) {
   2021                     // Discard timeout message
   2022                     mAudioHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
   2023                     mBluetoothHeadset = (BluetoothHeadset) proxy;
   2024                     deviceList = mBluetoothHeadset.getConnectedDevices();
   2025                     if (deviceList.size() > 0) {
   2026                         mBluetoothHeadsetDevice = deviceList.get(0);
   2027                     } else {
   2028                         mBluetoothHeadsetDevice = null;
   2029                     }
   2030                     // Refresh SCO audio state
   2031                     checkScoAudioState();
   2032                     // Continue pending action if any
   2033                     if (mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
   2034                             mScoAudioState == SCO_STATE_DEACTIVATE_REQ ||
   2035                             mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
   2036                         boolean status = false;
   2037                         if (mBluetoothHeadsetDevice != null) {
   2038                             switch (mScoAudioState) {
   2039                             case SCO_STATE_ACTIVATE_REQ:
   2040                                 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
   2041                                 status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
   2042                                         mBluetoothHeadsetDevice);
   2043                                 break;
   2044                             case SCO_STATE_DEACTIVATE_REQ:
   2045                                 status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
   2046                                         mBluetoothHeadsetDevice);
   2047                                 break;
   2048                             case SCO_STATE_DEACTIVATE_EXT_REQ:
   2049                                 status = mBluetoothHeadset.stopVoiceRecognition(
   2050                                         mBluetoothHeadsetDevice);
   2051                             }
   2052                         }
   2053                         if (!status) {
   2054                             sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED,
   2055                                     SENDMSG_REPLACE, 0, 0, null, 0);
   2056                         }
   2057                     }
   2058                 }
   2059                 break;
   2060 
   2061             default:
   2062                 break;
   2063             }
   2064         }
   2065         public void onServiceDisconnected(int profile) {
   2066             switch(profile) {
   2067             case BluetoothProfile.A2DP:
   2068                 synchronized (mConnectedDevices) {
   2069                     if (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)) {
   2070                         makeA2dpDeviceUnavailableNow(
   2071                                 mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
   2072                     }
   2073                 }
   2074                 break;
   2075 
   2076             case BluetoothProfile.HEADSET:
   2077                 synchronized (mScoClients) {
   2078                     mBluetoothHeadset = null;
   2079                 }
   2080                 break;
   2081 
   2082             default:
   2083                 break;
   2084             }
   2085         }
   2086     };
   2087 
   2088     ///////////////////////////////////////////////////////////////////////////
   2089     // Internal methods
   2090     ///////////////////////////////////////////////////////////////////////////
   2091 
   2092     /**
   2093      * Checks if the adjustment should change ringer mode instead of just
   2094      * adjusting volume. If so, this will set the proper ringer mode and volume
   2095      * indices on the stream states.
   2096      */
   2097     private boolean checkForRingerModeChange(int oldIndex, int direction,  int step) {
   2098         boolean adjustVolumeIndex = true;
   2099         int ringerMode = getRingerMode();
   2100 
   2101         switch (ringerMode) {
   2102         case RINGER_MODE_NORMAL:
   2103             if (direction == AudioManager.ADJUST_LOWER) {
   2104                 if (mHasVibrator) {
   2105                     // "step" is the delta in internal index units corresponding to a
   2106                     // change of 1 in UI index units.
   2107                     // Because of rounding when rescaling from one stream index range to its alias
   2108                     // index range, we cannot simply test oldIndex == step:
   2109                     //   (step <= oldIndex < 2 * step) is equivalent to: (old UI index == 1)
   2110                     if (step <= oldIndex && oldIndex < 2 * step) {
   2111                         ringerMode = RINGER_MODE_VIBRATE;
   2112                     }
   2113                 } else {
   2114                     // (oldIndex < step) is equivalent to (old UI index == 0)
   2115                     if ((oldIndex < step) && mPrevVolDirection != AudioManager.ADJUST_LOWER) {
   2116                         ringerMode = RINGER_MODE_SILENT;
   2117                     }
   2118                 }
   2119             }
   2120             break;
   2121         case RINGER_MODE_VIBRATE:
   2122             if (!mHasVibrator) {
   2123                 Log.e(TAG, "checkForRingerModeChange() current ringer mode is vibrate" +
   2124                         "but no vibrator is present");
   2125                 break;
   2126             }
   2127             if ((direction == AudioManager.ADJUST_LOWER)) {
   2128                 if (mPrevVolDirection != AudioManager.ADJUST_LOWER) {
   2129                     ringerMode = RINGER_MODE_SILENT;
   2130                 }
   2131             } else if (direction == AudioManager.ADJUST_RAISE) {
   2132                 ringerMode = RINGER_MODE_NORMAL;
   2133             }
   2134             adjustVolumeIndex = false;
   2135             break;
   2136         case RINGER_MODE_SILENT:
   2137             if (direction == AudioManager.ADJUST_RAISE) {
   2138                 if (mHasVibrator) {
   2139                     ringerMode = RINGER_MODE_VIBRATE;
   2140                 } else {
   2141                     ringerMode = RINGER_MODE_NORMAL;
   2142                 }
   2143             }
   2144             adjustVolumeIndex = false;
   2145             break;
   2146         default:
   2147             Log.e(TAG, "checkForRingerModeChange() wrong ringer mode: "+ringerMode);
   2148             break;
   2149         }
   2150 
   2151         setRingerMode(ringerMode);
   2152 
   2153         mPrevVolDirection = direction;
   2154 
   2155         return adjustVolumeIndex;
   2156     }
   2157 
   2158     public boolean isStreamAffectedByRingerMode(int streamType) {
   2159         return (mRingerModeAffectedStreams & (1 << streamType)) != 0;
   2160     }
   2161 
   2162     private boolean isStreamMutedByRingerMode(int streamType) {
   2163         return (mRingerModeMutedStreams & (1 << streamType)) != 0;
   2164     }
   2165 
   2166     public boolean isStreamAffectedByMute(int streamType) {
   2167         return (mMuteAffectedStreams & (1 << streamType)) != 0;
   2168     }
   2169 
   2170     private void ensureValidDirection(int direction) {
   2171         if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) {
   2172             throw new IllegalArgumentException("Bad direction " + direction);
   2173         }
   2174     }
   2175 
   2176     private void ensureValidSteps(int steps) {
   2177         if (Math.abs(steps) > MAX_BATCH_VOLUME_ADJUST_STEPS) {
   2178             throw new IllegalArgumentException("Bad volume adjust steps " + steps);
   2179         }
   2180     }
   2181 
   2182     private void ensureValidStreamType(int streamType) {
   2183         if (streamType < 0 || streamType >= mStreamStates.length) {
   2184             throw new IllegalArgumentException("Bad stream type " + streamType);
   2185         }
   2186     }
   2187 
   2188     private boolean isInCommunication() {
   2189         boolean isOffhook = false;
   2190 
   2191         if (mVoiceCapable) {
   2192             try {
   2193                 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
   2194                 if (phone != null) isOffhook = phone.isOffhook();
   2195             } catch (RemoteException e) {
   2196                 Log.w(TAG, "Couldn't connect to phone service", e);
   2197             }
   2198         }
   2199         return (isOffhook || getMode() == AudioManager.MODE_IN_COMMUNICATION);
   2200     }
   2201 
   2202     private int getActiveStreamType(int suggestedStreamType) {
   2203         if (mVoiceCapable) {
   2204             if (isInCommunication()) {
   2205                 if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
   2206                         == AudioSystem.FORCE_BT_SCO) {
   2207                     // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
   2208                     return AudioSystem.STREAM_BLUETOOTH_SCO;
   2209                 } else {
   2210                     // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
   2211                     return AudioSystem.STREAM_VOICE_CALL;
   2212                 }
   2213             } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
   2214                 // Having the suggested stream be USE_DEFAULT_STREAM_TYPE is how remote control
   2215                 // volume can have priority over STREAM_MUSIC
   2216                 if (checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
   2217                     if (DEBUG_VOL)
   2218                         Log.v(TAG, "getActiveStreamType: Forcing STREAM_REMOTE_MUSIC");
   2219                     return STREAM_REMOTE_MUSIC;
   2220                 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
   2221                     if (DEBUG_VOL)
   2222                         Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC stream active");
   2223                     return AudioSystem.STREAM_MUSIC;
   2224                 } else {
   2225                     if (DEBUG_VOL)
   2226                         Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING b/c default");
   2227                     return AudioSystem.STREAM_RING;
   2228                 }
   2229             } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
   2230                 if (DEBUG_VOL)
   2231                     Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC stream active");
   2232                 return AudioSystem.STREAM_MUSIC;
   2233             } else {
   2234                 if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Returning suggested type "
   2235                         + suggestedStreamType);
   2236                 return suggestedStreamType;
   2237             }
   2238         } else {
   2239             if (isInCommunication()) {
   2240                 if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
   2241                         == AudioSystem.FORCE_BT_SCO) {
   2242                     if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO");
   2243                     return AudioSystem.STREAM_BLUETOOTH_SCO;
   2244                 } else {
   2245                     if (DEBUG_VOL)  Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL");
   2246                     return AudioSystem.STREAM_VOICE_CALL;
   2247                 }
   2248             } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_NOTIFICATION,
   2249                     NOTIFICATION_VOLUME_DELAY_MS) ||
   2250                     AudioSystem.isStreamActive(AudioSystem.STREAM_RING,
   2251                             NOTIFICATION_VOLUME_DELAY_MS)) {
   2252                 if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION");
   2253                 return AudioSystem.STREAM_NOTIFICATION;
   2254             } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
   2255                 if (checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
   2256                     // Having the suggested stream be USE_DEFAULT_STREAM_TYPE is how remote control
   2257                     // volume can have priority over STREAM_MUSIC
   2258                     if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_REMOTE_MUSIC");
   2259                     return STREAM_REMOTE_MUSIC;
   2260                 } else {
   2261                     if (DEBUG_VOL)
   2262                         Log.v(TAG, "getActiveStreamType: using STREAM_MUSIC as default");
   2263                     return AudioSystem.STREAM_MUSIC;
   2264                 }
   2265             } else {
   2266                 if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Returning suggested type "
   2267                         + suggestedStreamType);
   2268                 return suggestedStreamType;
   2269             }
   2270         }
   2271     }
   2272 
   2273     private void broadcastRingerMode(int ringerMode) {
   2274         // Send sticky broadcast
   2275         Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION);
   2276         broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, ringerMode);
   2277         broadcast.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
   2278                 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
   2279         long origCallerIdentityToken = Binder.clearCallingIdentity();
   2280         mContext.sendStickyBroadcast(broadcast);
   2281         Binder.restoreCallingIdentity(origCallerIdentityToken);
   2282     }
   2283 
   2284     private void broadcastVibrateSetting(int vibrateType) {
   2285         // Send broadcast
   2286         if (ActivityManagerNative.isSystemReady()) {
   2287             Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
   2288             broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType);
   2289             broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType));
   2290             mContext.sendBroadcast(broadcast);
   2291         }
   2292     }
   2293 
   2294     // Message helper methods
   2295     /**
   2296      * Queue a message on the given handler's message queue, after acquiring the service wake lock.
   2297      * Note that the wake lock needs to be released after the message has been handled.
   2298      */
   2299     private void queueMsgUnderWakeLock(Handler handler, int msg,
   2300             int arg1, int arg2, Object obj, int delay) {
   2301         mMediaEventWakeLock.acquire();
   2302         sendMsg(handler, msg, SENDMSG_QUEUE, arg1, arg2, obj, delay);
   2303     }
   2304 
   2305     private static void sendMsg(Handler handler, int msg,
   2306             int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {
   2307 
   2308         if (existingMsgPolicy == SENDMSG_REPLACE) {
   2309             handler.removeMessages(msg);
   2310         } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
   2311             return;
   2312         }
   2313 
   2314         handler.sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
   2315     }
   2316 
   2317     boolean checkAudioSettingsPermission(String method) {
   2318         if (mContext.checkCallingOrSelfPermission("android.permission.MODIFY_AUDIO_SETTINGS")
   2319                 == PackageManager.PERMISSION_GRANTED) {
   2320             return true;
   2321         }
   2322         String msg = "Audio Settings Permission Denial: " + method + " from pid="
   2323                 + Binder.getCallingPid()
   2324                 + ", uid=" + Binder.getCallingUid();
   2325         Log.w(TAG, msg);
   2326         return false;
   2327     }
   2328 
   2329     private int getDeviceForStream(int stream) {
   2330         int device = AudioSystem.getDevicesForStream(stream);
   2331         if ((device & (device - 1)) != 0) {
   2332             // Multiple device selection is either:
   2333             //  - speaker + one other device: give priority to speaker in this case.
   2334             //  - one A2DP device + another device: happens with duplicated output. In this case
   2335             // retain the device on the A2DP output as the other must not correspond to an active
   2336             // selection if not the speaker.
   2337             if ((device & AudioSystem.DEVICE_OUT_SPEAKER) != 0) {
   2338                 device = AudioSystem.DEVICE_OUT_SPEAKER;
   2339             } else {
   2340                 device &= AudioSystem.DEVICE_OUT_ALL_A2DP;
   2341             }
   2342         }
   2343         return device;
   2344     }
   2345 
   2346     public void setWiredDeviceConnectionState(int device, int state, String name) {
   2347         synchronized (mConnectedDevices) {
   2348             int delay = checkSendBecomingNoisyIntent(device, state);
   2349             queueMsgUnderWakeLock(mAudioHandler,
   2350                     MSG_SET_WIRED_DEVICE_CONNECTION_STATE,
   2351                     device,
   2352                     state,
   2353                     name,
   2354                     delay);
   2355         }
   2356     }
   2357 
   2358     public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state)
   2359     {
   2360         int delay;
   2361         synchronized (mConnectedDevices) {
   2362             delay = checkSendBecomingNoisyIntent(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
   2363                                             (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0);
   2364             queueMsgUnderWakeLock(mAudioHandler,
   2365                     MSG_SET_A2DP_CONNECTION_STATE,
   2366                     state,
   2367                     0,
   2368                     device,
   2369                     delay);
   2370         }
   2371         return delay;
   2372     }
   2373 
   2374     ///////////////////////////////////////////////////////////////////////////
   2375     // Inner classes
   2376     ///////////////////////////////////////////////////////////////////////////
   2377 
   2378     public class VolumeStreamState {
   2379         private final int mStreamType;
   2380 
   2381         private String mVolumeIndexSettingName;
   2382         private String mLastAudibleVolumeIndexSettingName;
   2383         private int mIndexMax;
   2384         private final ConcurrentHashMap<Integer, Integer> mIndex =
   2385                                             new ConcurrentHashMap<Integer, Integer>(8, 0.75f, 4);
   2386         private final ConcurrentHashMap<Integer, Integer> mLastAudibleIndex =
   2387                                             new ConcurrentHashMap<Integer, Integer>(8, 0.75f, 4);
   2388         private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo clients death
   2389 
   2390         private VolumeStreamState(String settingName, int streamType) {
   2391 
   2392             mVolumeIndexSettingName = settingName;
   2393             mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
   2394 
   2395             mStreamType = streamType;
   2396             mIndexMax = MAX_STREAM_VOLUME[streamType];
   2397             AudioSystem.initStreamVolume(streamType, 0, mIndexMax);
   2398             mIndexMax *= 10;
   2399 
   2400             readSettings();
   2401 
   2402             mDeathHandlers = new ArrayList<VolumeDeathHandler>();
   2403         }
   2404 
   2405         public String getSettingNameForDevice(boolean lastAudible, int device) {
   2406             String name = lastAudible ?
   2407                             mLastAudibleVolumeIndexSettingName :
   2408                             mVolumeIndexSettingName;
   2409             String suffix = AudioSystem.getDeviceName(device);
   2410             if (suffix.isEmpty()) {
   2411                 return name;
   2412             }
   2413             return name + "_" + suffix;
   2414         }
   2415 
   2416         public synchronized void readSettings() {
   2417             int remainingDevices = AudioSystem.DEVICE_OUT_ALL;
   2418 
   2419             for (int i = 0; remainingDevices != 0; i++) {
   2420                 int device = (1 << i);
   2421                 if ((device & remainingDevices) == 0) {
   2422                     continue;
   2423                 }
   2424                 remainingDevices &= ~device;
   2425 
   2426                 // retrieve current volume for device
   2427                 String name = getSettingNameForDevice(false /* lastAudible */, device);
   2428                 // if no volume stored for current stream and device, use default volume if default
   2429                 // device, continue otherwise
   2430                 int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT) ?
   2431                                         AudioManager.DEFAULT_STREAM_VOLUME[mStreamType] : -1;
   2432                 int index = Settings.System.getInt(mContentResolver, name, defaultIndex);
   2433                 if (index == -1) {
   2434                     continue;
   2435                 }
   2436 
   2437                 // retrieve last audible volume for device
   2438                 name = getSettingNameForDevice(true  /* lastAudible */, device);
   2439                 // use stored last audible index if present, otherwise use current index if not 0
   2440                 // or default index
   2441                 defaultIndex = (index > 0) ?
   2442                                     index : AudioManager.DEFAULT_STREAM_VOLUME[mStreamType];
   2443                 int lastAudibleIndex = Settings.System.getInt(mContentResolver, name, defaultIndex);
   2444 
   2445                 // a last audible index of 0 should never be stored for ring and notification
   2446                 // streams on phones (voice capable devices).
   2447                 // same for system stream on phones and tablets
   2448                 if ((lastAudibleIndex == 0) &&
   2449                         ((mVoiceCapable &&
   2450                                 (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_RING)) ||
   2451                          (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_SYSTEM))) {
   2452                     lastAudibleIndex = AudioManager.DEFAULT_STREAM_VOLUME[mStreamType];
   2453                     // Correct the data base
   2454                     sendMsg(mAudioHandler,
   2455                             MSG_PERSIST_VOLUME,
   2456                             SENDMSG_QUEUE,
   2457                             PERSIST_LAST_AUDIBLE,
   2458                             device,
   2459                             this,
   2460                             PERSIST_DELAY);
   2461                 }
   2462                 mLastAudibleIndex.put(device, getValidIndex(10 * lastAudibleIndex));
   2463                 // the initial index should never be 0 for ring and notification streams on phones
   2464                 // (voice capable devices) if not in silent or vibrate mode.
   2465                 // same for system stream on phones and tablets
   2466                 if ((index == 0) && (mRingerMode == AudioManager.RINGER_MODE_NORMAL) &&
   2467                         ((mVoiceCapable &&
   2468                                 (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_RING)) ||
   2469                          (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_SYSTEM))) {
   2470                     index = lastAudibleIndex;
   2471                     // Correct the data base
   2472                     sendMsg(mAudioHandler,
   2473                             MSG_PERSIST_VOLUME,
   2474                             SENDMSG_QUEUE,
   2475                             PERSIST_CURRENT,
   2476                             device,
   2477                             this,
   2478                             PERSIST_DELAY);
   2479                 }
   2480                 mIndex.put(device, getValidIndex(10 * index));
   2481             }
   2482         }
   2483 
   2484         public void applyDeviceVolume(int device) {
   2485             AudioSystem.setStreamVolumeIndex(mStreamType,
   2486                                              (getIndex(device, false  /* lastAudible */) + 5)/10,
   2487                                              device);
   2488         }
   2489 
   2490         public synchronized void applyAllVolumes() {
   2491             // apply default volume first: by convention this will reset all
   2492             // devices volumes in audio policy manager to the supplied value
   2493             AudioSystem.setStreamVolumeIndex(mStreamType,
   2494                     (getIndex(AudioSystem.DEVICE_OUT_DEFAULT, false /* lastAudible */) + 5)/10,
   2495                     AudioSystem.DEVICE_OUT_DEFAULT);
   2496             // then apply device specific volumes
   2497             Set set = mIndex.entrySet();
   2498             Iterator i = set.iterator();
   2499             while (i.hasNext()) {
   2500                 Map.Entry entry = (Map.Entry)i.next();
   2501                 int device = ((Integer)entry.getKey()).intValue();
   2502                 if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
   2503                     AudioSystem.setStreamVolumeIndex(mStreamType,
   2504                                                      ((Integer)entry.getValue() + 5)/10,
   2505                                                      device);
   2506                 }
   2507             }
   2508         }
   2509 
   2510         public boolean adjustIndex(int deltaIndex, int device) {
   2511             return setIndex(getIndex(device,
   2512                                      false  /* lastAudible */) + deltaIndex,
   2513                             device,
   2514                             true  /* lastAudible */);
   2515         }
   2516 
   2517         public synchronized boolean setIndex(int index, int device, boolean lastAudible) {
   2518             int oldIndex = getIndex(device, false  /* lastAudible */);
   2519             index = getValidIndex(index);
   2520             mIndex.put(device, getValidIndex(index));
   2521 
   2522             if (oldIndex != index) {
   2523                 if (lastAudible) {
   2524                     mLastAudibleIndex.put(device, index);
   2525                 }
   2526                 // Apply change to all streams using this one as alias
   2527                 // if changing volume of current device, also change volume of current
   2528                 // device on aliased stream
   2529                 boolean currentDevice = (device == getDeviceForStream(mStreamType));
   2530                 int numStreamTypes = AudioSystem.getNumStreamTypes();
   2531                 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
   2532                     if (streamType != mStreamType &&
   2533                             mStreamVolumeAlias[streamType] == mStreamType) {
   2534                         int scaledIndex = rescaleIndex(index, mStreamType, streamType);
   2535                         mStreamStates[streamType].setIndex(scaledIndex,
   2536                                                            device,
   2537                                                            lastAudible);
   2538                         if (currentDevice) {
   2539                             mStreamStates[streamType].setIndex(scaledIndex,
   2540                                                                getDeviceForStream(streamType),
   2541                                                                lastAudible);
   2542                         }
   2543                     }
   2544                 }
   2545                 return true;
   2546             } else {
   2547                 return false;
   2548             }
   2549         }
   2550 
   2551         public synchronized int getIndex(int device, boolean lastAudible) {
   2552             ConcurrentHashMap <Integer, Integer> indexes;
   2553             if (lastAudible) {
   2554                 indexes = mLastAudibleIndex;
   2555             } else {
   2556                 indexes = mIndex;
   2557             }
   2558             Integer index = indexes.get(device);
   2559             if (index == null) {
   2560                 // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
   2561                 index = indexes.get(AudioSystem.DEVICE_OUT_DEFAULT);
   2562             }
   2563             return index.intValue();
   2564         }
   2565 
   2566         public synchronized void setLastAudibleIndex(int index, int device) {
   2567             // Apply change to all streams using this one as alias
   2568             // if changing volume of current device, also change volume of current
   2569             // device on aliased stream
   2570             boolean currentDevice = (device == getDeviceForStream(mStreamType));
   2571             int numStreamTypes = AudioSystem.getNumStreamTypes();
   2572             for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
   2573                 if (streamType != mStreamType &&
   2574                         mStreamVolumeAlias[streamType] == mStreamType) {
   2575                     int scaledIndex = rescaleIndex(index, mStreamType, streamType);
   2576                     mStreamStates[streamType].setLastAudibleIndex(scaledIndex, device);
   2577                     if (currentDevice) {
   2578                         mStreamStates[streamType].setLastAudibleIndex(scaledIndex,
   2579                                                                    getDeviceForStream(streamType));
   2580                     }
   2581                 }
   2582             }
   2583             mLastAudibleIndex.put(device, getValidIndex(index));
   2584         }
   2585 
   2586         public synchronized void adjustLastAudibleIndex(int deltaIndex, int device) {
   2587             setLastAudibleIndex(getIndex(device,
   2588                                          true  /* lastAudible */) + deltaIndex,
   2589                                 device);
   2590         }
   2591 
   2592         public int getMaxIndex() {
   2593             return mIndexMax;
   2594         }
   2595 
   2596         // only called by setAllIndexes() which is already synchronized
   2597         public ConcurrentHashMap <Integer, Integer> getAllIndexes(boolean lastAudible) {
   2598             if (lastAudible) {
   2599                 return mLastAudibleIndex;
   2600             } else {
   2601                 return mIndex;
   2602             }
   2603         }
   2604 
   2605         public synchronized void setAllIndexes(VolumeStreamState srcStream, boolean lastAudible) {
   2606             ConcurrentHashMap <Integer, Integer> indexes = srcStream.getAllIndexes(lastAudible);
   2607             Set set = indexes.entrySet();
   2608             Iterator i = set.iterator();
   2609             while (i.hasNext()) {
   2610                 Map.Entry entry = (Map.Entry)i.next();
   2611                 int device = ((Integer)entry.getKey()).intValue();
   2612                 int index = ((Integer)entry.getValue()).intValue();
   2613                 index = rescaleIndex(index, srcStream.getStreamType(), mStreamType);
   2614                 setIndex(index, device, lastAudible);
   2615             }
   2616         }
   2617 
   2618         public synchronized void mute(IBinder cb, boolean state) {
   2619             VolumeDeathHandler handler = getDeathHandler(cb, state);
   2620             if (handler == null) {
   2621                 Log.e(TAG, "Could not get client death handler for stream: "+mStreamType);
   2622                 return;
   2623             }
   2624             handler.mute(state);
   2625         }
   2626 
   2627         public int getStreamType() {
   2628             return mStreamType;
   2629         }
   2630 
   2631         private int getValidIndex(int index) {
   2632             if (index < 0) {
   2633                 return 0;
   2634             } else if (index > mIndexMax) {
   2635                 return mIndexMax;
   2636             }
   2637 
   2638             return index;
   2639         }
   2640 
   2641         private class VolumeDeathHandler implements IBinder.DeathRecipient {
   2642             private IBinder mICallback; // To be notified of client's death
   2643             private int mMuteCount; // Number of active mutes for this client
   2644 
   2645             VolumeDeathHandler(IBinder cb) {
   2646                 mICallback = cb;
   2647             }
   2648 
   2649             // must be called while synchronized on parent VolumeStreamState
   2650             public void mute(boolean state) {
   2651                 if (state) {
   2652                     if (mMuteCount == 0) {
   2653                         // Register for client death notification
   2654                         try {
   2655                             // mICallback can be 0 if muted by AudioService
   2656                             if (mICallback != null) {
   2657                                 mICallback.linkToDeath(this, 0);
   2658                             }
   2659                             mDeathHandlers.add(this);
   2660                             // If the stream is not yet muted by any client, set level to 0
   2661                             if (muteCount() == 0) {
   2662                                 Set set = mIndex.entrySet();
   2663                                 Iterator i = set.iterator();
   2664                                 while (i.hasNext()) {
   2665                                     Map.Entry entry = (Map.Entry)i.next();
   2666                                     int device = ((Integer)entry.getKey()).intValue();
   2667                                     setIndex(0, device, false /* lastAudible */);
   2668                                 }
   2669                                 sendMsg(mAudioHandler,
   2670                                         MSG_SET_ALL_VOLUMES,
   2671                                         SENDMSG_QUEUE,
   2672                                         0,
   2673                                         0,
   2674                                         VolumeStreamState.this, 0);
   2675                             }
   2676                         } catch (RemoteException e) {
   2677                             // Client has died!
   2678                             binderDied();
   2679                             return;
   2680                         }
   2681                     } else {
   2682                         Log.w(TAG, "stream: "+mStreamType+" was already muted by this client");
   2683                     }
   2684                     mMuteCount++;
   2685                 } else {
   2686                     if (mMuteCount == 0) {
   2687                         Log.e(TAG, "unexpected unmute for stream: "+mStreamType);
   2688                     } else {
   2689                         mMuteCount--;
   2690                         if (mMuteCount == 0) {
   2691                             // Unregister from client death notification
   2692                             mDeathHandlers.remove(this);
   2693                             // mICallback can be 0 if muted by AudioService
   2694                             if (mICallback != null) {
   2695                                 mICallback.unlinkToDeath(this, 0);
   2696                             }
   2697                             if (muteCount() == 0) {
   2698                                 // If the stream is not muted any more, restore its volume if
   2699                                 // ringer mode allows it
   2700                                 if (!isStreamAffectedByRingerMode(mStreamType) ||
   2701                                         mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
   2702                                     Set set = mIndex.entrySet();
   2703                                     Iterator i = set.iterator();
   2704                                     while (i.hasNext()) {
   2705                                         Map.Entry entry = (Map.Entry)i.next();
   2706                                         int device = ((Integer)entry.getKey()).intValue();
   2707                                         setIndex(getIndex(device,
   2708                                                           true  /* lastAudible */),
   2709                                                  device,
   2710                                                  false  /* lastAudible */);
   2711                                     }
   2712                                     sendMsg(mAudioHandler,
   2713                                             MSG_SET_ALL_VOLUMES,
   2714                                             SENDMSG_QUEUE,
   2715                                             0,
   2716                                             0,
   2717                                             VolumeStreamState.this, 0);
   2718                                 }
   2719                             }
   2720                         }
   2721                     }
   2722                 }
   2723             }
   2724 
   2725             public void binderDied() {
   2726                 Log.w(TAG, "Volume service client died for stream: "+mStreamType);
   2727                 if (mMuteCount != 0) {
   2728                     // Reset all active mute requests from this client.
   2729                     mMuteCount = 1;
   2730                     mute(false);
   2731                 }
   2732             }
   2733         }
   2734 
   2735         private synchronized int muteCount() {
   2736             int count = 0;
   2737             int size = mDeathHandlers.size();
   2738             for (int i = 0; i < size; i++) {
   2739                 count += mDeathHandlers.get(i).mMuteCount;
   2740             }
   2741             return count;
   2742         }
   2743 
   2744         // only called by mute() which is already synchronized
   2745         private VolumeDeathHandler getDeathHandler(IBinder cb, boolean state) {
   2746             VolumeDeathHandler handler;
   2747             int size = mDeathHandlers.size();
   2748             for (int i = 0; i < size; i++) {
   2749                 handler = mDeathHandlers.get(i);
   2750                 if (cb == handler.mICallback) {
   2751                     return handler;
   2752                 }
   2753             }
   2754             // If this is the first mute request for this client, create a new
   2755             // client death handler. Otherwise, it is an out of sequence unmute request.
   2756             if (state) {
   2757                 handler = new VolumeDeathHandler(cb);
   2758             } else {
   2759                 Log.w(TAG, "stream was not muted by this client");
   2760                 handler = null;
   2761             }
   2762             return handler;
   2763         }
   2764 
   2765         private void dump(PrintWriter pw) {
   2766             pw.print("   Current: ");
   2767             Set set = mIndex.entrySet();
   2768             Iterator i = set.iterator();
   2769             while (i.hasNext()) {
   2770                 Map.Entry entry = (Map.Entry)i.next();
   2771                 pw.print(Integer.toHexString(((Integer)entry.getKey()).intValue())
   2772                              + ": " + ((((Integer)entry.getValue()).intValue() + 5) / 10)+", ");
   2773             }
   2774             pw.print("\n   Last audible: ");
   2775             set = mLastAudibleIndex.entrySet();
   2776             i = set.iterator();
   2777             while (i.hasNext()) {
   2778                 Map.Entry entry = (Map.Entry)i.next();
   2779                 pw.print(Integer.toHexString(((Integer)entry.getKey()).intValue())
   2780                              + ": " + ((((Integer)entry.getValue()).intValue() + 5) / 10)+", ");
   2781             }
   2782         }
   2783     }
   2784 
   2785     /** Thread that handles native AudioSystem control. */
   2786     private class AudioSystemThread extends Thread {
   2787         AudioSystemThread() {
   2788             super("AudioService");
   2789         }
   2790 
   2791         @Override
   2792         public void run() {
   2793             // Set this thread up so the handler will work on it
   2794             Looper.prepare();
   2795 
   2796             synchronized(AudioService.this) {
   2797                 mAudioHandler = new AudioHandler();
   2798 
   2799                 // Notify that the handler has been created
   2800                 AudioService.this.notify();
   2801             }
   2802 
   2803             // Listen for volume change requests that are set by VolumePanel
   2804             Looper.loop();
   2805         }
   2806     }
   2807 
   2808     /** Handles internal volume messages in separate volume thread. */
   2809     private class AudioHandler extends Handler {
   2810 
   2811         private void setDeviceVolume(VolumeStreamState streamState, int device) {
   2812 
   2813             // Apply volume
   2814             streamState.applyDeviceVolume(device);
   2815 
   2816             // Apply change to all streams using this one as alias
   2817             int numStreamTypes = AudioSystem.getNumStreamTypes();
   2818             for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
   2819                 if (streamType != streamState.mStreamType &&
   2820                         mStreamVolumeAlias[streamType] == streamState.mStreamType) {
   2821                     mStreamStates[streamType].applyDeviceVolume(getDeviceForStream(streamType));
   2822                 }
   2823             }
   2824 
   2825             // Post a persist volume msg
   2826             sendMsg(mAudioHandler,
   2827                     MSG_PERSIST_VOLUME,
   2828                     SENDMSG_QUEUE,
   2829                     PERSIST_CURRENT|PERSIST_LAST_AUDIBLE,
   2830                     device,
   2831                     streamState,
   2832                     PERSIST_DELAY);
   2833 
   2834         }
   2835 
   2836         private void setAllVolumes(VolumeStreamState streamState) {
   2837 
   2838             // Apply volume
   2839             streamState.applyAllVolumes();
   2840 
   2841             // Apply change to all streams using this one as alias
   2842             int numStreamTypes = AudioSystem.getNumStreamTypes();
   2843             for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
   2844                 if (streamType != streamState.mStreamType &&
   2845                         mStreamVolumeAlias[streamType] == streamState.mStreamType) {
   2846                     mStreamStates[streamType].applyAllVolumes();
   2847                 }
   2848             }
   2849         }
   2850 
   2851         private void persistVolume(VolumeStreamState streamState,
   2852                                    int persistType,
   2853                                    int device) {
   2854             if ((persistType & PERSIST_CURRENT) != 0) {
   2855                 System.putInt(mContentResolver,
   2856                           streamState.getSettingNameForDevice(false /* lastAudible */, device),
   2857                           (streamState.getIndex(device, false /* lastAudible */) + 5)/ 10);
   2858             }
   2859             if ((persistType & PERSIST_LAST_AUDIBLE) != 0) {
   2860                 System.putInt(mContentResolver,
   2861                         streamState.getSettingNameForDevice(true /* lastAudible */, device),
   2862                         (streamState.getIndex(device, true  /* lastAudible */) + 5) / 10);
   2863             }
   2864         }
   2865 
   2866         private void persistRingerMode(int ringerMode) {
   2867             System.putInt(mContentResolver, System.MODE_RINGER, ringerMode);
   2868         }
   2869 
   2870         private void playSoundEffect(int effectType, int volume) {
   2871             synchronized (mSoundEffectsLock) {
   2872                 if (mSoundPool == null) {
   2873                     return;
   2874                 }
   2875                 float volFloat;
   2876                 // use default if volume is not specified by caller
   2877                 if (volume < 0) {
   2878                     volFloat = (float)Math.pow(10, SOUND_EFFECT_VOLUME_DB/20);
   2879                 } else {
   2880                     volFloat = (float) volume / 1000.0f;
   2881                 }
   2882 
   2883                 if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) {
   2884                     mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f);
   2885                 } else {
   2886                     MediaPlayer mediaPlayer = new MediaPlayer();
   2887                     try {
   2888                         String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]];
   2889                         mediaPlayer.setDataSource(filePath);
   2890                         mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM);
   2891                         mediaPlayer.prepare();
   2892                         mediaPlayer.setVolume(volFloat, volFloat);
   2893                         mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
   2894                             public void onCompletion(MediaPlayer mp) {
   2895                                 cleanupPlayer(mp);
   2896                             }
   2897                         });
   2898                         mediaPlayer.setOnErrorListener(new OnErrorListener() {
   2899                             public boolean onError(MediaPlayer mp, int what, int extra) {
   2900                                 cleanupPlayer(mp);
   2901                                 return true;
   2902                             }
   2903                         });
   2904                         mediaPlayer.start();
   2905                     } catch (IOException ex) {
   2906                         Log.w(TAG, "MediaPlayer IOException: "+ex);
   2907                     } catch (IllegalArgumentException ex) {
   2908                         Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex);
   2909                     } catch (IllegalStateException ex) {
   2910                         Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
   2911                     }
   2912                 }
   2913             }
   2914         }
   2915 
   2916         private void onHandlePersistMediaButtonReceiver(ComponentName receiver) {
   2917             Settings.System.putString(mContentResolver, Settings.System.MEDIA_BUTTON_RECEIVER,
   2918                     receiver == null ? "" : receiver.flattenToString());
   2919         }
   2920 
   2921         private void cleanupPlayer(MediaPlayer mp) {
   2922             if (mp != null) {
   2923                 try {
   2924                     mp.stop();
   2925                     mp.release();
   2926                 } catch (IllegalStateException ex) {
   2927                     Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
   2928                 }
   2929             }
   2930         }
   2931 
   2932         private void setForceUse(int usage, int config) {
   2933             AudioSystem.setForceUse(usage, config);
   2934         }
   2935 
   2936         @Override
   2937         public void handleMessage(Message msg) {
   2938 
   2939             switch (msg.what) {
   2940 
   2941                 case MSG_SET_DEVICE_VOLUME:
   2942                     setDeviceVolume((VolumeStreamState) msg.obj, msg.arg1);
   2943                     break;
   2944 
   2945                 case MSG_SET_ALL_VOLUMES:
   2946                     setAllVolumes((VolumeStreamState) msg.obj);
   2947                     break;
   2948 
   2949                 case MSG_PERSIST_VOLUME:
   2950                     persistVolume((VolumeStreamState) msg.obj, msg.arg1, msg.arg2);
   2951                     break;
   2952 
   2953                 case MSG_PERSIST_MASTER_VOLUME:
   2954                     Settings.System.putFloat(mContentResolver, Settings.System.VOLUME_MASTER,
   2955                             (float)msg.arg1 / (float)1000.0);
   2956                     break;
   2957 
   2958                 case MSG_PERSIST_MASTER_VOLUME_MUTE:
   2959                     Settings.System.putInt(mContentResolver, Settings.System.VOLUME_MASTER_MUTE,
   2960                             msg.arg1);
   2961                     break;
   2962 
   2963                 case MSG_PERSIST_RINGER_MODE:
   2964                     // note that the value persisted is the current ringer mode, not the
   2965                     // value of ringer mode as of the time the request was made to persist
   2966                     persistRingerMode(getRingerMode());
   2967                     break;
   2968 
   2969                 case MSG_MEDIA_SERVER_DIED:
   2970                     if (!mMediaServerOk) {
   2971                         Log.e(TAG, "Media server died.");
   2972                         // Force creation of new IAudioFlinger interface so that we are notified
   2973                         // when new media_server process is back to life.
   2974                         AudioSystem.setErrorCallback(mAudioSystemCallback);
   2975                         sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SENDMSG_NOOP, 0, 0,
   2976                                 null, 500);
   2977                     }
   2978                     break;
   2979 
   2980                 case MSG_MEDIA_SERVER_STARTED:
   2981                     Log.e(TAG, "Media server started.");
   2982                     // indicate to audio HAL that we start the reconfiguration phase after a media
   2983                     // server crash
   2984                     // Note that MSG_MEDIA_SERVER_STARTED message is only received when the media server
   2985                     // process restarts after a crash, not the first time it is started.
   2986                     AudioSystem.setParameters("restarting=true");
   2987 
   2988                     // Restore device connection states
   2989                     synchronized (mConnectedDevices) {
   2990                         Set set = mConnectedDevices.entrySet();
   2991                         Iterator i = set.iterator();
   2992                         while (i.hasNext()) {
   2993                             Map.Entry device = (Map.Entry)i.next();
   2994                             AudioSystem.setDeviceConnectionState(
   2995                                                             ((Integer)device.getKey()).intValue(),
   2996                                                             AudioSystem.DEVICE_STATE_AVAILABLE,
   2997                                                             (String)device.getValue());
   2998                         }
   2999                     }
   3000                     // Restore call state
   3001                     AudioSystem.setPhoneState(mMode);
   3002 
   3003                     // Restore forced usage for communcations and record
   3004                     AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm);
   3005                     AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm);
   3006 
   3007                     // Restore stream volumes
   3008                     int numStreamTypes = AudioSystem.getNumStreamTypes();
   3009                     for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
   3010                         VolumeStreamState streamState = mStreamStates[streamType];
   3011                         AudioSystem.initStreamVolume(streamType, 0, (streamState.mIndexMax + 5) / 10);
   3012 
   3013                         streamState.applyAllVolumes();
   3014                     }
   3015 
   3016                     // Restore ringer mode
   3017                     setRingerModeInt(getRingerMode(), false);
   3018 
   3019                     // Restore master volume
   3020                     restoreMasterVolume();
   3021 
   3022                     // Reset device orientation (if monitored for this device)
   3023                     if (SystemProperties.getBoolean("ro.audio.monitorOrientation", false)) {
   3024                         setOrientationForAudioSystem();
   3025                     }
   3026 
   3027                     synchronized (mBluetoothA2dpEnabledLock) {
   3028                         AudioSystem.setForceUse(AudioSystem.FOR_MEDIA,
   3029                                 mBluetoothA2dpEnabled ?
   3030                                         AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP);
   3031                     }
   3032                     // indicate the end of reconfiguration phase to audio HAL
   3033                     AudioSystem.setParameters("restarting=false");
   3034                     break;
   3035 
   3036                 case MSG_LOAD_SOUND_EFFECTS:
   3037                     loadSoundEffects();
   3038                     break;
   3039 
   3040                 case MSG_PLAY_SOUND_EFFECT:
   3041                     playSoundEffect(msg.arg1, msg.arg2);
   3042                     break;
   3043 
   3044                 case MSG_BTA2DP_DOCK_TIMEOUT:
   3045                     // msg.obj  == address of BTA2DP device
   3046                     synchronized (mConnectedDevices) {
   3047                         makeA2dpDeviceUnavailableNow( (String) msg.obj );
   3048                     }
   3049                     break;
   3050 
   3051                 case MSG_SET_FORCE_USE:
   3052                     setForceUse(msg.arg1, msg.arg2);
   3053                     break;
   3054 
   3055                 case MSG_PERSIST_MEDIABUTTONRECEIVER:
   3056                     onHandlePersistMediaButtonReceiver( (ComponentName) msg.obj );
   3057                     break;
   3058 
   3059                 case MSG_RCDISPLAY_CLEAR:
   3060                     onRcDisplayClear();
   3061                     break;
   3062 
   3063                 case MSG_RCDISPLAY_UPDATE:
   3064                     // msg.obj is guaranteed to be non null
   3065                     onRcDisplayUpdate( (RemoteControlStackEntry) msg.obj, msg.arg1);
   3066                     break;
   3067 
   3068                 case MSG_BT_HEADSET_CNCT_FAILED:
   3069                     resetBluetoothSco();
   3070                     break;
   3071 
   3072                 case MSG_SET_WIRED_DEVICE_CONNECTION_STATE:
   3073                     onSetWiredDeviceConnectionState(msg.arg1, msg.arg2, (String)msg.obj);
   3074                     mMediaEventWakeLock.release();
   3075                     break;
   3076 
   3077                 case MSG_SET_A2DP_CONNECTION_STATE:
   3078                     onSetA2dpConnectionState((BluetoothDevice)msg.obj, msg.arg1);
   3079                     mMediaEventWakeLock.release();
   3080                     break;
   3081 
   3082                 case MSG_REPORT_NEW_ROUTES: {
   3083                     int N = mRoutesObservers.beginBroadcast();
   3084                     if (N > 0) {
   3085                         AudioRoutesInfo routes;
   3086                         synchronized (mCurAudioRoutes) {
   3087                             routes = new AudioRoutesInfo(mCurAudioRoutes);
   3088                         }
   3089                         while (N > 0) {
   3090                             N--;
   3091                             IAudioRoutesObserver obs = mRoutesObservers.getBroadcastItem(N);
   3092                             try {
   3093                                 obs.dispatchAudioRoutesChanged(routes);
   3094                             } catch (RemoteException e) {
   3095                             }
   3096                         }
   3097                     }
   3098                     mRoutesObservers.finishBroadcast();
   3099                     break;
   3100                 }
   3101 
   3102                 case MSG_REEVALUATE_REMOTE:
   3103                     onReevaluateRemote();
   3104                     break;
   3105 
   3106                 case MSG_RCC_NEW_PLAYBACK_INFO:
   3107                     onNewPlaybackInfoForRcc(msg.arg1 /* rccId */, msg.arg2 /* key */,
   3108                             ((Integer)msg.obj).intValue() /* value */);
   3109                     break;
   3110                 case MSG_RCC_NEW_VOLUME_OBS:
   3111                     onRegisterVolumeObserverForRcc(msg.arg1 /* rccId */,
   3112                             (IRemoteVolumeObserver)msg.obj /* rvo */);
   3113                     break;
   3114             }
   3115         }
   3116     }
   3117 
   3118     private class SettingsObserver extends ContentObserver {
   3119 
   3120         SettingsObserver() {
   3121             super(new Handler());
   3122             mContentResolver.registerContentObserver(Settings.System.getUriFor(
   3123                 Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this);
   3124         }
   3125 
   3126         @Override
   3127         public void onChange(boolean selfChange) {
   3128             super.onChange(selfChange);
   3129             // FIXME This synchronized is not necessary if mSettingsLock only protects mRingerMode.
   3130             //       However there appear to be some missing locks around mRingerModeMutedStreams
   3131             //       and mRingerModeAffectedStreams, so will leave this synchronized for now.
   3132             //       mRingerModeMutedStreams and mMuteAffectedStreams are safe (only accessed once).
   3133             synchronized (mSettingsLock) {
   3134                 int ringerModeAffectedStreams = Settings.System.getInt(mContentResolver,
   3135                        Settings.System.MODE_RINGER_STREAMS_AFFECTED,
   3136                        ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
   3137                        (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)));
   3138                 if (mVoiceCapable) {
   3139                     ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
   3140                 } else {
   3141                     ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
   3142                 }
   3143                 if (ringerModeAffectedStreams != mRingerModeAffectedStreams) {
   3144                     /*
   3145                      * Ensure all stream types that should be affected by ringer mode
   3146                      * are in the proper state.
   3147                      */
   3148                     mRingerModeAffectedStreams = ringerModeAffectedStreams;
   3149                     setRingerModeInt(getRingerMode(), false);
   3150                 }
   3151             }
   3152         }
   3153     }
   3154 
   3155     // must be called synchronized on mConnectedDevices
   3156     private void makeA2dpDeviceAvailable(String address) {
   3157         // enable A2DP before notifying A2DP connection to avoid unecessary processing in
   3158         // audio policy manager
   3159         setBluetoothA2dpOnInt(true);
   3160         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
   3161                 AudioSystem.DEVICE_STATE_AVAILABLE,
   3162                 address);
   3163         // Reset A2DP suspend state each time a new sink is connected
   3164         AudioSystem.setParameters("A2dpSuspended=false");
   3165         mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP),
   3166                 address);
   3167     }
   3168 
   3169     private void sendBecomingNoisyIntent() {
   3170         mContext.sendBroadcast(new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
   3171     }
   3172 
   3173     // must be called synchronized on mConnectedDevices
   3174     private void makeA2dpDeviceUnavailableNow(String address) {
   3175         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
   3176                 AudioSystem.DEVICE_STATE_UNAVAILABLE,
   3177                 address);
   3178         mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
   3179     }
   3180 
   3181     // must be called synchronized on mConnectedDevices
   3182     private void makeA2dpDeviceUnavailableLater(String address) {
   3183         // prevent any activity on the A2DP audio output to avoid unwanted
   3184         // reconnection of the sink.
   3185         AudioSystem.setParameters("A2dpSuspended=true");
   3186         // the device will be made unavailable later, so consider it disconnected right away
   3187         mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
   3188         // send the delayed message to make the device unavailable later
   3189         Message msg = mAudioHandler.obtainMessage(MSG_BTA2DP_DOCK_TIMEOUT, address);
   3190         mAudioHandler.sendMessageDelayed(msg, BTA2DP_DOCK_TIMEOUT_MILLIS);
   3191 
   3192     }
   3193 
   3194     // must be called synchronized on mConnectedDevices
   3195     private void cancelA2dpDeviceTimeout() {
   3196         mAudioHandler.removeMessages(MSG_BTA2DP_DOCK_TIMEOUT);
   3197     }
   3198 
   3199     // must be called synchronized on mConnectedDevices
   3200     private boolean hasScheduledA2dpDockTimeout() {
   3201         return mAudioHandler.hasMessages(MSG_BTA2DP_DOCK_TIMEOUT);
   3202     }
   3203 
   3204     private void onSetA2dpConnectionState(BluetoothDevice btDevice, int state)
   3205     {
   3206         if (btDevice == null) {
   3207             return;
   3208         }
   3209         String address = btDevice.getAddress();
   3210         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
   3211             address = "";
   3212         }
   3213         synchronized (mConnectedDevices) {
   3214             boolean isConnected =
   3215                 (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
   3216                  mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP).equals(address));
   3217 
   3218             if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
   3219                 if (btDevice.isBluetoothDock()) {
   3220                     if (state == BluetoothProfile.STATE_DISCONNECTED) {
   3221                         // introduction of a delay for transient disconnections of docks when
   3222                         // power is rapidly turned off/on, this message will be canceled if
   3223                         // we reconnect the dock under a preset delay
   3224                         makeA2dpDeviceUnavailableLater(address);
   3225                         // the next time isConnected is evaluated, it will be false for the dock
   3226                     }
   3227                 } else {
   3228                     makeA2dpDeviceUnavailableNow(address);
   3229                 }
   3230                 synchronized (mCurAudioRoutes) {
   3231                     if (mCurAudioRoutes.mBluetoothName != null) {
   3232                         mCurAudioRoutes.mBluetoothName = null;
   3233                         sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
   3234                                 SENDMSG_NOOP, 0, 0, null, 0);
   3235                     }
   3236                 }
   3237             } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
   3238                 if (btDevice.isBluetoothDock()) {
   3239                     // this could be a reconnection after a transient disconnection
   3240                     cancelA2dpDeviceTimeout();
   3241                     mDockAddress = address;
   3242                 } else {
   3243                     // this could be a connection of another A2DP device before the timeout of
   3244                     // a dock: cancel the dock timeout, and make the dock unavailable now
   3245                     if(hasScheduledA2dpDockTimeout()) {
   3246                         cancelA2dpDeviceTimeout();
   3247                         makeA2dpDeviceUnavailableNow(mDockAddress);
   3248                     }
   3249                 }
   3250                 makeA2dpDeviceAvailable(address);
   3251                 synchronized (mCurAudioRoutes) {
   3252                     String name = btDevice.getAliasName();
   3253                     if (!TextUtils.equals(mCurAudioRoutes.mBluetoothName, name)) {
   3254                         mCurAudioRoutes.mBluetoothName = name;
   3255                         sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
   3256                                 SENDMSG_NOOP, 0, 0, null, 0);
   3257                     }
   3258                 }
   3259             }
   3260         }
   3261     }
   3262 
   3263     private boolean handleDeviceConnection(boolean connected, int device, String params) {
   3264         synchronized (mConnectedDevices) {
   3265             boolean isConnected = (mConnectedDevices.containsKey(device) &&
   3266                     (params.isEmpty() || mConnectedDevices.get(device).equals(params)));
   3267 
   3268             if (isConnected && !connected) {
   3269                 AudioSystem.setDeviceConnectionState(device,
   3270                                               AudioSystem.DEVICE_STATE_UNAVAILABLE,
   3271                                               mConnectedDevices.get(device));
   3272                  mConnectedDevices.remove(device);
   3273                  return true;
   3274             } else if (!isConnected && connected) {
   3275                  AudioSystem.setDeviceConnectionState(device,
   3276                                                       AudioSystem.DEVICE_STATE_AVAILABLE,
   3277                                                       params);
   3278                  mConnectedDevices.put(new Integer(device), params);
   3279                  return true;
   3280             }
   3281         }
   3282         return false;
   3283     }
   3284 
   3285     // Devices which removal triggers intent ACTION_AUDIO_BECOMING_NOISY. The intent is only
   3286     // sent if none of these devices is connected.
   3287     int mBecomingNoisyIntentDevices =
   3288             AudioSystem.DEVICE_OUT_WIRED_HEADSET | AudioSystem.DEVICE_OUT_WIRED_HEADPHONE |
   3289             AudioSystem.DEVICE_OUT_ALL_A2DP;
   3290 
   3291     // must be called before removing the device from mConnectedDevices
   3292     private int checkSendBecomingNoisyIntent(int device, int state) {
   3293         int delay = 0;
   3294         if ((state == 0) && ((device & mBecomingNoisyIntentDevices) != 0)) {
   3295             int devices = 0;
   3296             for (int dev : mConnectedDevices.keySet()) {
   3297                 if ((dev & mBecomingNoisyIntentDevices) != 0) {
   3298                    devices |= dev;
   3299                 }
   3300             }
   3301             if (devices == device) {
   3302                 delay = 1000;
   3303                 sendBecomingNoisyIntent();
   3304             }
   3305         }
   3306 
   3307         if (mAudioHandler.hasMessages(MSG_SET_A2DP_CONNECTION_STATE) ||
   3308                 mAudioHandler.hasMessages(MSG_SET_WIRED_DEVICE_CONNECTION_STATE)) {
   3309             delay = 1000;
   3310         }
   3311         return delay;
   3312     }
   3313 
   3314     private void sendDeviceConnectionIntent(int device, int state, String name)
   3315     {
   3316         Intent intent = new Intent();
   3317 
   3318         intent.putExtra("state", state);
   3319         intent.putExtra("name", name);
   3320         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
   3321 
   3322         int connType = 0;
   3323 
   3324         if (device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) {
   3325             connType = AudioRoutesInfo.MAIN_HEADSET;
   3326             intent.setAction(Intent.ACTION_HEADSET_PLUG);
   3327             intent.putExtra("microphone", 1);
   3328         } else if (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) {
   3329             connType = AudioRoutesInfo.MAIN_HEADPHONES;
   3330             intent.setAction(Intent.ACTION_HEADSET_PLUG);
   3331             intent.putExtra("microphone", 0);
   3332         } else if (device == AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET) {
   3333             connType = AudioRoutesInfo.MAIN_DOCK_SPEAKERS;
   3334             intent.setAction(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG);
   3335         } else if (device == AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET) {
   3336             connType = AudioRoutesInfo.MAIN_DOCK_SPEAKERS;
   3337             intent.setAction(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG);
   3338         } else if (device == AudioSystem.DEVICE_OUT_AUX_DIGITAL) {
   3339             connType = AudioRoutesInfo.MAIN_HDMI;
   3340             intent.setAction(Intent.ACTION_HDMI_AUDIO_PLUG);
   3341         }
   3342 
   3343         synchronized (mCurAudioRoutes) {
   3344             if (connType != 0) {
   3345                 int newConn = mCurAudioRoutes.mMainType;
   3346                 if (state != 0) {
   3347                     newConn |= connType;
   3348                 } else {
   3349                     newConn &= ~connType;
   3350                 }
   3351                 if (newConn != mCurAudioRoutes.mMainType) {
   3352                     mCurAudioRoutes.mMainType = newConn;
   3353                     sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
   3354                             SENDMSG_NOOP, 0, 0, null, 0);
   3355                 }
   3356             }
   3357         }
   3358 
   3359         ActivityManagerNative.broadcastStickyIntent(intent, null);
   3360     }
   3361 
   3362     private void onSetWiredDeviceConnectionState(int device, int state, String name)
   3363     {
   3364         synchronized (mConnectedDevices) {
   3365             if ((state == 0) && ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
   3366                     (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE))) {
   3367                 setBluetoothA2dpOnInt(true);
   3368             }
   3369             handleDeviceConnection((state == 1), device, "");
   3370             if ((state != 0) && ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
   3371                     (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE))) {
   3372                 setBluetoothA2dpOnInt(false);
   3373             }
   3374             sendDeviceConnectionIntent(device, state, name);
   3375         }
   3376     }
   3377 
   3378     /* cache of the address of the last dock the device was connected to */
   3379     private String mDockAddress;
   3380 
   3381     /**
   3382      * Receiver for misc intent broadcasts the Phone app cares about.
   3383      */
   3384     private class AudioServiceBroadcastReceiver extends BroadcastReceiver {
   3385         @Override
   3386         public void onReceive(Context context, Intent intent) {
   3387             String action = intent.getAction();
   3388             int device;
   3389             int state;
   3390 
   3391             if (action.equals(Intent.ACTION_DOCK_EVENT)) {
   3392                 int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
   3393                         Intent.EXTRA_DOCK_STATE_UNDOCKED);
   3394                 int config;
   3395                 switch (dockState) {
   3396                     case Intent.EXTRA_DOCK_STATE_DESK:
   3397                         config = AudioSystem.FORCE_BT_DESK_DOCK;
   3398                         break;
   3399                     case Intent.EXTRA_DOCK_STATE_CAR:
   3400                         config = AudioSystem.FORCE_BT_CAR_DOCK;
   3401                         break;
   3402                     case Intent.EXTRA_DOCK_STATE_LE_DESK:
   3403                         config = AudioSystem.FORCE_ANALOG_DOCK;
   3404                         break;
   3405                     case Intent.EXTRA_DOCK_STATE_HE_DESK:
   3406                         config = AudioSystem.FORCE_DIGITAL_DOCK;
   3407                         break;
   3408                     case Intent.EXTRA_DOCK_STATE_UNDOCKED:
   3409                     default:
   3410                         config = AudioSystem.FORCE_NONE;
   3411                 }
   3412                 AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
   3413             } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
   3414                 state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
   3415                                                BluetoothProfile.STATE_DISCONNECTED);
   3416                 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
   3417                 String address = null;
   3418 
   3419                 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
   3420                 if (btDevice == null) {
   3421                     return;
   3422                 }
   3423 
   3424                 address = btDevice.getAddress();
   3425                 BluetoothClass btClass = btDevice.getBluetoothClass();
   3426                 if (btClass != null) {
   3427                     switch (btClass.getDeviceClass()) {
   3428                     case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
   3429                     case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
   3430                         device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
   3431                         break;
   3432                     case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
   3433                         device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
   3434                         break;
   3435                     }
   3436                 }
   3437 
   3438                 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
   3439                     address = "";
   3440                 }
   3441 
   3442                 boolean connected = (state == BluetoothProfile.STATE_CONNECTED);
   3443                 if (handleDeviceConnection(connected, device, address)) {
   3444                     synchronized (mScoClients) {
   3445                         if (connected) {
   3446                             mBluetoothHeadsetDevice = btDevice;
   3447                         } else {
   3448                             mBluetoothHeadsetDevice = null;
   3449                             resetBluetoothSco();
   3450                         }
   3451                     }
   3452                 }
   3453             } else if (action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ||
   3454                            action.equals(Intent.ACTION_USB_AUDIO_DEVICE_PLUG)) {
   3455                 state = intent.getIntExtra("state", 0);
   3456                 int alsaCard = intent.getIntExtra("card", -1);
   3457                 int alsaDevice = intent.getIntExtra("device", -1);
   3458                 String params = (alsaCard == -1 && alsaDevice == -1 ? ""
   3459                                     : "card=" + alsaCard + ";device=" + alsaDevice);
   3460                 device = action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ?
   3461                         AudioSystem.DEVICE_OUT_USB_ACCESSORY : AudioSystem.DEVICE_OUT_USB_DEVICE;
   3462                 Log.v(TAG, "Broadcast Receiver: Got "
   3463                         + (action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ?
   3464                               "ACTION_USB_AUDIO_ACCESSORY_PLUG" : "ACTION_USB_AUDIO_DEVICE_PLUG")
   3465                         + ", state = " + state + ", card: " + alsaCard + ", device: " + alsaDevice);
   3466                 handleDeviceConnection((state == 1), device, params);
   3467             } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
   3468                 boolean broadcast = false;
   3469                 int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
   3470                 synchronized (mScoClients) {
   3471                     int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
   3472                     // broadcast intent if the connection was initated by AudioService
   3473                     if (!mScoClients.isEmpty() &&
   3474                             (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
   3475                              mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
   3476                              mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
   3477                         broadcast = true;
   3478                     }
   3479                     switch (btState) {
   3480                     case BluetoothHeadset.STATE_AUDIO_CONNECTED:
   3481                         scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
   3482                         if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
   3483                             mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
   3484                             mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
   3485                             mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
   3486                         }
   3487                         break;
   3488                     case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
   3489                         scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
   3490                         mScoAudioState = SCO_STATE_INACTIVE;
   3491                         clearAllScoClients(0, false);
   3492                         break;
   3493                     case BluetoothHeadset.STATE_AUDIO_CONNECTING:
   3494                         if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
   3495                             mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
   3496                             mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
   3497                             mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
   3498                         }
   3499                     default:
   3500                         // do not broadcast CONNECTING or invalid state
   3501                         broadcast = false;
   3502                         break;
   3503                     }
   3504                 }
   3505                 if (broadcast) {
   3506                     broadcastScoConnectionState(scoAudioState);
   3507                     //FIXME: this is to maintain compatibility with deprecated intent
   3508                     // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
   3509                     Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
   3510                     newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, scoAudioState);
   3511                     mContext.sendStickyBroadcast(newIntent);
   3512                 }
   3513             } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
   3514                 mBootCompleted = true;
   3515                 sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_NOOP,
   3516                         0, 0, null, 0);
   3517 
   3518                 mKeyguardManager =
   3519                         (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
   3520                 mScoConnectionState = AudioManager.SCO_AUDIO_STATE_ERROR;
   3521                 resetBluetoothSco();
   3522                 getBluetoothHeadset();
   3523                 //FIXME: this is to maintain compatibility with deprecated intent
   3524                 // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
   3525                 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
   3526                 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
   3527                         AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
   3528                 mContext.sendStickyBroadcast(newIntent);
   3529 
   3530                 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
   3531                 if (adapter != null) {
   3532                     adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
   3533                                             BluetoothProfile.A2DP);
   3534                 }
   3535             } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) {
   3536                 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
   3537                     // a package is being removed, not replaced
   3538                     String packageName = intent.getData().getSchemeSpecificPart();
   3539                     if (packageName != null) {
   3540                         removeMediaButtonReceiverForPackage(packageName);
   3541                     }
   3542                 }
   3543             } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
   3544                 AudioSystem.setParameters("screen_state=on");
   3545             } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
   3546                 AudioSystem.setParameters("screen_state=off");
   3547             } else if (action.equalsIgnoreCase(Intent.ACTION_CONFIGURATION_CHANGED)) {
   3548                 handleConfigurationChanged(context);
   3549             }
   3550         }
   3551     }
   3552 
   3553     //==========================================================================================
   3554     // AudioFocus
   3555     //==========================================================================================
   3556 
   3557     /* constant to identify focus stack entry that is used to hold the focus while the phone
   3558      * is ringing or during a call. Used by com.android.internal.telephony.CallManager when
   3559      * entering and exiting calls.
   3560      */
   3561     public final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls";
   3562 
   3563     private final static Object mAudioFocusLock = new Object();
   3564 
   3565     private final static Object mRingingLock = new Object();
   3566 
   3567     private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
   3568         @Override
   3569         public void onCallStateChanged(int state, String incomingNumber) {
   3570             if (state == TelephonyManager.CALL_STATE_RINGING) {
   3571                 //Log.v(TAG, " CALL_STATE_RINGING");
   3572                 synchronized(mRingingLock) {
   3573                     mIsRinging = true;
   3574                 }
   3575             } else if ((state == TelephonyManager.CALL_STATE_OFFHOOK)
   3576                     || (state == TelephonyManager.CALL_STATE_IDLE)) {
   3577                 synchronized(mRingingLock) {
   3578                     mIsRinging = false;
   3579                 }
   3580             }
   3581         }
   3582     };
   3583 
   3584     private void notifyTopOfAudioFocusStack() {
   3585         // notify the top of the stack it gained focus
   3586         if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
   3587             if (canReassignAudioFocus()) {
   3588                 try {
   3589                     mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
   3590                             AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId);
   3591                 } catch (RemoteException e) {
   3592                     Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e);
   3593                     e.printStackTrace();
   3594                 }
   3595             }
   3596         }
   3597     }
   3598 
   3599     private static class FocusStackEntry {
   3600         public int mStreamType = -1;// no stream type
   3601         public IAudioFocusDispatcher mFocusDispatcher = null;
   3602         public IBinder mSourceRef = null;
   3603         public String mClientId;
   3604         public int mFocusChangeType;
   3605         public AudioFocusDeathHandler mHandler;
   3606         public String mPackageName;
   3607         public int mCallingUid;
   3608 
   3609         public FocusStackEntry() {
   3610         }
   3611 
   3612         public FocusStackEntry(int streamType, int duration,
   3613                 IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr,
   3614                 String pn, int uid) {
   3615             mStreamType = streamType;
   3616             mFocusDispatcher = afl;
   3617             mSourceRef = source;
   3618             mClientId = id;
   3619             mFocusChangeType = duration;
   3620             mHandler = hdlr;
   3621             mPackageName = pn;
   3622             mCallingUid = uid;
   3623         }
   3624 
   3625         public void unlinkToDeath() {
   3626             try {
   3627                 if (mSourceRef != null && mHandler != null) {
   3628                     mSourceRef.unlinkToDeath(mHandler, 0);
   3629                     mHandler = null;
   3630                 }
   3631             } catch (java.util.NoSuchElementException e) {
   3632                 Log.e(TAG, "Encountered " + e + " in FocusStackEntry.unlinkToDeath()");
   3633             }
   3634         }
   3635 
   3636         @Override
   3637         protected void finalize() throws Throwable {
   3638             unlinkToDeath(); // unlink exception handled inside method
   3639             super.finalize();
   3640         }
   3641     }
   3642 
   3643     private final Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>();
   3644 
   3645     /**
   3646      * Helper function:
   3647      * Display in the log the current entries in the audio focus stack
   3648      */
   3649     private void dumpFocusStack(PrintWriter pw) {
   3650         pw.println("\nAudio Focus stack entries:");
   3651         synchronized(mAudioFocusLock) {
   3652             Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
   3653             while(stackIterator.hasNext()) {
   3654                 FocusStackEntry fse = stackIterator.next();
   3655                 pw.println("     source:" + fse.mSourceRef + " -- client: " + fse.mClientId
   3656                         + " -- duration: " + fse.mFocusChangeType
   3657                         + " -- uid: " + fse.mCallingUid);
   3658             }
   3659         }
   3660     }
   3661 
   3662     /**
   3663      * Helper function:
   3664      * Called synchronized on mAudioFocusLock
   3665      * Remove a focus listener from the focus stack.
   3666      * @param focusListenerToRemove the focus listener
   3667      * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
   3668      *   focus, notify the next item in the stack it gained focus.
   3669      */
   3670     private void removeFocusStackEntry(String clientToRemove, boolean signal) {
   3671         // is the current top of the focus stack abandoning focus? (because of request, not death)
   3672         if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove))
   3673         {
   3674             //Log.i(TAG, "   removeFocusStackEntry() removing top of stack");
   3675             FocusStackEntry fse = mFocusStack.pop();
   3676             fse.unlinkToDeath();
   3677             if (signal) {
   3678                 // notify the new top of the stack it gained focus
   3679                 notifyTopOfAudioFocusStack();
   3680                 // there's a new top of the stack, let the remote control know
   3681                 synchronized(mRCStack) {
   3682                     checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
   3683                 }
   3684             }
   3685         } else {
   3686             // focus is abandoned by a client that's not at the top of the stack,
   3687             // no need to update focus.
   3688             Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
   3689             while(stackIterator.hasNext()) {
   3690                 FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
   3691                 if(fse.mClientId.equals(clientToRemove)) {
   3692                     Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
   3693                             + fse.mClientId);
   3694                     stackIterator.remove();
   3695                     fse.unlinkToDeath();
   3696                 }
   3697             }
   3698         }
   3699     }
   3700 
   3701     /**
   3702      * Helper function:
   3703      * Called synchronized on mAudioFocusLock
   3704      * Remove focus listeners from the focus stack for a particular client when it has died.
   3705      */
   3706     private void removeFocusStackEntryForClient(IBinder cb) {
   3707         // is the owner of the audio focus part of the client to remove?
   3708         boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
   3709                 mFocusStack.peek().mSourceRef.equals(cb);
   3710         Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
   3711         while(stackIterator.hasNext()) {
   3712             FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
   3713             if(fse.mSourceRef.equals(cb)) {
   3714                 Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
   3715                         + fse.mClientId);
   3716                 stackIterator.remove();
   3717                 // the client just died, no need to unlink to its death
   3718             }
   3719         }
   3720         if (isTopOfStackForClientToRemove) {
   3721             // we removed an entry at the top of the stack:
   3722             //  notify the new top of the stack it gained focus.
   3723             notifyTopOfAudioFocusStack();
   3724             // there's a new top of the stack, let the remote control know
   3725             synchronized(mRCStack) {
   3726                 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
   3727             }
   3728         }
   3729     }
   3730 
   3731     /**
   3732      * Helper function:
   3733      * Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
   3734      */
   3735     private boolean canReassignAudioFocus() {
   3736         // focus requests are rejected during a phone call or when the phone is ringing
   3737         // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus
   3738         if (!mFocusStack.isEmpty() && IN_VOICE_COMM_FOCUS_ID.equals(mFocusStack.peek().mClientId)) {
   3739             return false;
   3740         }
   3741         return true;
   3742     }
   3743 
   3744     /**
   3745      * Inner class to monitor audio focus client deaths, and remove them from the audio focus
   3746      * stack if necessary.
   3747      */
   3748     private class AudioFocusDeathHandler implements IBinder.DeathRecipient {
   3749         private IBinder mCb; // To be notified of client's death
   3750 
   3751         AudioFocusDeathHandler(IBinder cb) {
   3752             mCb = cb;
   3753         }
   3754 
   3755         public void binderDied() {
   3756             synchronized(mAudioFocusLock) {
   3757                 Log.w(TAG, "  AudioFocus   audio focus client died");
   3758                 removeFocusStackEntryForClient(mCb);
   3759             }
   3760         }
   3761 
   3762         public IBinder getBinder() {
   3763             return mCb;
   3764         }
   3765     }
   3766 
   3767 
   3768     /** @see AudioManager#requestAudioFocus(IAudioFocusDispatcher, int, int) */
   3769     public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb,
   3770             IAudioFocusDispatcher fd, String clientId, String callingPackageName) {
   3771         Log.i(TAG, " AudioFocus  requestAudioFocus() from " + clientId);
   3772         // the main stream type for the audio focus request is currently not used. It may
   3773         // potentially be used to handle multiple stream type-dependent audio focuses.
   3774 
   3775         // we need a valid binder callback for clients
   3776         if (!cb.pingBinder()) {
   3777             Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
   3778             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
   3779         }
   3780 
   3781         synchronized(mAudioFocusLock) {
   3782             if (!canReassignAudioFocus()) {
   3783                 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
   3784             }
   3785 
   3786             // handle the potential premature death of the new holder of the focus
   3787             // (premature death == death before abandoning focus)
   3788             // Register for client death notification
   3789             AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
   3790             try {
   3791                 cb.linkToDeath(afdh, 0);
   3792             } catch (RemoteException e) {
   3793                 // client has already died!
   3794                 Log.w(TAG, "AudioFocus  requestAudioFocus() could not link to "+cb+" binder death");
   3795                 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
   3796             }
   3797 
   3798             if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) {
   3799                 // if focus is already owned by this client and the reason for acquiring the focus
   3800                 // hasn't changed, don't do anything
   3801                 if (mFocusStack.peek().mFocusChangeType == focusChangeHint) {
   3802                     // unlink death handler so it can be gc'ed.
   3803                     // linkToDeath() creates a JNI global reference preventing collection.
   3804                     cb.unlinkToDeath(afdh, 0);
   3805                     return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
   3806                 }
   3807                 // the reason for the audio focus request has changed: remove the current top of
   3808                 // stack and respond as if we had a new focus owner
   3809                 FocusStackEntry fse = mFocusStack.pop();
   3810                 fse.unlinkToDeath();
   3811             }
   3812 
   3813             // notify current top of stack it is losing focus
   3814             if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
   3815                 try {
   3816                     mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
   3817                             -1 * focusChangeHint, // loss and gain codes are inverse of each other
   3818                             mFocusStack.peek().mClientId);
   3819                 } catch (RemoteException e) {
   3820                     Log.e(TAG, " Failure to signal loss of focus due to "+ e);
   3821                     e.printStackTrace();
   3822                 }
   3823             }
   3824 
   3825             // focus requester might already be somewhere below in the stack, remove it
   3826             removeFocusStackEntry(clientId, false /* signal */);
   3827 
   3828             // push focus requester at the top of the audio focus stack
   3829             mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, fd, cb,
   3830                     clientId, afdh, callingPackageName, Binder.getCallingUid()));
   3831 
   3832             // there's a new top of the stack, let the remote control know
   3833             synchronized(mRCStack) {
   3834                 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
   3835             }
   3836         }//synchronized(mAudioFocusLock)
   3837 
   3838         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
   3839     }
   3840 
   3841     /** @see AudioManager#abandonAudioFocus(IAudioFocusDispatcher) */
   3842     public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) {
   3843         Log.i(TAG, " AudioFocus  abandonAudioFocus() from " + clientId);
   3844         try {
   3845             // this will take care of notifying the new focus owner if needed
   3846             synchronized(mAudioFocusLock) {
   3847                 removeFocusStackEntry(clientId, true);
   3848             }
   3849         } catch (java.util.ConcurrentModificationException cme) {
   3850             // Catching this exception here is temporary. It is here just to prevent
   3851             // a crash seen when the "Silent" notification is played. This is believed to be fixed
   3852             // but this try catch block is left just to be safe.
   3853             Log.e(TAG, "FATAL EXCEPTION AudioFocus  abandonAudioFocus() caused " + cme);
   3854             cme.printStackTrace();
   3855         }
   3856 
   3857         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
   3858     }
   3859 
   3860 
   3861     public void unregisterAudioFocusClient(String clientId) {
   3862         synchronized(mAudioFocusLock) {
   3863             removeFocusStackEntry(clientId, false);
   3864         }
   3865     }
   3866 
   3867 
   3868     //==========================================================================================
   3869     // RemoteControl
   3870     //==========================================================================================
   3871     public void dispatchMediaKeyEvent(KeyEvent keyEvent) {
   3872         filterMediaKeyEvent(keyEvent, false /*needWakeLock*/);
   3873     }
   3874 
   3875     public void dispatchMediaKeyEventUnderWakelock(KeyEvent keyEvent) {
   3876         filterMediaKeyEvent(keyEvent, true /*needWakeLock*/);
   3877     }
   3878 
   3879     private void filterMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
   3880         // sanity check on the incoming key event
   3881         if (!isValidMediaKeyEvent(keyEvent)) {
   3882             Log.e(TAG, "not dispatching invalid media key event " + keyEvent);
   3883             return;
   3884         }
   3885         // event filtering for telephony
   3886         synchronized(mRingingLock) {
   3887             synchronized(mRCStack) {
   3888                 if ((mMediaReceiverForCalls != null) &&
   3889                         (mIsRinging || (getMode() == AudioSystem.MODE_IN_CALL))) {
   3890                     dispatchMediaKeyEventForCalls(keyEvent, needWakeLock);
   3891                     return;
   3892                 }
   3893             }
   3894         }
   3895         // event filtering based on voice-based interactions
   3896         if (isValidVoiceInputKeyCode(keyEvent.getKeyCode())) {
   3897             filterVoiceInputKeyEvent(keyEvent, needWakeLock);
   3898         } else {
   3899             dispatchMediaKeyEvent(keyEvent, needWakeLock);
   3900         }
   3901     }
   3902 
   3903     /**
   3904      * Handles the dispatching of the media button events to the telephony package.
   3905      * Precondition: mMediaReceiverForCalls != null
   3906      * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons
   3907      * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
   3908      *     is dispatched.
   3909      */
   3910     private void dispatchMediaKeyEventForCalls(KeyEvent keyEvent, boolean needWakeLock) {
   3911         Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
   3912         keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
   3913         keyIntent.setPackage(mMediaReceiverForCalls.getPackageName());
   3914         if (needWakeLock) {
   3915             mMediaEventWakeLock.acquire();
   3916             keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
   3917         }
   3918         mContext.sendOrderedBroadcast(keyIntent, null, mKeyEventDone,
   3919                 mAudioHandler, Activity.RESULT_OK, null, null);
   3920     }
   3921 
   3922     /**
   3923      * Handles the dispatching of the media button events to one of the registered listeners,
   3924      * or if there was none, broadcast an ACTION_MEDIA_BUTTON intent to the rest of the system.
   3925      * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons
   3926      * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
   3927      *     is dispatched.
   3928      */
   3929     private void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
   3930         if (needWakeLock) {
   3931             mMediaEventWakeLock.acquire();
   3932         }
   3933         Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
   3934         keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
   3935         synchronized(mRCStack) {
   3936             if (!mRCStack.empty()) {
   3937                 // send the intent that was registered by the client
   3938                 try {
   3939                     mRCStack.peek().mMediaIntent.send(mContext,
   3940                             needWakeLock ? WAKELOCK_RELEASE_ON_FINISHED : 0 /*code*/,
   3941                             keyIntent, AudioService.this, mAudioHandler);
   3942                 } catch (CanceledException e) {
   3943                     Log.e(TAG, "Error sending pending intent " + mRCStack.peek());
   3944                     e.printStackTrace();
   3945                 }
   3946             } else {
   3947                 // legacy behavior when nobody registered their media button event receiver
   3948                 //    through AudioManager
   3949                 if (needWakeLock) {
   3950                     keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
   3951                 }
   3952                 mContext.sendOrderedBroadcast(keyIntent, null, mKeyEventDone,
   3953                         mAudioHandler, Activity.RESULT_OK, null, null);
   3954             }
   3955         }
   3956     }
   3957 
   3958     /**
   3959      * The different actions performed in response to a voice button key event.
   3960      */
   3961     private final static int VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS = 1;
   3962     private final static int VOICEBUTTON_ACTION_START_VOICE_INPUT = 2;
   3963     private final static int VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS = 3;
   3964 
   3965     private final Object mVoiceEventLock = new Object();
   3966     private boolean mVoiceButtonDown;
   3967     private boolean mVoiceButtonHandled;
   3968 
   3969     /**
   3970      * Filter key events that may be used for voice-based interactions
   3971      * @param keyEvent a non-null KeyEvent whose key code is that of one of the supported
   3972      *    media buttons that can be used to trigger voice-based interactions.
   3973      * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
   3974      *     is dispatched.
   3975      */
   3976     private void filterVoiceInputKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
   3977         if (DEBUG_RC) {
   3978             Log.v(TAG, "voice input key event: " + keyEvent + ", needWakeLock=" + needWakeLock);
   3979         }
   3980 
   3981         int voiceButtonAction = VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS;
   3982         int keyAction = keyEvent.getAction();
   3983         synchronized (mVoiceEventLock) {
   3984             if (keyAction == KeyEvent.ACTION_DOWN) {
   3985                 if (keyEvent.getRepeatCount() == 0) {
   3986                     // initial down
   3987                     mVoiceButtonDown = true;
   3988                     mVoiceButtonHandled = false;
   3989                 } else if (mVoiceButtonDown && !mVoiceButtonHandled
   3990                         && (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
   3991                     // long-press, start voice-based interactions
   3992                     mVoiceButtonHandled = true;
   3993                     voiceButtonAction = VOICEBUTTON_ACTION_START_VOICE_INPUT;
   3994                 }
   3995             } else if (keyAction == KeyEvent.ACTION_UP) {
   3996                 if (mVoiceButtonDown) {
   3997                     // voice button up
   3998                     mVoiceButtonDown = false;
   3999                     if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
   4000                         voiceButtonAction = VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS;
   4001                     }
   4002                 }
   4003             }
   4004         }//synchronized (mVoiceEventLock)
   4005 
   4006         // take action after media button event filtering for voice-based interactions
   4007         switch (voiceButtonAction) {
   4008             case VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS:
   4009                 if (DEBUG_RC) Log.v(TAG, "   ignore key event");
   4010                 break;
   4011             case VOICEBUTTON_ACTION_START_VOICE_INPUT:
   4012                 if (DEBUG_RC) Log.v(TAG, "   start voice-based interactions");
   4013                 // then start the voice-based interactions
   4014                 startVoiceBasedInteractions(needWakeLock);
   4015                 break;
   4016             case VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS:
   4017                 if (DEBUG_RC) Log.v(TAG, "   send simulated key event");
   4018                 sendSimulatedMediaButtonEvent(keyEvent, needWakeLock);
   4019                 break;
   4020         }
   4021     }
   4022 
   4023     private void sendSimulatedMediaButtonEvent(KeyEvent originalKeyEvent, boolean needWakeLock) {
   4024         // send DOWN event
   4025         KeyEvent keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_DOWN);
   4026         dispatchMediaKeyEvent(keyEvent, needWakeLock);
   4027         // send UP event
   4028         keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_UP);
   4029         dispatchMediaKeyEvent(keyEvent, needWakeLock);
   4030 
   4031     }
   4032 
   4033 
   4034     private static boolean isValidMediaKeyEvent(KeyEvent keyEvent) {
   4035         if (keyEvent == null) {
   4036             return false;
   4037         }
   4038         final int keyCode = keyEvent.getKeyCode();
   4039         switch (keyCode) {
   4040             case KeyEvent.KEYCODE_MUTE:
   4041             case KeyEvent.KEYCODE_HEADSETHOOK:
   4042             case KeyEvent.KEYCODE_MEDIA_PLAY:
   4043             case KeyEvent.KEYCODE_MEDIA_PAUSE:
   4044             case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
   4045             case KeyEvent.KEYCODE_MEDIA_STOP:
   4046             case KeyEvent.KEYCODE_MEDIA_NEXT:
   4047             case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
   4048             case KeyEvent.KEYCODE_MEDIA_REWIND:
   4049             case KeyEvent.KEYCODE_MEDIA_RECORD:
   4050             case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
   4051             case KeyEvent.KEYCODE_MEDIA_CLOSE:
   4052             case KeyEvent.KEYCODE_MEDIA_EJECT:
   4053                 break;
   4054             default:
   4055                 return false;
   4056         }
   4057         return true;
   4058     }
   4059 
   4060     /**
   4061      * Checks whether the given key code is one that can trigger the launch of voice-based
   4062      *   interactions.
   4063      * @param keyCode the key code associated with the key event
   4064      * @return true if the key is one of the supported voice-based interaction triggers
   4065      */
   4066     private static boolean isValidVoiceInputKeyCode(int keyCode) {
   4067         if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK) {
   4068             return true;
   4069         } else {
   4070             return false;
   4071         }
   4072     }
   4073 
   4074     /**
   4075      * Tell the system to start voice-based interactions / voice commands
   4076      */
   4077     private void startVoiceBasedInteractions(boolean needWakeLock) {
   4078         Intent voiceIntent = null;
   4079         // select which type of search to launch:
   4080         // - screen on and device unlocked: action is ACTION_WEB_SEARCH
   4081         // - device locked or screen off: action is ACTION_VOICE_SEARCH_HANDS_FREE
   4082         //    with EXTRA_SECURE set to true if the device is securely locked
   4083         PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
   4084         boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
   4085         if (!isLocked && pm.isScreenOn()) {
   4086             voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
   4087         } else {
   4088             voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
   4089             voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
   4090                     isLocked && mKeyguardManager.isKeyguardSecure());
   4091         }
   4092         // start the search activity
   4093         if (needWakeLock) {
   4094             mMediaEventWakeLock.acquire();
   4095         }
   4096         try {
   4097             if (voiceIntent != null) {
   4098                 voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
   4099                         | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
   4100                 mContext.startActivity(voiceIntent);
   4101             }
   4102         } catch (ActivityNotFoundException e) {
   4103             Log.w(TAG, "No activity for search: " + e);
   4104         } finally {
   4105             if (needWakeLock) {
   4106                 mMediaEventWakeLock.release();
   4107             }
   4108         }
   4109     }
   4110 
   4111     private PowerManager.WakeLock mMediaEventWakeLock;
   4112 
   4113     private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; //magic number
   4114 
   4115     // only set when wakelock was acquired, no need to check value when received
   4116     private static final String EXTRA_WAKELOCK_ACQUIRED =
   4117             "android.media.AudioService.WAKELOCK_ACQUIRED";
   4118 
   4119     public void onSendFinished(PendingIntent pendingIntent, Intent intent,
   4120             int resultCode, String resultData, Bundle resultExtras) {
   4121         if (resultCode == WAKELOCK_RELEASE_ON_FINISHED) {
   4122             mMediaEventWakeLock.release();
   4123         }
   4124     }
   4125 
   4126     BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
   4127         public void onReceive(Context context, Intent intent) {
   4128             if (intent == null) {
   4129                 return;
   4130             }
   4131             Bundle extras = intent.getExtras();
   4132             if (extras == null) {
   4133                 return;
   4134             }
   4135             if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)) {
   4136                 mMediaEventWakeLock.release();
   4137             }
   4138         }
   4139     };
   4140 
   4141     private final Object mCurrentRcLock = new Object();
   4142     /**
   4143      * The one remote control client which will receive a request for display information.
   4144      * This object may be null.
   4145      * Access protected by mCurrentRcLock.
   4146      */
   4147     private IRemoteControlClient mCurrentRcClient = null;
   4148 
   4149     private final static int RC_INFO_NONE = 0;
   4150     private final static int RC_INFO_ALL =
   4151         RemoteControlClient.FLAG_INFORMATION_REQUEST_ALBUM_ART |
   4152         RemoteControlClient.FLAG_INFORMATION_REQUEST_KEY_MEDIA |
   4153         RemoteControlClient.FLAG_INFORMATION_REQUEST_METADATA |
   4154         RemoteControlClient.FLAG_INFORMATION_REQUEST_PLAYSTATE;
   4155 
   4156     /**
   4157      * A monotonically increasing generation counter for mCurrentRcClient.
   4158      * Only accessed with a lock on mCurrentRcLock.
   4159      * No value wrap-around issues as we only act on equal values.
   4160      */
   4161     private int mCurrentRcClientGen = 0;
   4162 
   4163     /**
   4164      * Inner class to monitor remote control client deaths, and remove the client for the
   4165      * remote control stack if necessary.
   4166      */
   4167     private class RcClientDeathHandler implements IBinder.DeathRecipient {
   4168         private IBinder mCb; // To be notified of client's death
   4169         private PendingIntent mMediaIntent;
   4170 
   4171         RcClientDeathHandler(IBinder cb, PendingIntent pi) {
   4172             mCb = cb;
   4173             mMediaIntent = pi;
   4174         }
   4175 
   4176         public void binderDied() {
   4177             Log.w(TAG, "  RemoteControlClient died");
   4178             // remote control client died, make sure the displays don't use it anymore
   4179             //  by setting its remote control client to null
   4180             registerRemoteControlClient(mMediaIntent, null/*rcClient*/, null/*ignored*/);
   4181             // the dead client was maybe handling remote playback, reevaluate
   4182             postReevaluateRemote();
   4183         }
   4184 
   4185         public IBinder getBinder() {
   4186             return mCb;
   4187         }
   4188     }
   4189 
   4190     /**
   4191      * A global counter for RemoteControlClient identifiers
   4192      */
   4193     private static int sLastRccId = 0;
   4194 
   4195     private class RemotePlaybackState {
   4196         int mRccId;
   4197         int mVolume;
   4198         int mVolumeMax;
   4199         int mVolumeHandling;
   4200 
   4201         private RemotePlaybackState(int id, int vol, int volMax) {
   4202             mRccId = id;
   4203             mVolume = vol;
   4204             mVolumeMax = volMax;
   4205             mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
   4206         }
   4207     }
   4208 
   4209     /**
   4210      * Internal cache for the playback information of the RemoteControlClient whose volume gets to
   4211      * be controlled by the volume keys ("main"), so we don't have to iterate over the RC stack
   4212      * every time we need this info.
   4213      */
   4214     private RemotePlaybackState mMainRemote;
   4215     /**
   4216      * Indicates whether the "main" RemoteControlClient is considered active.
   4217      * Use synchronized on mMainRemote.
   4218      */
   4219     private boolean mMainRemoteIsActive;
   4220     /**
   4221      * Indicates whether there is remote playback going on. True even if there is no "active"
   4222      * remote playback (mMainRemoteIsActive is false), but a RemoteControlClient has declared it
   4223      * handles remote playback.
   4224      * Use synchronized on mMainRemote.
   4225      */
   4226     private boolean mHasRemotePlayback;
   4227 
   4228     private static class RemoteControlStackEntry {
   4229         public int mRccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
   4230         /**
   4231          * The target for the ACTION_MEDIA_BUTTON events.
   4232          * Always non null.
   4233          */
   4234         public PendingIntent mMediaIntent;
   4235         /**
   4236          * The registered media button event receiver.
   4237          * Always non null.
   4238          */
   4239         public ComponentName mReceiverComponent;
   4240         public String mCallingPackageName;
   4241         public int mCallingUid;
   4242         /**
   4243          * Provides access to the information to display on the remote control.
   4244          * May be null (when a media button event receiver is registered,
   4245          *     but no remote control client has been registered) */
   4246         public IRemoteControlClient mRcClient;
   4247         public RcClientDeathHandler mRcClientDeathHandler;
   4248         /**
   4249          * Information only used for non-local playback
   4250          */
   4251         public int mPlaybackType;
   4252         public int mPlaybackVolume;
   4253         public int mPlaybackVolumeMax;
   4254         public int mPlaybackVolumeHandling;
   4255         public int mPlaybackStream;
   4256         public int mPlaybackState;
   4257         public IRemoteVolumeObserver mRemoteVolumeObs;
   4258 
   4259         public void resetPlaybackInfo() {
   4260             mPlaybackType = RemoteControlClient.PLAYBACK_TYPE_LOCAL;
   4261             mPlaybackVolume = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
   4262             mPlaybackVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
   4263             mPlaybackVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
   4264             mPlaybackStream = AudioManager.STREAM_MUSIC;
   4265             mPlaybackState = RemoteControlClient.PLAYSTATE_STOPPED;
   4266             mRemoteVolumeObs = null;
   4267         }
   4268 
   4269         /** precondition: mediaIntent != null, eventReceiver != null */
   4270         public RemoteControlStackEntry(PendingIntent mediaIntent, ComponentName eventReceiver) {
   4271             mMediaIntent = mediaIntent;
   4272             mReceiverComponent = eventReceiver;
   4273             mCallingUid = -1;
   4274             mRcClient = null;
   4275             mRccId = ++sLastRccId;
   4276 
   4277             resetPlaybackInfo();
   4278         }
   4279 
   4280         public void unlinkToRcClientDeath() {
   4281             if ((mRcClientDeathHandler != null) && (mRcClientDeathHandler.mCb != null)) {
   4282                 try {
   4283                     mRcClientDeathHandler.mCb.unlinkToDeath(mRcClientDeathHandler, 0);
   4284                     mRcClientDeathHandler = null;
   4285                 } catch (java.util.NoSuchElementException e) {
   4286                     // not much we can do here
   4287                     Log.e(TAG, "Encountered " + e + " in unlinkToRcClientDeath()");
   4288                     e.printStackTrace();
   4289                 }
   4290             }
   4291         }
   4292 
   4293         @Override
   4294         protected void finalize() throws Throwable {
   4295             unlinkToRcClientDeath();// unlink exception handled inside method
   4296             super.finalize();
   4297         }
   4298     }
   4299 
   4300     /**
   4301      *  The stack of remote control event receivers.
   4302      *  Code sections and methods that modify the remote control event receiver stack are
   4303      *  synchronized on mRCStack, but also BEFORE on mFocusLock as any change in either
   4304      *  stack, audio focus or RC, can lead to a change in the remote control display
   4305      */
   4306     private final Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>();
   4307 
   4308     /**
   4309      * The component the telephony package can register so telephony calls have priority to
   4310      * handle media button events
   4311      */
   4312     private ComponentName mMediaReceiverForCalls = null;
   4313 
   4314     /**
   4315      * Helper function:
   4316      * Display in the log the current entries in the remote control focus stack
   4317      */
   4318     private void dumpRCStack(PrintWriter pw) {
   4319         pw.println("\nRemote Control stack entries:");
   4320         synchronized(mRCStack) {
   4321             Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   4322             while(stackIterator.hasNext()) {
   4323                 RemoteControlStackEntry rcse = stackIterator.next();
   4324                 pw.println("  pi: " + rcse.mMediaIntent +
   4325                         "  -- ercvr: " + rcse.mReceiverComponent +
   4326                         "  -- client: " + rcse.mRcClient +
   4327                         "  -- uid: " + rcse.mCallingUid +
   4328                         "  -- type: " + rcse.mPlaybackType +
   4329                         "  state: " + rcse.mPlaybackState);
   4330             }
   4331         }
   4332     }
   4333 
   4334     /**
   4335      * Helper function:
   4336      * Display in the log the current entries in the remote control stack, focusing
   4337      * on RemoteControlClient data
   4338      */
   4339     private void dumpRCCStack(PrintWriter pw) {
   4340         pw.println("\nRemote Control Client stack entries:");
   4341         synchronized(mRCStack) {
   4342             Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   4343             while(stackIterator.hasNext()) {
   4344                 RemoteControlStackEntry rcse = stackIterator.next();
   4345                 pw.println("  uid: " + rcse.mCallingUid +
   4346                         "  -- id: " + rcse.mRccId +
   4347                         "  -- type: " + rcse.mPlaybackType +
   4348                         "  -- state: " + rcse.mPlaybackState +
   4349                         "  -- vol handling: " + rcse.mPlaybackVolumeHandling +
   4350                         "  -- vol: " + rcse.mPlaybackVolume +
   4351                         "  -- volMax: " + rcse.mPlaybackVolumeMax +
   4352                         "  -- volObs: " + rcse.mRemoteVolumeObs);
   4353 
   4354             }
   4355         }
   4356         synchronized (mMainRemote) {
   4357             pw.println("\nRemote Volume State:");
   4358             pw.println("  has remote: " + mHasRemotePlayback);
   4359             pw.println("  is remote active: " + mMainRemoteIsActive);
   4360             pw.println("  rccId: " + mMainRemote.mRccId);
   4361             pw.println("  volume handling: "
   4362                     + ((mMainRemote.mVolumeHandling == RemoteControlClient.PLAYBACK_VOLUME_FIXED) ?
   4363                             "PLAYBACK_VOLUME_FIXED(0)" : "PLAYBACK_VOLUME_VARIABLE(1)"));
   4364             pw.println("  volume: " + mMainRemote.mVolume);
   4365             pw.println("  volume steps: " + mMainRemote.mVolumeMax);
   4366         }
   4367     }
   4368 
   4369     /**
   4370      * Helper function:
   4371      * Remove any entry in the remote control stack that has the same package name as packageName
   4372      * Pre-condition: packageName != null
   4373      */
   4374     private void removeMediaButtonReceiverForPackage(String packageName) {
   4375         synchronized(mRCStack) {
   4376             if (mRCStack.empty()) {
   4377                 return;
   4378             } else {
   4379                 RemoteControlStackEntry oldTop = mRCStack.peek();
   4380                 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   4381                 // iterate over the stack entries
   4382                 while(stackIterator.hasNext()) {
   4383                     RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
   4384                     if (packageName.equalsIgnoreCase(rcse.mReceiverComponent.getPackageName())) {
   4385                         // a stack entry is from the package being removed, remove it from the stack
   4386                         stackIterator.remove();
   4387                         rcse.unlinkToRcClientDeath();
   4388                     }
   4389                 }
   4390                 if (mRCStack.empty()) {
   4391                     // no saved media button receiver
   4392                     mAudioHandler.sendMessage(
   4393                             mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
   4394                                     null));
   4395                 } else if (oldTop != mRCStack.peek()) {
   4396                     // the top of the stack has changed, save it in the system settings
   4397                     // by posting a message to persist it
   4398                     mAudioHandler.sendMessage(
   4399                             mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
   4400                                     mRCStack.peek().mReceiverComponent));
   4401                 }
   4402             }
   4403         }
   4404     }
   4405 
   4406     /**
   4407      * Helper function:
   4408      * Restore remote control receiver from the system settings.
   4409      */
   4410     private void restoreMediaButtonReceiver() {
   4411         String receiverName = Settings.System.getString(mContentResolver,
   4412                 Settings.System.MEDIA_BUTTON_RECEIVER);
   4413         if ((null != receiverName) && !receiverName.isEmpty()) {
   4414             ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
   4415             // construct a PendingIntent targeted to the restored component name
   4416             // for the media button and register it
   4417             Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
   4418             //     the associated intent will be handled by the component being registered
   4419             mediaButtonIntent.setComponent(eventReceiver);
   4420             PendingIntent pi = PendingIntent.getBroadcast(mContext,
   4421                     0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
   4422             registerMediaButtonIntent(pi, eventReceiver);
   4423         }
   4424     }
   4425 
   4426     /**
   4427      * Helper function:
   4428      * Set the new remote control receiver at the top of the RC focus stack.
   4429      * precondition: mediaIntent != null, target != null
   4430      */
   4431     private void pushMediaButtonReceiver(PendingIntent mediaIntent, ComponentName target) {
   4432         // already at top of stack?
   4433         if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(mediaIntent)) {
   4434             return;
   4435         }
   4436         Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   4437         RemoteControlStackEntry rcse = null;
   4438         boolean wasInsideStack = false;
   4439         while(stackIterator.hasNext()) {
   4440             rcse = (RemoteControlStackEntry)stackIterator.next();
   4441             if(rcse.mMediaIntent.equals(mediaIntent)) {
   4442                 wasInsideStack = true;
   4443                 stackIterator.remove();
   4444                 break;
   4445             }
   4446         }
   4447         if (!wasInsideStack) {
   4448             rcse = new RemoteControlStackEntry(mediaIntent, target);
   4449         }
   4450         mRCStack.push(rcse);
   4451 
   4452         // post message to persist the default media button receiver
   4453         mAudioHandler.sendMessage( mAudioHandler.obtainMessage(
   4454                 MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) );
   4455     }
   4456 
   4457     /**
   4458      * Helper function:
   4459      * Remove the remote control receiver from the RC focus stack.
   4460      * precondition: pi != null
   4461      */
   4462     private void removeMediaButtonReceiver(PendingIntent pi) {
   4463         Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   4464         while(stackIterator.hasNext()) {
   4465             RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
   4466             if(rcse.mMediaIntent.equals(pi)) {
   4467                 stackIterator.remove();
   4468                 rcse.unlinkToRcClientDeath();
   4469                 break;
   4470             }
   4471         }
   4472     }
   4473 
   4474     /**
   4475      * Helper function:
   4476      * Called synchronized on mRCStack
   4477      */
   4478     private boolean isCurrentRcController(PendingIntent pi) {
   4479         if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(pi)) {
   4480             return true;
   4481         }
   4482         return false;
   4483     }
   4484 
   4485     //==========================================================================================
   4486     // Remote control display / client
   4487     //==========================================================================================
   4488     /**
   4489      * Update the remote control displays with the new "focused" client generation
   4490      */
   4491     private void setNewRcClientOnDisplays_syncRcsCurrc(int newClientGeneration,
   4492             PendingIntent newMediaIntent, boolean clearing) {
   4493         // NOTE: Only one IRemoteControlDisplay supported in this implementation
   4494         if (mRcDisplay != null) {
   4495             try {
   4496                 mRcDisplay.setCurrentClientId(
   4497                         newClientGeneration, newMediaIntent, clearing);
   4498             } catch (RemoteException e) {
   4499                 Log.e(TAG, "Dead display in setNewRcClientOnDisplays_syncRcsCurrc() "+e);
   4500                 // if we had a display before, stop monitoring its death
   4501                 rcDisplay_stopDeathMonitor_syncRcStack();
   4502                 mRcDisplay = null;
   4503             }
   4504         }
   4505     }
   4506 
   4507     /**
   4508      * Update the remote control clients with the new "focused" client generation
   4509      */
   4510     private void setNewRcClientGenerationOnClients_syncRcsCurrc(int newClientGeneration) {
   4511         Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   4512         while(stackIterator.hasNext()) {
   4513             RemoteControlStackEntry se = stackIterator.next();
   4514             if ((se != null) && (se.mRcClient != null)) {
   4515                 try {
   4516                     se.mRcClient.setCurrentClientGenerationId(newClientGeneration);
   4517                 } catch (RemoteException e) {
   4518                     Log.w(TAG, "Dead client in setNewRcClientGenerationOnClients_syncRcsCurrc()"+e);
   4519                     stackIterator.remove();
   4520                     se.unlinkToRcClientDeath();
   4521                 }
   4522             }
   4523         }
   4524     }
   4525 
   4526     /**
   4527      * Update the displays and clients with the new "focused" client generation and name
   4528      * @param newClientGeneration the new generation value matching a client update
   4529      * @param newClientEventReceiver the media button event receiver associated with the client.
   4530      *    May be null, which implies there is no registered media button event receiver.
   4531      * @param clearing true if the new client generation value maps to a remote control update
   4532      *    where the display should be cleared.
   4533      */
   4534     private void setNewRcClient_syncRcsCurrc(int newClientGeneration,
   4535             PendingIntent newMediaIntent, boolean clearing) {
   4536         // send the new valid client generation ID to all displays
   4537         setNewRcClientOnDisplays_syncRcsCurrc(newClientGeneration, newMediaIntent, clearing);
   4538         // send the new valid client generation ID to all clients
   4539         setNewRcClientGenerationOnClients_syncRcsCurrc(newClientGeneration);
   4540     }
   4541 
   4542     /**
   4543      * Called when processing MSG_RCDISPLAY_CLEAR event
   4544      */
   4545     private void onRcDisplayClear() {
   4546         if (DEBUG_RC) Log.i(TAG, "Clear remote control display");
   4547 
   4548         synchronized(mRCStack) {
   4549             synchronized(mCurrentRcLock) {
   4550                 mCurrentRcClientGen++;
   4551                 // synchronously update the displays and clients with the new client generation
   4552                 setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
   4553                         null /*newMediaIntent*/, true /*clearing*/);
   4554             }
   4555         }
   4556     }
   4557 
   4558     /**
   4559      * Called when processing MSG_RCDISPLAY_UPDATE event
   4560      */
   4561     private void onRcDisplayUpdate(RemoteControlStackEntry rcse, int flags /* USED ?*/) {
   4562         synchronized(mRCStack) {
   4563             synchronized(mCurrentRcLock) {
   4564                 if ((mCurrentRcClient != null) && (mCurrentRcClient.equals(rcse.mRcClient))) {
   4565                     if (DEBUG_RC) Log.i(TAG, "Display/update remote control ");
   4566 
   4567                     mCurrentRcClientGen++;
   4568                     // synchronously update the displays and clients with
   4569                     //      the new client generation
   4570                     setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
   4571                             rcse.mMediaIntent /*newMediaIntent*/,
   4572                             false /*clearing*/);
   4573 
   4574                     // tell the current client that it needs to send info
   4575                     try {
   4576                         mCurrentRcClient.onInformationRequested(mCurrentRcClientGen,
   4577                                 flags, mArtworkExpectedWidth, mArtworkExpectedHeight);
   4578                     } catch (RemoteException e) {
   4579                         Log.e(TAG, "Current valid remote client is dead: "+e);
   4580                         mCurrentRcClient = null;
   4581                     }
   4582                 } else {
   4583                     // the remote control display owner has changed between the
   4584                     // the message to update the display was sent, and the time it
   4585                     // gets to be processed (now)
   4586                 }
   4587             }
   4588         }
   4589     }
   4590 
   4591 
   4592     /**
   4593      * Helper function:
   4594      * Called synchronized on mRCStack
   4595      */
   4596     private void clearRemoteControlDisplay_syncAfRcs() {
   4597         synchronized(mCurrentRcLock) {
   4598             mCurrentRcClient = null;
   4599         }
   4600         // will cause onRcDisplayClear() to be called in AudioService's handler thread
   4601         mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_CLEAR) );
   4602     }
   4603 
   4604     /**
   4605      * Helper function for code readability: only to be called from
   4606      *    checkUpdateRemoteControlDisplay_syncAfRcs() which checks the preconditions for
   4607      *    this method.
   4608      * Preconditions:
   4609      *    - called synchronized mAudioFocusLock then on mRCStack
   4610      *    - mRCStack.isEmpty() is false
   4611      */
   4612     private void updateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
   4613         RemoteControlStackEntry rcse = mRCStack.peek();
   4614         int infoFlagsAboutToBeUsed = infoChangedFlags;
   4615         // this is where we enforce opt-in for information display on the remote controls
   4616         //   with the new AudioManager.registerRemoteControlClient() API
   4617         if (rcse.mRcClient == null) {
   4618             //Log.w(TAG, "Can't update remote control display with null remote control client");
   4619             clearRemoteControlDisplay_syncAfRcs();
   4620             return;
   4621         }
   4622         synchronized(mCurrentRcLock) {
   4623             if (!rcse.mRcClient.equals(mCurrentRcClient)) {
   4624                 // new RC client, assume every type of information shall be queried
   4625                 infoFlagsAboutToBeUsed = RC_INFO_ALL;
   4626             }
   4627             mCurrentRcClient = rcse.mRcClient;
   4628         }
   4629         // will cause onRcDisplayUpdate() to be called in AudioService's handler thread
   4630         mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_UPDATE,
   4631                 infoFlagsAboutToBeUsed /* arg1 */, 0, rcse /* obj, != null */) );
   4632     }
   4633 
   4634     /**
   4635      * Helper function:
   4636      * Called synchronized on mAudioFocusLock, then mRCStack
   4637      * Check whether the remote control display should be updated, triggers the update if required
   4638      * @param infoChangedFlags the flags corresponding to the remote control client information
   4639      *     that has changed, if applicable (checking for the update conditions might trigger a
   4640      *     clear, rather than an update event).
   4641      */
   4642     private void checkUpdateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
   4643         // determine whether the remote control display should be refreshed
   4644         // if either stack is empty, there is a mismatch, so clear the RC display
   4645         if (mRCStack.isEmpty() || mFocusStack.isEmpty()) {
   4646             clearRemoteControlDisplay_syncAfRcs();
   4647             return;
   4648         }
   4649         // if the top of the two stacks belong to different packages, there is a mismatch, clear
   4650         if ((mRCStack.peek().mCallingPackageName != null)
   4651                 && (mFocusStack.peek().mPackageName != null)
   4652                 && !(mRCStack.peek().mCallingPackageName.compareTo(
   4653                         mFocusStack.peek().mPackageName) == 0)) {
   4654             clearRemoteControlDisplay_syncAfRcs();
   4655             return;
   4656         }
   4657         // if the audio focus didn't originate from the same Uid as the one in which the remote
   4658         //   control information will be retrieved, clear
   4659         if (mRCStack.peek().mCallingUid != mFocusStack.peek().mCallingUid) {
   4660             clearRemoteControlDisplay_syncAfRcs();
   4661             return;
   4662         }
   4663         // refresh conditions were verified: update the remote controls
   4664         // ok to call: synchronized mAudioFocusLock then on mRCStack, mRCStack is not empty
   4665         updateRemoteControlDisplay_syncAfRcs(infoChangedFlags);
   4666     }
   4667 
   4668     /**
   4669      * see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
   4670      * precondition: mediaIntent != null, target != null
   4671      */
   4672     public void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver) {
   4673         Log.i(TAG, "  Remote Control   registerMediaButtonIntent() for " + mediaIntent);
   4674 
   4675         synchronized(mAudioFocusLock) {
   4676             synchronized(mRCStack) {
   4677                 pushMediaButtonReceiver(mediaIntent, eventReceiver);
   4678                 // new RC client, assume every type of information shall be queried
   4679                 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
   4680             }
   4681         }
   4682     }
   4683 
   4684     /**
   4685      * see AudioManager.unregisterMediaButtonIntent(PendingIntent mediaIntent)
   4686      * precondition: mediaIntent != null, eventReceiver != null
   4687      */
   4688     public void unregisterMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver)
   4689     {
   4690         Log.i(TAG, "  Remote Control   unregisterMediaButtonIntent() for " + mediaIntent);
   4691 
   4692         synchronized(mAudioFocusLock) {
   4693             synchronized(mRCStack) {
   4694                 boolean topOfStackWillChange = isCurrentRcController(mediaIntent);
   4695                 removeMediaButtonReceiver(mediaIntent);
   4696                 if (topOfStackWillChange) {
   4697                     // current RC client will change, assume every type of info needs to be queried
   4698                     checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
   4699                 }
   4700             }
   4701         }
   4702     }
   4703 
   4704     /**
   4705      * see AudioManager.registerMediaButtonEventReceiverForCalls(ComponentName c)
   4706      * precondition: c != null
   4707      */
   4708     public void registerMediaButtonEventReceiverForCalls(ComponentName c) {
   4709         if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE")
   4710                 != PackageManager.PERMISSION_GRANTED) {
   4711             Log.e(TAG, "Invalid permissions to register media button receiver for calls");
   4712             return;
   4713         }
   4714         synchronized(mRCStack) {
   4715             mMediaReceiverForCalls = c;
   4716         }
   4717     }
   4718 
   4719     /**
   4720      * see AudioManager.unregisterMediaButtonEventReceiverForCalls()
   4721      */
   4722     public void unregisterMediaButtonEventReceiverForCalls() {
   4723         if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE")
   4724                 != PackageManager.PERMISSION_GRANTED) {
   4725             Log.e(TAG, "Invalid permissions to unregister media button receiver for calls");
   4726             return;
   4727         }
   4728         synchronized(mRCStack) {
   4729             mMediaReceiverForCalls = null;
   4730         }
   4731     }
   4732 
   4733     /**
   4734      * see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...)
   4735      * @return the unique ID of the RemoteControlStackEntry associated with the RemoteControlClient
   4736      * Note: using this method with rcClient == null is a way to "disable" the IRemoteControlClient
   4737      *     without modifying the RC stack, but while still causing the display to refresh (will
   4738      *     become blank as a result of this)
   4739      */
   4740     public int registerRemoteControlClient(PendingIntent mediaIntent,
   4741             IRemoteControlClient rcClient, String callingPackageName) {
   4742         if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient);
   4743         int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
   4744         synchronized(mAudioFocusLock) {
   4745             synchronized(mRCStack) {
   4746                 // store the new display information
   4747                 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   4748                 while(stackIterator.hasNext()) {
   4749                     RemoteControlStackEntry rcse = stackIterator.next();
   4750                     if(rcse.mMediaIntent.equals(mediaIntent)) {
   4751                         // already had a remote control client?
   4752                         if (rcse.mRcClientDeathHandler != null) {
   4753                             // stop monitoring the old client's death
   4754                             rcse.unlinkToRcClientDeath();
   4755                         }
   4756                         // save the new remote control client
   4757                         rcse.mRcClient = rcClient;
   4758                         rcse.mCallingPackageName = callingPackageName;
   4759                         rcse.mCallingUid = Binder.getCallingUid();
   4760                         if (rcClient == null) {
   4761                             // here rcse.mRcClientDeathHandler is null;
   4762                             rcse.resetPlaybackInfo();
   4763                             break;
   4764                         }
   4765                         rccId = rcse.mRccId;
   4766 
   4767                         // there is a new (non-null) client:
   4768                         // 1/ give the new client the current display (if any)
   4769                         if (mRcDisplay != null) {
   4770                             try {
   4771                                 rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
   4772                             } catch (RemoteException e) {
   4773                                 Log.e(TAG, "Error connecting remote control display to client: "+e);
   4774                                 e.printStackTrace();
   4775                             }
   4776                         }
   4777                         // 2/ monitor the new client's death
   4778                         IBinder b = rcse.mRcClient.asBinder();
   4779                         RcClientDeathHandler rcdh =
   4780                                 new RcClientDeathHandler(b, rcse.mMediaIntent);
   4781                         try {
   4782                             b.linkToDeath(rcdh, 0);
   4783                         } catch (RemoteException e) {
   4784                             // remote control client is DOA, disqualify it
   4785                             Log.w(TAG, "registerRemoteControlClient() has a dead client " + b);
   4786                             rcse.mRcClient = null;
   4787                         }
   4788                         rcse.mRcClientDeathHandler = rcdh;
   4789                         break;
   4790                     }
   4791                 }
   4792                 // if the eventReceiver is at the top of the stack
   4793                 // then check for potential refresh of the remote controls
   4794                 if (isCurrentRcController(mediaIntent)) {
   4795                     checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
   4796                 }
   4797             }
   4798         }
   4799         return rccId;
   4800     }
   4801 
   4802     /**
   4803      * see AudioManager.unregisterRemoteControlClient(PendingIntent pi, ...)
   4804      * rcClient is guaranteed non-null
   4805      */
   4806     public void unregisterRemoteControlClient(PendingIntent mediaIntent,
   4807             IRemoteControlClient rcClient) {
   4808         synchronized(mAudioFocusLock) {
   4809             synchronized(mRCStack) {
   4810                 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   4811                 while(stackIterator.hasNext()) {
   4812                     RemoteControlStackEntry rcse = stackIterator.next();
   4813                     if ((rcse.mMediaIntent.equals(mediaIntent))
   4814                             && rcClient.equals(rcse.mRcClient)) {
   4815                         // we found the IRemoteControlClient to unregister
   4816                         // stop monitoring its death
   4817                         rcse.unlinkToRcClientDeath();
   4818                         // reset the client-related fields
   4819                         rcse.mRcClient = null;
   4820                         rcse.mCallingPackageName = null;
   4821                     }
   4822                 }
   4823             }
   4824         }
   4825     }
   4826 
   4827     /**
   4828      * The remote control displays.
   4829      * Access synchronized on mRCStack
   4830      * NOTE: Only one IRemoteControlDisplay supported in this implementation
   4831      */
   4832     private IRemoteControlDisplay mRcDisplay;
   4833     private RcDisplayDeathHandler mRcDisplayDeathHandler;
   4834     private int mArtworkExpectedWidth = -1;
   4835     private int mArtworkExpectedHeight = -1;
   4836     /**
   4837      * Inner class to monitor remote control display deaths, and unregister them from the list
   4838      * of displays if necessary.
   4839      */
   4840     private class RcDisplayDeathHandler implements IBinder.DeathRecipient {
   4841         private IBinder mCb; // To be notified of client's death
   4842 
   4843         public RcDisplayDeathHandler(IBinder b) {
   4844             if (DEBUG_RC) Log.i(TAG, "new RcDisplayDeathHandler for "+b);
   4845             mCb = b;
   4846         }
   4847 
   4848         public void binderDied() {
   4849             synchronized(mRCStack) {
   4850                 Log.w(TAG, "RemoteControl: display died");
   4851                 mRcDisplay = null;
   4852             }
   4853         }
   4854 
   4855         public void unlinkToRcDisplayDeath() {
   4856             if (DEBUG_RC) Log.i(TAG, "unlinkToRcDisplayDeath for "+mCb);
   4857             try {
   4858                 mCb.unlinkToDeath(this, 0);
   4859             } catch (java.util.NoSuchElementException e) {
   4860                 // not much we can do here, the display was being unregistered anyway
   4861                 Log.e(TAG, "Encountered " + e + " in unlinkToRcDisplayDeath()");
   4862                 e.printStackTrace();
   4863             }
   4864         }
   4865 
   4866     }
   4867 
   4868     private void rcDisplay_stopDeathMonitor_syncRcStack() {
   4869         if (mRcDisplay != null) { // implies (mRcDisplayDeathHandler != null)
   4870             // we had a display before, stop monitoring its death
   4871             mRcDisplayDeathHandler.unlinkToRcDisplayDeath();
   4872         }
   4873     }
   4874 
   4875     private void rcDisplay_startDeathMonitor_syncRcStack() {
   4876         if (mRcDisplay != null) {
   4877             // new non-null display, monitor its death
   4878             IBinder b = mRcDisplay.asBinder();
   4879             mRcDisplayDeathHandler = new RcDisplayDeathHandler(b);
   4880             try {
   4881                 b.linkToDeath(mRcDisplayDeathHandler, 0);
   4882             } catch (RemoteException e) {
   4883                 // remote control display is DOA, disqualify it
   4884                 Log.w(TAG, "registerRemoteControlDisplay() has a dead client " + b);
   4885                 mRcDisplay = null;
   4886             }
   4887         }
   4888     }
   4889 
   4890     /**
   4891      * Register an IRemoteControlDisplay.
   4892      * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient
   4893      * at the top of the stack to update the new display with its information.
   4894      * Since only one IRemoteControlDisplay is supported, this will unregister the previous display.
   4895      * @param rcd the IRemoteControlDisplay to register. No effect if null.
   4896      */
   4897     public void registerRemoteControlDisplay(IRemoteControlDisplay rcd) {
   4898         if (DEBUG_RC) Log.d(TAG, ">>> registerRemoteControlDisplay("+rcd+")");
   4899         synchronized(mAudioFocusLock) {
   4900             synchronized(mRCStack) {
   4901                 if ((mRcDisplay == rcd) || (rcd == null)) {
   4902                     return;
   4903                 }
   4904                 // if we had a display before, stop monitoring its death
   4905                 rcDisplay_stopDeathMonitor_syncRcStack();
   4906                 mRcDisplay = rcd;
   4907                 // new display, start monitoring its death
   4908                 rcDisplay_startDeathMonitor_syncRcStack();
   4909 
   4910                 // let all the remote control clients there is a new display
   4911                 // no need to unplug the previous because we only support one display
   4912                 // and the clients don't track the death of the display
   4913                 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   4914                 while(stackIterator.hasNext()) {
   4915                     RemoteControlStackEntry rcse = stackIterator.next();
   4916                     if(rcse.mRcClient != null) {
   4917                         try {
   4918                             rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
   4919                         } catch (RemoteException e) {
   4920                             Log.e(TAG, "Error connecting remote control display to client: " + e);
   4921                             e.printStackTrace();
   4922                         }
   4923                     }
   4924                 }
   4925 
   4926                 // we have a new display, of which all the clients are now aware: have it be updated
   4927                 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
   4928             }
   4929         }
   4930     }
   4931 
   4932     /**
   4933      * Unregister an IRemoteControlDisplay.
   4934      * Since only one IRemoteControlDisplay is supported, this has no effect if the one to
   4935      *    unregister is not the current one.
   4936      * @param rcd the IRemoteControlDisplay to unregister. No effect if null.
   4937      */
   4938     public void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) {
   4939         if (DEBUG_RC) Log.d(TAG, "<<< unregisterRemoteControlDisplay("+rcd+")");
   4940         synchronized(mRCStack) {
   4941             // only one display here, so you can only unregister the current display
   4942             if ((rcd == null) || (rcd != mRcDisplay)) {
   4943                 if (DEBUG_RC) Log.w(TAG, "    trying to unregister unregistered RCD");
   4944                 return;
   4945             }
   4946             // if we had a display before, stop monitoring its death
   4947             rcDisplay_stopDeathMonitor_syncRcStack();
   4948             mRcDisplay = null;
   4949 
   4950             // disconnect this remote control display from all the clients
   4951             Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   4952             while(stackIterator.hasNext()) {
   4953                 RemoteControlStackEntry rcse = stackIterator.next();
   4954                 if(rcse.mRcClient != null) {
   4955                     try {
   4956                         rcse.mRcClient.unplugRemoteControlDisplay(rcd);
   4957                     } catch (RemoteException e) {
   4958                         Log.e(TAG, "Error disconnecting remote control display to client: " + e);
   4959                         e.printStackTrace();
   4960                     }
   4961                 }
   4962             }
   4963         }
   4964     }
   4965 
   4966     public void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) {
   4967         synchronized(mRCStack) {
   4968             // NOTE: Only one IRemoteControlDisplay supported in this implementation
   4969             mArtworkExpectedWidth = w;
   4970             mArtworkExpectedHeight = h;
   4971         }
   4972     }
   4973 
   4974     public void setPlaybackInfoForRcc(int rccId, int what, int value) {
   4975         sendMsg(mAudioHandler, MSG_RCC_NEW_PLAYBACK_INFO, SENDMSG_QUEUE,
   4976                 rccId /* arg1 */, what /* arg2 */, Integer.valueOf(value) /* obj */, 0 /* delay */);
   4977     }
   4978 
   4979     // handler for MSG_RCC_NEW_PLAYBACK_INFO
   4980     private void onNewPlaybackInfoForRcc(int rccId, int key, int value) {
   4981         if(DEBUG_RC) Log.d(TAG, "onNewPlaybackInfoForRcc(id=" + rccId +
   4982                 ", what=" + key + ",val=" + value + ")");
   4983         synchronized(mRCStack) {
   4984             Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   4985             while(stackIterator.hasNext()) {
   4986                 RemoteControlStackEntry rcse = stackIterator.next();
   4987                 if (rcse.mRccId == rccId) {
   4988                     switch (key) {
   4989                         case RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE:
   4990                             rcse.mPlaybackType = value;
   4991                             postReevaluateRemote();
   4992                             break;
   4993                         case RemoteControlClient.PLAYBACKINFO_VOLUME:
   4994                             rcse.mPlaybackVolume = value;
   4995                             synchronized (mMainRemote) {
   4996                                 if (rccId == mMainRemote.mRccId) {
   4997                                     mMainRemote.mVolume = value;
   4998                                     mVolumePanel.postHasNewRemotePlaybackInfo();
   4999                                 }
   5000                             }
   5001                             break;
   5002                         case RemoteControlClient.PLAYBACKINFO_VOLUME_MAX:
   5003                             rcse.mPlaybackVolumeMax = value;
   5004                             synchronized (mMainRemote) {
   5005                                 if (rccId == mMainRemote.mRccId) {
   5006                                     mMainRemote.mVolumeMax = value;
   5007                                     mVolumePanel.postHasNewRemotePlaybackInfo();
   5008                                 }
   5009                             }
   5010                             break;
   5011                         case RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING:
   5012                             rcse.mPlaybackVolumeHandling = value;
   5013                             synchronized (mMainRemote) {
   5014                                 if (rccId == mMainRemote.mRccId) {
   5015                                     mMainRemote.mVolumeHandling = value;
   5016                                     mVolumePanel.postHasNewRemotePlaybackInfo();
   5017                                 }
   5018                             }
   5019                             break;
   5020                         case RemoteControlClient.PLAYBACKINFO_USES_STREAM:
   5021                             rcse.mPlaybackStream = value;
   5022                             break;
   5023                         case RemoteControlClient.PLAYBACKINFO_PLAYSTATE:
   5024                             rcse.mPlaybackState = value;
   5025                             synchronized (mMainRemote) {
   5026                                 if (rccId == mMainRemote.mRccId) {
   5027                                     mMainRemoteIsActive = isPlaystateActive(value);
   5028                                     postReevaluateRemote();
   5029                                 }
   5030                             }
   5031                             break;
   5032                         default:
   5033                             Log.e(TAG, "unhandled key " + key + " for RCC " + rccId);
   5034                             break;
   5035                     }
   5036                     return;
   5037                 }
   5038             }
   5039         }
   5040     }
   5041 
   5042     public void registerRemoteVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
   5043         sendMsg(mAudioHandler, MSG_RCC_NEW_VOLUME_OBS, SENDMSG_QUEUE,
   5044                 rccId /* arg1 */, 0, rvo /* obj */, 0 /* delay */);
   5045     }
   5046 
   5047     // handler for MSG_RCC_NEW_VOLUME_OBS
   5048     private void onRegisterVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
   5049         synchronized(mRCStack) {
   5050             Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   5051             while(stackIterator.hasNext()) {
   5052                 RemoteControlStackEntry rcse = stackIterator.next();
   5053                 if (rcse.mRccId == rccId) {
   5054                     rcse.mRemoteVolumeObs = rvo;
   5055                     break;
   5056                 }
   5057             }
   5058         }
   5059     }
   5060 
   5061     /**
   5062      * Checks if a remote client is active on the supplied stream type. Update the remote stream
   5063      * volume state if found and playing
   5064      * @param streamType
   5065      * @return false if no remote playing is currently playing
   5066      */
   5067     private boolean checkUpdateRemoteStateIfActive(int streamType) {
   5068         synchronized(mRCStack) {
   5069             Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   5070             while(stackIterator.hasNext()) {
   5071                 RemoteControlStackEntry rcse = stackIterator.next();
   5072                 if ((rcse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE)
   5073                         && isPlaystateActive(rcse.mPlaybackState)
   5074                         && (rcse.mPlaybackStream == streamType)) {
   5075                     if (DEBUG_RC) Log.d(TAG, "remote playback active on stream " + streamType
   5076                             + ", vol =" + rcse.mPlaybackVolume);
   5077                     synchronized (mMainRemote) {
   5078                         mMainRemote.mRccId = rcse.mRccId;
   5079                         mMainRemote.mVolume = rcse.mPlaybackVolume;
   5080                         mMainRemote.mVolumeMax = rcse.mPlaybackVolumeMax;
   5081                         mMainRemote.mVolumeHandling = rcse.mPlaybackVolumeHandling;
   5082                         mMainRemoteIsActive = true;
   5083                     }
   5084                     return true;
   5085                 }
   5086             }
   5087         }
   5088         synchronized (mMainRemote) {
   5089             mMainRemoteIsActive = false;
   5090         }
   5091         return false;
   5092     }
   5093 
   5094     /**
   5095      * Returns true if the given playback state is considered "active", i.e. it describes a state
   5096      * where playback is happening, or about to
   5097      * @param playState the playback state to evaluate
   5098      * @return true if active, false otherwise (inactive or unknown)
   5099      */
   5100     private static boolean isPlaystateActive(int playState) {
   5101         switch (playState) {
   5102             case RemoteControlClient.PLAYSTATE_PLAYING:
   5103             case RemoteControlClient.PLAYSTATE_BUFFERING:
   5104             case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
   5105             case RemoteControlClient.PLAYSTATE_REWINDING:
   5106             case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
   5107             case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
   5108                 return true;
   5109             default:
   5110                 return false;
   5111         }
   5112     }
   5113 
   5114     private void adjustRemoteVolume(int streamType, int direction, int flags) {
   5115         int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
   5116         boolean volFixed = false;
   5117         synchronized (mMainRemote) {
   5118             if (!mMainRemoteIsActive) {
   5119                 if (DEBUG_VOL) Log.w(TAG, "adjustRemoteVolume didn't find an active client");
   5120                 return;
   5121             }
   5122             rccId = mMainRemote.mRccId;
   5123             volFixed = (mMainRemote.mVolumeHandling ==
   5124                     RemoteControlClient.PLAYBACK_VOLUME_FIXED);
   5125         }
   5126         // unlike "local" stream volumes, we can't compute the new volume based on the direction,
   5127         // we can only notify the remote that volume needs to be updated, and we'll get an async'
   5128         // update through setPlaybackInfoForRcc()
   5129         if (!volFixed) {
   5130             sendVolumeUpdateToRemote(rccId, direction);
   5131         }
   5132 
   5133         // fire up the UI
   5134         mVolumePanel.postRemoteVolumeChanged(streamType, flags);
   5135     }
   5136 
   5137     private void sendVolumeUpdateToRemote(int rccId, int direction) {
   5138         if (DEBUG_VOL) { Log.d(TAG, "sendVolumeUpdateToRemote(rccId="+rccId+" , dir="+direction); }
   5139         if (direction == 0) {
   5140             // only handling discrete events
   5141             return;
   5142         }
   5143         IRemoteVolumeObserver rvo = null;
   5144         synchronized (mRCStack) {
   5145             Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   5146             while(stackIterator.hasNext()) {
   5147                 RemoteControlStackEntry rcse = stackIterator.next();
   5148                 //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
   5149                 if (rcse.mRccId == rccId) {
   5150                     rvo = rcse.mRemoteVolumeObs;
   5151                     break;
   5152                 }
   5153             }
   5154         }
   5155         if (rvo != null) {
   5156             try {
   5157                 rvo.dispatchRemoteVolumeUpdate(direction, -1);
   5158             } catch (RemoteException e) {
   5159                 Log.e(TAG, "Error dispatching relative volume update", e);
   5160             }
   5161         }
   5162     }
   5163 
   5164     public int getRemoteStreamMaxVolume() {
   5165         synchronized (mMainRemote) {
   5166             if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
   5167                 return 0;
   5168             }
   5169             return mMainRemote.mVolumeMax;
   5170         }
   5171     }
   5172 
   5173     public int getRemoteStreamVolume() {
   5174         synchronized (mMainRemote) {
   5175             if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
   5176                 return 0;
   5177             }
   5178             return mMainRemote.mVolume;
   5179         }
   5180     }
   5181 
   5182     public void setRemoteStreamVolume(int vol) {
   5183         if (DEBUG_VOL) { Log.d(TAG, "setRemoteStreamVolume(vol="+vol+")"); }
   5184         int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
   5185         synchronized (mMainRemote) {
   5186             if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
   5187                 return;
   5188             }
   5189             rccId = mMainRemote.mRccId;
   5190         }
   5191         IRemoteVolumeObserver rvo = null;
   5192         synchronized (mRCStack) {
   5193             Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   5194             while(stackIterator.hasNext()) {
   5195                 RemoteControlStackEntry rcse = stackIterator.next();
   5196                 if (rcse.mRccId == rccId) {
   5197                     //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
   5198                     rvo = rcse.mRemoteVolumeObs;
   5199                     break;
   5200                 }
   5201             }
   5202         }
   5203         if (rvo != null) {
   5204             try {
   5205                 rvo.dispatchRemoteVolumeUpdate(0, vol);
   5206             } catch (RemoteException e) {
   5207                 Log.e(TAG, "Error dispatching absolute volume update", e);
   5208             }
   5209         }
   5210     }
   5211 
   5212     /**
   5213      * Call to make AudioService reevaluate whether it's in a mode where remote players should
   5214      * have their volume controlled. In this implementation this is only to reset whether
   5215      * VolumePanel should display remote volumes
   5216      */
   5217     private void postReevaluateRemote() {
   5218         sendMsg(mAudioHandler, MSG_REEVALUATE_REMOTE, SENDMSG_QUEUE, 0, 0, null, 0);
   5219     }
   5220 
   5221     private void onReevaluateRemote() {
   5222         if (DEBUG_VOL) { Log.w(TAG, "onReevaluateRemote()"); }
   5223         // is there a registered RemoteControlClient that is handling remote playback
   5224         boolean hasRemotePlayback = false;
   5225         synchronized (mRCStack) {
   5226             Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   5227             while(stackIterator.hasNext()) {
   5228                 RemoteControlStackEntry rcse = stackIterator.next();
   5229                 if (rcse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) {
   5230                     hasRemotePlayback = true;
   5231                     break;
   5232                 }
   5233             }
   5234         }
   5235         synchronized (mMainRemote) {
   5236             if (mHasRemotePlayback != hasRemotePlayback) {
   5237                 mHasRemotePlayback = hasRemotePlayback;
   5238                 mVolumePanel.postRemoteSliderVisibility(hasRemotePlayback);
   5239             }
   5240         }
   5241     }
   5242 
   5243     //==========================================================================================
   5244     // Device orientation
   5245     //==========================================================================================
   5246     /**
   5247      * Handles device configuration changes that may map to a change in the orientation.
   5248      * This feature is optional, and is defined by the definition and value of the
   5249      * "ro.audio.monitorOrientation" system property.
   5250      */
   5251     private void handleConfigurationChanged(Context context) {
   5252         try {
   5253             // reading new orientation "safely" (i.e. under try catch) in case anything
   5254             // goes wrong when obtaining resources and configuration
   5255             int newOrientation = context.getResources().getConfiguration().orientation;
   5256             if (newOrientation != mDeviceOrientation) {
   5257                 mDeviceOrientation = newOrientation;
   5258                 setOrientationForAudioSystem();
   5259             }
   5260         } catch (Exception e) {
   5261             Log.e(TAG, "Error retrieving device orientation: " + e);
   5262         }
   5263     }
   5264 
   5265     private void setOrientationForAudioSystem() {
   5266         switch (mDeviceOrientation) {
   5267             case Configuration.ORIENTATION_LANDSCAPE:
   5268                 //Log.i(TAG, "orientation is landscape");
   5269                 AudioSystem.setParameters("orientation=landscape");
   5270                 break;
   5271             case Configuration.ORIENTATION_PORTRAIT:
   5272                 //Log.i(TAG, "orientation is portrait");
   5273                 AudioSystem.setParameters("orientation=portrait");
   5274                 break;
   5275             case Configuration.ORIENTATION_SQUARE:
   5276                 //Log.i(TAG, "orientation is square");
   5277                 AudioSystem.setParameters("orientation=square");
   5278                 break;
   5279             case Configuration.ORIENTATION_UNDEFINED:
   5280                 //Log.i(TAG, "orientation is undefined");
   5281                 AudioSystem.setParameters("orientation=undefined");
   5282                 break;
   5283             default:
   5284                 Log.e(TAG, "Unknown orientation");
   5285         }
   5286     }
   5287 
   5288 
   5289     // Handles request to override default use of A2DP for media.
   5290     public void setBluetoothA2dpOnInt(boolean on) {
   5291         synchronized (mBluetoothA2dpEnabledLock) {
   5292             mBluetoothA2dpEnabled = on;
   5293             sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
   5294                     AudioSystem.FOR_MEDIA,
   5295                     mBluetoothA2dpEnabled ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP,
   5296                     null, 0);
   5297         }
   5298     }
   5299 
   5300     @Override
   5301     public void setRingtonePlayer(IRingtonePlayer player) {
   5302         mContext.enforceCallingOrSelfPermission(REMOTE_AUDIO_PLAYBACK, null);
   5303         mRingtonePlayer = player;
   5304     }
   5305 
   5306     @Override
   5307     public IRingtonePlayer getRingtonePlayer() {
   5308         return mRingtonePlayer;
   5309     }
   5310 
   5311     @Override
   5312     public AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
   5313         synchronized (mCurAudioRoutes) {
   5314             AudioRoutesInfo routes = new AudioRoutesInfo(mCurAudioRoutes);
   5315             mRoutesObservers.register(observer);
   5316             return routes;
   5317         }
   5318     }
   5319 
   5320     @Override
   5321     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   5322         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
   5323 
   5324         dumpFocusStack(pw);
   5325         dumpRCStack(pw);
   5326         dumpRCCStack(pw);
   5327         dumpStreamStates(pw);
   5328         pw.println("\nAudio routes:");
   5329         pw.print("  mMainType=0x"); pw.println(Integer.toHexString(mCurAudioRoutes.mMainType));
   5330         pw.print("  mBluetoothName="); pw.println(mCurAudioRoutes.mBluetoothName);
   5331     }
   5332 }
   5333