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