AudioService.java revision d5176cfe6eae954e9cef1e2ec17859a5089e1330
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 android.app.ActivityManagerNative;
20import android.content.BroadcastReceiver;
21import android.content.ContentResolver;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
25import android.bluetooth.BluetoothA2dp;
26import android.bluetooth.BluetoothClass;
27import android.bluetooth.BluetoothDevice;
28import android.bluetooth.BluetoothHeadset;
29import android.content.pm.PackageManager;
30import android.database.ContentObserver;
31import android.media.MediaPlayer.OnCompletionListener;
32import android.media.MediaPlayer.OnErrorListener;
33import android.os.Binder;
34import android.os.Environment;
35import android.os.Handler;
36import android.os.IBinder;
37import android.os.Looper;
38import android.os.Message;
39import android.os.RemoteException;
40import android.os.ServiceManager;
41import android.provider.Settings;
42import android.provider.Settings.System;
43import android.util.Log;
44import android.view.VolumePanel;
45import android.os.SystemProperties;
46
47import com.android.internal.telephony.ITelephony;
48
49import java.io.FileDescriptor;
50import java.io.IOException;
51import java.io.PrintWriter;
52import java.util.ArrayList;
53import java.util.HashMap;
54import java.util.Iterator;
55import java.util.Map;
56import java.util.Set;
57import java.util.Stack;
58
59/**
60 * The implementation of the volume manager service.
61 * <p>
62 * This implementation focuses on delivering a responsive UI. Most methods are
63 * asynchronous to external calls. For example, the task of setting a volume
64 * will update our internal state, but in a separate thread will set the system
65 * volume and later persist to the database. Similarly, setting the ringer mode
66 * will update the state and broadcast a change and in a separate thread later
67 * persist the ringer mode.
68 *
69 * @hide
70 */
71public class AudioService extends IAudioService.Stub {
72
73    private static final String TAG = "AudioService";
74
75    /** How long to delay before persisting a change in volume/ringer mode. */
76    private static final int PERSIST_DELAY = 3000;
77
78    private Context mContext;
79    private ContentResolver mContentResolver;
80
81
82    /** The UI */
83    private VolumePanel mVolumePanel;
84
85    // sendMsg() flags
86    /** Used when a message should be shared across all stream types. */
87    private static final int SHARED_MSG = -1;
88    /** If the msg is already queued, replace it with this one. */
89    private static final int SENDMSG_REPLACE = 0;
90    /** If the msg is already queued, ignore this one and leave the old. */
91    private static final int SENDMSG_NOOP = 1;
92    /** If the msg is already queued, queue this one and leave the old. */
93    private static final int SENDMSG_QUEUE = 2;
94
95    // AudioHandler message.whats
96    private static final int MSG_SET_SYSTEM_VOLUME = 0;
97    private static final int MSG_PERSIST_VOLUME = 1;
98    private static final int MSG_PERSIST_RINGER_MODE = 3;
99    private static final int MSG_PERSIST_VIBRATE_SETTING = 4;
100    private static final int MSG_MEDIA_SERVER_DIED = 5;
101    private static final int MSG_MEDIA_SERVER_STARTED = 6;
102    private static final int MSG_PLAY_SOUND_EFFECT = 7;
103
104    /** @see AudioSystemThread */
105    private AudioSystemThread mAudioSystemThread;
106    /** @see AudioHandler */
107    private AudioHandler mAudioHandler;
108    /** @see VolumeStreamState */
109    private VolumeStreamState[] mStreamStates;
110    private SettingsObserver mSettingsObserver;
111
112    private int mMode;
113    private Object mSettingsLock = new Object();
114    private boolean mMediaServerOk;
115
116    /** cached value of the BT dock address to recognize undocking events */
117    private static String sBtDockAddress = "";
118
119    private SoundPool mSoundPool;
120    private Object mSoundEffectsLock = new Object();
121    private static final int NUM_SOUNDPOOL_CHANNELS = 4;
122    private static final int SOUND_EFFECT_VOLUME = 1000;
123
124    /* Sound effect file names  */
125    private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/";
126    private static final String[] SOUND_EFFECT_FILES = new String[] {
127        "Effect_Tick.ogg",
128        "KeypressStandard.ogg",
129        "KeypressSpacebar.ogg",
130        "KeypressDelete.ogg",
131        "KeypressReturn.ogg"
132    };
133
134    /* Sound effect file name mapping sound effect id (AudioManager.FX_xxx) to
135     * file index in SOUND_EFFECT_FILES[] (first column) and indicating if effect
136     * uses soundpool (second column) */
137    private int[][] SOUND_EFFECT_FILES_MAP = new int[][] {
138        {0, -1},  // FX_KEY_CLICK
139        {0, -1},  // FX_FOCUS_NAVIGATION_UP
140        {0, -1},  // FX_FOCUS_NAVIGATION_DOWN
141        {0, -1},  // FX_FOCUS_NAVIGATION_LEFT
142        {0, -1},  // FX_FOCUS_NAVIGATION_RIGHT
143        {1, -1},  // FX_KEYPRESS_STANDARD
144        {2, -1},  // FX_KEYPRESS_SPACEBAR
145        {3, -1},  // FX_FOCUS_DELETE
146        {4, -1}   // FX_FOCUS_RETURN
147    };
148
149   /** @hide Maximum volume index values for audio streams */
150    private int[] MAX_STREAM_VOLUME = new int[] {
151        5,  // STREAM_VOICE_CALL
152        7,  // STREAM_SYSTEM
153        7,  // STREAM_RING
154        15, // STREAM_MUSIC
155        7,  // STREAM_ALARM
156        7,  // STREAM_NOTIFICATION
157        15, // STREAM_BLUETOOTH_SCO
158        7,  // STREAM_SYSTEM_ENFORCED
159        15, // STREAM_DTMF
160        15  // STREAM_TTS
161    };
162    /* STREAM_VOLUME_ALIAS[] indicates for each stream if it uses the volume settings
163     * of another stream: This avoids multiplying the volume settings for hidden
164     * stream types that follow other stream behavior for volume settings
165     * NOTE: do not create loops in aliases! */
166    private int[] STREAM_VOLUME_ALIAS = new int[] {
167        AudioSystem.STREAM_VOICE_CALL,  // STREAM_VOICE_CALL
168        AudioSystem.STREAM_SYSTEM,  // STREAM_SYSTEM
169        AudioSystem.STREAM_RING,  // STREAM_RING
170        AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
171        AudioSystem.STREAM_ALARM,  // STREAM_ALARM
172        AudioSystem.STREAM_NOTIFICATION,  // STREAM_NOTIFICATION
173        AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO
174        AudioSystem.STREAM_SYSTEM,  // STREAM_SYSTEM_ENFORCED
175        AudioSystem.STREAM_VOICE_CALL, // STREAM_DTMF
176        AudioSystem.STREAM_MUSIC  // STREAM_TTS
177    };
178
179    private AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() {
180        public void onError(int error) {
181            switch (error) {
182            case AudioSystem.AUDIO_STATUS_SERVER_DIED:
183                if (mMediaServerOk) {
184                    sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
185                            null, 1500);
186                    mMediaServerOk = false;
187                }
188                break;
189            case AudioSystem.AUDIO_STATUS_OK:
190                if (!mMediaServerOk) {
191                    sendMsg(mAudioHandler, MSG_MEDIA_SERVER_STARTED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
192                            null, 0);
193                    mMediaServerOk = true;
194                }
195                break;
196            default:
197                break;
198            }
199       }
200    };
201
202    /**
203     * Current ringer mode from one of {@link AudioManager#RINGER_MODE_NORMAL},
204     * {@link AudioManager#RINGER_MODE_SILENT}, or
205     * {@link AudioManager#RINGER_MODE_VIBRATE}.
206     */
207    private int mRingerMode;
208
209    /** @see System#MODE_RINGER_STREAMS_AFFECTED */
210    private int mRingerModeAffectedStreams;
211
212    /** @see System#MUTE_STREAMS_AFFECTED */
213    private int mMuteAffectedStreams;
214
215    /**
216     * Has multiple bits per vibrate type to indicate the type's vibrate
217     * setting. See {@link #setVibrateSetting(int, int)}.
218     * <p>
219     * NOTE: This is not the final decision of whether vibrate is on/off for the
220     * type since it depends on the ringer mode. See {@link #shouldVibrate(int)}.
221     */
222    private int mVibrateSetting;
223
224    /** @see System#NOTIFICATIONS_USE_RING_VOLUME */
225    private int mNotificationsUseRingVolume;
226
227    // Broadcast receiver for device connections intent broadcasts
228    private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
229
230    // Devices currently connected
231    private HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>();
232
233    // Forced device usage for communications
234    private int mForcedUseForComm;
235
236    // List of binder death handlers for setMode() client processes.
237    // The last process to have called setMode() is at the top of the list.
238    private ArrayList <SetModeDeathHandler> mSetModeDeathHandlers = new ArrayList <SetModeDeathHandler>();
239
240    ///////////////////////////////////////////////////////////////////////////
241    // Construction
242    ///////////////////////////////////////////////////////////////////////////
243
244    /** @hide */
245    public AudioService(Context context) {
246        mContext = context;
247        mContentResolver = context.getContentResolver();
248
249       // Intialized volume
250        MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = SystemProperties.getInt(
251            "ro.config.vc_call_vol_steps",
252           MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL]);
253
254        mVolumePanel = new VolumePanel(context, this);
255        mSettingsObserver = new SettingsObserver();
256        mForcedUseForComm = AudioSystem.FORCE_NONE;
257        createAudioSystemThread();
258        readPersistedSettings();
259        createStreamStates();
260        // Call setMode() to initialize mSetModeDeathHandlers
261        mMode = AudioSystem.MODE_INVALID;
262        setMode(AudioSystem.MODE_NORMAL, null);
263        mMediaServerOk = true;
264        AudioSystem.setErrorCallback(mAudioSystemCallback);
265        loadSoundEffects();
266
267        // Register for device connection intent broadcasts.
268        IntentFilter intentFilter =
269                new IntentFilter(Intent.ACTION_HEADSET_PLUG);
270        intentFilter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
271        intentFilter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
272        context.registerReceiver(mReceiver, intentFilter);
273
274    }
275
276    private void createAudioSystemThread() {
277        mAudioSystemThread = new AudioSystemThread();
278        mAudioSystemThread.start();
279        waitForAudioHandlerCreation();
280    }
281
282    /** Waits for the volume handler to be created by the other thread. */
283    private void waitForAudioHandlerCreation() {
284        synchronized(this) {
285            while (mAudioHandler == null) {
286                try {
287                    // Wait for mAudioHandler to be set by the other thread
288                    wait();
289                } catch (InterruptedException e) {
290                    Log.e(TAG, "Interrupted while waiting on volume handler.");
291                }
292            }
293        }
294    }
295
296    private void createStreamStates() {
297        int numStreamTypes = AudioSystem.getNumStreamTypes();
298        VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
299
300        for (int i = 0; i < numStreamTypes; i++) {
301            streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[i]], i);
302        }
303
304        // Correct stream index values for streams with aliases
305        for (int i = 0; i < numStreamTypes; i++) {
306            if (STREAM_VOLUME_ALIAS[i] != i) {
307                int index = rescaleIndex(streams[i].mIndex, STREAM_VOLUME_ALIAS[i], i);
308                streams[i].mIndex = streams[i].getValidIndex(index);
309                setStreamVolumeIndex(i, index);
310                index = rescaleIndex(streams[i].mLastAudibleIndex, STREAM_VOLUME_ALIAS[i], i);
311                streams[i].mLastAudibleIndex = streams[i].getValidIndex(index);
312            }
313        }
314    }
315
316    private void readPersistedSettings() {
317        final ContentResolver cr = mContentResolver;
318
319        mRingerMode = System.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);
320
321        mVibrateSetting = System.getInt(cr, System.VIBRATE_ON, 0);
322
323        mRingerModeAffectedStreams = Settings.System.getInt(cr,
324                Settings.System.MODE_RINGER_STREAMS_AFFECTED,
325                ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
326                 (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)));
327
328        mMuteAffectedStreams = System.getInt(cr,
329                System.MUTE_STREAMS_AFFECTED,
330                ((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM)));
331
332        mNotificationsUseRingVolume = System.getInt(cr,
333                Settings.System.NOTIFICATIONS_USE_RING_VOLUME, 1);
334
335        if (mNotificationsUseRingVolume == 1) {
336            STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING;
337        }
338        // Each stream will read its own persisted settings
339
340        // Broadcast the sticky intent
341        broadcastRingerMode();
342
343        // Broadcast vibrate settings
344        broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
345        broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION);
346    }
347
348    private void setStreamVolumeIndex(int stream, int index) {
349        AudioSystem.setStreamVolumeIndex(stream, (index + 5)/10);
350    }
351
352    private int rescaleIndex(int index, int srcStream, int dstStream) {
353        return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex();
354    }
355
356    ///////////////////////////////////////////////////////////////////////////
357    // IPC methods
358    ///////////////////////////////////////////////////////////////////////////
359
360    /** @see AudioManager#adjustVolume(int, int) */
361    public void adjustVolume(int direction, int flags) {
362        adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags);
363    }
364
365    /** @see AudioManager#adjustVolume(int, int, int) */
366    public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
367
368        int streamType = getActiveStreamType(suggestedStreamType);
369
370        // Don't play sound on other streams
371        if (streamType != AudioSystem.STREAM_RING && (flags & AudioManager.FLAG_PLAY_SOUND) != 0) {
372            flags &= ~AudioManager.FLAG_PLAY_SOUND;
373        }
374
375        adjustStreamVolume(streamType, direction, flags);
376    }
377
378    /** @see AudioManager#adjustStreamVolume(int, int, int) */
379    public void adjustStreamVolume(int streamType, int direction, int flags) {
380        ensureValidDirection(direction);
381        ensureValidStreamType(streamType);
382
383
384        VolumeStreamState streamState = mStreamStates[STREAM_VOLUME_ALIAS[streamType]];
385        final int oldIndex = streamState.mIndex;
386        boolean adjustVolume = true;
387
388        // If either the client forces allowing ringer modes for this adjustment,
389        // or the stream type is one that is affected by ringer modes
390        if ((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0
391                || streamType == AudioSystem.STREAM_RING) {
392            // Check if the ringer mode changes with this volume adjustment. If
393            // it does, it will handle adjusting the volume, so we won't below
394            adjustVolume = checkForRingerModeChange(oldIndex, direction);
395        }
396
397        if (adjustVolume && streamState.adjustIndex(direction)) {
398            // Post message to set system volume (it in turn will post a message
399            // to persist). Do not change volume if stream is muted.
400            if (streamState.muteCount() == 0) {
401                sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, STREAM_VOLUME_ALIAS[streamType], SENDMSG_NOOP, 0, 0,
402                        streamState, 0);
403            }
404        }
405
406        // UI
407        mVolumePanel.postVolumeChanged(streamType, flags);
408        // Broadcast Intent
409        sendVolumeUpdate(streamType, oldIndex, streamState.mIndex);
410    }
411
412    /** @see AudioManager#setStreamVolume(int, int, int) */
413    public void setStreamVolume(int streamType, int index, int flags) {
414        ensureValidStreamType(streamType);
415
416        final int oldIndex = mStreamStates[STREAM_VOLUME_ALIAS[streamType]].mIndex;
417
418        index = rescaleIndex(index * 10, streamType, STREAM_VOLUME_ALIAS[streamType]);
419        setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, false, true);
420
421        // UI, etc.
422        mVolumePanel.postVolumeChanged(streamType, flags);
423        // Broadcast Intent
424        sendVolumeUpdate(streamType, oldIndex, index);
425    }
426
427    private void sendVolumeUpdate(int streamType, int oldIndex, int index) {
428        oldIndex = (oldIndex + 5) / 10;
429        index = (index + 5) / 10;
430
431        Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
432        intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
433        intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index);
434        intent.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex);
435
436        mContext.sendBroadcast(intent);
437    }
438
439    /**
440     * Sets the stream state's index, and posts a message to set system volume.
441     * This will not call out to the UI. Assumes a valid stream type.
442     *
443     * @param streamType Type of the stream
444     * @param index Desired volume index of the stream
445     * @param force If true, set the volume even if the desired volume is same
446     * as the current volume.
447     * @param lastAudible If true, stores new index as last audible one
448     */
449    private void setStreamVolumeInt(int streamType, int index, boolean force, boolean lastAudible) {
450        VolumeStreamState streamState = mStreamStates[streamType];
451        if (streamState.setIndex(index, lastAudible) || force) {
452            // Post message to set system volume (it in turn will post a message
453            // to persist). Do not change volume if stream is muted.
454            if (streamState.muteCount() == 0) {
455                sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamType, SENDMSG_NOOP, 0, 0,
456                        streamState, 0);
457            }
458        }
459    }
460
461    /** @see AudioManager#setStreamSolo(int, boolean) */
462    public void setStreamSolo(int streamType, boolean state, IBinder cb) {
463        for (int stream = 0; stream < mStreamStates.length; stream++) {
464            if (!isStreamAffectedByMute(stream) || stream == streamType) continue;
465            // Bring back last audible volume
466            mStreamStates[stream].mute(cb, state);
467         }
468    }
469
470    /** @see AudioManager#setStreamMute(int, boolean) */
471    public void setStreamMute(int streamType, boolean state, IBinder cb) {
472        if (isStreamAffectedByMute(streamType)) {
473            mStreamStates[streamType].mute(cb, state);
474        }
475    }
476
477    /** @see AudioManager#getStreamVolume(int) */
478    public int getStreamVolume(int streamType) {
479        ensureValidStreamType(streamType);
480        return (mStreamStates[streamType].mIndex + 5) / 10;
481    }
482
483    /** @see AudioManager#getStreamMaxVolume(int) */
484    public int getStreamMaxVolume(int streamType) {
485        ensureValidStreamType(streamType);
486        return (mStreamStates[streamType].getMaxIndex() + 5) / 10;
487    }
488
489    /** @see AudioManager#getRingerMode() */
490    public int getRingerMode() {
491        return mRingerMode;
492    }
493
494    /** @see AudioManager#setRingerMode(int) */
495    public void setRingerMode(int ringerMode) {
496        synchronized (mSettingsLock) {
497            if (ringerMode != mRingerMode) {
498                setRingerModeInt(ringerMode, true);
499                // Send sticky broadcast
500                broadcastRingerMode();
501            }
502        }
503    }
504
505    private void setRingerModeInt(int ringerMode, boolean persist) {
506        mRingerMode = ringerMode;
507
508        // Adjust volumes via posting message
509        int numStreamTypes = AudioSystem.getNumStreamTypes();
510        if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
511            for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
512                if (!isStreamAffectedByRingerMode(streamType)) continue;
513                // Bring back last audible volume
514                setStreamVolumeInt(streamType, mStreamStates[streamType].mLastAudibleIndex,
515                                   false, false);
516            }
517        } else {
518            for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
519                if (isStreamAffectedByRingerMode(streamType)) {
520                    // Either silent or vibrate, either way volume is 0
521                    setStreamVolumeInt(streamType, 0, false, false);
522                } else {
523                    // restore stream volume in the case the stream changed from affected
524                    // to non affected by ringer mode. Does not arm to do it for streams that
525                    // are not affected as well.
526                    setStreamVolumeInt(streamType, mStreamStates[streamType].mLastAudibleIndex,
527                            false, false);
528                }
529            }
530        }
531
532        // Post a persist ringer mode msg
533        if (persist) {
534            sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE, SHARED_MSG,
535                    SENDMSG_REPLACE, 0, 0, null, PERSIST_DELAY);
536        }
537    }
538
539    /** @see AudioManager#shouldVibrate(int) */
540    public boolean shouldVibrate(int vibrateType) {
541
542        switch (getVibrateSetting(vibrateType)) {
543
544            case AudioManager.VIBRATE_SETTING_ON:
545                return mRingerMode != AudioManager.RINGER_MODE_SILENT;
546
547            case AudioManager.VIBRATE_SETTING_ONLY_SILENT:
548                return mRingerMode == AudioManager.RINGER_MODE_VIBRATE;
549
550            case AudioManager.VIBRATE_SETTING_OFF:
551                // Phone ringer should always vibrate in vibrate mode
552                if (vibrateType == AudioManager.VIBRATE_TYPE_RINGER) {
553                    return mRingerMode == AudioManager.RINGER_MODE_VIBRATE;
554                }
555
556            default:
557                return false;
558        }
559    }
560
561    /** @see AudioManager#getVibrateSetting(int) */
562    public int getVibrateSetting(int vibrateType) {
563        return (mVibrateSetting >> (vibrateType * 2)) & 3;
564    }
565
566    /** @see AudioManager#setVibrateSetting(int, int) */
567    public void setVibrateSetting(int vibrateType, int vibrateSetting) {
568
569        mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, vibrateType, vibrateSetting);
570
571        // Broadcast change
572        broadcastVibrateSetting(vibrateType);
573
574        // Post message to set ringer mode (it in turn will post a message
575        // to persist)
576        sendMsg(mAudioHandler, MSG_PERSIST_VIBRATE_SETTING, SHARED_MSG, SENDMSG_NOOP, 0, 0,
577                null, 0);
578    }
579
580    /**
581     * @see #setVibrateSetting(int, int)
582     */
583    public static int getValueForVibrateSetting(int existingValue, int vibrateType,
584            int vibrateSetting) {
585
586        // First clear the existing setting. Each vibrate type has two bits in
587        // the value. Note '3' is '11' in binary.
588        existingValue &= ~(3 << (vibrateType * 2));
589
590        // Set into the old value
591        existingValue |= (vibrateSetting & 3) << (vibrateType * 2);
592
593        return existingValue;
594    }
595
596    private class SetModeDeathHandler implements IBinder.DeathRecipient {
597        private IBinder mCb; // To be notified of client's death
598        private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client
599
600        SetModeDeathHandler(IBinder cb) {
601            mCb = cb;
602        }
603
604        public void binderDied() {
605            synchronized(mSetModeDeathHandlers) {
606                Log.w(TAG, "setMode() client died");
607                int index = mSetModeDeathHandlers.indexOf(this);
608                if (index < 0) {
609                    Log.w(TAG, "unregistered setMode() client died");
610                } else {
611                    mSetModeDeathHandlers.remove(this);
612                    // If dead client was a the top of client list,
613                    // apply next mode in the stack
614                    if (index == 0) {
615                        // mSetModeDeathHandlers is never empty as the initial entry
616                        // created when AudioService starts is never removed
617                        SetModeDeathHandler hdlr = mSetModeDeathHandlers.get(0);
618                        int mode = hdlr.getMode();
619                        if (AudioService.this.mMode != mode) {
620                            if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) {
621                                AudioService.this.mMode = mode;
622                            }
623                        }
624                    }
625                }
626            }
627        }
628
629        public void setMode(int mode) {
630            mMode = mode;
631        }
632
633        public int getMode() {
634            return mMode;
635        }
636
637        public IBinder getBinder() {
638            return mCb;
639        }
640    }
641
642    /** @see AudioManager#setMode(int) */
643    public void setMode(int mode, IBinder cb) {
644        if (!checkAudioSettingsPermission("setMode()")) {
645            return;
646        }
647
648        if (mode < AudioSystem.MODE_CURRENT || mode > AudioSystem.MODE_IN_CALL) {
649            return;
650        }
651
652        synchronized (mSettingsLock) {
653            if (mode == AudioSystem.MODE_CURRENT) {
654                mode = mMode;
655            }
656            if (mode != mMode) {
657                if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) {
658                    mMode = mode;
659
660                    synchronized(mSetModeDeathHandlers) {
661                        SetModeDeathHandler hdlr = null;
662                        Iterator iter = mSetModeDeathHandlers.iterator();
663                        while (iter.hasNext()) {
664                            SetModeDeathHandler h = (SetModeDeathHandler)iter.next();
665                            if (h.getBinder() == cb) {
666                                hdlr = h;
667                                // Remove from client list so that it is re-inserted at top of list
668                                iter.remove();
669                                break;
670                            }
671                        }
672                        if (hdlr == null) {
673                            hdlr = new SetModeDeathHandler(cb);
674                            // cb is null when setMode() is called by AudioService constructor
675                            if (cb != null) {
676                                // Register for client death notification
677                                try {
678                                    cb.linkToDeath(hdlr, 0);
679                                } catch (RemoteException e) {
680                                    // Client has died!
681                                    Log.w(TAG, "setMode() could not link to "+cb+" binder death");
682                                }
683                            }
684                        }
685                        // Last client to call setMode() is always at top of client list
686                        // as required by SetModeDeathHandler.binderDied()
687                        mSetModeDeathHandlers.add(0, hdlr);
688                        hdlr.setMode(mode);
689                    }
690                }
691            }
692            int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
693            int index = mStreamStates[STREAM_VOLUME_ALIAS[streamType]].mIndex;
694            setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, true, true);
695        }
696    }
697
698    /** @see AudioManager#getMode() */
699    public int getMode() {
700        return mMode;
701    }
702
703    /** @see AudioManager#playSoundEffect(int) */
704    public void playSoundEffect(int effectType) {
705        sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
706                effectType, -1, null, 0);
707    }
708
709    /** @see AudioManager#playSoundEffect(int, float) */
710    public void playSoundEffectVolume(int effectType, float volume) {
711        loadSoundEffects();
712        sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
713                effectType, (int) (volume * 1000), null, 0);
714    }
715
716    /**
717     * Loads samples into the soundpool.
718     * This method must be called at when sound effects are enabled
719     */
720    public boolean loadSoundEffects() {
721        synchronized (mSoundEffectsLock) {
722            if (mSoundPool != null) {
723                return true;
724            }
725            mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
726            if (mSoundPool == null) {
727                return false;
728            }
729            /*
730             * poolId table: The value -1 in this table indicates that corresponding
731             * file (same index in SOUND_EFFECT_FILES[] has not been loaded.
732             * Once loaded, the value in poolId is the sample ID and the same
733             * sample can be reused for another effect using the same file.
734             */
735            int[] poolId = new int[SOUND_EFFECT_FILES.length];
736            for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
737                poolId[fileIdx] = -1;
738            }
739            /*
740             * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded.
741             * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0:
742             * this indicates we have a valid sample loaded for this effect.
743             */
744            for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
745                // Do not load sample if this effect uses the MediaPlayer
746                if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) {
747                    continue;
748                }
749                if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
750                    String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effect][0]];
751                    int sampleId = mSoundPool.load(filePath, 0);
752                    SOUND_EFFECT_FILES_MAP[effect][1] = sampleId;
753                    poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId;
754                    if (sampleId <= 0) {
755                        Log.w(TAG, "Soundpool could not load file: "+filePath);
756                    }
757                } else {
758                    SOUND_EFFECT_FILES_MAP[effect][1] = poolId[SOUND_EFFECT_FILES_MAP[effect][0]];
759                }
760            }
761        }
762
763        return true;
764    }
765
766    /**
767     *  Unloads samples from the sound pool.
768     *  This method can be called to free some memory when
769     *  sound effects are disabled.
770     */
771    public void unloadSoundEffects() {
772        synchronized (mSoundEffectsLock) {
773            if (mSoundPool == null) {
774                return;
775            }
776            int[] poolId = new int[SOUND_EFFECT_FILES.length];
777            for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
778                poolId[fileIdx] = 0;
779            }
780
781            for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
782                if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) {
783                    continue;
784                }
785                if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) {
786                    mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]);
787                    SOUND_EFFECT_FILES_MAP[effect][1] = -1;
788                    poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1;
789                }
790            }
791            mSoundPool = null;
792        }
793    }
794
795    /** @see AudioManager#reloadAudioSettings() */
796    public void reloadAudioSettings() {
797        // restore ringer mode, ringer mode affected streams, mute affected streams and vibrate settings
798        readPersistedSettings();
799
800        // restore volume settings
801        int numStreamTypes = AudioSystem.getNumStreamTypes();
802        for (int streamType = 0; streamType < numStreamTypes; streamType++) {
803            VolumeStreamState streamState = mStreamStates[streamType];
804
805            String settingName = System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[streamType]];
806            String lastAudibleSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
807            int index = Settings.System.getInt(mContentResolver,
808                                           settingName,
809                                           AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
810            if (STREAM_VOLUME_ALIAS[streamType] != streamType) {
811                index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType);
812            } else {
813                index *= 10;
814            }
815            streamState.mIndex = streamState.getValidIndex(index);
816
817            index = (index + 5) / 10;
818            index = Settings.System.getInt(mContentResolver,
819                                            lastAudibleSettingName,
820                                            (index > 0) ? index : AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
821            if (STREAM_VOLUME_ALIAS[streamType] != streamType) {
822                index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType);
823            } else {
824                index *= 10;
825            }
826            streamState.mLastAudibleIndex = streamState.getValidIndex(index);
827
828            // unmute stream that whas muted but is not affect by mute anymore
829            if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType)) {
830                int size = streamState.mDeathHandlers.size();
831                for (int i = 0; i < size; i++) {
832                    streamState.mDeathHandlers.get(i).mMuteCount = 1;
833                    streamState.mDeathHandlers.get(i).mute(false);
834                }
835            }
836            // apply stream volume
837            if (streamState.muteCount() == 0) {
838                setStreamVolumeIndex(streamType, streamState.mIndex);
839            }
840        }
841
842        // apply new ringer mode
843        setRingerModeInt(getRingerMode(), false);
844    }
845
846    /** @see AudioManager#setSpeakerphoneOn() */
847    public void setSpeakerphoneOn(boolean on){
848        if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
849            return;
850        }
851        if (on) {
852            AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_SPEAKER);
853            mForcedUseForComm = AudioSystem.FORCE_SPEAKER;
854        } else {
855            AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_NONE);
856            mForcedUseForComm = AudioSystem.FORCE_NONE;
857        }
858    }
859
860    /** @see AudioManager#isSpeakerphoneOn() */
861    public boolean isSpeakerphoneOn() {
862        if (mForcedUseForComm == AudioSystem.FORCE_SPEAKER) {
863            return true;
864        } else {
865            return false;
866        }
867    }
868
869    /** @see AudioManager#setBluetoothScoOn() */
870    public void setBluetoothScoOn(boolean on){
871        if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
872            return;
873        }
874        if (on) {
875            AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_BT_SCO);
876            AudioSystem.setForceUse(AudioSystem.FOR_RECORD, AudioSystem.FORCE_BT_SCO);
877            mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
878        } else {
879            AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_NONE);
880            AudioSystem.setForceUse(AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE);
881            mForcedUseForComm = AudioSystem.FORCE_NONE;
882        }
883    }
884
885    /** @see AudioManager#isBluetoothScoOn() */
886    public boolean isBluetoothScoOn() {
887        if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
888            return true;
889        } else {
890            return false;
891        }
892    }
893
894
895    ///////////////////////////////////////////////////////////////////////////
896    // Internal methods
897    ///////////////////////////////////////////////////////////////////////////
898
899    /**
900     * Checks if the adjustment should change ringer mode instead of just
901     * adjusting volume. If so, this will set the proper ringer mode and volume
902     * indices on the stream states.
903     */
904    private boolean checkForRingerModeChange(int oldIndex, int direction) {
905        boolean adjustVolumeIndex = true;
906        int newRingerMode = mRingerMode;
907
908        if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
909            // audible mode, at the bottom of the scale
910            if (direction == AudioManager.ADJUST_LOWER
911                    && (oldIndex + 5) / 10 == 1) {
912                // "silent mode", but which one?
913                newRingerMode = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1
914                    ? AudioManager.RINGER_MODE_VIBRATE
915                    : AudioManager.RINGER_MODE_SILENT;
916            }
917        } else {
918            if (direction == AudioManager.ADJUST_RAISE) {
919                // exiting silent mode
920                newRingerMode = AudioManager.RINGER_MODE_NORMAL;
921            }
922        }
923
924        if (newRingerMode != mRingerMode) {
925            setRingerMode(newRingerMode);
926
927            /*
928             * If we are changing ringer modes, do not increment/decrement the
929             * volume index. Instead, the handler for the message above will
930             * take care of changing the index.
931             */
932            adjustVolumeIndex = false;
933        }
934
935        return adjustVolumeIndex;
936    }
937
938    public boolean isStreamAffectedByRingerMode(int streamType) {
939        return (mRingerModeAffectedStreams & (1 << streamType)) != 0;
940    }
941
942    public boolean isStreamAffectedByMute(int streamType) {
943        return (mMuteAffectedStreams & (1 << streamType)) != 0;
944    }
945
946    private void ensureValidDirection(int direction) {
947        if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) {
948            throw new IllegalArgumentException("Bad direction " + direction);
949        }
950    }
951
952    private void ensureValidStreamType(int streamType) {
953        if (streamType < 0 || streamType >= mStreamStates.length) {
954            throw new IllegalArgumentException("Bad stream type " + streamType);
955        }
956    }
957
958    private int getActiveStreamType(int suggestedStreamType) {
959        boolean isOffhook = false;
960        try {
961            ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
962            if (phone != null) isOffhook = phone.isOffhook();
963        } catch (RemoteException e) {
964            Log.w(TAG, "Couldn't connect to phone service", e);
965        }
966
967        if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION) == AudioSystem.FORCE_BT_SCO) {
968            // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
969            return AudioSystem.STREAM_BLUETOOTH_SCO;
970        } else if (isOffhook || AudioSystem.isStreamActive(AudioSystem.STREAM_VOICE_CALL)) {
971            // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
972            return AudioSystem.STREAM_VOICE_CALL;
973        } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC)) {
974            // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC...");
975            return AudioSystem.STREAM_MUSIC;
976        } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
977            // Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING...");
978            return AudioSystem.STREAM_RING;
979        } else {
980            // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType);
981            return suggestedStreamType;
982        }
983    }
984
985    private void broadcastRingerMode() {
986        // Send sticky broadcast
987        Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION);
988        broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, mRingerMode);
989        broadcast.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
990                | Intent.FLAG_RECEIVER_REPLACE_PENDING);
991        long origCallerIdentityToken = Binder.clearCallingIdentity();
992        mContext.sendStickyBroadcast(broadcast);
993        Binder.restoreCallingIdentity(origCallerIdentityToken);
994    }
995
996    private void broadcastVibrateSetting(int vibrateType) {
997        // Send broadcast
998        if (ActivityManagerNative.isSystemReady()) {
999            Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
1000            broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType);
1001            broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType));
1002            mContext.sendBroadcast(broadcast);
1003        }
1004    }
1005
1006    // Message helper methods
1007    private static int getMsg(int baseMsg, int streamType) {
1008        return (baseMsg & 0xffff) | streamType << 16;
1009    }
1010
1011    private static int getMsgBase(int msg) {
1012        return msg & 0xffff;
1013    }
1014
1015    private static void sendMsg(Handler handler, int baseMsg, int streamType,
1016            int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {
1017        int msg = (streamType == SHARED_MSG) ? baseMsg : getMsg(baseMsg, streamType);
1018
1019        if (existingMsgPolicy == SENDMSG_REPLACE) {
1020            handler.removeMessages(msg);
1021        } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
1022            return;
1023        }
1024
1025        handler
1026                .sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
1027    }
1028
1029    boolean checkAudioSettingsPermission(String method) {
1030        if (mContext.checkCallingOrSelfPermission("android.permission.MODIFY_AUDIO_SETTINGS")
1031                == PackageManager.PERMISSION_GRANTED) {
1032            return true;
1033        }
1034        String msg = "Audio Settings Permission Denial: " + method + " from pid="
1035                + Binder.getCallingPid()
1036                + ", uid=" + Binder.getCallingUid();
1037        Log.w(TAG, msg);
1038        return false;
1039    }
1040
1041
1042    ///////////////////////////////////////////////////////////////////////////
1043    // Inner classes
1044    ///////////////////////////////////////////////////////////////////////////
1045
1046    public class VolumeStreamState {
1047        private final int mStreamType;
1048
1049        private String mVolumeIndexSettingName;
1050        private String mLastAudibleVolumeIndexSettingName;
1051        private int mIndexMax;
1052        private int mIndex;
1053        private int mLastAudibleIndex;
1054        private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo requests client death
1055
1056        private VolumeStreamState(String settingName, int streamType) {
1057
1058            setVolumeIndexSettingName(settingName);
1059
1060            mStreamType = streamType;
1061
1062            final ContentResolver cr = mContentResolver;
1063            mIndexMax = MAX_STREAM_VOLUME[streamType];
1064            mIndex = Settings.System.getInt(cr,
1065                                            mVolumeIndexSettingName,
1066                                            AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
1067            mLastAudibleIndex = Settings.System.getInt(cr,
1068                                                       mLastAudibleVolumeIndexSettingName,
1069                                                       (mIndex > 0) ? mIndex : AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
1070            AudioSystem.initStreamVolume(streamType, 0, mIndexMax);
1071            mIndexMax *= 10;
1072            mIndex = getValidIndex(10 * mIndex);
1073            mLastAudibleIndex = getValidIndex(10 * mLastAudibleIndex);
1074            setStreamVolumeIndex(streamType, mIndex);
1075            mDeathHandlers = new ArrayList<VolumeDeathHandler>();
1076        }
1077
1078        public void setVolumeIndexSettingName(String settingName) {
1079            mVolumeIndexSettingName = settingName;
1080            mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
1081        }
1082
1083        public boolean adjustIndex(int deltaIndex) {
1084            return setIndex(mIndex + deltaIndex * 10, true);
1085        }
1086
1087        public boolean setIndex(int index, boolean lastAudible) {
1088            int oldIndex = mIndex;
1089            mIndex = getValidIndex(index);
1090
1091            if (oldIndex != mIndex) {
1092                if (lastAudible) {
1093                    mLastAudibleIndex = mIndex;
1094                }
1095                // Apply change to all streams using this one as alias
1096                int numStreamTypes = AudioSystem.getNumStreamTypes();
1097                for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
1098                    if (streamType != mStreamType && STREAM_VOLUME_ALIAS[streamType] == mStreamType) {
1099                        mStreamStates[streamType].setIndex(rescaleIndex(mIndex, mStreamType, streamType), lastAudible);
1100                    }
1101                }
1102                return true;
1103            } else {
1104                return false;
1105            }
1106        }
1107
1108        public int getMaxIndex() {
1109            return mIndexMax;
1110        }
1111
1112        public void mute(IBinder cb, boolean state) {
1113            VolumeDeathHandler handler = getDeathHandler(cb, state);
1114            if (handler == null) {
1115                Log.e(TAG, "Could not get client death handler for stream: "+mStreamType);
1116                return;
1117            }
1118            handler.mute(state);
1119        }
1120
1121        private int getValidIndex(int index) {
1122            if (index < 0) {
1123                return 0;
1124            } else if (index > mIndexMax) {
1125                return mIndexMax;
1126            }
1127
1128            return index;
1129        }
1130
1131        private class VolumeDeathHandler implements IBinder.DeathRecipient {
1132            private IBinder mICallback; // To be notified of client's death
1133            private int mMuteCount; // Number of active mutes for this client
1134
1135            VolumeDeathHandler(IBinder cb) {
1136                mICallback = cb;
1137            }
1138
1139            public void mute(boolean state) {
1140                synchronized(mDeathHandlers) {
1141                    if (state) {
1142                        if (mMuteCount == 0) {
1143                            // Register for client death notification
1144                            try {
1145                                mICallback.linkToDeath(this, 0);
1146                                mDeathHandlers.add(this);
1147                                // If the stream is not yet muted by any client, set lvel to 0
1148                                if (muteCount() == 0) {
1149                                    setIndex(0, false);
1150                                    sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0,
1151                                            VolumeStreamState.this, 0);
1152                                }
1153                            } catch (RemoteException e) {
1154                                // Client has died!
1155                                binderDied();
1156                                mDeathHandlers.notify();
1157                                return;
1158                            }
1159                        } else {
1160                            Log.w(TAG, "stream: "+mStreamType+" was already muted by this client");
1161                        }
1162                        mMuteCount++;
1163                    } else {
1164                        if (mMuteCount == 0) {
1165                            Log.e(TAG, "unexpected unmute for stream: "+mStreamType);
1166                        } else {
1167                            mMuteCount--;
1168                            if (mMuteCount == 0) {
1169                                // Unregistr from client death notification
1170                                mDeathHandlers.remove(this);
1171                                mICallback.unlinkToDeath(this, 0);
1172                                if (muteCount() == 0) {
1173                                    // If the stream is not mut any more, restore it's volume if
1174                                    // ringer mode allows it
1175                                    if (!isStreamAffectedByRingerMode(mStreamType) || mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
1176                                        setIndex(mLastAudibleIndex, false);
1177                                        sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0,
1178                                                VolumeStreamState.this, 0);
1179                                    }
1180                                }
1181                            }
1182                        }
1183                    }
1184                    mDeathHandlers.notify();
1185                }
1186            }
1187
1188            public void binderDied() {
1189                Log.w(TAG, "Volume service client died for stream: "+mStreamType);
1190                if (mMuteCount != 0) {
1191                    // Reset all active mute requests from this client.
1192                    mMuteCount = 1;
1193                    mute(false);
1194                }
1195            }
1196        }
1197
1198        private int muteCount() {
1199            int count = 0;
1200            int size = mDeathHandlers.size();
1201            for (int i = 0; i < size; i++) {
1202                count += mDeathHandlers.get(i).mMuteCount;
1203            }
1204            return count;
1205        }
1206
1207        private VolumeDeathHandler getDeathHandler(IBinder cb, boolean state) {
1208            synchronized(mDeathHandlers) {
1209                VolumeDeathHandler handler;
1210                int size = mDeathHandlers.size();
1211                for (int i = 0; i < size; i++) {
1212                    handler = mDeathHandlers.get(i);
1213                    if (cb.equals(handler.mICallback)) {
1214                        return handler;
1215                    }
1216                }
1217                // If this is the first mute request for this client, create a new
1218                // client death handler. Otherwise, it is an out of sequence unmute request.
1219                if (state) {
1220                    handler = new VolumeDeathHandler(cb);
1221                } else {
1222                    Log.w(TAG, "stream was not muted by this client");
1223                    handler = null;
1224                }
1225                return handler;
1226            }
1227        }
1228    }
1229
1230    /** Thread that handles native AudioSystem control. */
1231    private class AudioSystemThread extends Thread {
1232        AudioSystemThread() {
1233            super("AudioService");
1234        }
1235
1236        @Override
1237        public void run() {
1238            // Set this thread up so the handler will work on it
1239            Looper.prepare();
1240
1241            synchronized(AudioService.this) {
1242                mAudioHandler = new AudioHandler();
1243
1244                // Notify that the handler has been created
1245                AudioService.this.notify();
1246            }
1247
1248            // Listen for volume change requests that are set by VolumePanel
1249            Looper.loop();
1250        }
1251    }
1252
1253    /** Handles internal volume messages in separate volume thread. */
1254    private class AudioHandler extends Handler {
1255
1256        private void setSystemVolume(VolumeStreamState streamState) {
1257
1258            // Adjust volume
1259            setStreamVolumeIndex(streamState.mStreamType, streamState.mIndex);
1260
1261            // Apply change to all streams using this one as alias
1262            int numStreamTypes = AudioSystem.getNumStreamTypes();
1263            for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
1264                if (streamType != streamState.mStreamType &&
1265                    STREAM_VOLUME_ALIAS[streamType] == streamState.mStreamType) {
1266                    setStreamVolumeIndex(streamType, mStreamStates[streamType].mIndex);
1267                }
1268            }
1269
1270            // Post a persist volume msg
1271            sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamState.mStreamType,
1272                    SENDMSG_REPLACE, 0, 0, streamState, PERSIST_DELAY);
1273        }
1274
1275        private void persistVolume(VolumeStreamState streamState) {
1276            System.putInt(mContentResolver, streamState.mVolumeIndexSettingName,
1277                    (streamState.mIndex + 5)/ 10);
1278            System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName,
1279                    (streamState.mLastAudibleIndex + 5) / 10);
1280        }
1281
1282        private void persistRingerMode() {
1283            System.putInt(mContentResolver, System.MODE_RINGER, mRingerMode);
1284        }
1285
1286        private void persistVibrateSetting() {
1287            System.putInt(mContentResolver, System.VIBRATE_ON, mVibrateSetting);
1288        }
1289
1290        private void playSoundEffect(int effectType, int volume) {
1291            synchronized (mSoundEffectsLock) {
1292                if (mSoundPool == null) {
1293                    return;
1294                }
1295                float volFloat;
1296                // use STREAM_MUSIC volume attenuated by 3 dB if volume is not specified by caller
1297                if (volume < 0) {
1298                    // Same linear to log conversion as in native AudioSystem::linearToLog() (AudioSystem.cpp)
1299                    float dBPerStep = (float)((0.5 * 100) / MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC]);
1300                    int musicVolIndex = (mStreamStates[AudioSystem.STREAM_MUSIC].mIndex + 5) / 10;
1301                    float musicVoldB = dBPerStep * (musicVolIndex - MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC]);
1302                    volFloat = (float)Math.pow(10, (musicVoldB - 3)/20);
1303                } else {
1304                    volFloat = (float) volume / 1000.0f;
1305                }
1306
1307                if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) {
1308                    mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f);
1309                } else {
1310                    MediaPlayer mediaPlayer = new MediaPlayer();
1311                    if (mediaPlayer != null) {
1312                        try {
1313                            String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]];
1314                            mediaPlayer.setDataSource(filePath);
1315                            mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM);
1316                            mediaPlayer.prepare();
1317                            mediaPlayer.setVolume(volFloat, volFloat);
1318                            mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
1319                                public void onCompletion(MediaPlayer mp) {
1320                                    cleanupPlayer(mp);
1321                                }
1322                            });
1323                            mediaPlayer.setOnErrorListener(new OnErrorListener() {
1324                                public boolean onError(MediaPlayer mp, int what, int extra) {
1325                                    cleanupPlayer(mp);
1326                                    return true;
1327                                }
1328                            });
1329                            mediaPlayer.start();
1330                        } catch (IOException ex) {
1331                            Log.w(TAG, "MediaPlayer IOException: "+ex);
1332                        } catch (IllegalArgumentException ex) {
1333                            Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex);
1334                        } catch (IllegalStateException ex) {
1335                            Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
1336                        }
1337                    }
1338                }
1339            }
1340        }
1341
1342        private void cleanupPlayer(MediaPlayer mp) {
1343            if (mp != null) {
1344                try {
1345                    mp.stop();
1346                    mp.release();
1347                } catch (IllegalStateException ex) {
1348                    Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
1349                }
1350            }
1351        }
1352
1353        @Override
1354        public void handleMessage(Message msg) {
1355            int baseMsgWhat = getMsgBase(msg.what);
1356
1357            switch (baseMsgWhat) {
1358
1359                case MSG_SET_SYSTEM_VOLUME:
1360                    setSystemVolume((VolumeStreamState) msg.obj);
1361                    break;
1362
1363                case MSG_PERSIST_VOLUME:
1364                    persistVolume((VolumeStreamState) msg.obj);
1365                    break;
1366
1367                case MSG_PERSIST_RINGER_MODE:
1368                    persistRingerMode();
1369                    break;
1370
1371                case MSG_PERSIST_VIBRATE_SETTING:
1372                    persistVibrateSetting();
1373                    break;
1374
1375                case MSG_MEDIA_SERVER_DIED:
1376                    // Force creation of new IAudioflinger interface
1377                    if (!mMediaServerOk) {
1378                        Log.e(TAG, "Media server died.");
1379                        AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC);
1380                        sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
1381                                null, 500);
1382                    }
1383                    break;
1384
1385                case MSG_MEDIA_SERVER_STARTED:
1386                    Log.e(TAG, "Media server started.");
1387                    // Restore device connection states
1388                    Set set = mConnectedDevices.entrySet();
1389                    Iterator i = set.iterator();
1390                    while(i.hasNext()){
1391                        Map.Entry device = (Map.Entry)i.next();
1392                        AudioSystem.setDeviceConnectionState(((Integer)device.getKey()).intValue(),
1393                                                             AudioSystem.DEVICE_STATE_AVAILABLE,
1394                                                             (String)device.getValue());
1395                    }
1396
1397                    // Restore call state
1398                    AudioSystem.setPhoneState(mMode);
1399
1400                    // Restore forced usage for communcations and record
1401                    AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm);
1402                    AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm);
1403
1404                    // Restore stream volumes
1405                    int numStreamTypes = AudioSystem.getNumStreamTypes();
1406                    for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
1407                        int index;
1408                        VolumeStreamState streamState = mStreamStates[streamType];
1409                        AudioSystem.initStreamVolume(streamType, 0, (streamState.mIndexMax + 5) / 10);
1410                        if (streamState.muteCount() == 0) {
1411                            index = streamState.mIndex;
1412                        } else {
1413                            index = 0;
1414                        }
1415                        setStreamVolumeIndex(streamType, index);
1416                    }
1417
1418                    // Restore ringer mode
1419                    setRingerModeInt(getRingerMode(), false);
1420                    break;
1421
1422                case MSG_PLAY_SOUND_EFFECT:
1423                    playSoundEffect(msg.arg1, msg.arg2);
1424                    break;
1425            }
1426        }
1427    }
1428
1429    private class SettingsObserver extends ContentObserver {
1430
1431        SettingsObserver() {
1432            super(new Handler());
1433            mContentResolver.registerContentObserver(Settings.System.getUriFor(
1434                Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this);
1435            mContentResolver.registerContentObserver(Settings.System.getUriFor(
1436                    Settings.System.NOTIFICATIONS_USE_RING_VOLUME), false, this);
1437        }
1438
1439        @Override
1440        public void onChange(boolean selfChange) {
1441            super.onChange(selfChange);
1442            synchronized (mSettingsLock) {
1443                int ringerModeAffectedStreams = Settings.System.getInt(mContentResolver,
1444                        Settings.System.MODE_RINGER_STREAMS_AFFECTED,
1445                        0);
1446                if (ringerModeAffectedStreams != mRingerModeAffectedStreams) {
1447                    /*
1448                     * Ensure all stream types that should be affected by ringer mode
1449                     * are in the proper state.
1450                     */
1451                    mRingerModeAffectedStreams = ringerModeAffectedStreams;
1452                    setRingerModeInt(getRingerMode(), false);
1453                }
1454
1455                int notificationsUseRingVolume = Settings.System.getInt(mContentResolver,
1456                        Settings.System.NOTIFICATIONS_USE_RING_VOLUME,
1457                        1);
1458                if (notificationsUseRingVolume != mNotificationsUseRingVolume) {
1459                    mNotificationsUseRingVolume = notificationsUseRingVolume;
1460                    if (mNotificationsUseRingVolume == 1) {
1461                        STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING;
1462                        mStreamStates[AudioSystem.STREAM_NOTIFICATION].setVolumeIndexSettingName(
1463                                System.VOLUME_SETTINGS[AudioSystem.STREAM_RING]);
1464                    } else {
1465                        STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_NOTIFICATION;
1466                        mStreamStates[AudioSystem.STREAM_NOTIFICATION].setVolumeIndexSettingName(
1467                                System.VOLUME_SETTINGS[AudioSystem.STREAM_NOTIFICATION]);
1468                        // Persist notification volume volume as it was not persisted while aliased to ring volume
1469                        //  and persist with no delay as there might be registered observers of the persisted
1470                        //  notification volume.
1471                        sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, AudioSystem.STREAM_NOTIFICATION,
1472                                SENDMSG_REPLACE, 0, 0, mStreamStates[AudioSystem.STREAM_NOTIFICATION], 0);
1473                    }
1474                }
1475            }
1476        }
1477    }
1478
1479    /**
1480     * Receiver for misc intent broadcasts the Phone app cares about.
1481     */
1482    private class AudioServiceBroadcastReceiver extends BroadcastReceiver {
1483        @Override
1484        public void onReceive(Context context, Intent intent) {
1485            String action = intent.getAction();
1486
1487            if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
1488                int state = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE,
1489                                               BluetoothA2dp.STATE_DISCONNECTED);
1490                BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
1491                String address = btDevice.getAddress();
1492                boolean isConnected = (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
1493                                       ((String)mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)).equals(address));
1494
1495                if (isConnected &&
1496                    state != BluetoothA2dp.STATE_CONNECTED && state != BluetoothA2dp.STATE_PLAYING) {
1497                    if (address.equals(sBtDockAddress)) {
1498                        Log.v(TAG, "Recognized undocking from BT dock");
1499                        AudioSystem.setForceUse(AudioSystem.FOR_DOCK, AudioSystem.FORCE_NONE);
1500                    }
1501                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
1502                            AudioSystem.DEVICE_STATE_UNAVAILABLE,
1503                            address);
1504                    mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
1505                } else if (!isConnected &&
1506                             (state == BluetoothA2dp.STATE_CONNECTED ||
1507                              state == BluetoothA2dp.STATE_PLAYING)) {
1508                    if (btDevice.isBluetoothDock()) {
1509                        Log.v(TAG, "Recognized connection to BT dock");
1510                        sBtDockAddress = address;
1511                        Intent i = context.registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT));
1512                        if (i != null) {
1513                            int dockState = i.getIntExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_UNDOCKED);
1514                            int config;
1515                            switch (dockState) {
1516                                case Intent.EXTRA_DOCK_STATE_DESK:
1517                                    config = AudioSystem.FORCE_BT_DESK_DOCK;
1518                                    break;
1519                                case Intent.EXTRA_DOCK_STATE_CAR:
1520                                    config = AudioSystem.FORCE_BT_CAR_DOCK;
1521                                    break;
1522                                case Intent.EXTRA_DOCK_STATE_UNDOCKED:
1523                                default:
1524                                    config = AudioSystem.FORCE_NONE;
1525                            }
1526                            AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
1527                        }
1528                    }
1529                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
1530                                                         AudioSystem.DEVICE_STATE_AVAILABLE,
1531                                                         address);
1532                    // Reset A2DP suspend state each time a new sink is connected
1533                    AudioSystem.setParameters("A2dpSuspended=false");
1534                    mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP),
1535                            address);
1536                }
1537            } else if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) {
1538                int state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
1539                                               BluetoothHeadset.STATE_ERROR);
1540                int device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
1541                BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
1542                String address = null;
1543                if (btDevice != null) {
1544                    address = btDevice.getAddress();
1545                    BluetoothClass btClass = btDevice.getBluetoothClass();
1546                    if (btClass != null) {
1547                        switch (btClass.getDeviceClass()) {
1548                        case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
1549                        case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
1550                            device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
1551                            break;
1552                        case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
1553                            device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
1554                            break;
1555                        }
1556                    }
1557                }
1558
1559                boolean isConnected = (mConnectedDevices.containsKey(device) &&
1560                                       ((String)mConnectedDevices.get(device)).equals(address));
1561
1562                if (isConnected && state != BluetoothHeadset.STATE_CONNECTED) {
1563                    AudioSystem.setDeviceConnectionState(device,
1564                                                         AudioSystem.DEVICE_STATE_UNAVAILABLE,
1565                                                         address);
1566                    mConnectedDevices.remove(device);
1567                } else if (!isConnected && state == BluetoothHeadset.STATE_CONNECTED) {
1568                    AudioSystem.setDeviceConnectionState(device,
1569                                                         AudioSystem.DEVICE_STATE_AVAILABLE,
1570                                                         address);
1571                    mConnectedDevices.put(new Integer(device), address);
1572                }
1573            } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
1574                int state = intent.getIntExtra("state", 0);
1575                int microphone = intent.getIntExtra("microphone", 0);
1576
1577                if (microphone != 0) {
1578                    boolean isConnected = mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
1579                    if (state == 0 && isConnected) {
1580                        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
1581                                AudioSystem.DEVICE_STATE_UNAVAILABLE,
1582                                "");
1583                        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
1584                    } else if (state == 1 && !isConnected)  {
1585                        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
1586                                AudioSystem.DEVICE_STATE_AVAILABLE,
1587                                "");
1588                        mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADSET), "");
1589                    }
1590                } else {
1591                    boolean isConnected = mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
1592                    if (state == 0 && isConnected) {
1593                        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
1594                                AudioSystem.DEVICE_STATE_UNAVAILABLE,
1595                                "");
1596                        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
1597                    } else if (state == 1 && !isConnected)  {
1598                        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
1599                                AudioSystem.DEVICE_STATE_AVAILABLE,
1600                                "");
1601                        mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), "");
1602                    }
1603                }
1604            }
1605        }
1606    }
1607
1608    //==========================================================================================
1609    // AudioFocus
1610    //==========================================================================================
1611    private static class FocusStackEntry {
1612        public int mStreamType = -1;// no stream type
1613        public boolean mIsTransportControlReceiver = false;
1614        public IAudioFocusDispatcher mFocusDispatcher = null;
1615        public IBinder mSourceRef = null;
1616        public String mClientId;
1617        public int mDurationHint;
1618
1619        public FocusStackEntry() {
1620        }
1621
1622        public FocusStackEntry(int streamType, int duration, boolean isTransportControlReceiver,
1623                IAudioFocusDispatcher afl, IBinder source, String id) {
1624            mStreamType = streamType;
1625            mIsTransportControlReceiver = isTransportControlReceiver;
1626            mFocusDispatcher = afl;
1627            mSourceRef = source;
1628            mClientId = id;
1629            mDurationHint = duration;
1630        }
1631    }
1632
1633    private Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>();
1634
1635    /**
1636     * Helper function:
1637     * Display in the log the current entries in the audio focus stack
1638     */
1639    private void dumpFocusStack(PrintWriter pw) {
1640        pw.println("Audio Focus stack entries:");
1641        synchronized(mFocusStack) {
1642            Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
1643            while(stackIterator.hasNext()) {
1644                FocusStackEntry fse = stackIterator.next();
1645                pw.println("     source:" + fse.mSourceRef + " -- client: " + fse.mClientId
1646                        + " -- duration: " +fse.mDurationHint);
1647            }
1648        }
1649    }
1650
1651    /**
1652     * Helper function:
1653     * Remove a focus listener from the focus stack.
1654     * @param focusListenerToRemove the focus listener
1655     * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
1656     *   focus, notify the next item in the stack it gained focus.
1657     */
1658    private void removeFocusStackEntry(String clientToRemove, boolean signal) {
1659        // is the current top of the focus stack abandoning focus? (because of death or request)
1660        if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove))
1661        {
1662            //Log.i(TAG, "   removeFocusStackEntry() removing top of stack");
1663            mFocusStack.pop();
1664            if (signal) {
1665                // notify the new top of the stack it gained focus
1666                if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)
1667                        && canReassignFocus()) {
1668                    try {
1669                        mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
1670                                AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId);
1671                    } catch (RemoteException e) {
1672                        Log.e(TAG, " Failure to signal gain of focus due to "+ e);
1673                        e.printStackTrace();
1674                    }
1675                }
1676            }
1677        } else {
1678            // focus is abandoned by a client that's not at the top of the stack,
1679            // no need to update focus.
1680            Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
1681            while(stackIterator.hasNext()) {
1682                FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
1683                if(fse.mClientId.equals(clientToRemove)) {
1684                    Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
1685                            + fse.mClientId);
1686                    mFocusStack.remove(fse);
1687                }
1688            }
1689        }
1690    }
1691
1692    /**
1693     * Helper function:
1694     * Remove focus listeners from the focus stack for a particular client.
1695     */
1696    private void removeFocusStackEntryForClient(IBinder cb) {
1697        // focus is abandoned by a client that's not at the top of the stack,
1698        // no need to update focus.
1699        Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
1700        while(stackIterator.hasNext()) {
1701            FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
1702            if(fse.mSourceRef.equals(cb)) {
1703                Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
1704                        + fse.mClientId);
1705                mFocusStack.remove(fse);
1706            }
1707        }
1708    }
1709
1710    /**
1711     * Helper function:
1712     * Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
1713     */
1714    private boolean canReassignFocus() {
1715        // focus requests are rejected during a phone call
1716        if (getMode() == AudioSystem.MODE_IN_CALL) {
1717            Log.i(TAG, " AudioFocus  can't be reassigned during a call, exiting");
1718            return false;
1719        }
1720        return true;
1721    }
1722
1723    /**
1724     * Inner class to monitor audio focus client deaths, and remove them from the audio focus
1725     * stack if necessary.
1726     */
1727    private class AudioFocusDeathHandler implements IBinder.DeathRecipient {
1728        private IBinder mCb; // To be notified of client's death
1729
1730        AudioFocusDeathHandler(IBinder cb) {
1731            mCb = cb;
1732        }
1733
1734        public void binderDied() {
1735            synchronized(mFocusStack) {
1736                Log.w(TAG, "  AudioFocus   audio focus client died");
1737                removeFocusStackEntryForClient(mCb);
1738            }
1739        }
1740
1741        public IBinder getBinder() {
1742            return mCb;
1743        }
1744    }
1745
1746
1747    /** @see AudioManager#requestAudioFocus(int, int, IBinder, IAudioFocusDispatcher, String) */
1748    public int requestAudioFocus(int mainStreamType, int durationHint, IBinder cb,
1749            IAudioFocusDispatcher fd, String clientId) {
1750        Log.i(TAG, " AudioFocus  requestAudioFocus() from " + clientId);
1751        // the main stream type for the audio focus request is currently not used. It may
1752        // potentially be used to handle multiple stream type-dependent audio focuses.
1753
1754        if ((cb == null) || !cb.pingBinder()) {
1755            Log.i(TAG, " AudioFocus  DOA client for requestAudioFocus(), exiting");
1756            return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
1757        }
1758
1759        if (!canReassignFocus()) {
1760            return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
1761        }
1762
1763        synchronized(mFocusStack) {
1764            if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) {
1765                mFocusStack.peek().mDurationHint = durationHint;
1766                // if focus is already owned by this client, don't do anything
1767                return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
1768            }
1769
1770            // notify current top of stack it is losing focus
1771            if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
1772                try {
1773                    mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
1774                            (durationHint == AudioManager.AUDIOFOCUS_GAIN) ?
1775                                    AudioManager.AUDIOFOCUS_LOSS :
1776                                        AudioManager.AUDIOFOCUS_LOSS_TRANSIENT,
1777                            mFocusStack.peek().mClientId);
1778                } catch (RemoteException e) {
1779                    Log.e(TAG, " Failure to signal loss of focus due to "+ e);
1780                    e.printStackTrace();
1781                }
1782            }
1783
1784            // push focus requester at the top of the audio focus stack
1785            mFocusStack.push(new FocusStackEntry(mainStreamType, durationHint, false, fd, cb,
1786                    clientId));
1787        }//synchronized(mFocusStack)
1788
1789        // handle the potential premature death of the new holder of the focus
1790        // (premature death == death before abandoning focus)
1791        // Register for client death notification
1792        AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
1793        try {
1794            cb.linkToDeath(afdh, 0);
1795        } catch (RemoteException e) {
1796            // client has already died!
1797            Log.w(TAG, " AudioFocus  requestAudioFocus() could not link to "+cb+" binder death");
1798        }
1799
1800        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
1801    }
1802
1803    /** @see AudioManager#abandonAudioFocus(IBinder, IAudioFocusDispatcher, String) */
1804    public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) {
1805        Log.i(TAG, " AudioFocus  abandonAudioFocus() from " + clientId);
1806
1807        // this will take care of notifying the new focus owner if needed
1808        removeFocusStackEntry(clientId, true);
1809
1810        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
1811    }
1812
1813
1814    public void unregisterFocusClient(String clientId) {
1815        removeFocusStackEntry(clientId, false);
1816    }
1817
1818
1819    @Override
1820    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1821        // TODO probably a lot more to do here than just the audio focus stack
1822        dumpFocusStack(pw);
1823    }
1824
1825
1826}
1827