Home | History | Annotate | Download | only in media
      1 /*
      2  * Copyright (C) 2006 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.media;
     18 
     19 import static android.Manifest.permission.REMOTE_AUDIO_PLAYBACK;
     20 import static android.media.AudioManager.RINGER_MODE_NORMAL;
     21 import static android.media.AudioManager.RINGER_MODE_SILENT;
     22 import static android.media.AudioManager.RINGER_MODE_VIBRATE;
     23 
     24 import android.app.Activity;
     25 import android.app.ActivityManagerNative;
     26 import android.app.KeyguardManager;
     27 import android.app.PendingIntent;
     28 import android.app.PendingIntent.CanceledException;
     29 import android.app.PendingIntent.OnFinished;
     30 import android.bluetooth.BluetoothA2dp;
     31 import android.bluetooth.BluetoothAdapter;
     32 import android.bluetooth.BluetoothClass;
     33 import android.bluetooth.BluetoothDevice;
     34 import android.bluetooth.BluetoothHeadset;
     35 import android.bluetooth.BluetoothProfile;
     36 import android.content.ActivityNotFoundException;
     37 import android.content.BroadcastReceiver;
     38 import android.content.ComponentName;
     39 import android.content.ContentResolver;
     40 import android.content.Context;
     41 import android.content.Intent;
     42 import android.content.IntentFilter;
     43 import android.content.pm.PackageManager;
     44 import android.content.res.Configuration;
     45 import android.content.res.Resources;
     46 import android.content.res.XmlResourceParser;
     47 import android.database.ContentObserver;
     48 import android.media.MediaPlayer.OnCompletionListener;
     49 import android.media.MediaPlayer.OnErrorListener;
     50 import android.os.Binder;
     51 import android.os.Build;
     52 import android.os.Bundle;
     53 import android.os.Environment;
     54 import android.os.Handler;
     55 import android.os.IBinder;
     56 import android.os.Looper;
     57 import android.os.Message;
     58 import android.os.PowerManager;
     59 import android.os.RemoteCallbackList;
     60 import android.os.RemoteException;
     61 import android.os.ServiceManager;
     62 import android.os.SystemProperties;
     63 import android.os.UserHandle;
     64 import android.os.Vibrator;
     65 import android.provider.Settings;
     66 import android.provider.Settings.System;
     67 import android.speech.RecognizerIntent;
     68 import android.telephony.PhoneStateListener;
     69 import android.telephony.TelephonyManager;
     70 import android.text.TextUtils;
     71 import android.util.Log;
     72 import android.view.KeyEvent;
     73 import android.view.Surface;
     74 import android.view.VolumePanel;
     75 import android.view.WindowManager;
     76 
     77 import com.android.internal.telephony.ITelephony;
     78 import com.android.internal.util.XmlUtils;
     79 
     80 import org.xmlpull.v1.XmlPullParserException;
     81 
     82 import java.io.FileDescriptor;
     83 import java.io.IOException;
     84 import java.io.PrintWriter;
     85 import java.lang.reflect.Field;
     86 import java.util.ArrayList;
     87 import java.util.concurrent.ConcurrentHashMap;
     88 import java.util.HashMap;
     89 import java.util.Iterator;
     90 import java.util.List;
     91 import java.util.Map;
     92 import java.util.NoSuchElementException;
     93 import java.util.Set;
     94 import java.util.Stack;
     95 
     96 /**
     97  * The implementation of the volume manager service.
     98  * <p>
     99  * This implementation focuses on delivering a responsive UI. Most methods are
    100  * asynchronous to external calls. For example, the task of setting a volume
    101  * will update our internal state, but in a separate thread will set the system
    102  * volume and later persist to the database. Similarly, setting the ringer mode
    103  * will update the state and broadcast a change and in a separate thread later
    104  * persist the ringer mode.
    105  *
    106  * @hide
    107  */
    108 public class AudioService extends IAudioService.Stub implements OnFinished {
    109 
    110     private static final String TAG = "AudioService";
    111 
    112     /** Debug remote control client/display feature */
    113     protected static final boolean DEBUG_RC = false;
    114     /** Debug volumes */
    115     protected static final boolean DEBUG_VOL = false;
    116 
    117     /** How long to delay before persisting a change in volume/ringer mode. */
    118     private static final int PERSIST_DELAY = 500;
    119 
    120     private Context mContext;
    121     private ContentResolver mContentResolver;
    122     private boolean mVoiceCapable;
    123 
    124     /** The UI */
    125     private VolumePanel mVolumePanel;
    126 
    127     // sendMsg() flags
    128     /** If the msg is already queued, replace it with this one. */
    129     private static final int SENDMSG_REPLACE = 0;
    130     /** If the msg is already queued, ignore this one and leave the old. */
    131     private static final int SENDMSG_NOOP = 1;
    132     /** If the msg is already queued, queue this one and leave the old. */
    133     private static final int SENDMSG_QUEUE = 2;
    134 
    135     // AudioHandler messages
    136     private static final int MSG_SET_DEVICE_VOLUME = 0;
    137     private static final int MSG_PERSIST_VOLUME = 1;
    138     private static final int MSG_PERSIST_MASTER_VOLUME = 2;
    139     private static final int MSG_PERSIST_RINGER_MODE = 3;
    140     private static final int MSG_MEDIA_SERVER_DIED = 4;
    141     private static final int MSG_MEDIA_SERVER_STARTED = 5;
    142     private static final int MSG_PLAY_SOUND_EFFECT = 6;
    143     private static final int MSG_BTA2DP_DOCK_TIMEOUT = 7;
    144     private static final int MSG_LOAD_SOUND_EFFECTS = 8;
    145     private static final int MSG_SET_FORCE_USE = 9;
    146     private static final int MSG_PERSIST_MEDIABUTTONRECEIVER = 10;
    147     private static final int MSG_BT_HEADSET_CNCT_FAILED = 11;
    148     private static final int MSG_RCDISPLAY_CLEAR = 12;
    149     private static final int MSG_RCDISPLAY_UPDATE = 13;
    150     private static final int MSG_SET_ALL_VOLUMES = 14;
    151     private static final int MSG_PERSIST_MASTER_VOLUME_MUTE = 15;
    152     private static final int MSG_REPORT_NEW_ROUTES = 16;
    153     private static final int MSG_REEVALUATE_REMOTE = 17;
    154     private static final int MSG_RCC_NEW_PLAYBACK_INFO = 18;
    155     private static final int MSG_RCC_NEW_VOLUME_OBS = 19;
    156     private static final int MSG_SET_FORCE_BT_A2DP_USE = 20;
    157     // start of messages handled under wakelock
    158     //   these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
    159     //   and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
    160     private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 21;
    161     private static final int MSG_SET_A2DP_CONNECTION_STATE = 22;
    162     // end of messages handled under wakelock
    163     private static final int MSG_SET_RSX_CONNECTION_STATE = 23; // change remote submix connection
    164     private static final int MSG_CHECK_MUSIC_ACTIVE = 24;
    165     private static final int MSG_BROADCAST_AUDIO_BECOMING_NOISY = 25;
    166     private static final int MSG_CONFIGURE_SAFE_MEDIA_VOLUME = 26;
    167     private static final int MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED = 27;
    168     private static final int MSG_PERSIST_SAFE_VOLUME_STATE = 28;
    169     private static final int MSG_PROMOTE_RCC = 29;
    170     private static final int MSG_BROADCAST_BT_CONNECTION_STATE = 30;
    171     private static final int MSG_UNLOAD_SOUND_EFFECTS = 31;
    172     private static final int MSG_RCC_NEW_PLAYBACK_STATE = 32;
    173     private static final int MSG_RCC_SEEK_REQUEST = 33;
    174 
    175     private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
    176     // Timeout for connection to bluetooth headset service
    177     private static final int BT_HEADSET_CNCT_TIMEOUT_MS = 3000;
    178 
    179     /** @see AudioSystemThread */
    180     private AudioSystemThread mAudioSystemThread;
    181     /** @see AudioHandler */
    182     private AudioHandler mAudioHandler;
    183     /** @see VolumeStreamState */
    184     private VolumeStreamState[] mStreamStates;
    185     private SettingsObserver mSettingsObserver;
    186 
    187     private int mMode;
    188     // protects mRingerMode
    189     private final Object mSettingsLock = new Object();
    190 
    191     private boolean mMediaServerOk;
    192 
    193     private SoundPool mSoundPool;
    194     private final Object mSoundEffectsLock = new Object();
    195     private static final int NUM_SOUNDPOOL_CHANNELS = 4;
    196 
    197     // Internally master volume is a float in the 0.0 - 1.0 range,
    198     // but to support integer based AudioManager API we translate it to 0 - 100
    199     private static final int MAX_MASTER_VOLUME = 100;
    200 
    201     // Maximum volume adjust steps allowed in a single batch call.
    202     private static final int MAX_BATCH_VOLUME_ADJUST_STEPS = 4;
    203 
    204     /* Sound effect file names  */
    205     private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/";
    206     private static final List<String> SOUND_EFFECT_FILES = new ArrayList<String>();
    207 
    208     /* Sound effect file name mapping sound effect id (AudioManager.FX_xxx) to
    209      * file index in SOUND_EFFECT_FILES[] (first column) and indicating if effect
    210      * uses soundpool (second column) */
    211     private final int[][] SOUND_EFFECT_FILES_MAP = new int[AudioManager.NUM_SOUND_EFFECTS][2];
    212 
    213    /** @hide Maximum volume index values for audio streams */
    214     private final int[] MAX_STREAM_VOLUME = new int[] {
    215         5,  // STREAM_VOICE_CALL
    216         7,  // STREAM_SYSTEM
    217         7,  // STREAM_RING
    218         15, // STREAM_MUSIC
    219         7,  // STREAM_ALARM
    220         7,  // STREAM_NOTIFICATION
    221         15, // STREAM_BLUETOOTH_SCO
    222         7,  // STREAM_SYSTEM_ENFORCED
    223         15, // STREAM_DTMF
    224         15  // STREAM_TTS
    225     };
    226     /* mStreamVolumeAlias[] indicates for each stream if it uses the volume settings
    227      * of another stream: This avoids multiplying the volume settings for hidden
    228      * stream types that follow other stream behavior for volume settings
    229      * NOTE: do not create loops in aliases!
    230      * Some streams alias to different streams according to device category (phone or tablet) or
    231      * use case (in call s off call...).See updateStreamVolumeAlias() for more details
    232      *  mStreamVolumeAlias contains the default aliases for a voice capable device (phone) and
    233      *  STREAM_VOLUME_ALIAS_NON_VOICE for a non voice capable device (tablet).*/
    234     private final int[] STREAM_VOLUME_ALIAS = new int[] {
    235         AudioSystem.STREAM_VOICE_CALL,      // STREAM_VOICE_CALL
    236         AudioSystem.STREAM_RING,            // STREAM_SYSTEM
    237         AudioSystem.STREAM_RING,            // STREAM_RING
    238         AudioSystem.STREAM_MUSIC,           // STREAM_MUSIC
    239         AudioSystem.STREAM_ALARM,           // STREAM_ALARM
    240         AudioSystem.STREAM_RING,            // STREAM_NOTIFICATION
    241         AudioSystem.STREAM_BLUETOOTH_SCO,   // STREAM_BLUETOOTH_SCO
    242         AudioSystem.STREAM_RING,            // STREAM_SYSTEM_ENFORCED
    243         AudioSystem.STREAM_RING,            // STREAM_DTMF
    244         AudioSystem.STREAM_MUSIC            // STREAM_TTS
    245     };
    246     private final int[] STREAM_VOLUME_ALIAS_NON_VOICE = new int[] {
    247         AudioSystem.STREAM_VOICE_CALL,      // STREAM_VOICE_CALL
    248         AudioSystem.STREAM_MUSIC,           // STREAM_SYSTEM
    249         AudioSystem.STREAM_RING,            // STREAM_RING
    250         AudioSystem.STREAM_MUSIC,           // STREAM_MUSIC
    251         AudioSystem.STREAM_ALARM,           // STREAM_ALARM
    252         AudioSystem.STREAM_RING,            // STREAM_NOTIFICATION
    253         AudioSystem.STREAM_BLUETOOTH_SCO,   // STREAM_BLUETOOTH_SCO
    254         AudioSystem.STREAM_MUSIC,           // STREAM_SYSTEM_ENFORCED
    255         AudioSystem.STREAM_MUSIC,           // STREAM_DTMF
    256         AudioSystem.STREAM_MUSIC            // STREAM_TTS
    257     };
    258     private int[] mStreamVolumeAlias;
    259 
    260     private final boolean mUseFixedVolume;
    261 
    262     // stream names used by dumpStreamStates()
    263     private final String[] STREAM_NAMES = new String[] {
    264             "STREAM_VOICE_CALL",
    265             "STREAM_SYSTEM",
    266             "STREAM_RING",
    267             "STREAM_MUSIC",
    268             "STREAM_ALARM",
    269             "STREAM_NOTIFICATION",
    270             "STREAM_BLUETOOTH_SCO",
    271             "STREAM_SYSTEM_ENFORCED",
    272             "STREAM_DTMF",
    273             "STREAM_TTS"
    274     };
    275 
    276     private final AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() {
    277         public void onError(int error) {
    278             switch (error) {
    279             case AudioSystem.AUDIO_STATUS_SERVER_DIED:
    280                 if (mMediaServerOk) {
    281                     sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SENDMSG_NOOP, 0, 0,
    282                             null, 1500);
    283                     mMediaServerOk = false;
    284                 }
    285                 break;
    286             case AudioSystem.AUDIO_STATUS_OK:
    287                 if (!mMediaServerOk) {
    288                     sendMsg(mAudioHandler, MSG_MEDIA_SERVER_STARTED, SENDMSG_NOOP, 0, 0,
    289                             null, 0);
    290                     mMediaServerOk = true;
    291                 }
    292                 break;
    293             default:
    294                 break;
    295             }
    296        }
    297     };
    298 
    299     /**
    300      * Current ringer mode from one of {@link AudioManager#RINGER_MODE_NORMAL},
    301      * {@link AudioManager#RINGER_MODE_SILENT}, or
    302      * {@link AudioManager#RINGER_MODE_VIBRATE}.
    303      */
    304     // protected by mSettingsLock
    305     private int mRingerMode;
    306 
    307     /** @see System#MODE_RINGER_STREAMS_AFFECTED */
    308     private int mRingerModeAffectedStreams;
    309 
    310     // Streams currently muted by ringer mode
    311     private int mRingerModeMutedStreams;
    312 
    313     /** @see System#MUTE_STREAMS_AFFECTED */
    314     private int mMuteAffectedStreams;
    315 
    316     /**
    317      * NOTE: setVibrateSetting(), getVibrateSetting(), shouldVibrate() are deprecated.
    318      * mVibrateSetting is just maintained during deprecation period but vibration policy is
    319      * now only controlled by mHasVibrator and mRingerMode
    320      */
    321     private int mVibrateSetting;
    322 
    323     // Is there a vibrator
    324     private final boolean mHasVibrator;
    325 
    326     // Broadcast receiver for device connections intent broadcasts
    327     private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
    328 
    329     // Used to alter media button redirection when the phone is ringing.
    330     private boolean mIsRinging = false;
    331 
    332     // Devices currently connected
    333     private final HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>();
    334 
    335     // Forced device usage for communications
    336     private int mForcedUseForComm;
    337 
    338     // True if we have master volume support
    339     private final boolean mUseMasterVolume;
    340 
    341     private final int[] mMasterVolumeRamp;
    342 
    343     // List of binder death handlers for setMode() client processes.
    344     // The last process to have called setMode() is at the top of the list.
    345     private final ArrayList <SetModeDeathHandler> mSetModeDeathHandlers = new ArrayList <SetModeDeathHandler>();
    346 
    347     // List of clients having issued a SCO start request
    348     private final ArrayList <ScoClient> mScoClients = new ArrayList <ScoClient>();
    349 
    350     // BluetoothHeadset API to control SCO connection
    351     private BluetoothHeadset mBluetoothHeadset;
    352 
    353     // Bluetooth headset device
    354     private BluetoothDevice mBluetoothHeadsetDevice;
    355 
    356     // Indicate if SCO audio connection is currently active and if the initiator is
    357     // audio service (internal) or bluetooth headset (external)
    358     private int mScoAudioState;
    359     // SCO audio state is not active
    360     private static final int SCO_STATE_INACTIVE = 0;
    361     // SCO audio activation request waiting for headset service to connect
    362     private static final int SCO_STATE_ACTIVATE_REQ = 1;
    363     // SCO audio state is active or starting due to a request from AudioManager API
    364     private static final int SCO_STATE_ACTIVE_INTERNAL = 3;
    365     // SCO audio deactivation request waiting for headset service to connect
    366     private static final int SCO_STATE_DEACTIVATE_REQ = 5;
    367 
    368     // SCO audio state is active due to an action in BT handsfree (either voice recognition or
    369     // in call audio)
    370     private static final int SCO_STATE_ACTIVE_EXTERNAL = 2;
    371     // Deactivation request for all SCO connections (initiated by audio mode change)
    372     // waiting for headset service to connect
    373     private static final int SCO_STATE_DEACTIVATE_EXT_REQ = 4;
    374 
    375     // Indicates the mode used for SCO audio connection. The mode is virtual call if the request
    376     // originated from an app targeting an API version before JB MR2 and raw audio after that.
    377     private int mScoAudioMode;
    378     // SCO audio mode is virtual voice call (BluetoothHeadset.startScoUsingVirtualVoiceCall())
    379     private static final int SCO_MODE_VIRTUAL_CALL = 0;
    380     // SCO audio mode is raw audio (BluetoothHeadset.connectAudio())
    381     private static final int SCO_MODE_RAW = 1;
    382 
    383     // Current connection state indicated by bluetooth headset
    384     private int mScoConnectionState;
    385 
    386     // true if boot sequence has been completed
    387     private boolean mBootCompleted;
    388     // listener for SoundPool sample load completion indication
    389     private SoundPoolCallback mSoundPoolCallBack;
    390     // thread for SoundPool listener
    391     private SoundPoolListenerThread mSoundPoolListenerThread;
    392     // message looper for SoundPool listener
    393     private Looper mSoundPoolLooper = null;
    394     // volume applied to sound played with playSoundEffect()
    395     private static int sSoundEffectVolumeDb;
    396     // getActiveStreamType() will return:
    397     // - STREAM_NOTIFICATION on tablets during this period after a notification stopped
    398     // - STREAM_MUSIC on phones during this period after music or talkback/voice search prompt
    399     // stopped
    400     private static final int DEFAULT_STREAM_TYPE_OVERRIDE_DELAY_MS = 5000;
    401     // previous volume adjustment direction received by checkForRingerModeChange()
    402     private int mPrevVolDirection = AudioManager.ADJUST_SAME;
    403     // Keyguard manager proxy
    404     private KeyguardManager mKeyguardManager;
    405     // mVolumeControlStream is set by VolumePanel to temporarily force the stream type which volume
    406     // is controlled by Vol keys.
    407     private int  mVolumeControlStream = -1;
    408     private final Object mForceControlStreamLock = new Object();
    409     // VolumePanel is currently the only client of forceVolumeControlStream() and runs in system
    410     // server process so in theory it is not necessary to monitor the client death.
    411     // However it is good to be ready for future evolutions.
    412     private ForceControlStreamClient mForceControlStreamClient = null;
    413     // Used to play ringtones outside system_server
    414     private volatile IRingtonePlayer mRingtonePlayer;
    415 
    416     private int mDeviceOrientation = Configuration.ORIENTATION_UNDEFINED;
    417     private int mDeviceRotation = Surface.ROTATION_0;
    418 
    419     // Request to override default use of A2DP for media.
    420     private boolean mBluetoothA2dpEnabled;
    421     private final Object mBluetoothA2dpEnabledLock = new Object();
    422 
    423     // Monitoring of audio routes.  Protected by mCurAudioRoutes.
    424     final AudioRoutesInfo mCurAudioRoutes = new AudioRoutesInfo();
    425     final RemoteCallbackList<IAudioRoutesObserver> mRoutesObservers
    426             = new RemoteCallbackList<IAudioRoutesObserver>();
    427 
    428     /**
    429      * A fake stream type to match the notion of remote media playback
    430      */
    431     public final static int STREAM_REMOTE_MUSIC = -200;
    432 
    433     // Devices for which the volume is fixed and VolumePanel slider should be disabled
    434     final int mFixedVolumeDevices = AudioSystem.DEVICE_OUT_AUX_DIGITAL |
    435             AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET |
    436             AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET |
    437             AudioSystem.DEVICE_OUT_ALL_USB;
    438 
    439     // TODO merge orientation and rotation
    440     private final boolean mMonitorOrientation;
    441     private final boolean mMonitorRotation;
    442 
    443     private boolean mDockAudioMediaEnabled = true;
    444 
    445     private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
    446 
    447     // Used when safe volume warning message display is requested by setStreamVolume(). In this
    448     // case, the new requested volume, stream type and device are stored in mPendingVolumeCommand
    449     // and used later when/if disableSafeMediaVolume() is called.
    450     private StreamVolumeCommand mPendingVolumeCommand;
    451 
    452     ///////////////////////////////////////////////////////////////////////////
    453     // Construction
    454     ///////////////////////////////////////////////////////////////////////////
    455 
    456     /** @hide */
    457     public AudioService(Context context) {
    458         mContext = context;
    459         mContentResolver = context.getContentResolver();
    460         mVoiceCapable = mContext.getResources().getBoolean(
    461                 com.android.internal.R.bool.config_voice_capable);
    462 
    463         PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
    464         mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
    465 
    466         Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
    467         mHasVibrator = vibrator == null ? false : vibrator.hasVibrator();
    468 
    469        // Intialized volume
    470         MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = SystemProperties.getInt(
    471             "ro.config.vc_call_vol_steps",
    472            MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL]);
    473 
    474         sSoundEffectVolumeDb = context.getResources().getInteger(
    475                 com.android.internal.R.integer.config_soundEffectVolumeDb);
    476 
    477         mVolumePanel = new VolumePanel(context, this);
    478         mMode = AudioSystem.MODE_NORMAL;
    479         mForcedUseForComm = AudioSystem.FORCE_NONE;
    480 
    481         createAudioSystemThread();
    482 
    483         boolean cameraSoundForced = mContext.getResources().getBoolean(
    484                 com.android.internal.R.bool.config_camera_sound_forced);
    485         mCameraSoundForced = new Boolean(cameraSoundForced);
    486         sendMsg(mAudioHandler,
    487                 MSG_SET_FORCE_USE,
    488                 SENDMSG_QUEUE,
    489                 AudioSystem.FOR_SYSTEM,
    490                 cameraSoundForced ?
    491                         AudioSystem.FORCE_SYSTEM_ENFORCED : AudioSystem.FORCE_NONE,
    492                 null,
    493                 0);
    494 
    495         mSafeMediaVolumeState = new Integer(Settings.Global.getInt(mContentResolver,
    496                                                         Settings.Global.AUDIO_SAFE_VOLUME_STATE,
    497                                                         SAFE_MEDIA_VOLUME_NOT_CONFIGURED));
    498         // The default safe volume index read here will be replaced by the actual value when
    499         // the mcc is read by onConfigureSafeVolume()
    500         mSafeMediaVolumeIndex = mContext.getResources().getInteger(
    501                 com.android.internal.R.integer.config_safe_media_volume_index) * 10;
    502 
    503         mUseFixedVolume = mContext.getResources().getBoolean(
    504                 com.android.internal.R.bool.config_useFixedVolume);
    505 
    506         readPersistedSettings();
    507         mSettingsObserver = new SettingsObserver();
    508         updateStreamVolumeAlias(false /*updateVolumes*/);
    509         createStreamStates();
    510 
    511         mMediaServerOk = true;
    512 
    513         // Call setRingerModeInt() to apply correct mute
    514         // state on streams affected by ringer mode.
    515         mRingerModeMutedStreams = 0;
    516         setRingerModeInt(getRingerMode(), false);
    517 
    518         AudioSystem.setErrorCallback(mAudioSystemCallback);
    519 
    520         // Register for device connection intent broadcasts.
    521         IntentFilter intentFilter =
    522                 new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
    523         intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
    524         intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
    525         intentFilter.addAction(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG);
    526         intentFilter.addAction(Intent.ACTION_USB_AUDIO_DEVICE_PLUG);
    527         intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
    528         intentFilter.addAction(Intent.ACTION_SCREEN_ON);
    529         intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
    530         intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
    531 
    532         intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
    533         // TODO merge orientation and rotation
    534         mMonitorOrientation = SystemProperties.getBoolean("ro.audio.monitorOrientation", false);
    535         if (mMonitorOrientation) {
    536             Log.v(TAG, "monitoring device orientation");
    537             // initialize orientation in AudioSystem
    538             setOrientationForAudioSystem();
    539         }
    540         mMonitorRotation = SystemProperties.getBoolean("ro.audio.monitorRotation", false);
    541         if (mMonitorRotation) {
    542             mDeviceRotation = ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE))
    543                     .getDefaultDisplay().getRotation();
    544             Log.v(TAG, "monitoring device rotation, initial=" + mDeviceRotation);
    545             // initialize rotation in AudioSystem
    546             setRotationForAudioSystem();
    547         }
    548 
    549         context.registerReceiver(mReceiver, intentFilter);
    550 
    551         // Register for package removal intent broadcasts for media button receiver persistence
    552         IntentFilter pkgFilter = new IntentFilter();
    553         pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
    554         pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
    555         pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
    556         pkgFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
    557         pkgFilter.addDataScheme("package");
    558         context.registerReceiver(mReceiver, pkgFilter);
    559 
    560         // Register for phone state monitoring
    561         TelephonyManager tmgr = (TelephonyManager)
    562                 context.getSystemService(Context.TELEPHONY_SERVICE);
    563         tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
    564 
    565         mUseMasterVolume = context.getResources().getBoolean(
    566                 com.android.internal.R.bool.config_useMasterVolume);
    567         restoreMasterVolume();
    568 
    569         mMasterVolumeRamp = context.getResources().getIntArray(
    570                 com.android.internal.R.array.config_masterVolumeRamp);
    571 
    572         mMainRemote = new RemotePlaybackState(-1, MAX_STREAM_VOLUME[AudioManager.STREAM_MUSIC],
    573                 MAX_STREAM_VOLUME[AudioManager.STREAM_MUSIC]);
    574         mHasRemotePlayback = false;
    575         mMainRemoteIsActive = false;
    576         postReevaluateRemote();
    577     }
    578 
    579     private void createAudioSystemThread() {
    580         mAudioSystemThread = new AudioSystemThread();
    581         mAudioSystemThread.start();
    582         waitForAudioHandlerCreation();
    583     }
    584 
    585     /** Waits for the volume handler to be created by the other thread. */
    586     private void waitForAudioHandlerCreation() {
    587         synchronized(this) {
    588             while (mAudioHandler == null) {
    589                 try {
    590                     // Wait for mAudioHandler to be set by the other thread
    591                     wait();
    592                 } catch (InterruptedException e) {
    593                     Log.e(TAG, "Interrupted while waiting on volume handler.");
    594                 }
    595             }
    596         }
    597     }
    598 
    599     private void checkAllAliasStreamVolumes() {
    600         int numStreamTypes = AudioSystem.getNumStreamTypes();
    601         for (int streamType = 0; streamType < numStreamTypes; streamType++) {
    602             if (streamType != mStreamVolumeAlias[streamType]) {
    603                 mStreamStates[streamType].
    604                                     setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]]);
    605             }
    606             // apply stream volume
    607             if (!mStreamStates[streamType].isMuted()) {
    608                 mStreamStates[streamType].applyAllVolumes();
    609             }
    610         }
    611     }
    612 
    613     private void createStreamStates() {
    614         int numStreamTypes = AudioSystem.getNumStreamTypes();
    615         VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
    616 
    617         for (int i = 0; i < numStreamTypes; i++) {
    618             streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[mStreamVolumeAlias[i]], i);
    619         }
    620 
    621         checkAllAliasStreamVolumes();
    622     }
    623 
    624     private void dumpStreamStates(PrintWriter pw) {
    625         pw.println("\nStream volumes (device: index)");
    626         int numStreamTypes = AudioSystem.getNumStreamTypes();
    627         for (int i = 0; i < numStreamTypes; i++) {
    628             pw.println("- "+STREAM_NAMES[i]+":");
    629             mStreamStates[i].dump(pw);
    630             pw.println("");
    631         }
    632         pw.print("\n- mute affected streams = 0x");
    633         pw.println(Integer.toHexString(mMuteAffectedStreams));
    634     }
    635 
    636 
    637     private void updateStreamVolumeAlias(boolean updateVolumes) {
    638         int dtmfStreamAlias;
    639         if (mVoiceCapable) {
    640             mStreamVolumeAlias = STREAM_VOLUME_ALIAS;
    641             dtmfStreamAlias = AudioSystem.STREAM_RING;
    642         } else {
    643             mStreamVolumeAlias = STREAM_VOLUME_ALIAS_NON_VOICE;
    644             dtmfStreamAlias = AudioSystem.STREAM_MUSIC;
    645         }
    646         if (isInCommunication()) {
    647             dtmfStreamAlias = AudioSystem.STREAM_VOICE_CALL;
    648         }
    649         mStreamVolumeAlias[AudioSystem.STREAM_DTMF] = dtmfStreamAlias;
    650         if (updateVolumes) {
    651             mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias]);
    652             sendMsg(mAudioHandler,
    653                     MSG_SET_ALL_VOLUMES,
    654                     SENDMSG_QUEUE,
    655                     0,
    656                     0,
    657                     mStreamStates[AudioSystem.STREAM_DTMF], 0);
    658         }
    659     }
    660 
    661     private void readDockAudioSettings(ContentResolver cr)
    662     {
    663         mDockAudioMediaEnabled = Settings.Global.getInt(
    664                                         cr, Settings.Global.DOCK_AUDIO_MEDIA_ENABLED, 0) == 1;
    665 
    666         if (mDockAudioMediaEnabled) {
    667             mBecomingNoisyIntentDevices |= AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET;
    668         } else {
    669             mBecomingNoisyIntentDevices &= ~AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET;
    670         }
    671 
    672         sendMsg(mAudioHandler,
    673                 MSG_SET_FORCE_USE,
    674                 SENDMSG_QUEUE,
    675                 AudioSystem.FOR_DOCK,
    676                 mDockAudioMediaEnabled ?
    677                         AudioSystem.FORCE_ANALOG_DOCK : AudioSystem.FORCE_NONE,
    678                 null,
    679                 0);
    680     }
    681 
    682     private void readPersistedSettings() {
    683         final ContentResolver cr = mContentResolver;
    684 
    685         int ringerModeFromSettings =
    686                 Settings.Global.getInt(
    687                         cr, Settings.Global.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);
    688         int ringerMode = ringerModeFromSettings;
    689         // sanity check in case the settings are restored from a device with incompatible
    690         // ringer modes
    691         if (!AudioManager.isValidRingerMode(ringerMode)) {
    692             ringerMode = AudioManager.RINGER_MODE_NORMAL;
    693         }
    694         if ((ringerMode == AudioManager.RINGER_MODE_VIBRATE) && !mHasVibrator) {
    695             ringerMode = AudioManager.RINGER_MODE_SILENT;
    696         }
    697         if (ringerMode != ringerModeFromSettings) {
    698             Settings.Global.putInt(cr, Settings.Global.MODE_RINGER, ringerMode);
    699         }
    700         if (mUseFixedVolume) {
    701             ringerMode = AudioManager.RINGER_MODE_NORMAL;
    702         }
    703         synchronized(mSettingsLock) {
    704             mRingerMode = ringerMode;
    705 
    706             // System.VIBRATE_ON is not used any more but defaults for mVibrateSetting
    707             // are still needed while setVibrateSetting() and getVibrateSetting() are being
    708             // deprecated.
    709             mVibrateSetting = getValueForVibrateSetting(0,
    710                                             AudioManager.VIBRATE_TYPE_NOTIFICATION,
    711                                             mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT
    712                                                             : AudioManager.VIBRATE_SETTING_OFF);
    713             mVibrateSetting = getValueForVibrateSetting(mVibrateSetting,
    714                                             AudioManager.VIBRATE_TYPE_RINGER,
    715                                             mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT
    716                                                             : AudioManager.VIBRATE_SETTING_OFF);
    717 
    718             // make sure settings for ringer mode are consistent with device type: non voice capable
    719             // devices (tablets) include media stream in silent mode whereas phones don't.
    720             mRingerModeAffectedStreams = Settings.System.getIntForUser(cr,
    721                     Settings.System.MODE_RINGER_STREAMS_AFFECTED,
    722                     ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
    723                      (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)),
    724                      UserHandle.USER_CURRENT);
    725 
    726             // ringtone, notification and system streams are always affected by ringer mode
    727             mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_RING)|
    728                                             (1 << AudioSystem.STREAM_NOTIFICATION)|
    729                                             (1 << AudioSystem.STREAM_SYSTEM);
    730 
    731             if (mVoiceCapable) {
    732                 mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
    733             } else {
    734                 mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
    735             }
    736             synchronized (mCameraSoundForced) {
    737                 if (mCameraSoundForced) {
    738                     mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
    739                 } else {
    740                     mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
    741                 }
    742             }
    743 
    744             Settings.System.putIntForUser(cr,
    745                     Settings.System.MODE_RINGER_STREAMS_AFFECTED,
    746                     mRingerModeAffectedStreams,
    747                     UserHandle.USER_CURRENT);
    748 
    749             readDockAudioSettings(cr);
    750         }
    751 
    752         mMuteAffectedStreams = System.getIntForUser(cr,
    753                 System.MUTE_STREAMS_AFFECTED,
    754                 ((1 << AudioSystem.STREAM_MUSIC)|
    755                  (1 << AudioSystem.STREAM_RING)|
    756                  (1 << AudioSystem.STREAM_SYSTEM)),
    757                  UserHandle.USER_CURRENT);
    758 
    759         boolean masterMute = System.getIntForUser(cr, System.VOLUME_MASTER_MUTE,
    760                                                   0, UserHandle.USER_CURRENT) == 1;
    761         if (mUseFixedVolume) {
    762             masterMute = false;
    763             AudioSystem.setMasterVolume(1.0f);
    764         }
    765         AudioSystem.setMasterMute(masterMute);
    766         broadcastMasterMuteStatus(masterMute);
    767 
    768         // Each stream will read its own persisted settings
    769 
    770         // Broadcast the sticky intent
    771         broadcastRingerMode(ringerMode);
    772 
    773         // Broadcast vibrate settings
    774         broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
    775         broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION);
    776 
    777         // Restore the default media button receiver from the system settings
    778         restoreMediaButtonReceiver();
    779     }
    780 
    781     private int rescaleIndex(int index, int srcStream, int dstStream) {
    782         return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex();
    783     }
    784 
    785     ///////////////////////////////////////////////////////////////////////////
    786     // IPC methods
    787     ///////////////////////////////////////////////////////////////////////////
    788 
    789     /** @see AudioManager#adjustVolume(int, int) */
    790     public void adjustVolume(int direction, int flags) {
    791         adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags);
    792     }
    793 
    794     /** @see AudioManager#adjustLocalOrRemoteStreamVolume(int, int) with current assumption
    795      *  on streamType: fixed to STREAM_MUSIC */
    796     public void adjustLocalOrRemoteStreamVolume(int streamType, int direction) {
    797         if (DEBUG_VOL) Log.d(TAG, "adjustLocalOrRemoteStreamVolume(dir="+direction+")");
    798         if (checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
    799             adjustRemoteVolume(AudioSystem.STREAM_MUSIC, direction, 0);
    800         } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
    801             adjustStreamVolume(AudioSystem.STREAM_MUSIC, direction, 0);
    802         }
    803     }
    804 
    805     /** @see AudioManager#adjustVolume(int, int) */
    806     public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
    807         if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream="+suggestedStreamType);
    808         int streamType;
    809         if (mVolumeControlStream != -1) {
    810             streamType = mVolumeControlStream;
    811         } else {
    812             streamType = getActiveStreamType(suggestedStreamType);
    813         }
    814 
    815         // Play sounds on STREAM_RING only and if lock screen is not on.
    816         if ((streamType != STREAM_REMOTE_MUSIC) &&
    817                 (flags & AudioManager.FLAG_PLAY_SOUND) != 0 &&
    818                 ((mStreamVolumeAlias[streamType] != AudioSystem.STREAM_RING)
    819                  || (mKeyguardManager != null && mKeyguardManager.isKeyguardLocked()))) {
    820             flags &= ~AudioManager.FLAG_PLAY_SOUND;
    821         }
    822 
    823         if (streamType == STREAM_REMOTE_MUSIC) {
    824             // don't play sounds for remote
    825             flags &= ~(AudioManager.FLAG_PLAY_SOUND|AudioManager.FLAG_FIXED_VOLUME);
    826             //if (DEBUG_VOL) Log.i(TAG, "Need to adjust remote volume: calling adjustRemoteVolume()");
    827             adjustRemoteVolume(AudioSystem.STREAM_MUSIC, direction, flags);
    828         } else {
    829             adjustStreamVolume(streamType, direction, flags);
    830         }
    831     }
    832 
    833     /** @see AudioManager#adjustStreamVolume(int, int, int) */
    834     public void adjustStreamVolume(int streamType, int direction, int flags) {
    835         if (mUseFixedVolume) {
    836             return;
    837         }
    838         if (DEBUG_VOL) Log.d(TAG, "adjustStreamVolume() stream="+streamType+", dir="+direction);
    839 
    840         ensureValidDirection(direction);
    841         ensureValidStreamType(streamType);
    842 
    843         // use stream type alias here so that streams with same alias have the same behavior,
    844         // including with regard to silent mode control (e.g the use of STREAM_RING below and in
    845         // checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION)
    846         int streamTypeAlias = mStreamVolumeAlias[streamType];
    847         VolumeStreamState streamState = mStreamStates[streamTypeAlias];
    848 
    849         final int device = getDeviceForStream(streamTypeAlias);
    850 
    851         int aliasIndex = streamState.getIndex(device);
    852         boolean adjustVolume = true;
    853         int step;
    854 
    855         // reset any pending volume command
    856         synchronized (mSafeMediaVolumeState) {
    857             mPendingVolumeCommand = null;
    858         }
    859 
    860         flags &= ~AudioManager.FLAG_FIXED_VOLUME;
    861         if ((streamTypeAlias == AudioSystem.STREAM_MUSIC) &&
    862                ((device & mFixedVolumeDevices) != 0)) {
    863             flags |= AudioManager.FLAG_FIXED_VOLUME;
    864 
    865             // Always toggle between max safe volume and 0 for fixed volume devices where safe
    866             // volume is enforced, and max and 0 for the others.
    867             // This is simulated by stepping by the full allowed volume range
    868             if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE &&
    869                     (device & mSafeMediaVolumeDevices) != 0) {
    870                 step = mSafeMediaVolumeIndex;
    871             } else {
    872                 step = streamState.getMaxIndex();
    873             }
    874             if (aliasIndex != 0) {
    875                 aliasIndex = step;
    876             }
    877         } else {
    878             // convert one UI step (+/-1) into a number of internal units on the stream alias
    879             step = rescaleIndex(10, streamType, streamTypeAlias);
    880         }
    881 
    882         // If either the client forces allowing ringer modes for this adjustment,
    883         // or the stream type is one that is affected by ringer modes
    884         if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
    885                 (streamTypeAlias == getMasterStreamType())) {
    886             int ringerMode = getRingerMode();
    887             // do not vibrate if already in vibrate mode
    888             if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
    889                 flags &= ~AudioManager.FLAG_VIBRATE;
    890             }
    891             // Check if the ringer mode changes with this volume adjustment. If
    892             // it does, it will handle adjusting the volume, so we won't below
    893             adjustVolume = checkForRingerModeChange(aliasIndex, direction, step);
    894         }
    895 
    896         int oldIndex = mStreamStates[streamType].getIndex(device);
    897 
    898         if (adjustVolume && (direction != AudioManager.ADJUST_SAME)) {
    899             if ((direction == AudioManager.ADJUST_RAISE) &&
    900                     !checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) {
    901                 Log.e(TAG, "adjustStreamVolume() safe volume index = "+oldIndex);
    902                 mVolumePanel.postDisplaySafeVolumeWarning(flags);
    903             } else if (streamState.adjustIndex(direction * step, device)) {
    904                 // Post message to set system volume (it in turn will post a message
    905                 // to persist). Do not change volume if stream is muted.
    906                 sendMsg(mAudioHandler,
    907                         MSG_SET_DEVICE_VOLUME,
    908                         SENDMSG_QUEUE,
    909                         device,
    910                         0,
    911                         streamState,
    912                         0);
    913             }
    914         }
    915         int index = mStreamStates[streamType].getIndex(device);
    916         sendVolumeUpdate(streamType, oldIndex, index, flags);
    917     }
    918 
    919     /** @see AudioManager#adjustMasterVolume(int, int) */
    920     public void adjustMasterVolume(int steps, int flags) {
    921         if (mUseFixedVolume) {
    922             return;
    923         }
    924         ensureValidSteps(steps);
    925         int volume = Math.round(AudioSystem.getMasterVolume() * MAX_MASTER_VOLUME);
    926         int delta = 0;
    927         int numSteps = Math.abs(steps);
    928         int direction = steps > 0 ? AudioManager.ADJUST_RAISE : AudioManager.ADJUST_LOWER;
    929         for (int i = 0; i < numSteps; ++i) {
    930             delta = findVolumeDelta(direction, volume);
    931             volume += delta;
    932         }
    933 
    934         //Log.d(TAG, "adjustMasterVolume volume: " + volume + " steps: " + steps);
    935         setMasterVolume(volume, flags);
    936     }
    937 
    938     // StreamVolumeCommand contains the information needed to defer the process of
    939     // setStreamVolume() in case the user has to acknowledge the safe volume warning message.
    940     class StreamVolumeCommand {
    941         public final int mStreamType;
    942         public final int mIndex;
    943         public final int mFlags;
    944         public final int mDevice;
    945 
    946         StreamVolumeCommand(int streamType, int index, int flags, int device) {
    947             mStreamType = streamType;
    948             mIndex = index;
    949             mFlags = flags;
    950             mDevice = device;
    951         }
    952     };
    953 
    954     private void onSetStreamVolume(int streamType, int index, int flags, int device) {
    955         setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, false);
    956         // setting volume on master stream type also controls silent mode
    957         if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
    958                 (mStreamVolumeAlias[streamType] == getMasterStreamType())) {
    959             int newRingerMode;
    960             if (index == 0) {
    961                 newRingerMode = mHasVibrator ? AudioManager.RINGER_MODE_VIBRATE
    962                                               : AudioManager.RINGER_MODE_SILENT;
    963             } else {
    964                 newRingerMode = AudioManager.RINGER_MODE_NORMAL;
    965             }
    966             setRingerMode(newRingerMode);
    967         }
    968     }
    969 
    970     /** @see AudioManager#setStreamVolume(int, int, int) */
    971     public void setStreamVolume(int streamType, int index, int flags) {
    972         if (mUseFixedVolume) {
    973             return;
    974         }
    975 
    976         ensureValidStreamType(streamType);
    977         VolumeStreamState streamState = mStreamStates[mStreamVolumeAlias[streamType]];
    978 
    979         final int device = getDeviceForStream(streamType);
    980         int oldIndex;
    981 
    982         synchronized (mSafeMediaVolumeState) {
    983             // reset any pending volume command
    984             mPendingVolumeCommand = null;
    985 
    986             oldIndex = streamState.getIndex(device);
    987 
    988             index = rescaleIndex(index * 10, streamType, mStreamVolumeAlias[streamType]);
    989 
    990             flags &= ~AudioManager.FLAG_FIXED_VOLUME;
    991             if ((mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) &&
    992                     ((device & mFixedVolumeDevices) != 0)) {
    993                 flags |= AudioManager.FLAG_FIXED_VOLUME;
    994 
    995                 // volume is either 0 or max allowed for fixed volume devices
    996                 if (index != 0) {
    997                     if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE &&
    998                             (device & mSafeMediaVolumeDevices) != 0) {
    999                         index = mSafeMediaVolumeIndex;
   1000                     } else {
   1001                         index = streamState.getMaxIndex();
   1002                     }
   1003                 }
   1004             }
   1005 
   1006             if (!checkSafeMediaVolume(mStreamVolumeAlias[streamType], index, device)) {
   1007                 mVolumePanel.postDisplaySafeVolumeWarning(flags);
   1008                 mPendingVolumeCommand = new StreamVolumeCommand(
   1009                                                     streamType, index, flags, device);
   1010             } else {
   1011                 onSetStreamVolume(streamType, index, flags, device);
   1012                 index = mStreamStates[streamType].getIndex(device);
   1013             }
   1014         }
   1015         sendVolumeUpdate(streamType, oldIndex, index, flags);
   1016     }
   1017 
   1018     /** @see AudioManager#forceVolumeControlStream(int) */
   1019     public void forceVolumeControlStream(int streamType, IBinder cb) {
   1020         synchronized(mForceControlStreamLock) {
   1021             mVolumeControlStream = streamType;
   1022             if (mVolumeControlStream == -1) {
   1023                 if (mForceControlStreamClient != null) {
   1024                     mForceControlStreamClient.release();
   1025                     mForceControlStreamClient = null;
   1026                 }
   1027             } else {
   1028                 mForceControlStreamClient = new ForceControlStreamClient(cb);
   1029             }
   1030         }
   1031     }
   1032 
   1033     private class ForceControlStreamClient implements IBinder.DeathRecipient {
   1034         private IBinder mCb; // To be notified of client's death
   1035 
   1036         ForceControlStreamClient(IBinder cb) {
   1037             if (cb != null) {
   1038                 try {
   1039                     cb.linkToDeath(this, 0);
   1040                 } catch (RemoteException e) {
   1041                     // Client has died!
   1042                     Log.w(TAG, "ForceControlStreamClient() could not link to "+cb+" binder death");
   1043                     cb = null;
   1044                 }
   1045             }
   1046             mCb = cb;
   1047         }
   1048 
   1049         public void binderDied() {
   1050             synchronized(mForceControlStreamLock) {
   1051                 Log.w(TAG, "SCO client died");
   1052                 if (mForceControlStreamClient != this) {
   1053                     Log.w(TAG, "unregistered control stream client died");
   1054                 } else {
   1055                     mForceControlStreamClient = null;
   1056                     mVolumeControlStream = -1;
   1057                 }
   1058             }
   1059         }
   1060 
   1061         public void release() {
   1062             if (mCb != null) {
   1063                 mCb.unlinkToDeath(this, 0);
   1064                 mCb = null;
   1065             }
   1066         }
   1067     }
   1068 
   1069     private int findVolumeDelta(int direction, int volume) {
   1070         int delta = 0;
   1071         if (direction == AudioManager.ADJUST_RAISE) {
   1072             if (volume == MAX_MASTER_VOLUME) {
   1073                 return 0;
   1074             }
   1075             // This is the default value if we make it to the end
   1076             delta = mMasterVolumeRamp[1];
   1077             // If we're raising the volume move down the ramp array until we
   1078             // find the volume we're above and use that groups delta.
   1079             for (int i = mMasterVolumeRamp.length - 1; i > 1; i -= 2) {
   1080                 if (volume >= mMasterVolumeRamp[i - 1]) {
   1081                     delta = mMasterVolumeRamp[i];
   1082                     break;
   1083                 }
   1084             }
   1085         } else if (direction == AudioManager.ADJUST_LOWER){
   1086             if (volume == 0) {
   1087                 return 0;
   1088             }
   1089             int length = mMasterVolumeRamp.length;
   1090             // This is the default value if we make it to the end
   1091             delta = -mMasterVolumeRamp[length - 1];
   1092             // If we're lowering the volume move up the ramp array until we
   1093             // find the volume we're below and use the group below it's delta
   1094             for (int i = 2; i < length; i += 2) {
   1095                 if (volume <= mMasterVolumeRamp[i]) {
   1096                     delta = -mMasterVolumeRamp[i - 1];
   1097                     break;
   1098                 }
   1099             }
   1100         }
   1101         return delta;
   1102     }
   1103 
   1104     private void sendBroadcastToAll(Intent intent) {
   1105         final long ident = Binder.clearCallingIdentity();
   1106         try {
   1107             mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
   1108         } finally {
   1109             Binder.restoreCallingIdentity(ident);
   1110         }
   1111     }
   1112 
   1113     private void sendStickyBroadcastToAll(Intent intent) {
   1114         final long ident = Binder.clearCallingIdentity();
   1115         try {
   1116             mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
   1117         } finally {
   1118             Binder.restoreCallingIdentity(ident);
   1119         }
   1120     }
   1121 
   1122     // UI update and Broadcast Intent
   1123     private void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags) {
   1124         if (!mVoiceCapable && (streamType == AudioSystem.STREAM_RING)) {
   1125             streamType = AudioSystem.STREAM_NOTIFICATION;
   1126         }
   1127 
   1128         mVolumePanel.postVolumeChanged(streamType, flags);
   1129 
   1130         if ((flags & AudioManager.FLAG_FIXED_VOLUME) == 0) {
   1131             oldIndex = (oldIndex + 5) / 10;
   1132             index = (index + 5) / 10;
   1133             Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
   1134             intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
   1135             intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index);
   1136             intent.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex);
   1137             sendBroadcastToAll(intent);
   1138         }
   1139     }
   1140 
   1141     // UI update and Broadcast Intent
   1142     private void sendMasterVolumeUpdate(int flags, int oldVolume, int newVolume) {
   1143         mVolumePanel.postMasterVolumeChanged(flags);
   1144 
   1145         Intent intent = new Intent(AudioManager.MASTER_VOLUME_CHANGED_ACTION);
   1146         intent.putExtra(AudioManager.EXTRA_PREV_MASTER_VOLUME_VALUE, oldVolume);
   1147         intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_VALUE, newVolume);
   1148         sendBroadcastToAll(intent);
   1149     }
   1150 
   1151     // UI update and Broadcast Intent
   1152     private void sendMasterMuteUpdate(boolean muted, int flags) {
   1153         mVolumePanel.postMasterMuteChanged(flags);
   1154         broadcastMasterMuteStatus(muted);
   1155     }
   1156 
   1157     private void broadcastMasterMuteStatus(boolean muted) {
   1158         Intent intent = new Intent(AudioManager.MASTER_MUTE_CHANGED_ACTION);
   1159         intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_MUTED, muted);
   1160         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
   1161                 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
   1162         sendStickyBroadcastToAll(intent);
   1163     }
   1164 
   1165     /**
   1166      * Sets the stream state's index, and posts a message to set system volume.
   1167      * This will not call out to the UI. Assumes a valid stream type.
   1168      *
   1169      * @param streamType Type of the stream
   1170      * @param index Desired volume index of the stream
   1171      * @param device the device whose volume must be changed
   1172      * @param force If true, set the volume even if the desired volume is same
   1173      * as the current volume.
   1174      */
   1175     private void setStreamVolumeInt(int streamType,
   1176                                     int index,
   1177                                     int device,
   1178                                     boolean force) {
   1179         VolumeStreamState streamState = mStreamStates[streamType];
   1180 
   1181         if (streamState.setIndex(index, device) || force) {
   1182             // Post message to set system volume (it in turn will post a message
   1183             // to persist).
   1184             sendMsg(mAudioHandler,
   1185                     MSG_SET_DEVICE_VOLUME,
   1186                     SENDMSG_QUEUE,
   1187                     device,
   1188                     0,
   1189                     streamState,
   1190                     0);
   1191         }
   1192     }
   1193 
   1194     /** @see AudioManager#setStreamSolo(int, boolean) */
   1195     public void setStreamSolo(int streamType, boolean state, IBinder cb) {
   1196         if (mUseFixedVolume) {
   1197             return;
   1198         }
   1199 
   1200         for (int stream = 0; stream < mStreamStates.length; stream++) {
   1201             if (!isStreamAffectedByMute(stream) || stream == streamType) continue;
   1202             mStreamStates[stream].mute(cb, state);
   1203          }
   1204     }
   1205 
   1206     /** @see AudioManager#setStreamMute(int, boolean) */
   1207     public void setStreamMute(int streamType, boolean state, IBinder cb) {
   1208         if (mUseFixedVolume) {
   1209             return;
   1210         }
   1211 
   1212         if (isStreamAffectedByMute(streamType)) {
   1213             mStreamStates[streamType].mute(cb, state);
   1214         }
   1215     }
   1216 
   1217     /** get stream mute state. */
   1218     public boolean isStreamMute(int streamType) {
   1219         return mStreamStates[streamType].isMuted();
   1220     }
   1221 
   1222     /** @see AudioManager#setMasterMute(boolean, int) */
   1223     public void setMasterMute(boolean state, int flags, IBinder cb) {
   1224         if (mUseFixedVolume) {
   1225             return;
   1226         }
   1227 
   1228         if (state != AudioSystem.getMasterMute()) {
   1229             AudioSystem.setMasterMute(state);
   1230             // Post a persist master volume msg
   1231             sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME_MUTE, SENDMSG_REPLACE, state ? 1
   1232                     : 0, 0, null, PERSIST_DELAY);
   1233             sendMasterMuteUpdate(state, flags);
   1234         }
   1235     }
   1236 
   1237     /** get master mute state. */
   1238     public boolean isMasterMute() {
   1239         return AudioSystem.getMasterMute();
   1240     }
   1241 
   1242     /** @see AudioManager#getStreamVolume(int) */
   1243     public int getStreamVolume(int streamType) {
   1244         ensureValidStreamType(streamType);
   1245         int device = getDeviceForStream(streamType);
   1246         int index = mStreamStates[streamType].getIndex(device);
   1247 
   1248         // by convention getStreamVolume() returns 0 when a stream is muted.
   1249         if (mStreamStates[streamType].isMuted()) {
   1250             index = 0;
   1251         }
   1252         if (index != 0 && (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) &&
   1253                 (device & mFixedVolumeDevices) != 0) {
   1254             index = mStreamStates[streamType].getMaxIndex();
   1255         }
   1256         return (index + 5) / 10;
   1257     }
   1258 
   1259     public int getMasterVolume() {
   1260         if (isMasterMute()) return 0;
   1261         return getLastAudibleMasterVolume();
   1262     }
   1263 
   1264     public void setMasterVolume(int volume, int flags) {
   1265         if (mUseFixedVolume) {
   1266             return;
   1267         }
   1268 
   1269         if (volume < 0) {
   1270             volume = 0;
   1271         } else if (volume > MAX_MASTER_VOLUME) {
   1272             volume = MAX_MASTER_VOLUME;
   1273         }
   1274         doSetMasterVolume((float)volume / MAX_MASTER_VOLUME, flags);
   1275     }
   1276 
   1277     private void doSetMasterVolume(float volume, int flags) {
   1278         // don't allow changing master volume when muted
   1279         if (!AudioSystem.getMasterMute()) {
   1280             int oldVolume = getMasterVolume();
   1281             AudioSystem.setMasterVolume(volume);
   1282 
   1283             int newVolume = getMasterVolume();
   1284             if (newVolume != oldVolume) {
   1285                 // Post a persist master volume msg
   1286                 sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME, SENDMSG_REPLACE,
   1287                         Math.round(volume * (float)1000.0), 0, null, PERSIST_DELAY);
   1288             }
   1289             // Send the volume update regardless whether there was a change.
   1290             sendMasterVolumeUpdate(flags, oldVolume, newVolume);
   1291         }
   1292     }
   1293 
   1294     /** @see AudioManager#getStreamMaxVolume(int) */
   1295     public int getStreamMaxVolume(int streamType) {
   1296         ensureValidStreamType(streamType);
   1297         return (mStreamStates[streamType].getMaxIndex() + 5) / 10;
   1298     }
   1299 
   1300     public int getMasterMaxVolume() {
   1301         return MAX_MASTER_VOLUME;
   1302     }
   1303 
   1304     /** Get last audible volume before stream was muted. */
   1305     public int getLastAudibleStreamVolume(int streamType) {
   1306         ensureValidStreamType(streamType);
   1307         int device = getDeviceForStream(streamType);
   1308         return (mStreamStates[streamType].getIndex(device) + 5) / 10;
   1309     }
   1310 
   1311     /** Get last audible master volume before it was muted. */
   1312     public int getLastAudibleMasterVolume() {
   1313         return Math.round(AudioSystem.getMasterVolume() * MAX_MASTER_VOLUME);
   1314     }
   1315 
   1316     /** @see AudioManager#getMasterStreamType()  */
   1317     public int getMasterStreamType() {
   1318         if (mVoiceCapable) {
   1319             return AudioSystem.STREAM_RING;
   1320         } else {
   1321             return AudioSystem.STREAM_MUSIC;
   1322         }
   1323     }
   1324 
   1325     /** @see AudioManager#getRingerMode() */
   1326     public int getRingerMode() {
   1327         synchronized(mSettingsLock) {
   1328             return mRingerMode;
   1329         }
   1330     }
   1331 
   1332     private void ensureValidRingerMode(int ringerMode) {
   1333         if (!AudioManager.isValidRingerMode(ringerMode)) {
   1334             throw new IllegalArgumentException("Bad ringer mode " + ringerMode);
   1335         }
   1336     }
   1337 
   1338     /** @see AudioManager#setRingerMode(int) */
   1339     public void setRingerMode(int ringerMode) {
   1340         if (mUseFixedVolume) {
   1341             return;
   1342         }
   1343 
   1344         if ((ringerMode == AudioManager.RINGER_MODE_VIBRATE) && !mHasVibrator) {
   1345             ringerMode = AudioManager.RINGER_MODE_SILENT;
   1346         }
   1347         if (ringerMode != getRingerMode()) {
   1348             setRingerModeInt(ringerMode, true);
   1349             // Send sticky broadcast
   1350             broadcastRingerMode(ringerMode);
   1351         }
   1352     }
   1353 
   1354     private void setRingerModeInt(int ringerMode, boolean persist) {
   1355         synchronized(mSettingsLock) {
   1356             mRingerMode = ringerMode;
   1357         }
   1358 
   1359         // Mute stream if not previously muted by ringer mode and ringer mode
   1360         // is not RINGER_MODE_NORMAL and stream is affected by ringer mode.
   1361         // Unmute stream if previously muted by ringer mode and ringer mode
   1362         // is RINGER_MODE_NORMAL or stream is not affected by ringer mode.
   1363         int numStreamTypes = AudioSystem.getNumStreamTypes();
   1364         for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
   1365             if (isStreamMutedByRingerMode(streamType)) {
   1366                 if (!isStreamAffectedByRingerMode(streamType) ||
   1367                     ringerMode == AudioManager.RINGER_MODE_NORMAL) {
   1368                     // ring and notifications volume should never be 0 when not silenced
   1369                     // on voice capable devices
   1370                     if (mVoiceCapable &&
   1371                             mStreamVolumeAlias[streamType] == AudioSystem.STREAM_RING) {
   1372                         synchronized (mStreamStates[streamType]) {
   1373                             Set set = mStreamStates[streamType].mIndex.entrySet();
   1374                             Iterator i = set.iterator();
   1375                             while (i.hasNext()) {
   1376                                 Map.Entry entry = (Map.Entry)i.next();
   1377                                 if ((Integer)entry.getValue() == 0) {
   1378                                     entry.setValue(10);
   1379                                 }
   1380                             }
   1381                         }
   1382                     }
   1383                     mStreamStates[streamType].mute(null, false);
   1384                     mRingerModeMutedStreams &= ~(1 << streamType);
   1385                 }
   1386             } else {
   1387                 if (isStreamAffectedByRingerMode(streamType) &&
   1388                     ringerMode != AudioManager.RINGER_MODE_NORMAL) {
   1389                    mStreamStates[streamType].mute(null, true);
   1390                    mRingerModeMutedStreams |= (1 << streamType);
   1391                }
   1392             }
   1393         }
   1394 
   1395         // Post a persist ringer mode msg
   1396         if (persist) {
   1397             sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE,
   1398                     SENDMSG_REPLACE, 0, 0, null, PERSIST_DELAY);
   1399         }
   1400     }
   1401 
   1402     private void restoreMasterVolume() {
   1403         if (mUseFixedVolume) {
   1404             AudioSystem.setMasterVolume(1.0f);
   1405             return;
   1406         }
   1407         if (mUseMasterVolume) {
   1408             float volume = Settings.System.getFloatForUser(mContentResolver,
   1409                     Settings.System.VOLUME_MASTER, -1.0f, UserHandle.USER_CURRENT);
   1410             if (volume >= 0.0f) {
   1411                 AudioSystem.setMasterVolume(volume);
   1412             }
   1413         }
   1414     }
   1415 
   1416     /** @see AudioManager#shouldVibrate(int) */
   1417     public boolean shouldVibrate(int vibrateType) {
   1418         if (!mHasVibrator) return false;
   1419 
   1420         switch (getVibrateSetting(vibrateType)) {
   1421 
   1422             case AudioManager.VIBRATE_SETTING_ON:
   1423                 return getRingerMode() != AudioManager.RINGER_MODE_SILENT;
   1424 
   1425             case AudioManager.VIBRATE_SETTING_ONLY_SILENT:
   1426                 return getRingerMode() == AudioManager.RINGER_MODE_VIBRATE;
   1427 
   1428             case AudioManager.VIBRATE_SETTING_OFF:
   1429                 // return false, even for incoming calls
   1430                 return false;
   1431 
   1432             default:
   1433                 return false;
   1434         }
   1435     }
   1436 
   1437     /** @see AudioManager#getVibrateSetting(int) */
   1438     public int getVibrateSetting(int vibrateType) {
   1439         if (!mHasVibrator) return AudioManager.VIBRATE_SETTING_OFF;
   1440         return (mVibrateSetting >> (vibrateType * 2)) & 3;
   1441     }
   1442 
   1443     /** @see AudioManager#setVibrateSetting(int, int) */
   1444     public void setVibrateSetting(int vibrateType, int vibrateSetting) {
   1445 
   1446         if (!mHasVibrator) return;
   1447 
   1448         mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, vibrateType, vibrateSetting);
   1449 
   1450         // Broadcast change
   1451         broadcastVibrateSetting(vibrateType);
   1452 
   1453     }
   1454 
   1455     /**
   1456      * @see #setVibrateSetting(int, int)
   1457      */
   1458     public static int getValueForVibrateSetting(int existingValue, int vibrateType,
   1459             int vibrateSetting) {
   1460 
   1461         // First clear the existing setting. Each vibrate type has two bits in
   1462         // the value. Note '3' is '11' in binary.
   1463         existingValue &= ~(3 << (vibrateType * 2));
   1464 
   1465         // Set into the old value
   1466         existingValue |= (vibrateSetting & 3) << (vibrateType * 2);
   1467 
   1468         return existingValue;
   1469     }
   1470 
   1471     private class SetModeDeathHandler implements IBinder.DeathRecipient {
   1472         private IBinder mCb; // To be notified of client's death
   1473         private int mPid;
   1474         private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client
   1475 
   1476         SetModeDeathHandler(IBinder cb, int pid) {
   1477             mCb = cb;
   1478             mPid = pid;
   1479         }
   1480 
   1481         public void binderDied() {
   1482             int newModeOwnerPid = 0;
   1483             synchronized(mSetModeDeathHandlers) {
   1484                 Log.w(TAG, "setMode() client died");
   1485                 int index = mSetModeDeathHandlers.indexOf(this);
   1486                 if (index < 0) {
   1487                     Log.w(TAG, "unregistered setMode() client died");
   1488                 } else {
   1489                     newModeOwnerPid = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid);
   1490                 }
   1491             }
   1492             // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
   1493             // SCO connections not started by the application changing the mode
   1494             if (newModeOwnerPid != 0) {
   1495                 final long ident = Binder.clearCallingIdentity();
   1496                 disconnectBluetoothSco(newModeOwnerPid);
   1497                 Binder.restoreCallingIdentity(ident);
   1498             }
   1499         }
   1500 
   1501         public int getPid() {
   1502             return mPid;
   1503         }
   1504 
   1505         public void setMode(int mode) {
   1506             mMode = mode;
   1507         }
   1508 
   1509         public int getMode() {
   1510             return mMode;
   1511         }
   1512 
   1513         public IBinder getBinder() {
   1514             return mCb;
   1515         }
   1516     }
   1517 
   1518     /** @see AudioManager#setMode(int) */
   1519     public void setMode(int mode, IBinder cb) {
   1520         if (!checkAudioSettingsPermission("setMode()")) {
   1521             return;
   1522         }
   1523 
   1524         if (mode < AudioSystem.MODE_CURRENT || mode >= AudioSystem.NUM_MODES) {
   1525             return;
   1526         }
   1527 
   1528         int newModeOwnerPid = 0;
   1529         synchronized(mSetModeDeathHandlers) {
   1530             if (mode == AudioSystem.MODE_CURRENT) {
   1531                 mode = mMode;
   1532             }
   1533             newModeOwnerPid = setModeInt(mode, cb, Binder.getCallingPid());
   1534         }
   1535         // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
   1536         // SCO connections not started by the application changing the mode
   1537         if (newModeOwnerPid != 0) {
   1538              disconnectBluetoothSco(newModeOwnerPid);
   1539         }
   1540     }
   1541 
   1542     // must be called synchronized on mSetModeDeathHandlers
   1543     // setModeInt() returns a valid PID if the audio mode was successfully set to
   1544     // any mode other than NORMAL.
   1545     int setModeInt(int mode, IBinder cb, int pid) {
   1546         int newModeOwnerPid = 0;
   1547         if (cb == null) {
   1548             Log.e(TAG, "setModeInt() called with null binder");
   1549             return newModeOwnerPid;
   1550         }
   1551 
   1552         SetModeDeathHandler hdlr = null;
   1553         Iterator iter = mSetModeDeathHandlers.iterator();
   1554         while (iter.hasNext()) {
   1555             SetModeDeathHandler h = (SetModeDeathHandler)iter.next();
   1556             if (h.getPid() == pid) {
   1557                 hdlr = h;
   1558                 // Remove from client list so that it is re-inserted at top of list
   1559                 iter.remove();
   1560                 hdlr.getBinder().unlinkToDeath(hdlr, 0);
   1561                 break;
   1562             }
   1563         }
   1564         int status = AudioSystem.AUDIO_STATUS_OK;
   1565         do {
   1566             if (mode == AudioSystem.MODE_NORMAL) {
   1567                 // get new mode from client at top the list if any
   1568                 if (!mSetModeDeathHandlers.isEmpty()) {
   1569                     hdlr = mSetModeDeathHandlers.get(0);
   1570                     cb = hdlr.getBinder();
   1571                     mode = hdlr.getMode();
   1572                 }
   1573             } else {
   1574                 if (hdlr == null) {
   1575                     hdlr = new SetModeDeathHandler(cb, pid);
   1576                 }
   1577                 // Register for client death notification
   1578                 try {
   1579                     cb.linkToDeath(hdlr, 0);
   1580                 } catch (RemoteException e) {
   1581                     // Client has died!
   1582                     Log.w(TAG, "setMode() could not link to "+cb+" binder death");
   1583                 }
   1584 
   1585                 // Last client to call setMode() is always at top of client list
   1586                 // as required by SetModeDeathHandler.binderDied()
   1587                 mSetModeDeathHandlers.add(0, hdlr);
   1588                 hdlr.setMode(mode);
   1589             }
   1590 
   1591             if (mode != mMode) {
   1592                 status = AudioSystem.setPhoneState(mode);
   1593                 if (status == AudioSystem.AUDIO_STATUS_OK) {
   1594                     mMode = mode;
   1595                 } else {
   1596                     if (hdlr != null) {
   1597                         mSetModeDeathHandlers.remove(hdlr);
   1598                         cb.unlinkToDeath(hdlr, 0);
   1599                     }
   1600                     // force reading new top of mSetModeDeathHandlers stack
   1601                     mode = AudioSystem.MODE_NORMAL;
   1602                 }
   1603             } else {
   1604                 status = AudioSystem.AUDIO_STATUS_OK;
   1605             }
   1606         } while (status != AudioSystem.AUDIO_STATUS_OK && !mSetModeDeathHandlers.isEmpty());
   1607 
   1608         if (status == AudioSystem.AUDIO_STATUS_OK) {
   1609             if (mode != AudioSystem.MODE_NORMAL) {
   1610                 if (mSetModeDeathHandlers.isEmpty()) {
   1611                     Log.e(TAG, "setMode() different from MODE_NORMAL with empty mode client stack");
   1612                 } else {
   1613                     newModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
   1614                 }
   1615             }
   1616             int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
   1617             if (streamType == STREAM_REMOTE_MUSIC) {
   1618                 // here handle remote media playback the same way as local playback
   1619                 streamType = AudioManager.STREAM_MUSIC;
   1620             }
   1621             int device = getDeviceForStream(streamType);
   1622             int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device);
   1623             setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true);
   1624 
   1625             updateStreamVolumeAlias(true /*updateVolumes*/);
   1626         }
   1627         return newModeOwnerPid;
   1628     }
   1629 
   1630     /** @see AudioManager#getMode() */
   1631     public int getMode() {
   1632         return mMode;
   1633     }
   1634 
   1635     //==========================================================================================
   1636     // Sound Effects
   1637     //==========================================================================================
   1638 
   1639     private static final String TAG_AUDIO_ASSETS = "audio_assets";
   1640     private static final String ATTR_VERSION = "version";
   1641     private static final String TAG_GROUP = "group";
   1642     private static final String ATTR_GROUP_NAME = "name";
   1643     private static final String TAG_ASSET = "asset";
   1644     private static final String ATTR_ASSET_ID = "id";
   1645     private static final String ATTR_ASSET_FILE = "file";
   1646 
   1647     private static final String ASSET_FILE_VERSION = "1.0";
   1648     private static final String GROUP_TOUCH_SOUNDS = "touch_sounds";
   1649 
   1650     private static final int SOUND_EFECTS_LOAD_TIMEOUT_MS = 5000;
   1651 
   1652     class LoadSoundEffectReply {
   1653         public int mStatus = 1;
   1654     };
   1655 
   1656     private void loadTouchSoundAssetDefaults() {
   1657         SOUND_EFFECT_FILES.add("Effect_Tick.ogg");
   1658         for (int i = 0; i < AudioManager.NUM_SOUND_EFFECTS; i++) {
   1659             SOUND_EFFECT_FILES_MAP[i][0] = 0;
   1660             SOUND_EFFECT_FILES_MAP[i][1] = -1;
   1661         }
   1662     }
   1663 
   1664     private void loadTouchSoundAssets() {
   1665         XmlResourceParser parser = null;
   1666 
   1667         // only load assets once.
   1668         if (!SOUND_EFFECT_FILES.isEmpty()) {
   1669             return;
   1670         }
   1671 
   1672         loadTouchSoundAssetDefaults();
   1673 
   1674         try {
   1675             parser = mContext.getResources().getXml(com.android.internal.R.xml.audio_assets);
   1676 
   1677             XmlUtils.beginDocument(parser, TAG_AUDIO_ASSETS);
   1678             String version = parser.getAttributeValue(null, ATTR_VERSION);
   1679             boolean inTouchSoundsGroup = false;
   1680 
   1681             if (ASSET_FILE_VERSION.equals(version)) {
   1682                 while (true) {
   1683                     XmlUtils.nextElement(parser);
   1684                     String element = parser.getName();
   1685                     if (element == null) {
   1686                         break;
   1687                     }
   1688                     if (element.equals(TAG_GROUP)) {
   1689                         String name = parser.getAttributeValue(null, ATTR_GROUP_NAME);
   1690                         if (GROUP_TOUCH_SOUNDS.equals(name)) {
   1691                             inTouchSoundsGroup = true;
   1692                             break;
   1693                         }
   1694                     }
   1695                 }
   1696                 while (inTouchSoundsGroup) {
   1697                     XmlUtils.nextElement(parser);
   1698                     String element = parser.getName();
   1699                     if (element == null) {
   1700                         break;
   1701                     }
   1702                     if (element.equals(TAG_ASSET)) {
   1703                         String id = parser.getAttributeValue(null, ATTR_ASSET_ID);
   1704                         String file = parser.getAttributeValue(null, ATTR_ASSET_FILE);
   1705                         int fx;
   1706 
   1707                         try {
   1708                             Field field = AudioManager.class.getField(id);
   1709                             fx = field.getInt(null);
   1710                         } catch (Exception e) {
   1711                             Log.w(TAG, "Invalid touch sound ID: "+id);
   1712                             continue;
   1713                         }
   1714 
   1715                         int i = SOUND_EFFECT_FILES.indexOf(file);
   1716                         if (i == -1) {
   1717                             i = SOUND_EFFECT_FILES.size();
   1718                             SOUND_EFFECT_FILES.add(file);
   1719                         }
   1720                         SOUND_EFFECT_FILES_MAP[fx][0] = i;
   1721                     } else {
   1722                         break;
   1723                     }
   1724                 }
   1725             }
   1726         } catch (Resources.NotFoundException e) {
   1727             Log.w(TAG, "audio assets file not found", e);
   1728         } catch (XmlPullParserException e) {
   1729             Log.w(TAG, "XML parser exception reading touch sound assets", e);
   1730         } catch (IOException e) {
   1731             Log.w(TAG, "I/O exception reading touch sound assets", e);
   1732         } finally {
   1733             if (parser != null) {
   1734                 parser.close();
   1735             }
   1736         }
   1737     }
   1738 
   1739     /** @see AudioManager#playSoundEffect(int) */
   1740     public void playSoundEffect(int effectType) {
   1741         playSoundEffectVolume(effectType, -1.0f);
   1742     }
   1743 
   1744     /** @see AudioManager#playSoundEffect(int, float) */
   1745     public void playSoundEffectVolume(int effectType, float volume) {
   1746         sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_QUEUE,
   1747                 effectType, (int) (volume * 1000), null, 0);
   1748     }
   1749 
   1750     /**
   1751      * Loads samples into the soundpool.
   1752      * This method must be called at first when sound effects are enabled
   1753      */
   1754     public boolean loadSoundEffects() {
   1755         int attempts = 3;
   1756         LoadSoundEffectReply reply = new LoadSoundEffectReply();
   1757 
   1758         synchronized (reply) {
   1759             sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_QUEUE, 0, 0, reply, 0);
   1760             while ((reply.mStatus == 1) && (attempts-- > 0)) {
   1761                 try {
   1762                     reply.wait(SOUND_EFECTS_LOAD_TIMEOUT_MS);
   1763                 } catch (InterruptedException e) {
   1764                     Log.w(TAG, "loadSoundEffects Interrupted while waiting sound pool loaded.");
   1765                 }
   1766             }
   1767         }
   1768         return (reply.mStatus == 0);
   1769     }
   1770 
   1771     /**
   1772      *  Unloads samples from the sound pool.
   1773      *  This method can be called to free some memory when
   1774      *  sound effects are disabled.
   1775      */
   1776     public void unloadSoundEffects() {
   1777         sendMsg(mAudioHandler, MSG_UNLOAD_SOUND_EFFECTS, SENDMSG_QUEUE, 0, 0, null, 0);
   1778     }
   1779 
   1780     class SoundPoolListenerThread extends Thread {
   1781         public SoundPoolListenerThread() {
   1782             super("SoundPoolListenerThread");
   1783         }
   1784 
   1785         @Override
   1786         public void run() {
   1787 
   1788             Looper.prepare();
   1789             mSoundPoolLooper = Looper.myLooper();
   1790 
   1791             synchronized (mSoundEffectsLock) {
   1792                 if (mSoundPool != null) {
   1793                     mSoundPoolCallBack = new SoundPoolCallback();
   1794                     mSoundPool.setOnLoadCompleteListener(mSoundPoolCallBack);
   1795                 }
   1796                 mSoundEffectsLock.notify();
   1797             }
   1798             Looper.loop();
   1799         }
   1800     }
   1801 
   1802     private final class SoundPoolCallback implements
   1803             android.media.SoundPool.OnLoadCompleteListener {
   1804 
   1805         int mStatus = 1; // 1 means neither error nor last sample loaded yet
   1806         List<Integer> mSamples = new ArrayList<Integer>();
   1807 
   1808         public int status() {
   1809             return mStatus;
   1810         }
   1811 
   1812         public void setSamples(int[] samples) {
   1813             for (int i = 0; i < samples.length; i++) {
   1814                 // do not wait ack for samples rejected upfront by SoundPool
   1815                 if (samples[i] > 0) {
   1816                     mSamples.add(samples[i]);
   1817                 }
   1818             }
   1819         }
   1820 
   1821         public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
   1822             synchronized (mSoundEffectsLock) {
   1823                 int i = mSamples.indexOf(sampleId);
   1824                 if (i >= 0) {
   1825                     mSamples.remove(i);
   1826                 }
   1827                 if ((status != 0) || mSamples. isEmpty()) {
   1828                     mStatus = status;
   1829                     mSoundEffectsLock.notify();
   1830                 }
   1831             }
   1832         }
   1833     }
   1834 
   1835     /** @see AudioManager#reloadAudioSettings() */
   1836     public void reloadAudioSettings() {
   1837         readAudioSettings(false /*userSwitch*/);
   1838     }
   1839 
   1840     private void readAudioSettings(boolean userSwitch) {
   1841         // restore ringer mode, ringer mode affected streams, mute affected streams and vibrate settings
   1842         readPersistedSettings();
   1843 
   1844         // restore volume settings
   1845         int numStreamTypes = AudioSystem.getNumStreamTypes();
   1846         for (int streamType = 0; streamType < numStreamTypes; streamType++) {
   1847             VolumeStreamState streamState = mStreamStates[streamType];
   1848 
   1849             if (userSwitch && mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) {
   1850                 continue;
   1851             }
   1852 
   1853             synchronized (streamState) {
   1854                 streamState.readSettings();
   1855 
   1856                 // unmute stream that was muted but is not affect by mute anymore
   1857                 if (streamState.isMuted() && ((!isStreamAffectedByMute(streamType) &&
   1858                         !isStreamMutedByRingerMode(streamType)) || mUseFixedVolume)) {
   1859                     int size = streamState.mDeathHandlers.size();
   1860                     for (int i = 0; i < size; i++) {
   1861                         streamState.mDeathHandlers.get(i).mMuteCount = 1;
   1862                         streamState.mDeathHandlers.get(i).mute(false);
   1863                     }
   1864                 }
   1865             }
   1866         }
   1867 
   1868         // apply new ringer mode before checking volume for alias streams so that streams
   1869         // muted by ringer mode have the correct volume
   1870         setRingerModeInt(getRingerMode(), false);
   1871 
   1872         checkAllAliasStreamVolumes();
   1873 
   1874         synchronized (mSafeMediaVolumeState) {
   1875             if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE) {
   1876                 enforceSafeMediaVolume();
   1877             }
   1878         }
   1879     }
   1880 
   1881     /** @see AudioManager#setSpeakerphoneOn(boolean) */
   1882     public void setSpeakerphoneOn(boolean on){
   1883         if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
   1884             return;
   1885         }
   1886         mForcedUseForComm = on ? AudioSystem.FORCE_SPEAKER : AudioSystem.FORCE_NONE;
   1887 
   1888         sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
   1889                 AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0);
   1890     }
   1891 
   1892     /** @see AudioManager#isSpeakerphoneOn() */
   1893     public boolean isSpeakerphoneOn() {
   1894         return (mForcedUseForComm == AudioSystem.FORCE_SPEAKER);
   1895     }
   1896 
   1897     /** @see AudioManager#setBluetoothScoOn(boolean) */
   1898     public void setBluetoothScoOn(boolean on){
   1899         if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
   1900             return;
   1901         }
   1902         mForcedUseForComm = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE;
   1903 
   1904         sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
   1905                 AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0);
   1906         sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
   1907                 AudioSystem.FOR_RECORD, mForcedUseForComm, null, 0);
   1908     }
   1909 
   1910     /** @see AudioManager#isBluetoothScoOn() */
   1911     public boolean isBluetoothScoOn() {
   1912         return (mForcedUseForComm == AudioSystem.FORCE_BT_SCO);
   1913     }
   1914 
   1915     /** @see AudioManager#setBluetoothA2dpOn(boolean) */
   1916     public void setBluetoothA2dpOn(boolean on) {
   1917         synchronized (mBluetoothA2dpEnabledLock) {
   1918             mBluetoothA2dpEnabled = on;
   1919             sendMsg(mAudioHandler, MSG_SET_FORCE_BT_A2DP_USE, SENDMSG_QUEUE,
   1920                     AudioSystem.FOR_MEDIA,
   1921                     mBluetoothA2dpEnabled ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP,
   1922                     null, 0);
   1923         }
   1924     }
   1925 
   1926     /** @see AudioManager#isBluetoothA2dpOn() */
   1927     public boolean isBluetoothA2dpOn() {
   1928         synchronized (mBluetoothA2dpEnabledLock) {
   1929             return mBluetoothA2dpEnabled;
   1930         }
   1931     }
   1932 
   1933     /** @see AudioManager#startBluetoothSco() */
   1934     public void startBluetoothSco(IBinder cb, int targetSdkVersion){
   1935         if (!checkAudioSettingsPermission("startBluetoothSco()") ||
   1936                 !mBootCompleted) {
   1937             return;
   1938         }
   1939         ScoClient client = getScoClient(cb, true);
   1940         // The calling identity must be cleared before calling ScoClient.incCount().
   1941         // inCount() calls requestScoState() which in turn can call BluetoothHeadset APIs
   1942         // and this must be done on behalf of system server to make sure permissions are granted.
   1943         // The caller identity must be cleared after getScoClient() because it is needed if a new
   1944         // client is created.
   1945         final long ident = Binder.clearCallingIdentity();
   1946         client.incCount(targetSdkVersion);
   1947         Binder.restoreCallingIdentity(ident);
   1948     }
   1949 
   1950     /** @see AudioManager#stopBluetoothSco() */
   1951     public void stopBluetoothSco(IBinder cb){
   1952         if (!checkAudioSettingsPermission("stopBluetoothSco()") ||
   1953                 !mBootCompleted) {
   1954             return;
   1955         }
   1956         ScoClient client = getScoClient(cb, false);
   1957         // The calling identity must be cleared before calling ScoClient.decCount().
   1958         // decCount() calls requestScoState() which in turn can call BluetoothHeadset APIs
   1959         // and this must be done on behalf of system server to make sure permissions are granted.
   1960         final long ident = Binder.clearCallingIdentity();
   1961         if (client != null) {
   1962             client.decCount();
   1963         }
   1964         Binder.restoreCallingIdentity(ident);
   1965     }
   1966 
   1967 
   1968     private class ScoClient implements IBinder.DeathRecipient {
   1969         private IBinder mCb; // To be notified of client's death
   1970         private int mCreatorPid;
   1971         private int mStartcount; // number of SCO connections started by this client
   1972 
   1973         ScoClient(IBinder cb) {
   1974             mCb = cb;
   1975             mCreatorPid = Binder.getCallingPid();
   1976             mStartcount = 0;
   1977         }
   1978 
   1979         public void binderDied() {
   1980             synchronized(mScoClients) {
   1981                 Log.w(TAG, "SCO client died");
   1982                 int index = mScoClients.indexOf(this);
   1983                 if (index < 0) {
   1984                     Log.w(TAG, "unregistered SCO client died");
   1985                 } else {
   1986                     clearCount(true);
   1987                     mScoClients.remove(this);
   1988                 }
   1989             }
   1990         }
   1991 
   1992         public void incCount(int targetSdkVersion) {
   1993             synchronized(mScoClients) {
   1994                 requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, targetSdkVersion);
   1995                 if (mStartcount == 0) {
   1996                     try {
   1997                         mCb.linkToDeath(this, 0);
   1998                     } catch (RemoteException e) {
   1999                         // client has already died!
   2000                         Log.w(TAG, "ScoClient  incCount() could not link to "+mCb+" binder death");
   2001                     }
   2002                 }
   2003                 mStartcount++;
   2004             }
   2005         }
   2006 
   2007         public void decCount() {
   2008             synchronized(mScoClients) {
   2009                 if (mStartcount == 0) {
   2010                     Log.w(TAG, "ScoClient.decCount() already 0");
   2011                 } else {
   2012                     mStartcount--;
   2013                     if (mStartcount == 0) {
   2014                         try {
   2015                             mCb.unlinkToDeath(this, 0);
   2016                         } catch (NoSuchElementException e) {
   2017                             Log.w(TAG, "decCount() going to 0 but not registered to binder");
   2018                         }
   2019                     }
   2020                     requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 0);
   2021                 }
   2022             }
   2023         }
   2024 
   2025         public void clearCount(boolean stopSco) {
   2026             synchronized(mScoClients) {
   2027                 if (mStartcount != 0) {
   2028                     try {
   2029                         mCb.unlinkToDeath(this, 0);
   2030                     } catch (NoSuchElementException e) {
   2031                         Log.w(TAG, "clearCount() mStartcount: "+mStartcount+" != 0 but not registered to binder");
   2032                     }
   2033                 }
   2034                 mStartcount = 0;
   2035                 if (stopSco) {
   2036                     requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 0);
   2037                 }
   2038             }
   2039         }
   2040 
   2041         public int getCount() {
   2042             return mStartcount;
   2043         }
   2044 
   2045         public IBinder getBinder() {
   2046             return mCb;
   2047         }
   2048 
   2049         public int getPid() {
   2050             return mCreatorPid;
   2051         }
   2052 
   2053         public int totalCount() {
   2054             synchronized(mScoClients) {
   2055                 int count = 0;
   2056                 int size = mScoClients.size();
   2057                 for (int i = 0; i < size; i++) {
   2058                     count += mScoClients.get(i).getCount();
   2059                 }
   2060                 return count;
   2061             }
   2062         }
   2063 
   2064         private void requestScoState(int state, int targetSdkVersion) {
   2065             checkScoAudioState();
   2066             if (totalCount() == 0) {
   2067                 if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
   2068                     // Make sure that the state transitions to CONNECTING even if we cannot initiate
   2069                     // the connection.
   2070                     broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
   2071                     // Accept SCO audio activation only in NORMAL audio mode or if the mode is
   2072                     // currently controlled by the same client process.
   2073                     synchronized(mSetModeDeathHandlers) {
   2074                         if ((mSetModeDeathHandlers.isEmpty() ||
   2075                                 mSetModeDeathHandlers.get(0).getPid() == mCreatorPid) &&
   2076                                 (mScoAudioState == SCO_STATE_INACTIVE ||
   2077                                  mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
   2078                             if (mScoAudioState == SCO_STATE_INACTIVE) {
   2079                                 mScoAudioMode =
   2080                                         (targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2) ?
   2081                                                 SCO_MODE_VIRTUAL_CALL : SCO_MODE_RAW;
   2082                                 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
   2083                                     boolean status;
   2084                                     if (mScoAudioMode == SCO_MODE_RAW) {
   2085                                         status = mBluetoothHeadset.connectAudio();
   2086                                     } else {
   2087                                         status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
   2088                                                                             mBluetoothHeadsetDevice);
   2089                                     }
   2090                                     if (status) {
   2091                                         mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
   2092                                     } else {
   2093                                         broadcastScoConnectionState(
   2094                                                 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
   2095                                     }
   2096                                 } else if (getBluetoothHeadset()) {
   2097                                     mScoAudioState = SCO_STATE_ACTIVATE_REQ;
   2098                                 }
   2099                             } else {
   2100                                 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
   2101                                 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
   2102                             }
   2103                         } else {
   2104                             broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
   2105                         }
   2106                     }
   2107                 } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED &&
   2108                               (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
   2109                                mScoAudioState == SCO_STATE_ACTIVATE_REQ)) {
   2110                     if (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) {
   2111                         if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
   2112                             boolean status;
   2113                             if (mScoAudioMode == SCO_MODE_RAW) {
   2114                                 status = mBluetoothHeadset.disconnectAudio();
   2115                             } else {
   2116                                 status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
   2117                                                                         mBluetoothHeadsetDevice);
   2118                             }
   2119                             if (!status) {
   2120                                 mScoAudioState = SCO_STATE_INACTIVE;
   2121                                 broadcastScoConnectionState(
   2122                                         AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
   2123                             }
   2124                         } else if (getBluetoothHeadset()) {
   2125                             mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
   2126                         }
   2127                     } else {
   2128                         mScoAudioState = SCO_STATE_INACTIVE;
   2129                         broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
   2130                     }
   2131                 }
   2132             }
   2133         }
   2134     }
   2135 
   2136     private void checkScoAudioState() {
   2137         if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null &&
   2138                 mScoAudioState == SCO_STATE_INACTIVE &&
   2139                 mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
   2140                 != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
   2141             mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
   2142         }
   2143     }
   2144 
   2145     private ScoClient getScoClient(IBinder cb, boolean create) {
   2146         synchronized(mScoClients) {
   2147             ScoClient client = null;
   2148             int size = mScoClients.size();
   2149             for (int i = 0; i < size; i++) {
   2150                 client = mScoClients.get(i);
   2151                 if (client.getBinder() == cb)
   2152                     return client;
   2153             }
   2154             if (create) {
   2155                 client = new ScoClient(cb);
   2156                 mScoClients.add(client);
   2157             }
   2158             return client;
   2159         }
   2160     }
   2161 
   2162     public void clearAllScoClients(int exceptPid, boolean stopSco) {
   2163         synchronized(mScoClients) {
   2164             ScoClient savedClient = null;
   2165             int size = mScoClients.size();
   2166             for (int i = 0; i < size; i++) {
   2167                 ScoClient cl = mScoClients.get(i);
   2168                 if (cl.getPid() != exceptPid) {
   2169                     cl.clearCount(stopSco);
   2170                 } else {
   2171                     savedClient = cl;
   2172                 }
   2173             }
   2174             mScoClients.clear();
   2175             if (savedClient != null) {
   2176                 mScoClients.add(savedClient);
   2177             }
   2178         }
   2179     }
   2180 
   2181     private boolean getBluetoothHeadset() {
   2182         boolean result = false;
   2183         BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
   2184         if (adapter != null) {
   2185             result = adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
   2186                                     BluetoothProfile.HEADSET);
   2187         }
   2188         // If we could not get a bluetooth headset proxy, send a failure message
   2189         // without delay to reset the SCO audio state and clear SCO clients.
   2190         // If we could get a proxy, send a delayed failure message that will reset our state
   2191         // in case we don't receive onServiceConnected().
   2192         sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED,
   2193                 SENDMSG_REPLACE, 0, 0, null, result ? BT_HEADSET_CNCT_TIMEOUT_MS : 0);
   2194         return result;
   2195     }
   2196 
   2197     private void disconnectBluetoothSco(int exceptPid) {
   2198         synchronized(mScoClients) {
   2199             checkScoAudioState();
   2200             if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL ||
   2201                     mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
   2202                 if (mBluetoothHeadsetDevice != null) {
   2203                     if (mBluetoothHeadset != null) {
   2204                         if (!mBluetoothHeadset.stopVoiceRecognition(
   2205                                 mBluetoothHeadsetDevice)) {
   2206                             sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED,
   2207                                     SENDMSG_REPLACE, 0, 0, null, 0);
   2208                         }
   2209                     } else if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL &&
   2210                             getBluetoothHeadset()) {
   2211                         mScoAudioState = SCO_STATE_DEACTIVATE_EXT_REQ;
   2212                     }
   2213                 }
   2214             } else {
   2215                 clearAllScoClients(exceptPid, true);
   2216             }
   2217         }
   2218     }
   2219 
   2220     private void resetBluetoothSco() {
   2221         synchronized(mScoClients) {
   2222             clearAllScoClients(0, false);
   2223             mScoAudioState = SCO_STATE_INACTIVE;
   2224             broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
   2225         }
   2226     }
   2227 
   2228     private void broadcastScoConnectionState(int state) {
   2229         sendMsg(mAudioHandler, MSG_BROADCAST_BT_CONNECTION_STATE,
   2230                 SENDMSG_QUEUE, state, 0, null, 0);
   2231     }
   2232 
   2233     private void onBroadcastScoConnectionState(int state) {
   2234         if (state != mScoConnectionState) {
   2235             Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
   2236             newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
   2237             newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE,
   2238                     mScoConnectionState);
   2239             sendStickyBroadcastToAll(newIntent);
   2240             mScoConnectionState = state;
   2241         }
   2242     }
   2243 
   2244     private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
   2245         new BluetoothProfile.ServiceListener() {
   2246         public void onServiceConnected(int profile, BluetoothProfile proxy) {
   2247             BluetoothDevice btDevice;
   2248             List<BluetoothDevice> deviceList;
   2249             switch(profile) {
   2250             case BluetoothProfile.A2DP:
   2251                 BluetoothA2dp a2dp = (BluetoothA2dp) proxy;
   2252                 deviceList = a2dp.getConnectedDevices();
   2253                 if (deviceList.size() > 0) {
   2254                     btDevice = deviceList.get(0);
   2255                     synchronized (mConnectedDevices) {
   2256                         int state = a2dp.getConnectionState(btDevice);
   2257                         int delay = checkSendBecomingNoisyIntent(
   2258                                                 AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
   2259                                                 (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0);
   2260                         queueMsgUnderWakeLock(mAudioHandler,
   2261                                 MSG_SET_A2DP_CONNECTION_STATE,
   2262                                 state,
   2263                                 0,
   2264                                 btDevice,
   2265                                 delay);
   2266                     }
   2267                 }
   2268                 break;
   2269 
   2270             case BluetoothProfile.HEADSET:
   2271                 synchronized (mScoClients) {
   2272                     // Discard timeout message
   2273                     mAudioHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
   2274                     mBluetoothHeadset = (BluetoothHeadset) proxy;
   2275                     deviceList = mBluetoothHeadset.getConnectedDevices();
   2276                     if (deviceList.size() > 0) {
   2277                         mBluetoothHeadsetDevice = deviceList.get(0);
   2278                     } else {
   2279                         mBluetoothHeadsetDevice = null;
   2280                     }
   2281                     // Refresh SCO audio state
   2282                     checkScoAudioState();
   2283                     // Continue pending action if any
   2284                     if (mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
   2285                             mScoAudioState == SCO_STATE_DEACTIVATE_REQ ||
   2286                             mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
   2287                         boolean status = false;
   2288                         if (mBluetoothHeadsetDevice != null) {
   2289                             switch (mScoAudioState) {
   2290                             case SCO_STATE_ACTIVATE_REQ:
   2291                                 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
   2292                                 if (mScoAudioMode == SCO_MODE_RAW) {
   2293                                     status = mBluetoothHeadset.connectAudio();
   2294                                 } else {
   2295                                     status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
   2296                                                                         mBluetoothHeadsetDevice);
   2297                                 }
   2298                                 break;
   2299                             case SCO_STATE_DEACTIVATE_REQ:
   2300                                 if (mScoAudioMode == SCO_MODE_RAW) {
   2301                                     status = mBluetoothHeadset.disconnectAudio();
   2302                                 } else {
   2303                                     status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
   2304                                                                         mBluetoothHeadsetDevice);
   2305                                 }
   2306                                 break;
   2307                             case SCO_STATE_DEACTIVATE_EXT_REQ:
   2308                                 status = mBluetoothHeadset.stopVoiceRecognition(
   2309                                         mBluetoothHeadsetDevice);
   2310                             }
   2311                         }
   2312                         if (!status) {
   2313                             sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED,
   2314                                     SENDMSG_REPLACE, 0, 0, null, 0);
   2315                         }
   2316                     }
   2317                 }
   2318                 break;
   2319 
   2320             default:
   2321                 break;
   2322             }
   2323         }
   2324         public void onServiceDisconnected(int profile) {
   2325             switch(profile) {
   2326             case BluetoothProfile.A2DP:
   2327                 synchronized (mConnectedDevices) {
   2328                     if (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)) {
   2329                         makeA2dpDeviceUnavailableNow(
   2330                                 mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
   2331                     }
   2332                 }
   2333                 break;
   2334 
   2335             case BluetoothProfile.HEADSET:
   2336                 synchronized (mScoClients) {
   2337                     mBluetoothHeadset = null;
   2338                 }
   2339                 break;
   2340 
   2341             default:
   2342                 break;
   2343             }
   2344         }
   2345     };
   2346 
   2347     /** see AudioManager.setRemoteSubmixOn(boolean on) */
   2348     public void setRemoteSubmixOn(boolean on, int address) {
   2349         sendMsg(mAudioHandler, MSG_SET_RSX_CONNECTION_STATE,
   2350                 SENDMSG_REPLACE /* replace with QUEUE when multiple addresses are supported */,
   2351                 on ? 1 : 0 /*arg1*/,
   2352                 address /*arg2*/,
   2353                 null/*obj*/, 0/*delay*/);
   2354     }
   2355 
   2356     private void onSetRsxConnectionState(int available, int address) {
   2357         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_REMOTE_SUBMIX,
   2358                 available == 1 ?
   2359                         AudioSystem.DEVICE_STATE_AVAILABLE : AudioSystem.DEVICE_STATE_UNAVAILABLE,
   2360                 String.valueOf(address) /*device_address*/);
   2361         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_REMOTE_SUBMIX,
   2362                 available == 1 ?
   2363                         AudioSystem.DEVICE_STATE_AVAILABLE : AudioSystem.DEVICE_STATE_UNAVAILABLE,
   2364                 String.valueOf(address) /*device_address*/);
   2365     }
   2366 
   2367     private void onCheckMusicActive() {
   2368         synchronized (mSafeMediaVolumeState) {
   2369             if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_INACTIVE) {
   2370                 int device = getDeviceForStream(AudioSystem.STREAM_MUSIC);
   2371 
   2372                 if ((device & mSafeMediaVolumeDevices) != 0) {
   2373                     sendMsg(mAudioHandler,
   2374                             MSG_CHECK_MUSIC_ACTIVE,
   2375                             SENDMSG_REPLACE,
   2376                             0,
   2377                             0,
   2378                             null,
   2379                             MUSIC_ACTIVE_POLL_PERIOD_MS);
   2380                     int index = mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(device);
   2381                     if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0) &&
   2382                             (index > mSafeMediaVolumeIndex)) {
   2383                         // Approximate cumulative active music time
   2384                         mMusicActiveMs += MUSIC_ACTIVE_POLL_PERIOD_MS;
   2385                         if (mMusicActiveMs > UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX) {
   2386                             setSafeMediaVolumeEnabled(true);
   2387                             mMusicActiveMs = 0;
   2388                         }
   2389                     }
   2390                 }
   2391             }
   2392         }
   2393     }
   2394 
   2395     private void onConfigureSafeVolume(boolean force) {
   2396         synchronized (mSafeMediaVolumeState) {
   2397             int mcc = mContext.getResources().getConfiguration().mcc;
   2398             if ((mMcc != mcc) || ((mMcc == 0) && force)) {
   2399                 mSafeMediaVolumeIndex = mContext.getResources().getInteger(
   2400                         com.android.internal.R.integer.config_safe_media_volume_index) * 10;
   2401                 boolean safeMediaVolumeEnabled = mContext.getResources().getBoolean(
   2402                         com.android.internal.R.bool.config_safe_media_volume_enabled);
   2403 
   2404                 // The persisted state is either "disabled" or "active": this is the state applied
   2405                 // next time we boot and cannot be "inactive"
   2406                 int persistedState;
   2407                 if (safeMediaVolumeEnabled) {
   2408                     persistedState = SAFE_MEDIA_VOLUME_ACTIVE;
   2409                     // The state can already be "inactive" here if the user has forced it before
   2410                     // the 30 seconds timeout for forced configuration. In this case we don't reset
   2411                     // it to "active".
   2412                     if (mSafeMediaVolumeState != SAFE_MEDIA_VOLUME_INACTIVE) {
   2413                         mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_ACTIVE;
   2414                         enforceSafeMediaVolume();
   2415                     }
   2416                 } else {
   2417                     persistedState = SAFE_MEDIA_VOLUME_DISABLED;
   2418                     mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_DISABLED;
   2419                 }
   2420                 mMcc = mcc;
   2421                 sendMsg(mAudioHandler,
   2422                         MSG_PERSIST_SAFE_VOLUME_STATE,
   2423                         SENDMSG_QUEUE,
   2424                         persistedState,
   2425                         0,
   2426                         null,
   2427                         0);
   2428             }
   2429         }
   2430     }
   2431 
   2432     ///////////////////////////////////////////////////////////////////////////
   2433     // Internal methods
   2434     ///////////////////////////////////////////////////////////////////////////
   2435 
   2436     /**
   2437      * Checks if the adjustment should change ringer mode instead of just
   2438      * adjusting volume. If so, this will set the proper ringer mode and volume
   2439      * indices on the stream states.
   2440      */
   2441     private boolean checkForRingerModeChange(int oldIndex, int direction,  int step) {
   2442         boolean adjustVolumeIndex = true;
   2443         int ringerMode = getRingerMode();
   2444 
   2445         switch (ringerMode) {
   2446         case RINGER_MODE_NORMAL:
   2447             if (direction == AudioManager.ADJUST_LOWER) {
   2448                 if (mHasVibrator) {
   2449                     // "step" is the delta in internal index units corresponding to a
   2450                     // change of 1 in UI index units.
   2451                     // Because of rounding when rescaling from one stream index range to its alias
   2452                     // index range, we cannot simply test oldIndex == step:
   2453                     //   (step <= oldIndex < 2 * step) is equivalent to: (old UI index == 1)
   2454                     if (step <= oldIndex && oldIndex < 2 * step) {
   2455                         ringerMode = RINGER_MODE_VIBRATE;
   2456                     }
   2457                 } else {
   2458                     // (oldIndex < step) is equivalent to (old UI index == 0)
   2459                     if ((oldIndex < step) && mPrevVolDirection != AudioManager.ADJUST_LOWER) {
   2460                         ringerMode = RINGER_MODE_SILENT;
   2461                     }
   2462                 }
   2463             }
   2464             break;
   2465         case RINGER_MODE_VIBRATE:
   2466             if (!mHasVibrator) {
   2467                 Log.e(TAG, "checkForRingerModeChange() current ringer mode is vibrate" +
   2468                         "but no vibrator is present");
   2469                 break;
   2470             }
   2471             if ((direction == AudioManager.ADJUST_LOWER)) {
   2472                 if (mPrevVolDirection != AudioManager.ADJUST_LOWER) {
   2473                     ringerMode = RINGER_MODE_SILENT;
   2474                 }
   2475             } else if (direction == AudioManager.ADJUST_RAISE) {
   2476                 ringerMode = RINGER_MODE_NORMAL;
   2477             }
   2478             adjustVolumeIndex = false;
   2479             break;
   2480         case RINGER_MODE_SILENT:
   2481             if (direction == AudioManager.ADJUST_RAISE) {
   2482                 if (mHasVibrator) {
   2483                     ringerMode = RINGER_MODE_VIBRATE;
   2484                 } else {
   2485                     ringerMode = RINGER_MODE_NORMAL;
   2486                 }
   2487             }
   2488             adjustVolumeIndex = false;
   2489             break;
   2490         default:
   2491             Log.e(TAG, "checkForRingerModeChange() wrong ringer mode: "+ringerMode);
   2492             break;
   2493         }
   2494 
   2495         setRingerMode(ringerMode);
   2496 
   2497         mPrevVolDirection = direction;
   2498 
   2499         return adjustVolumeIndex;
   2500     }
   2501 
   2502     public boolean isStreamAffectedByRingerMode(int streamType) {
   2503         return (mRingerModeAffectedStreams & (1 << streamType)) != 0;
   2504     }
   2505 
   2506     private boolean isStreamMutedByRingerMode(int streamType) {
   2507         return (mRingerModeMutedStreams & (1 << streamType)) != 0;
   2508     }
   2509 
   2510     public boolean isStreamAffectedByMute(int streamType) {
   2511         return (mMuteAffectedStreams & (1 << streamType)) != 0;
   2512     }
   2513 
   2514     private void ensureValidDirection(int direction) {
   2515         if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) {
   2516             throw new IllegalArgumentException("Bad direction " + direction);
   2517         }
   2518     }
   2519 
   2520     private void ensureValidSteps(int steps) {
   2521         if (Math.abs(steps) > MAX_BATCH_VOLUME_ADJUST_STEPS) {
   2522             throw new IllegalArgumentException("Bad volume adjust steps " + steps);
   2523         }
   2524     }
   2525 
   2526     private void ensureValidStreamType(int streamType) {
   2527         if (streamType < 0 || streamType >= mStreamStates.length) {
   2528             throw new IllegalArgumentException("Bad stream type " + streamType);
   2529         }
   2530     }
   2531 
   2532     private boolean isInCommunication() {
   2533         boolean isOffhook = false;
   2534 
   2535         if (mVoiceCapable) {
   2536             try {
   2537                 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
   2538                 if (phone != null) isOffhook = phone.isOffhook();
   2539             } catch (RemoteException e) {
   2540                 Log.w(TAG, "Couldn't connect to phone service", e);
   2541             }
   2542         }
   2543         return (isOffhook || getMode() == AudioManager.MODE_IN_COMMUNICATION);
   2544     }
   2545 
   2546     private int getActiveStreamType(int suggestedStreamType) {
   2547         if (mVoiceCapable) {
   2548             if (isInCommunication()) {
   2549                 if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
   2550                         == AudioSystem.FORCE_BT_SCO) {
   2551                     // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
   2552                     return AudioSystem.STREAM_BLUETOOTH_SCO;
   2553                 } else {
   2554                     // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
   2555                     return AudioSystem.STREAM_VOICE_CALL;
   2556                 }
   2557             } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
   2558                 // Having the suggested stream be USE_DEFAULT_STREAM_TYPE is how remote control
   2559                 // volume can have priority over STREAM_MUSIC
   2560                 if (checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
   2561                     if (DEBUG_VOL)
   2562                         Log.v(TAG, "getActiveStreamType: Forcing STREAM_REMOTE_MUSIC");
   2563                     return STREAM_REMOTE_MUSIC;
   2564                 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC,
   2565                             DEFAULT_STREAM_TYPE_OVERRIDE_DELAY_MS)) {
   2566                     if (DEBUG_VOL)
   2567                         Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC stream active");
   2568                     return AudioSystem.STREAM_MUSIC;
   2569                 } else {
   2570                     if (DEBUG_VOL)
   2571                         Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING b/c default");
   2572                     return AudioSystem.STREAM_RING;
   2573                 }
   2574             } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
   2575                 if (DEBUG_VOL)
   2576                     Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC stream active");
   2577                 return AudioSystem.STREAM_MUSIC;
   2578             } else {
   2579                 if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Returning suggested type "
   2580                         + suggestedStreamType);
   2581                 return suggestedStreamType;
   2582             }
   2583         } else {
   2584             if (isInCommunication()) {
   2585                 if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
   2586                         == AudioSystem.FORCE_BT_SCO) {
   2587                     if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO");
   2588                     return AudioSystem.STREAM_BLUETOOTH_SCO;
   2589                 } else {
   2590                     if (DEBUG_VOL)  Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL");
   2591                     return AudioSystem.STREAM_VOICE_CALL;
   2592                 }
   2593             } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_NOTIFICATION,
   2594                     DEFAULT_STREAM_TYPE_OVERRIDE_DELAY_MS) ||
   2595                     AudioSystem.isStreamActive(AudioSystem.STREAM_RING,
   2596                             DEFAULT_STREAM_TYPE_OVERRIDE_DELAY_MS)) {
   2597                 if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION");
   2598                 return AudioSystem.STREAM_NOTIFICATION;
   2599             } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
   2600                 if (checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
   2601                     // Having the suggested stream be USE_DEFAULT_STREAM_TYPE is how remote control
   2602                     // volume can have priority over STREAM_MUSIC
   2603                     if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_REMOTE_MUSIC");
   2604                     return STREAM_REMOTE_MUSIC;
   2605                 } else {
   2606                     if (DEBUG_VOL)
   2607                         Log.v(TAG, "getActiveStreamType: using STREAM_MUSIC as default");
   2608                     return AudioSystem.STREAM_MUSIC;
   2609                 }
   2610             } else {
   2611                 if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Returning suggested type "
   2612                         + suggestedStreamType);
   2613                 return suggestedStreamType;
   2614             }
   2615         }
   2616     }
   2617 
   2618     private void broadcastRingerMode(int ringerMode) {
   2619         // Send sticky broadcast
   2620         Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION);
   2621         broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, ringerMode);
   2622         broadcast.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
   2623                 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
   2624         sendStickyBroadcastToAll(broadcast);
   2625     }
   2626 
   2627     private void broadcastVibrateSetting(int vibrateType) {
   2628         // Send broadcast
   2629         if (ActivityManagerNative.isSystemReady()) {
   2630             Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
   2631             broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType);
   2632             broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType));
   2633             sendBroadcastToAll(broadcast);
   2634         }
   2635     }
   2636 
   2637     // Message helper methods
   2638     /**
   2639      * Queue a message on the given handler's message queue, after acquiring the service wake lock.
   2640      * Note that the wake lock needs to be released after the message has been handled.
   2641      */
   2642     private void queueMsgUnderWakeLock(Handler handler, int msg,
   2643             int arg1, int arg2, Object obj, int delay) {
   2644         mMediaEventWakeLock.acquire();
   2645         sendMsg(handler, msg, SENDMSG_QUEUE, arg1, arg2, obj, delay);
   2646     }
   2647 
   2648     private static void sendMsg(Handler handler, int msg,
   2649             int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {
   2650 
   2651         if (existingMsgPolicy == SENDMSG_REPLACE) {
   2652             handler.removeMessages(msg);
   2653         } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
   2654             return;
   2655         }
   2656 
   2657         handler.sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
   2658     }
   2659 
   2660     boolean checkAudioSettingsPermission(String method) {
   2661         if (mContext.checkCallingOrSelfPermission("android.permission.MODIFY_AUDIO_SETTINGS")
   2662                 == PackageManager.PERMISSION_GRANTED) {
   2663             return true;
   2664         }
   2665         String msg = "Audio Settings Permission Denial: " + method + " from pid="
   2666                 + Binder.getCallingPid()
   2667                 + ", uid=" + Binder.getCallingUid();
   2668         Log.w(TAG, msg);
   2669         return false;
   2670     }
   2671 
   2672     private int getDeviceForStream(int stream) {
   2673         int device = AudioSystem.getDevicesForStream(stream);
   2674         if ((device & (device - 1)) != 0) {
   2675             // Multiple device selection is either:
   2676             //  - speaker + one other device: give priority to speaker in this case.
   2677             //  - one A2DP device + another device: happens with duplicated output. In this case
   2678             // retain the device on the A2DP output as the other must not correspond to an active
   2679             // selection if not the speaker.
   2680             if ((device & AudioSystem.DEVICE_OUT_SPEAKER) != 0) {
   2681                 device = AudioSystem.DEVICE_OUT_SPEAKER;
   2682             } else {
   2683                 device &= AudioSystem.DEVICE_OUT_ALL_A2DP;
   2684             }
   2685         }
   2686         return device;
   2687     }
   2688 
   2689     public void setWiredDeviceConnectionState(int device, int state, String name) {
   2690         synchronized (mConnectedDevices) {
   2691             int delay = checkSendBecomingNoisyIntent(device, state);
   2692             queueMsgUnderWakeLock(mAudioHandler,
   2693                     MSG_SET_WIRED_DEVICE_CONNECTION_STATE,
   2694                     device,
   2695                     state,
   2696                     name,
   2697                     delay);
   2698         }
   2699     }
   2700 
   2701     public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state)
   2702     {
   2703         int delay;
   2704         synchronized (mConnectedDevices) {
   2705             delay = checkSendBecomingNoisyIntent(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
   2706                                             (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0);
   2707             queueMsgUnderWakeLock(mAudioHandler,
   2708                     MSG_SET_A2DP_CONNECTION_STATE,
   2709                     state,
   2710                     0,
   2711                     device,
   2712                     delay);
   2713         }
   2714         return delay;
   2715     }
   2716 
   2717     ///////////////////////////////////////////////////////////////////////////
   2718     // Inner classes
   2719     ///////////////////////////////////////////////////////////////////////////
   2720 
   2721     public class VolumeStreamState {
   2722         private final int mStreamType;
   2723 
   2724         private String mVolumeIndexSettingName;
   2725         private int mIndexMax;
   2726         private final ConcurrentHashMap<Integer, Integer> mIndex =
   2727                                             new ConcurrentHashMap<Integer, Integer>(8, 0.75f, 4);
   2728         private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo clients death
   2729 
   2730         private VolumeStreamState(String settingName, int streamType) {
   2731 
   2732             mVolumeIndexSettingName = settingName;
   2733 
   2734             mStreamType = streamType;
   2735             mIndexMax = MAX_STREAM_VOLUME[streamType];
   2736             AudioSystem.initStreamVolume(streamType, 0, mIndexMax);
   2737             mIndexMax *= 10;
   2738 
   2739             // mDeathHandlers must be created before calling readSettings()
   2740             mDeathHandlers = new ArrayList<VolumeDeathHandler>();
   2741 
   2742             readSettings();
   2743         }
   2744 
   2745         public String getSettingNameForDevice(int device) {
   2746             String name = mVolumeIndexSettingName;
   2747             String suffix = AudioSystem.getDeviceName(device);
   2748             if (suffix.isEmpty()) {
   2749                 return name;
   2750             }
   2751             return name + "_" + suffix;
   2752         }
   2753 
   2754         public synchronized void readSettings() {
   2755             // force maximum volume on all streams if fixed volume property is set
   2756             if (mUseFixedVolume) {
   2757                 mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, mIndexMax);
   2758                 return;
   2759             }
   2760             // do not read system stream volume from settings: this stream is always aliased
   2761             // to another stream type and its volume is never persisted. Values in settings can
   2762             // only be stale values
   2763             if ((mStreamType == AudioSystem.STREAM_SYSTEM) ||
   2764                     (mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED)) {
   2765                 int index = 10 * AudioManager.DEFAULT_STREAM_VOLUME[mStreamType];
   2766                 synchronized (mCameraSoundForced) {
   2767                     if (mCameraSoundForced) {
   2768                         index = mIndexMax;
   2769                     }
   2770                 }
   2771                 mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index);
   2772                 return;
   2773             }
   2774 
   2775             int remainingDevices = AudioSystem.DEVICE_OUT_ALL;
   2776 
   2777             for (int i = 0; remainingDevices != 0; i++) {
   2778                 int device = (1 << i);
   2779                 if ((device & remainingDevices) == 0) {
   2780                     continue;
   2781                 }
   2782                 remainingDevices &= ~device;
   2783 
   2784                 // retrieve current volume for device
   2785                 String name = getSettingNameForDevice(device);
   2786                 // if no volume stored for current stream and device, use default volume if default
   2787                 // device, continue otherwise
   2788                 int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT) ?
   2789                                         AudioManager.DEFAULT_STREAM_VOLUME[mStreamType] : -1;
   2790                 int index = Settings.System.getIntForUser(
   2791                         mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
   2792                 if (index == -1) {
   2793                     continue;
   2794                 }
   2795 
   2796                 // ignore settings for fixed volume devices: volume should always be at max or 0
   2797                 if ((mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_MUSIC) &&
   2798                         ((device & mFixedVolumeDevices) != 0)) {
   2799                     mIndex.put(device, (index != 0) ? mIndexMax : 0);
   2800                 } else {
   2801                     mIndex.put(device, getValidIndex(10 * index));
   2802                 }
   2803             }
   2804         }
   2805 
   2806         public void applyDeviceVolume(int device) {
   2807             int index;
   2808             if (isMuted()) {
   2809                 index = 0;
   2810             } else {
   2811                 index = (getIndex(device) + 5)/10;
   2812             }
   2813             AudioSystem.setStreamVolumeIndex(mStreamType, index, device);
   2814         }
   2815 
   2816         public synchronized void applyAllVolumes() {
   2817             // apply default volume first: by convention this will reset all
   2818             // devices volumes in audio policy manager to the supplied value
   2819             int index;
   2820             if (isMuted()) {
   2821                 index = 0;
   2822             } else {
   2823                 index = (getIndex(AudioSystem.DEVICE_OUT_DEFAULT) + 5)/10;
   2824             }
   2825             AudioSystem.setStreamVolumeIndex(mStreamType, index, AudioSystem.DEVICE_OUT_DEFAULT);
   2826             // then apply device specific volumes
   2827             Set set = mIndex.entrySet();
   2828             Iterator i = set.iterator();
   2829             while (i.hasNext()) {
   2830                 Map.Entry entry = (Map.Entry)i.next();
   2831                 int device = ((Integer)entry.getKey()).intValue();
   2832                 if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
   2833                     if (isMuted()) {
   2834                         index = 0;
   2835                     } else {
   2836                         index = ((Integer)entry.getValue() + 5)/10;
   2837                     }
   2838                     AudioSystem.setStreamVolumeIndex(mStreamType, index, device);
   2839                 }
   2840             }
   2841         }
   2842 
   2843         public boolean adjustIndex(int deltaIndex, int device) {
   2844             return setIndex(getIndex(device) + deltaIndex,
   2845                             device);
   2846         }
   2847 
   2848         public synchronized boolean setIndex(int index, int device) {
   2849             int oldIndex = getIndex(device);
   2850             index = getValidIndex(index);
   2851             synchronized (mCameraSoundForced) {
   2852                 if ((mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) && mCameraSoundForced) {
   2853                     index = mIndexMax;
   2854                 }
   2855             }
   2856             mIndex.put(device, index);
   2857 
   2858             if (oldIndex != index) {
   2859                 // Apply change to all streams using this one as alias
   2860                 // if changing volume of current device, also change volume of current
   2861                 // device on aliased stream
   2862                 boolean currentDevice = (device == getDeviceForStream(mStreamType));
   2863                 int numStreamTypes = AudioSystem.getNumStreamTypes();
   2864                 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
   2865                     if (streamType != mStreamType &&
   2866                             mStreamVolumeAlias[streamType] == mStreamType) {
   2867                         int scaledIndex = rescaleIndex(index, mStreamType, streamType);
   2868                         mStreamStates[streamType].setIndex(scaledIndex,
   2869                                                            device);
   2870                         if (currentDevice) {
   2871                             mStreamStates[streamType].setIndex(scaledIndex,
   2872                                                                getDeviceForStream(streamType));
   2873                         }
   2874                     }
   2875                 }
   2876                 return true;
   2877             } else {
   2878                 return false;
   2879             }
   2880         }
   2881 
   2882         public synchronized int getIndex(int device) {
   2883             Integer index = mIndex.get(device);
   2884             if (index == null) {
   2885                 // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
   2886                 index = mIndex.get(AudioSystem.DEVICE_OUT_DEFAULT);
   2887             }
   2888             return index.intValue();
   2889         }
   2890 
   2891         public int getMaxIndex() {
   2892             return mIndexMax;
   2893         }
   2894 
   2895         public synchronized void setAllIndexes(VolumeStreamState srcStream) {
   2896             Set set = srcStream.mIndex.entrySet();
   2897             Iterator i = set.iterator();
   2898             while (i.hasNext()) {
   2899                 Map.Entry entry = (Map.Entry)i.next();
   2900                 int device = ((Integer)entry.getKey()).intValue();
   2901                 int index = ((Integer)entry.getValue()).intValue();
   2902                 index = rescaleIndex(index, srcStream.getStreamType(), mStreamType);
   2903 
   2904                 setIndex(index, device);
   2905             }
   2906         }
   2907 
   2908         public synchronized void setAllIndexesToMax() {
   2909             Set set = mIndex.entrySet();
   2910             Iterator i = set.iterator();
   2911             while (i.hasNext()) {
   2912                 Map.Entry entry = (Map.Entry)i.next();
   2913                 entry.setValue(mIndexMax);
   2914             }
   2915         }
   2916 
   2917         public synchronized void mute(IBinder cb, boolean state) {
   2918             VolumeDeathHandler handler = getDeathHandler(cb, state);
   2919             if (handler == null) {
   2920                 Log.e(TAG, "Could not get client death handler for stream: "+mStreamType);
   2921                 return;
   2922             }
   2923             handler.mute(state);
   2924         }
   2925 
   2926         public int getStreamType() {
   2927             return mStreamType;
   2928         }
   2929 
   2930         private int getValidIndex(int index) {
   2931             if (index < 0) {
   2932                 return 0;
   2933             } else if (mUseFixedVolume || index > mIndexMax) {
   2934                 return mIndexMax;
   2935             }
   2936 
   2937             return index;
   2938         }
   2939 
   2940         private class VolumeDeathHandler implements IBinder.DeathRecipient {
   2941             private IBinder mICallback; // To be notified of client's death
   2942             private int mMuteCount; // Number of active mutes for this client
   2943 
   2944             VolumeDeathHandler(IBinder cb) {
   2945                 mICallback = cb;
   2946             }
   2947 
   2948             // must be called while synchronized on parent VolumeStreamState
   2949             public void mute(boolean state) {
   2950                 boolean updateVolume = false;
   2951                 if (state) {
   2952                     if (mMuteCount == 0) {
   2953                         // Register for client death notification
   2954                         try {
   2955                             // mICallback can be 0 if muted by AudioService
   2956                             if (mICallback != null) {
   2957                                 mICallback.linkToDeath(this, 0);
   2958                             }
   2959                             VolumeStreamState.this.mDeathHandlers.add(this);
   2960                             // If the stream is not yet muted by any client, set level to 0
   2961                             if (!VolumeStreamState.this.isMuted()) {
   2962                                 updateVolume = true;
   2963                             }
   2964                         } catch (RemoteException e) {
   2965                             // Client has died!
   2966                             binderDied();
   2967                             return;
   2968                         }
   2969                     } else {
   2970                         Log.w(TAG, "stream: "+mStreamType+" was already muted by this client");
   2971                     }
   2972                     mMuteCount++;
   2973                 } else {
   2974                     if (mMuteCount == 0) {
   2975                         Log.e(TAG, "unexpected unmute for stream: "+mStreamType);
   2976                     } else {
   2977                         mMuteCount--;
   2978                         if (mMuteCount == 0) {
   2979                             // Unregister from client death notification
   2980                             VolumeStreamState.this.mDeathHandlers.remove(this);
   2981                             // mICallback can be 0 if muted by AudioService
   2982                             if (mICallback != null) {
   2983                                 mICallback.unlinkToDeath(this, 0);
   2984                             }
   2985                             if (!VolumeStreamState.this.isMuted()) {
   2986                                 updateVolume = true;
   2987                             }
   2988                         }
   2989                     }
   2990                 }
   2991                 if (updateVolume) {
   2992                     sendMsg(mAudioHandler,
   2993                             MSG_SET_ALL_VOLUMES,
   2994                             SENDMSG_QUEUE,
   2995                             0,
   2996                             0,
   2997                             VolumeStreamState.this, 0);
   2998                 }
   2999             }
   3000 
   3001             public void binderDied() {
   3002                 Log.w(TAG, "Volume service client died for stream: "+mStreamType);
   3003                 if (mMuteCount != 0) {
   3004                     // Reset all active mute requests from this client.
   3005                     mMuteCount = 1;
   3006                     mute(false);
   3007                 }
   3008             }
   3009         }
   3010 
   3011         private synchronized int muteCount() {
   3012             int count = 0;
   3013             int size = mDeathHandlers.size();
   3014             for (int i = 0; i < size; i++) {
   3015                 count += mDeathHandlers.get(i).mMuteCount;
   3016             }
   3017             return count;
   3018         }
   3019 
   3020         private synchronized boolean isMuted() {
   3021             return muteCount() != 0;
   3022         }
   3023 
   3024         // only called by mute() which is already synchronized
   3025         private VolumeDeathHandler getDeathHandler(IBinder cb, boolean state) {
   3026             VolumeDeathHandler handler;
   3027             int size = mDeathHandlers.size();
   3028             for (int i = 0; i < size; i++) {
   3029                 handler = mDeathHandlers.get(i);
   3030                 if (cb == handler.mICallback) {
   3031                     return handler;
   3032                 }
   3033             }
   3034             // If this is the first mute request for this client, create a new
   3035             // client death handler. Otherwise, it is an out of sequence unmute request.
   3036             if (state) {
   3037                 handler = new VolumeDeathHandler(cb);
   3038             } else {
   3039                 Log.w(TAG, "stream was not muted by this client");
   3040                 handler = null;
   3041             }
   3042             return handler;
   3043         }
   3044 
   3045         private void dump(PrintWriter pw) {
   3046             pw.print("   Mute count: ");
   3047             pw.println(muteCount());
   3048             pw.print("   Current: ");
   3049             Set set = mIndex.entrySet();
   3050             Iterator i = set.iterator();
   3051             while (i.hasNext()) {
   3052                 Map.Entry entry = (Map.Entry)i.next();
   3053                 pw.print(Integer.toHexString(((Integer)entry.getKey()).intValue())
   3054                              + ": " + ((((Integer)entry.getValue()).intValue() + 5) / 10)+", ");
   3055             }
   3056         }
   3057     }
   3058 
   3059     /** Thread that handles native AudioSystem control. */
   3060     private class AudioSystemThread extends Thread {
   3061         AudioSystemThread() {
   3062             super("AudioService");
   3063         }
   3064 
   3065         @Override
   3066         public void run() {
   3067             // Set this thread up so the handler will work on it
   3068             Looper.prepare();
   3069 
   3070             synchronized(AudioService.this) {
   3071                 mAudioHandler = new AudioHandler();
   3072 
   3073                 // Notify that the handler has been created
   3074                 AudioService.this.notify();
   3075             }
   3076 
   3077             // Listen for volume change requests that are set by VolumePanel
   3078             Looper.loop();
   3079         }
   3080     }
   3081 
   3082     /** Handles internal volume messages in separate volume thread. */
   3083     private class AudioHandler extends Handler {
   3084 
   3085         private void setDeviceVolume(VolumeStreamState streamState, int device) {
   3086 
   3087             // Apply volume
   3088             streamState.applyDeviceVolume(device);
   3089 
   3090             // Apply change to all streams using this one as alias
   3091             int numStreamTypes = AudioSystem.getNumStreamTypes();
   3092             for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
   3093                 if (streamType != streamState.mStreamType &&
   3094                         mStreamVolumeAlias[streamType] == streamState.mStreamType) {
   3095                     mStreamStates[streamType].applyDeviceVolume(getDeviceForStream(streamType));
   3096                 }
   3097             }
   3098 
   3099             // Post a persist volume msg
   3100             sendMsg(mAudioHandler,
   3101                     MSG_PERSIST_VOLUME,
   3102                     SENDMSG_QUEUE,
   3103                     device,
   3104                     0,
   3105                     streamState,
   3106                     PERSIST_DELAY);
   3107 
   3108         }
   3109 
   3110         private void setAllVolumes(VolumeStreamState streamState) {
   3111 
   3112             // Apply volume
   3113             streamState.applyAllVolumes();
   3114 
   3115             // Apply change to all streams using this one as alias
   3116             int numStreamTypes = AudioSystem.getNumStreamTypes();
   3117             for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
   3118                 if (streamType != streamState.mStreamType &&
   3119                         mStreamVolumeAlias[streamType] == streamState.mStreamType) {
   3120                     mStreamStates[streamType].applyAllVolumes();
   3121                 }
   3122             }
   3123         }
   3124 
   3125         private void persistVolume(VolumeStreamState streamState, int device) {
   3126             if (mUseFixedVolume) {
   3127                 return;
   3128             }
   3129             System.putIntForUser(mContentResolver,
   3130                       streamState.getSettingNameForDevice(device),
   3131                       (streamState.getIndex(device) + 5)/ 10,
   3132                       UserHandle.USER_CURRENT);
   3133         }
   3134 
   3135         private void persistRingerMode(int ringerMode) {
   3136             if (mUseFixedVolume) {
   3137                 return;
   3138             }
   3139             Settings.Global.putInt(mContentResolver, Settings.Global.MODE_RINGER, ringerMode);
   3140         }
   3141 
   3142         private boolean onLoadSoundEffects() {
   3143             int status;
   3144 
   3145             synchronized (mSoundEffectsLock) {
   3146                 if (!mBootCompleted) {
   3147                     Log.w(TAG, "onLoadSoundEffects() called before boot complete");
   3148                     return false;
   3149                 }
   3150 
   3151                 if (mSoundPool != null) {
   3152                     return true;
   3153                 }
   3154 
   3155                 loadTouchSoundAssets();
   3156 
   3157                 mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
   3158                 mSoundPoolCallBack = null;
   3159                 mSoundPoolListenerThread = new SoundPoolListenerThread();
   3160                 mSoundPoolListenerThread.start();
   3161                 int attempts = 3;
   3162                 while ((mSoundPoolCallBack == null) && (attempts-- > 0)) {
   3163                     try {
   3164                         // Wait for mSoundPoolCallBack to be set by the other thread
   3165                         mSoundEffectsLock.wait(SOUND_EFECTS_LOAD_TIMEOUT_MS);
   3166                     } catch (InterruptedException e) {
   3167                         Log.w(TAG, "Interrupted while waiting sound pool listener thread.");
   3168                     }
   3169                 }
   3170 
   3171                 if (mSoundPoolCallBack == null) {
   3172                     Log.w(TAG, "onLoadSoundEffects() SoundPool listener or thread creation error");
   3173                     if (mSoundPoolLooper != null) {
   3174                         mSoundPoolLooper.quit();
   3175                         mSoundPoolLooper = null;
   3176                     }
   3177                     mSoundPoolListenerThread = null;
   3178                     mSoundPool.release();
   3179                     mSoundPool = null;
   3180                     return false;
   3181                 }
   3182                 /*
   3183                  * poolId table: The value -1 in this table indicates that corresponding
   3184                  * file (same index in SOUND_EFFECT_FILES[] has not been loaded.
   3185                  * Once loaded, the value in poolId is the sample ID and the same
   3186                  * sample can be reused for another effect using the same file.
   3187                  */
   3188                 int[] poolId = new int[SOUND_EFFECT_FILES.size()];
   3189                 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.size(); fileIdx++) {
   3190                     poolId[fileIdx] = -1;
   3191                 }
   3192                 /*
   3193                  * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded.
   3194                  * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0:
   3195                  * this indicates we have a valid sample loaded for this effect.
   3196                  */
   3197 
   3198                 int numSamples = 0;
   3199                 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
   3200                     // Do not load sample if this effect uses the MediaPlayer
   3201                     if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) {
   3202                         continue;
   3203                     }
   3204                     if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
   3205                         String filePath = Environment.getRootDirectory()
   3206                                 + SOUND_EFFECTS_PATH
   3207                                 + SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effect][0]);
   3208                         int sampleId = mSoundPool.load(filePath, 0);
   3209                         if (sampleId <= 0) {
   3210                             Log.w(TAG, "Soundpool could not load file: "+filePath);
   3211                         } else {
   3212                             SOUND_EFFECT_FILES_MAP[effect][1] = sampleId;
   3213                             poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId;
   3214                             numSamples++;
   3215                         }
   3216                     } else {
   3217                         SOUND_EFFECT_FILES_MAP[effect][1] =
   3218                                 poolId[SOUND_EFFECT_FILES_MAP[effect][0]];
   3219                     }
   3220                 }
   3221                 // wait for all samples to be loaded
   3222                 if (numSamples > 0) {
   3223                     mSoundPoolCallBack.setSamples(poolId);
   3224 
   3225                     attempts = 3;
   3226                     status = 1;
   3227                     while ((status == 1) && (attempts-- > 0)) {
   3228                         try {
   3229                             mSoundEffectsLock.wait(SOUND_EFECTS_LOAD_TIMEOUT_MS);
   3230                             status = mSoundPoolCallBack.status();
   3231                         } catch (InterruptedException e) {
   3232                             Log.w(TAG, "Interrupted while waiting sound pool callback.");
   3233                         }
   3234                     }
   3235                 } else {
   3236                     status = -1;
   3237                 }
   3238 
   3239                 if (mSoundPoolLooper != null) {
   3240                     mSoundPoolLooper.quit();
   3241                     mSoundPoolLooper = null;
   3242                 }
   3243                 mSoundPoolListenerThread = null;
   3244                 if (status != 0) {
   3245                     Log.w(TAG,
   3246                             "onLoadSoundEffects(), Error "+status+ " while loading samples");
   3247                     for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
   3248                         if (SOUND_EFFECT_FILES_MAP[effect][1] > 0) {
   3249                             SOUND_EFFECT_FILES_MAP[effect][1] = -1;
   3250                         }
   3251                     }
   3252 
   3253                     mSoundPool.release();
   3254                     mSoundPool = null;
   3255                 }
   3256             }
   3257             return (status == 0);
   3258         }
   3259 
   3260         /**
   3261          *  Unloads samples from the sound pool.
   3262          *  This method can be called to free some memory when
   3263          *  sound effects are disabled.
   3264          */
   3265         private void onUnloadSoundEffects() {
   3266             synchronized (mSoundEffectsLock) {
   3267                 if (mSoundPool == null) {
   3268                     return;
   3269                 }
   3270 
   3271                 int[] poolId = new int[SOUND_EFFECT_FILES.size()];
   3272                 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.size(); fileIdx++) {
   3273                     poolId[fileIdx] = 0;
   3274                 }
   3275 
   3276                 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
   3277                     if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) {
   3278                         continue;
   3279                     }
   3280                     if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) {
   3281                         mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]);
   3282                         SOUND_EFFECT_FILES_MAP[effect][1] = -1;
   3283                         poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1;
   3284                     }
   3285                 }
   3286                 mSoundPool.release();
   3287                 mSoundPool = null;
   3288             }
   3289         }
   3290 
   3291         private void onPlaySoundEffect(int effectType, int volume) {
   3292             synchronized (mSoundEffectsLock) {
   3293 
   3294                 onLoadSoundEffects();
   3295 
   3296                 if (mSoundPool == null) {
   3297                     return;
   3298                 }
   3299                 float volFloat;
   3300                 // use default if volume is not specified by caller
   3301                 if (volume < 0) {
   3302                     volFloat = (float)Math.pow(10, (float)sSoundEffectVolumeDb/20);
   3303                 } else {
   3304                     volFloat = (float) volume / 1000.0f;
   3305                 }
   3306 
   3307                 if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) {
   3308                     mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1],
   3309                                         volFloat, volFloat, 0, 0, 1.0f);
   3310                 } else {
   3311                     MediaPlayer mediaPlayer = new MediaPlayer();
   3312                     try {
   3313                         String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH +
   3314                                     SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effectType][0]);
   3315                         mediaPlayer.setDataSource(filePath);
   3316                         mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM);
   3317                         mediaPlayer.prepare();
   3318                         mediaPlayer.setVolume(volFloat);
   3319                         mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
   3320                             public void onCompletion(MediaPlayer mp) {
   3321                                 cleanupPlayer(mp);
   3322                             }
   3323                         });
   3324                         mediaPlayer.setOnErrorListener(new OnErrorListener() {
   3325                             public boolean onError(MediaPlayer mp, int what, int extra) {
   3326                                 cleanupPlayer(mp);
   3327                                 return true;
   3328                             }
   3329                         });
   3330                         mediaPlayer.start();
   3331                     } catch (IOException ex) {
   3332                         Log.w(TAG, "MediaPlayer IOException: "+ex);
   3333                     } catch (IllegalArgumentException ex) {
   3334                         Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex);
   3335                     } catch (IllegalStateException ex) {
   3336                         Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
   3337                     }
   3338                 }
   3339             }
   3340         }
   3341 
   3342         private void onHandlePersistMediaButtonReceiver(ComponentName receiver) {
   3343             Settings.System.putStringForUser(mContentResolver,
   3344                                              Settings.System.MEDIA_BUTTON_RECEIVER,
   3345                                              receiver == null ? "" : receiver.flattenToString(),
   3346                                              UserHandle.USER_CURRENT);
   3347         }
   3348 
   3349         private void cleanupPlayer(MediaPlayer mp) {
   3350             if (mp != null) {
   3351                 try {
   3352                     mp.stop();
   3353                     mp.release();
   3354                 } catch (IllegalStateException ex) {
   3355                     Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
   3356                 }
   3357             }
   3358         }
   3359 
   3360         private void setForceUse(int usage, int config) {
   3361             AudioSystem.setForceUse(usage, config);
   3362         }
   3363 
   3364         private void onPersistSafeVolumeState(int state) {
   3365             Settings.Global.putInt(mContentResolver,
   3366                     Settings.Global.AUDIO_SAFE_VOLUME_STATE,
   3367                     state);
   3368         }
   3369 
   3370         @Override
   3371         public void handleMessage(Message msg) {
   3372 
   3373             switch (msg.what) {
   3374 
   3375                 case MSG_SET_DEVICE_VOLUME:
   3376                     setDeviceVolume((VolumeStreamState) msg.obj, msg.arg1);
   3377                     break;
   3378 
   3379                 case MSG_SET_ALL_VOLUMES:
   3380                     setAllVolumes((VolumeStreamState) msg.obj);
   3381                     break;
   3382 
   3383                 case MSG_PERSIST_VOLUME:
   3384                     persistVolume((VolumeStreamState) msg.obj, msg.arg1);
   3385                     break;
   3386 
   3387                 case MSG_PERSIST_MASTER_VOLUME:
   3388                     if (mUseFixedVolume) {
   3389                         return;
   3390                     }
   3391                     Settings.System.putFloatForUser(mContentResolver,
   3392                                                     Settings.System.VOLUME_MASTER,
   3393                                                     (float)msg.arg1 / (float)1000.0,
   3394                                                     UserHandle.USER_CURRENT);
   3395                     break;
   3396 
   3397                 case MSG_PERSIST_MASTER_VOLUME_MUTE:
   3398                     if (mUseFixedVolume) {
   3399                         return;
   3400                     }
   3401                     Settings.System.putIntForUser(mContentResolver,
   3402                                                  Settings.System.VOLUME_MASTER_MUTE,
   3403                                                  msg.arg1,
   3404                                                  UserHandle.USER_CURRENT);
   3405                     break;
   3406 
   3407                 case MSG_PERSIST_RINGER_MODE:
   3408                     // note that the value persisted is the current ringer mode, not the
   3409                     // value of ringer mode as of the time the request was made to persist
   3410                     persistRingerMode(getRingerMode());
   3411                     break;
   3412 
   3413                 case MSG_MEDIA_SERVER_DIED:
   3414                     if (!mMediaServerOk) {
   3415                         Log.e(TAG, "Media server died.");
   3416                         // Force creation of new IAudioFlinger interface so that we are notified
   3417                         // when new media_server process is back to life.
   3418                         AudioSystem.setErrorCallback(mAudioSystemCallback);
   3419                         sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SENDMSG_NOOP, 0, 0,
   3420                                 null, 500);
   3421                     }
   3422                     break;
   3423 
   3424                 case MSG_MEDIA_SERVER_STARTED:
   3425                     Log.e(TAG, "Media server started.");
   3426                     // indicate to audio HAL that we start the reconfiguration phase after a media
   3427                     // server crash
   3428                     // Note that MSG_MEDIA_SERVER_STARTED message is only received when the media server
   3429                     // process restarts after a crash, not the first time it is started.
   3430                     AudioSystem.setParameters("restarting=true");
   3431 
   3432                     // Restore device connection states
   3433                     synchronized (mConnectedDevices) {
   3434                         Set set = mConnectedDevices.entrySet();
   3435                         Iterator i = set.iterator();
   3436                         while (i.hasNext()) {
   3437                             Map.Entry device = (Map.Entry)i.next();
   3438                             AudioSystem.setDeviceConnectionState(
   3439                                                             ((Integer)device.getKey()).intValue(),
   3440                                                             AudioSystem.DEVICE_STATE_AVAILABLE,
   3441                                                             (String)device.getValue());
   3442                         }
   3443                     }
   3444                     // Restore call state
   3445                     AudioSystem.setPhoneState(mMode);
   3446 
   3447                     // Restore forced usage for communcations and record
   3448                     AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm);
   3449                     AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm);
   3450                     AudioSystem.setForceUse(AudioSystem.FOR_SYSTEM, mCameraSoundForced ?
   3451                                     AudioSystem.FORCE_SYSTEM_ENFORCED : AudioSystem.FORCE_NONE);
   3452 
   3453                     // Restore stream volumes
   3454                     int numStreamTypes = AudioSystem.getNumStreamTypes();
   3455                     for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
   3456                         VolumeStreamState streamState = mStreamStates[streamType];
   3457                         AudioSystem.initStreamVolume(streamType, 0, (streamState.mIndexMax + 5) / 10);
   3458 
   3459                         streamState.applyAllVolumes();
   3460                     }
   3461 
   3462                     // Restore ringer mode
   3463                     setRingerModeInt(getRingerMode(), false);
   3464 
   3465                     // Restore master volume
   3466                     restoreMasterVolume();
   3467 
   3468                     // Reset device orientation (if monitored for this device)
   3469                     if (mMonitorOrientation) {
   3470                         setOrientationForAudioSystem();
   3471                     }
   3472                     if (mMonitorRotation) {
   3473                         setRotationForAudioSystem();
   3474                     }
   3475 
   3476                     synchronized (mBluetoothA2dpEnabledLock) {
   3477                         AudioSystem.setForceUse(AudioSystem.FOR_MEDIA,
   3478                                 mBluetoothA2dpEnabled ?
   3479                                         AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP);
   3480                     }
   3481 
   3482                     synchronized (mSettingsLock) {
   3483                         AudioSystem.setForceUse(AudioSystem.FOR_DOCK,
   3484                                 mDockAudioMediaEnabled ?
   3485                                         AudioSystem.FORCE_ANALOG_DOCK : AudioSystem.FORCE_NONE);
   3486                     }
   3487 
   3488                     // indicate the end of reconfiguration phase to audio HAL
   3489                     AudioSystem.setParameters("restarting=false");
   3490                     break;
   3491 
   3492                 case MSG_UNLOAD_SOUND_EFFECTS:
   3493                     onUnloadSoundEffects();
   3494                     break;
   3495 
   3496                 case MSG_LOAD_SOUND_EFFECTS:
   3497                     //FIXME: onLoadSoundEffects() should be executed in a separate thread as it
   3498                     // can take several dozens of milliseconds to complete
   3499                     boolean loaded = onLoadSoundEffects();
   3500                     if (msg.obj != null) {
   3501                         LoadSoundEffectReply reply = (LoadSoundEffectReply)msg.obj;
   3502                         synchronized (reply) {
   3503                             reply.mStatus = loaded ? 0 : -1;
   3504                             reply.notify();
   3505                         }
   3506                     }
   3507                     break;
   3508 
   3509                 case MSG_PLAY_SOUND_EFFECT:
   3510                     onPlaySoundEffect(msg.arg1, msg.arg2);
   3511                     break;
   3512 
   3513                 case MSG_BTA2DP_DOCK_TIMEOUT:
   3514                     // msg.obj  == address of BTA2DP device
   3515                     synchronized (mConnectedDevices) {
   3516                         makeA2dpDeviceUnavailableNow( (String) msg.obj );
   3517                     }
   3518                     break;
   3519 
   3520                 case MSG_SET_FORCE_USE:
   3521                 case MSG_SET_FORCE_BT_A2DP_USE:
   3522                     setForceUse(msg.arg1, msg.arg2);
   3523                     break;
   3524 
   3525                 case MSG_PERSIST_MEDIABUTTONRECEIVER:
   3526                     onHandlePersistMediaButtonReceiver( (ComponentName) msg.obj );
   3527                     break;
   3528 
   3529                 case MSG_RCDISPLAY_CLEAR:
   3530                     onRcDisplayClear();
   3531                     break;
   3532 
   3533                 case MSG_RCDISPLAY_UPDATE:
   3534                     // msg.obj is guaranteed to be non null
   3535                     onRcDisplayUpdate( (RemoteControlStackEntry) msg.obj, msg.arg1);
   3536                     break;
   3537 
   3538                 case MSG_BT_HEADSET_CNCT_FAILED:
   3539                     resetBluetoothSco();
   3540                     break;
   3541 
   3542                 case MSG_SET_WIRED_DEVICE_CONNECTION_STATE:
   3543                     onSetWiredDeviceConnectionState(msg.arg1, msg.arg2, (String)msg.obj);
   3544                     mMediaEventWakeLock.release();
   3545                     break;
   3546 
   3547                 case MSG_SET_A2DP_CONNECTION_STATE:
   3548                     onSetA2dpConnectionState((BluetoothDevice)msg.obj, msg.arg1);
   3549                     mMediaEventWakeLock.release();
   3550                     break;
   3551 
   3552                 case MSG_REPORT_NEW_ROUTES: {
   3553                     int N = mRoutesObservers.beginBroadcast();
   3554                     if (N > 0) {
   3555                         AudioRoutesInfo routes;
   3556                         synchronized (mCurAudioRoutes) {
   3557                             routes = new AudioRoutesInfo(mCurAudioRoutes);
   3558                         }
   3559                         while (N > 0) {
   3560                             N--;
   3561                             IAudioRoutesObserver obs = mRoutesObservers.getBroadcastItem(N);
   3562                             try {
   3563                                 obs.dispatchAudioRoutesChanged(routes);
   3564                             } catch (RemoteException e) {
   3565                             }
   3566                         }
   3567                     }
   3568                     mRoutesObservers.finishBroadcast();
   3569                     break;
   3570                 }
   3571 
   3572                 case MSG_REEVALUATE_REMOTE:
   3573                     onReevaluateRemote();
   3574                     break;
   3575 
   3576                 case MSG_RCC_NEW_PLAYBACK_INFO:
   3577                     onNewPlaybackInfoForRcc(msg.arg1 /* rccId */, msg.arg2 /* key */,
   3578                             ((Integer)msg.obj).intValue() /* value */);
   3579                     break;
   3580                 case MSG_RCC_NEW_VOLUME_OBS:
   3581                     onRegisterVolumeObserverForRcc(msg.arg1 /* rccId */,
   3582                             (IRemoteVolumeObserver)msg.obj /* rvo */);
   3583                     break;
   3584                 case MSG_RCC_NEW_PLAYBACK_STATE:
   3585                     onNewPlaybackStateForRcc(msg.arg1 /* rccId */, msg.arg2 /* state */,
   3586                             (RccPlaybackState)msg.obj /* newState */);
   3587                     break;
   3588                 case MSG_RCC_SEEK_REQUEST:
   3589                     onSetRemoteControlClientPlaybackPosition(msg.arg1 /* generationId */,
   3590                             ((Long)msg.obj).longValue() /* timeMs */);
   3591 
   3592                 case MSG_SET_RSX_CONNECTION_STATE:
   3593                     onSetRsxConnectionState(msg.arg1/*available*/, msg.arg2/*address*/);
   3594                     break;
   3595 
   3596                 case MSG_CHECK_MUSIC_ACTIVE:
   3597                     onCheckMusicActive();
   3598                     break;
   3599 
   3600                 case MSG_BROADCAST_AUDIO_BECOMING_NOISY:
   3601                     onSendBecomingNoisyIntent();
   3602                     break;
   3603 
   3604                 case MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED:
   3605                 case MSG_CONFIGURE_SAFE_MEDIA_VOLUME:
   3606                     onConfigureSafeVolume((msg.what == MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED));
   3607                     break;
   3608                 case MSG_PERSIST_SAFE_VOLUME_STATE:
   3609                     onPersistSafeVolumeState(msg.arg1);
   3610                     break;
   3611 
   3612                 case MSG_PROMOTE_RCC:
   3613                     onPromoteRcc(msg.arg1);
   3614                     break;
   3615 
   3616                 case MSG_BROADCAST_BT_CONNECTION_STATE:
   3617                     onBroadcastScoConnectionState(msg.arg1);
   3618                     break;
   3619             }
   3620         }
   3621     }
   3622 
   3623     private class SettingsObserver extends ContentObserver {
   3624 
   3625         SettingsObserver() {
   3626             super(new Handler());
   3627             mContentResolver.registerContentObserver(Settings.System.getUriFor(
   3628                 Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this);
   3629             mContentResolver.registerContentObserver(Settings.Global.getUriFor(
   3630                 Settings.Global.DOCK_AUDIO_MEDIA_ENABLED), false, this);
   3631         }
   3632 
   3633         @Override
   3634         public void onChange(boolean selfChange) {
   3635             super.onChange(selfChange);
   3636             // FIXME This synchronized is not necessary if mSettingsLock only protects mRingerMode.
   3637             //       However there appear to be some missing locks around mRingerModeMutedStreams
   3638             //       and mRingerModeAffectedStreams, so will leave this synchronized for now.
   3639             //       mRingerModeMutedStreams and mMuteAffectedStreams are safe (only accessed once).
   3640             synchronized (mSettingsLock) {
   3641                 int ringerModeAffectedStreams = Settings.System.getIntForUser(mContentResolver,
   3642                        Settings.System.MODE_RINGER_STREAMS_AFFECTED,
   3643                        ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
   3644                        (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)),
   3645                        UserHandle.USER_CURRENT);
   3646                 if (mVoiceCapable) {
   3647                     ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
   3648                 } else {
   3649                     ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
   3650                 }
   3651                 synchronized (mCameraSoundForced) {
   3652                     if (mCameraSoundForced) {
   3653                         ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
   3654                     } else {
   3655                         ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
   3656                     }
   3657                 }
   3658                 if (ringerModeAffectedStreams != mRingerModeAffectedStreams) {
   3659                     /*
   3660                      * Ensure all stream types that should be affected by ringer mode
   3661                      * are in the proper state.
   3662                      */
   3663                     mRingerModeAffectedStreams = ringerModeAffectedStreams;
   3664                     setRingerModeInt(getRingerMode(), false);
   3665                 }
   3666                 readDockAudioSettings(mContentResolver);
   3667             }
   3668         }
   3669     }
   3670 
   3671     // must be called synchronized on mConnectedDevices
   3672     private void makeA2dpDeviceAvailable(String address) {
   3673         // enable A2DP before notifying A2DP connection to avoid unecessary processing in
   3674         // audio policy manager
   3675         setBluetoothA2dpOnInt(true);
   3676         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
   3677                 AudioSystem.DEVICE_STATE_AVAILABLE,
   3678                 address);
   3679         // Reset A2DP suspend state each time a new sink is connected
   3680         AudioSystem.setParameters("A2dpSuspended=false");
   3681         mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP),
   3682                 address);
   3683     }
   3684 
   3685     private void onSendBecomingNoisyIntent() {
   3686         sendBroadcastToAll(new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
   3687     }
   3688 
   3689     // must be called synchronized on mConnectedDevices
   3690     private void makeA2dpDeviceUnavailableNow(String address) {
   3691         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
   3692                 AudioSystem.DEVICE_STATE_UNAVAILABLE,
   3693                 address);
   3694         mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
   3695     }
   3696 
   3697     // must be called synchronized on mConnectedDevices
   3698     private void makeA2dpDeviceUnavailableLater(String address) {
   3699         // prevent any activity on the A2DP audio output to avoid unwanted
   3700         // reconnection of the sink.
   3701         AudioSystem.setParameters("A2dpSuspended=true");
   3702         // the device will be made unavailable later, so consider it disconnected right away
   3703         mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
   3704         // send the delayed message to make the device unavailable later
   3705         Message msg = mAudioHandler.obtainMessage(MSG_BTA2DP_DOCK_TIMEOUT, address);
   3706         mAudioHandler.sendMessageDelayed(msg, BTA2DP_DOCK_TIMEOUT_MILLIS);
   3707 
   3708     }
   3709 
   3710     // must be called synchronized on mConnectedDevices
   3711     private void cancelA2dpDeviceTimeout() {
   3712         mAudioHandler.removeMessages(MSG_BTA2DP_DOCK_TIMEOUT);
   3713     }
   3714 
   3715     // must be called synchronized on mConnectedDevices
   3716     private boolean hasScheduledA2dpDockTimeout() {
   3717         return mAudioHandler.hasMessages(MSG_BTA2DP_DOCK_TIMEOUT);
   3718     }
   3719 
   3720     private void onSetA2dpConnectionState(BluetoothDevice btDevice, int state)
   3721     {
   3722         if (btDevice == null) {
   3723             return;
   3724         }
   3725         String address = btDevice.getAddress();
   3726         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
   3727             address = "";
   3728         }
   3729         synchronized (mConnectedDevices) {
   3730             boolean isConnected =
   3731                 (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
   3732                  mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP).equals(address));
   3733 
   3734             if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
   3735                 if (btDevice.isBluetoothDock()) {
   3736                     if (state == BluetoothProfile.STATE_DISCONNECTED) {
   3737                         // introduction of a delay for transient disconnections of docks when
   3738                         // power is rapidly turned off/on, this message will be canceled if
   3739                         // we reconnect the dock under a preset delay
   3740                         makeA2dpDeviceUnavailableLater(address);
   3741                         // the next time isConnected is evaluated, it will be false for the dock
   3742                     }
   3743                 } else {
   3744                     makeA2dpDeviceUnavailableNow(address);
   3745                 }
   3746                 synchronized (mCurAudioRoutes) {
   3747                     if (mCurAudioRoutes.mBluetoothName != null) {
   3748                         mCurAudioRoutes.mBluetoothName = null;
   3749                         sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
   3750                                 SENDMSG_NOOP, 0, 0, null, 0);
   3751                     }
   3752                 }
   3753             } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
   3754                 if (btDevice.isBluetoothDock()) {
   3755                     // this could be a reconnection after a transient disconnection
   3756                     cancelA2dpDeviceTimeout();
   3757                     mDockAddress = address;
   3758                 } else {
   3759                     // this could be a connection of another A2DP device before the timeout of
   3760                     // a dock: cancel the dock timeout, and make the dock unavailable now
   3761                     if(hasScheduledA2dpDockTimeout()) {
   3762                         cancelA2dpDeviceTimeout();
   3763                         makeA2dpDeviceUnavailableNow(mDockAddress);
   3764                     }
   3765                 }
   3766                 makeA2dpDeviceAvailable(address);
   3767                 synchronized (mCurAudioRoutes) {
   3768                     String name = btDevice.getAliasName();
   3769                     if (!TextUtils.equals(mCurAudioRoutes.mBluetoothName, name)) {
   3770                         mCurAudioRoutes.mBluetoothName = name;
   3771                         sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
   3772                                 SENDMSG_NOOP, 0, 0, null, 0);
   3773                     }
   3774                 }
   3775             }
   3776         }
   3777     }
   3778 
   3779     private boolean handleDeviceConnection(boolean connected, int device, String params) {
   3780         synchronized (mConnectedDevices) {
   3781             boolean isConnected = (mConnectedDevices.containsKey(device) &&
   3782                     (params.isEmpty() || mConnectedDevices.get(device).equals(params)));
   3783 
   3784             if (isConnected && !connected) {
   3785                 AudioSystem.setDeviceConnectionState(device,
   3786                                               AudioSystem.DEVICE_STATE_UNAVAILABLE,
   3787                                               mConnectedDevices.get(device));
   3788                  mConnectedDevices.remove(device);
   3789                  return true;
   3790             } else if (!isConnected && connected) {
   3791                  AudioSystem.setDeviceConnectionState(device,
   3792                                                       AudioSystem.DEVICE_STATE_AVAILABLE,
   3793                                                       params);
   3794                  mConnectedDevices.put(new Integer(device), params);
   3795                  return true;
   3796             }
   3797         }
   3798         return false;
   3799     }
   3800 
   3801     // Devices which removal triggers intent ACTION_AUDIO_BECOMING_NOISY. The intent is only
   3802     // sent if none of these devices is connected.
   3803     int mBecomingNoisyIntentDevices =
   3804             AudioSystem.DEVICE_OUT_WIRED_HEADSET | AudioSystem.DEVICE_OUT_WIRED_HEADPHONE |
   3805             AudioSystem.DEVICE_OUT_ALL_A2DP | AudioSystem.DEVICE_OUT_AUX_DIGITAL |
   3806             AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET | AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET |
   3807             AudioSystem.DEVICE_OUT_ALL_USB;
   3808 
   3809     // must be called before removing the device from mConnectedDevices
   3810     private int checkSendBecomingNoisyIntent(int device, int state) {
   3811         int delay = 0;
   3812         if ((state == 0) && ((device & mBecomingNoisyIntentDevices) != 0)) {
   3813             int devices = 0;
   3814             for (int dev : mConnectedDevices.keySet()) {
   3815                 if ((dev & mBecomingNoisyIntentDevices) != 0) {
   3816                    devices |= dev;
   3817                 }
   3818             }
   3819             if (devices == device) {
   3820                 sendMsg(mAudioHandler,
   3821                         MSG_BROADCAST_AUDIO_BECOMING_NOISY,
   3822                         SENDMSG_REPLACE,
   3823                         0,
   3824                         0,
   3825                         null,
   3826                         0);
   3827                 delay = 1000;
   3828             }
   3829         }
   3830 
   3831         if (mAudioHandler.hasMessages(MSG_SET_A2DP_CONNECTION_STATE) ||
   3832                 mAudioHandler.hasMessages(MSG_SET_WIRED_DEVICE_CONNECTION_STATE)) {
   3833             delay = 1000;
   3834         }
   3835         return delay;
   3836     }
   3837 
   3838     private void sendDeviceConnectionIntent(int device, int state, String name)
   3839     {
   3840         Intent intent = new Intent();
   3841 
   3842         intent.putExtra("state", state);
   3843         intent.putExtra("name", name);
   3844         intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
   3845 
   3846         int connType = 0;
   3847 
   3848         if (device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) {
   3849             connType = AudioRoutesInfo.MAIN_HEADSET;
   3850             intent.setAction(Intent.ACTION_HEADSET_PLUG);
   3851             intent.putExtra("microphone", 1);
   3852         } else if (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) {
   3853             connType = AudioRoutesInfo.MAIN_HEADPHONES;
   3854             intent.setAction(Intent.ACTION_HEADSET_PLUG);
   3855             intent.putExtra("microphone", 0);
   3856         } else if (device == AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET) {
   3857             connType = AudioRoutesInfo.MAIN_DOCK_SPEAKERS;
   3858             intent.setAction(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG);
   3859         } else if (device == AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET) {
   3860             connType = AudioRoutesInfo.MAIN_DOCK_SPEAKERS;
   3861             intent.setAction(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG);
   3862         } else if (device == AudioSystem.DEVICE_OUT_AUX_DIGITAL) {
   3863             connType = AudioRoutesInfo.MAIN_HDMI;
   3864             intent.setAction(Intent.ACTION_HDMI_AUDIO_PLUG);
   3865         }
   3866 
   3867         synchronized (mCurAudioRoutes) {
   3868             if (connType != 0) {
   3869                 int newConn = mCurAudioRoutes.mMainType;
   3870                 if (state != 0) {
   3871                     newConn |= connType;
   3872                 } else {
   3873                     newConn &= ~connType;
   3874                 }
   3875                 if (newConn != mCurAudioRoutes.mMainType) {
   3876                     mCurAudioRoutes.mMainType = newConn;
   3877                     sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
   3878                             SENDMSG_NOOP, 0, 0, null, 0);
   3879                 }
   3880             }
   3881         }
   3882 
   3883         final long ident = Binder.clearCallingIdentity();
   3884         try {
   3885             ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);
   3886         } finally {
   3887             Binder.restoreCallingIdentity(ident);
   3888         }
   3889     }
   3890 
   3891     private void onSetWiredDeviceConnectionState(int device, int state, String name)
   3892     {
   3893         synchronized (mConnectedDevices) {
   3894             if ((state == 0) && ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
   3895                     (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE))) {
   3896                 setBluetoothA2dpOnInt(true);
   3897             }
   3898             boolean isUsb = ((device & AudioSystem.DEVICE_OUT_ALL_USB) != 0);
   3899             handleDeviceConnection((state == 1), device, (isUsb ? name : ""));
   3900             if (state != 0) {
   3901                 if ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
   3902                     (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE)) {
   3903                     setBluetoothA2dpOnInt(false);
   3904                 }
   3905                 if ((device & mSafeMediaVolumeDevices) != 0) {
   3906                     sendMsg(mAudioHandler,
   3907                             MSG_CHECK_MUSIC_ACTIVE,
   3908                             SENDMSG_REPLACE,
   3909                             0,
   3910                             0,
   3911                             null,
   3912                             MUSIC_ACTIVE_POLL_PERIOD_MS);
   3913                 }
   3914             }
   3915             if (!isUsb) {
   3916                 sendDeviceConnectionIntent(device, state, name);
   3917             }
   3918         }
   3919     }
   3920 
   3921     /* cache of the address of the last dock the device was connected to */
   3922     private String mDockAddress;
   3923 
   3924     /**
   3925      * Receiver for misc intent broadcasts the Phone app cares about.
   3926      */
   3927     private class AudioServiceBroadcastReceiver extends BroadcastReceiver {
   3928         @Override
   3929         public void onReceive(Context context, Intent intent) {
   3930             String action = intent.getAction();
   3931             int device;
   3932             int state;
   3933 
   3934             if (action.equals(Intent.ACTION_DOCK_EVENT)) {
   3935                 int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
   3936                         Intent.EXTRA_DOCK_STATE_UNDOCKED);
   3937                 int config;
   3938                 switch (dockState) {
   3939                     case Intent.EXTRA_DOCK_STATE_DESK:
   3940                         config = AudioSystem.FORCE_BT_DESK_DOCK;
   3941                         break;
   3942                     case Intent.EXTRA_DOCK_STATE_CAR:
   3943                         config = AudioSystem.FORCE_BT_CAR_DOCK;
   3944                         break;
   3945                     case Intent.EXTRA_DOCK_STATE_LE_DESK:
   3946                         config = AudioSystem.FORCE_ANALOG_DOCK;
   3947                         break;
   3948                     case Intent.EXTRA_DOCK_STATE_HE_DESK:
   3949                         config = AudioSystem.FORCE_DIGITAL_DOCK;
   3950                         break;
   3951                     case Intent.EXTRA_DOCK_STATE_UNDOCKED:
   3952                     default:
   3953                         config = AudioSystem.FORCE_NONE;
   3954                 }
   3955                 // Low end docks have a menu to enable or disable audio
   3956                 // (see mDockAudioMediaEnabled)
   3957                 if (!((dockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
   3958                       ((dockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) &&
   3959                        (mDockState == Intent.EXTRA_DOCK_STATE_LE_DESK)))) {
   3960                     AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
   3961                 }
   3962                 mDockState = dockState;
   3963             } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
   3964                 state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
   3965                                                BluetoothProfile.STATE_DISCONNECTED);
   3966                 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
   3967                 String address = null;
   3968 
   3969                 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
   3970                 if (btDevice == null) {
   3971                     return;
   3972                 }
   3973 
   3974                 address = btDevice.getAddress();
   3975                 BluetoothClass btClass = btDevice.getBluetoothClass();
   3976                 if (btClass != null) {
   3977                     switch (btClass.getDeviceClass()) {
   3978                     case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
   3979                     case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
   3980                         device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
   3981                         break;
   3982                     case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
   3983                         device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
   3984                         break;
   3985                     }
   3986                 }
   3987 
   3988                 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
   3989                     address = "";
   3990                 }
   3991 
   3992                 boolean connected = (state == BluetoothProfile.STATE_CONNECTED);
   3993                 if (handleDeviceConnection(connected, device, address)) {
   3994                     synchronized (mScoClients) {
   3995                         if (connected) {
   3996                             mBluetoothHeadsetDevice = btDevice;
   3997                         } else {
   3998                             mBluetoothHeadsetDevice = null;
   3999                             resetBluetoothSco();
   4000                         }
   4001                     }
   4002                 }
   4003             } else if (action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ||
   4004                            action.equals(Intent.ACTION_USB_AUDIO_DEVICE_PLUG)) {
   4005                 state = intent.getIntExtra("state", 0);
   4006                 int alsaCard = intent.getIntExtra("card", -1);
   4007                 int alsaDevice = intent.getIntExtra("device", -1);
   4008                 String params = (alsaCard == -1 && alsaDevice == -1 ? ""
   4009                                     : "card=" + alsaCard + ";device=" + alsaDevice);
   4010                 device = action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ?
   4011                         AudioSystem.DEVICE_OUT_USB_ACCESSORY : AudioSystem.DEVICE_OUT_USB_DEVICE;
   4012                 Log.v(TAG, "Broadcast Receiver: Got "
   4013                         + (action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ?
   4014                               "ACTION_USB_AUDIO_ACCESSORY_PLUG" : "ACTION_USB_AUDIO_DEVICE_PLUG")
   4015                         + ", state = " + state + ", card: " + alsaCard + ", device: " + alsaDevice);
   4016                 setWiredDeviceConnectionState(device, state, params);
   4017             } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
   4018                 boolean broadcast = false;
   4019                 int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
   4020                 synchronized (mScoClients) {
   4021                     int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
   4022                     // broadcast intent if the connection was initated by AudioService
   4023                     if (!mScoClients.isEmpty() &&
   4024                             (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
   4025                              mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
   4026                              mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
   4027                         broadcast = true;
   4028                     }
   4029                     switch (btState) {
   4030                     case BluetoothHeadset.STATE_AUDIO_CONNECTED:
   4031                         scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
   4032                         if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
   4033                             mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
   4034                             mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
   4035                             mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
   4036                         }
   4037                         break;
   4038                     case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
   4039                         scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
   4040                         mScoAudioState = SCO_STATE_INACTIVE;
   4041                         clearAllScoClients(0, false);
   4042                         break;
   4043                     case BluetoothHeadset.STATE_AUDIO_CONNECTING:
   4044                         if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
   4045                             mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
   4046                             mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
   4047                             mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
   4048                         }
   4049                     default:
   4050                         // do not broadcast CONNECTING or invalid state
   4051                         broadcast = false;
   4052                         break;
   4053                     }
   4054                 }
   4055                 if (broadcast) {
   4056                     broadcastScoConnectionState(scoAudioState);
   4057                     //FIXME: this is to maintain compatibility with deprecated intent
   4058                     // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
   4059                     Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
   4060                     newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, scoAudioState);
   4061                     sendStickyBroadcastToAll(newIntent);
   4062                 }
   4063             } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
   4064                 mBootCompleted = true;
   4065                 sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_QUEUE,
   4066                         0, 0, null, 0);
   4067 
   4068                 mKeyguardManager =
   4069                         (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
   4070                 mScoConnectionState = AudioManager.SCO_AUDIO_STATE_ERROR;
   4071                 resetBluetoothSco();
   4072                 getBluetoothHeadset();
   4073                 //FIXME: this is to maintain compatibility with deprecated intent
   4074                 // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
   4075                 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
   4076                 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
   4077                         AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
   4078                 sendStickyBroadcastToAll(newIntent);
   4079 
   4080                 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
   4081                 if (adapter != null) {
   4082                     adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
   4083                                             BluetoothProfile.A2DP);
   4084                 }
   4085 
   4086                 sendMsg(mAudioHandler,
   4087                         MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED,
   4088                         SENDMSG_REPLACE,
   4089                         0,
   4090                         0,
   4091                         null,
   4092                         SAFE_VOLUME_CONFIGURE_TIMEOUT_MS);
   4093             } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
   4094                     || action.equals(Intent.ACTION_PACKAGE_DATA_CLEARED)) {
   4095                 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
   4096                     // a package is being removed, not replaced
   4097                     String packageName = intent.getData().getSchemeSpecificPart();
   4098                     if (packageName != null) {
   4099                         cleanupMediaButtonReceiverForPackage(packageName, true);
   4100                     }
   4101                 }
   4102             } else if (action.equals(Intent.ACTION_PACKAGE_ADDED)
   4103                     || action.equals(Intent.ACTION_PACKAGE_CHANGED)) {
   4104                 String packageName = intent.getData().getSchemeSpecificPart();
   4105                 if (packageName != null) {
   4106                     cleanupMediaButtonReceiverForPackage(packageName, false);
   4107                 }
   4108             } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
   4109                 AudioSystem.setParameters("screen_state=on");
   4110             } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
   4111                 AudioSystem.setParameters("screen_state=off");
   4112             } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
   4113                 handleConfigurationChanged(context);
   4114             } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
   4115                 // attempt to stop music playback for background user
   4116                 sendMsg(mAudioHandler,
   4117                         MSG_BROADCAST_AUDIO_BECOMING_NOISY,
   4118                         SENDMSG_REPLACE,
   4119                         0,
   4120                         0,
   4121                         null,
   4122                         0);
   4123                 // the current audio focus owner is no longer valid
   4124                 discardAudioFocusOwner();
   4125 
   4126                 // load volume settings for new user
   4127                 readAudioSettings(true /*userSwitch*/);
   4128                 // preserve STREAM_MUSIC volume from one user to the next.
   4129                 sendMsg(mAudioHandler,
   4130                         MSG_SET_ALL_VOLUMES,
   4131                         SENDMSG_QUEUE,
   4132                         0,
   4133                         0,
   4134                         mStreamStates[AudioSystem.STREAM_MUSIC], 0);
   4135             }
   4136         }
   4137     }
   4138 
   4139     //==========================================================================================
   4140     // AudioFocus
   4141     //==========================================================================================
   4142 
   4143     /* constant to identify focus stack entry that is used to hold the focus while the phone
   4144      * is ringing or during a call. Used by com.android.internal.telephony.CallManager when
   4145      * entering and exiting calls.
   4146      */
   4147     public final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls";
   4148 
   4149     private final static Object mAudioFocusLock = new Object();
   4150 
   4151     private final static Object mRingingLock = new Object();
   4152 
   4153     private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
   4154         @Override
   4155         public void onCallStateChanged(int state, String incomingNumber) {
   4156             if (state == TelephonyManager.CALL_STATE_RINGING) {
   4157                 //Log.v(TAG, " CALL_STATE_RINGING");
   4158                 synchronized(mRingingLock) {
   4159                     mIsRinging = true;
   4160                 }
   4161             } else if ((state == TelephonyManager.CALL_STATE_OFFHOOK)
   4162                     || (state == TelephonyManager.CALL_STATE_IDLE)) {
   4163                 synchronized(mRingingLock) {
   4164                     mIsRinging = false;
   4165                 }
   4166             }
   4167         }
   4168     };
   4169 
   4170     /**
   4171      * Discard the current audio focus owner.
   4172      * Notify top of audio focus stack that it lost focus (regardless of possibility to reassign
   4173      * focus), remove it from the stack, and clear the remote control display.
   4174      */
   4175     private void discardAudioFocusOwner() {
   4176         synchronized(mAudioFocusLock) {
   4177             if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
   4178                 // notify the current focus owner it lost focus after removing it from stack
   4179                 FocusStackEntry focusOwner = mFocusStack.pop();
   4180                 try {
   4181                     focusOwner.mFocusDispatcher.dispatchAudioFocusChange(
   4182                             AudioManager.AUDIOFOCUS_LOSS, focusOwner.mClientId);
   4183                 } catch (RemoteException e) {
   4184                     Log.e(TAG, "Failure to signal loss of audio focus due to "+ e);
   4185                     e.printStackTrace();
   4186                 }
   4187                 focusOwner.unlinkToDeath();
   4188                 // clear RCD
   4189                 synchronized(mRCStack) {
   4190                     clearRemoteControlDisplay_syncAfRcs();
   4191                 }
   4192             }
   4193         }
   4194     }
   4195 
   4196     private void notifyTopOfAudioFocusStack() {
   4197         // notify the top of the stack it gained focus
   4198         if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
   4199             if (canReassignAudioFocus()) {
   4200                 try {
   4201                     mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
   4202                             AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId);
   4203                 } catch (RemoteException e) {
   4204                     Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e);
   4205                     e.printStackTrace();
   4206                 }
   4207             }
   4208         }
   4209     }
   4210 
   4211     private static class FocusStackEntry {
   4212         public int mStreamType = -1;// no stream type
   4213         public IAudioFocusDispatcher mFocusDispatcher = null;
   4214         public IBinder mSourceRef = null;
   4215         public String mClientId;
   4216         public int mFocusChangeType;
   4217         public AudioFocusDeathHandler mHandler;
   4218         public String mPackageName;
   4219         public int mCallingUid;
   4220 
   4221         public FocusStackEntry() {
   4222         }
   4223 
   4224         public FocusStackEntry(int streamType, int duration,
   4225                 IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr,
   4226                 String pn, int uid) {
   4227             mStreamType = streamType;
   4228             mFocusDispatcher = afl;
   4229             mSourceRef = source;
   4230             mClientId = id;
   4231             mFocusChangeType = duration;
   4232             mHandler = hdlr;
   4233             mPackageName = pn;
   4234             mCallingUid = uid;
   4235         }
   4236 
   4237         public void unlinkToDeath() {
   4238             try {
   4239                 if (mSourceRef != null && mHandler != null) {
   4240                     mSourceRef.unlinkToDeath(mHandler, 0);
   4241                     mHandler = null;
   4242                 }
   4243             } catch (java.util.NoSuchElementException e) {
   4244                 Log.e(TAG, "Encountered " + e + " in FocusStackEntry.unlinkToDeath()");
   4245             }
   4246         }
   4247 
   4248         @Override
   4249         protected void finalize() throws Throwable {
   4250             unlinkToDeath(); // unlink exception handled inside method
   4251             super.finalize();
   4252         }
   4253     }
   4254 
   4255     private final Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>();
   4256 
   4257     /**
   4258      * Helper function:
   4259      * Display in the log the current entries in the audio focus stack
   4260      */
   4261     private void dumpFocusStack(PrintWriter pw) {
   4262         pw.println("\nAudio Focus stack entries (last is top of stack):");
   4263         synchronized(mAudioFocusLock) {
   4264             Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
   4265             while(stackIterator.hasNext()) {
   4266                 FocusStackEntry fse = stackIterator.next();
   4267                 pw.println("  source:" + fse.mSourceRef
   4268                         + " -- pack: " + fse.mPackageName
   4269                         + " -- client: " + fse.mClientId
   4270                         + " -- duration: " + fse.mFocusChangeType
   4271                         + " -- uid: " + fse.mCallingUid
   4272                         + " -- stream: " + fse.mStreamType);
   4273             }
   4274         }
   4275     }
   4276 
   4277     /**
   4278      * Helper function:
   4279      * Called synchronized on mAudioFocusLock
   4280      * Remove a focus listener from the focus stack.
   4281      * @param clientToRemove the focus listener
   4282      * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
   4283      *   focus, notify the next item in the stack it gained focus.
   4284      */
   4285     private void removeFocusStackEntry(String clientToRemove, boolean signal) {
   4286         // is the current top of the focus stack abandoning focus? (because of request, not death)
   4287         if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove))
   4288         {
   4289             //Log.i(TAG, "   removeFocusStackEntry() removing top of stack");
   4290             FocusStackEntry fse = mFocusStack.pop();
   4291             fse.unlinkToDeath();
   4292             if (signal) {
   4293                 // notify the new top of the stack it gained focus
   4294                 notifyTopOfAudioFocusStack();
   4295                 // there's a new top of the stack, let the remote control know
   4296                 synchronized(mRCStack) {
   4297                     checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
   4298                 }
   4299             }
   4300         } else {
   4301             // focus is abandoned by a client that's not at the top of the stack,
   4302             // no need to update focus.
   4303             // (using an iterator on the stack so we can safely remove an entry after having
   4304             //  evaluated it, traversal order doesn't matter here)
   4305             Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
   4306             while(stackIterator.hasNext()) {
   4307                 FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
   4308                 if(fse.mClientId.equals(clientToRemove)) {
   4309                     Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
   4310                             + fse.mClientId);
   4311                     stackIterator.remove();
   4312                     fse.unlinkToDeath();
   4313                 }
   4314             }
   4315         }
   4316     }
   4317 
   4318     /**
   4319      * Helper function:
   4320      * Called synchronized on mAudioFocusLock
   4321      * Remove focus listeners from the focus stack for a particular client when it has died.
   4322      */
   4323     private void removeFocusStackEntryForClient(IBinder cb) {
   4324         // is the owner of the audio focus part of the client to remove?
   4325         boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
   4326                 mFocusStack.peek().mSourceRef.equals(cb);
   4327         // (using an iterator on the stack so we can safely remove an entry after having
   4328         //  evaluated it, traversal order doesn't matter here)
   4329         Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
   4330         while(stackIterator.hasNext()) {
   4331             FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
   4332             if(fse.mSourceRef.equals(cb)) {
   4333                 Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
   4334                         + fse.mClientId);
   4335                 stackIterator.remove();
   4336                 // the client just died, no need to unlink to its death
   4337             }
   4338         }
   4339         if (isTopOfStackForClientToRemove) {
   4340             // we removed an entry at the top of the stack:
   4341             //  notify the new top of the stack it gained focus.
   4342             notifyTopOfAudioFocusStack();
   4343             // there's a new top of the stack, let the remote control know
   4344             synchronized(mRCStack) {
   4345                 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
   4346             }
   4347         }
   4348     }
   4349 
   4350     /**
   4351      * Helper function:
   4352      * Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
   4353      */
   4354     private boolean canReassignAudioFocus() {
   4355         // focus requests are rejected during a phone call or when the phone is ringing
   4356         // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus
   4357         if (!mFocusStack.isEmpty() && IN_VOICE_COMM_FOCUS_ID.equals(mFocusStack.peek().mClientId)) {
   4358             return false;
   4359         }
   4360         return true;
   4361     }
   4362 
   4363     /**
   4364      * Inner class to monitor audio focus client deaths, and remove them from the audio focus
   4365      * stack if necessary.
   4366      */
   4367     private class AudioFocusDeathHandler implements IBinder.DeathRecipient {
   4368         private IBinder mCb; // To be notified of client's death
   4369 
   4370         AudioFocusDeathHandler(IBinder cb) {
   4371             mCb = cb;
   4372         }
   4373 
   4374         public void binderDied() {
   4375             synchronized(mAudioFocusLock) {
   4376                 Log.w(TAG, "  AudioFocus   audio focus client died");
   4377                 removeFocusStackEntryForClient(mCb);
   4378             }
   4379         }
   4380 
   4381         public IBinder getBinder() {
   4382             return mCb;
   4383         }
   4384     }
   4385 
   4386 
   4387     /** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int)  */
   4388     public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb,
   4389             IAudioFocusDispatcher fd, String clientId, String callingPackageName) {
   4390         Log.i(TAG, " AudioFocus  requestAudioFocus() from " + clientId);
   4391         // the main stream type for the audio focus request is currently not used. It may
   4392         // potentially be used to handle multiple stream type-dependent audio focuses.
   4393 
   4394         // we need a valid binder callback for clients
   4395         if (!cb.pingBinder()) {
   4396             Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
   4397             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
   4398         }
   4399 
   4400         synchronized(mAudioFocusLock) {
   4401             if (!canReassignAudioFocus()) {
   4402                 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
   4403             }
   4404 
   4405             // handle the potential premature death of the new holder of the focus
   4406             // (premature death == death before abandoning focus)
   4407             // Register for client death notification
   4408             AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
   4409             try {
   4410                 cb.linkToDeath(afdh, 0);
   4411             } catch (RemoteException e) {
   4412                 // client has already died!
   4413                 Log.w(TAG, "AudioFocus  requestAudioFocus() could not link to "+cb+" binder death");
   4414                 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
   4415             }
   4416 
   4417             if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) {
   4418                 // if focus is already owned by this client and the reason for acquiring the focus
   4419                 // hasn't changed, don't do anything
   4420                 if (mFocusStack.peek().mFocusChangeType == focusChangeHint) {
   4421                     // unlink death handler so it can be gc'ed.
   4422                     // linkToDeath() creates a JNI global reference preventing collection.
   4423                     cb.unlinkToDeath(afdh, 0);
   4424                     return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
   4425                 }
   4426                 // the reason for the audio focus request has changed: remove the current top of
   4427                 // stack and respond as if we had a new focus owner
   4428                 FocusStackEntry fse = mFocusStack.pop();
   4429                 fse.unlinkToDeath();
   4430             }
   4431 
   4432             // notify current top of stack it is losing focus
   4433             if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
   4434                 try {
   4435                     mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
   4436                             -1 * focusChangeHint, // loss and gain codes are inverse of each other
   4437                             mFocusStack.peek().mClientId);
   4438                 } catch (RemoteException e) {
   4439                     Log.e(TAG, " Failure to signal loss of focus due to "+ e);
   4440                     e.printStackTrace();
   4441                 }
   4442             }
   4443 
   4444             // focus requester might already be somewhere below in the stack, remove it
   4445             removeFocusStackEntry(clientId, false /* signal */);
   4446 
   4447             // push focus requester at the top of the audio focus stack
   4448             mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, fd, cb,
   4449                     clientId, afdh, callingPackageName, Binder.getCallingUid()));
   4450 
   4451             // there's a new top of the stack, let the remote control know
   4452             synchronized(mRCStack) {
   4453                 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
   4454             }
   4455         }//synchronized(mAudioFocusLock)
   4456 
   4457         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
   4458     }
   4459 
   4460     /** @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener)  */
   4461     public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) {
   4462         Log.i(TAG, " AudioFocus  abandonAudioFocus() from " + clientId);
   4463         try {
   4464             // this will take care of notifying the new focus owner if needed
   4465             synchronized(mAudioFocusLock) {
   4466                 removeFocusStackEntry(clientId, true);
   4467             }
   4468         } catch (java.util.ConcurrentModificationException cme) {
   4469             // Catching this exception here is temporary. It is here just to prevent
   4470             // a crash seen when the "Silent" notification is played. This is believed to be fixed
   4471             // but this try catch block is left just to be safe.
   4472             Log.e(TAG, "FATAL EXCEPTION AudioFocus  abandonAudioFocus() caused " + cme);
   4473             cme.printStackTrace();
   4474         }
   4475 
   4476         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
   4477     }
   4478 
   4479 
   4480     public void unregisterAudioFocusClient(String clientId) {
   4481         synchronized(mAudioFocusLock) {
   4482             removeFocusStackEntry(clientId, false);
   4483         }
   4484     }
   4485 
   4486 
   4487     //==========================================================================================
   4488     // RemoteControl
   4489     //==========================================================================================
   4490     public void dispatchMediaKeyEvent(KeyEvent keyEvent) {
   4491         filterMediaKeyEvent(keyEvent, false /*needWakeLock*/);
   4492     }
   4493 
   4494     public void dispatchMediaKeyEventUnderWakelock(KeyEvent keyEvent) {
   4495         filterMediaKeyEvent(keyEvent, true /*needWakeLock*/);
   4496     }
   4497 
   4498     private void filterMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
   4499         // sanity check on the incoming key event
   4500         if (!isValidMediaKeyEvent(keyEvent)) {
   4501             Log.e(TAG, "not dispatching invalid media key event " + keyEvent);
   4502             return;
   4503         }
   4504         // event filtering for telephony
   4505         synchronized(mRingingLock) {
   4506             synchronized(mRCStack) {
   4507                 if ((mMediaReceiverForCalls != null) &&
   4508                         (mIsRinging || (getMode() == AudioSystem.MODE_IN_CALL))) {
   4509                     dispatchMediaKeyEventForCalls(keyEvent, needWakeLock);
   4510                     return;
   4511                 }
   4512             }
   4513         }
   4514         // event filtering based on voice-based interactions
   4515         if (isValidVoiceInputKeyCode(keyEvent.getKeyCode())) {
   4516             filterVoiceInputKeyEvent(keyEvent, needWakeLock);
   4517         } else {
   4518             dispatchMediaKeyEvent(keyEvent, needWakeLock);
   4519         }
   4520     }
   4521 
   4522     /**
   4523      * Handles the dispatching of the media button events to the telephony package.
   4524      * Precondition: mMediaReceiverForCalls != null
   4525      * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons
   4526      * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
   4527      *     is dispatched.
   4528      */
   4529     private void dispatchMediaKeyEventForCalls(KeyEvent keyEvent, boolean needWakeLock) {
   4530         Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
   4531         keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
   4532         keyIntent.setPackage(mMediaReceiverForCalls.getPackageName());
   4533         if (needWakeLock) {
   4534             mMediaEventWakeLock.acquire();
   4535             keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
   4536         }
   4537         final long ident = Binder.clearCallingIdentity();
   4538         try {
   4539             mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
   4540                     null, mKeyEventDone, mAudioHandler, Activity.RESULT_OK, null, null);
   4541         } finally {
   4542             Binder.restoreCallingIdentity(ident);
   4543         }
   4544     }
   4545 
   4546     /**
   4547      * Handles the dispatching of the media button events to one of the registered listeners,
   4548      * or if there was none, broadcast an ACTION_MEDIA_BUTTON intent to the rest of the system.
   4549      * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons
   4550      * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
   4551      *     is dispatched.
   4552      */
   4553     private void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
   4554         if (needWakeLock) {
   4555             mMediaEventWakeLock.acquire();
   4556         }
   4557         Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
   4558         keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
   4559         synchronized(mRCStack) {
   4560             if (!mRCStack.empty()) {
   4561                 // send the intent that was registered by the client
   4562                 try {
   4563                     mRCStack.peek().mMediaIntent.send(mContext,
   4564                             needWakeLock ? WAKELOCK_RELEASE_ON_FINISHED : 0 /*code*/,
   4565                             keyIntent, AudioService.this, mAudioHandler);
   4566                 } catch (CanceledException e) {
   4567                     Log.e(TAG, "Error sending pending intent " + mRCStack.peek());
   4568                     e.printStackTrace();
   4569                 }
   4570             } else {
   4571                 // legacy behavior when nobody registered their media button event receiver
   4572                 //    through AudioManager
   4573                 if (needWakeLock) {
   4574                     keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
   4575                 }
   4576                 final long ident = Binder.clearCallingIdentity();
   4577                 try {
   4578                     mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
   4579                             null, mKeyEventDone,
   4580                             mAudioHandler, Activity.RESULT_OK, null, null);
   4581                 } finally {
   4582                     Binder.restoreCallingIdentity(ident);
   4583                 }
   4584             }
   4585         }
   4586     }
   4587 
   4588     /**
   4589      * The different actions performed in response to a voice button key event.
   4590      */
   4591     private final static int VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS = 1;
   4592     private final static int VOICEBUTTON_ACTION_START_VOICE_INPUT = 2;
   4593     private final static int VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS = 3;
   4594 
   4595     private final Object mVoiceEventLock = new Object();
   4596     private boolean mVoiceButtonDown;
   4597     private boolean mVoiceButtonHandled;
   4598 
   4599     /**
   4600      * Filter key events that may be used for voice-based interactions
   4601      * @param keyEvent a non-null KeyEvent whose key code is that of one of the supported
   4602      *    media buttons that can be used to trigger voice-based interactions.
   4603      * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
   4604      *     is dispatched.
   4605      */
   4606     private void filterVoiceInputKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
   4607         if (DEBUG_RC) {
   4608             Log.v(TAG, "voice input key event: " + keyEvent + ", needWakeLock=" + needWakeLock);
   4609         }
   4610 
   4611         int voiceButtonAction = VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS;
   4612         int keyAction = keyEvent.getAction();
   4613         synchronized (mVoiceEventLock) {
   4614             if (keyAction == KeyEvent.ACTION_DOWN) {
   4615                 if (keyEvent.getRepeatCount() == 0) {
   4616                     // initial down
   4617                     mVoiceButtonDown = true;
   4618                     mVoiceButtonHandled = false;
   4619                 } else if (mVoiceButtonDown && !mVoiceButtonHandled
   4620                         && (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
   4621                     // long-press, start voice-based interactions
   4622                     mVoiceButtonHandled = true;
   4623                     voiceButtonAction = VOICEBUTTON_ACTION_START_VOICE_INPUT;
   4624                 }
   4625             } else if (keyAction == KeyEvent.ACTION_UP) {
   4626                 if (mVoiceButtonDown) {
   4627                     // voice button up
   4628                     mVoiceButtonDown = false;
   4629                     if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
   4630                         voiceButtonAction = VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS;
   4631                     }
   4632                 }
   4633             }
   4634         }//synchronized (mVoiceEventLock)
   4635 
   4636         // take action after media button event filtering for voice-based interactions
   4637         switch (voiceButtonAction) {
   4638             case VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS:
   4639                 if (DEBUG_RC) Log.v(TAG, "   ignore key event");
   4640                 break;
   4641             case VOICEBUTTON_ACTION_START_VOICE_INPUT:
   4642                 if (DEBUG_RC) Log.v(TAG, "   start voice-based interactions");
   4643                 // then start the voice-based interactions
   4644                 startVoiceBasedInteractions(needWakeLock);
   4645                 break;
   4646             case VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS:
   4647                 if (DEBUG_RC) Log.v(TAG, "   send simulated key event, wakelock=" + needWakeLock);
   4648                 sendSimulatedMediaButtonEvent(keyEvent, needWakeLock);
   4649                 break;
   4650         }
   4651     }
   4652 
   4653     private void sendSimulatedMediaButtonEvent(KeyEvent originalKeyEvent, boolean needWakeLock) {
   4654         // send DOWN event
   4655         KeyEvent keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_DOWN);
   4656         dispatchMediaKeyEvent(keyEvent, needWakeLock);
   4657         // send UP event
   4658         keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_UP);
   4659         dispatchMediaKeyEvent(keyEvent, needWakeLock);
   4660 
   4661     }
   4662 
   4663 
   4664     private static boolean isValidMediaKeyEvent(KeyEvent keyEvent) {
   4665         if (keyEvent == null) {
   4666             return false;
   4667         }
   4668         final int keyCode = keyEvent.getKeyCode();
   4669         switch (keyCode) {
   4670             case KeyEvent.KEYCODE_MUTE:
   4671             case KeyEvent.KEYCODE_HEADSETHOOK:
   4672             case KeyEvent.KEYCODE_MEDIA_PLAY:
   4673             case KeyEvent.KEYCODE_MEDIA_PAUSE:
   4674             case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
   4675             case KeyEvent.KEYCODE_MEDIA_STOP:
   4676             case KeyEvent.KEYCODE_MEDIA_NEXT:
   4677             case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
   4678             case KeyEvent.KEYCODE_MEDIA_REWIND:
   4679             case KeyEvent.KEYCODE_MEDIA_RECORD:
   4680             case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
   4681             case KeyEvent.KEYCODE_MEDIA_CLOSE:
   4682             case KeyEvent.KEYCODE_MEDIA_EJECT:
   4683                 break;
   4684             default:
   4685                 return false;
   4686         }
   4687         return true;
   4688     }
   4689 
   4690     /**
   4691      * Checks whether the given key code is one that can trigger the launch of voice-based
   4692      *   interactions.
   4693      * @param keyCode the key code associated with the key event
   4694      * @return true if the key is one of the supported voice-based interaction triggers
   4695      */
   4696     private static boolean isValidVoiceInputKeyCode(int keyCode) {
   4697         if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK) {
   4698             return true;
   4699         } else {
   4700             return false;
   4701         }
   4702     }
   4703 
   4704     /**
   4705      * Tell the system to start voice-based interactions / voice commands
   4706      */
   4707     private void startVoiceBasedInteractions(boolean needWakeLock) {
   4708         Intent voiceIntent = null;
   4709         // select which type of search to launch:
   4710         // - screen on and device unlocked: action is ACTION_WEB_SEARCH
   4711         // - device locked or screen off: action is ACTION_VOICE_SEARCH_HANDS_FREE
   4712         //    with EXTRA_SECURE set to true if the device is securely locked
   4713         PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
   4714         boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
   4715         if (!isLocked && pm.isScreenOn()) {
   4716             voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
   4717             Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
   4718         } else {
   4719             voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
   4720             voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
   4721                     isLocked && mKeyguardManager.isKeyguardSecure());
   4722             Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
   4723         }
   4724         // start the search activity
   4725         if (needWakeLock) {
   4726             mMediaEventWakeLock.acquire();
   4727         }
   4728         try {
   4729             if (voiceIntent != null) {
   4730                 voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
   4731                         | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
   4732                 mContext.startActivity(voiceIntent);
   4733             }
   4734         } catch (ActivityNotFoundException e) {
   4735             Log.w(TAG, "No activity for search: " + e);
   4736         } finally {
   4737             if (needWakeLock) {
   4738                 mMediaEventWakeLock.release();
   4739             }
   4740         }
   4741     }
   4742 
   4743     private PowerManager.WakeLock mMediaEventWakeLock;
   4744 
   4745     private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; //magic number
   4746 
   4747     // only set when wakelock was acquired, no need to check value when received
   4748     private static final String EXTRA_WAKELOCK_ACQUIRED =
   4749             "android.media.AudioService.WAKELOCK_ACQUIRED";
   4750 
   4751     public void onSendFinished(PendingIntent pendingIntent, Intent intent,
   4752             int resultCode, String resultData, Bundle resultExtras) {
   4753         if (resultCode == WAKELOCK_RELEASE_ON_FINISHED) {
   4754             mMediaEventWakeLock.release();
   4755         }
   4756     }
   4757 
   4758     BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
   4759         public void onReceive(Context context, Intent intent) {
   4760             if (intent == null) {
   4761                 return;
   4762             }
   4763             Bundle extras = intent.getExtras();
   4764             if (extras == null) {
   4765                 return;
   4766             }
   4767             if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)) {
   4768                 mMediaEventWakeLock.release();
   4769             }
   4770         }
   4771     };
   4772 
   4773     /**
   4774      * Synchronization on mCurrentRcLock always inside a block synchronized on mRCStack
   4775      */
   4776     private final Object mCurrentRcLock = new Object();
   4777     /**
   4778      * The one remote control client which will receive a request for display information.
   4779      * This object may be null.
   4780      * Access protected by mCurrentRcLock.
   4781      */
   4782     private IRemoteControlClient mCurrentRcClient = null;
   4783 
   4784     private final static int RC_INFO_NONE = 0;
   4785     private final static int RC_INFO_ALL =
   4786         RemoteControlClient.FLAG_INFORMATION_REQUEST_ALBUM_ART |
   4787         RemoteControlClient.FLAG_INFORMATION_REQUEST_KEY_MEDIA |
   4788         RemoteControlClient.FLAG_INFORMATION_REQUEST_METADATA |
   4789         RemoteControlClient.FLAG_INFORMATION_REQUEST_PLAYSTATE;
   4790 
   4791     /**
   4792      * A monotonically increasing generation counter for mCurrentRcClient.
   4793      * Only accessed with a lock on mCurrentRcLock.
   4794      * No value wrap-around issues as we only act on equal values.
   4795      */
   4796     private int mCurrentRcClientGen = 0;
   4797 
   4798     /**
   4799      * Inner class to monitor remote control client deaths, and remove the client for the
   4800      * remote control stack if necessary.
   4801      */
   4802     private class RcClientDeathHandler implements IBinder.DeathRecipient {
   4803         final private IBinder mCb; // To be notified of client's death
   4804         final private PendingIntent mMediaIntent;
   4805 
   4806         RcClientDeathHandler(IBinder cb, PendingIntent pi) {
   4807             mCb = cb;
   4808             mMediaIntent = pi;
   4809         }
   4810 
   4811         public void binderDied() {
   4812             Log.w(TAG, "  RemoteControlClient died");
   4813             // remote control client died, make sure the displays don't use it anymore
   4814             //  by setting its remote control client to null
   4815             registerRemoteControlClient(mMediaIntent, null/*rcClient*/, null/*ignored*/);
   4816             // the dead client was maybe handling remote playback, reevaluate
   4817             postReevaluateRemote();
   4818         }
   4819 
   4820         public IBinder getBinder() {
   4821             return mCb;
   4822         }
   4823     }
   4824 
   4825     /**
   4826      * A global counter for RemoteControlClient identifiers
   4827      */
   4828     private static int sLastRccId = 0;
   4829 
   4830     private class RemotePlaybackState {
   4831         int mRccId;
   4832         int mVolume;
   4833         int mVolumeMax;
   4834         int mVolumeHandling;
   4835 
   4836         private RemotePlaybackState(int id, int vol, int volMax) {
   4837             mRccId = id;
   4838             mVolume = vol;
   4839             mVolumeMax = volMax;
   4840             mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
   4841         }
   4842     }
   4843 
   4844     /**
   4845      * Internal cache for the playback information of the RemoteControlClient whose volume gets to
   4846      * be controlled by the volume keys ("main"), so we don't have to iterate over the RC stack
   4847      * every time we need this info.
   4848      */
   4849     private RemotePlaybackState mMainRemote;
   4850     /**
   4851      * Indicates whether the "main" RemoteControlClient is considered active.
   4852      * Use synchronized on mMainRemote.
   4853      */
   4854     private boolean mMainRemoteIsActive;
   4855     /**
   4856      * Indicates whether there is remote playback going on. True even if there is no "active"
   4857      * remote playback (mMainRemoteIsActive is false), but a RemoteControlClient has declared it
   4858      * handles remote playback.
   4859      * Use synchronized on mMainRemote.
   4860      */
   4861     private boolean mHasRemotePlayback;
   4862 
   4863     private static class RccPlaybackState {
   4864         public int mState;
   4865         public long mPositionMs;
   4866         public float mSpeed;
   4867 
   4868         public RccPlaybackState(int state, long positionMs, float speed) {
   4869             mState = state;
   4870             mPositionMs = positionMs;
   4871             mSpeed = speed;
   4872         }
   4873 
   4874         public void reset() {
   4875             mState = RemoteControlClient.PLAYSTATE_STOPPED;
   4876             mPositionMs = RemoteControlClient.PLAYBACK_POSITION_INVALID;
   4877             mSpeed = RemoteControlClient.PLAYBACK_SPEED_1X;
   4878         }
   4879 
   4880         @Override
   4881         public String toString() {
   4882             return stateToString() + ", "
   4883                     + ((mPositionMs == RemoteControlClient.PLAYBACK_POSITION_INVALID) ?
   4884                             "PLAYBACK_POSITION_INVALID ," : String.valueOf(mPositionMs)) + "ms ,"
   4885                     + mSpeed + "X";
   4886         }
   4887 
   4888         private String stateToString() {
   4889             switch (mState) {
   4890                 case RemoteControlClient.PLAYSTATE_NONE:
   4891                     return "PLAYSTATE_NONE";
   4892                 case RemoteControlClient.PLAYSTATE_STOPPED:
   4893                     return "PLAYSTATE_STOPPED";
   4894                 case RemoteControlClient.PLAYSTATE_PAUSED:
   4895                     return "PLAYSTATE_PAUSED";
   4896                 case RemoteControlClient.PLAYSTATE_PLAYING:
   4897                     return "PLAYSTATE_PLAYING";
   4898                 case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
   4899                     return "PLAYSTATE_FAST_FORWARDING";
   4900                 case RemoteControlClient.PLAYSTATE_REWINDING:
   4901                     return "PLAYSTATE_REWINDING";
   4902                 case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
   4903                     return "PLAYSTATE_SKIPPING_FORWARDS";
   4904                 case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
   4905                     return "PLAYSTATE_SKIPPING_BACKWARDS";
   4906                 case RemoteControlClient.PLAYSTATE_BUFFERING:
   4907                     return "PLAYSTATE_BUFFERING";
   4908                 case RemoteControlClient.PLAYSTATE_ERROR:
   4909                     return "PLAYSTATE_ERROR";
   4910                 default:
   4911                     return "[invalid playstate]";
   4912             }
   4913         }
   4914     }
   4915 
   4916     private static class RemoteControlStackEntry implements DeathRecipient {
   4917         public int mRccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
   4918         final public AudioService mService;
   4919         /**
   4920          * The target for the ACTION_MEDIA_BUTTON events.
   4921          * Always non null.
   4922          */
   4923         final public PendingIntent mMediaIntent;
   4924         /**
   4925          * The registered media button event receiver.
   4926          * Always non null.
   4927          */
   4928         final public ComponentName mReceiverComponent;
   4929         public IBinder mToken;
   4930         public String mCallingPackageName;
   4931         public int mCallingUid;
   4932         /**
   4933          * Provides access to the information to display on the remote control.
   4934          * May be null (when a media button event receiver is registered,
   4935          *     but no remote control client has been registered) */
   4936         public IRemoteControlClient mRcClient;
   4937         public RcClientDeathHandler mRcClientDeathHandler;
   4938         /**
   4939          * Information only used for non-local playback
   4940          */
   4941         public int mPlaybackType;
   4942         public int mPlaybackVolume;
   4943         public int mPlaybackVolumeMax;
   4944         public int mPlaybackVolumeHandling;
   4945         public int mPlaybackStream;
   4946         public RccPlaybackState mPlaybackState;
   4947         public IRemoteVolumeObserver mRemoteVolumeObs;
   4948 
   4949         public void resetPlaybackInfo() {
   4950             mPlaybackType = RemoteControlClient.PLAYBACK_TYPE_LOCAL;
   4951             mPlaybackVolume = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
   4952             mPlaybackVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
   4953             mPlaybackVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
   4954             mPlaybackStream = AudioManager.STREAM_MUSIC;
   4955             mPlaybackState.reset();
   4956             mRemoteVolumeObs = null;
   4957         }
   4958 
   4959         /** precondition: mediaIntent != null */
   4960         public RemoteControlStackEntry(AudioService service, PendingIntent mediaIntent,
   4961                 ComponentName eventReceiver, IBinder token) {
   4962             mService = service;
   4963             mMediaIntent = mediaIntent;
   4964             mReceiverComponent = eventReceiver;
   4965             mToken = token;
   4966             mCallingUid = -1;
   4967             mRcClient = null;
   4968             mRccId = ++sLastRccId;
   4969             mPlaybackState = new RccPlaybackState(
   4970                     RemoteControlClient.PLAYSTATE_STOPPED,
   4971                     RemoteControlClient.PLAYBACK_POSITION_INVALID,
   4972                     RemoteControlClient.PLAYBACK_SPEED_1X);
   4973 
   4974             resetPlaybackInfo();
   4975             if (mToken != null) {
   4976                 try {
   4977                     mToken.linkToDeath(this, 0);
   4978                 } catch (RemoteException e) {
   4979                     mService.mAudioHandler.post(new Runnable() {
   4980                         @Override public void run() {
   4981                             mService.unregisterMediaButtonIntent(mMediaIntent);
   4982                         }
   4983                     });
   4984                 }
   4985             }
   4986         }
   4987 
   4988         public void unlinkToRcClientDeath() {
   4989             if ((mRcClientDeathHandler != null) && (mRcClientDeathHandler.mCb != null)) {
   4990                 try {
   4991                     mRcClientDeathHandler.mCb.unlinkToDeath(mRcClientDeathHandler, 0);
   4992                     mRcClientDeathHandler = null;
   4993                 } catch (java.util.NoSuchElementException e) {
   4994                     // not much we can do here
   4995                     Log.e(TAG, "Encountered " + e + " in unlinkToRcClientDeath()");
   4996                     e.printStackTrace();
   4997                 }
   4998             }
   4999         }
   5000 
   5001         public void destroy() {
   5002             unlinkToRcClientDeath();
   5003             if (mToken != null) {
   5004                 mToken.unlinkToDeath(this, 0);
   5005                 mToken = null;
   5006             }
   5007         }
   5008 
   5009         @Override
   5010         public void binderDied() {
   5011             mService.unregisterMediaButtonIntent(mMediaIntent);
   5012         }
   5013 
   5014         @Override
   5015         protected void finalize() throws Throwable {
   5016             destroy(); // unlink exception handled inside method
   5017             super.finalize();
   5018         }
   5019     }
   5020 
   5021     /**
   5022      *  The stack of remote control event receivers.
   5023      *  Code sections and methods that modify the remote control event receiver stack are
   5024      *  synchronized on mRCStack, but also BEFORE on mFocusLock as any change in either
   5025      *  stack, audio focus or RC, can lead to a change in the remote control display
   5026      */
   5027     private final Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>();
   5028 
   5029     /**
   5030      * The component the telephony package can register so telephony calls have priority to
   5031      * handle media button events
   5032      */
   5033     private ComponentName mMediaReceiverForCalls = null;
   5034 
   5035     /**
   5036      * Helper function:
   5037      * Display in the log the current entries in the remote control focus stack
   5038      */
   5039     private void dumpRCStack(PrintWriter pw) {
   5040         pw.println("\nRemote Control stack entries (last is top of stack):");
   5041         synchronized(mRCStack) {
   5042             Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   5043             while(stackIterator.hasNext()) {
   5044                 RemoteControlStackEntry rcse = stackIterator.next();
   5045                 pw.println("  pi: " + rcse.mMediaIntent +
   5046                         " -- pack: " + rcse.mCallingPackageName +
   5047                         "  -- ercvr: " + rcse.mReceiverComponent +
   5048                         "  -- client: " + rcse.mRcClient +
   5049                         "  -- uid: " + rcse.mCallingUid +
   5050                         "  -- type: " + rcse.mPlaybackType +
   5051                         "  state: " + rcse.mPlaybackState);
   5052             }
   5053         }
   5054     }
   5055 
   5056     /**
   5057      * Helper function:
   5058      * Display in the log the current entries in the remote control stack, focusing
   5059      * on RemoteControlClient data
   5060      */
   5061     private void dumpRCCStack(PrintWriter pw) {
   5062         pw.println("\nRemote Control Client stack entries (last is top of stack):");
   5063         synchronized(mRCStack) {
   5064             Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   5065             while(stackIterator.hasNext()) {
   5066                 RemoteControlStackEntry rcse = stackIterator.next();
   5067                 pw.println("  uid: " + rcse.mCallingUid +
   5068                         "  -- id: " + rcse.mRccId +
   5069                         "  -- type: " + rcse.mPlaybackType +
   5070                         "  -- state: " + rcse.mPlaybackState +
   5071                         "  -- vol handling: " + rcse.mPlaybackVolumeHandling +
   5072                         "  -- vol: " + rcse.mPlaybackVolume +
   5073                         "  -- volMax: " + rcse.mPlaybackVolumeMax +
   5074                         "  -- volObs: " + rcse.mRemoteVolumeObs);
   5075             }
   5076             synchronized(mCurrentRcLock) {
   5077                 pw.println("\nCurrent remote control generation ID = " + mCurrentRcClientGen);
   5078             }
   5079         }
   5080         synchronized (mMainRemote) {
   5081             pw.println("\nRemote Volume State:");
   5082             pw.println("  has remote: " + mHasRemotePlayback);
   5083             pw.println("  is remote active: " + mMainRemoteIsActive);
   5084             pw.println("  rccId: " + mMainRemote.mRccId);
   5085             pw.println("  volume handling: "
   5086                     + ((mMainRemote.mVolumeHandling == RemoteControlClient.PLAYBACK_VOLUME_FIXED) ?
   5087                             "PLAYBACK_VOLUME_FIXED(0)" : "PLAYBACK_VOLUME_VARIABLE(1)"));
   5088             pw.println("  volume: " + mMainRemote.mVolume);
   5089             pw.println("  volume steps: " + mMainRemote.mVolumeMax);
   5090         }
   5091     }
   5092 
   5093     /**
   5094      * Helper function:
   5095      * Display in the log the current entries in the list of remote control displays
   5096      */
   5097     private void dumpRCDList(PrintWriter pw) {
   5098         pw.println("\nRemote Control Display list entries:");
   5099         synchronized(mRCStack) {
   5100             final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
   5101             while (displayIterator.hasNext()) {
   5102                 final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
   5103                 pw.println("  IRCD: " + di.mRcDisplay +
   5104                         "  -- w:" + di.mArtworkExpectedWidth +
   5105                         "  -- h:" + di.mArtworkExpectedHeight+
   5106                         "  -- wantsPosSync:" + di.mWantsPositionSync);
   5107             }
   5108         }
   5109     }
   5110 
   5111     /**
   5112      * Helper function:
   5113      * Remove any entry in the remote control stack that has the same package name as packageName
   5114      * Pre-condition: packageName != null
   5115      */
   5116     private void cleanupMediaButtonReceiverForPackage(String packageName, boolean removeAll) {
   5117         synchronized(mRCStack) {
   5118             if (mRCStack.empty()) {
   5119                 return;
   5120             } else {
   5121                 final PackageManager pm = mContext.getPackageManager();
   5122                 RemoteControlStackEntry oldTop = mRCStack.peek();
   5123                 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   5124                 // iterate over the stack entries
   5125                 // (using an iterator on the stack so we can safely remove an entry after having
   5126                 //  evaluated it, traversal order doesn't matter here)
   5127                 while(stackIterator.hasNext()) {
   5128                     RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
   5129                     if (removeAll && packageName.equals(rcse.mMediaIntent.getCreatorPackage())) {
   5130                         // a stack entry is from the package being removed, remove it from the stack
   5131                         stackIterator.remove();
   5132                         rcse.destroy();
   5133                     } else if (rcse.mReceiverComponent != null) {
   5134                         try {
   5135                             // Check to see if this receiver still exists.
   5136                             pm.getReceiverInfo(rcse.mReceiverComponent, 0);
   5137                         } catch (PackageManager.NameNotFoundException e) {
   5138                             // Not found -- remove it!
   5139                             stackIterator.remove();
   5140                             rcse.destroy();
   5141                         }
   5142                     }
   5143                 }
   5144                 if (mRCStack.empty()) {
   5145                     // no saved media button receiver
   5146                     mAudioHandler.sendMessage(
   5147                             mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
   5148                                     null));
   5149                 } else if (oldTop != mRCStack.peek()) {
   5150                     // the top of the stack has changed, save it in the system settings
   5151                     // by posting a message to persist it; only do this however if it has
   5152                     // a concrete component name (is not a transient registration)
   5153                     RemoteControlStackEntry rcse = mRCStack.peek();
   5154                     if (rcse.mReceiverComponent != null) {
   5155                         mAudioHandler.sendMessage(
   5156                                 mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
   5157                                         rcse.mReceiverComponent));
   5158                     }
   5159                 }
   5160             }
   5161         }
   5162     }
   5163 
   5164     /**
   5165      * Helper function:
   5166      * Restore remote control receiver from the system settings.
   5167      */
   5168     private void restoreMediaButtonReceiver() {
   5169         String receiverName = Settings.System.getStringForUser(mContentResolver,
   5170                 Settings.System.MEDIA_BUTTON_RECEIVER, UserHandle.USER_CURRENT);
   5171         if ((null != receiverName) && !receiverName.isEmpty()) {
   5172             ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
   5173             if (eventReceiver == null) {
   5174                 // an invalid name was persisted
   5175                 return;
   5176             }
   5177             // construct a PendingIntent targeted to the restored component name
   5178             // for the media button and register it
   5179             Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
   5180             //     the associated intent will be handled by the component being registered
   5181             mediaButtonIntent.setComponent(eventReceiver);
   5182             PendingIntent pi = PendingIntent.getBroadcast(mContext,
   5183                     0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
   5184             registerMediaButtonIntent(pi, eventReceiver, null);
   5185         }
   5186     }
   5187 
   5188     /**
   5189      * Helper function:
   5190      * Set the new remote control receiver at the top of the RC focus stack.
   5191      * Called synchronized on mAudioFocusLock, then mRCStack
   5192      * precondition: mediaIntent != null
   5193      */
   5194     private void pushMediaButtonReceiver_syncAfRcs(PendingIntent mediaIntent, ComponentName target,
   5195             IBinder token) {
   5196         // already at top of stack?
   5197         if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(mediaIntent)) {
   5198             return;
   5199         }
   5200         RemoteControlStackEntry rcse = null;
   5201         boolean wasInsideStack = false;
   5202         try {
   5203             for (int index = mRCStack.size()-1; index >= 0; index--) {
   5204                 rcse = mRCStack.elementAt(index);
   5205                 if(rcse.mMediaIntent.equals(mediaIntent)) {
   5206                     // ok to remove element while traversing the stack since we're leaving the loop
   5207                     mRCStack.removeElementAt(index);
   5208                     wasInsideStack = true;
   5209                     break;
   5210                 }
   5211             }
   5212         } catch (ArrayIndexOutOfBoundsException e) {
   5213             // not expected to happen, indicates improper concurrent modification
   5214             Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
   5215         }
   5216         if (!wasInsideStack) {
   5217             rcse = new RemoteControlStackEntry(this, mediaIntent, target, token);
   5218         }
   5219         mRCStack.push(rcse); // rcse is never null
   5220 
   5221         // post message to persist the default media button receiver
   5222         if (target != null) {
   5223             mAudioHandler.sendMessage( mAudioHandler.obtainMessage(
   5224                     MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) );
   5225         }
   5226     }
   5227 
   5228     /**
   5229      * Helper function:
   5230      * Remove the remote control receiver from the RC focus stack.
   5231      * Called synchronized on mAudioFocusLock, then mRCStack
   5232      * precondition: pi != null
   5233      */
   5234     private void removeMediaButtonReceiver_syncAfRcs(PendingIntent pi) {
   5235         try {
   5236             for (int index = mRCStack.size()-1; index >= 0; index--) {
   5237                 final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
   5238                 if (rcse.mMediaIntent.equals(pi)) {
   5239                     rcse.destroy();
   5240                     // ok to remove element while traversing the stack since we're leaving the loop
   5241                     mRCStack.removeElementAt(index);
   5242                     break;
   5243                 }
   5244             }
   5245         } catch (ArrayIndexOutOfBoundsException e) {
   5246             // not expected to happen, indicates improper concurrent modification
   5247             Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
   5248         }
   5249     }
   5250 
   5251     /**
   5252      * Helper function:
   5253      * Called synchronized on mRCStack
   5254      */
   5255     private boolean isCurrentRcController(PendingIntent pi) {
   5256         if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(pi)) {
   5257             return true;
   5258         }
   5259         return false;
   5260     }
   5261 
   5262     //==========================================================================================
   5263     // Remote control display / client
   5264     //==========================================================================================
   5265     /**
   5266      * Update the remote control displays with the new "focused" client generation
   5267      */
   5268     private void setNewRcClientOnDisplays_syncRcsCurrc(int newClientGeneration,
   5269             PendingIntent newMediaIntent, boolean clearing) {
   5270         synchronized(mRCStack) {
   5271             if (mRcDisplays.size() > 0) {
   5272                 final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
   5273                 while (displayIterator.hasNext()) {
   5274                     final DisplayInfoForServer di = displayIterator.next();
   5275                     try {
   5276                         di.mRcDisplay.setCurrentClientId(
   5277                                 newClientGeneration, newMediaIntent, clearing);
   5278                     } catch (RemoteException e) {
   5279                         Log.e(TAG, "Dead display in setNewRcClientOnDisplays_syncRcsCurrc()",e);
   5280                         di.release();
   5281                         displayIterator.remove();
   5282                     }
   5283                 }
   5284             }
   5285         }
   5286     }
   5287 
   5288     /**
   5289      * Update the remote control clients with the new "focused" client generation
   5290      */
   5291     private void setNewRcClientGenerationOnClients_syncRcsCurrc(int newClientGeneration) {
   5292         // (using an iterator on the stack so we can safely remove an entry if needed,
   5293         //  traversal order doesn't matter here as we update all entries)
   5294         Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   5295         while(stackIterator.hasNext()) {
   5296             RemoteControlStackEntry se = stackIterator.next();
   5297             if ((se != null) && (se.mRcClient != null)) {
   5298                 try {
   5299                     se.mRcClient.setCurrentClientGenerationId(newClientGeneration);
   5300                 } catch (RemoteException e) {
   5301                     Log.w(TAG, "Dead client in setNewRcClientGenerationOnClients_syncRcsCurrc()",e);
   5302                     stackIterator.remove();
   5303                     se.unlinkToRcClientDeath();
   5304                 }
   5305             }
   5306         }
   5307     }
   5308 
   5309     /**
   5310      * Update the displays and clients with the new "focused" client generation and name
   5311      * @param newClientGeneration the new generation value matching a client update
   5312      * @param newMediaIntent the media button event receiver associated with the client.
   5313      *    May be null, which implies there is no registered media button event receiver.
   5314      * @param clearing true if the new client generation value maps to a remote control update
   5315      *    where the display should be cleared.
   5316      */
   5317     private void setNewRcClient_syncRcsCurrc(int newClientGeneration,
   5318             PendingIntent newMediaIntent, boolean clearing) {
   5319         // send the new valid client generation ID to all displays
   5320         setNewRcClientOnDisplays_syncRcsCurrc(newClientGeneration, newMediaIntent, clearing);
   5321         // send the new valid client generation ID to all clients
   5322         setNewRcClientGenerationOnClients_syncRcsCurrc(newClientGeneration);
   5323     }
   5324 
   5325     /**
   5326      * Called when processing MSG_RCDISPLAY_CLEAR event
   5327      */
   5328     private void onRcDisplayClear() {
   5329         if (DEBUG_RC) Log.i(TAG, "Clear remote control display");
   5330 
   5331         synchronized(mRCStack) {
   5332             synchronized(mCurrentRcLock) {
   5333                 mCurrentRcClientGen++;
   5334                 // synchronously update the displays and clients with the new client generation
   5335                 setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
   5336                         null /*newMediaIntent*/, true /*clearing*/);
   5337             }
   5338         }
   5339     }
   5340 
   5341     /**
   5342      * Called when processing MSG_RCDISPLAY_UPDATE event
   5343      */
   5344     private void onRcDisplayUpdate(RemoteControlStackEntry rcse, int flags /* USED ?*/) {
   5345         synchronized(mRCStack) {
   5346             synchronized(mCurrentRcLock) {
   5347                 if ((mCurrentRcClient != null) && (mCurrentRcClient.equals(rcse.mRcClient))) {
   5348                     if (DEBUG_RC) Log.i(TAG, "Display/update remote control ");
   5349 
   5350                     mCurrentRcClientGen++;
   5351                     // synchronously update the displays and clients with
   5352                     //      the new client generation
   5353                     setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
   5354                             rcse.mMediaIntent /*newMediaIntent*/,
   5355                             false /*clearing*/);
   5356 
   5357                     // tell the current client that it needs to send info
   5358                     try {
   5359                         mCurrentRcClient.onInformationRequested(mCurrentRcClientGen, flags);
   5360                     } catch (RemoteException e) {
   5361                         Log.e(TAG, "Current valid remote client is dead: "+e);
   5362                         mCurrentRcClient = null;
   5363                     }
   5364                 } else {
   5365                     // the remote control display owner has changed between the
   5366                     // the message to update the display was sent, and the time it
   5367                     // gets to be processed (now)
   5368                 }
   5369             }
   5370         }
   5371     }
   5372 
   5373 
   5374     /**
   5375      * Helper function:
   5376      * Called synchronized on mRCStack
   5377      */
   5378     private void clearRemoteControlDisplay_syncAfRcs() {
   5379         synchronized(mCurrentRcLock) {
   5380             mCurrentRcClient = null;
   5381         }
   5382         // will cause onRcDisplayClear() to be called in AudioService's handler thread
   5383         mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_CLEAR) );
   5384     }
   5385 
   5386     /**
   5387      * Helper function for code readability: only to be called from
   5388      *    checkUpdateRemoteControlDisplay_syncAfRcs() which checks the preconditions for
   5389      *    this method.
   5390      * Preconditions:
   5391      *    - called synchronized mAudioFocusLock then on mRCStack
   5392      *    - mRCStack.isEmpty() is false
   5393      */
   5394     private void updateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
   5395         RemoteControlStackEntry rcse = mRCStack.peek();
   5396         int infoFlagsAboutToBeUsed = infoChangedFlags;
   5397         // this is where we enforce opt-in for information display on the remote controls
   5398         //   with the new AudioManager.registerRemoteControlClient() API
   5399         if (rcse.mRcClient == null) {
   5400             //Log.w(TAG, "Can't update remote control display with null remote control client");
   5401             clearRemoteControlDisplay_syncAfRcs();
   5402             return;
   5403         }
   5404         synchronized(mCurrentRcLock) {
   5405             if (!rcse.mRcClient.equals(mCurrentRcClient)) {
   5406                 // new RC client, assume every type of information shall be queried
   5407                 infoFlagsAboutToBeUsed = RC_INFO_ALL;
   5408             }
   5409             mCurrentRcClient = rcse.mRcClient;
   5410         }
   5411         // will cause onRcDisplayUpdate() to be called in AudioService's handler thread
   5412         mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_UPDATE,
   5413                 infoFlagsAboutToBeUsed /* arg1 */, 0, rcse /* obj, != null */) );
   5414     }
   5415 
   5416     /**
   5417      * Helper function:
   5418      * Called synchronized on mAudioFocusLock, then mRCStack
   5419      * Check whether the remote control display should be updated, triggers the update if required
   5420      * @param infoChangedFlags the flags corresponding to the remote control client information
   5421      *     that has changed, if applicable (checking for the update conditions might trigger a
   5422      *     clear, rather than an update event).
   5423      */
   5424     private void checkUpdateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
   5425         // determine whether the remote control display should be refreshed
   5426         // if either stack is empty, there is a mismatch, so clear the RC display
   5427         if (mRCStack.isEmpty() || mFocusStack.isEmpty()) {
   5428             clearRemoteControlDisplay_syncAfRcs();
   5429             return;
   5430         }
   5431 
   5432         // determine which entry in the AudioFocus stack to consider, and compare against the
   5433         // top of the stack for the media button event receivers : simply using the top of the
   5434         // stack would make the entry disappear from the RemoteControlDisplay in conditions such as
   5435         // notifications playing during music playback.
   5436         // Crawl the AudioFocus stack from the top until an entry is found with the following
   5437         // characteristics:
   5438         // - focus gain on STREAM_MUSIC stream
   5439         // - non-transient focus gain on a stream other than music
   5440         FocusStackEntry af = null;
   5441         try {
   5442             for (int index = mFocusStack.size()-1; index >= 0; index--) {
   5443                 FocusStackEntry fse = mFocusStack.elementAt(index);
   5444                 if ((fse.mStreamType == AudioManager.STREAM_MUSIC)
   5445                         || (fse.mFocusChangeType == AudioManager.AUDIOFOCUS_GAIN)) {
   5446                     af = fse;
   5447                     break;
   5448                 }
   5449             }
   5450         } catch (ArrayIndexOutOfBoundsException e) {
   5451             Log.e(TAG, "Wrong index accessing audio focus stack when updating RCD: " + e);
   5452             af = null;
   5453         }
   5454         if (af == null) {
   5455             clearRemoteControlDisplay_syncAfRcs();
   5456             return;
   5457         }
   5458 
   5459         // if the audio focus and RC owners belong to different packages, there is a mismatch, clear
   5460         if ((mRCStack.peek().mCallingPackageName != null)
   5461                 && (af.mPackageName != null)
   5462                 && !(mRCStack.peek().mCallingPackageName.compareTo(
   5463                         af.mPackageName) == 0)) {
   5464             clearRemoteControlDisplay_syncAfRcs();
   5465             return;
   5466         }
   5467         // if the audio focus didn't originate from the same Uid as the one in which the remote
   5468         //   control information will be retrieved, clear
   5469         if (mRCStack.peek().mCallingUid != af.mCallingUid) {
   5470             clearRemoteControlDisplay_syncAfRcs();
   5471             return;
   5472         }
   5473 
   5474         // refresh conditions were verified: update the remote controls
   5475         // ok to call: synchronized mAudioFocusLock then on mRCStack, mRCStack is not empty
   5476         updateRemoteControlDisplay_syncAfRcs(infoChangedFlags);
   5477     }
   5478 
   5479     /**
   5480      * Helper function:
   5481      * Post a message to asynchronously move the media button event receiver associated with the
   5482      * given remote control client ID to the top of the remote control stack
   5483      * @param rccId
   5484      */
   5485     private void postPromoteRcc(int rccId) {
   5486         sendMsg(mAudioHandler, MSG_PROMOTE_RCC, SENDMSG_REPLACE,
   5487                 rccId /*arg1*/, 0, null, 0/*delay*/);
   5488     }
   5489 
   5490     private void onPromoteRcc(int rccId) {
   5491         if (DEBUG_RC) { Log.d(TAG, "Promoting RCC " + rccId); }
   5492         synchronized(mAudioFocusLock) {
   5493             synchronized(mRCStack) {
   5494                 // ignore if given RCC ID is already at top of remote control stack
   5495                 if (!mRCStack.isEmpty() && (mRCStack.peek().mRccId == rccId)) {
   5496                     return;
   5497                 }
   5498                 int indexToPromote = -1;
   5499                 try {
   5500                     for (int index = mRCStack.size()-1; index >= 0; index--) {
   5501                         final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
   5502                         if (rcse.mRccId == rccId) {
   5503                             indexToPromote = index;
   5504                             break;
   5505                         }
   5506                     }
   5507                     if (indexToPromote >= 0) {
   5508                         if (DEBUG_RC) { Log.d(TAG, "  moving RCC from index " + indexToPromote
   5509                                 + " to " + (mRCStack.size()-1)); }
   5510                         final RemoteControlStackEntry rcse = mRCStack.remove(indexToPromote);
   5511                         mRCStack.push(rcse);
   5512                         // the RC stack changed, reevaluate the display
   5513                         checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
   5514                     }
   5515                 } catch (ArrayIndexOutOfBoundsException e) {
   5516                     // not expected to happen, indicates improper concurrent modification
   5517                     Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
   5518                 }
   5519             }//synchronized(mRCStack)
   5520         }//synchronized(mAudioFocusLock)
   5521     }
   5522 
   5523     /**
   5524      * see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
   5525      * precondition: mediaIntent != null
   5526      */
   5527     public void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver,
   5528             IBinder token) {
   5529         Log.i(TAG, "  Remote Control   registerMediaButtonIntent() for " + mediaIntent);
   5530 
   5531         synchronized(mAudioFocusLock) {
   5532             synchronized(mRCStack) {
   5533                 pushMediaButtonReceiver_syncAfRcs(mediaIntent, eventReceiver, token);
   5534                 // new RC client, assume every type of information shall be queried
   5535                 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
   5536             }
   5537         }
   5538     }
   5539 
   5540     /**
   5541      * see AudioManager.unregisterMediaButtonIntent(PendingIntent mediaIntent)
   5542      * precondition: mediaIntent != null, eventReceiver != null
   5543      */
   5544     public void unregisterMediaButtonIntent(PendingIntent mediaIntent)
   5545     {
   5546         Log.i(TAG, "  Remote Control   unregisterMediaButtonIntent() for " + mediaIntent);
   5547 
   5548         synchronized(mAudioFocusLock) {
   5549             synchronized(mRCStack) {
   5550                 boolean topOfStackWillChange = isCurrentRcController(mediaIntent);
   5551                 removeMediaButtonReceiver_syncAfRcs(mediaIntent);
   5552                 if (topOfStackWillChange) {
   5553                     // current RC client will change, assume every type of info needs to be queried
   5554                     checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
   5555                 }
   5556             }
   5557         }
   5558     }
   5559 
   5560     /**
   5561      * see AudioManager.registerMediaButtonEventReceiverForCalls(ComponentName c)
   5562      * precondition: c != null
   5563      */
   5564     public void registerMediaButtonEventReceiverForCalls(ComponentName c) {
   5565         if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE")
   5566                 != PackageManager.PERMISSION_GRANTED) {
   5567             Log.e(TAG, "Invalid permissions to register media button receiver for calls");
   5568             return;
   5569         }
   5570         synchronized(mRCStack) {
   5571             mMediaReceiverForCalls = c;
   5572         }
   5573     }
   5574 
   5575     /**
   5576      * see AudioManager.unregisterMediaButtonEventReceiverForCalls()
   5577      */
   5578     public void unregisterMediaButtonEventReceiverForCalls() {
   5579         if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE")
   5580                 != PackageManager.PERMISSION_GRANTED) {
   5581             Log.e(TAG, "Invalid permissions to unregister media button receiver for calls");
   5582             return;
   5583         }
   5584         synchronized(mRCStack) {
   5585             mMediaReceiverForCalls = null;
   5586         }
   5587     }
   5588 
   5589     /**
   5590      * see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...)
   5591      * @return the unique ID of the RemoteControlStackEntry associated with the RemoteControlClient
   5592      * Note: using this method with rcClient == null is a way to "disable" the IRemoteControlClient
   5593      *     without modifying the RC stack, but while still causing the display to refresh (will
   5594      *     become blank as a result of this)
   5595      */
   5596     public int registerRemoteControlClient(PendingIntent mediaIntent,
   5597             IRemoteControlClient rcClient, String callingPackageName) {
   5598         if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient);
   5599         int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
   5600         synchronized(mAudioFocusLock) {
   5601             synchronized(mRCStack) {
   5602                 // store the new display information
   5603                 try {
   5604                     for (int index = mRCStack.size()-1; index >= 0; index--) {
   5605                         final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
   5606                         if(rcse.mMediaIntent.equals(mediaIntent)) {
   5607                             // already had a remote control client?
   5608                             if (rcse.mRcClientDeathHandler != null) {
   5609                                 // stop monitoring the old client's death
   5610                                 rcse.unlinkToRcClientDeath();
   5611                             }
   5612                             // save the new remote control client
   5613                             rcse.mRcClient = rcClient;
   5614                             rcse.mCallingPackageName = callingPackageName;
   5615                             rcse.mCallingUid = Binder.getCallingUid();
   5616                             if (rcClient == null) {
   5617                                 // here rcse.mRcClientDeathHandler is null;
   5618                                 rcse.resetPlaybackInfo();
   5619                                 break;
   5620                             }
   5621                             rccId = rcse.mRccId;
   5622 
   5623                             // there is a new (non-null) client:
   5624                             // 1/ give the new client the displays (if any)
   5625                             if (mRcDisplays.size() > 0) {
   5626                                 plugRemoteControlDisplaysIntoClient_syncRcStack(rcse.mRcClient);
   5627                             }
   5628                             // 2/ monitor the new client's death
   5629                             IBinder b = rcse.mRcClient.asBinder();
   5630                             RcClientDeathHandler rcdh =
   5631                                     new RcClientDeathHandler(b, rcse.mMediaIntent);
   5632                             try {
   5633                                 b.linkToDeath(rcdh, 0);
   5634                             } catch (RemoteException e) {
   5635                                 // remote control client is DOA, disqualify it
   5636                                 Log.w(TAG, "registerRemoteControlClient() has a dead client " + b);
   5637                                 rcse.mRcClient = null;
   5638                             }
   5639                             rcse.mRcClientDeathHandler = rcdh;
   5640                             break;
   5641                         }
   5642                     }//for
   5643                 } catch (ArrayIndexOutOfBoundsException e) {
   5644                     // not expected to happen, indicates improper concurrent modification
   5645                     Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
   5646                 }
   5647 
   5648                 // if the eventReceiver is at the top of the stack
   5649                 // then check for potential refresh of the remote controls
   5650                 if (isCurrentRcController(mediaIntent)) {
   5651                     checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
   5652                 }
   5653             }//synchronized(mRCStack)
   5654         }//synchronized(mAudioFocusLock)
   5655         return rccId;
   5656     }
   5657 
   5658     /**
   5659      * see AudioManager.unregisterRemoteControlClient(PendingIntent pi, ...)
   5660      * rcClient is guaranteed non-null
   5661      */
   5662     public void unregisterRemoteControlClient(PendingIntent mediaIntent,
   5663             IRemoteControlClient rcClient) {
   5664         if (DEBUG_RC) Log.i(TAG, "Unregister remote control client rcClient="+rcClient);
   5665         synchronized(mAudioFocusLock) {
   5666             synchronized(mRCStack) {
   5667                 boolean topRccChange = false;
   5668                 try {
   5669                     for (int index = mRCStack.size()-1; index >= 0; index--) {
   5670                         final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
   5671                         if ((rcse.mMediaIntent.equals(mediaIntent))
   5672                                 && rcClient.equals(rcse.mRcClient)) {
   5673                             // we found the IRemoteControlClient to unregister
   5674                             // stop monitoring its death
   5675                             rcse.unlinkToRcClientDeath();
   5676                             // reset the client-related fields
   5677                             rcse.mRcClient = null;
   5678                             rcse.mCallingPackageName = null;
   5679                             topRccChange = (index == mRCStack.size()-1);
   5680                             // there can only be one matching RCC in the RC stack, we're done
   5681                             break;
   5682                         }
   5683                     }
   5684                 } catch (ArrayIndexOutOfBoundsException e) {
   5685                     // not expected to happen, indicates improper concurrent modification
   5686                     Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
   5687                 }
   5688                 if (topRccChange) {
   5689                     // no more RCC for the RCD, check for potential refresh of the remote controls
   5690                     checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
   5691                 }
   5692             }
   5693         }
   5694     }
   5695 
   5696 
   5697     /**
   5698      * A class to encapsulate all the information about a remote control display.
   5699      * After instanciation, init() must always be called before the object is added in the list
   5700      * of displays.
   5701      * Before being removed from the list of displays, release() must always be called (otherwise
   5702      * it will leak death handlers).
   5703      */
   5704     private class DisplayInfoForServer implements IBinder.DeathRecipient {
   5705         /** may never be null */
   5706         private IRemoteControlDisplay mRcDisplay;
   5707         private IBinder mRcDisplayBinder;
   5708         private int mArtworkExpectedWidth = -1;
   5709         private int mArtworkExpectedHeight = -1;
   5710         private boolean mWantsPositionSync = false;
   5711 
   5712         public DisplayInfoForServer(IRemoteControlDisplay rcd, int w, int h) {
   5713             if (DEBUG_RC) Log.i(TAG, "new DisplayInfoForServer for " + rcd + " w=" + w + " h=" + h);
   5714             mRcDisplay = rcd;
   5715             mRcDisplayBinder = rcd.asBinder();
   5716             mArtworkExpectedWidth = w;
   5717             mArtworkExpectedHeight = h;
   5718         }
   5719 
   5720         public boolean init() {
   5721             try {
   5722                 mRcDisplayBinder.linkToDeath(this, 0);
   5723             } catch (RemoteException e) {
   5724                 // remote control display is DOA, disqualify it
   5725                 Log.w(TAG, "registerRemoteControlDisplay() has a dead client " + mRcDisplayBinder);
   5726                 return false;
   5727             }
   5728             return true;
   5729         }
   5730 
   5731         public void release() {
   5732             try {
   5733                 mRcDisplayBinder.unlinkToDeath(this, 0);
   5734             } catch (java.util.NoSuchElementException e) {
   5735                 // not much we can do here, the display should have been unregistered anyway
   5736                 Log.e(TAG, "Error in DisplaInfoForServer.relase()", e);
   5737             }
   5738         }
   5739 
   5740         public void binderDied() {
   5741             synchronized(mRCStack) {
   5742                 Log.w(TAG, "RemoteControl: display " + mRcDisplay + " died");
   5743                 // remove the display from the list
   5744                 final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
   5745                 while (displayIterator.hasNext()) {
   5746                     final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
   5747                     if (di.mRcDisplay == mRcDisplay) {
   5748                         if (DEBUG_RC) Log.w(TAG, " RCD removed from list");
   5749                         displayIterator.remove();
   5750                         return;
   5751                     }
   5752                 }
   5753             }
   5754         }
   5755     }
   5756 
   5757     /**
   5758      * The remote control displays.
   5759      * Access synchronized on mRCStack
   5760      */
   5761     private ArrayList<DisplayInfoForServer> mRcDisplays = new ArrayList<DisplayInfoForServer>(1);
   5762 
   5763     /**
   5764      * Plug each registered display into the specified client
   5765      * @param rcc, guaranteed non null
   5766      */
   5767     private void plugRemoteControlDisplaysIntoClient_syncRcStack(IRemoteControlClient rcc) {
   5768         final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
   5769         while (displayIterator.hasNext()) {
   5770             final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
   5771             try {
   5772                 rcc.plugRemoteControlDisplay(di.mRcDisplay, di.mArtworkExpectedWidth,
   5773                         di.mArtworkExpectedHeight);
   5774                 if (di.mWantsPositionSync) {
   5775                     rcc.setWantsSyncForDisplay(di.mRcDisplay, true);
   5776                 }
   5777             } catch (RemoteException e) {
   5778                 Log.e(TAG, "Error connecting RCD to RCC in RCC registration",e);
   5779             }
   5780         }
   5781     }
   5782 
   5783     /**
   5784      * Is the remote control display interface already registered
   5785      * @param rcd
   5786      * @return true if the IRemoteControlDisplay is already in the list of displays
   5787      */
   5788     private boolean rcDisplayIsPluggedIn_syncRcStack(IRemoteControlDisplay rcd) {
   5789         final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
   5790         while (displayIterator.hasNext()) {
   5791             final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
   5792             if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
   5793                 return true;
   5794             }
   5795         }
   5796         return false;
   5797     }
   5798 
   5799     /**
   5800      * Register an IRemoteControlDisplay.
   5801      * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient
   5802      * at the top of the stack to update the new display with its information.
   5803      * @see android.media.IAudioService#registerRemoteControlDisplay(android.media.IRemoteControlDisplay, int, int)
   5804      * @param rcd the IRemoteControlDisplay to register. No effect if null.
   5805      * @param w the maximum width of the expected bitmap. Negative or zero values indicate this
   5806      *   display doesn't need to receive artwork.
   5807      * @param h the maximum height of the expected bitmap. Negative or zero values indicate this
   5808      *   display doesn't need to receive artwork.
   5809      */
   5810     public void registerRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h) {
   5811         if (DEBUG_RC) Log.d(TAG, ">>> registerRemoteControlDisplay("+rcd+")");
   5812         synchronized(mAudioFocusLock) {
   5813             synchronized(mRCStack) {
   5814                 if ((rcd == null) || rcDisplayIsPluggedIn_syncRcStack(rcd)) {
   5815                     return;
   5816                 }
   5817                 DisplayInfoForServer di = new DisplayInfoForServer(rcd, w, h);
   5818                 if (!di.init()) {
   5819                     if (DEBUG_RC) Log.e(TAG, " error registering RCD");
   5820                     return;
   5821                 }
   5822                 // add RCD to list of displays
   5823                 mRcDisplays.add(di);
   5824 
   5825                 // let all the remote control clients know there is a new display (so the remote
   5826                 //   control stack traversal order doesn't matter).
   5827                 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   5828                 while(stackIterator.hasNext()) {
   5829                     RemoteControlStackEntry rcse = stackIterator.next();
   5830                     if(rcse.mRcClient != null) {
   5831                         try {
   5832                             rcse.mRcClient.plugRemoteControlDisplay(rcd, w, h);
   5833                         } catch (RemoteException e) {
   5834                             Log.e(TAG, "Error connecting RCD to client: ", e);
   5835                         }
   5836                     }
   5837                 }
   5838 
   5839                 // we have a new display, of which all the clients are now aware: have it be updated
   5840                 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
   5841             }
   5842         }
   5843     }
   5844 
   5845     /**
   5846      * Unregister an IRemoteControlDisplay.
   5847      * No effect if the IRemoteControlDisplay hasn't been successfully registered.
   5848      * @see android.media.IAudioService#unregisterRemoteControlDisplay(android.media.IRemoteControlDisplay)
   5849      * @param rcd the IRemoteControlDisplay to unregister. No effect if null.
   5850      */
   5851     public void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) {
   5852         if (DEBUG_RC) Log.d(TAG, "<<< unregisterRemoteControlDisplay("+rcd+")");
   5853         synchronized(mRCStack) {
   5854             if (rcd == null) {
   5855                 return;
   5856             }
   5857 
   5858             boolean displayWasPluggedIn = false;
   5859             final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
   5860             while (displayIterator.hasNext() && !displayWasPluggedIn) {
   5861                 final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
   5862                 if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
   5863                     displayWasPluggedIn = true;
   5864                     di.release();
   5865                     displayIterator.remove();
   5866                 }
   5867             }
   5868 
   5869             if (displayWasPluggedIn) {
   5870                 // disconnect this remote control display from all the clients, so the remote
   5871                 //   control stack traversal order doesn't matter
   5872                 final Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   5873                 while(stackIterator.hasNext()) {
   5874                     final RemoteControlStackEntry rcse = stackIterator.next();
   5875                     if(rcse.mRcClient != null) {
   5876                         try {
   5877                             rcse.mRcClient.unplugRemoteControlDisplay(rcd);
   5878                         } catch (RemoteException e) {
   5879                             Log.e(TAG, "Error disconnecting remote control display to client: ", e);
   5880                         }
   5881                     }
   5882                 }
   5883             } else {
   5884                 if (DEBUG_RC) Log.w(TAG, "  trying to unregister unregistered RCD");
   5885             }
   5886         }
   5887     }
   5888 
   5889     /**
   5890      * Update the size of the artwork used by an IRemoteControlDisplay.
   5891      * @see android.media.IAudioService#remoteControlDisplayUsesBitmapSize(android.media.IRemoteControlDisplay, int, int)
   5892      * @param rcd the IRemoteControlDisplay with the new artwork size requirement
   5893      * @param w the maximum width of the expected bitmap. Negative or zero values indicate this
   5894      *   display doesn't need to receive artwork.
   5895      * @param h the maximum height of the expected bitmap. Negative or zero values indicate this
   5896      *   display doesn't need to receive artwork.
   5897      */
   5898     public void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) {
   5899         synchronized(mRCStack) {
   5900             final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
   5901             boolean artworkSizeUpdate = false;
   5902             while (displayIterator.hasNext() && !artworkSizeUpdate) {
   5903                 final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
   5904                 if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
   5905                     if ((di.mArtworkExpectedWidth != w) || (di.mArtworkExpectedHeight != h)) {
   5906                         di.mArtworkExpectedWidth = w;
   5907                         di.mArtworkExpectedHeight = h;
   5908                         artworkSizeUpdate = true;
   5909                     }
   5910                 }
   5911             }
   5912             if (artworkSizeUpdate) {
   5913                 // RCD is currently plugged in and its artwork size has changed, notify all RCCs,
   5914                 // stack traversal order doesn't matter
   5915                 final Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   5916                 while(stackIterator.hasNext()) {
   5917                     final RemoteControlStackEntry rcse = stackIterator.next();
   5918                     if(rcse.mRcClient != null) {
   5919                         try {
   5920                             rcse.mRcClient.setBitmapSizeForDisplay(rcd, w, h);
   5921                         } catch (RemoteException e) {
   5922                             Log.e(TAG, "Error setting bitmap size for RCD on RCC: ", e);
   5923                         }
   5924                     }
   5925                 }
   5926             }
   5927         }
   5928     }
   5929 
   5930     /**
   5931      * Controls whether a remote control display needs periodic checks of the RemoteControlClient
   5932      * playback position to verify that the estimated position has not drifted from the actual
   5933      * position. By default the check is not performed.
   5934      * The IRemoteControlDisplay must have been previously registered for this to have any effect.
   5935      * @param rcd the IRemoteControlDisplay for which the anti-drift mechanism will be enabled
   5936      *     or disabled. Not null.
   5937      * @param wantsSync if true, RemoteControlClient instances which expose their playback position
   5938      *     to the framework will regularly compare the estimated playback position with the actual
   5939      *     position, and will update the IRemoteControlDisplay implementation whenever a drift is
   5940      *     detected.
   5941      */
   5942     public void remoteControlDisplayWantsPlaybackPositionSync(IRemoteControlDisplay rcd,
   5943             boolean wantsSync) {
   5944         synchronized(mRCStack) {
   5945             boolean rcdRegistered = false;
   5946             // store the information about this display
   5947             // (display stack traversal order doesn't matter).
   5948             final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
   5949             while (displayIterator.hasNext()) {
   5950                 final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
   5951                 if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
   5952                     di.mWantsPositionSync = wantsSync;
   5953                     rcdRegistered = true;
   5954                     break;
   5955                 }
   5956             }
   5957             if (!rcdRegistered) {
   5958                 return;
   5959             }
   5960             // notify all current RemoteControlClients
   5961             // (stack traversal order doesn't matter as we notify all RCCs)
   5962             final Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   5963             while (stackIterator.hasNext()) {
   5964                 final RemoteControlStackEntry rcse = stackIterator.next();
   5965                 if (rcse.mRcClient != null) {
   5966                     try {
   5967                         rcse.mRcClient.setWantsSyncForDisplay(rcd, wantsSync);
   5968                     } catch (RemoteException e) {
   5969                         Log.e(TAG, "Error setting position sync flag for RCD on RCC: ", e);
   5970                     }
   5971                 }
   5972             }
   5973         }
   5974     }
   5975 
   5976     public void setRemoteControlClientPlaybackPosition(int generationId, long timeMs) {
   5977         // ignore position change requests if invalid generation ID
   5978         synchronized(mRCStack) {
   5979             synchronized(mCurrentRcLock) {
   5980                 if (mCurrentRcClientGen != generationId) {
   5981                     return;
   5982                 }
   5983             }
   5984         }
   5985         // discard any unprocessed seek request in the message queue, and replace with latest
   5986         sendMsg(mAudioHandler, MSG_RCC_SEEK_REQUEST, SENDMSG_REPLACE, generationId /* arg1 */,
   5987                 0 /* arg2 ignored*/, new Long(timeMs) /* obj */, 0 /* delay */);
   5988     }
   5989 
   5990     public void onSetRemoteControlClientPlaybackPosition(int generationId, long timeMs) {
   5991         if(DEBUG_RC) Log.d(TAG, "onSetRemoteControlClientPlaybackPosition(genId=" + generationId +
   5992                 ", timeMs=" + timeMs + ")");
   5993         synchronized(mRCStack) {
   5994             synchronized(mCurrentRcLock) {
   5995                 if ((mCurrentRcClient != null) && (mCurrentRcClientGen == generationId)) {
   5996                     // tell the current client to seek to the requested location
   5997                     try {
   5998                         mCurrentRcClient.seekTo(generationId, timeMs);
   5999                     } catch (RemoteException e) {
   6000                         Log.e(TAG, "Current valid remote client is dead: "+e);
   6001                         mCurrentRcClient = null;
   6002                     }
   6003                 }
   6004             }
   6005         }
   6006     }
   6007 
   6008     public void setPlaybackInfoForRcc(int rccId, int what, int value) {
   6009         sendMsg(mAudioHandler, MSG_RCC_NEW_PLAYBACK_INFO, SENDMSG_QUEUE,
   6010                 rccId /* arg1 */, what /* arg2 */, Integer.valueOf(value) /* obj */, 0 /* delay */);
   6011     }
   6012 
   6013     // handler for MSG_RCC_NEW_PLAYBACK_INFO
   6014     private void onNewPlaybackInfoForRcc(int rccId, int key, int value) {
   6015         if(DEBUG_RC) Log.d(TAG, "onNewPlaybackInfoForRcc(id=" + rccId +
   6016                 ", what=" + key + ",val=" + value + ")");
   6017         synchronized(mRCStack) {
   6018             // iterating from top of stack as playback information changes are more likely
   6019             //   on entries at the top of the remote control stack
   6020             try {
   6021                 for (int index = mRCStack.size()-1; index >= 0; index--) {
   6022                     final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
   6023                     if (rcse.mRccId == rccId) {
   6024                         switch (key) {
   6025                             case RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE:
   6026                                 rcse.mPlaybackType = value;
   6027                                 postReevaluateRemote();
   6028                                 break;
   6029                             case RemoteControlClient.PLAYBACKINFO_VOLUME:
   6030                                 rcse.mPlaybackVolume = value;
   6031                                 synchronized (mMainRemote) {
   6032                                     if (rccId == mMainRemote.mRccId) {
   6033                                         mMainRemote.mVolume = value;
   6034                                         mVolumePanel.postHasNewRemotePlaybackInfo();
   6035                                     }
   6036                                 }
   6037                                 break;
   6038                             case RemoteControlClient.PLAYBACKINFO_VOLUME_MAX:
   6039                                 rcse.mPlaybackVolumeMax = value;
   6040                                 synchronized (mMainRemote) {
   6041                                     if (rccId == mMainRemote.mRccId) {
   6042                                         mMainRemote.mVolumeMax = value;
   6043                                         mVolumePanel.postHasNewRemotePlaybackInfo();
   6044                                     }
   6045                                 }
   6046                                 break;
   6047                             case RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING:
   6048                                 rcse.mPlaybackVolumeHandling = value;
   6049                                 synchronized (mMainRemote) {
   6050                                     if (rccId == mMainRemote.mRccId) {
   6051                                         mMainRemote.mVolumeHandling = value;
   6052                                         mVolumePanel.postHasNewRemotePlaybackInfo();
   6053                                     }
   6054                                 }
   6055                                 break;
   6056                             case RemoteControlClient.PLAYBACKINFO_USES_STREAM:
   6057                                 rcse.mPlaybackStream = value;
   6058                                 break;
   6059                             default:
   6060                                 Log.e(TAG, "unhandled key " + key + " for RCC " + rccId);
   6061                                 break;
   6062                         }
   6063                         return;
   6064                     }
   6065                 }//for
   6066             } catch (ArrayIndexOutOfBoundsException e) {
   6067                 // not expected to happen, indicates improper concurrent modification
   6068                 Log.e(TAG, "Wrong index mRCStack on onNewPlaybackInfoForRcc, lock error? ", e);
   6069             }
   6070         }
   6071     }
   6072 
   6073     public void setPlaybackStateForRcc(int rccId, int state, long timeMs, float speed) {
   6074         sendMsg(mAudioHandler, MSG_RCC_NEW_PLAYBACK_STATE, SENDMSG_QUEUE,
   6075                 rccId /* arg1 */, state /* arg2 */,
   6076                 new RccPlaybackState(state, timeMs, speed) /* obj */, 0 /* delay */);
   6077     }
   6078 
   6079     public void onNewPlaybackStateForRcc(int rccId, int state, RccPlaybackState newState) {
   6080         if(DEBUG_RC) Log.d(TAG, "onNewPlaybackStateForRcc(id=" + rccId + ", state=" + state
   6081                 + ", time=" + newState.mPositionMs + ", speed=" + newState.mSpeed + ")");
   6082         synchronized(mRCStack) {
   6083             // iterating from top of stack as playback information changes are more likely
   6084             //   on entries at the top of the remote control stack
   6085             try {
   6086                 for (int index = mRCStack.size()-1; index >= 0; index--) {
   6087                     final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
   6088                     if (rcse.mRccId == rccId) {
   6089                         rcse.mPlaybackState = newState;
   6090                         synchronized (mMainRemote) {
   6091                             if (rccId == mMainRemote.mRccId) {
   6092                                 mMainRemoteIsActive = isPlaystateActive(state);
   6093                                 postReevaluateRemote();
   6094                             }
   6095                         }
   6096                         // an RCC moving to a "playing" state should become the media button
   6097                         //   event receiver so it can be controlled, without requiring the
   6098                         //   app to re-register its receiver
   6099                         if (isPlaystateActive(state)) {
   6100                             postPromoteRcc(rccId);
   6101                         }
   6102                     }
   6103                 }//for
   6104             } catch (ArrayIndexOutOfBoundsException e) {
   6105                 // not expected to happen, indicates improper concurrent modification
   6106                 Log.e(TAG, "Wrong index on mRCStack in onNewPlaybackStateForRcc, lock error? ", e);
   6107             }
   6108         }
   6109     }
   6110 
   6111     public void registerRemoteVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
   6112         sendMsg(mAudioHandler, MSG_RCC_NEW_VOLUME_OBS, SENDMSG_QUEUE,
   6113                 rccId /* arg1 */, 0, rvo /* obj */, 0 /* delay */);
   6114     }
   6115 
   6116     // handler for MSG_RCC_NEW_VOLUME_OBS
   6117     private void onRegisterVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
   6118         synchronized(mRCStack) {
   6119             // The stack traversal order doesn't matter because there is only one stack entry
   6120             //  with this RCC ID, but the matching ID is more likely at the top of the stack, so
   6121             //  start iterating from the top.
   6122             try {
   6123                 for (int index = mRCStack.size()-1; index >= 0; index--) {
   6124                     final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
   6125                     if (rcse.mRccId == rccId) {
   6126                         rcse.mRemoteVolumeObs = rvo;
   6127                         break;
   6128                     }
   6129                 }
   6130             } catch (ArrayIndexOutOfBoundsException e) {
   6131                 // not expected to happen, indicates improper concurrent modification
   6132                 Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
   6133             }
   6134         }
   6135     }
   6136 
   6137     /**
   6138      * Checks if a remote client is active on the supplied stream type. Update the remote stream
   6139      * volume state if found and playing
   6140      * @param streamType
   6141      * @return false if no remote playing is currently playing
   6142      */
   6143     private boolean checkUpdateRemoteStateIfActive(int streamType) {
   6144         synchronized(mRCStack) {
   6145             // iterating from top of stack as active playback is more likely on entries at the top
   6146             try {
   6147                 for (int index = mRCStack.size()-1; index >= 0; index--) {
   6148                     final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
   6149                     if ((rcse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE)
   6150                             && isPlaystateActive(rcse.mPlaybackState.mState)
   6151                             && (rcse.mPlaybackStream == streamType)) {
   6152                         if (DEBUG_RC) Log.d(TAG, "remote playback active on stream " + streamType
   6153                                 + ", vol =" + rcse.mPlaybackVolume);
   6154                         synchronized (mMainRemote) {
   6155                             mMainRemote.mRccId = rcse.mRccId;
   6156                             mMainRemote.mVolume = rcse.mPlaybackVolume;
   6157                             mMainRemote.mVolumeMax = rcse.mPlaybackVolumeMax;
   6158                             mMainRemote.mVolumeHandling = rcse.mPlaybackVolumeHandling;
   6159                             mMainRemoteIsActive = true;
   6160                         }
   6161                         return true;
   6162                     }
   6163                 }
   6164             } catch (ArrayIndexOutOfBoundsException e) {
   6165                 // not expected to happen, indicates improper concurrent modification
   6166                 Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
   6167             }
   6168         }
   6169         synchronized (mMainRemote) {
   6170             mMainRemoteIsActive = false;
   6171         }
   6172         return false;
   6173     }
   6174 
   6175     /**
   6176      * Returns true if the given playback state is considered "active", i.e. it describes a state
   6177      * where playback is happening, or about to
   6178      * @param playState the playback state to evaluate
   6179      * @return true if active, false otherwise (inactive or unknown)
   6180      */
   6181     private static boolean isPlaystateActive(int playState) {
   6182         switch (playState) {
   6183             case RemoteControlClient.PLAYSTATE_PLAYING:
   6184             case RemoteControlClient.PLAYSTATE_BUFFERING:
   6185             case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
   6186             case RemoteControlClient.PLAYSTATE_REWINDING:
   6187             case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
   6188             case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
   6189                 return true;
   6190             default:
   6191                 return false;
   6192         }
   6193     }
   6194 
   6195     private void adjustRemoteVolume(int streamType, int direction, int flags) {
   6196         int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
   6197         boolean volFixed = false;
   6198         synchronized (mMainRemote) {
   6199             if (!mMainRemoteIsActive) {
   6200                 if (DEBUG_VOL) Log.w(TAG, "adjustRemoteVolume didn't find an active client");
   6201                 return;
   6202             }
   6203             rccId = mMainRemote.mRccId;
   6204             volFixed = (mMainRemote.mVolumeHandling ==
   6205                     RemoteControlClient.PLAYBACK_VOLUME_FIXED);
   6206         }
   6207         // unlike "local" stream volumes, we can't compute the new volume based on the direction,
   6208         // we can only notify the remote that volume needs to be updated, and we'll get an async'
   6209         // update through setPlaybackInfoForRcc()
   6210         if (!volFixed) {
   6211             sendVolumeUpdateToRemote(rccId, direction);
   6212         }
   6213 
   6214         // fire up the UI
   6215         mVolumePanel.postRemoteVolumeChanged(streamType, flags);
   6216     }
   6217 
   6218     private void sendVolumeUpdateToRemote(int rccId, int direction) {
   6219         if (DEBUG_VOL) { Log.d(TAG, "sendVolumeUpdateToRemote(rccId="+rccId+" , dir="+direction); }
   6220         if (direction == 0) {
   6221             // only handling discrete events
   6222             return;
   6223         }
   6224         IRemoteVolumeObserver rvo = null;
   6225         synchronized (mRCStack) {
   6226             // The stack traversal order doesn't matter because there is only one stack entry
   6227             //  with this RCC ID, but the matching ID is more likely at the top of the stack, so
   6228             //  start iterating from the top.
   6229             try {
   6230                 for (int index = mRCStack.size()-1; index >= 0; index--) {
   6231                     final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
   6232                     //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
   6233                     if (rcse.mRccId == rccId) {
   6234                         rvo = rcse.mRemoteVolumeObs;
   6235                         break;
   6236                     }
   6237                 }
   6238             } catch (ArrayIndexOutOfBoundsException e) {
   6239                 // not expected to happen, indicates improper concurrent modification
   6240                 Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
   6241             }
   6242         }
   6243         if (rvo != null) {
   6244             try {
   6245                 rvo.dispatchRemoteVolumeUpdate(direction, -1);
   6246             } catch (RemoteException e) {
   6247                 Log.e(TAG, "Error dispatching relative volume update", e);
   6248             }
   6249         }
   6250     }
   6251 
   6252     public int getRemoteStreamMaxVolume() {
   6253         synchronized (mMainRemote) {
   6254             if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
   6255                 return 0;
   6256             }
   6257             return mMainRemote.mVolumeMax;
   6258         }
   6259     }
   6260 
   6261     public int getRemoteStreamVolume() {
   6262         synchronized (mMainRemote) {
   6263             if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
   6264                 return 0;
   6265             }
   6266             return mMainRemote.mVolume;
   6267         }
   6268     }
   6269 
   6270     public void setRemoteStreamVolume(int vol) {
   6271         if (DEBUG_VOL) { Log.d(TAG, "setRemoteStreamVolume(vol="+vol+")"); }
   6272         int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
   6273         synchronized (mMainRemote) {
   6274             if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
   6275                 return;
   6276             }
   6277             rccId = mMainRemote.mRccId;
   6278         }
   6279         IRemoteVolumeObserver rvo = null;
   6280         synchronized (mRCStack) {
   6281             // The stack traversal order doesn't matter because there is only one stack entry
   6282             //  with this RCC ID, but the matching ID is more likely at the top of the stack, so
   6283             //  start iterating from the top.
   6284             try {
   6285                 for (int index = mRCStack.size()-1; index >= 0; index--) {
   6286                     final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
   6287                     //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
   6288                     if (rcse.mRccId == rccId) {
   6289                         rvo = rcse.mRemoteVolumeObs;
   6290                         break;
   6291                     }
   6292                 }
   6293             } catch (ArrayIndexOutOfBoundsException e) {
   6294                 // not expected to happen, indicates improper concurrent modification
   6295                 Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
   6296             }
   6297         }
   6298         if (rvo != null) {
   6299             try {
   6300                 rvo.dispatchRemoteVolumeUpdate(0, vol);
   6301             } catch (RemoteException e) {
   6302                 Log.e(TAG, "Error dispatching absolute volume update", e);
   6303             }
   6304         }
   6305     }
   6306 
   6307     /**
   6308      * Call to make AudioService reevaluate whether it's in a mode where remote players should
   6309      * have their volume controlled. In this implementation this is only to reset whether
   6310      * VolumePanel should display remote volumes
   6311      */
   6312     private void postReevaluateRemote() {
   6313         sendMsg(mAudioHandler, MSG_REEVALUATE_REMOTE, SENDMSG_QUEUE, 0, 0, null, 0);
   6314     }
   6315 
   6316     private void onReevaluateRemote() {
   6317         if (DEBUG_VOL) { Log.w(TAG, "onReevaluateRemote()"); }
   6318         // is there a registered RemoteControlClient that is handling remote playback
   6319         boolean hasRemotePlayback = false;
   6320         synchronized (mRCStack) {
   6321             // iteration stops when PLAYBACK_TYPE_REMOTE is found, so remote control stack
   6322             //   traversal order doesn't matter
   6323             Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
   6324             while(stackIterator.hasNext()) {
   6325                 RemoteControlStackEntry rcse = stackIterator.next();
   6326                 if (rcse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) {
   6327                     hasRemotePlayback = true;
   6328                     break;
   6329                 }
   6330             }
   6331         }
   6332         synchronized (mMainRemote) {
   6333             if (mHasRemotePlayback != hasRemotePlayback) {
   6334                 mHasRemotePlayback = hasRemotePlayback;
   6335                 mVolumePanel.postRemoteSliderVisibility(hasRemotePlayback);
   6336             }
   6337         }
   6338     }
   6339 
   6340     //==========================================================================================
   6341     // Device orientation
   6342     //==========================================================================================
   6343     /**
   6344      * Handles device configuration changes that may map to a change in the orientation
   6345      * or orientation.
   6346      * Monitoring orientation and rotation is optional, and is defined by the definition and value
   6347      * of the "ro.audio.monitorOrientation" and "ro.audio.monitorRotation" system properties.
   6348      */
   6349     private void handleConfigurationChanged(Context context) {
   6350         try {
   6351             // reading new orientation "safely" (i.e. under try catch) in case anything
   6352             // goes wrong when obtaining resources and configuration
   6353             Configuration config = context.getResources().getConfiguration();
   6354             // TODO merge rotation and orientation
   6355             if (mMonitorOrientation) {
   6356                 int newOrientation = config.orientation;
   6357                 if (newOrientation != mDeviceOrientation) {
   6358                     mDeviceOrientation = newOrientation;
   6359                     setOrientationForAudioSystem();
   6360                 }
   6361             }
   6362             if (mMonitorRotation) {
   6363                 int newRotation = ((WindowManager) context.getSystemService(
   6364                         Context.WINDOW_SERVICE)).getDefaultDisplay().getRotation();
   6365                 if (newRotation != mDeviceRotation) {
   6366                     mDeviceRotation = newRotation;
   6367                     setRotationForAudioSystem();
   6368                 }
   6369             }
   6370             sendMsg(mAudioHandler,
   6371                     MSG_CONFIGURE_SAFE_MEDIA_VOLUME,
   6372                     SENDMSG_REPLACE,
   6373                     0,
   6374                     0,
   6375                     null,
   6376                     0);
   6377 
   6378             boolean cameraSoundForced = mContext.getResources().getBoolean(
   6379                     com.android.internal.R.bool.config_camera_sound_forced);
   6380             synchronized (mSettingsLock) {
   6381                 synchronized (mCameraSoundForced) {
   6382                     if (cameraSoundForced != mCameraSoundForced) {
   6383                         mCameraSoundForced = cameraSoundForced;
   6384 
   6385                         VolumeStreamState s = mStreamStates[AudioSystem.STREAM_SYSTEM_ENFORCED];
   6386                         if (cameraSoundForced) {
   6387                             s.setAllIndexesToMax();
   6388                             mRingerModeAffectedStreams &=
   6389                                     ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
   6390                         } else {
   6391                             s.setAllIndexes(mStreamStates[AudioSystem.STREAM_SYSTEM]);
   6392                             mRingerModeAffectedStreams |=
   6393                                     (1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
   6394                         }
   6395                         // take new state into account for streams muted by ringer mode
   6396                         setRingerModeInt(getRingerMode(), false);
   6397 
   6398                         sendMsg(mAudioHandler,
   6399                                 MSG_SET_FORCE_USE,
   6400                                 SENDMSG_QUEUE,
   6401                                 AudioSystem.FOR_SYSTEM,
   6402                                 cameraSoundForced ?
   6403                                         AudioSystem.FORCE_SYSTEM_ENFORCED : AudioSystem.FORCE_NONE,
   6404                                 null,
   6405                                 0);
   6406 
   6407                         sendMsg(mAudioHandler,
   6408                                 MSG_SET_ALL_VOLUMES,
   6409                                 SENDMSG_QUEUE,
   6410                                 0,
   6411                                 0,
   6412                                 mStreamStates[AudioSystem.STREAM_SYSTEM_ENFORCED], 0);
   6413                     }
   6414                 }
   6415             }
   6416             mVolumePanel.setLayoutDirection(config.getLayoutDirection());
   6417         } catch (Exception e) {
   6418             Log.e(TAG, "Error handling configuration change: ", e);
   6419         }
   6420     }
   6421 
   6422     private void setOrientationForAudioSystem() {
   6423         switch (mDeviceOrientation) {
   6424             case Configuration.ORIENTATION_LANDSCAPE:
   6425                 //Log.i(TAG, "orientation is landscape");
   6426                 AudioSystem.setParameters("orientation=landscape");
   6427                 break;
   6428             case Configuration.ORIENTATION_PORTRAIT:
   6429                 //Log.i(TAG, "orientation is portrait");
   6430                 AudioSystem.setParameters("orientation=portrait");
   6431                 break;
   6432             case Configuration.ORIENTATION_SQUARE:
   6433                 //Log.i(TAG, "orientation is square");
   6434                 AudioSystem.setParameters("orientation=square");
   6435                 break;
   6436             case Configuration.ORIENTATION_UNDEFINED:
   6437                 //Log.i(TAG, "orientation is undefined");
   6438                 AudioSystem.setParameters("orientation=undefined");
   6439                 break;
   6440             default:
   6441                 Log.e(TAG, "Unknown orientation");
   6442         }
   6443     }
   6444 
   6445     private void setRotationForAudioSystem() {
   6446         switch (mDeviceRotation) {
   6447             case Surface.ROTATION_0:
   6448                 AudioSystem.setParameters("rotation=0");
   6449                 break;
   6450             case Surface.ROTATION_90:
   6451                 AudioSystem.setParameters("rotation=90");
   6452                 break;
   6453             case Surface.ROTATION_180:
   6454                 AudioSystem.setParameters("rotation=180");
   6455                 break;
   6456             case Surface.ROTATION_270:
   6457                 AudioSystem.setParameters("rotation=270");
   6458                 break;
   6459             default:
   6460                 Log.e(TAG, "Unknown device rotation");
   6461         }
   6462     }
   6463 
   6464 
   6465     // Handles request to override default use of A2DP for media.
   6466     public void setBluetoothA2dpOnInt(boolean on) {
   6467         synchronized (mBluetoothA2dpEnabledLock) {
   6468             mBluetoothA2dpEnabled = on;
   6469             mAudioHandler.removeMessages(MSG_SET_FORCE_BT_A2DP_USE);
   6470             AudioSystem.setForceUse(AudioSystem.FOR_MEDIA,
   6471                     mBluetoothA2dpEnabled ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP);
   6472         }
   6473     }
   6474 
   6475     @Override
   6476     public void setRingtonePlayer(IRingtonePlayer player) {
   6477         mContext.enforceCallingOrSelfPermission(REMOTE_AUDIO_PLAYBACK, null);
   6478         mRingtonePlayer = player;
   6479     }
   6480 
   6481     @Override
   6482     public IRingtonePlayer getRingtonePlayer() {
   6483         return mRingtonePlayer;
   6484     }
   6485 
   6486     @Override
   6487     public AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
   6488         synchronized (mCurAudioRoutes) {
   6489             AudioRoutesInfo routes = new AudioRoutesInfo(mCurAudioRoutes);
   6490             mRoutesObservers.register(observer);
   6491             return routes;
   6492         }
   6493     }
   6494 
   6495 
   6496     //==========================================================================================
   6497     // Safe media volume management.
   6498     // MUSIC stream volume level is limited when headphones are connected according to safety
   6499     // regulation. When the user attempts to raise the volume above the limit, a warning is
   6500     // displayed and the user has to acknowlegde before the volume is actually changed.
   6501     // The volume index corresponding to the limit is stored in config_safe_media_volume_index
   6502     // property. Platforms with a different limit must set this property accordingly in their
   6503     // overlay.
   6504     //==========================================================================================
   6505 
   6506     // mSafeMediaVolumeState indicates whether the media volume is limited over headphones.
   6507     // It is SAFE_MEDIA_VOLUME_NOT_CONFIGURED at boot time until a network service is connected
   6508     // or the configure time is elapsed. It is then set to SAFE_MEDIA_VOLUME_ACTIVE or
   6509     // SAFE_MEDIA_VOLUME_DISABLED according to country option. If not SAFE_MEDIA_VOLUME_DISABLED, it
   6510     // can be set to SAFE_MEDIA_VOLUME_INACTIVE by calling AudioService.disableSafeMediaVolume()
   6511     // (when user opts out).
   6512     private final int SAFE_MEDIA_VOLUME_NOT_CONFIGURED = 0;
   6513     private final int SAFE_MEDIA_VOLUME_DISABLED = 1;
   6514     private final int SAFE_MEDIA_VOLUME_INACTIVE = 2;
   6515     private final int SAFE_MEDIA_VOLUME_ACTIVE = 3;
   6516     private Integer mSafeMediaVolumeState;
   6517 
   6518     private int mMcc = 0;
   6519     // mSafeMediaVolumeIndex is the cached value of config_safe_media_volume_index property
   6520     private int mSafeMediaVolumeIndex;
   6521     // mSafeMediaVolumeDevices lists the devices for which safe media volume is enforced,
   6522     private final int mSafeMediaVolumeDevices = AudioSystem.DEVICE_OUT_WIRED_HEADSET |
   6523                                                 AudioSystem.DEVICE_OUT_WIRED_HEADPHONE;
   6524     // mMusicActiveMs is the cumulative time of music activity since safe volume was disabled.
   6525     // When this time reaches UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX, the safe media volume is re-enabled
   6526     // automatically. mMusicActiveMs is rounded to a multiple of MUSIC_ACTIVE_POLL_PERIOD_MS.
   6527     private int mMusicActiveMs;
   6528     private static final int UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX = (20 * 3600 * 1000); // 20 hours
   6529     private static final int MUSIC_ACTIVE_POLL_PERIOD_MS = 60000;  // 1 minute polling interval
   6530     private static final int SAFE_VOLUME_CONFIGURE_TIMEOUT_MS = 30000;  // 30s after boot completed
   6531 
   6532     private void setSafeMediaVolumeEnabled(boolean on) {
   6533         synchronized (mSafeMediaVolumeState) {
   6534             if ((mSafeMediaVolumeState != SAFE_MEDIA_VOLUME_NOT_CONFIGURED) &&
   6535                     (mSafeMediaVolumeState != SAFE_MEDIA_VOLUME_DISABLED)) {
   6536                 if (on && (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_INACTIVE)) {
   6537                     mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_ACTIVE;
   6538                     enforceSafeMediaVolume();
   6539                 } else if (!on && (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE)) {
   6540                     mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_INACTIVE;
   6541                     mMusicActiveMs = 0;
   6542                     sendMsg(mAudioHandler,
   6543                             MSG_CHECK_MUSIC_ACTIVE,
   6544                             SENDMSG_REPLACE,
   6545                             0,
   6546                             0,
   6547                             null,
   6548                             MUSIC_ACTIVE_POLL_PERIOD_MS);
   6549                 }
   6550             }
   6551         }
   6552     }
   6553 
   6554     private void enforceSafeMediaVolume() {
   6555         VolumeStreamState streamState = mStreamStates[AudioSystem.STREAM_MUSIC];
   6556         int devices = mSafeMediaVolumeDevices;
   6557         int i = 0;
   6558 
   6559         while (devices != 0) {
   6560             int device = 1 << i++;
   6561             if ((device & devices) == 0) {
   6562                 continue;
   6563             }
   6564             int index = streamState.getIndex(device);
   6565             if (index > mSafeMediaVolumeIndex) {
   6566                 streamState.setIndex(mSafeMediaVolumeIndex, device);
   6567                 sendMsg(mAudioHandler,
   6568                         MSG_SET_DEVICE_VOLUME,
   6569                         SENDMSG_QUEUE,
   6570                         device,
   6571                         0,
   6572                         streamState,
   6573                         0);
   6574             }
   6575             devices &= ~device;
   6576         }
   6577     }
   6578 
   6579     private boolean checkSafeMediaVolume(int streamType, int index, int device) {
   6580         synchronized (mSafeMediaVolumeState) {
   6581             if ((mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE) &&
   6582                     (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) &&
   6583                     ((device & mSafeMediaVolumeDevices) != 0) &&
   6584                     (index > mSafeMediaVolumeIndex)) {
   6585                 return false;
   6586             }
   6587             return true;
   6588         }
   6589     }
   6590 
   6591     public void disableSafeMediaVolume() {
   6592         synchronized (mSafeMediaVolumeState) {
   6593             setSafeMediaVolumeEnabled(false);
   6594             if (mPendingVolumeCommand != null) {
   6595                 onSetStreamVolume(mPendingVolumeCommand.mStreamType,
   6596                                   mPendingVolumeCommand.mIndex,
   6597                                   mPendingVolumeCommand.mFlags,
   6598                                   mPendingVolumeCommand.mDevice);
   6599                 mPendingVolumeCommand = null;
   6600             }
   6601         }
   6602     }
   6603 
   6604 
   6605     //==========================================================================================
   6606     // Camera shutter sound policy.
   6607     // config_camera_sound_forced configuration option in config.xml defines if the camera shutter
   6608     // sound is forced (sound even if the device is in silent mode) or not. This option is false by
   6609     // default and can be overridden by country specific overlay in values-mccXXX/config.xml.
   6610     //==========================================================================================
   6611 
   6612     // cached value of com.android.internal.R.bool.config_camera_sound_forced
   6613     private Boolean mCameraSoundForced;
   6614 
   6615     // called by android.hardware.Camera to populate CameraInfo.canDisableShutterSound
   6616     public boolean isCameraSoundForced() {
   6617         synchronized (mCameraSoundForced) {
   6618             return mCameraSoundForced;
   6619         }
   6620     }
   6621 
   6622     private static final String[] RINGER_MODE_NAMES = new String[] {
   6623             "SILENT",
   6624             "VIBRATE",
   6625             "NORMAL"
   6626     };
   6627 
   6628     private void dumpRingerMode(PrintWriter pw) {
   6629         pw.println("\nRinger mode: ");
   6630         pw.println("- mode: "+RINGER_MODE_NAMES[mRingerMode]);
   6631         pw.print("- ringer mode affected streams = 0x");
   6632         pw.println(Integer.toHexString(mRingerModeAffectedStreams));
   6633         pw.print("- ringer mode muted streams = 0x");
   6634         pw.println(Integer.toHexString(mRingerModeMutedStreams));
   6635     }
   6636 
   6637     @Override
   6638     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   6639         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
   6640 
   6641         dumpFocusStack(pw);
   6642         dumpRCStack(pw);
   6643         dumpRCCStack(pw);
   6644         dumpRCDList(pw);
   6645         dumpStreamStates(pw);
   6646         dumpRingerMode(pw);
   6647         pw.println("\nAudio routes:");
   6648         pw.print("  mMainType=0x"); pw.println(Integer.toHexString(mCurAudioRoutes.mMainType));
   6649         pw.print("  mBluetoothName="); pw.println(mCurAudioRoutes.mBluetoothName);
   6650     }
   6651 }
   6652