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.media.AudioManager.RINGER_MODE_NORMAL;
     20 import static android.media.AudioManager.RINGER_MODE_SILENT;
     21 import static android.media.AudioManager.RINGER_MODE_VIBRATE;
     22 
     23 import android.app.ActivityManagerNative;
     24 import android.app.KeyguardManager;
     25 import android.app.PendingIntent;
     26 import android.app.PendingIntent.CanceledException;
     27 import android.bluetooth.BluetoothA2dp;
     28 import android.bluetooth.BluetoothAdapter;
     29 import android.bluetooth.BluetoothClass;
     30 import android.bluetooth.BluetoothDevice;
     31 import android.bluetooth.BluetoothHeadset;
     32 import android.bluetooth.BluetoothProfile;
     33 import android.content.BroadcastReceiver;
     34 import android.content.ComponentName;
     35 import android.content.ContentResolver;
     36 import android.content.Context;
     37 import android.content.Intent;
     38 import android.content.IntentFilter;
     39 import android.content.pm.PackageManager;
     40 import android.database.ContentObserver;
     41 import android.media.MediaPlayer.OnCompletionListener;
     42 import android.media.MediaPlayer.OnErrorListener;
     43 import android.os.Binder;
     44 import android.os.Bundle;
     45 import android.os.Environment;
     46 import android.os.Handler;
     47 import android.os.IBinder;
     48 import android.os.Looper;
     49 import android.os.Message;
     50 import android.os.RemoteException;
     51 import android.os.ServiceManager;
     52 import android.os.SystemProperties;
     53 import android.provider.Settings;
     54 import android.provider.Settings.System;
     55 import android.telephony.PhoneStateListener;
     56 import android.telephony.TelephonyManager;
     57 import android.util.Log;
     58 import android.view.KeyEvent;
     59 import android.view.VolumePanel;
     60 
     61 import com.android.internal.telephony.ITelephony;
     62 
     63 import java.io.FileDescriptor;
     64 import java.io.IOException;
     65 import java.io.PrintWriter;
     66 import java.util.ArrayList;
     67 import java.util.HashMap;
     68 import java.util.Iterator;
     69 import java.util.List;
     70 import java.util.Map;
     71 import java.util.NoSuchElementException;
     72 import java.util.Set;
     73 import java.util.Stack;
     74 
     75 /**
     76  * The implementation of the volume manager service.
     77  * <p>
     78  * This implementation focuses on delivering a responsive UI. Most methods are
     79  * asynchronous to external calls. For example, the task of setting a volume
     80  * will update our internal state, but in a separate thread will set the system
     81  * volume and later persist to the database. Similarly, setting the ringer mode
     82  * will update the state and broadcast a change and in a separate thread later
     83  * persist the ringer mode.
     84  *
     85  * @hide
     86  */
     87 public class AudioService extends IAudioService.Stub {
     88 
     89     private static final String TAG = "AudioService";
     90 
     91     /** Debug remote control client/display feature */
     92     protected static final boolean DEBUG_RC = false;
     93 
     94     /** How long to delay before persisting a change in volume/ringer mode. */
     95     private static final int PERSIST_DELAY = 3000;
     96 
     97     private Context mContext;
     98     private ContentResolver mContentResolver;
     99     private boolean mVoiceCapable;
    100 
    101     /** The UI */
    102     private VolumePanel mVolumePanel;
    103 
    104     // sendMsg() flags
    105     /** Used when a message should be shared across all stream types. */
    106     private static final int SHARED_MSG = -1;
    107     /** If the msg is already queued, replace it with this one. */
    108     private static final int SENDMSG_REPLACE = 0;
    109     /** If the msg is already queued, ignore this one and leave the old. */
    110     private static final int SENDMSG_NOOP = 1;
    111     /** If the msg is already queued, queue this one and leave the old. */
    112     private static final int SENDMSG_QUEUE = 2;
    113 
    114     // AudioHandler message.whats
    115     private static final int MSG_SET_SYSTEM_VOLUME = 0;
    116     private static final int MSG_PERSIST_VOLUME = 1;
    117     private static final int MSG_PERSIST_RINGER_MODE = 3;
    118     private static final int MSG_PERSIST_VIBRATE_SETTING = 4;
    119     private static final int MSG_MEDIA_SERVER_DIED = 5;
    120     private static final int MSG_MEDIA_SERVER_STARTED = 6;
    121     private static final int MSG_PLAY_SOUND_EFFECT = 7;
    122     private static final int MSG_BTA2DP_DOCK_TIMEOUT = 8;
    123     private static final int MSG_LOAD_SOUND_EFFECTS = 9;
    124     private static final int MSG_SET_FORCE_USE = 10;
    125     private static final int MSG_PERSIST_MEDIABUTTONRECEIVER = 11;
    126     private static final int MSG_BT_HEADSET_CNCT_FAILED = 12;
    127     private static final int MSG_RCDISPLAY_CLEAR = 13;
    128     private static final int MSG_RCDISPLAY_UPDATE = 14;
    129 
    130     private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
    131     // Timeout for connection to bluetooth headset service
    132     private static final int BT_HEADSET_CNCT_TIMEOUT_MS = 3000;
    133 
    134 
    135     /** @see AudioSystemThread */
    136     private AudioSystemThread mAudioSystemThread;
    137     /** @see AudioHandler */
    138     private AudioHandler mAudioHandler;
    139     /** @see VolumeStreamState */
    140     private VolumeStreamState[] mStreamStates;
    141     private SettingsObserver mSettingsObserver;
    142 
    143     private int mMode;
    144     private Object mSettingsLock = new Object();
    145     private boolean mMediaServerOk;
    146 
    147     private SoundPool mSoundPool;
    148     private Object mSoundEffectsLock = new Object();
    149     private static final int NUM_SOUNDPOOL_CHANNELS = 4;
    150     private static final int SOUND_EFFECT_VOLUME = 1000;
    151 
    152     /* Sound effect file names  */
    153     private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/";
    154     private static final String[] SOUND_EFFECT_FILES = new String[] {
    155         "Effect_Tick.ogg",
    156         "KeypressStandard.ogg",
    157         "KeypressSpacebar.ogg",
    158         "KeypressDelete.ogg",
    159         "KeypressReturn.ogg"
    160     };
    161 
    162     /* Sound effect file name mapping sound effect id (AudioManager.FX_xxx) to
    163      * file index in SOUND_EFFECT_FILES[] (first column) and indicating if effect
    164      * uses soundpool (second column) */
    165     private int[][] SOUND_EFFECT_FILES_MAP = new int[][] {
    166         {0, -1},  // FX_KEY_CLICK
    167         {0, -1},  // FX_FOCUS_NAVIGATION_UP
    168         {0, -1},  // FX_FOCUS_NAVIGATION_DOWN
    169         {0, -1},  // FX_FOCUS_NAVIGATION_LEFT
    170         {0, -1},  // FX_FOCUS_NAVIGATION_RIGHT
    171         {1, -1},  // FX_KEYPRESS_STANDARD
    172         {2, -1},  // FX_KEYPRESS_SPACEBAR
    173         {3, -1},  // FX_FOCUS_DELETE
    174         {4, -1}   // FX_FOCUS_RETURN
    175     };
    176 
    177    /** @hide Maximum volume index values for audio streams */
    178     private int[] MAX_STREAM_VOLUME = new int[] {
    179         5,  // STREAM_VOICE_CALL
    180         7,  // STREAM_SYSTEM
    181         7,  // STREAM_RING
    182         15, // STREAM_MUSIC
    183         7,  // STREAM_ALARM
    184         7,  // STREAM_NOTIFICATION
    185         15, // STREAM_BLUETOOTH_SCO
    186         7,  // STREAM_SYSTEM_ENFORCED
    187         15, // STREAM_DTMF
    188         15  // STREAM_TTS
    189     };
    190     /* STREAM_VOLUME_ALIAS[] indicates for each stream if it uses the volume settings
    191      * of another stream: This avoids multiplying the volume settings for hidden
    192      * stream types that follow other stream behavior for volume settings
    193      * NOTE: do not create loops in aliases! */
    194     private int[] STREAM_VOLUME_ALIAS = new int[] {
    195         AudioSystem.STREAM_VOICE_CALL,  // STREAM_VOICE_CALL
    196         AudioSystem.STREAM_SYSTEM,  // STREAM_SYSTEM
    197         AudioSystem.STREAM_RING,  // STREAM_RING
    198         AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
    199         AudioSystem.STREAM_ALARM,  // STREAM_ALARM
    200         AudioSystem.STREAM_RING,   // STREAM_NOTIFICATION
    201         AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO
    202         AudioSystem.STREAM_SYSTEM,  // STREAM_SYSTEM_ENFORCED
    203         AudioSystem.STREAM_VOICE_CALL, // STREAM_DTMF
    204         AudioSystem.STREAM_MUSIC  // STREAM_TTS
    205     };
    206 
    207     private AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() {
    208         public void onError(int error) {
    209             switch (error) {
    210             case AudioSystem.AUDIO_STATUS_SERVER_DIED:
    211                 if (mMediaServerOk) {
    212                     sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
    213                             null, 1500);
    214                     mMediaServerOk = false;
    215                 }
    216                 break;
    217             case AudioSystem.AUDIO_STATUS_OK:
    218                 if (!mMediaServerOk) {
    219                     sendMsg(mAudioHandler, MSG_MEDIA_SERVER_STARTED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
    220                             null, 0);
    221                     mMediaServerOk = true;
    222                 }
    223                 break;
    224             default:
    225                 break;
    226             }
    227        }
    228     };
    229 
    230     /**
    231      * Current ringer mode from one of {@link AudioManager#RINGER_MODE_NORMAL},
    232      * {@link AudioManager#RINGER_MODE_SILENT}, or
    233      * {@link AudioManager#RINGER_MODE_VIBRATE}.
    234      */
    235     private int mRingerMode;
    236 
    237     /** @see System#MODE_RINGER_STREAMS_AFFECTED */
    238     private int mRingerModeAffectedStreams;
    239 
    240     // Streams currently muted by ringer mode
    241     private int mRingerModeMutedStreams;
    242 
    243     /** @see System#MUTE_STREAMS_AFFECTED */
    244     private int mMuteAffectedStreams;
    245 
    246     /**
    247      * Has multiple bits per vibrate type to indicate the type's vibrate
    248      * setting. See {@link #setVibrateSetting(int, int)}.
    249      * <p>
    250      * NOTE: This is not the final decision of whether vibrate is on/off for the
    251      * type since it depends on the ringer mode. See {@link #shouldVibrate(int)}.
    252      */
    253     private int mVibrateSetting;
    254 
    255     // Broadcast receiver for device connections intent broadcasts
    256     private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
    257 
    258     //  Broadcast receiver for media button broadcasts (separate from mReceiver to
    259     //  independently change its priority)
    260     private final BroadcastReceiver mMediaButtonReceiver = new MediaButtonBroadcastReceiver();
    261 
    262     // Used to alter media button redirection when the phone is ringing.
    263     private boolean mIsRinging = false;
    264 
    265     // Devices currently connected
    266     private HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>();
    267 
    268     // Forced device usage for communications
    269     private int mForcedUseForComm;
    270 
    271     // List of binder death handlers for setMode() client processes.
    272     // The last process to have called setMode() is at the top of the list.
    273     private ArrayList <SetModeDeathHandler> mSetModeDeathHandlers = new ArrayList <SetModeDeathHandler>();
    274 
    275     // List of clients having issued a SCO start request
    276     private ArrayList <ScoClient> mScoClients = new ArrayList <ScoClient>();
    277 
    278     // BluetoothHeadset API to control SCO connection
    279     private BluetoothHeadset mBluetoothHeadset;
    280 
    281     // Bluetooth headset device
    282     private BluetoothDevice mBluetoothHeadsetDevice;
    283 
    284     // Indicate if SCO audio connection is currently active and if the initiator is
    285     // audio service (internal) or bluetooth headset (external)
    286     private int mScoAudioState;
    287     // SCO audio state is not active
    288     private static final int SCO_STATE_INACTIVE = 0;
    289     // SCO audio activation request waiting for headset service to connect
    290     private static final int SCO_STATE_ACTIVATE_REQ = 1;
    291     // SCO audio state is active or starting due to a local request to start a virtual call
    292     private static final int SCO_STATE_ACTIVE_INTERNAL = 3;
    293     // SCO audio deactivation request waiting for headset service to connect
    294     private static final int SCO_STATE_DEACTIVATE_REQ = 5;
    295 
    296     // SCO audio state is active due to an action in BT handsfree (either voice recognition or
    297     // in call audio)
    298     private static final int SCO_STATE_ACTIVE_EXTERNAL = 2;
    299     // Deactivation request for all SCO connections (initiated by audio mode change)
    300     // waiting for headset service to connect
    301     private static final int SCO_STATE_DEACTIVATE_EXT_REQ = 4;
    302 
    303     // Current connection state indicated by bluetooth headset
    304     private int mScoConnectionState;
    305 
    306     // true if boot sequence has been completed
    307     private boolean mBootCompleted;
    308     // listener for SoundPool sample load completion indication
    309     private SoundPoolCallback mSoundPoolCallBack;
    310     // thread for SoundPool listener
    311     private SoundPoolListenerThread mSoundPoolListenerThread;
    312     // message looper for SoundPool listener
    313     private Looper mSoundPoolLooper = null;
    314     // default volume applied to sound played with playSoundEffect()
    315     private static final int SOUND_EFFECT_DEFAULT_VOLUME_DB = -20;
    316     // volume applied to sound played with playSoundEffect() read from ro.config.sound_fx_volume
    317     private int SOUND_EFFECT_VOLUME_DB;
    318     // getActiveStreamType() will return STREAM_NOTIFICATION during this period after a notification
    319     // stopped
    320     private static final int NOTIFICATION_VOLUME_DELAY_MS = 5000;
    321     // previous volume adjustment direction received by checkForRingerModeChange()
    322     private int mPrevVolDirection = AudioManager.ADJUST_SAME;
    323     // Keyguard manager proxy
    324     private KeyguardManager mKeyguardManager;
    325 
    326     ///////////////////////////////////////////////////////////////////////////
    327     // Construction
    328     ///////////////////////////////////////////////////////////////////////////
    329 
    330     /** @hide */
    331     public AudioService(Context context) {
    332         mContext = context;
    333         mContentResolver = context.getContentResolver();
    334         mVoiceCapable = mContext.getResources().getBoolean(
    335                 com.android.internal.R.bool.config_voice_capable);
    336 
    337        // Intialized volume
    338         MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = SystemProperties.getInt(
    339             "ro.config.vc_call_vol_steps",
    340            MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL]);
    341 
    342         SOUND_EFFECT_VOLUME_DB = SystemProperties.getInt(
    343                 "ro.config.sound_fx_volume",
    344                 SOUND_EFFECT_DEFAULT_VOLUME_DB);
    345 
    346         mVolumePanel = new VolumePanel(context, this);
    347         mForcedUseForComm = AudioSystem.FORCE_NONE;
    348         createAudioSystemThread();
    349         readPersistedSettings();
    350         mSettingsObserver = new SettingsObserver();
    351         createStreamStates();
    352 
    353         mMode = AudioSystem.MODE_NORMAL;
    354         mMediaServerOk = true;
    355 
    356         // Call setRingerModeInt() to apply correct mute
    357         // state on streams affected by ringer mode.
    358         mRingerModeMutedStreams = 0;
    359         setRingerModeInt(getRingerMode(), false);
    360 
    361         AudioSystem.setErrorCallback(mAudioSystemCallback);
    362 
    363         // Register for device connection intent broadcasts.
    364         IntentFilter intentFilter =
    365                 new IntentFilter(Intent.ACTION_HEADSET_PLUG);
    366 
    367         intentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
    368         intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
    369         intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
    370         intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
    371         intentFilter.addAction(Intent.ACTION_USB_ANLG_HEADSET_PLUG);
    372         intentFilter.addAction(Intent.ACTION_USB_DGTL_HEADSET_PLUG);
    373         intentFilter.addAction(Intent.ACTION_HDMI_AUDIO_PLUG);
    374         intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
    375         intentFilter.addAction(Intent.ACTION_SCREEN_ON);
    376         intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
    377         context.registerReceiver(mReceiver, intentFilter);
    378 
    379         // Register for package removal intent broadcasts for media button receiver persistence
    380         IntentFilter pkgFilter = new IntentFilter();
    381         pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
    382         pkgFilter.addDataScheme("package");
    383         context.registerReceiver(mReceiver, pkgFilter);
    384 
    385         // Register for media button intent broadcasts.
    386         intentFilter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON);
    387         // Workaround for bug on priority setting
    388         //intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
    389         intentFilter.setPriority(Integer.MAX_VALUE);
    390         context.registerReceiver(mMediaButtonReceiver, intentFilter);
    391 
    392         // Register for phone state monitoring
    393         TelephonyManager tmgr = (TelephonyManager)
    394                 context.getSystemService(Context.TELEPHONY_SERVICE);
    395         tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
    396     }
    397 
    398     private void createAudioSystemThread() {
    399         mAudioSystemThread = new AudioSystemThread();
    400         mAudioSystemThread.start();
    401         waitForAudioHandlerCreation();
    402     }
    403 
    404     /** Waits for the volume handler to be created by the other thread. */
    405     private void waitForAudioHandlerCreation() {
    406         synchronized(this) {
    407             while (mAudioHandler == null) {
    408                 try {
    409                     // Wait for mAudioHandler to be set by the other thread
    410                     wait();
    411                 } catch (InterruptedException e) {
    412                     Log.e(TAG, "Interrupted while waiting on volume handler.");
    413                 }
    414             }
    415         }
    416     }
    417 
    418     private void createStreamStates() {
    419         int numStreamTypes = AudioSystem.getNumStreamTypes();
    420         VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
    421 
    422         for (int i = 0; i < numStreamTypes; i++) {
    423             streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[i]], i);
    424         }
    425 
    426         // Correct stream index values for streams with aliases
    427         for (int i = 0; i < numStreamTypes; i++) {
    428             if (STREAM_VOLUME_ALIAS[i] != i) {
    429                 int index = rescaleIndex(streams[i].mIndex, STREAM_VOLUME_ALIAS[i], i);
    430                 streams[i].mIndex = streams[i].getValidIndex(index);
    431                 setStreamVolumeIndex(i, index);
    432                 index = rescaleIndex(streams[i].mLastAudibleIndex, STREAM_VOLUME_ALIAS[i], i);
    433                 streams[i].mLastAudibleIndex = streams[i].getValidIndex(index);
    434             }
    435         }
    436     }
    437 
    438     private void readPersistedSettings() {
    439         final ContentResolver cr = mContentResolver;
    440 
    441         mRingerMode = System.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);
    442         // sanity check in case the settings are restored from a device with incompatible
    443         // ringer modes
    444         if (!AudioManager.isValidRingerMode(mRingerMode)) {
    445             mRingerMode = AudioManager.RINGER_MODE_NORMAL;
    446             System.putInt(cr, System.MODE_RINGER, mRingerMode);
    447         }
    448 
    449         mVibrateSetting = System.getInt(cr, System.VIBRATE_ON, 0);
    450 
    451         // make sure settings for ringer mode are consistent with device type: non voice capable
    452         // devices (tablets) include media stream in silent mode whereas phones don't.
    453         mRingerModeAffectedStreams = Settings.System.getInt(cr,
    454                 Settings.System.MODE_RINGER_STREAMS_AFFECTED,
    455                 ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
    456                  (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)));
    457         if (mVoiceCapable) {
    458             mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
    459         } else {
    460             mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
    461         }
    462         Settings.System.putInt(cr,
    463                 Settings.System.MODE_RINGER_STREAMS_AFFECTED, mRingerModeAffectedStreams);
    464 
    465         mMuteAffectedStreams = System.getInt(cr,
    466                 System.MUTE_STREAMS_AFFECTED,
    467                 ((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM)));
    468 
    469         // Each stream will read its own persisted settings
    470 
    471         // Broadcast the sticky intent
    472         broadcastRingerMode();
    473 
    474         // Broadcast vibrate settings
    475         broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
    476         broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION);
    477 
    478         // Restore the default media button receiver from the system settings
    479         restoreMediaButtonReceiver();
    480     }
    481 
    482     private void setStreamVolumeIndex(int stream, int index) {
    483         AudioSystem.setStreamVolumeIndex(stream, (index + 5)/10);
    484     }
    485 
    486     private int rescaleIndex(int index, int srcStream, int dstStream) {
    487         return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex();
    488     }
    489 
    490     ///////////////////////////////////////////////////////////////////////////
    491     // IPC methods
    492     ///////////////////////////////////////////////////////////////////////////
    493 
    494     /** @see AudioManager#adjustVolume(int, int) */
    495     public void adjustVolume(int direction, int flags) {
    496         adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags);
    497     }
    498 
    499     /** @see AudioManager#adjustVolume(int, int, int) */
    500     public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
    501 
    502         int streamType;
    503         if ((flags & AudioManager.FLAG_FORCE_STREAM) != 0) {
    504             streamType = suggestedStreamType;
    505         } else {
    506             streamType = getActiveStreamType(suggestedStreamType);
    507         }
    508 
    509         // Play sounds on STREAM_RING only and if lock screen is not on.
    510         if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 &&
    511                 ((STREAM_VOLUME_ALIAS[streamType] != AudioSystem.STREAM_RING) ||
    512                  (mKeyguardManager != null && mKeyguardManager.isKeyguardLocked()))) {
    513             flags &= ~AudioManager.FLAG_PLAY_SOUND;
    514         }
    515 
    516         adjustStreamVolume(streamType, direction, flags);
    517     }
    518 
    519     /** @see AudioManager#adjustStreamVolume(int, int, int) */
    520     public void adjustStreamVolume(int streamType, int direction, int flags) {
    521         ensureValidDirection(direction);
    522         ensureValidStreamType(streamType);
    523 
    524         int streamTypeAlias = STREAM_VOLUME_ALIAS[streamType];
    525         VolumeStreamState streamState = mStreamStates[streamTypeAlias];
    526         final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
    527         boolean adjustVolume = true;
    528 
    529         // If either the client forces allowing ringer modes for this adjustment,
    530         // or the stream type is one that is affected by ringer modes
    531         if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
    532              (!mVoiceCapable && streamType != AudioSystem.STREAM_VOICE_CALL &&
    533                streamType != AudioSystem.STREAM_BLUETOOTH_SCO) ||
    534                 (mVoiceCapable && streamTypeAlias == AudioSystem.STREAM_RING)) {
    535             // do not vibrate if already in vibrate mode
    536             if (mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
    537                 flags &= ~AudioManager.FLAG_VIBRATE;
    538             }
    539             // Check if the ringer mode changes with this volume adjustment. If
    540             // it does, it will handle adjusting the volume, so we won't below
    541             adjustVolume = checkForRingerModeChange(oldIndex, direction, streamTypeAlias);
    542         }
    543 
    544         // If stream is muted, adjust last audible index only
    545         int index;
    546         if (streamState.muteCount() != 0) {
    547             if (adjustVolume) {
    548                 streamState.adjustLastAudibleIndex(direction);
    549                 // Post a persist volume msg
    550                 sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType,
    551                         SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY);
    552             }
    553             index = streamState.mLastAudibleIndex;
    554         } else {
    555             if (adjustVolume && streamState.adjustIndex(direction)) {
    556                 // Post message to set system volume (it in turn will post a message
    557                 // to persist). Do not change volume if stream is muted.
    558                 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamTypeAlias, SENDMSG_NOOP, 0, 0,
    559                         streamState, 0);
    560             }
    561             index = streamState.mIndex;
    562         }
    563 
    564         sendVolumeUpdate(streamType, oldIndex, index, flags);
    565     }
    566 
    567     /** @see AudioManager#setStreamVolume(int, int, int) */
    568     public void setStreamVolume(int streamType, int index, int flags) {
    569         ensureValidStreamType(streamType);
    570         VolumeStreamState streamState = mStreamStates[STREAM_VOLUME_ALIAS[streamType]];
    571 
    572         final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
    573 
    574         // setting ring or notifications volume to 0 on voice capable devices enters silent mode
    575         if (mVoiceCapable && (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
    576                 (STREAM_VOLUME_ALIAS[streamType] == AudioSystem.STREAM_RING))) {
    577             int newRingerMode = mRingerMode;
    578             if (index == 0) {
    579                 newRingerMode = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1
    580                     ? AudioManager.RINGER_MODE_VIBRATE
    581                     : AudioManager.RINGER_MODE_SILENT;
    582                 setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, false, true);
    583             } else {
    584                 newRingerMode = AudioManager.RINGER_MODE_NORMAL;
    585             }
    586             if (newRingerMode != mRingerMode) {
    587                 setRingerMode(newRingerMode);
    588             }
    589         }
    590 
    591         index = rescaleIndex(index * 10, streamType, STREAM_VOLUME_ALIAS[streamType]);
    592         setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, false, true);
    593 
    594         index = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
    595 
    596         sendVolumeUpdate(streamType, oldIndex, index, flags);
    597     }
    598 
    599     // UI update and Broadcast Intent
    600     private void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags) {
    601         if (!mVoiceCapable && (streamType == AudioSystem.STREAM_RING)) {
    602             streamType = AudioSystem.STREAM_NOTIFICATION;
    603         }
    604 
    605         mVolumePanel.postVolumeChanged(streamType, flags);
    606 
    607         oldIndex = (oldIndex + 5) / 10;
    608         index = (index + 5) / 10;
    609         Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
    610         intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
    611         intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index);
    612         intent.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex);
    613         mContext.sendBroadcast(intent);
    614     }
    615 
    616     /**
    617      * Sets the stream state's index, and posts a message to set system volume.
    618      * This will not call out to the UI. Assumes a valid stream type.
    619      *
    620      * @param streamType Type of the stream
    621      * @param index Desired volume index of the stream
    622      * @param force If true, set the volume even if the desired volume is same
    623      * as the current volume.
    624      * @param lastAudible If true, stores new index as last audible one
    625      */
    626     private void setStreamVolumeInt(int streamType, int index, boolean force, boolean lastAudible) {
    627         VolumeStreamState streamState = mStreamStates[streamType];
    628 
    629         // If stream is muted, set last audible index only
    630         if (streamState.muteCount() != 0) {
    631             // Do not allow last audible index to be 0
    632             if (index != 0) {
    633                 streamState.setLastAudibleIndex(index);
    634                 // Post a persist volume msg
    635                 sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType,
    636                         SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY);
    637             }
    638         } else {
    639             if (streamState.setIndex(index, lastAudible) || force) {
    640                 // Post message to set system volume (it in turn will post a message
    641                 // to persist).
    642                 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamType, SENDMSG_NOOP, 0, 0,
    643                         streamState, 0);
    644             }
    645         }
    646     }
    647 
    648     /** @see AudioManager#setStreamSolo(int, boolean) */
    649     public void setStreamSolo(int streamType, boolean state, IBinder cb) {
    650         for (int stream = 0; stream < mStreamStates.length; stream++) {
    651             if (!isStreamAffectedByMute(stream) || stream == streamType) continue;
    652             // Bring back last audible volume
    653             mStreamStates[stream].mute(cb, state);
    654          }
    655     }
    656 
    657     /** @see AudioManager#setStreamMute(int, boolean) */
    658     public void setStreamMute(int streamType, boolean state, IBinder cb) {
    659         if (isStreamAffectedByMute(streamType)) {
    660             mStreamStates[streamType].mute(cb, state);
    661         }
    662     }
    663 
    664     /** get stream mute state. */
    665     public boolean isStreamMute(int streamType) {
    666         return (mStreamStates[streamType].muteCount() != 0);
    667     }
    668 
    669     /** @see AudioManager#getStreamVolume(int) */
    670     public int getStreamVolume(int streamType) {
    671         ensureValidStreamType(streamType);
    672         return (mStreamStates[streamType].mIndex + 5) / 10;
    673     }
    674 
    675     /** @see AudioManager#getStreamMaxVolume(int) */
    676     public int getStreamMaxVolume(int streamType) {
    677         ensureValidStreamType(streamType);
    678         return (mStreamStates[streamType].getMaxIndex() + 5) / 10;
    679     }
    680 
    681 
    682     /** Get last audible volume before stream was muted. */
    683     public int getLastAudibleStreamVolume(int streamType) {
    684         ensureValidStreamType(streamType);
    685         return (mStreamStates[streamType].mLastAudibleIndex + 5) / 10;
    686     }
    687 
    688     /** @see AudioManager#getRingerMode() */
    689     public int getRingerMode() {
    690         return mRingerMode;
    691     }
    692 
    693     /** @see AudioManager#setRingerMode(int) */
    694     public void setRingerMode(int ringerMode) {
    695         synchronized (mSettingsLock) {
    696             if (ringerMode != mRingerMode) {
    697                 setRingerModeInt(ringerMode, true);
    698                 // Send sticky broadcast
    699                 broadcastRingerMode();
    700             }
    701         }
    702     }
    703 
    704     private void setRingerModeInt(int ringerMode, boolean persist) {
    705         mRingerMode = ringerMode;
    706 
    707         // Mute stream if not previously muted by ringer mode and ringer mode
    708         // is not RINGER_MODE_NORMAL and stream is affected by ringer mode.
    709         // Unmute stream if previously muted by ringer mode and ringer mode
    710         // is RINGER_MODE_NORMAL or stream is not affected by ringer mode.
    711         int numStreamTypes = AudioSystem.getNumStreamTypes();
    712         for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
    713             if (isStreamMutedByRingerMode(streamType)) {
    714                 if (!isStreamAffectedByRingerMode(streamType) ||
    715                     mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
    716                     // ring and notifications volume should never be 0 when not silenced
    717                     // on voice capable devices
    718                     if (mVoiceCapable &&
    719                             STREAM_VOLUME_ALIAS[streamType] == AudioSystem.STREAM_RING &&
    720                             mStreamStates[streamType].mLastAudibleIndex == 0) {
    721                         mStreamStates[streamType].mLastAudibleIndex = 10;
    722                     }
    723                     mStreamStates[streamType].mute(null, false);
    724                     mRingerModeMutedStreams &= ~(1 << streamType);
    725                 }
    726             } else {
    727                 if (isStreamAffectedByRingerMode(streamType) &&
    728                     mRingerMode != AudioManager.RINGER_MODE_NORMAL) {
    729                    mStreamStates[streamType].mute(null, true);
    730                    mRingerModeMutedStreams |= (1 << streamType);
    731                }
    732             }
    733         }
    734 
    735         // Post a persist ringer mode msg
    736         if (persist) {
    737             sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE, SHARED_MSG,
    738                     SENDMSG_REPLACE, 0, 0, null, PERSIST_DELAY);
    739         }
    740     }
    741 
    742     /** @see AudioManager#shouldVibrate(int) */
    743     public boolean shouldVibrate(int vibrateType) {
    744 
    745         switch (getVibrateSetting(vibrateType)) {
    746 
    747             case AudioManager.VIBRATE_SETTING_ON:
    748                 return mRingerMode != AudioManager.RINGER_MODE_SILENT;
    749 
    750             case AudioManager.VIBRATE_SETTING_ONLY_SILENT:
    751                 return mRingerMode == AudioManager.RINGER_MODE_VIBRATE;
    752 
    753             case AudioManager.VIBRATE_SETTING_OFF:
    754                 // return false, even for incoming calls
    755                 return false;
    756 
    757             default:
    758                 return false;
    759         }
    760     }
    761 
    762     /** @see AudioManager#getVibrateSetting(int) */
    763     public int getVibrateSetting(int vibrateType) {
    764         return (mVibrateSetting >> (vibrateType * 2)) & 3;
    765     }
    766 
    767     /** @see AudioManager#setVibrateSetting(int, int) */
    768     public void setVibrateSetting(int vibrateType, int vibrateSetting) {
    769 
    770         mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, vibrateType, vibrateSetting);
    771 
    772         // Broadcast change
    773         broadcastVibrateSetting(vibrateType);
    774 
    775         // Post message to set ringer mode (it in turn will post a message
    776         // to persist)
    777         sendMsg(mAudioHandler, MSG_PERSIST_VIBRATE_SETTING, SHARED_MSG, SENDMSG_NOOP, 0, 0,
    778                 null, 0);
    779     }
    780 
    781     /**
    782      * @see #setVibrateSetting(int, int)
    783      */
    784     public static int getValueForVibrateSetting(int existingValue, int vibrateType,
    785             int vibrateSetting) {
    786 
    787         // First clear the existing setting. Each vibrate type has two bits in
    788         // the value. Note '3' is '11' in binary.
    789         existingValue &= ~(3 << (vibrateType * 2));
    790 
    791         // Set into the old value
    792         existingValue |= (vibrateSetting & 3) << (vibrateType * 2);
    793 
    794         return existingValue;
    795     }
    796 
    797     private class SetModeDeathHandler implements IBinder.DeathRecipient {
    798         private IBinder mCb; // To be notified of client's death
    799         private int mPid;
    800         private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client
    801 
    802         SetModeDeathHandler(IBinder cb, int pid) {
    803             mCb = cb;
    804             mPid = pid;
    805         }
    806 
    807         public void binderDied() {
    808             int newModeOwnerPid = 0;
    809             synchronized(mSetModeDeathHandlers) {
    810                 Log.w(TAG, "setMode() client died");
    811                 int index = mSetModeDeathHandlers.indexOf(this);
    812                 if (index < 0) {
    813                     Log.w(TAG, "unregistered setMode() client died");
    814                 } else {
    815                     newModeOwnerPid = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid);
    816                 }
    817             }
    818             // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
    819             // SCO connections not started by the application changing the mode
    820             if (newModeOwnerPid != 0) {
    821                  disconnectBluetoothSco(newModeOwnerPid);
    822             }
    823         }
    824 
    825         public int getPid() {
    826             return mPid;
    827         }
    828 
    829         public void setMode(int mode) {
    830             mMode = mode;
    831         }
    832 
    833         public int getMode() {
    834             return mMode;
    835         }
    836 
    837         public IBinder getBinder() {
    838             return mCb;
    839         }
    840     }
    841 
    842     /** @see AudioManager#setMode(int) */
    843     public void setMode(int mode, IBinder cb) {
    844         if (!checkAudioSettingsPermission("setMode()")) {
    845             return;
    846         }
    847 
    848         if (mode < AudioSystem.MODE_CURRENT || mode >= AudioSystem.NUM_MODES) {
    849             return;
    850         }
    851 
    852         int newModeOwnerPid = 0;
    853         synchronized(mSetModeDeathHandlers) {
    854             if (mode == AudioSystem.MODE_CURRENT) {
    855                 mode = mMode;
    856             }
    857             newModeOwnerPid = setModeInt(mode, cb, Binder.getCallingPid());
    858         }
    859         // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
    860         // SCO connections not started by the application changing the mode
    861         if (newModeOwnerPid != 0) {
    862              disconnectBluetoothSco(newModeOwnerPid);
    863         }
    864     }
    865 
    866     // must be called synchronized on mSetModeDeathHandlers
    867     // setModeInt() returns a valid PID if the audio mode was successfully set to
    868     // any mode other than NORMAL.
    869     int setModeInt(int mode, IBinder cb, int pid) {
    870         int newModeOwnerPid = 0;
    871         if (cb == null) {
    872             Log.e(TAG, "setModeInt() called with null binder");
    873             return newModeOwnerPid;
    874         }
    875 
    876         SetModeDeathHandler hdlr = null;
    877         Iterator iter = mSetModeDeathHandlers.iterator();
    878         while (iter.hasNext()) {
    879             SetModeDeathHandler h = (SetModeDeathHandler)iter.next();
    880             if (h.getPid() == pid) {
    881                 hdlr = h;
    882                 // Remove from client list so that it is re-inserted at top of list
    883                 iter.remove();
    884                 hdlr.getBinder().unlinkToDeath(hdlr, 0);
    885                 break;
    886             }
    887         }
    888         int status = AudioSystem.AUDIO_STATUS_OK;
    889         do {
    890             if (mode == AudioSystem.MODE_NORMAL) {
    891                 // get new mode from client at top the list if any
    892                 if (!mSetModeDeathHandlers.isEmpty()) {
    893                     hdlr = mSetModeDeathHandlers.get(0);
    894                     cb = hdlr.getBinder();
    895                     mode = hdlr.getMode();
    896                 }
    897             } else {
    898                 if (hdlr == null) {
    899                     hdlr = new SetModeDeathHandler(cb, pid);
    900                 }
    901                 // Register for client death notification
    902                 try {
    903                     cb.linkToDeath(hdlr, 0);
    904                 } catch (RemoteException e) {
    905                     // Client has died!
    906                     Log.w(TAG, "setMode() could not link to "+cb+" binder death");
    907                 }
    908 
    909                 // Last client to call setMode() is always at top of client list
    910                 // as required by SetModeDeathHandler.binderDied()
    911                 mSetModeDeathHandlers.add(0, hdlr);
    912                 hdlr.setMode(mode);
    913             }
    914 
    915             if (mode != mMode) {
    916                 status = AudioSystem.setPhoneState(mode);
    917                 if (status == AudioSystem.AUDIO_STATUS_OK) {
    918                     // automatically handle audio focus for mode changes
    919                     handleFocusForCalls(mMode, mode, cb);
    920                     mMode = mode;
    921                 } else {
    922                     if (hdlr != null) {
    923                         mSetModeDeathHandlers.remove(hdlr);
    924                         cb.unlinkToDeath(hdlr, 0);
    925                     }
    926                     // force reading new top of mSetModeDeathHandlers stack
    927                     mode = AudioSystem.MODE_NORMAL;
    928                 }
    929             } else {
    930                 status = AudioSystem.AUDIO_STATUS_OK;
    931             }
    932         } while (status != AudioSystem.AUDIO_STATUS_OK && !mSetModeDeathHandlers.isEmpty());
    933 
    934         if (status == AudioSystem.AUDIO_STATUS_OK) {
    935             if (mode != AudioSystem.MODE_NORMAL) {
    936                 if (mSetModeDeathHandlers.isEmpty()) {
    937                     Log.e(TAG, "setMode() different from MODE_NORMAL with empty mode client stack");
    938                 } else {
    939                     newModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
    940                 }
    941             }
    942             int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
    943             int index = mStreamStates[STREAM_VOLUME_ALIAS[streamType]].mIndex;
    944             setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, true, false);
    945         }
    946         return newModeOwnerPid;
    947     }
    948 
    949     /** pre-condition: oldMode != newMode */
    950     private void handleFocusForCalls(int oldMode, int newMode, IBinder cb) {
    951         // if ringing
    952         if (newMode == AudioSystem.MODE_RINGTONE) {
    953             // if not ringing silently
    954             int ringVolume = AudioService.this.getStreamVolume(AudioManager.STREAM_RING);
    955             if (ringVolume > 0) {
    956                 // request audio focus for the communication focus entry
    957                 requestAudioFocus(AudioManager.STREAM_RING,
    958                         AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, cb,
    959                         null /* IAudioFocusDispatcher allowed to be null only for this clientId */,
    960                         IN_VOICE_COMM_FOCUS_ID /*clientId*/,
    961                         "system");
    962 
    963             }
    964         }
    965         // if entering call
    966         else if ((newMode == AudioSystem.MODE_IN_CALL)
    967                 || (newMode == AudioSystem.MODE_IN_COMMUNICATION)) {
    968             // request audio focus for the communication focus entry
    969             // (it's ok if focus was already requested during ringing)
    970             requestAudioFocus(AudioManager.STREAM_RING,
    971                     AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, cb,
    972                     null /* IAudioFocusDispatcher allowed to be null only for this clientId */,
    973                     IN_VOICE_COMM_FOCUS_ID /*clientId*/,
    974                     "system");
    975         }
    976         // if exiting call
    977         else if (newMode == AudioSystem.MODE_NORMAL) {
    978             // abandon audio focus for communication focus entry
    979             abandonAudioFocus(null, IN_VOICE_COMM_FOCUS_ID);
    980         }
    981     }
    982 
    983     /** @see AudioManager#getMode() */
    984     public int getMode() {
    985         return mMode;
    986     }
    987 
    988     /** @see AudioManager#playSoundEffect(int) */
    989     public void playSoundEffect(int effectType) {
    990         sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
    991                 effectType, -1, null, 0);
    992     }
    993 
    994     /** @see AudioManager#playSoundEffect(int, float) */
    995     public void playSoundEffectVolume(int effectType, float volume) {
    996         loadSoundEffects();
    997         sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
    998                 effectType, (int) (volume * 1000), null, 0);
    999     }
   1000 
   1001     /**
   1002      * Loads samples into the soundpool.
   1003      * This method must be called at when sound effects are enabled
   1004      */
   1005     public boolean loadSoundEffects() {
   1006         int status;
   1007 
   1008         synchronized (mSoundEffectsLock) {
   1009             if (!mBootCompleted) {
   1010                 Log.w(TAG, "loadSoundEffects() called before boot complete");
   1011                 return false;
   1012             }
   1013 
   1014             if (mSoundPool != null) {
   1015                 return true;
   1016             }
   1017             mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
   1018             if (mSoundPool == null) {
   1019                 Log.w(TAG, "loadSoundEffects() could not allocate sound pool");
   1020                 return false;
   1021             }
   1022 
   1023             try {
   1024                 mSoundPoolCallBack = null;
   1025                 mSoundPoolListenerThread = new SoundPoolListenerThread();
   1026                 mSoundPoolListenerThread.start();
   1027                 // Wait for mSoundPoolCallBack to be set by the other thread
   1028                 mSoundEffectsLock.wait();
   1029             } catch (InterruptedException e) {
   1030                 Log.w(TAG, "Interrupted while waiting sound pool listener thread.");
   1031             }
   1032 
   1033             if (mSoundPoolCallBack == null) {
   1034                 Log.w(TAG, "loadSoundEffects() could not create SoundPool listener or thread");
   1035                 if (mSoundPoolLooper != null) {
   1036                     mSoundPoolLooper.quit();
   1037                     mSoundPoolLooper = null;
   1038                 }
   1039                 mSoundPoolListenerThread = null;
   1040                 mSoundPool.release();
   1041                 mSoundPool = null;
   1042                 return false;
   1043             }
   1044             /*
   1045              * poolId table: The value -1 in this table indicates that corresponding
   1046              * file (same index in SOUND_EFFECT_FILES[] has not been loaded.
   1047              * Once loaded, the value in poolId is the sample ID and the same
   1048              * sample can be reused for another effect using the same file.
   1049              */
   1050             int[] poolId = new int[SOUND_EFFECT_FILES.length];
   1051             for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
   1052                 poolId[fileIdx] = -1;
   1053             }
   1054             /*
   1055              * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded.
   1056              * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0:
   1057              * this indicates we have a valid sample loaded for this effect.
   1058              */
   1059 
   1060             int lastSample = 0;
   1061             for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
   1062                 // Do not load sample if this effect uses the MediaPlayer
   1063                 if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) {
   1064                     continue;
   1065                 }
   1066                 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
   1067                     String filePath = Environment.getRootDirectory()
   1068                             + SOUND_EFFECTS_PATH
   1069                             + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effect][0]];
   1070                     int sampleId = mSoundPool.load(filePath, 0);
   1071                     if (sampleId <= 0) {
   1072                         Log.w(TAG, "Soundpool could not load file: "+filePath);
   1073                     } else {
   1074                         SOUND_EFFECT_FILES_MAP[effect][1] = sampleId;
   1075                         poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId;
   1076                         lastSample = sampleId;
   1077                     }
   1078                 } else {
   1079                     SOUND_EFFECT_FILES_MAP[effect][1] = poolId[SOUND_EFFECT_FILES_MAP[effect][0]];
   1080                 }
   1081             }
   1082             // wait for all samples to be loaded
   1083             if (lastSample != 0) {
   1084                 mSoundPoolCallBack.setLastSample(lastSample);
   1085 
   1086                 try {
   1087                     mSoundEffectsLock.wait();
   1088                     status = mSoundPoolCallBack.status();
   1089                 } catch (java.lang.InterruptedException e) {
   1090                     Log.w(TAG, "Interrupted while waiting sound pool callback.");
   1091                     status = -1;
   1092                 }
   1093             } else {
   1094                 status = -1;
   1095             }
   1096 
   1097             if (mSoundPoolLooper != null) {
   1098                 mSoundPoolLooper.quit();
   1099                 mSoundPoolLooper = null;
   1100             }
   1101             mSoundPoolListenerThread = null;
   1102             if (status != 0) {
   1103                 Log.w(TAG,
   1104                         "loadSoundEffects(), Error "
   1105                                 + ((lastSample != 0) ? mSoundPoolCallBack.status() : -1)
   1106                                 + " while loading samples");
   1107                 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
   1108                     if (SOUND_EFFECT_FILES_MAP[effect][1] > 0) {
   1109                         SOUND_EFFECT_FILES_MAP[effect][1] = -1;
   1110                     }
   1111                 }
   1112 
   1113                 mSoundPool.release();
   1114                 mSoundPool = null;
   1115             }
   1116         }
   1117         return (status == 0);
   1118     }
   1119 
   1120     /**
   1121      *  Unloads samples from the sound pool.
   1122      *  This method can be called to free some memory when
   1123      *  sound effects are disabled.
   1124      */
   1125     public void unloadSoundEffects() {
   1126         synchronized (mSoundEffectsLock) {
   1127             if (mSoundPool == null) {
   1128                 return;
   1129             }
   1130 
   1131             mAudioHandler.removeMessages(MSG_LOAD_SOUND_EFFECTS);
   1132             mAudioHandler.removeMessages(MSG_PLAY_SOUND_EFFECT);
   1133 
   1134             int[] poolId = new int[SOUND_EFFECT_FILES.length];
   1135             for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
   1136                 poolId[fileIdx] = 0;
   1137             }
   1138 
   1139             for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
   1140                 if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) {
   1141                     continue;
   1142                 }
   1143                 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) {
   1144                     mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]);
   1145                     SOUND_EFFECT_FILES_MAP[effect][1] = -1;
   1146                     poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1;
   1147                 }
   1148             }
   1149             mSoundPool.release();
   1150             mSoundPool = null;
   1151         }
   1152     }
   1153 
   1154     class SoundPoolListenerThread extends Thread {
   1155         public SoundPoolListenerThread() {
   1156             super("SoundPoolListenerThread");
   1157         }
   1158 
   1159         @Override
   1160         public void run() {
   1161 
   1162             Looper.prepare();
   1163             mSoundPoolLooper = Looper.myLooper();
   1164 
   1165             synchronized (mSoundEffectsLock) {
   1166                 if (mSoundPool != null) {
   1167                     mSoundPoolCallBack = new SoundPoolCallback();
   1168                     mSoundPool.setOnLoadCompleteListener(mSoundPoolCallBack);
   1169                 }
   1170                 mSoundEffectsLock.notify();
   1171             }
   1172             Looper.loop();
   1173         }
   1174     }
   1175 
   1176     private final class SoundPoolCallback implements
   1177             android.media.SoundPool.OnLoadCompleteListener {
   1178 
   1179         int mStatus;
   1180         int mLastSample;
   1181 
   1182         public int status() {
   1183             return mStatus;
   1184         }
   1185 
   1186         public void setLastSample(int sample) {
   1187             mLastSample = sample;
   1188         }
   1189 
   1190         public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
   1191             synchronized (mSoundEffectsLock) {
   1192                 if (status != 0) {
   1193                     mStatus = status;
   1194                 }
   1195                 if (sampleId == mLastSample) {
   1196                     mSoundEffectsLock.notify();
   1197                 }
   1198             }
   1199         }
   1200     }
   1201 
   1202     /** @see AudioManager#reloadAudioSettings() */
   1203     public void reloadAudioSettings() {
   1204         // restore ringer mode, ringer mode affected streams, mute affected streams and vibrate settings
   1205         readPersistedSettings();
   1206 
   1207         // restore volume settings
   1208         int numStreamTypes = AudioSystem.getNumStreamTypes();
   1209         for (int streamType = 0; streamType < numStreamTypes; streamType++) {
   1210             VolumeStreamState streamState = mStreamStates[streamType];
   1211 
   1212             String settingName = System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[streamType]];
   1213             String lastAudibleSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
   1214             int index = Settings.System.getInt(mContentResolver,
   1215                                            settingName,
   1216                                            AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
   1217             if (STREAM_VOLUME_ALIAS[streamType] != streamType) {
   1218                 index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType);
   1219             } else {
   1220                 index *= 10;
   1221             }
   1222             streamState.mIndex = streamState.getValidIndex(index);
   1223 
   1224             index = (index + 5) / 10;
   1225             index = Settings.System.getInt(mContentResolver,
   1226                                             lastAudibleSettingName,
   1227                                             (index > 0) ? index : AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
   1228             if (STREAM_VOLUME_ALIAS[streamType] != streamType) {
   1229                 index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType);
   1230             } else {
   1231                 index *= 10;
   1232             }
   1233             streamState.mLastAudibleIndex = streamState.getValidIndex(index);
   1234 
   1235             // unmute stream that was muted but is not affect by mute anymore
   1236             if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType)) {
   1237                 int size = streamState.mDeathHandlers.size();
   1238                 for (int i = 0; i < size; i++) {
   1239                     streamState.mDeathHandlers.get(i).mMuteCount = 1;
   1240                     streamState.mDeathHandlers.get(i).mute(false);
   1241                 }
   1242             }
   1243             // apply stream volume
   1244             if (streamState.muteCount() == 0) {
   1245                 setStreamVolumeIndex(streamType, streamState.mIndex);
   1246             }
   1247         }
   1248 
   1249         // apply new ringer mode
   1250         setRingerModeInt(getRingerMode(), false);
   1251     }
   1252 
   1253     /** @see AudioManager#setSpeakerphoneOn() */
   1254     public void setSpeakerphoneOn(boolean on){
   1255         if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
   1256             return;
   1257         }
   1258         mForcedUseForComm = on ? AudioSystem.FORCE_SPEAKER : AudioSystem.FORCE_NONE;
   1259 
   1260         sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SHARED_MSG, SENDMSG_QUEUE,
   1261                 AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0);
   1262     }
   1263 
   1264     /** @see AudioManager#isSpeakerphoneOn() */
   1265     public boolean isSpeakerphoneOn() {
   1266         return (mForcedUseForComm == AudioSystem.FORCE_SPEAKER);
   1267     }
   1268 
   1269     /** @see AudioManager#setBluetoothScoOn() */
   1270     public void setBluetoothScoOn(boolean on){
   1271         if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
   1272             return;
   1273         }
   1274         mForcedUseForComm = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE;
   1275 
   1276         sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SHARED_MSG, SENDMSG_QUEUE,
   1277                 AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0);
   1278         sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SHARED_MSG, SENDMSG_QUEUE,
   1279                 AudioSystem.FOR_RECORD, mForcedUseForComm, null, 0);
   1280     }
   1281 
   1282     /** @see AudioManager#isBluetoothScoOn() */
   1283     public boolean isBluetoothScoOn() {
   1284         return (mForcedUseForComm == AudioSystem.FORCE_BT_SCO);
   1285     }
   1286 
   1287     /** @see AudioManager#startBluetoothSco() */
   1288     public void startBluetoothSco(IBinder cb){
   1289         if (!checkAudioSettingsPermission("startBluetoothSco()") ||
   1290                 !mBootCompleted) {
   1291             return;
   1292         }
   1293         ScoClient client = getScoClient(cb, true);
   1294         client.incCount();
   1295     }
   1296 
   1297     /** @see AudioManager#stopBluetoothSco() */
   1298     public void stopBluetoothSco(IBinder cb){
   1299         if (!checkAudioSettingsPermission("stopBluetoothSco()") ||
   1300                 !mBootCompleted) {
   1301             return;
   1302         }
   1303         ScoClient client = getScoClient(cb, false);
   1304         if (client != null) {
   1305             client.decCount();
   1306         }
   1307     }
   1308 
   1309     private class ScoClient implements IBinder.DeathRecipient {
   1310         private IBinder mCb; // To be notified of client's death
   1311         private int mCreatorPid;
   1312         private int mStartcount; // number of SCO connections started by this client
   1313 
   1314         ScoClient(IBinder cb) {
   1315             mCb = cb;
   1316             mCreatorPid = Binder.getCallingPid();
   1317             mStartcount = 0;
   1318         }
   1319 
   1320         public void binderDied() {
   1321             synchronized(mScoClients) {
   1322                 Log.w(TAG, "SCO client died");
   1323                 int index = mScoClients.indexOf(this);
   1324                 if (index < 0) {
   1325                     Log.w(TAG, "unregistered SCO client died");
   1326                 } else {
   1327                     clearCount(true);
   1328                     mScoClients.remove(this);
   1329                 }
   1330             }
   1331         }
   1332 
   1333         public void incCount() {
   1334             synchronized(mScoClients) {
   1335                 requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED);
   1336                 if (mStartcount == 0) {
   1337                     try {
   1338                         mCb.linkToDeath(this, 0);
   1339                     } catch (RemoteException e) {
   1340                         // client has already died!
   1341                         Log.w(TAG, "ScoClient  incCount() could not link to "+mCb+" binder death");
   1342                     }
   1343                 }
   1344                 mStartcount++;
   1345             }
   1346         }
   1347 
   1348         public void decCount() {
   1349             synchronized(mScoClients) {
   1350                 if (mStartcount == 0) {
   1351                     Log.w(TAG, "ScoClient.decCount() already 0");
   1352                 } else {
   1353                     mStartcount--;
   1354                     if (mStartcount == 0) {
   1355                         try {
   1356                             mCb.unlinkToDeath(this, 0);
   1357                         } catch (NoSuchElementException e) {
   1358                             Log.w(TAG, "decCount() going to 0 but not registered to binder");
   1359                         }
   1360                     }
   1361                     requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
   1362                 }
   1363             }
   1364         }
   1365 
   1366         public void clearCount(boolean stopSco) {
   1367             synchronized(mScoClients) {
   1368                 if (mStartcount != 0) {
   1369                     try {
   1370                         mCb.unlinkToDeath(this, 0);
   1371                     } catch (NoSuchElementException e) {
   1372                         Log.w(TAG, "clearCount() mStartcount: "+mStartcount+" != 0 but not registered to binder");
   1373                     }
   1374                 }
   1375                 mStartcount = 0;
   1376                 if (stopSco) {
   1377                     requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
   1378                 }
   1379             }
   1380         }
   1381 
   1382         public int getCount() {
   1383             return mStartcount;
   1384         }
   1385 
   1386         public IBinder getBinder() {
   1387             return mCb;
   1388         }
   1389 
   1390         public int getPid() {
   1391             return mCreatorPid;
   1392         }
   1393 
   1394         public int totalCount() {
   1395             synchronized(mScoClients) {
   1396                 int count = 0;
   1397                 int size = mScoClients.size();
   1398                 for (int i = 0; i < size; i++) {
   1399                     count += mScoClients.get(i).getCount();
   1400                 }
   1401                 return count;
   1402             }
   1403         }
   1404 
   1405         private void requestScoState(int state) {
   1406             checkScoAudioState();
   1407             if (totalCount() == 0) {
   1408                 if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
   1409                     // Make sure that the state transitions to CONNECTING even if we cannot initiate
   1410                     // the connection.
   1411                     broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
   1412                     // Accept SCO audio activation only in NORMAL audio mode or if the mode is
   1413                     // currently controlled by the same client process.
   1414                     synchronized(mSetModeDeathHandlers) {
   1415                         if ((mSetModeDeathHandlers.isEmpty() ||
   1416                                 mSetModeDeathHandlers.get(0).getPid() == mCreatorPid) &&
   1417                                 (mScoAudioState == SCO_STATE_INACTIVE ||
   1418                                  mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
   1419                             if (mScoAudioState == SCO_STATE_INACTIVE) {
   1420                                 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
   1421                                     if (mBluetoothHeadset.startScoUsingVirtualVoiceCall(
   1422                                             mBluetoothHeadsetDevice)) {
   1423                                         mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
   1424                                     } else {
   1425                                         broadcastScoConnectionState(
   1426                                                 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
   1427                                     }
   1428                                 } else if (getBluetoothHeadset()) {
   1429                                     mScoAudioState = SCO_STATE_ACTIVATE_REQ;
   1430                                 }
   1431                             } else {
   1432                                 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
   1433                                 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
   1434                             }
   1435                         } else {
   1436                             broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
   1437                         }
   1438                     }
   1439                 } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED &&
   1440                               (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
   1441                                mScoAudioState == SCO_STATE_ACTIVATE_REQ)) {
   1442                     if (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) {
   1443                         if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
   1444                             if (!mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
   1445                                     mBluetoothHeadsetDevice)) {
   1446                                 mScoAudioState = SCO_STATE_INACTIVE;
   1447                                 broadcastScoConnectionState(
   1448                                         AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
   1449                             }
   1450                         } else if (getBluetoothHeadset()) {
   1451                             mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
   1452                         }
   1453                     } else {
   1454                         mScoAudioState = SCO_STATE_INACTIVE;
   1455                         broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
   1456                     }
   1457                 }
   1458             }
   1459         }
   1460     }
   1461 
   1462     private void checkScoAudioState() {
   1463         if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null &&
   1464                 mScoAudioState == SCO_STATE_INACTIVE &&
   1465                 mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
   1466                 != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
   1467             mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
   1468         }
   1469     }
   1470 
   1471     private ScoClient getScoClient(IBinder cb, boolean create) {
   1472         synchronized(mScoClients) {
   1473             ScoClient client = null;
   1474             int size = mScoClients.size();
   1475             for (int i = 0; i < size; i++) {
   1476                 client = mScoClients.get(i);
   1477                 if (client.getBinder() == cb)
   1478                     return client;
   1479             }
   1480             if (create) {
   1481                 client = new ScoClient(cb);
   1482                 mScoClients.add(client);
   1483             }
   1484             return client;
   1485         }
   1486     }
   1487 
   1488     public void clearAllScoClients(int exceptPid, boolean stopSco) {
   1489         synchronized(mScoClients) {
   1490             ScoClient savedClient = null;
   1491             int size = mScoClients.size();
   1492             for (int i = 0; i < size; i++) {
   1493                 ScoClient cl = mScoClients.get(i);
   1494                 if (cl.getPid() != exceptPid) {
   1495                     cl.clearCount(stopSco);
   1496                 } else {
   1497                     savedClient = cl;
   1498                 }
   1499             }
   1500             mScoClients.clear();
   1501             if (savedClient != null) {
   1502                 mScoClients.add(savedClient);
   1503             }
   1504         }
   1505     }
   1506 
   1507     private boolean getBluetoothHeadset() {
   1508         boolean result = false;
   1509         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
   1510         if (adapter != null) {
   1511             result = adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
   1512                                     BluetoothProfile.HEADSET);
   1513         }
   1514         // If we could not get a bluetooth headset proxy, send a failure message
   1515         // without delay to reset the SCO audio state and clear SCO clients.
   1516         // If we could get a proxy, send a delayed failure message that will reset our state
   1517         // in case we don't receive onServiceConnected().
   1518         sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0,
   1519                 SENDMSG_REPLACE, 0, 0, null, result ? BT_HEADSET_CNCT_TIMEOUT_MS : 0);
   1520         return result;
   1521     }
   1522 
   1523     private void disconnectBluetoothSco(int exceptPid) {
   1524         synchronized(mScoClients) {
   1525             checkScoAudioState();
   1526             if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL ||
   1527                     mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
   1528                 if (mBluetoothHeadsetDevice != null) {
   1529                     if (mBluetoothHeadset != null) {
   1530                         if (!mBluetoothHeadset.stopVoiceRecognition(
   1531                                 mBluetoothHeadsetDevice)) {
   1532                             sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0,
   1533                                     SENDMSG_REPLACE, 0, 0, null, 0);
   1534                         }
   1535                     } else if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL &&
   1536                             getBluetoothHeadset()) {
   1537                         mScoAudioState = SCO_STATE_DEACTIVATE_EXT_REQ;
   1538                     }
   1539                 }
   1540             } else {
   1541                 clearAllScoClients(exceptPid, true);
   1542             }
   1543         }
   1544     }
   1545 
   1546     private void resetBluetoothSco() {
   1547         synchronized(mScoClients) {
   1548             clearAllScoClients(0, false);
   1549             mScoAudioState = SCO_STATE_INACTIVE;
   1550             broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
   1551         }
   1552     }
   1553 
   1554     private void broadcastScoConnectionState(int state) {
   1555         if (state != mScoConnectionState) {
   1556             Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
   1557             newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
   1558             newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE,
   1559                     mScoConnectionState);
   1560             mContext.sendStickyBroadcast(newIntent);
   1561             mScoConnectionState = state;
   1562         }
   1563     }
   1564 
   1565     private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
   1566         new BluetoothProfile.ServiceListener() {
   1567         public void onServiceConnected(int profile, BluetoothProfile proxy) {
   1568             synchronized (mScoClients) {
   1569                 // Discard timeout message
   1570                 mAudioHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
   1571                 mBluetoothHeadset = (BluetoothHeadset) proxy;
   1572                 List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
   1573                 if (deviceList.size() > 0) {
   1574                     mBluetoothHeadsetDevice = deviceList.get(0);
   1575                 } else {
   1576                     mBluetoothHeadsetDevice = null;
   1577                 }
   1578                 // Refresh SCO audio state
   1579                 checkScoAudioState();
   1580                 // Continue pending action if any
   1581                 if (mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
   1582                         mScoAudioState == SCO_STATE_DEACTIVATE_REQ ||
   1583                         mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
   1584                     boolean status = false;
   1585                     if (mBluetoothHeadsetDevice != null) {
   1586                         switch (mScoAudioState) {
   1587                         case SCO_STATE_ACTIVATE_REQ:
   1588                             mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
   1589                             status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
   1590                                     mBluetoothHeadsetDevice);
   1591                             break;
   1592                         case SCO_STATE_DEACTIVATE_REQ:
   1593                             status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
   1594                                     mBluetoothHeadsetDevice);
   1595                             break;
   1596                         case SCO_STATE_DEACTIVATE_EXT_REQ:
   1597                             status = mBluetoothHeadset.stopVoiceRecognition(
   1598                                     mBluetoothHeadsetDevice);
   1599                         }
   1600                     }
   1601                     if (!status) {
   1602                         sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0,
   1603                                 SENDMSG_REPLACE, 0, 0, null, 0);
   1604                     }
   1605                 }
   1606             }
   1607         }
   1608         public void onServiceDisconnected(int profile) {
   1609             synchronized (mScoClients) {
   1610                 mBluetoothHeadset = null;
   1611             }
   1612         }
   1613     };
   1614 
   1615     ///////////////////////////////////////////////////////////////////////////
   1616     // Internal methods
   1617     ///////////////////////////////////////////////////////////////////////////
   1618 
   1619     /**
   1620      * Checks if the adjustment should change ringer mode instead of just
   1621      * adjusting volume. If so, this will set the proper ringer mode and volume
   1622      * indices on the stream states.
   1623      */
   1624     private boolean checkForRingerModeChange(int oldIndex, int direction, int streamType) {
   1625         boolean adjustVolumeIndex = true;
   1626         int newRingerMode = mRingerMode;
   1627         int uiIndex = (oldIndex + 5) / 10;
   1628         boolean vibeInSilent = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1;
   1629 
   1630         if (mRingerMode == RINGER_MODE_NORMAL) {
   1631             if ((direction == AudioManager.ADJUST_LOWER) && (uiIndex <= 1)) {
   1632                 // enter silent mode if current index is the last audible one and not repeating a
   1633                 // volume key down
   1634                 if (vibeInSilent || mPrevVolDirection != AudioManager.ADJUST_LOWER) {
   1635                     // "silent mode", but which one?
   1636                     newRingerMode = vibeInSilent ? RINGER_MODE_VIBRATE : RINGER_MODE_SILENT;
   1637                 }
   1638                 if (uiIndex == 0 ||
   1639                         (!vibeInSilent &&
   1640                          mPrevVolDirection == AudioManager.ADJUST_LOWER &&
   1641                          mVoiceCapable && streamType == AudioSystem.STREAM_RING)) {
   1642                     adjustVolumeIndex = false;
   1643                 }
   1644             }
   1645         } else if (mRingerMode == RINGER_MODE_VIBRATE) {
   1646             if ((direction == AudioManager.ADJUST_LOWER)) {
   1647                 // Set it to silent, if it wasn't a long-press
   1648                 if (mPrevVolDirection != AudioManager.ADJUST_LOWER) {
   1649                     newRingerMode = RINGER_MODE_SILENT;
   1650                 }
   1651             } else if (direction == AudioManager.ADJUST_RAISE) {
   1652                 newRingerMode = RINGER_MODE_NORMAL;
   1653             }
   1654             adjustVolumeIndex = false;
   1655         } else {
   1656             if (direction == AudioManager.ADJUST_RAISE) {
   1657                 // exiting silent mode
   1658                 // If VIBRATE_IN_SILENT, then go into vibrate mode
   1659                 newRingerMode = vibeInSilent ? RINGER_MODE_VIBRATE : RINGER_MODE_NORMAL;
   1660             }
   1661             adjustVolumeIndex = false;
   1662         }
   1663 
   1664         if (newRingerMode != mRingerMode) {
   1665             setRingerMode(newRingerMode);
   1666         }
   1667 
   1668         mPrevVolDirection = direction;
   1669 
   1670         return adjustVolumeIndex;
   1671     }
   1672 
   1673     public boolean isStreamAffectedByRingerMode(int streamType) {
   1674         return (mRingerModeAffectedStreams & (1 << streamType)) != 0;
   1675     }
   1676 
   1677     private boolean isStreamMutedByRingerMode(int streamType) {
   1678         return (mRingerModeMutedStreams & (1 << streamType)) != 0;
   1679     }
   1680 
   1681     public boolean isStreamAffectedByMute(int streamType) {
   1682         return (mMuteAffectedStreams & (1 << streamType)) != 0;
   1683     }
   1684 
   1685     private void ensureValidDirection(int direction) {
   1686         if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) {
   1687             throw new IllegalArgumentException("Bad direction " + direction);
   1688         }
   1689     }
   1690 
   1691     private void ensureValidStreamType(int streamType) {
   1692         if (streamType < 0 || streamType >= mStreamStates.length) {
   1693             throw new IllegalArgumentException("Bad stream type " + streamType);
   1694         }
   1695     }
   1696 
   1697     private int getActiveStreamType(int suggestedStreamType) {
   1698 
   1699         if (mVoiceCapable) {
   1700             boolean isOffhook = false;
   1701             try {
   1702                 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
   1703                 if (phone != null) isOffhook = phone.isOffhook();
   1704             } catch (RemoteException e) {
   1705                 Log.w(TAG, "Couldn't connect to phone service", e);
   1706             }
   1707 
   1708             if (isOffhook || getMode() == AudioManager.MODE_IN_COMMUNICATION) {
   1709                 if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
   1710                         == AudioSystem.FORCE_BT_SCO) {
   1711                     // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
   1712                     return AudioSystem.STREAM_BLUETOOTH_SCO;
   1713                 } else {
   1714                     // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
   1715                     return AudioSystem.STREAM_VOICE_CALL;
   1716                 }
   1717             } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
   1718                 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC...");
   1719                 return AudioSystem.STREAM_MUSIC;
   1720             } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
   1721                 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING..."
   1722                 //        + " b/c USE_DEFAULT_STREAM_TYPE...");
   1723                 return AudioSystem.STREAM_RING;
   1724             } else {
   1725                 // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType);
   1726                 return suggestedStreamType;
   1727             }
   1728         } else {
   1729             if (getMode() == AudioManager.MODE_IN_COMMUNICATION) {
   1730                 if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
   1731                         == AudioSystem.FORCE_BT_SCO) {
   1732                     // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
   1733                     return AudioSystem.STREAM_BLUETOOTH_SCO;
   1734                 } else {
   1735                     // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
   1736                     return AudioSystem.STREAM_VOICE_CALL;
   1737                 }
   1738             } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_NOTIFICATION,
   1739                             NOTIFICATION_VOLUME_DELAY_MS) ||
   1740                        AudioSystem.isStreamActive(AudioSystem.STREAM_RING,
   1741                             NOTIFICATION_VOLUME_DELAY_MS)) {
   1742                 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION...");
   1743                 return AudioSystem.STREAM_NOTIFICATION;
   1744             } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0) ||
   1745                        (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE)) {
   1746                 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC "
   1747                 //        + " b/c USE_DEFAULT_STREAM_TYPE...");
   1748                 return AudioSystem.STREAM_MUSIC;
   1749             } else {
   1750                 // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType);
   1751                 return suggestedStreamType;
   1752             }
   1753         }
   1754     }
   1755 
   1756     private void broadcastRingerMode() {
   1757         // Send sticky broadcast
   1758         Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION);
   1759         broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, mRingerMode);
   1760         broadcast.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
   1761                 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
   1762         long origCallerIdentityToken = Binder.clearCallingIdentity();
   1763         mContext.sendStickyBroadcast(broadcast);
   1764         Binder.restoreCallingIdentity(origCallerIdentityToken);
   1765     }
   1766 
   1767     private void broadcastVibrateSetting(int vibrateType) {
   1768         // Send broadcast
   1769         if (ActivityManagerNative.isSystemReady()) {
   1770             Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
   1771             broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType);
   1772             broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType));
   1773             mContext.sendBroadcast(broadcast);
   1774         }
   1775     }
   1776 
   1777     // Message helper methods
   1778     private static int getMsg(int baseMsg, int streamType) {
   1779         return (baseMsg & 0xffff) | streamType << 16;
   1780     }
   1781 
   1782     private static int getMsgBase(int msg) {
   1783         return msg & 0xffff;
   1784     }
   1785 
   1786     private static void sendMsg(Handler handler, int baseMsg, int streamType,
   1787             int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {
   1788         int msg = (streamType == SHARED_MSG) ? baseMsg : getMsg(baseMsg, streamType);
   1789 
   1790         if (existingMsgPolicy == SENDMSG_REPLACE) {
   1791             handler.removeMessages(msg);
   1792         } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
   1793             return;
   1794         }
   1795 
   1796         handler
   1797                 .sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
   1798     }
   1799 
   1800     boolean checkAudioSettingsPermission(String method) {
   1801         if (mContext.checkCallingOrSelfPermission("android.permission.MODIFY_AUDIO_SETTINGS")
   1802                 == PackageManager.PERMISSION_GRANTED) {
   1803             return true;
   1804         }
   1805         String msg = "Audio Settings Permission Denial: " + method + " from pid="
   1806                 + Binder.getCallingPid()
   1807                 + ", uid=" + Binder.getCallingUid();
   1808         Log.w(TAG, msg);
   1809         return false;
   1810     }
   1811 
   1812 
   1813     ///////////////////////////////////////////////////////////////////////////
   1814     // Inner classes
   1815     ///////////////////////////////////////////////////////////////////////////
   1816 
   1817     public class VolumeStreamState {
   1818         private final int mStreamType;
   1819 
   1820         private String mVolumeIndexSettingName;
   1821         private String mLastAudibleVolumeIndexSettingName;
   1822         private int mIndexMax;
   1823         private int mIndex;
   1824         private int mLastAudibleIndex;
   1825         private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo requests client death
   1826 
   1827         private VolumeStreamState(String settingName, int streamType) {
   1828 
   1829             setVolumeIndexSettingName(settingName);
   1830 
   1831             mStreamType = streamType;
   1832 
   1833             final ContentResolver cr = mContentResolver;
   1834             mIndexMax = MAX_STREAM_VOLUME[streamType];
   1835             mIndex = Settings.System.getInt(cr,
   1836                                             mVolumeIndexSettingName,
   1837                                             AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
   1838             mLastAudibleIndex = Settings.System.getInt(cr,
   1839                                                        mLastAudibleVolumeIndexSettingName,
   1840                                                        (mIndex > 0) ? mIndex : AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
   1841             AudioSystem.initStreamVolume(streamType, 0, mIndexMax);
   1842             mIndexMax *= 10;
   1843             mIndex = getValidIndex(10 * mIndex);
   1844             mLastAudibleIndex = getValidIndex(10 * mLastAudibleIndex);
   1845             setStreamVolumeIndex(streamType, mIndex);
   1846             mDeathHandlers = new ArrayList<VolumeDeathHandler>();
   1847         }
   1848 
   1849         public void setVolumeIndexSettingName(String settingName) {
   1850             mVolumeIndexSettingName = settingName;
   1851             mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
   1852         }
   1853 
   1854         public boolean adjustIndex(int deltaIndex) {
   1855             return setIndex(mIndex + deltaIndex * 10, true);
   1856         }
   1857 
   1858         public boolean setIndex(int index, boolean lastAudible) {
   1859             int oldIndex = mIndex;
   1860             mIndex = getValidIndex(index);
   1861 
   1862             if (oldIndex != mIndex) {
   1863                 if (lastAudible) {
   1864                     mLastAudibleIndex = mIndex;
   1865                 }
   1866                 // Apply change to all streams using this one as alias
   1867                 int numStreamTypes = AudioSystem.getNumStreamTypes();
   1868                 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
   1869                     if (streamType != mStreamType && STREAM_VOLUME_ALIAS[streamType] == mStreamType) {
   1870                         mStreamStates[streamType].setIndex(rescaleIndex(mIndex, mStreamType, streamType), lastAudible);
   1871                     }
   1872                 }
   1873                 return true;
   1874             } else {
   1875                 return false;
   1876             }
   1877         }
   1878 
   1879         public void setLastAudibleIndex(int index) {
   1880             mLastAudibleIndex = getValidIndex(index);
   1881         }
   1882 
   1883         public void adjustLastAudibleIndex(int deltaIndex) {
   1884             setLastAudibleIndex(mLastAudibleIndex + deltaIndex * 10);
   1885         }
   1886 
   1887         public int getMaxIndex() {
   1888             return mIndexMax;
   1889         }
   1890 
   1891         public void mute(IBinder cb, boolean state) {
   1892             VolumeDeathHandler handler = getDeathHandler(cb, state);
   1893             if (handler == null) {
   1894                 Log.e(TAG, "Could not get client death handler for stream: "+mStreamType);
   1895                 return;
   1896             }
   1897             handler.mute(state);
   1898         }
   1899 
   1900         private int getValidIndex(int index) {
   1901             if (index < 0) {
   1902                 return 0;
   1903             } else if (index > mIndexMax) {
   1904                 return mIndexMax;
   1905             }
   1906 
   1907             return index;
   1908         }
   1909 
   1910         private class VolumeDeathHandler implements IBinder.DeathRecipient {
   1911             private IBinder mICallback; // To be notified of client's death
   1912             private int mMuteCount; // Number of active mutes for this client
   1913 
   1914             VolumeDeathHandler(IBinder cb) {
   1915                 mICallback = cb;
   1916             }
   1917 
   1918             public void mute(boolean state) {
   1919                 synchronized(mDeathHandlers) {
   1920                     if (state) {
   1921                         if (mMuteCount == 0) {
   1922                             // Register for client death notification
   1923                             try {
   1924                                 // mICallback can be 0 if muted by AudioService
   1925                                 if (mICallback != null) {
   1926                                     mICallback.linkToDeath(this, 0);
   1927                                 }
   1928                                 mDeathHandlers.add(this);
   1929                                 // If the stream is not yet muted by any client, set lvel to 0
   1930                                 if (muteCount() == 0) {
   1931                                     setIndex(0, false);
   1932                                     sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0,
   1933                                             VolumeStreamState.this, 0);
   1934                                 }
   1935                             } catch (RemoteException e) {
   1936                                 // Client has died!
   1937                                 binderDied();
   1938                                 mDeathHandlers.notify();
   1939                                 return;
   1940                             }
   1941                         } else {
   1942                             Log.w(TAG, "stream: "+mStreamType+" was already muted by this client");
   1943                         }
   1944                         mMuteCount++;
   1945                     } else {
   1946                         if (mMuteCount == 0) {
   1947                             Log.e(TAG, "unexpected unmute for stream: "+mStreamType);
   1948                         } else {
   1949                             mMuteCount--;
   1950                             if (mMuteCount == 0) {
   1951                                 // Unregistr from client death notification
   1952                                 mDeathHandlers.remove(this);
   1953                                 // mICallback can be 0 if muted by AudioService
   1954                                 if (mICallback != null) {
   1955                                     mICallback.unlinkToDeath(this, 0);
   1956                                 }
   1957                                 if (muteCount() == 0) {
   1958                                     // If the stream is not muted any more, restore it's volume if
   1959                                     // ringer mode allows it
   1960                                     if (!isStreamAffectedByRingerMode(mStreamType) || mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
   1961                                         setIndex(mLastAudibleIndex, false);
   1962                                         sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0,
   1963                                                 VolumeStreamState.this, 0);
   1964                                     }
   1965                                 }
   1966                             }
   1967                         }
   1968                     }
   1969                     mDeathHandlers.notify();
   1970                 }
   1971             }
   1972 
   1973             public void binderDied() {
   1974                 Log.w(TAG, "Volume service client died for stream: "+mStreamType);
   1975                 if (mMuteCount != 0) {
   1976                     // Reset all active mute requests from this client.
   1977                     mMuteCount = 1;
   1978                     mute(false);
   1979                 }
   1980             }
   1981         }
   1982 
   1983         private int muteCount() {
   1984             int count = 0;
   1985             int size = mDeathHandlers.size();
   1986             for (int i = 0; i < size; i++) {
   1987                 count += mDeathHandlers.get(i).mMuteCount;
   1988             }
   1989             return count;
   1990         }
   1991 
   1992         private VolumeDeathHandler getDeathHandler(IBinder cb, boolean state) {
   1993             synchronized(mDeathHandlers) {
   1994                 VolumeDeathHandler handler;
   1995                 int size = mDeathHandlers.size();
   1996                 for (int i = 0; i < size; i++) {
   1997                     handler = mDeathHandlers.get(i);
   1998                     if (cb == handler.mICallback) {
   1999                         return handler;
   2000                     }
   2001                 }
   2002                 // If this is the first mute request for this client, create a new
   2003                 // client death handler. Otherwise, it is an out of sequence unmute request.
   2004                 if (state) {
   2005                     handler = new VolumeDeathHandler(cb);
   2006                 } else {
   2007                     Log.w(TAG, "stream was not muted by this client");
   2008                     handler = null;
   2009                 }
   2010                 return handler;
   2011             }
   2012         }
   2013     }
   2014 
   2015     /** Thread that handles native AudioSystem control. */
   2016     private class AudioSystemThread extends Thread {
   2017         AudioSystemThread() {
   2018             super("AudioService");
   2019         }
   2020 
   2021         @Override
   2022         public void run() {
   2023             // Set this thread up so the handler will work on it
   2024             Looper.prepare();
   2025 
   2026             synchronized(AudioService.this) {
   2027                 mAudioHandler = new AudioHandler();
   2028 
   2029                 // Notify that the handler has been created
   2030                 AudioService.this.notify();
   2031             }
   2032 
   2033             // Listen for volume change requests that are set by VolumePanel
   2034             Looper.loop();
   2035         }
   2036     }
   2037 
   2038     /** Handles internal volume messages in separate volume thread. */
   2039     private class AudioHandler extends Handler {
   2040 
   2041         private void setSystemVolume(VolumeStreamState streamState) {
   2042 
   2043             // Adjust volume
   2044             setStreamVolumeIndex(streamState.mStreamType, streamState.mIndex);
   2045 
   2046             // Apply change to all streams using this one as alias
   2047             int numStreamTypes = AudioSystem.getNumStreamTypes();
   2048             for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
   2049                 if (streamType != streamState.mStreamType &&
   2050                     STREAM_VOLUME_ALIAS[streamType] == streamState.mStreamType) {
   2051                     setStreamVolumeIndex(streamType, mStreamStates[streamType].mIndex);
   2052                 }
   2053             }
   2054 
   2055             // Post a persist volume msg
   2056             sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamState.mStreamType,
   2057                     SENDMSG_REPLACE, 1, 1, streamState, PERSIST_DELAY);
   2058         }
   2059 
   2060         private void persistVolume(VolumeStreamState streamState, boolean current, boolean lastAudible) {
   2061             if (current) {
   2062                 System.putInt(mContentResolver, streamState.mVolumeIndexSettingName,
   2063                               (streamState.mIndex + 5)/ 10);
   2064             }
   2065             if (lastAudible) {
   2066                 System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName,
   2067                     (streamState.mLastAudibleIndex + 5) / 10);
   2068             }
   2069         }
   2070 
   2071         private void persistRingerMode() {
   2072             System.putInt(mContentResolver, System.MODE_RINGER, mRingerMode);
   2073         }
   2074 
   2075         private void persistVibrateSetting() {
   2076             System.putInt(mContentResolver, System.VIBRATE_ON, mVibrateSetting);
   2077         }
   2078 
   2079         private void playSoundEffect(int effectType, int volume) {
   2080             synchronized (mSoundEffectsLock) {
   2081                 if (mSoundPool == null) {
   2082                     return;
   2083                 }
   2084                 float volFloat;
   2085                 // use default if volume is not specified by caller
   2086                 if (volume < 0) {
   2087                     volFloat = (float)Math.pow(10, SOUND_EFFECT_VOLUME_DB/20);
   2088                 } else {
   2089                     volFloat = (float) volume / 1000.0f;
   2090                 }
   2091 
   2092                 if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) {
   2093                     mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f);
   2094                 } else {
   2095                     MediaPlayer mediaPlayer = new MediaPlayer();
   2096                     if (mediaPlayer != null) {
   2097                         try {
   2098                             String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]];
   2099                             mediaPlayer.setDataSource(filePath);
   2100                             mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM);
   2101                             mediaPlayer.prepare();
   2102                             mediaPlayer.setVolume(volFloat, volFloat);
   2103                             mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
   2104                                 public void onCompletion(MediaPlayer mp) {
   2105                                     cleanupPlayer(mp);
   2106                                 }
   2107                             });
   2108                             mediaPlayer.setOnErrorListener(new OnErrorListener() {
   2109                                 public boolean onError(MediaPlayer mp, int what, int extra) {
   2110                                     cleanupPlayer(mp);
   2111                                     return true;
   2112                                 }
   2113                             });
   2114                             mediaPlayer.start();
   2115                         } catch (IOException ex) {
   2116                             Log.w(TAG, "MediaPlayer IOException: "+ex);
   2117                         } catch (IllegalArgumentException ex) {
   2118                             Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex);
   2119                         } catch (IllegalStateException ex) {
   2120                             Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
   2121                         }
   2122                     }
   2123                 }
   2124             }
   2125         }
   2126 
   2127         private void onHandlePersistMediaButtonReceiver(ComponentName receiver) {
   2128             Settings.System.putString(mContentResolver, Settings.System.MEDIA_BUTTON_RECEIVER,
   2129                     receiver == null ? "" : receiver.flattenToString());
   2130         }
   2131 
   2132         private void cleanupPlayer(MediaPlayer mp) {
   2133             if (mp != null) {
   2134                 try {
   2135                     mp.stop();
   2136                     mp.release();
   2137                 } catch (IllegalStateException ex) {
   2138                     Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
   2139                 }
   2140             }
   2141         }
   2142 
   2143         private void setForceUse(int usage, int config) {
   2144             AudioSystem.setForceUse(usage, config);
   2145         }
   2146 
   2147         @Override
   2148         public void handleMessage(Message msg) {
   2149             int baseMsgWhat = getMsgBase(msg.what);
   2150 
   2151             switch (baseMsgWhat) {
   2152 
   2153                 case MSG_SET_SYSTEM_VOLUME:
   2154                     setSystemVolume((VolumeStreamState) msg.obj);
   2155                     break;
   2156 
   2157                 case MSG_PERSIST_VOLUME:
   2158                     persistVolume((VolumeStreamState) msg.obj, (msg.arg1 != 0), (msg.arg2 != 0));
   2159                     break;
   2160 
   2161                 case MSG_PERSIST_RINGER_MODE:
   2162                     persistRingerMode();
   2163                     break;
   2164 
   2165                 case MSG_PERSIST_VIBRATE_SETTING:
   2166                     persistVibrateSetting();
   2167                     break;
   2168 
   2169                 case MSG_MEDIA_SERVER_DIED:
   2170                     if (!mMediaServerOk) {
   2171                         Log.e(TAG, "Media server died.");
   2172                         // Force creation of new IAudioFlinger interface so that we are notified
   2173                         // when new media_server process is back to life.
   2174                         AudioSystem.setErrorCallback(mAudioSystemCallback);
   2175                         sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
   2176                                 null, 500);
   2177                     }
   2178                     break;
   2179 
   2180                 case MSG_MEDIA_SERVER_STARTED:
   2181                     Log.e(TAG, "Media server started.");
   2182                     // indicate to audio HAL that we start the reconfiguration phase after a media
   2183                     // server crash
   2184                     // Note that MSG_MEDIA_SERVER_STARTED message is only received when the media server
   2185                     // process restarts after a crash, not the first time it is started.
   2186                     AudioSystem.setParameters("restarting=true");
   2187 
   2188                     // Restore device connection states
   2189                     Set set = mConnectedDevices.entrySet();
   2190                     Iterator i = set.iterator();
   2191                     while(i.hasNext()){
   2192                         Map.Entry device = (Map.Entry)i.next();
   2193                         AudioSystem.setDeviceConnectionState(((Integer)device.getKey()).intValue(),
   2194                                                              AudioSystem.DEVICE_STATE_AVAILABLE,
   2195                                                              (String)device.getValue());
   2196                     }
   2197 
   2198                     // Restore call state
   2199                     AudioSystem.setPhoneState(mMode);
   2200 
   2201                     // Restore forced usage for communcations and record
   2202                     AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm);
   2203                     AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm);
   2204 
   2205                     // Restore stream volumes
   2206                     int numStreamTypes = AudioSystem.getNumStreamTypes();
   2207                     for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
   2208                         int index;
   2209                         VolumeStreamState streamState = mStreamStates[streamType];
   2210                         AudioSystem.initStreamVolume(streamType, 0, (streamState.mIndexMax + 5) / 10);
   2211                         if (streamState.muteCount() == 0) {
   2212                             index = streamState.mIndex;
   2213                         } else {
   2214                             index = 0;
   2215                         }
   2216                         setStreamVolumeIndex(streamType, index);
   2217                     }
   2218 
   2219                     // Restore ringer mode
   2220                     setRingerModeInt(getRingerMode(), false);
   2221 
   2222                     // indicate the end of reconfiguration phase to audio HAL
   2223                     AudioSystem.setParameters("restarting=false");
   2224                     break;
   2225 
   2226                 case MSG_LOAD_SOUND_EFFECTS:
   2227                     loadSoundEffects();
   2228                     break;
   2229 
   2230                 case MSG_PLAY_SOUND_EFFECT:
   2231                     playSoundEffect(msg.arg1, msg.arg2);
   2232                     break;
   2233 
   2234                 case MSG_BTA2DP_DOCK_TIMEOUT:
   2235                     // msg.obj  == address of BTA2DP device
   2236                     makeA2dpDeviceUnavailableNow( (String) msg.obj );
   2237                     break;
   2238 
   2239                 case MSG_SET_FORCE_USE:
   2240                     setForceUse(msg.arg1, msg.arg2);
   2241                     break;
   2242 
   2243                 case MSG_PERSIST_MEDIABUTTONRECEIVER:
   2244                     onHandlePersistMediaButtonReceiver( (ComponentName) msg.obj );
   2245                     break;
   2246 
   2247                 case MSG_RCDISPLAY_CLEAR:
   2248                     onRcDisplayClear();
   2249                     break;
   2250 
   2251                 case MSG_RCDISPLAY_UPDATE:
   2252                     // msg.obj is guaranteed to be non null
   2253                     onRcDisplayUpdate( (RemoteControlStackEntry) msg.obj, msg.arg1);
   2254                     break;
   2255 
   2256                 case MSG_BT_HEADSET_CNCT_FAILED:
   2257                     resetBluetoothSco();
   2258                     break;
   2259             }
   2260         }
   2261     }
   2262 
   2263     private class SettingsObserver extends ContentObserver {
   2264 
   2265         SettingsObserver() {
   2266             super(new Handler());
   2267             mContentResolver.registerContentObserver(Settings.System.getUriFor(
   2268                 Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this);
   2269         }
   2270 
   2271         @Override
   2272         public void onChange(boolean selfChange) {
   2273             super.onChange(selfChange);
   2274             synchronized (mSettingsLock) {
   2275                 int ringerModeAffectedStreams = Settings.System.getInt(mContentResolver,
   2276                        Settings.System.MODE_RINGER_STREAMS_AFFECTED,
   2277                        ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
   2278                        (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)));
   2279                 if (mVoiceCapable) {
   2280                     ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
   2281                 } else {
   2282                     ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
   2283                 }
   2284                 if (ringerModeAffectedStreams != mRingerModeAffectedStreams) {
   2285                     /*
   2286                      * Ensure all stream types that should be affected by ringer mode
   2287                      * are in the proper state.
   2288                      */
   2289                     mRingerModeAffectedStreams = ringerModeAffectedStreams;
   2290                     setRingerModeInt(getRingerMode(), false);
   2291                 }
   2292             }
   2293         }
   2294     }
   2295 
   2296     private void makeA2dpDeviceAvailable(String address) {
   2297         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
   2298                 AudioSystem.DEVICE_STATE_AVAILABLE,
   2299                 address);
   2300         // Reset A2DP suspend state each time a new sink is connected
   2301         AudioSystem.setParameters("A2dpSuspended=false");
   2302         mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP),
   2303                 address);
   2304     }
   2305 
   2306     private void makeA2dpDeviceUnavailableNow(String address) {
   2307         Intent noisyIntent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
   2308         mContext.sendBroadcast(noisyIntent);
   2309         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
   2310                 AudioSystem.DEVICE_STATE_UNAVAILABLE,
   2311                 address);
   2312         mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
   2313     }
   2314 
   2315     private void makeA2dpDeviceUnavailableLater(String address) {
   2316         // prevent any activity on the A2DP audio output to avoid unwanted
   2317         // reconnection of the sink.
   2318         AudioSystem.setParameters("A2dpSuspended=true");
   2319         // the device will be made unavailable later, so consider it disconnected right away
   2320         mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
   2321         // send the delayed message to make the device unavailable later
   2322         Message msg = mAudioHandler.obtainMessage(MSG_BTA2DP_DOCK_TIMEOUT, address);
   2323         mAudioHandler.sendMessageDelayed(msg, BTA2DP_DOCK_TIMEOUT_MILLIS);
   2324 
   2325     }
   2326 
   2327     private void cancelA2dpDeviceTimeout() {
   2328         mAudioHandler.removeMessages(MSG_BTA2DP_DOCK_TIMEOUT);
   2329     }
   2330 
   2331     private boolean hasScheduledA2dpDockTimeout() {
   2332         return mAudioHandler.hasMessages(MSG_BTA2DP_DOCK_TIMEOUT);
   2333     }
   2334 
   2335     /* cache of the address of the last dock the device was connected to */
   2336     private String mDockAddress;
   2337 
   2338     /**
   2339      * Receiver for misc intent broadcasts the Phone app cares about.
   2340      */
   2341     private class AudioServiceBroadcastReceiver extends BroadcastReceiver {
   2342         @Override
   2343         public void onReceive(Context context, Intent intent) {
   2344             String action = intent.getAction();
   2345 
   2346             if (action.equals(Intent.ACTION_DOCK_EVENT)) {
   2347                 int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
   2348                         Intent.EXTRA_DOCK_STATE_UNDOCKED);
   2349                 int config;
   2350                 switch (dockState) {
   2351                     case Intent.EXTRA_DOCK_STATE_DESK:
   2352                         config = AudioSystem.FORCE_BT_DESK_DOCK;
   2353                         break;
   2354                     case Intent.EXTRA_DOCK_STATE_CAR:
   2355                         config = AudioSystem.FORCE_BT_CAR_DOCK;
   2356                         break;
   2357                     case Intent.EXTRA_DOCK_STATE_LE_DESK:
   2358                         config = AudioSystem.FORCE_ANALOG_DOCK;
   2359                         break;
   2360                     case Intent.EXTRA_DOCK_STATE_HE_DESK:
   2361                         config = AudioSystem.FORCE_DIGITAL_DOCK;
   2362                         break;
   2363                     case Intent.EXTRA_DOCK_STATE_UNDOCKED:
   2364                     default:
   2365                         config = AudioSystem.FORCE_NONE;
   2366                 }
   2367                 AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
   2368             } else if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
   2369                 int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
   2370                                                BluetoothProfile.STATE_DISCONNECTED);
   2371                 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
   2372                 if (btDevice == null) {
   2373                     return;
   2374                 }
   2375                 String address = btDevice.getAddress();
   2376                 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
   2377                     address = "";
   2378                 }
   2379                 boolean isConnected =
   2380                     (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
   2381                      mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP).equals(address));
   2382 
   2383                 if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
   2384                     if (btDevice.isBluetoothDock()) {
   2385                         if (state == BluetoothProfile.STATE_DISCONNECTED) {
   2386                             // introduction of a delay for transient disconnections of docks when
   2387                             // power is rapidly turned off/on, this message will be canceled if
   2388                             // we reconnect the dock under a preset delay
   2389                             makeA2dpDeviceUnavailableLater(address);
   2390                             // the next time isConnected is evaluated, it will be false for the dock
   2391                         }
   2392                     } else {
   2393                         makeA2dpDeviceUnavailableNow(address);
   2394                     }
   2395                 } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
   2396                     if (btDevice.isBluetoothDock()) {
   2397                         // this could be a reconnection after a transient disconnection
   2398                         cancelA2dpDeviceTimeout();
   2399                         mDockAddress = address;
   2400                     } else {
   2401                         // this could be a connection of another A2DP device before the timeout of
   2402                         // a dock: cancel the dock timeout, and make the dock unavailable now
   2403                         if(hasScheduledA2dpDockTimeout()) {
   2404                             cancelA2dpDeviceTimeout();
   2405                             makeA2dpDeviceUnavailableNow(mDockAddress);
   2406                         }
   2407                     }
   2408                     makeA2dpDeviceAvailable(address);
   2409                 }
   2410             } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
   2411                 int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
   2412                                                BluetoothProfile.STATE_DISCONNECTED);
   2413                 int device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
   2414                 String address = null;
   2415 
   2416                 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
   2417                 if (btDevice == null) {
   2418                     return;
   2419                 }
   2420 
   2421                 address = btDevice.getAddress();
   2422                 BluetoothClass btClass = btDevice.getBluetoothClass();
   2423                 if (btClass != null) {
   2424                     switch (btClass.getDeviceClass()) {
   2425                     case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
   2426                     case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
   2427                         device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
   2428                         break;
   2429                     case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
   2430                         device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
   2431                         break;
   2432                     }
   2433                 }
   2434 
   2435                 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
   2436                     address = "";
   2437                 }
   2438                 boolean isConnected = (mConnectedDevices.containsKey(device) &&
   2439                                        mConnectedDevices.get(device).equals(address));
   2440 
   2441                 synchronized (mScoClients) {
   2442                     if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
   2443                         AudioSystem.setDeviceConnectionState(device,
   2444                                                              AudioSystem.DEVICE_STATE_UNAVAILABLE,
   2445                                                              address);
   2446                         mConnectedDevices.remove(device);
   2447                         mBluetoothHeadsetDevice = null;
   2448                         resetBluetoothSco();
   2449                     } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
   2450                         AudioSystem.setDeviceConnectionState(device,
   2451                                                              AudioSystem.DEVICE_STATE_AVAILABLE,
   2452                                                              address);
   2453                         mConnectedDevices.put(new Integer(device), address);
   2454                         mBluetoothHeadsetDevice = btDevice;
   2455                     }
   2456                 }
   2457             } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
   2458                 int state = intent.getIntExtra("state", 0);
   2459                 int microphone = intent.getIntExtra("microphone", 0);
   2460 
   2461                 if (microphone != 0) {
   2462                     boolean isConnected =
   2463                         mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
   2464                     if (state == 0 && isConnected) {
   2465                         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
   2466                                 AudioSystem.DEVICE_STATE_UNAVAILABLE,
   2467                                 "");
   2468                         mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
   2469                     } else if (state == 1 && !isConnected)  {
   2470                         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
   2471                                 AudioSystem.DEVICE_STATE_AVAILABLE,
   2472                                 "");
   2473                         mConnectedDevices.put(
   2474                                 new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADSET), "");
   2475                     }
   2476                 } else {
   2477                     boolean isConnected =
   2478                         mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
   2479                     if (state == 0 && isConnected) {
   2480                         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
   2481                                 AudioSystem.DEVICE_STATE_UNAVAILABLE,
   2482                                 "");
   2483                         mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
   2484                     } else if (state == 1 && !isConnected)  {
   2485                         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
   2486                                 AudioSystem.DEVICE_STATE_AVAILABLE,
   2487                                 "");
   2488                         mConnectedDevices.put(
   2489                                 new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), "");
   2490                     }
   2491                 }
   2492             } else if (action.equals(Intent.ACTION_USB_ANLG_HEADSET_PLUG)) {
   2493                 int state = intent.getIntExtra("state", 0);
   2494                 Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_ANLG_HEADSET_PLUG, state = "+state);
   2495                 boolean isConnected =
   2496                     mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
   2497                 if (state == 0 && isConnected) {
   2498                     AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
   2499                                                          AudioSystem.DEVICE_STATE_UNAVAILABLE, "");
   2500                     mConnectedDevices.remove(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
   2501                 } else if (state == 1 && !isConnected)  {
   2502                     AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
   2503                                                          AudioSystem.DEVICE_STATE_AVAILABLE, "");
   2504                     mConnectedDevices.put(
   2505                             new Integer(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET), "");
   2506                 }
   2507             } else if (action.equals(Intent.ACTION_HDMI_AUDIO_PLUG)) {
   2508                 int state = intent.getIntExtra("state", 0);
   2509                 Log.v(TAG, "Broadcast Receiver: Got ACTION_HDMI_AUDIO_PLUG, state = "+state);
   2510                 boolean isConnected =
   2511                     mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_AUX_DIGITAL);
   2512                 if (state == 0 && isConnected) {
   2513                     AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL,
   2514                                                          AudioSystem.DEVICE_STATE_UNAVAILABLE, "");
   2515                     mConnectedDevices.remove(AudioSystem.DEVICE_OUT_AUX_DIGITAL);
   2516                 } else if (state == 1 && !isConnected)  {
   2517                     AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL,
   2518                                                          AudioSystem.DEVICE_STATE_AVAILABLE, "");
   2519                     mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_AUX_DIGITAL), "");
   2520                 }
   2521             } else if (action.equals(Intent.ACTION_USB_DGTL_HEADSET_PLUG)) {
   2522                 int state = intent.getIntExtra("state", 0);
   2523                 Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_DGTL_HEADSET_PLUG, state = "+state);
   2524                 boolean isConnected =
   2525                     mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
   2526                 if (state == 0 && isConnected) {
   2527                     AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
   2528                                                          AudioSystem.DEVICE_STATE_UNAVAILABLE, "");
   2529                     mConnectedDevices.remove(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
   2530                 } else if (state == 1 && !isConnected)  {
   2531                     AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
   2532                                                          AudioSystem.DEVICE_STATE_AVAILABLE, "");
   2533                     mConnectedDevices.put(
   2534                             new Integer(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET), "");
   2535                 }
   2536             } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
   2537                 boolean broadcast = false;
   2538                 int state = AudioManager.SCO_AUDIO_STATE_ERROR;
   2539                 synchronized (mScoClients) {
   2540                     int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
   2541                     // broadcast intent if the connection was initated by AudioService
   2542                     if (!mScoClients.isEmpty() &&
   2543                             (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
   2544                              mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
   2545                              mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
   2546                         broadcast = true;
   2547                     }
   2548                     switch (btState) {
   2549                     case BluetoothHeadset.STATE_AUDIO_CONNECTED:
   2550                         state = AudioManager.SCO_AUDIO_STATE_CONNECTED;
   2551                         if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
   2552                             mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
   2553                             mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
   2554                             mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
   2555                         }
   2556                         break;
   2557                     case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
   2558                         state = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
   2559                         mScoAudioState = SCO_STATE_INACTIVE;
   2560                         clearAllScoClients(0, false);
   2561                         break;
   2562                     case BluetoothHeadset.STATE_AUDIO_CONNECTING:
   2563                         if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
   2564                             mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
   2565                             mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
   2566                             mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
   2567                         }
   2568                     default:
   2569                         // do not broadcast CONNECTING or invalid state
   2570                         broadcast = false;
   2571                         break;
   2572                     }
   2573                 }
   2574                 if (broadcast) {
   2575                     broadcastScoConnectionState(state);
   2576                     //FIXME: this is to maintain compatibility with deprecated intent
   2577                     // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
   2578                     Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
   2579                     newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
   2580                     mContext.sendStickyBroadcast(newIntent);
   2581                 }
   2582             } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
   2583                 mBootCompleted = true;
   2584                 sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SHARED_MSG, SENDMSG_NOOP,
   2585                         0, 0, null, 0);
   2586 
   2587                 mKeyguardManager =
   2588                     (KeyguardManager)mContext.getSystemService(Context.KEYGUARD_SERVICE);
   2589                 mScoConnectionState = AudioManager.SCO_AUDIO_STATE_ERROR;
   2590                 resetBluetoothSco();
   2591                 getBluetoothHeadset();
   2592                 //FIXME: this is to maintain compatibility with deprecated intent
   2593                 // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
   2594                 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
   2595                 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
   2596                         AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
   2597                 mContext.sendStickyBroadcast(newIntent);
   2598             } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) {
   2599                 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
   2600                     // a package is being removed, not replaced
   2601                     String packageName = intent.getData().getSchemeSpecificPart();
   2602                     if (packageName != null) {
   2603                         removeMediaButtonReceiverForPackage(packageName);
   2604                     }
   2605                 }
   2606             } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
   2607                 AudioSystem.setParameters("screen_state=on");
   2608             } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
   2609                 AudioSystem.setParameters("screen_state=off");
   2610             }
   2611         }
   2612     }
   2613 
   2614     //==========================================================================================
   2615     // AudioFocus
   2616     //==========================================================================================
   2617 
   2618     /* constant to identify focus stack entry that is used to hold the focus while the phone
   2619      * is ringing or during a call
   2620      */
   2621     private final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls";
   2622 
   2623     private final static Object mAudioFocusLock = new Object();
   2624 
   2625     private final static Object mRingingLock = new Object();
   2626 
   2627     private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
   2628         @Override
   2629         public void onCallStateChanged(int state, String incomingNumber) {
   2630             if (state == TelephonyManager.CALL_STATE_RINGING) {
   2631                 //Log.v(TAG, " CALL_STATE_RINGING");
   2632                 synchronized(mRingingLock) {
   2633                     mIsRinging = true;
   2634                 }
   2635             } else if ((state == TelephonyManager.CALL_STATE_OFFHOOK)
   2636                     || (state == TelephonyManager.CALL_STATE_IDLE)) {
   2637                 synchronized(mRingingLock) {
   2638                     mIsRinging = false;
   2639                 }
   2640             }
   2641         }
   2642     };
   2643 
   2644     private void notifyTopOfAudioFocusStack() {
   2645         // notify the top of the stack it gained focus
   2646         if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
   2647             if (canReassignAudioFocus()) {
   2648                 try {
   2649                     mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
   2650                             AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId);
   2651                 } catch (RemoteException e) {
   2652                     Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e);
   2653                     e.printStackTrace();
   2654                 }
   2655             }
   2656         }
   2657     }
   2658 
   2659     private static class FocusStackEntry {
   2660         public int mStreamType = -1;// no stream type
   2661         public IAudioFocusDispatcher mFocusDispatcher = null;
   2662         public IBinder mSourceRef = null;
   2663         public String mClientId;
   2664         public int mFocusChangeType;
   2665         public AudioFocusDeathHandler mHandler;
   2666         public String mPackageName;
   2667         public int mCallingUid;
   2668 
   2669         public FocusStackEntry() {
   2670         }
   2671 
   2672         public FocusStackEntry(int streamType, int duration,
   2673                 IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr,
   2674                 String pn, int uid) {
   2675             mStreamType = streamType;
   2676             mFocusDispatcher = afl;
   2677             mSourceRef = source;
   2678             mClientId = id;
   2679             mFocusChangeType = duration;
   2680             mHandler = hdlr;
   2681             mPackageName = pn;
   2682             mCallingUid = uid;
   2683         }
   2684 
   2685         public void unlinkToDeath() {
   2686             if (mSourceRef != null && mHandler != null) {
   2687                 mSourceRef.unlinkToDeath(mHandler, 0);
   2688             }
   2689         }
   2690     }
   2691 
   2692     private Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>();
   2693 
   2694     /**
   2695      * Helper function:
   2696      * Display in the log the current entries in the audio focus stack
   2697      */
   2698     private void dumpFocusStack(PrintWriter pw) {
   2699         pw.println("\nAudio Focus stack entries:");
   2700         synchronized(mAudioFocusLock) {
   2701             Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
   2702             while(stackIterator.hasNext()) {
   2703                 FocusStackEntry fse = stackIterator.next();
   2704                 pw.println("     source:" + fse.mSourceRef + " -- client: " + fse.mClientId
   2705                         + " -- duration: " + fse.mFocusChangeType
   2706                         + " -- uid: " + fse.mCallingUid);
   2707             }
   2708         }
   2709     }
   2710 
   2711     /**
   2712      * Helper function:
   2713      * Called synchronized on mAudioFocusLock
   2714      * Remove a focus listener from the focus stack.
   2715      * @param focusListenerToRemove the focus listener
   2716      * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
   2717      *   focus, notify the next item in the stack it gained focus.
   2718      */
   2719     private void removeFocusStackEntry(String clientToRemove, boolean signal) {
   2720         // is the current top of the focus stack abandoning focus? (because of death or request)
   2721         if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove))
   2722         {
   2723             //Log.i(TAG, "   removeFocusStackEntry() removing top of stack");
   2724             FocusStackEntry fse = mFocusStack.pop();
   2725             fse.unlinkToDeath();
   2726             if (signal) {
   2727                 // notify the new top of the stack it gained focus
   2728                 notifyTopOfAudioFocusStack();
   2729                 // there's a new top of the stack, let the remote control know
   2730                 synchronized(mRCStack) {
   2731                     checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
   2732                 }
   2733             }
   2734         } else {
   2735             // focus is abandoned by a client that's not at the top of the stack,
   2736             // no need to update focus.
   2737             Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
   2738             while(stackIterator.hasNext()) {
   2739                 FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
   2740                 if(fse.mClientId.equals(clientToRemove)) {
   2741                     Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
   2742                             + fse.mClientId);
   2743                     stackIterator.remove();
   2744                     fse.unlinkToDeath();
   2745                 }
   2746             }
   2747         }
   2748     }
   2749 
   2750     /**
   2751      * Helper function:
   2752      * Called synchronized on mAudioFocusLock
   2753      * Remove focus listeners from the focus stack for a particular client.
   2754      */
   2755     private void removeFocusStackEntryForClient(IBinder cb) {
   2756         // is the owner of the audio focus part of the client to remove?
   2757         boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
   2758                 mFocusStack.peek().mSourceRef.equals(cb);
   2759         Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
   2760         while(stackIterator.hasNext()) {
   2761             FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
   2762             if(fse.mSourceRef.equals(cb)) {
   2763                 Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
   2764                         + fse.mClientId);
   2765                 stackIterator.remove();
   2766             }
   2767         }
   2768         if (isTopOfStackForClientToRemove) {
   2769             // we removed an entry at the top of the stack:
   2770             //  notify the new top of the stack it gained focus.
   2771             notifyTopOfAudioFocusStack();
   2772             // there's a new top of the stack, let the remote control know
   2773             synchronized(mRCStack) {
   2774                 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
   2775             }
   2776         }
   2777     }
   2778 
   2779     /**
   2780      * Helper function:
   2781      * Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
   2782      */
   2783     private boolean canReassignAudioFocus() {
   2784         // focus requests are rejected during a phone call or when the phone is ringing
   2785         // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus
   2786         if (!mFocusStack.isEmpty() && IN_VOICE_COMM_FOCUS_ID.equals(mFocusStack.peek().mClientId)) {
   2787             return false;
   2788         }
   2789         return true;
   2790     }
   2791 
   2792     /**
   2793      * Inner class to monitor audio focus client deaths, and remove them from the audio focus
   2794      * stack if necessary.
   2795      */
   2796     private class AudioFocusDeathHandler implements IBinder.DeathRecipient {
   2797         private IBinder mCb; // To be notified of client's death
   2798 
   2799         AudioFocusDeathHandler(IBinder cb) {
   2800             mCb = cb;
   2801         }
   2802 
   2803         public void binderDied() {
   2804             synchronized(mAudioFocusLock) {
   2805                 Log.w(TAG, "  AudioFocus   audio focus client died");
   2806                 removeFocusStackEntryForClient(mCb);
   2807             }
   2808         }
   2809 
   2810         public IBinder getBinder() {
   2811             return mCb;
   2812         }
   2813     }
   2814 
   2815 
   2816     /** @see AudioManager#requestAudioFocus(IAudioFocusDispatcher, int, int) */
   2817     public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb,
   2818             IAudioFocusDispatcher fd, String clientId, String callingPackageName) {
   2819         Log.i(TAG, " AudioFocus  requestAudioFocus() from " + clientId);
   2820         // the main stream type for the audio focus request is currently not used. It may
   2821         // potentially be used to handle multiple stream type-dependent audio focuses.
   2822 
   2823         // we need a valid binder callback for clients
   2824         if (!cb.pingBinder()) {
   2825             Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
   2826             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
   2827         }
   2828 
   2829         synchronized(mAudioFocusLock) {
   2830             if (!canReassignAudioFocus()) {
   2831                 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
   2832             }
   2833 
   2834             // handle the potential premature death of the new holder of the focus
   2835             // (premature death == death before abandoning focus)
   2836             // Register for client death notification
   2837             AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
   2838             try {
   2839                 cb.linkToDeath(afdh, 0);
   2840             } catch (RemoteException e) {
   2841                 // client has already died!
   2842                 Log.w(TAG, "AudioFocus  requestAudioFocus() could not link to "+cb+" binder death");
   2843                 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
   2844             }
   2845 
   2846             if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) {
   2847                 // if focus is already owned by this client and the reason for acquiring the focus
   2848                 // hasn't changed, don't do anything
   2849                 if (mFocusStack.peek().mFocusChangeType == focusChangeHint) {
   2850                     return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
   2851                 }
   2852                 // the reason for the audio focus request has changed: remove the current top of
   2853                 // stack and respond as if we had a new focus owner
   2854                 mFocusStack.pop();
   2855             }
   2856 
   2857             // notify current top of stack it is losing focus
   2858             if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
   2859                 try {
   2860                     mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
   2861                             -1 * focusChangeHint, // loss and gain codes are inverse of each other
   2862                             mFocusStack.peek().mClientId);
   2863                 } catch (RemoteException e) {
   2864                     Log.e(TAG, " Failure to signal loss of focus due to "+ e);
   2865                     e.printStackTrace();
   2866                 }
   2867             }
   2868 
   2869             // focus requester might already be somewhere below in the stack, remove it
   2870             removeFocusStackEntry(clientId, false /* signal */);
   2871 
   2872             // push focus requester at the top of the audio focus stack
   2873             mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, fd, cb,
   2874                     clientId, afdh, callingPackageName, Binder.getCallingUid()));
   2875 
   2876             // there's a new top of the stack, let the remote control know
   2877             synchronized(mRCStack) {
   2878                 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
   2879             }
   2880         }//synchronized(mAudioFocusLock)
   2881 
   2882         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
   2883     }
   2884 
   2885     /** @see AudioManager#abandonAudioFocus(IAudioFocusDispatcher) */
   2886     public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) {
   2887         Log.i(TAG, " AudioFocus  abandonAudioFocus() from " + clientId);
   2888         try {
   2889             // this will take care of notifying the new focus owner if needed
   2890             synchronized(mAudioFocusLock) {
   2891                 removeFocusStackEntry(clientId, true);
   2892             }
   2893         } catch (java.util.ConcurrentModificationException cme) {
   2894             // Catching this exception here is temporary. It is here just to prevent
   2895             // a crash seen when the "Silent" notification is played. This is believed to be fixed
   2896             // but this try catch block is left just to be safe.
   2897             Log.e(TAG, "FATAL EXCEPTION AudioFocus  abandonAudioFocus() caused " + cme);
   2898             cme.printStackTrace();
   2899         }
   2900 
   2901         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
   2902     }
   2903 
   2904 
   2905     public void unregisterAudioFocusClient(String clientId) {
   2906         synchronized(mAudioFocusLock) {
   2907             removeFocusStackEntry(clientId, false);
   2908         }
   2909     }
   2910 
   2911 
   2912     //==========================================================================================
   2913     // RemoteControl
   2914     //==========================================================================================
   2915     /**
   2916      * Receiver for media button intents. Handles the dispatching of the media button event
   2917      * to one of the registered listeners, or if there was none, resumes the intent broadcast
   2918      * to the rest of the system.
   2919      */
   2920     private class MediaButtonBroadcastReceiver extends BroadcastReceiver {
   2921         @Override
   2922         public void onReceive(Context context, Intent intent) {
   2923             String action = intent.getAction();
   2924             if (!Intent.ACTION_MEDIA_BUTTON.equals(action)) {
   2925                 return;
   2926             }
   2927             KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
   2928             if (event != null) {
   2929                 // if in a call or ringing, do not break the current phone app behavior
   2930                 // TODO modify this to let the phone app specifically get the RC focus
   2931                 //      add modify the phone app to take advantage of the new API
   2932                 synchronized(mRingingLock) {
   2933                     if (mIsRinging || (getMode() == AudioSystem.MODE_IN_CALL) ||
   2934                             (getMode() == AudioSystem.MODE_IN_COMMUNICATION) ||
   2935                             (getMode() == AudioSystem.MODE_RINGTONE) ) {
   2936                         return;
   2937                     }
   2938                 }
   2939                 synchronized(mRCStack) {
   2940                     if (!mRCStack.empty()) {
   2941                         // create a new intent to fill in the extras of the registered PendingIntent
   2942                         Intent targetedIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
   2943                         Bundle extras = intent.getExtras();
   2944                         if (extras != null) {
   2945                             targetedIntent.putExtras(extras);
   2946                             // trap the current broadcast
   2947                             abortBroadcast();
   2948                             //Log.v(TAG, " Sending intent" + targetedIntent);
   2949                             // send the intent that was registered by the client
   2950                             try {
   2951                                 mRCStack.peek().mMediaIntent.send(context, 0, targetedIntent);
   2952                             } catch (CanceledException e) {
   2953                                 Log.e(TAG, "Error sending pending intent " + mRCStack.peek());
   2954                                 e.printStackTrace();
   2955                             }
   2956                         }
   2957                     }
   2958                 }
   2959             }
   2960         }
   2961     }
   2962 
   2963     private final Object mCurrentRcLock = new Object();
   2964     /**
   2965      * The one remote control client which will receive a request for display information.
   2966      * This object may be null.
   2967      * Access protected by mCurrentRcLock.
   2968      */
   2969     private IRemoteControlClient mCurrentRcClient = null;
   2970 
   2971     private final static int RC_INFO_NONE = 0;
   2972     private final static int RC_INFO_ALL =
   2973         RemoteControlClient.FLAG_INFORMATION_REQUEST_ALBUM_ART |
   2974         RemoteControlClient.FLAG_INFORMATION_REQUEST_KEY_MEDIA |
   2975         RemoteControlClient.FLAG_INFORMATION_REQUEST_METADATA |
   2976         RemoteControlClient.FLAG_INFORMATION_REQUEST_PLAYSTATE;
   2977 
   2978     /**
   2979      * A monotonically increasing generation counter for mCurrentRcClient.
   2980      * Only accessed with a lock on mCurrentRcLock.
   2981      * No value wrap-around issues as we only act on equal values.
   2982      */
   2983     private int mCurrentRcClientGen = 0;
   2984 
   2985     /**
   2986      * Inner class to monitor remote control client deaths, and remove the client for the
   2987      * remote control stack if necessary.
   2988      */
   2989     private class RcClientDeathHandler implements IBinder.DeathRecipient {
   2990         private IBinder mCb; // To be notified of client's death
   2991         private PendingIntent mMediaIntent;
   2992 
   2993         RcClientDeathHandler(IBinder cb, PendingIntent pi) {
   2994             mCb = cb;
   2995             mMediaIntent = pi;
   2996         }
   2997 
   2998         public void binderDied() {
   2999             Log.w(TAG, "  RemoteControlClient died");
   3000             // remote control client died, make sure the displays don't use it anymore
   3001             //  by setting its remote control client to null
   3002             registerRemoteControlClient(mMediaIntent, null/*rcClient*/, null/*ignored*/);
   3003         }
   3004 
   3005         public IBinder getBinder() {
   3006             return mCb;
   3007         }
   3008     }
   3009 
   3010     private static class RemoteControlStackEntry {
   3011         /**
   3012          * The target for the ACTION_MEDIA_BUTTON events.
   3013          * Always non null.
   3014          */
   3015         public PendingIntent mMediaIntent;
   3016         /**
   3017          * The registered media button event receiver.
   3018          * Always non null.
   3019          */
   3020         public ComponentName mReceiverComponent;
   3021         public String mCallingPackageName;
   3022         public int mCallingUid;
   3023         /**
   3024          * Provides access to the information to display on the remote control.
   3025          * May be null (when a media button event receiver is registered,
   3026          *     but no remote control client has been registered) */
   3027         public IRemoteControlClient mRcClient;
   3028         public RcClientDeathHandler mRcClientDeathHandler;
   3029 
   3030         /** precondition: mediaIntent != null, eventReceiver != null */
   3031         public RemoteControlStackEntry(PendingIntent mediaIntent, ComponentName eventReceiver) {
   3032             mMediaIntent = mediaIntent;
   3033             mReceiverComponent = eventReceiver;
   3034             mCallingUid = -1;
   3035             mRcClient = null;
   3036         }
   3037 
   3038         public void unlinkToRcClientDeath() {
   3039             if ((mRcClientDeathHandler != null) && (mRcClientDeathHandler.mCb != null)) {
   3040                 try {
   3041                     mRcClientDeathHandler.mCb.unlinkToDeath(mRcClientDeathHandler, 0);
   3042                 } catch (java.util.NoSuchElementException e) {
   3043                     // not much we can do here
   3044                     Log.e(TAG, "Encountered " + e + " in unlinkToRcClientDeath()");
   3045                     e.printStackTrace();
   3046                 }
   3047             }
   3048         }
   3049     }
   3050 
   3051     /**
   3052      *  The stack of remote control event receivers.
   3053      *  Code sections and methods that modify the remote control event receiver stack are
   3054      *  synchronized on mRCStack, but also BEFORE on mFocusLock as any change in either
   3055      *  stack, audio focus or RC, can lead to a change in the remote control display
   3056      */
   3057     private Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>();
   3058 
   3059     /**
   3060      * Helper function:
   3061      * Display in the log the current entries in the remote control focus stack
   3062      */
   3063     private void dumpRCStack(PrintWriter pw) {
   3064         pw.println("\nRemote Control stack entries:");
   3065         synchronized(mRCStack) {
   3066             Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   3067             while(stackIterator.hasNext()) {
   3068                 RemoteControlStackEntry rcse = stackIterator.next();
   3069                 pw.println("  pi: " + rcse.mMediaIntent +
   3070                         "  -- ercvr: " + rcse.mReceiverComponent +
   3071                         "  -- client: " + rcse.mRcClient +
   3072                         "  -- uid: " + rcse.mCallingUid);
   3073             }
   3074         }
   3075     }
   3076 
   3077     /**
   3078      * Helper function:
   3079      * Remove any entry in the remote control stack that has the same package name as packageName
   3080      * Pre-condition: packageName != null
   3081      */
   3082     private void removeMediaButtonReceiverForPackage(String packageName) {
   3083         synchronized(mRCStack) {
   3084             if (mRCStack.empty()) {
   3085                 return;
   3086             } else {
   3087                 RemoteControlStackEntry oldTop = mRCStack.peek();
   3088                 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   3089                 // iterate over the stack entries
   3090                 while(stackIterator.hasNext()) {
   3091                     RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
   3092                     if (packageName.equalsIgnoreCase(rcse.mReceiverComponent.getPackageName())) {
   3093                         // a stack entry is from the package being removed, remove it from the stack
   3094                         stackIterator.remove();
   3095                     }
   3096                 }
   3097                 if (mRCStack.empty()) {
   3098                     // no saved media button receiver
   3099                     mAudioHandler.sendMessage(
   3100                             mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
   3101                                     null));
   3102                 } else if (oldTop != mRCStack.peek()) {
   3103                     // the top of the stack has changed, save it in the system settings
   3104                     // by posting a message to persist it
   3105                     mAudioHandler.sendMessage(
   3106                             mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
   3107                                     mRCStack.peek().mReceiverComponent));
   3108                 }
   3109             }
   3110         }
   3111     }
   3112 
   3113     /**
   3114      * Helper function:
   3115      * Restore remote control receiver from the system settings.
   3116      */
   3117     private void restoreMediaButtonReceiver() {
   3118         String receiverName = Settings.System.getString(mContentResolver,
   3119                 Settings.System.MEDIA_BUTTON_RECEIVER);
   3120         if ((null != receiverName) && !receiverName.isEmpty()) {
   3121             ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
   3122             // construct a PendingIntent targeted to the restored component name
   3123             // for the media button and register it
   3124             Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
   3125             //     the associated intent will be handled by the component being registered
   3126             mediaButtonIntent.setComponent(eventReceiver);
   3127             PendingIntent pi = PendingIntent.getBroadcast(mContext,
   3128                     0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
   3129             registerMediaButtonIntent(pi, eventReceiver);
   3130         }
   3131     }
   3132 
   3133     /**
   3134      * Helper function:
   3135      * Set the new remote control receiver at the top of the RC focus stack.
   3136      * precondition: mediaIntent != null, target != null
   3137      */
   3138     private void pushMediaButtonReceiver(PendingIntent mediaIntent, ComponentName target) {
   3139         // already at top of stack?
   3140         if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(mediaIntent)) {
   3141             return;
   3142         }
   3143         Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   3144         RemoteControlStackEntry rcse = null;
   3145         boolean wasInsideStack = false;
   3146         while(stackIterator.hasNext()) {
   3147             rcse = (RemoteControlStackEntry)stackIterator.next();
   3148             if(rcse.mMediaIntent.equals(mediaIntent)) {
   3149                 wasInsideStack = true;
   3150                 stackIterator.remove();
   3151                 break;
   3152             }
   3153         }
   3154         if (!wasInsideStack) {
   3155             rcse = new RemoteControlStackEntry(mediaIntent, target);
   3156         }
   3157         mRCStack.push(rcse);
   3158 
   3159         // post message to persist the default media button receiver
   3160         mAudioHandler.sendMessage( mAudioHandler.obtainMessage(
   3161                 MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) );
   3162     }
   3163 
   3164     /**
   3165      * Helper function:
   3166      * Remove the remote control receiver from the RC focus stack.
   3167      * precondition: pi != null
   3168      */
   3169     private void removeMediaButtonReceiver(PendingIntent pi) {
   3170         Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   3171         while(stackIterator.hasNext()) {
   3172             RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
   3173             if(rcse.mMediaIntent.equals(pi)) {
   3174                 stackIterator.remove();
   3175                 break;
   3176             }
   3177         }
   3178     }
   3179 
   3180     /**
   3181      * Helper function:
   3182      * Called synchronized on mRCStack
   3183      */
   3184     private boolean isCurrentRcController(PendingIntent pi) {
   3185         if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(pi)) {
   3186             return true;
   3187         }
   3188         return false;
   3189     }
   3190 
   3191     //==========================================================================================
   3192     // Remote control display / client
   3193     //==========================================================================================
   3194     /**
   3195      * Update the remote control displays with the new "focused" client generation
   3196      */
   3197     private void setNewRcClientOnDisplays_syncRcsCurrc(int newClientGeneration,
   3198             PendingIntent newMediaIntent, boolean clearing) {
   3199         // NOTE: Only one IRemoteControlDisplay supported in this implementation
   3200         if (mRcDisplay != null) {
   3201             try {
   3202                 mRcDisplay.setCurrentClientId(
   3203                         newClientGeneration, newMediaIntent, clearing);
   3204             } catch (RemoteException e) {
   3205                 Log.e(TAG, "Dead display in setNewRcClientOnDisplays_syncRcsCurrc() "+e);
   3206                 // if we had a display before, stop monitoring its death
   3207                 rcDisplay_stopDeathMonitor_syncRcStack();
   3208                 mRcDisplay = null;
   3209             }
   3210         }
   3211     }
   3212 
   3213     /**
   3214      * Update the remote control clients with the new "focused" client generation
   3215      */
   3216     private void setNewRcClientGenerationOnClients_syncRcsCurrc(int newClientGeneration) {
   3217         Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   3218         while(stackIterator.hasNext()) {
   3219             RemoteControlStackEntry se = stackIterator.next();
   3220             if ((se != null) && (se.mRcClient != null)) {
   3221                 try {
   3222                     se.mRcClient.setCurrentClientGenerationId(newClientGeneration);
   3223                 } catch (RemoteException e) {
   3224                     Log.w(TAG, "Dead client in setNewRcClientGenerationOnClients_syncRcsCurrc()"+e);
   3225                     stackIterator.remove();
   3226                     se.unlinkToRcClientDeath();
   3227                 }
   3228             }
   3229         }
   3230     }
   3231 
   3232     /**
   3233      * Update the displays and clients with the new "focused" client generation and name
   3234      * @param newClientGeneration the new generation value matching a client update
   3235      * @param newClientEventReceiver the media button event receiver associated with the client.
   3236      *    May be null, which implies there is no registered media button event receiver.
   3237      * @param clearing true if the new client generation value maps to a remote control update
   3238      *    where the display should be cleared.
   3239      */
   3240     private void setNewRcClient_syncRcsCurrc(int newClientGeneration,
   3241             PendingIntent newMediaIntent, boolean clearing) {
   3242         // send the new valid client generation ID to all displays
   3243         setNewRcClientOnDisplays_syncRcsCurrc(newClientGeneration, newMediaIntent, clearing);
   3244         // send the new valid client generation ID to all clients
   3245         setNewRcClientGenerationOnClients_syncRcsCurrc(newClientGeneration);
   3246     }
   3247 
   3248     /**
   3249      * Called when processing MSG_RCDISPLAY_CLEAR event
   3250      */
   3251     private void onRcDisplayClear() {
   3252         if (DEBUG_RC) Log.i(TAG, "Clear remote control display");
   3253 
   3254         synchronized(mRCStack) {
   3255             synchronized(mCurrentRcLock) {
   3256                 mCurrentRcClientGen++;
   3257                 // synchronously update the displays and clients with the new client generation
   3258                 setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
   3259                         null /*newMediaIntent*/, true /*clearing*/);
   3260             }
   3261         }
   3262     }
   3263 
   3264     /**
   3265      * Called when processing MSG_RCDISPLAY_UPDATE event
   3266      */
   3267     private void onRcDisplayUpdate(RemoteControlStackEntry rcse, int flags /* USED ?*/) {
   3268         synchronized(mRCStack) {
   3269             synchronized(mCurrentRcLock) {
   3270                 if ((mCurrentRcClient != null) && (mCurrentRcClient.equals(rcse.mRcClient))) {
   3271                     if (DEBUG_RC) Log.i(TAG, "Display/update remote control ");
   3272 
   3273                     mCurrentRcClientGen++;
   3274                     // synchronously update the displays and clients with
   3275                     //      the new client generation
   3276                     setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
   3277                             rcse.mMediaIntent /*newMediaIntent*/,
   3278                             false /*clearing*/);
   3279 
   3280                     // tell the current client that it needs to send info
   3281                     try {
   3282                         mCurrentRcClient.onInformationRequested(mCurrentRcClientGen,
   3283                                 flags, mArtworkExpectedWidth, mArtworkExpectedHeight);
   3284                     } catch (RemoteException e) {
   3285                         Log.e(TAG, "Current valid remote client is dead: "+e);
   3286                         mCurrentRcClient = null;
   3287                     }
   3288                 } else {
   3289                     // the remote control display owner has changed between the
   3290                     // the message to update the display was sent, and the time it
   3291                     // gets to be processed (now)
   3292                 }
   3293             }
   3294         }
   3295     }
   3296 
   3297 
   3298     /**
   3299      * Helper function:
   3300      * Called synchronized on mRCStack
   3301      */
   3302     private void clearRemoteControlDisplay_syncAfRcs() {
   3303         synchronized(mCurrentRcLock) {
   3304             mCurrentRcClient = null;
   3305         }
   3306         // will cause onRcDisplayClear() to be called in AudioService's handler thread
   3307         mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_CLEAR) );
   3308     }
   3309 
   3310     /**
   3311      * Helper function for code readability: only to be called from
   3312      *    checkUpdateRemoteControlDisplay_syncAfRcs() which checks the preconditions for
   3313      *    this method.
   3314      * Preconditions:
   3315      *    - called synchronized mAudioFocusLock then on mRCStack
   3316      *    - mRCStack.isEmpty() is false
   3317      */
   3318     private void updateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
   3319         RemoteControlStackEntry rcse = mRCStack.peek();
   3320         int infoFlagsAboutToBeUsed = infoChangedFlags;
   3321         // this is where we enforce opt-in for information display on the remote controls
   3322         //   with the new AudioManager.registerRemoteControlClient() API
   3323         if (rcse.mRcClient == null) {
   3324             //Log.w(TAG, "Can't update remote control display with null remote control client");
   3325             clearRemoteControlDisplay_syncAfRcs();
   3326             return;
   3327         }
   3328         synchronized(mCurrentRcLock) {
   3329             if (!rcse.mRcClient.equals(mCurrentRcClient)) {
   3330                 // new RC client, assume every type of information shall be queried
   3331                 infoFlagsAboutToBeUsed = RC_INFO_ALL;
   3332             }
   3333             mCurrentRcClient = rcse.mRcClient;
   3334         }
   3335         // will cause onRcDisplayUpdate() to be called in AudioService's handler thread
   3336         mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_UPDATE,
   3337                 infoFlagsAboutToBeUsed /* arg1 */, 0, rcse /* obj, != null */) );
   3338     }
   3339 
   3340     /**
   3341      * Helper function:
   3342      * Called synchronized on mAudioFocusLock, then mRCStack
   3343      * Check whether the remote control display should be updated, triggers the update if required
   3344      * @param infoChangedFlags the flags corresponding to the remote control client information
   3345      *     that has changed, if applicable (checking for the update conditions might trigger a
   3346      *     clear, rather than an update event).
   3347      */
   3348     private void checkUpdateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
   3349         // determine whether the remote control display should be refreshed
   3350         // if either stack is empty, there is a mismatch, so clear the RC display
   3351         if (mRCStack.isEmpty() || mFocusStack.isEmpty()) {
   3352             clearRemoteControlDisplay_syncAfRcs();
   3353             return;
   3354         }
   3355         // if the top of the two stacks belong to different packages, there is a mismatch, clear
   3356         if ((mRCStack.peek().mCallingPackageName != null)
   3357                 && (mFocusStack.peek().mPackageName != null)
   3358                 && !(mRCStack.peek().mCallingPackageName.compareTo(
   3359                         mFocusStack.peek().mPackageName) == 0)) {
   3360             clearRemoteControlDisplay_syncAfRcs();
   3361             return;
   3362         }
   3363         // if the audio focus didn't originate from the same Uid as the one in which the remote
   3364         //   control information will be retrieved, clear
   3365         if (mRCStack.peek().mCallingUid != mFocusStack.peek().mCallingUid) {
   3366             clearRemoteControlDisplay_syncAfRcs();
   3367             return;
   3368         }
   3369         // refresh conditions were verified: update the remote controls
   3370         // ok to call: synchronized mAudioFocusLock then on mRCStack, mRCStack is not empty
   3371         updateRemoteControlDisplay_syncAfRcs(infoChangedFlags);
   3372     }
   3373 
   3374     /**
   3375      * see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
   3376      * precondition: mediaIntent != null, target != null
   3377      */
   3378     public void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver) {
   3379         Log.i(TAG, "  Remote Control   registerMediaButtonIntent() for " + mediaIntent);
   3380 
   3381         synchronized(mAudioFocusLock) {
   3382             synchronized(mRCStack) {
   3383                 pushMediaButtonReceiver(mediaIntent, eventReceiver);
   3384                 // new RC client, assume every type of information shall be queried
   3385                 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
   3386             }
   3387         }
   3388     }
   3389 
   3390     /**
   3391      * see AudioManager.unregisterMediaButtonIntent(PendingIntent mediaIntent)
   3392      * precondition: mediaIntent != null, eventReceiver != null
   3393      */
   3394     public void unregisterMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver)
   3395     {
   3396         Log.i(TAG, "  Remote Control   unregisterMediaButtonIntent() for " + mediaIntent);
   3397 
   3398         synchronized(mAudioFocusLock) {
   3399             synchronized(mRCStack) {
   3400                 boolean topOfStackWillChange = isCurrentRcController(mediaIntent);
   3401                 removeMediaButtonReceiver(mediaIntent);
   3402                 if (topOfStackWillChange) {
   3403                     // current RC client will change, assume every type of info needs to be queried
   3404                     checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
   3405                 }
   3406             }
   3407         }
   3408     }
   3409 
   3410     /**
   3411      * see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...)
   3412      * Note: using this method with rcClient == null is a way to "disable" the IRemoteControlClient
   3413      *     without modifying the RC stack, but while still causing the display to refresh (will
   3414      *     become blank as a result of this)
   3415      */
   3416     public void registerRemoteControlClient(PendingIntent mediaIntent,
   3417             IRemoteControlClient rcClient, String callingPackageName) {
   3418         if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient);
   3419         synchronized(mAudioFocusLock) {
   3420             synchronized(mRCStack) {
   3421                 // store the new display information
   3422                 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   3423                 while(stackIterator.hasNext()) {
   3424                     RemoteControlStackEntry rcse = stackIterator.next();
   3425                     if(rcse.mMediaIntent.equals(mediaIntent)) {
   3426                         // already had a remote control client?
   3427                         if (rcse.mRcClientDeathHandler != null) {
   3428                             // stop monitoring the old client's death
   3429                             rcse.unlinkToRcClientDeath();
   3430                         }
   3431                         // save the new remote control client
   3432                         rcse.mRcClient = rcClient;
   3433                         rcse.mCallingPackageName = callingPackageName;
   3434                         rcse.mCallingUid = Binder.getCallingUid();
   3435                         if (rcClient == null) {
   3436                             rcse.mRcClientDeathHandler = null;
   3437                             break;
   3438                         }
   3439 
   3440                         // there is a new (non-null) client:
   3441                         // 1/ give the new client the current display (if any)
   3442                         if (mRcDisplay != null) {
   3443                             try {
   3444                                 rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
   3445                             } catch (RemoteException e) {
   3446                                 Log.e(TAG, "Error connecting remote control display to client: "+e);
   3447                                 e.printStackTrace();
   3448                             }
   3449                         }
   3450                         // 2/ monitor the new client's death
   3451                         IBinder b = rcse.mRcClient.asBinder();
   3452                         RcClientDeathHandler rcdh =
   3453                                 new RcClientDeathHandler(b, rcse.mMediaIntent);
   3454                         try {
   3455                             b.linkToDeath(rcdh, 0);
   3456                         } catch (RemoteException e) {
   3457                             // remote control client is DOA, disqualify it
   3458                             Log.w(TAG, "registerRemoteControlClient() has a dead client " + b);
   3459                             rcse.mRcClient = null;
   3460                         }
   3461                         rcse.mRcClientDeathHandler = rcdh;
   3462                         break;
   3463                     }
   3464                 }
   3465                 // if the eventReceiver is at the top of the stack
   3466                 // then check for potential refresh of the remote controls
   3467                 if (isCurrentRcController(mediaIntent)) {
   3468                     checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
   3469                 }
   3470             }
   3471         }
   3472     }
   3473 
   3474     /**
   3475      * see AudioManager.unregisterRemoteControlClient(PendingIntent pi, ...)
   3476      * rcClient is guaranteed non-null
   3477      */
   3478     public void unregisterRemoteControlClient(PendingIntent mediaIntent,
   3479             IRemoteControlClient rcClient) {
   3480         synchronized(mAudioFocusLock) {
   3481             synchronized(mRCStack) {
   3482                 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   3483                 while(stackIterator.hasNext()) {
   3484                     RemoteControlStackEntry rcse = stackIterator.next();
   3485                     if ((rcse.mMediaIntent.equals(mediaIntent))
   3486                             && rcClient.equals(rcse.mRcClient)) {
   3487                         // we found the IRemoteControlClient to unregister
   3488                         // stop monitoring its death
   3489                         rcse.unlinkToRcClientDeath();
   3490                         // reset the client-related fields
   3491                         rcse.mRcClient = null;
   3492                         rcse.mRcClientDeathHandler = null;
   3493                         rcse.mCallingPackageName = null;
   3494                     }
   3495                 }
   3496             }
   3497         }
   3498     }
   3499 
   3500     /**
   3501      * The remote control displays.
   3502      * Access synchronized on mRCStack
   3503      * NOTE: Only one IRemoteControlDisplay supported in this implementation
   3504      */
   3505     private IRemoteControlDisplay mRcDisplay;
   3506     private RcDisplayDeathHandler mRcDisplayDeathHandler;
   3507     private int mArtworkExpectedWidth = -1;
   3508     private int mArtworkExpectedHeight = -1;
   3509     /**
   3510      * Inner class to monitor remote control display deaths, and unregister them from the list
   3511      * of displays if necessary.
   3512      */
   3513     private class RcDisplayDeathHandler implements IBinder.DeathRecipient {
   3514         private IBinder mCb; // To be notified of client's death
   3515 
   3516         public RcDisplayDeathHandler(IBinder b) {
   3517             if (DEBUG_RC) Log.i(TAG, "new RcDisplayDeathHandler for "+b);
   3518             mCb = b;
   3519         }
   3520 
   3521         public void binderDied() {
   3522             synchronized(mRCStack) {
   3523                 Log.w(TAG, "RemoteControl: display died");
   3524                 mRcDisplay = null;
   3525             }
   3526         }
   3527 
   3528         public void unlinkToRcDisplayDeath() {
   3529             if (DEBUG_RC) Log.i(TAG, "unlinkToRcDisplayDeath for "+mCb);
   3530             try {
   3531                 mCb.unlinkToDeath(this, 0);
   3532             } catch (java.util.NoSuchElementException e) {
   3533                 // not much we can do here, the display was being unregistered anyway
   3534                 Log.e(TAG, "Encountered " + e + " in unlinkToRcDisplayDeath()");
   3535                 e.printStackTrace();
   3536             }
   3537         }
   3538 
   3539     }
   3540 
   3541     private void rcDisplay_stopDeathMonitor_syncRcStack() {
   3542         if (mRcDisplay != null) { // implies (mRcDisplayDeathHandler != null)
   3543             // we had a display before, stop monitoring its death
   3544             mRcDisplayDeathHandler.unlinkToRcDisplayDeath();
   3545         }
   3546     }
   3547 
   3548     private void rcDisplay_startDeathMonitor_syncRcStack() {
   3549         if (mRcDisplay != null) {
   3550             // new non-null display, monitor its death
   3551             IBinder b = mRcDisplay.asBinder();
   3552             mRcDisplayDeathHandler = new RcDisplayDeathHandler(b);
   3553             try {
   3554                 b.linkToDeath(mRcDisplayDeathHandler, 0);
   3555             } catch (RemoteException e) {
   3556                 // remote control display is DOA, disqualify it
   3557                 Log.w(TAG, "registerRemoteControlDisplay() has a dead client " + b);
   3558                 mRcDisplay = null;
   3559             }
   3560         }
   3561     }
   3562 
   3563     /**
   3564      * Register an IRemoteControlDisplay.
   3565      * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient
   3566      * at the top of the stack to update the new display with its information.
   3567      * Since only one IRemoteControlDisplay is supported, this will unregister the previous display.
   3568      * @param rcd the IRemoteControlDisplay to register. No effect if null.
   3569      */
   3570     public void registerRemoteControlDisplay(IRemoteControlDisplay rcd) {
   3571         if (DEBUG_RC) Log.d(TAG, ">>> registerRemoteControlDisplay("+rcd+")");
   3572         synchronized(mAudioFocusLock) {
   3573             synchronized(mRCStack) {
   3574                 if ((mRcDisplay == rcd) || (rcd == null)) {
   3575                     return;
   3576                 }
   3577                 // if we had a display before, stop monitoring its death
   3578                 rcDisplay_stopDeathMonitor_syncRcStack();
   3579                 mRcDisplay = rcd;
   3580                 // new display, start monitoring its death
   3581                 rcDisplay_startDeathMonitor_syncRcStack();
   3582 
   3583                 // let all the remote control clients there is a new display
   3584                 // no need to unplug the previous because we only support one display
   3585                 // and the clients don't track the death of the display
   3586                 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   3587                 while(stackIterator.hasNext()) {
   3588                     RemoteControlStackEntry rcse = stackIterator.next();
   3589                     if(rcse.mRcClient != null) {
   3590                         try {
   3591                             rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
   3592                         } catch (RemoteException e) {
   3593                             Log.e(TAG, "Error connecting remote control display to client: " + e);
   3594                             e.printStackTrace();
   3595                         }
   3596                     }
   3597                 }
   3598 
   3599                 // we have a new display, of which all the clients are now aware: have it be updated
   3600                 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
   3601             }
   3602         }
   3603     }
   3604 
   3605     /**
   3606      * Unregister an IRemoteControlDisplay.
   3607      * Since only one IRemoteControlDisplay is supported, this has no effect if the one to
   3608      *    unregister is not the current one.
   3609      * @param rcd the IRemoteControlDisplay to unregister. No effect if null.
   3610      */
   3611     public void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) {
   3612         if (DEBUG_RC) Log.d(TAG, "<<< unregisterRemoteControlDisplay("+rcd+")");
   3613         synchronized(mRCStack) {
   3614             // only one display here, so you can only unregister the current display
   3615             if ((rcd == null) || (rcd != mRcDisplay)) {
   3616                 if (DEBUG_RC) Log.w(TAG, "    trying to unregister unregistered RCD");
   3617                 return;
   3618             }
   3619             // if we had a display before, stop monitoring its death
   3620             rcDisplay_stopDeathMonitor_syncRcStack();
   3621             mRcDisplay = null;
   3622 
   3623             // disconnect this remote control display from all the clients
   3624             Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   3625             while(stackIterator.hasNext()) {
   3626                 RemoteControlStackEntry rcse = stackIterator.next();
   3627                 if(rcse.mRcClient != null) {
   3628                     try {
   3629                         rcse.mRcClient.unplugRemoteControlDisplay(rcd);
   3630                     } catch (RemoteException e) {
   3631                         Log.e(TAG, "Error disconnecting remote control display to client: " + e);
   3632                         e.printStackTrace();
   3633                     }
   3634                 }
   3635             }
   3636         }
   3637     }
   3638 
   3639     public void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) {
   3640         synchronized(mRCStack) {
   3641             // NOTE: Only one IRemoteControlDisplay supported in this implementation
   3642             mArtworkExpectedWidth = w;
   3643             mArtworkExpectedHeight = h;
   3644         }
   3645     }
   3646 
   3647     @Override
   3648     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   3649         // TODO probably a lot more to do here than just the audio focus and remote control stacks
   3650         dumpFocusStack(pw);
   3651         dumpRCStack(pw);
   3652     }
   3653 
   3654 
   3655 }
   3656