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