blob: 25a37058a0bebab0217917540589ef8a108461b1 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
Amith Yamasanic696a532011-10-28 17:02:37 -070019import static android.media.AudioManager.RINGER_MODE_NORMAL;
20import static android.media.AudioManager.RINGER_MODE_SILENT;
21import static android.media.AudioManager.RINGER_MODE_VIBRATE;
22
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.app.ActivityManagerNative;
Eric Laurent96f218e2011-08-15 15:55:21 -070024import android.app.KeyguardManager;
Jean-Michel Trivif0cff042011-09-14 18:11:09 -070025import android.app.PendingIntent;
26import android.app.PendingIntent.CanceledException;
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -070027import android.bluetooth.BluetoothA2dp;
28import android.bluetooth.BluetoothAdapter;
29import android.bluetooth.BluetoothClass;
30import android.bluetooth.BluetoothDevice;
31import android.bluetooth.BluetoothHeadset;
32import android.bluetooth.BluetoothProfile;
Nick Pellybd022f42009-08-14 18:33:38 -070033import android.content.BroadcastReceiver;
Jean-Michel Trivid327f212010-03-16 21:44:33 -070034import android.content.ComponentName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.content.ContentResolver;
36import android.content.Context;
37import android.content.Intent;
Eric Laurenta553c252009-07-17 12:17:14 -070038import android.content.IntentFilter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.content.pm.PackageManager;
Jason Parekhb1096152009-03-24 17:48:25 -070040import android.database.ContentObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.media.MediaPlayer.OnCompletionListener;
42import android.media.MediaPlayer.OnErrorListener;
43import android.os.Binder;
Jean-Michel Trivif0cff042011-09-14 18:11:09 -070044import android.os.Bundle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import 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;
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -070052import android.os.SystemProperties;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.provider.Settings;
54import android.provider.Settings.System;
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -070055import android.telephony.PhoneStateListener;
56import android.telephony.TelephonyManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057import android.util.Log;
Jean-Michel Trivid327f212010-03-16 21:44:33 -070058import android.view.KeyEvent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059import android.view.VolumePanel;
60
61import com.android.internal.telephony.ITelephony;
62
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -080063import java.io.FileDescriptor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064import java.io.IOException;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -080065import java.io.PrintWriter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066import java.util.ArrayList;
Eric Laurentc42ac9d2009-07-29 08:53:03 -070067import java.util.HashMap;
68import java.util.Iterator;
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -070069import java.util.List;
Eric Laurentc42ac9d2009-07-29 08:53:03 -070070import java.util.Map;
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -070071import java.util.NoSuchElementException;
Eric Laurentc42ac9d2009-07-29 08:53:03 -070072import java.util.Set;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -080073import java.util.Stack;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074
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
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -070091 /** Debug remote control client/display feature */
Jean-Michel Trivi7ff866e2011-10-13 18:09:26 -070092 protected static final boolean DEBUG_RC = false;
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -070093
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094 /** 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;
Joe Onoratoc7fcba42011-01-05 16:53:11 -080099 private boolean mVoiceCapable;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -0800100
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 /** 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;
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -0700122 private static final int MSG_BTA2DP_DOCK_TIMEOUT = 8;
Eric Laurent117b7bb2011-01-16 17:07:27 -0800123 private static final int MSG_LOAD_SOUND_EFFECTS = 9;
Eric Laurentfa640152011-03-12 15:59:51 -0800124 private static final int MSG_SET_FORCE_USE = 10;
Jean-Michel Trivid589fea2011-04-15 11:28:10 -0700125 private static final int MSG_PERSIST_MEDIABUTTONRECEIVER = 11;
Eric Laurentdc03c612011-04-01 10:59:41 -0700126 private static final int MSG_BT_HEADSET_CNCT_FAILED = 12;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -0700127 private static final int MSG_RCDISPLAY_CLEAR = 13;
128 private static final int MSG_RCDISPLAY_UPDATE = 14;
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -0700129
130 private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
Eric Laurentdc03c612011-04-01 10:59:41 -0700131 // Timeout for connection to bluetooth headset service
132 private static final int BT_HEADSET_CNCT_TIMEOUT_MS = 3000;
133
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134
135 /** @see AudioSystemThread */
136 private AudioSystemThread mAudioSystemThread;
137 /** @see AudioHandler */
138 private AudioHandler mAudioHandler;
139 /** @see VolumeStreamState */
140 private VolumeStreamState[] mStreamStates;
Jason Parekhb1096152009-03-24 17:48:25 -0700141 private SettingsObserver mSettingsObserver;
Eric Laurenta553c252009-07-17 12:17:14 -0700142
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143 private int mMode;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800144 private Object mSettingsLock = new Object();
145 private boolean mMediaServerOk;
146
147 private SoundPool mSoundPool;
148 private Object mSoundEffectsLock = new Object();
149 private static final int NUM_SOUNDPOOL_CHANNELS = 4;
150 private static final int SOUND_EFFECT_VOLUME = 1000;
151
152 /* Sound effect file names */
153 private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/";
154 private static final String[] SOUND_EFFECT_FILES = new String[] {
155 "Effect_Tick.ogg",
156 "KeypressStandard.ogg",
157 "KeypressSpacebar.ogg",
158 "KeypressDelete.ogg",
159 "KeypressReturn.ogg"
160 };
161
162 /* Sound effect file name mapping sound effect id (AudioManager.FX_xxx) to
163 * file index in SOUND_EFFECT_FILES[] (first column) and indicating if effect
164 * uses soundpool (second column) */
165 private int[][] SOUND_EFFECT_FILES_MAP = new int[][] {
166 {0, -1}, // FX_KEY_CLICK
167 {0, -1}, // FX_FOCUS_NAVIGATION_UP
168 {0, -1}, // FX_FOCUS_NAVIGATION_DOWN
169 {0, -1}, // FX_FOCUS_NAVIGATION_LEFT
170 {0, -1}, // FX_FOCUS_NAVIGATION_RIGHT
171 {1, -1}, // FX_KEYPRESS_STANDARD
172 {2, -1}, // FX_KEYPRESS_SPACEBAR
173 {3, -1}, // FX_FOCUS_DELETE
174 {4, -1} // FX_FOCUS_RETURN
175 };
176
Jared Suttles59820132009-08-13 21:50:52 -0500177 /** @hide Maximum volume index values for audio streams */
178 private int[] MAX_STREAM_VOLUME = new int[] {
Eric Laurent6ee99522009-08-25 06:30:59 -0700179 5, // STREAM_VOICE_CALL
180 7, // STREAM_SYSTEM
181 7, // STREAM_RING
182 15, // STREAM_MUSIC
183 7, // STREAM_ALARM
184 7, // STREAM_NOTIFICATION
185 15, // STREAM_BLUETOOTH_SCO
186 7, // STREAM_SYSTEM_ENFORCED
187 15, // STREAM_DTMF
188 15 // STREAM_TTS
Jared Suttles59820132009-08-13 21:50:52 -0500189 };
Eric Laurenta553c252009-07-17 12:17:14 -0700190 /* STREAM_VOLUME_ALIAS[] indicates for each stream if it uses the volume settings
191 * of another stream: This avoids multiplying the volume settings for hidden
192 * stream types that follow other stream behavior for volume settings
193 * NOTE: do not create loops in aliases! */
194 private int[] STREAM_VOLUME_ALIAS = new int[] {
195 AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL
196 AudioSystem.STREAM_SYSTEM, // STREAM_SYSTEM
197 AudioSystem.STREAM_RING, // STREAM_RING
198 AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
199 AudioSystem.STREAM_ALARM, // STREAM_ALARM
Eric Laurent4eb5a572011-08-02 16:58:10 -0700200 AudioSystem.STREAM_RING, // STREAM_NOTIFICATION
Eric Laurent484d2882009-12-08 09:05:45 -0800201 AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO
Eric Laurenta553c252009-07-17 12:17:14 -0700202 AudioSystem.STREAM_SYSTEM, // STREAM_SYSTEM_ENFORCED
203 AudioSystem.STREAM_VOICE_CALL, // STREAM_DTMF
204 AudioSystem.STREAM_MUSIC // STREAM_TTS
205 };
206
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207 private AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() {
208 public void onError(int error) {
209 switch (error) {
210 case AudioSystem.AUDIO_STATUS_SERVER_DIED:
211 if (mMediaServerOk) {
212 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
213 null, 1500);
Eric Laurent89e74ba2009-09-30 18:26:36 -0700214 mMediaServerOk = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215 }
216 break;
217 case AudioSystem.AUDIO_STATUS_OK:
218 if (!mMediaServerOk) {
219 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_STARTED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
220 null, 0);
Eric Laurent89e74ba2009-09-30 18:26:36 -0700221 mMediaServerOk = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800222 }
223 break;
224 default:
225 break;
226 }
227 }
228 };
229
230 /**
231 * Current ringer mode from one of {@link AudioManager#RINGER_MODE_NORMAL},
232 * {@link AudioManager#RINGER_MODE_SILENT}, or
233 * {@link AudioManager#RINGER_MODE_VIBRATE}.
234 */
235 private int mRingerMode;
236
Eric Laurent9bcf4012009-06-12 06:09:28 -0700237 /** @see System#MODE_RINGER_STREAMS_AFFECTED */
238 private int mRingerModeAffectedStreams;
239
Eric Laurent5b4e6542010-03-19 20:02:21 -0700240 // Streams currently muted by ringer mode
241 private int mRingerModeMutedStreams;
242
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800243 /** @see System#MUTE_STREAMS_AFFECTED */
244 private int mMuteAffectedStreams;
245
246 /**
247 * Has multiple bits per vibrate type to indicate the type's vibrate
248 * setting. See {@link #setVibrateSetting(int, int)}.
249 * <p>
250 * NOTE: This is not the final decision of whether vibrate is on/off for the
251 * type since it depends on the ringer mode. See {@link #shouldVibrate(int)}.
252 */
253 private int mVibrateSetting;
254
Eric Laurenta553c252009-07-17 12:17:14 -0700255 // Broadcast receiver for device connections intent broadcasts
256 private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
257
Jean-Michel Trivid327f212010-03-16 21:44:33 -0700258 // Broadcast receiver for media button broadcasts (separate from mReceiver to
259 // independently change its priority)
260 private final BroadcastReceiver mMediaButtonReceiver = new MediaButtonBroadcastReceiver();
261
Jean-Michel Trivie73131a2010-06-14 09:53:30 -0700262 // Used to alter media button redirection when the phone is ringing.
263 private boolean mIsRinging = false;
264
Eric Laurentc42ac9d2009-07-29 08:53:03 -0700265 // Devices currently connected
266 private HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>();
267
268 // Forced device usage for communications
269 private int mForcedUseForComm;
270
Eric Laurent9272b4b2010-01-23 17:12:59 -0800271 // List of binder death handlers for setMode() client processes.
272 // The last process to have called setMode() is at the top of the list.
273 private ArrayList <SetModeDeathHandler> mSetModeDeathHandlers = new ArrayList <SetModeDeathHandler>();
Eric Laurenteb14a782009-12-17 03:12:59 -0800274
Eric Laurent3def1ee2010-03-17 23:26:26 -0700275 // List of clients having issued a SCO start request
276 private ArrayList <ScoClient> mScoClients = new ArrayList <ScoClient>();
277
278 // BluetoothHeadset API to control SCO connection
279 private BluetoothHeadset mBluetoothHeadset;
280
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -0700281 // Bluetooth headset device
282 private BluetoothDevice mBluetoothHeadsetDevice;
Eric Laurent3def1ee2010-03-17 23:26:26 -0700283
Eric Laurent62ef7672010-11-24 10:58:32 -0800284 // Indicate if SCO audio connection is currently active and if the initiator is
285 // audio service (internal) or bluetooth headset (external)
286 private int mScoAudioState;
287 // SCO audio state is not active
288 private static final int SCO_STATE_INACTIVE = 0;
Eric Laurentdc03c612011-04-01 10:59:41 -0700289 // SCO audio activation request waiting for headset service to connect
290 private static final int SCO_STATE_ACTIVATE_REQ = 1;
Eric Laurent62ef7672010-11-24 10:58:32 -0800291 // SCO audio state is active or starting due to a local request to start a virtual call
Eric Laurentdc03c612011-04-01 10:59:41 -0700292 private static final int SCO_STATE_ACTIVE_INTERNAL = 3;
293 // SCO audio deactivation request waiting for headset service to connect
294 private static final int SCO_STATE_DEACTIVATE_REQ = 5;
295
Eric Laurent62ef7672010-11-24 10:58:32 -0800296 // SCO audio state is active due to an action in BT handsfree (either voice recognition or
297 // in call audio)
298 private static final int SCO_STATE_ACTIVE_EXTERNAL = 2;
Eric Laurentdc03c612011-04-01 10:59:41 -0700299 // Deactivation request for all SCO connections (initiated by audio mode change)
300 // waiting for headset service to connect
301 private static final int SCO_STATE_DEACTIVATE_EXT_REQ = 4;
302
303 // Current connection state indicated by bluetooth headset
304 private int mScoConnectionState;
Eric Laurent62ef7672010-11-24 10:58:32 -0800305
Eric Laurenta60e2122010-12-28 16:49:07 -0800306 // true if boot sequence has been completed
307 private boolean mBootCompleted;
308 // listener for SoundPool sample load completion indication
309 private SoundPoolCallback mSoundPoolCallBack;
310 // thread for SoundPool listener
311 private SoundPoolListenerThread mSoundPoolListenerThread;
312 // message looper for SoundPool listener
313 private Looper mSoundPoolLooper = null;
Eric Laurent25101b02011-02-02 09:33:30 -0800314 // default volume applied to sound played with playSoundEffect()
315 private static final int SOUND_EFFECT_DEFAULT_VOLUME_DB = -20;
316 // volume applied to sound played with playSoundEffect() read from ro.config.sound_fx_volume
317 private int SOUND_EFFECT_VOLUME_DB;
318 // getActiveStreamType() will return STREAM_NOTIFICATION during this period after a notification
319 // stopped
320 private static final int NOTIFICATION_VOLUME_DELAY_MS = 5000;
321 // previous volume adjustment direction received by checkForRingerModeChange()
322 private int mPrevVolDirection = AudioManager.ADJUST_SAME;
Eric Laurent96f218e2011-08-15 15:55:21 -0700323 // Keyguard manager proxy
324 private KeyguardManager mKeyguardManager;
Eric Laurenta60e2122010-12-28 16:49:07 -0800325
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800326 ///////////////////////////////////////////////////////////////////////////
327 // Construction
328 ///////////////////////////////////////////////////////////////////////////
329
330 /** @hide */
331 public AudioService(Context context) {
332 mContext = context;
333 mContentResolver = context.getContentResolver();
Joe Onoratoc7fcba42011-01-05 16:53:11 -0800334 mVoiceCapable = mContext.getResources().getBoolean(
335 com.android.internal.R.bool.config_voice_capable);
Jared Suttles59820132009-08-13 21:50:52 -0500336
337 // Intialized volume
338 MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = SystemProperties.getInt(
339 "ro.config.vc_call_vol_steps",
340 MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL]);
341
Eric Laurent25101b02011-02-02 09:33:30 -0800342 SOUND_EFFECT_VOLUME_DB = SystemProperties.getInt(
343 "ro.config.sound_fx_volume",
344 SOUND_EFFECT_DEFAULT_VOLUME_DB);
345
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800346 mVolumePanel = new VolumePanel(context, this);
Eric Laurentc42ac9d2009-07-29 08:53:03 -0700347 mForcedUseForComm = AudioSystem.FORCE_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800348 createAudioSystemThread();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 readPersistedSettings();
Eric Laurentc1d41662011-07-19 11:21:13 -0700350 mSettingsObserver = new SettingsObserver();
Eric Laurenta553c252009-07-17 12:17:14 -0700351 createStreamStates();
Eric Laurent9f103de2011-09-08 15:04:23 -0700352
353 mMode = AudioSystem.MODE_NORMAL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354 mMediaServerOk = true;
Eric Laurent3891c4c2010-04-20 09:40:57 -0700355
356 // Call setRingerModeInt() to apply correct mute
357 // state on streams affected by ringer mode.
358 mRingerModeMutedStreams = 0;
359 setRingerModeInt(getRingerMode(), false);
360
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800361 AudioSystem.setErrorCallback(mAudioSystemCallback);
Eric Laurenta553c252009-07-17 12:17:14 -0700362
363 // Register for device connection intent broadcasts.
364 IntentFilter intentFilter =
365 new IntentFilter(Intent.ACTION_HEADSET_PLUG);
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500366
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -0700367 intentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
Eric Laurent3def1ee2010-03-17 23:26:26 -0700368 intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -0700369 intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
370 intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500371 intentFilter.addAction(Intent.ACTION_USB_ANLG_HEADSET_PLUG);
372 intentFilter.addAction(Intent.ACTION_USB_DGTL_HEADSET_PLUG);
Praveen Bharathi26e37342010-11-02 19:23:30 -0700373 intentFilter.addAction(Intent.ACTION_HDMI_AUDIO_PLUG);
Eric Laurent62ef7672010-11-24 10:58:32 -0800374 intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
Eric Laurent950e8cb2011-10-13 08:57:54 -0700375 intentFilter.addAction(Intent.ACTION_SCREEN_ON);
376 intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
Eric Laurenta553c252009-07-17 12:17:14 -0700377 context.registerReceiver(mReceiver, intentFilter);
Jared Suttles59820132009-08-13 21:50:52 -0500378
Jean-Michel Trivid589fea2011-04-15 11:28:10 -0700379 // Register for package removal intent broadcasts for media button receiver persistence
380 IntentFilter pkgFilter = new IntentFilter();
381 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
382 pkgFilter.addDataScheme("package");
383 context.registerReceiver(mReceiver, pkgFilter);
384
Jean-Michel Trivid327f212010-03-16 21:44:33 -0700385 // Register for media button intent broadcasts.
386 intentFilter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -0700387 // Workaround for bug on priority setting
388 //intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
389 intentFilter.setPriority(Integer.MAX_VALUE);
Jean-Michel Trivid327f212010-03-16 21:44:33 -0700390 context.registerReceiver(mMediaButtonReceiver, intentFilter);
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -0700391
392 // Register for phone state monitoring
393 TelephonyManager tmgr = (TelephonyManager)
394 context.getSystemService(Context.TELEPHONY_SERVICE);
395 tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800396 }
397
398 private void createAudioSystemThread() {
399 mAudioSystemThread = new AudioSystemThread();
400 mAudioSystemThread.start();
401 waitForAudioHandlerCreation();
402 }
403
404 /** Waits for the volume handler to be created by the other thread. */
405 private void waitForAudioHandlerCreation() {
406 synchronized(this) {
407 while (mAudioHandler == null) {
408 try {
409 // Wait for mAudioHandler to be set by the other thread
410 wait();
411 } catch (InterruptedException e) {
412 Log.e(TAG, "Interrupted while waiting on volume handler.");
413 }
414 }
415 }
416 }
417
418 private void createStreamStates() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419 int numStreamTypes = AudioSystem.getNumStreamTypes();
420 VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
421
422 for (int i = 0; i < numStreamTypes; i++) {
Eric Laurenta553c252009-07-17 12:17:14 -0700423 streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[i]], i);
424 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800425
Eric Laurenta553c252009-07-17 12:17:14 -0700426 // Correct stream index values for streams with aliases
427 for (int i = 0; i < numStreamTypes; i++) {
428 if (STREAM_VOLUME_ALIAS[i] != i) {
429 int index = rescaleIndex(streams[i].mIndex, STREAM_VOLUME_ALIAS[i], i);
430 streams[i].mIndex = streams[i].getValidIndex(index);
431 setStreamVolumeIndex(i, index);
432 index = rescaleIndex(streams[i].mLastAudibleIndex, STREAM_VOLUME_ALIAS[i], i);
433 streams[i].mLastAudibleIndex = streams[i].getValidIndex(index);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800434 }
435 }
436 }
437
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800438 private void readPersistedSettings() {
439 final ContentResolver cr = mContentResolver;
440
441 mRingerMode = System.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);
Eric Laurent72668b22011-07-19 16:04:27 -0700442 // sanity check in case the settings are restored from a device with incompatible
443 // ringer modes
444 if (!AudioManager.isValidRingerMode(mRingerMode)) {
445 mRingerMode = AudioManager.RINGER_MODE_NORMAL;
446 System.putInt(cr, System.MODE_RINGER, mRingerMode);
447 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800448
449 mVibrateSetting = System.getInt(cr, System.VIBRATE_ON, 0);
450
Eric Laurentc1d41662011-07-19 11:21:13 -0700451 // make sure settings for ringer mode are consistent with device type: non voice capable
452 // devices (tablets) include media stream in silent mode whereas phones don't.
Eric Laurent9bcf4012009-06-12 06:09:28 -0700453 mRingerModeAffectedStreams = Settings.System.getInt(cr,
454 Settings.System.MODE_RINGER_STREAMS_AFFECTED,
Eric Laurenta553c252009-07-17 12:17:14 -0700455 ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
Eric Laurentc1d41662011-07-19 11:21:13 -0700456 (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)));
457 if (mVoiceCapable) {
458 mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
459 } else {
Eric Laurent402f7f22011-02-04 12:30:32 -0800460 mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
461 }
Eric Laurentc1d41662011-07-19 11:21:13 -0700462 Settings.System.putInt(cr,
463 Settings.System.MODE_RINGER_STREAMS_AFFECTED, mRingerModeAffectedStreams);
464
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800465 mMuteAffectedStreams = System.getInt(cr,
466 System.MUTE_STREAMS_AFFECTED,
467 ((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM)));
468
469 // Each stream will read its own persisted settings
470
471 // Broadcast the sticky intent
472 broadcastRingerMode();
473
474 // Broadcast vibrate settings
475 broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
476 broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION);
Jean-Michel Trivid589fea2011-04-15 11:28:10 -0700477
478 // Restore the default media button receiver from the system settings
479 restoreMediaButtonReceiver();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800480 }
481
Eric Laurenta553c252009-07-17 12:17:14 -0700482 private void setStreamVolumeIndex(int stream, int index) {
483 AudioSystem.setStreamVolumeIndex(stream, (index + 5)/10);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800484 }
485
Eric Laurenta553c252009-07-17 12:17:14 -0700486 private int rescaleIndex(int index, int srcStream, int dstStream) {
487 return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex();
488 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800489
490 ///////////////////////////////////////////////////////////////////////////
491 // IPC methods
492 ///////////////////////////////////////////////////////////////////////////
493
494 /** @see AudioManager#adjustVolume(int, int) */
495 public void adjustVolume(int direction, int flags) {
496 adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags);
497 }
498
499 /** @see AudioManager#adjustVolume(int, int, int) */
500 public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
501
Eric Laurent402f7f22011-02-04 12:30:32 -0800502 int streamType;
503 if ((flags & AudioManager.FLAG_FORCE_STREAM) != 0) {
504 streamType = suggestedStreamType;
505 } else {
506 streamType = getActiveStreamType(suggestedStreamType);
507 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800508
Eric Laurent96f218e2011-08-15 15:55:21 -0700509 // Play sounds on STREAM_RING only and if lock screen is not on.
510 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 &&
511 ((STREAM_VOLUME_ALIAS[streamType] != AudioSystem.STREAM_RING) ||
512 (mKeyguardManager != null && mKeyguardManager.isKeyguardLocked()))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800513 flags &= ~AudioManager.FLAG_PLAY_SOUND;
514 }
515
516 adjustStreamVolume(streamType, direction, flags);
517 }
518
519 /** @see AudioManager#adjustStreamVolume(int, int, int) */
520 public void adjustStreamVolume(int streamType, int direction, int flags) {
521 ensureValidDirection(direction);
522 ensureValidStreamType(streamType);
523
Eric Laurentb024c302011-10-14 17:19:27 -0700524 int streamTypeAlias = STREAM_VOLUME_ALIAS[streamType];
525 VolumeStreamState streamState = mStreamStates[streamTypeAlias];
Eric Laurent5b4e6542010-03-19 20:02:21 -0700526 final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800527 boolean adjustVolume = true;
528
529 // If either the client forces allowing ringer modes for this adjustment,
530 // or the stream type is one that is affected by ringer modes
Eric Laurent25101b02011-02-02 09:33:30 -0800531 if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
532 (!mVoiceCapable && streamType != AudioSystem.STREAM_VOICE_CALL &&
533 streamType != AudioSystem.STREAM_BLUETOOTH_SCO) ||
Eric Laurentb024c302011-10-14 17:19:27 -0700534 (mVoiceCapable && streamTypeAlias == AudioSystem.STREAM_RING)) {
Amith Yamasanic696a532011-10-28 17:02:37 -0700535 // do not vibrate if already in vibrate mode
536 if (mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
Eric Laurentcc11b1b2011-08-17 12:42:07 -0700537 flags &= ~AudioManager.FLAG_VIBRATE;
538 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 // Check if the ringer mode changes with this volume adjustment. If
540 // it does, it will handle adjusting the volume, so we won't below
Eric Laurentb024c302011-10-14 17:19:27 -0700541 adjustVolume = checkForRingerModeChange(oldIndex, direction, streamTypeAlias);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800542 }
543
Eric Laurent5b4e6542010-03-19 20:02:21 -0700544 // If stream is muted, adjust last audible index only
545 int index;
546 if (streamState.muteCount() != 0) {
547 if (adjustVolume) {
548 streamState.adjustLastAudibleIndex(direction);
549 // Post a persist volume msg
550 sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType,
551 SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY);
552 }
553 index = streamState.mLastAudibleIndex;
554 } else {
555 if (adjustVolume && streamState.adjustIndex(direction)) {
556 // Post message to set system volume (it in turn will post a message
557 // to persist). Do not change volume if stream is muted.
Eric Laurentb024c302011-10-14 17:19:27 -0700558 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamTypeAlias, SENDMSG_NOOP, 0, 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800559 streamState, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800560 }
Eric Laurent5b4e6542010-03-19 20:02:21 -0700561 index = streamState.mIndex;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800562 }
Eric Laurent25101b02011-02-02 09:33:30 -0800563
564 sendVolumeUpdate(streamType, oldIndex, index, flags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800565 }
566
567 /** @see AudioManager#setStreamVolume(int, int, int) */
568 public void setStreamVolume(int streamType, int index, int flags) {
569 ensureValidStreamType(streamType);
Eric Laurent5b4e6542010-03-19 20:02:21 -0700570 VolumeStreamState streamState = mStreamStates[STREAM_VOLUME_ALIAS[streamType]];
Eric Laurent9ce379a2010-02-16 06:00:26 -0800571
Eric Laurent5b4e6542010-03-19 20:02:21 -0700572 final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
Eric Laurent9ce379a2010-02-16 06:00:26 -0800573
Eric Laurentb024c302011-10-14 17:19:27 -0700574 // setting ring or notifications volume to 0 on voice capable devices enters silent mode
575 if (mVoiceCapable && (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
576 (STREAM_VOLUME_ALIAS[streamType] == AudioSystem.STREAM_RING))) {
577 int newRingerMode = mRingerMode;
578 if (index == 0) {
579 newRingerMode = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1
580 ? AudioManager.RINGER_MODE_VIBRATE
581 : AudioManager.RINGER_MODE_SILENT;
582 setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, false, true);
583 } else {
584 newRingerMode = AudioManager.RINGER_MODE_NORMAL;
585 }
586 if (newRingerMode != mRingerMode) {
587 setRingerMode(newRingerMode);
588 }
589 }
590
Eric Laurenta553c252009-07-17 12:17:14 -0700591 index = rescaleIndex(index * 10, streamType, STREAM_VOLUME_ALIAS[streamType]);
592 setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, false, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800593
Eric Laurent5b4e6542010-03-19 20:02:21 -0700594 index = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
595
Eric Laurent25101b02011-02-02 09:33:30 -0800596 sendVolumeUpdate(streamType, oldIndex, index, flags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800597 }
598
Eric Laurent25101b02011-02-02 09:33:30 -0800599 // UI update and Broadcast Intent
600 private void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags) {
601 if (!mVoiceCapable && (streamType == AudioSystem.STREAM_RING)) {
602 streamType = AudioSystem.STREAM_NOTIFICATION;
603 }
604
605 mVolumePanel.postVolumeChanged(streamType, flags);
606
Eric Laurent9ce379a2010-02-16 06:00:26 -0800607 oldIndex = (oldIndex + 5) / 10;
608 index = (index + 5) / 10;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800609 Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
610 intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
Eric Laurent9ce379a2010-02-16 06:00:26 -0800611 intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index);
612 intent.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex);
Eric Laurent9ce379a2010-02-16 06:00:26 -0800613 mContext.sendBroadcast(intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800614 }
615
616 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800617 * Sets the stream state's index, and posts a message to set system volume.
618 * This will not call out to the UI. Assumes a valid stream type.
619 *
620 * @param streamType Type of the stream
621 * @param index Desired volume index of the stream
622 * @param force If true, set the volume even if the desired volume is same
623 * as the current volume.
Eric Laurent9bcf4012009-06-12 06:09:28 -0700624 * @param lastAudible If true, stores new index as last audible one
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800625 */
Eric Laurent9bcf4012009-06-12 06:09:28 -0700626 private void setStreamVolumeInt(int streamType, int index, boolean force, boolean lastAudible) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800627 VolumeStreamState streamState = mStreamStates[streamType];
Eric Laurent5b4e6542010-03-19 20:02:21 -0700628
629 // If stream is muted, set last audible index only
630 if (streamState.muteCount() != 0) {
Eric Laurent758dd522010-04-13 10:38:05 -0700631 // Do not allow last audible index to be 0
632 if (index != 0) {
633 streamState.setLastAudibleIndex(index);
634 // Post a persist volume msg
635 sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType,
636 SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY);
637 }
Eric Laurent5b4e6542010-03-19 20:02:21 -0700638 } else {
639 if (streamState.setIndex(index, lastAudible) || force) {
640 // Post message to set system volume (it in turn will post a message
641 // to persist).
Eric Laurent758dd522010-04-13 10:38:05 -0700642 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamType, SENDMSG_NOOP, 0, 0,
643 streamState, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800644 }
645 }
646 }
647
648 /** @see AudioManager#setStreamSolo(int, boolean) */
649 public void setStreamSolo(int streamType, boolean state, IBinder cb) {
650 for (int stream = 0; stream < mStreamStates.length; stream++) {
651 if (!isStreamAffectedByMute(stream) || stream == streamType) continue;
652 // Bring back last audible volume
653 mStreamStates[stream].mute(cb, state);
654 }
655 }
656
657 /** @see AudioManager#setStreamMute(int, boolean) */
658 public void setStreamMute(int streamType, boolean state, IBinder cb) {
659 if (isStreamAffectedByMute(streamType)) {
660 mStreamStates[streamType].mute(cb, state);
661 }
662 }
663
Eric Laurent25101b02011-02-02 09:33:30 -0800664 /** get stream mute state. */
665 public boolean isStreamMute(int streamType) {
666 return (mStreamStates[streamType].muteCount() != 0);
667 }
668
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800669 /** @see AudioManager#getStreamVolume(int) */
670 public int getStreamVolume(int streamType) {
671 ensureValidStreamType(streamType);
Eric Laurenta553c252009-07-17 12:17:14 -0700672 return (mStreamStates[streamType].mIndex + 5) / 10;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800673 }
674
675 /** @see AudioManager#getStreamMaxVolume(int) */
676 public int getStreamMaxVolume(int streamType) {
677 ensureValidStreamType(streamType);
Eric Laurenta553c252009-07-17 12:17:14 -0700678 return (mStreamStates[streamType].getMaxIndex() + 5) / 10;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800679 }
680
Eric Laurent25101b02011-02-02 09:33:30 -0800681
682 /** Get last audible volume before stream was muted. */
683 public int getLastAudibleStreamVolume(int streamType) {
684 ensureValidStreamType(streamType);
685 return (mStreamStates[streamType].mLastAudibleIndex + 5) / 10;
686 }
687
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800688 /** @see AudioManager#getRingerMode() */
689 public int getRingerMode() {
690 return mRingerMode;
691 }
692
693 /** @see AudioManager#setRingerMode(int) */
694 public void setRingerMode(int ringerMode) {
Eric Laurenta553c252009-07-17 12:17:14 -0700695 synchronized (mSettingsLock) {
696 if (ringerMode != mRingerMode) {
697 setRingerModeInt(ringerMode, true);
698 // Send sticky broadcast
699 broadcastRingerMode();
700 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800701 }
702 }
703
Eric Laurent4050c932009-07-08 02:52:14 -0700704 private void setRingerModeInt(int ringerMode, boolean persist) {
Jason Parekhb1096152009-03-24 17:48:25 -0700705 mRingerMode = ringerMode;
706
Eric Laurent5b4e6542010-03-19 20:02:21 -0700707 // Mute stream if not previously muted by ringer mode and ringer mode
708 // is not RINGER_MODE_NORMAL and stream is affected by ringer mode.
709 // Unmute stream if previously muted by ringer mode and ringer mode
710 // is RINGER_MODE_NORMAL or stream is not affected by ringer mode.
Jason Parekhb1096152009-03-24 17:48:25 -0700711 int numStreamTypes = AudioSystem.getNumStreamTypes();
Eric Laurent5b4e6542010-03-19 20:02:21 -0700712 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
713 if (isStreamMutedByRingerMode(streamType)) {
714 if (!isStreamAffectedByRingerMode(streamType) ||
715 mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
Eric Laurentb024c302011-10-14 17:19:27 -0700716 // ring and notifications volume should never be 0 when not silenced
717 // on voice capable devices
718 if (mVoiceCapable &&
719 STREAM_VOLUME_ALIAS[streamType] == AudioSystem.STREAM_RING &&
720 mStreamStates[streamType].mLastAudibleIndex == 0) {
721 mStreamStates[streamType].mLastAudibleIndex = 10;
722 }
Eric Laurent5b4e6542010-03-19 20:02:21 -0700723 mStreamStates[streamType].mute(null, false);
724 mRingerModeMutedStreams &= ~(1 << streamType);
Eric Laurent9bcf4012009-06-12 06:09:28 -0700725 }
Eric Laurent5b4e6542010-03-19 20:02:21 -0700726 } else {
727 if (isStreamAffectedByRingerMode(streamType) &&
728 mRingerMode != AudioManager.RINGER_MODE_NORMAL) {
729 mStreamStates[streamType].mute(null, true);
730 mRingerModeMutedStreams |= (1 << streamType);
731 }
Jason Parekhb1096152009-03-24 17:48:25 -0700732 }
733 }
Eric Laurenta553c252009-07-17 12:17:14 -0700734
Jason Parekhb1096152009-03-24 17:48:25 -0700735 // Post a persist ringer mode msg
Eric Laurent4050c932009-07-08 02:52:14 -0700736 if (persist) {
737 sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE, SHARED_MSG,
738 SENDMSG_REPLACE, 0, 0, null, PERSIST_DELAY);
739 }
Jason Parekhb1096152009-03-24 17:48:25 -0700740 }
741
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800742 /** @see AudioManager#shouldVibrate(int) */
743 public boolean shouldVibrate(int vibrateType) {
744
745 switch (getVibrateSetting(vibrateType)) {
746
747 case AudioManager.VIBRATE_SETTING_ON:
748 return mRingerMode != AudioManager.RINGER_MODE_SILENT;
749
750 case AudioManager.VIBRATE_SETTING_ONLY_SILENT:
751 return mRingerMode == AudioManager.RINGER_MODE_VIBRATE;
752
753 case AudioManager.VIBRATE_SETTING_OFF:
Daniel Sandlerbcac4962010-04-12 13:23:57 -0400754 // return false, even for incoming calls
755 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800756
757 default:
758 return false;
759 }
760 }
761
762 /** @see AudioManager#getVibrateSetting(int) */
763 public int getVibrateSetting(int vibrateType) {
764 return (mVibrateSetting >> (vibrateType * 2)) & 3;
765 }
766
767 /** @see AudioManager#setVibrateSetting(int, int) */
768 public void setVibrateSetting(int vibrateType, int vibrateSetting) {
769
770 mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, vibrateType, vibrateSetting);
771
772 // Broadcast change
773 broadcastVibrateSetting(vibrateType);
774
775 // Post message to set ringer mode (it in turn will post a message
776 // to persist)
777 sendMsg(mAudioHandler, MSG_PERSIST_VIBRATE_SETTING, SHARED_MSG, SENDMSG_NOOP, 0, 0,
778 null, 0);
779 }
780
781 /**
782 * @see #setVibrateSetting(int, int)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800783 */
784 public static int getValueForVibrateSetting(int existingValue, int vibrateType,
785 int vibrateSetting) {
786
787 // First clear the existing setting. Each vibrate type has two bits in
788 // the value. Note '3' is '11' in binary.
789 existingValue &= ~(3 << (vibrateType * 2));
790
791 // Set into the old value
792 existingValue |= (vibrateSetting & 3) << (vibrateType * 2);
793
794 return existingValue;
795 }
796
Eric Laurent9272b4b2010-01-23 17:12:59 -0800797 private class SetModeDeathHandler implements IBinder.DeathRecipient {
798 private IBinder mCb; // To be notified of client's death
Marco Nelissenf1ddd512011-08-10 14:15:44 -0700799 private int mPid;
Eric Laurent9272b4b2010-01-23 17:12:59 -0800800 private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client
801
Eric Laurent9f103de2011-09-08 15:04:23 -0700802 SetModeDeathHandler(IBinder cb, int pid) {
Eric Laurent9272b4b2010-01-23 17:12:59 -0800803 mCb = cb;
Eric Laurent9f103de2011-09-08 15:04:23 -0700804 mPid = pid;
Eric Laurent9272b4b2010-01-23 17:12:59 -0800805 }
806
807 public void binderDied() {
Eric Laurentd7454be2011-09-14 08:45:58 -0700808 int newModeOwnerPid = 0;
Eric Laurent9272b4b2010-01-23 17:12:59 -0800809 synchronized(mSetModeDeathHandlers) {
810 Log.w(TAG, "setMode() client died");
811 int index = mSetModeDeathHandlers.indexOf(this);
812 if (index < 0) {
813 Log.w(TAG, "unregistered setMode() client died");
814 } else {
Eric Laurentd7454be2011-09-14 08:45:58 -0700815 newModeOwnerPid = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid);
Eric Laurent9272b4b2010-01-23 17:12:59 -0800816 }
817 }
Eric Laurent9f103de2011-09-08 15:04:23 -0700818 // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
819 // SCO connections not started by the application changing the mode
Eric Laurentd7454be2011-09-14 08:45:58 -0700820 if (newModeOwnerPid != 0) {
821 disconnectBluetoothSco(newModeOwnerPid);
Eric Laurent9f103de2011-09-08 15:04:23 -0700822 }
Eric Laurent9272b4b2010-01-23 17:12:59 -0800823 }
824
Marco Nelissenf1ddd512011-08-10 14:15:44 -0700825 public int getPid() {
826 return mPid;
827 }
828
Eric Laurent9272b4b2010-01-23 17:12:59 -0800829 public void setMode(int mode) {
830 mMode = mode;
831 }
832
833 public int getMode() {
834 return mMode;
835 }
836
837 public IBinder getBinder() {
838 return mCb;
839 }
840 }
841
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800842 /** @see AudioManager#setMode(int) */
Eric Laurent9272b4b2010-01-23 17:12:59 -0800843 public void setMode(int mode, IBinder cb) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800844 if (!checkAudioSettingsPermission("setMode()")) {
845 return;
846 }
Eric Laurenta553c252009-07-17 12:17:14 -0700847
Jean-Michel Trivi8f677d62010-11-15 12:11:32 -0800848 if (mode < AudioSystem.MODE_CURRENT || mode >= AudioSystem.NUM_MODES) {
Eric Laurenta553c252009-07-17 12:17:14 -0700849 return;
850 }
851
Eric Laurentd7454be2011-09-14 08:45:58 -0700852 int newModeOwnerPid = 0;
Eric Laurent9f103de2011-09-08 15:04:23 -0700853 synchronized(mSetModeDeathHandlers) {
Eric Laurenta553c252009-07-17 12:17:14 -0700854 if (mode == AudioSystem.MODE_CURRENT) {
855 mode = mMode;
856 }
Eric Laurentd7454be2011-09-14 08:45:58 -0700857 newModeOwnerPid = setModeInt(mode, cb, Binder.getCallingPid());
Eric Laurent9f103de2011-09-08 15:04:23 -0700858 }
859 // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
860 // SCO connections not started by the application changing the mode
Eric Laurentd7454be2011-09-14 08:45:58 -0700861 if (newModeOwnerPid != 0) {
862 disconnectBluetoothSco(newModeOwnerPid);
Eric Laurent9f103de2011-09-08 15:04:23 -0700863 }
864 }
Jean-Michel Trivi2ade5762010-12-11 13:18:30 -0800865
Eric Laurent9f103de2011-09-08 15:04:23 -0700866 // must be called synchronized on mSetModeDeathHandlers
Eric Laurentd7454be2011-09-14 08:45:58 -0700867 // setModeInt() returns a valid PID if the audio mode was successfully set to
Eric Laurent9f103de2011-09-08 15:04:23 -0700868 // any mode other than NORMAL.
Eric Laurentd7454be2011-09-14 08:45:58 -0700869 int setModeInt(int mode, IBinder cb, int pid) {
870 int newModeOwnerPid = 0;
Eric Laurent9f103de2011-09-08 15:04:23 -0700871 if (cb == null) {
872 Log.e(TAG, "setModeInt() called with null binder");
Eric Laurentd7454be2011-09-14 08:45:58 -0700873 return newModeOwnerPid;
Eric Laurent9f103de2011-09-08 15:04:23 -0700874 }
Jean-Michel Trivi2ade5762010-12-11 13:18:30 -0800875
Eric Laurent9f103de2011-09-08 15:04:23 -0700876 SetModeDeathHandler hdlr = null;
877 Iterator iter = mSetModeDeathHandlers.iterator();
878 while (iter.hasNext()) {
879 SetModeDeathHandler h = (SetModeDeathHandler)iter.next();
880 if (h.getPid() == pid) {
881 hdlr = h;
882 // Remove from client list so that it is re-inserted at top of list
883 iter.remove();
884 hdlr.getBinder().unlinkToDeath(hdlr, 0);
885 break;
886 }
887 }
888 int status = AudioSystem.AUDIO_STATUS_OK;
889 do {
890 if (mode == AudioSystem.MODE_NORMAL) {
891 // get new mode from client at top the list if any
892 if (!mSetModeDeathHandlers.isEmpty()) {
893 hdlr = mSetModeDeathHandlers.get(0);
894 cb = hdlr.getBinder();
895 mode = hdlr.getMode();
Eric Laurentb9c9d262009-05-06 08:13:20 -0700896 }
Eric Laurent9f103de2011-09-08 15:04:23 -0700897 } else {
898 if (hdlr == null) {
899 hdlr = new SetModeDeathHandler(cb, pid);
900 }
901 // Register for client death notification
902 try {
903 cb.linkToDeath(hdlr, 0);
904 } catch (RemoteException e) {
905 // Client has died!
906 Log.w(TAG, "setMode() could not link to "+cb+" binder death");
907 }
908
909 // Last client to call setMode() is always at top of client list
910 // as required by SetModeDeathHandler.binderDied()
911 mSetModeDeathHandlers.add(0, hdlr);
912 hdlr.setMode(mode);
913 }
914
915 if (mode != mMode) {
916 status = AudioSystem.setPhoneState(mode);
917 if (status == AudioSystem.AUDIO_STATUS_OK) {
918 // automatically handle audio focus for mode changes
919 handleFocusForCalls(mMode, mode, cb);
920 mMode = mode;
921 } else {
922 if (hdlr != null) {
923 mSetModeDeathHandlers.remove(hdlr);
924 cb.unlinkToDeath(hdlr, 0);
925 }
926 // force reading new top of mSetModeDeathHandlers stack
927 mode = AudioSystem.MODE_NORMAL;
928 }
929 } else {
930 status = AudioSystem.AUDIO_STATUS_OK;
931 }
932 } while (status != AudioSystem.AUDIO_STATUS_OK && !mSetModeDeathHandlers.isEmpty());
933
934 if (status == AudioSystem.AUDIO_STATUS_OK) {
935 if (mode != AudioSystem.MODE_NORMAL) {
Eric Laurentd7454be2011-09-14 08:45:58 -0700936 if (mSetModeDeathHandlers.isEmpty()) {
937 Log.e(TAG, "setMode() different from MODE_NORMAL with empty mode client stack");
938 } else {
939 newModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
940 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800941 }
942 int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
Eric Laurenta553c252009-07-17 12:17:14 -0700943 int index = mStreamStates[STREAM_VOLUME_ALIAS[streamType]].mIndex;
Eric Laurent5b4e6542010-03-19 20:02:21 -0700944 setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, true, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800945 }
Eric Laurentd7454be2011-09-14 08:45:58 -0700946 return newModeOwnerPid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800947 }
948
Jean-Michel Trivi2ade5762010-12-11 13:18:30 -0800949 /** pre-condition: oldMode != newMode */
Jean-Michel Trivi382f4e6e2011-03-08 14:38:59 -0800950 private void handleFocusForCalls(int oldMode, int newMode, IBinder cb) {
Jean-Michel Trivi2ade5762010-12-11 13:18:30 -0800951 // if ringing
952 if (newMode == AudioSystem.MODE_RINGTONE) {
953 // if not ringing silently
954 int ringVolume = AudioService.this.getStreamVolume(AudioManager.STREAM_RING);
955 if (ringVolume > 0) {
956 // request audio focus for the communication focus entry
957 requestAudioFocus(AudioManager.STREAM_RING,
Jean-Michel Trivi382f4e6e2011-03-08 14:38:59 -0800958 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, cb,
959 null /* IAudioFocusDispatcher allowed to be null only for this clientId */,
Jean-Michel Trivi8f619182011-07-21 15:10:10 -0700960 IN_VOICE_COMM_FOCUS_ID /*clientId*/,
961 "system");
Jean-Michel Trivi2ade5762010-12-11 13:18:30 -0800962
963 }
964 }
965 // if entering call
966 else if ((newMode == AudioSystem.MODE_IN_CALL)
967 || (newMode == AudioSystem.MODE_IN_COMMUNICATION)) {
968 // request audio focus for the communication focus entry
969 // (it's ok if focus was already requested during ringing)
970 requestAudioFocus(AudioManager.STREAM_RING,
Jean-Michel Trivi382f4e6e2011-03-08 14:38:59 -0800971 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, cb,
972 null /* IAudioFocusDispatcher allowed to be null only for this clientId */,
Jean-Michel Trivi8f619182011-07-21 15:10:10 -0700973 IN_VOICE_COMM_FOCUS_ID /*clientId*/,
974 "system");
Jean-Michel Trivi2ade5762010-12-11 13:18:30 -0800975 }
976 // if exiting call
977 else if (newMode == AudioSystem.MODE_NORMAL) {
978 // abandon audio focus for communication focus entry
979 abandonAudioFocus(null, IN_VOICE_COMM_FOCUS_ID);
980 }
981 }
982
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800983 /** @see AudioManager#getMode() */
984 public int getMode() {
985 return mMode;
986 }
987
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800988 /** @see AudioManager#playSoundEffect(int) */
989 public void playSoundEffect(int effectType) {
990 sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
Eric Laurenta2ef57d2009-09-28 04:46:10 -0700991 effectType, -1, null, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800992 }
993
994 /** @see AudioManager#playSoundEffect(int, float) */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800995 public void playSoundEffectVolume(int effectType, float volume) {
Eric Laurenta2ef57d2009-09-28 04:46:10 -0700996 loadSoundEffects();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800997 sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
998 effectType, (int) (volume * 1000), null, 0);
999 }
1000
1001 /**
1002 * Loads samples into the soundpool.
1003 * This method must be called at when sound effects are enabled
1004 */
1005 public boolean loadSoundEffects() {
Eric Laurenta60e2122010-12-28 16:49:07 -08001006 int status;
1007
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001008 synchronized (mSoundEffectsLock) {
Eric Laurenta60e2122010-12-28 16:49:07 -08001009 if (!mBootCompleted) {
1010 Log.w(TAG, "loadSoundEffects() called before boot complete");
1011 return false;
1012 }
1013
Eric Laurenta2ef57d2009-09-28 04:46:10 -07001014 if (mSoundPool != null) {
1015 return true;
1016 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001017 mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
1018 if (mSoundPool == null) {
Eric Laurenta60e2122010-12-28 16:49:07 -08001019 Log.w(TAG, "loadSoundEffects() could not allocate sound pool");
1020 return false;
1021 }
1022
1023 try {
1024 mSoundPoolCallBack = null;
1025 mSoundPoolListenerThread = new SoundPoolListenerThread();
1026 mSoundPoolListenerThread.start();
1027 // Wait for mSoundPoolCallBack to be set by the other thread
1028 mSoundEffectsLock.wait();
1029 } catch (InterruptedException e) {
1030 Log.w(TAG, "Interrupted while waiting sound pool listener thread.");
1031 }
1032
1033 if (mSoundPoolCallBack == null) {
1034 Log.w(TAG, "loadSoundEffects() could not create SoundPool listener or thread");
1035 if (mSoundPoolLooper != null) {
1036 mSoundPoolLooper.quit();
1037 mSoundPoolLooper = null;
1038 }
1039 mSoundPoolListenerThread = null;
1040 mSoundPool.release();
1041 mSoundPool = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001042 return false;
1043 }
1044 /*
1045 * poolId table: The value -1 in this table indicates that corresponding
1046 * file (same index in SOUND_EFFECT_FILES[] has not been loaded.
1047 * Once loaded, the value in poolId is the sample ID and the same
1048 * sample can be reused for another effect using the same file.
1049 */
1050 int[] poolId = new int[SOUND_EFFECT_FILES.length];
1051 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
1052 poolId[fileIdx] = -1;
1053 }
1054 /*
1055 * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded.
1056 * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0:
1057 * this indicates we have a valid sample loaded for this effect.
1058 */
Eric Laurenta60e2122010-12-28 16:49:07 -08001059
Eric Laurent117b7bb2011-01-16 17:07:27 -08001060 int lastSample = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001061 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
1062 // Do not load sample if this effect uses the MediaPlayer
1063 if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) {
1064 continue;
1065 }
1066 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
Eric Laurenta60e2122010-12-28 16:49:07 -08001067 String filePath = Environment.getRootDirectory()
1068 + SOUND_EFFECTS_PATH
1069 + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effect][0]];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001070 int sampleId = mSoundPool.load(filePath, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001071 if (sampleId <= 0) {
1072 Log.w(TAG, "Soundpool could not load file: "+filePath);
Eric Laurent117b7bb2011-01-16 17:07:27 -08001073 } else {
1074 SOUND_EFFECT_FILES_MAP[effect][1] = sampleId;
1075 poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId;
1076 lastSample = sampleId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001077 }
1078 } else {
1079 SOUND_EFFECT_FILES_MAP[effect][1] = poolId[SOUND_EFFECT_FILES_MAP[effect][0]];
1080 }
1081 }
Eric Laurenta60e2122010-12-28 16:49:07 -08001082 // wait for all samples to be loaded
Eric Laurent117b7bb2011-01-16 17:07:27 -08001083 if (lastSample != 0) {
1084 mSoundPoolCallBack.setLastSample(lastSample);
1085
1086 try {
1087 mSoundEffectsLock.wait();
1088 status = mSoundPoolCallBack.status();
1089 } catch (java.lang.InterruptedException e) {
1090 Log.w(TAG, "Interrupted while waiting sound pool callback.");
1091 status = -1;
1092 }
1093 } else {
Eric Laurenta60e2122010-12-28 16:49:07 -08001094 status = -1;
1095 }
Eric Laurent117b7bb2011-01-16 17:07:27 -08001096
Eric Laurenta60e2122010-12-28 16:49:07 -08001097 if (mSoundPoolLooper != null) {
1098 mSoundPoolLooper.quit();
1099 mSoundPoolLooper = null;
1100 }
1101 mSoundPoolListenerThread = null;
1102 if (status != 0) {
1103 Log.w(TAG,
1104 "loadSoundEffects(), Error "
Eric Laurent117b7bb2011-01-16 17:07:27 -08001105 + ((lastSample != 0) ? mSoundPoolCallBack.status() : -1)
Eric Laurenta60e2122010-12-28 16:49:07 -08001106 + " while loading samples");
Eric Laurent117b7bb2011-01-16 17:07:27 -08001107 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
1108 if (SOUND_EFFECT_FILES_MAP[effect][1] > 0) {
1109 SOUND_EFFECT_FILES_MAP[effect][1] = -1;
1110 }
1111 }
1112
Eric Laurenta60e2122010-12-28 16:49:07 -08001113 mSoundPool.release();
1114 mSoundPool = null;
1115 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001116 }
Eric Laurenta60e2122010-12-28 16:49:07 -08001117 return (status == 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001118 }
1119
1120 /**
1121 * Unloads samples from the sound pool.
1122 * This method can be called to free some memory when
1123 * sound effects are disabled.
1124 */
1125 public void unloadSoundEffects() {
1126 synchronized (mSoundEffectsLock) {
1127 if (mSoundPool == null) {
1128 return;
1129 }
Eric Laurenta60e2122010-12-28 16:49:07 -08001130
Eric Laurent117b7bb2011-01-16 17:07:27 -08001131 mAudioHandler.removeMessages(MSG_LOAD_SOUND_EFFECTS);
Eric Laurenta60e2122010-12-28 16:49:07 -08001132 mAudioHandler.removeMessages(MSG_PLAY_SOUND_EFFECT);
1133
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001134 int[] poolId = new int[SOUND_EFFECT_FILES.length];
1135 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
1136 poolId[fileIdx] = 0;
1137 }
1138
1139 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
1140 if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) {
1141 continue;
1142 }
1143 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) {
1144 mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]);
1145 SOUND_EFFECT_FILES_MAP[effect][1] = -1;
1146 poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1;
1147 }
1148 }
Eric Laurenta60e2122010-12-28 16:49:07 -08001149 mSoundPool.release();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001150 mSoundPool = null;
1151 }
1152 }
1153
Eric Laurenta60e2122010-12-28 16:49:07 -08001154 class SoundPoolListenerThread extends Thread {
1155 public SoundPoolListenerThread() {
1156 super("SoundPoolListenerThread");
1157 }
1158
1159 @Override
1160 public void run() {
1161
1162 Looper.prepare();
1163 mSoundPoolLooper = Looper.myLooper();
1164
1165 synchronized (mSoundEffectsLock) {
1166 if (mSoundPool != null) {
1167 mSoundPoolCallBack = new SoundPoolCallback();
1168 mSoundPool.setOnLoadCompleteListener(mSoundPoolCallBack);
1169 }
1170 mSoundEffectsLock.notify();
1171 }
1172 Looper.loop();
1173 }
1174 }
1175
1176 private final class SoundPoolCallback implements
1177 android.media.SoundPool.OnLoadCompleteListener {
1178
1179 int mStatus;
1180 int mLastSample;
1181
1182 public int status() {
1183 return mStatus;
1184 }
1185
1186 public void setLastSample(int sample) {
1187 mLastSample = sample;
1188 }
1189
1190 public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
1191 synchronized (mSoundEffectsLock) {
1192 if (status != 0) {
1193 mStatus = status;
1194 }
1195 if (sampleId == mLastSample) {
Eric Laurenta60e2122010-12-28 16:49:07 -08001196 mSoundEffectsLock.notify();
1197 }
1198 }
1199 }
1200 }
1201
Eric Laurent4050c932009-07-08 02:52:14 -07001202 /** @see AudioManager#reloadAudioSettings() */
1203 public void reloadAudioSettings() {
1204 // restore ringer mode, ringer mode affected streams, mute affected streams and vibrate settings
1205 readPersistedSettings();
1206
1207 // restore volume settings
1208 int numStreamTypes = AudioSystem.getNumStreamTypes();
1209 for (int streamType = 0; streamType < numStreamTypes; streamType++) {
1210 VolumeStreamState streamState = mStreamStates[streamType];
1211
Eric Laurentd25ae672009-07-27 02:12:30 -07001212 String settingName = System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[streamType]];
1213 String lastAudibleSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
1214 int index = Settings.System.getInt(mContentResolver,
1215 settingName,
1216 AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
1217 if (STREAM_VOLUME_ALIAS[streamType] != streamType) {
1218 index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType);
1219 } else {
1220 index *= 10;
Eric Laurent4050c932009-07-08 02:52:14 -07001221 }
Eric Laurentd25ae672009-07-27 02:12:30 -07001222 streamState.mIndex = streamState.getValidIndex(index);
1223
1224 index = (index + 5) / 10;
1225 index = Settings.System.getInt(mContentResolver,
1226 lastAudibleSettingName,
1227 (index > 0) ? index : AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
1228 if (STREAM_VOLUME_ALIAS[streamType] != streamType) {
1229 index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType);
1230 } else {
1231 index *= 10;
1232 }
1233 streamState.mLastAudibleIndex = streamState.getValidIndex(index);
Eric Laurenta553c252009-07-17 12:17:14 -07001234
Eric Laurent5b4e6542010-03-19 20:02:21 -07001235 // unmute stream that was muted but is not affect by mute anymore
Eric Laurent4050c932009-07-08 02:52:14 -07001236 if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType)) {
1237 int size = streamState.mDeathHandlers.size();
1238 for (int i = 0; i < size; i++) {
1239 streamState.mDeathHandlers.get(i).mMuteCount = 1;
1240 streamState.mDeathHandlers.get(i).mute(false);
1241 }
1242 }
1243 // apply stream volume
1244 if (streamState.muteCount() == 0) {
Eric Laurenta553c252009-07-17 12:17:14 -07001245 setStreamVolumeIndex(streamType, streamState.mIndex);
Eric Laurent4050c932009-07-08 02:52:14 -07001246 }
1247 }
1248
1249 // apply new ringer mode
1250 setRingerModeInt(getRingerMode(), false);
1251 }
1252
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001253 /** @see AudioManager#setSpeakerphoneOn() */
1254 public void setSpeakerphoneOn(boolean on){
Eric Laurentdc1d17a2009-09-10 00:48:21 -07001255 if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
1256 return;
1257 }
Eric Laurentfa640152011-03-12 15:59:51 -08001258 mForcedUseForComm = on ? AudioSystem.FORCE_SPEAKER : AudioSystem.FORCE_NONE;
1259
1260 sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SHARED_MSG, SENDMSG_QUEUE,
1261 AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0);
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001262 }
1263
1264 /** @see AudioManager#isSpeakerphoneOn() */
1265 public boolean isSpeakerphoneOn() {
Eric Laurentfa640152011-03-12 15:59:51 -08001266 return (mForcedUseForComm == AudioSystem.FORCE_SPEAKER);
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001267 }
1268
1269 /** @see AudioManager#setBluetoothScoOn() */
1270 public void setBluetoothScoOn(boolean on){
Eric Laurentdc1d17a2009-09-10 00:48:21 -07001271 if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
1272 return;
1273 }
Eric Laurentfa640152011-03-12 15:59:51 -08001274 mForcedUseForComm = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE;
1275
1276 sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SHARED_MSG, SENDMSG_QUEUE,
1277 AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0);
1278 sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SHARED_MSG, SENDMSG_QUEUE,
1279 AudioSystem.FOR_RECORD, mForcedUseForComm, null, 0);
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001280 }
1281
1282 /** @see AudioManager#isBluetoothScoOn() */
1283 public boolean isBluetoothScoOn() {
Eric Laurentfa640152011-03-12 15:59:51 -08001284 return (mForcedUseForComm == AudioSystem.FORCE_BT_SCO);
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001285 }
1286
Eric Laurent3def1ee2010-03-17 23:26:26 -07001287 /** @see AudioManager#startBluetoothSco() */
1288 public void startBluetoothSco(IBinder cb){
Eric Laurentdc03c612011-04-01 10:59:41 -07001289 if (!checkAudioSettingsPermission("startBluetoothSco()") ||
1290 !mBootCompleted) {
Eric Laurent3def1ee2010-03-17 23:26:26 -07001291 return;
1292 }
Eric Laurent854938a2011-02-22 12:05:20 -08001293 ScoClient client = getScoClient(cb, true);
Eric Laurent3def1ee2010-03-17 23:26:26 -07001294 client.incCount();
1295 }
1296
1297 /** @see AudioManager#stopBluetoothSco() */
1298 public void stopBluetoothSco(IBinder cb){
Eric Laurentdc03c612011-04-01 10:59:41 -07001299 if (!checkAudioSettingsPermission("stopBluetoothSco()") ||
1300 !mBootCompleted) {
Eric Laurent3def1ee2010-03-17 23:26:26 -07001301 return;
1302 }
Eric Laurent854938a2011-02-22 12:05:20 -08001303 ScoClient client = getScoClient(cb, false);
1304 if (client != null) {
1305 client.decCount();
1306 }
Eric Laurent3def1ee2010-03-17 23:26:26 -07001307 }
1308
1309 private class ScoClient implements IBinder.DeathRecipient {
1310 private IBinder mCb; // To be notified of client's death
Marco Nelissenf1ddd512011-08-10 14:15:44 -07001311 private int mCreatorPid;
Eric Laurent3def1ee2010-03-17 23:26:26 -07001312 private int mStartcount; // number of SCO connections started by this client
1313
1314 ScoClient(IBinder cb) {
1315 mCb = cb;
Marco Nelissenf1ddd512011-08-10 14:15:44 -07001316 mCreatorPid = Binder.getCallingPid();
Eric Laurent3def1ee2010-03-17 23:26:26 -07001317 mStartcount = 0;
1318 }
1319
1320 public void binderDied() {
1321 synchronized(mScoClients) {
1322 Log.w(TAG, "SCO client died");
1323 int index = mScoClients.indexOf(this);
1324 if (index < 0) {
1325 Log.w(TAG, "unregistered SCO client died");
1326 } else {
1327 clearCount(true);
1328 mScoClients.remove(this);
1329 }
1330 }
1331 }
1332
1333 public void incCount() {
1334 synchronized(mScoClients) {
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07001335 requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED);
Eric Laurent3def1ee2010-03-17 23:26:26 -07001336 if (mStartcount == 0) {
1337 try {
1338 mCb.linkToDeath(this, 0);
1339 } catch (RemoteException e) {
1340 // client has already died!
1341 Log.w(TAG, "ScoClient incCount() could not link to "+mCb+" binder death");
1342 }
1343 }
1344 mStartcount++;
1345 }
1346 }
1347
1348 public void decCount() {
1349 synchronized(mScoClients) {
1350 if (mStartcount == 0) {
1351 Log.w(TAG, "ScoClient.decCount() already 0");
1352 } else {
1353 mStartcount--;
1354 if (mStartcount == 0) {
Eric Laurente2dd8c42010-06-30 19:41:56 -07001355 try {
1356 mCb.unlinkToDeath(this, 0);
1357 } catch (NoSuchElementException e) {
1358 Log.w(TAG, "decCount() going to 0 but not registered to binder");
1359 }
Eric Laurent3def1ee2010-03-17 23:26:26 -07001360 }
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07001361 requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
Eric Laurent3def1ee2010-03-17 23:26:26 -07001362 }
1363 }
1364 }
1365
1366 public void clearCount(boolean stopSco) {
1367 synchronized(mScoClients) {
Eric Laurente2dd8c42010-06-30 19:41:56 -07001368 if (mStartcount != 0) {
1369 try {
1370 mCb.unlinkToDeath(this, 0);
1371 } catch (NoSuchElementException e) {
1372 Log.w(TAG, "clearCount() mStartcount: "+mStartcount+" != 0 but not registered to binder");
1373 }
1374 }
Eric Laurent3def1ee2010-03-17 23:26:26 -07001375 mStartcount = 0;
Eric Laurent3def1ee2010-03-17 23:26:26 -07001376 if (stopSco) {
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07001377 requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
Eric Laurent3def1ee2010-03-17 23:26:26 -07001378 }
1379 }
1380 }
1381
1382 public int getCount() {
1383 return mStartcount;
1384 }
1385
1386 public IBinder getBinder() {
1387 return mCb;
1388 }
1389
Eric Laurentd7454be2011-09-14 08:45:58 -07001390 public int getPid() {
1391 return mCreatorPid;
1392 }
1393
Eric Laurent3def1ee2010-03-17 23:26:26 -07001394 public int totalCount() {
1395 synchronized(mScoClients) {
1396 int count = 0;
1397 int size = mScoClients.size();
1398 for (int i = 0; i < size; i++) {
1399 count += mScoClients.get(i).getCount();
1400 }
1401 return count;
1402 }
1403 }
1404
1405 private void requestScoState(int state) {
Eric Laurent62ef7672010-11-24 10:58:32 -08001406 checkScoAudioState();
Eric Laurentdc03c612011-04-01 10:59:41 -07001407 if (totalCount() == 0) {
1408 if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
1409 // Make sure that the state transitions to CONNECTING even if we cannot initiate
1410 // the connection.
1411 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
1412 // Accept SCO audio activation only in NORMAL audio mode or if the mode is
Marco Nelissenf1ddd512011-08-10 14:15:44 -07001413 // currently controlled by the same client process.
Eric Laurent9f103de2011-09-08 15:04:23 -07001414 synchronized(mSetModeDeathHandlers) {
1415 if ((mSetModeDeathHandlers.isEmpty() ||
1416 mSetModeDeathHandlers.get(0).getPid() == mCreatorPid) &&
1417 (mScoAudioState == SCO_STATE_INACTIVE ||
1418 mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
1419 if (mScoAudioState == SCO_STATE_INACTIVE) {
1420 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
1421 if (mBluetoothHeadset.startScoUsingVirtualVoiceCall(
1422 mBluetoothHeadsetDevice)) {
1423 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
1424 } else {
1425 broadcastScoConnectionState(
1426 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1427 }
1428 } else if (getBluetoothHeadset()) {
1429 mScoAudioState = SCO_STATE_ACTIVATE_REQ;
Eric Laurentdc03c612011-04-01 10:59:41 -07001430 }
Eric Laurent9f103de2011-09-08 15:04:23 -07001431 } else {
1432 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
1433 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
Eric Laurentdc03c612011-04-01 10:59:41 -07001434 }
1435 } else {
Eric Laurent9f103de2011-09-08 15:04:23 -07001436 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
Eric Laurentdc03c612011-04-01 10:59:41 -07001437 }
Eric Laurentdc03c612011-04-01 10:59:41 -07001438 }
Eric Laurent62ef7672010-11-24 10:58:32 -08001439 } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED &&
Eric Laurentdc03c612011-04-01 10:59:41 -07001440 (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
1441 mScoAudioState == SCO_STATE_ACTIVATE_REQ)) {
1442 if (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) {
Marco Nelissen671db6f2011-09-06 16:29:12 -07001443 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
Eric Laurentdc03c612011-04-01 10:59:41 -07001444 if (!mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
1445 mBluetoothHeadsetDevice)) {
1446 mScoAudioState = SCO_STATE_INACTIVE;
1447 broadcastScoConnectionState(
1448 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1449 }
1450 } else if (getBluetoothHeadset()) {
1451 mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
1452 }
1453 } else {
1454 mScoAudioState = SCO_STATE_INACTIVE;
1455 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1456 }
Eric Laurent3def1ee2010-03-17 23:26:26 -07001457 }
1458 }
1459 }
1460 }
1461
Eric Laurent62ef7672010-11-24 10:58:32 -08001462 private void checkScoAudioState() {
1463 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null &&
Eric Laurentdc03c612011-04-01 10:59:41 -07001464 mScoAudioState == SCO_STATE_INACTIVE &&
Eric Laurent62ef7672010-11-24 10:58:32 -08001465 mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
1466 != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1467 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
1468 }
1469 }
1470
Eric Laurent854938a2011-02-22 12:05:20 -08001471 private ScoClient getScoClient(IBinder cb, boolean create) {
Eric Laurent3def1ee2010-03-17 23:26:26 -07001472 synchronized(mScoClients) {
Eric Laurent854938a2011-02-22 12:05:20 -08001473 ScoClient client = null;
Eric Laurent3def1ee2010-03-17 23:26:26 -07001474 int size = mScoClients.size();
1475 for (int i = 0; i < size; i++) {
1476 client = mScoClients.get(i);
1477 if (client.getBinder() == cb)
1478 return client;
1479 }
Eric Laurent854938a2011-02-22 12:05:20 -08001480 if (create) {
1481 client = new ScoClient(cb);
1482 mScoClients.add(client);
1483 }
Eric Laurent3def1ee2010-03-17 23:26:26 -07001484 return client;
1485 }
1486 }
1487
Eric Laurentd7454be2011-09-14 08:45:58 -07001488 public void clearAllScoClients(int exceptPid, boolean stopSco) {
Eric Laurent3def1ee2010-03-17 23:26:26 -07001489 synchronized(mScoClients) {
Eric Laurent854938a2011-02-22 12:05:20 -08001490 ScoClient savedClient = null;
Eric Laurent3def1ee2010-03-17 23:26:26 -07001491 int size = mScoClients.size();
1492 for (int i = 0; i < size; i++) {
Eric Laurent854938a2011-02-22 12:05:20 -08001493 ScoClient cl = mScoClients.get(i);
Eric Laurentd7454be2011-09-14 08:45:58 -07001494 if (cl.getPid() != exceptPid) {
Eric Laurent854938a2011-02-22 12:05:20 -08001495 cl.clearCount(stopSco);
1496 } else {
1497 savedClient = cl;
1498 }
1499 }
1500 mScoClients.clear();
1501 if (savedClient != null) {
1502 mScoClients.add(savedClient);
Eric Laurent3def1ee2010-03-17 23:26:26 -07001503 }
1504 }
1505 }
1506
Eric Laurentdc03c612011-04-01 10:59:41 -07001507 private boolean getBluetoothHeadset() {
1508 boolean result = false;
1509 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
1510 if (adapter != null) {
1511 result = adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
1512 BluetoothProfile.HEADSET);
1513 }
1514 // If we could not get a bluetooth headset proxy, send a failure message
1515 // without delay to reset the SCO audio state and clear SCO clients.
1516 // If we could get a proxy, send a delayed failure message that will reset our state
1517 // in case we don't receive onServiceConnected().
1518 sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0,
1519 SENDMSG_REPLACE, 0, 0, null, result ? BT_HEADSET_CNCT_TIMEOUT_MS : 0);
1520 return result;
1521 }
1522
Eric Laurentd7454be2011-09-14 08:45:58 -07001523 private void disconnectBluetoothSco(int exceptPid) {
Eric Laurentdc03c612011-04-01 10:59:41 -07001524 synchronized(mScoClients) {
1525 checkScoAudioState();
1526 if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL ||
1527 mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
1528 if (mBluetoothHeadsetDevice != null) {
1529 if (mBluetoothHeadset != null) {
1530 if (!mBluetoothHeadset.stopVoiceRecognition(
Eric Laurentb06ac832011-05-25 15:55:18 -07001531 mBluetoothHeadsetDevice)) {
Eric Laurentdc03c612011-04-01 10:59:41 -07001532 sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0,
1533 SENDMSG_REPLACE, 0, 0, null, 0);
1534 }
1535 } else if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL &&
1536 getBluetoothHeadset()) {
1537 mScoAudioState = SCO_STATE_DEACTIVATE_EXT_REQ;
1538 }
1539 }
1540 } else {
Eric Laurentd7454be2011-09-14 08:45:58 -07001541 clearAllScoClients(exceptPid, true);
Eric Laurentdc03c612011-04-01 10:59:41 -07001542 }
1543 }
1544 }
1545
1546 private void resetBluetoothSco() {
1547 synchronized(mScoClients) {
Eric Laurentd7454be2011-09-14 08:45:58 -07001548 clearAllScoClients(0, false);
Eric Laurentdc03c612011-04-01 10:59:41 -07001549 mScoAudioState = SCO_STATE_INACTIVE;
1550 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1551 }
1552 }
1553
1554 private void broadcastScoConnectionState(int state) {
1555 if (state != mScoConnectionState) {
1556 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
1557 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
1558 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE,
1559 mScoConnectionState);
1560 mContext.sendStickyBroadcast(newIntent);
1561 mScoConnectionState = state;
1562 }
1563 }
1564
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07001565 private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
1566 new BluetoothProfile.ServiceListener() {
1567 public void onServiceConnected(int profile, BluetoothProfile proxy) {
Eric Laurent62ef7672010-11-24 10:58:32 -08001568 synchronized (mScoClients) {
Eric Laurentdc03c612011-04-01 10:59:41 -07001569 // Discard timeout message
1570 mAudioHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
Eric Laurent62ef7672010-11-24 10:58:32 -08001571 mBluetoothHeadset = (BluetoothHeadset) proxy;
1572 List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
1573 if (deviceList.size() > 0) {
1574 mBluetoothHeadsetDevice = deviceList.get(0);
1575 } else {
1576 mBluetoothHeadsetDevice = null;
1577 }
Eric Laurentdc03c612011-04-01 10:59:41 -07001578 // Refresh SCO audio state
1579 checkScoAudioState();
1580 // Continue pending action if any
1581 if (mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
1582 mScoAudioState == SCO_STATE_DEACTIVATE_REQ ||
1583 mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
1584 boolean status = false;
1585 if (mBluetoothHeadsetDevice != null) {
1586 switch (mScoAudioState) {
1587 case SCO_STATE_ACTIVATE_REQ:
1588 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
1589 status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
1590 mBluetoothHeadsetDevice);
1591 break;
1592 case SCO_STATE_DEACTIVATE_REQ:
1593 status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
1594 mBluetoothHeadsetDevice);
1595 break;
1596 case SCO_STATE_DEACTIVATE_EXT_REQ:
1597 status = mBluetoothHeadset.stopVoiceRecognition(
Eric Laurentdc03c612011-04-01 10:59:41 -07001598 mBluetoothHeadsetDevice);
1599 }
1600 }
1601 if (!status) {
1602 sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0,
1603 SENDMSG_REPLACE, 0, 0, null, 0);
1604 }
1605 }
Eric Laurent3def1ee2010-03-17 23:26:26 -07001606 }
1607 }
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07001608 public void onServiceDisconnected(int profile) {
Eric Laurent62ef7672010-11-24 10:58:32 -08001609 synchronized (mScoClients) {
Eric Laurentdc03c612011-04-01 10:59:41 -07001610 mBluetoothHeadset = null;
Eric Laurent3def1ee2010-03-17 23:26:26 -07001611 }
1612 }
1613 };
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08001614
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001615 ///////////////////////////////////////////////////////////////////////////
1616 // Internal methods
1617 ///////////////////////////////////////////////////////////////////////////
1618
1619 /**
1620 * Checks if the adjustment should change ringer mode instead of just
1621 * adjusting volume. If so, this will set the proper ringer mode and volume
1622 * indices on the stream states.
1623 */
Eric Laurentb024c302011-10-14 17:19:27 -07001624 private boolean checkForRingerModeChange(int oldIndex, int direction, int streamType) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001625 boolean adjustVolumeIndex = true;
1626 int newRingerMode = mRingerMode;
Eric Laurent3d4c06f2011-08-15 19:58:28 -07001627 int uiIndex = (oldIndex + 5) / 10;
Amith Yamasanic696a532011-10-28 17:02:37 -07001628 boolean vibeInSilent = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001629
Amith Yamasanic696a532011-10-28 17:02:37 -07001630 if (mRingerMode == RINGER_MODE_NORMAL) {
Eric Laurent3d4c06f2011-08-15 19:58:28 -07001631 if ((direction == AudioManager.ADJUST_LOWER) && (uiIndex <= 1)) {
1632 // enter silent mode if current index is the last audible one and not repeating a
1633 // volume key down
Amith Yamasanic696a532011-10-28 17:02:37 -07001634 if (vibeInSilent || mPrevVolDirection != AudioManager.ADJUST_LOWER) {
Eric Laurent3d4c06f2011-08-15 19:58:28 -07001635 // "silent mode", but which one?
Amith Yamasanic696a532011-10-28 17:02:37 -07001636 newRingerMode = vibeInSilent ? RINGER_MODE_VIBRATE : RINGER_MODE_SILENT;
Eric Laurent3d4c06f2011-08-15 19:58:28 -07001637 }
Eric Laurentb024c302011-10-14 17:19:27 -07001638 if (uiIndex == 0 || (mPrevVolDirection == AudioManager.ADJUST_LOWER &&
1639 mVoiceCapable && streamType == AudioSystem.STREAM_RING)) {
Eric Laurent3d4c06f2011-08-15 19:58:28 -07001640 adjustVolumeIndex = false;
1641 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001642 }
Amith Yamasanic696a532011-10-28 17:02:37 -07001643 } else if (mRingerMode == RINGER_MODE_VIBRATE) {
1644 if ((direction == AudioManager.ADJUST_LOWER)) {
1645 // Set it to silent, if it wasn't a long-press
1646 if (mPrevVolDirection != AudioManager.ADJUST_LOWER) {
1647 newRingerMode = RINGER_MODE_SILENT;
1648 }
1649 } else if (direction == AudioManager.ADJUST_RAISE) {
1650 newRingerMode = RINGER_MODE_NORMAL;
1651 }
1652 adjustVolumeIndex = false;
Daniel Sandler6329bf72010-02-26 15:17:44 -05001653 } else {
1654 if (direction == AudioManager.ADJUST_RAISE) {
1655 // exiting silent mode
Amith Yamasanic696a532011-10-28 17:02:37 -07001656 // If VIBRATE_IN_SILENT, then go into vibrate mode
1657 newRingerMode = vibeInSilent ? RINGER_MODE_VIBRATE : RINGER_MODE_NORMAL;
Daniel Sandler6329bf72010-02-26 15:17:44 -05001658 }
Eric Laurentb024c302011-10-14 17:19:27 -07001659 adjustVolumeIndex = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001660 }
1661
1662 if (newRingerMode != mRingerMode) {
1663 setRingerMode(newRingerMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001664 }
1665
Eric Laurent25101b02011-02-02 09:33:30 -08001666 mPrevVolDirection = direction;
1667
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001668 return adjustVolumeIndex;
1669 }
1670
1671 public boolean isStreamAffectedByRingerMode(int streamType) {
Eric Laurent9bcf4012009-06-12 06:09:28 -07001672 return (mRingerModeAffectedStreams & (1 << streamType)) != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001673 }
1674
Eric Laurent5b4e6542010-03-19 20:02:21 -07001675 private boolean isStreamMutedByRingerMode(int streamType) {
1676 return (mRingerModeMutedStreams & (1 << streamType)) != 0;
1677 }
1678
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001679 public boolean isStreamAffectedByMute(int streamType) {
1680 return (mMuteAffectedStreams & (1 << streamType)) != 0;
1681 }
1682
1683 private void ensureValidDirection(int direction) {
1684 if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) {
1685 throw new IllegalArgumentException("Bad direction " + direction);
1686 }
1687 }
1688
1689 private void ensureValidStreamType(int streamType) {
1690 if (streamType < 0 || streamType >= mStreamStates.length) {
1691 throw new IllegalArgumentException("Bad stream type " + streamType);
1692 }
1693 }
1694
1695 private int getActiveStreamType(int suggestedStreamType) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001696
Eric Laurent25101b02011-02-02 09:33:30 -08001697 if (mVoiceCapable) {
1698 boolean isOffhook = false;
1699 try {
1700 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1701 if (phone != null) isOffhook = phone.isOffhook();
1702 } catch (RemoteException e) {
1703 Log.w(TAG, "Couldn't connect to phone service", e);
1704 }
1705
1706 if (isOffhook || getMode() == AudioManager.MODE_IN_COMMUNICATION) {
1707 if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
1708 == AudioSystem.FORCE_BT_SCO) {
1709 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
1710 return AudioSystem.STREAM_BLUETOOTH_SCO;
1711 } else {
1712 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
1713 return AudioSystem.STREAM_VOICE_CALL;
1714 }
1715 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
1716 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC...");
1717 return AudioSystem.STREAM_MUSIC;
1718 } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
Joe Onoratoc7fcba42011-01-05 16:53:11 -08001719 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING..."
1720 // + " b/c USE_DEFAULT_STREAM_TYPE...");
1721 return AudioSystem.STREAM_RING;
1722 } else {
Eric Laurent25101b02011-02-02 09:33:30 -08001723 // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType);
1724 return suggestedStreamType;
1725 }
1726 } else {
1727 if (getMode() == AudioManager.MODE_IN_COMMUNICATION) {
1728 if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
1729 == AudioSystem.FORCE_BT_SCO) {
1730 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
1731 return AudioSystem.STREAM_BLUETOOTH_SCO;
1732 } else {
1733 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
1734 return AudioSystem.STREAM_VOICE_CALL;
1735 }
1736 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_NOTIFICATION,
1737 NOTIFICATION_VOLUME_DELAY_MS) ||
1738 AudioSystem.isStreamActive(AudioSystem.STREAM_RING,
1739 NOTIFICATION_VOLUME_DELAY_MS)) {
1740 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION...");
1741 return AudioSystem.STREAM_NOTIFICATION;
1742 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0) ||
1743 (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE)) {
Joe Onoratoc7fcba42011-01-05 16:53:11 -08001744 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC "
1745 // + " b/c USE_DEFAULT_STREAM_TYPE...");
1746 return AudioSystem.STREAM_MUSIC;
Eric Laurent25101b02011-02-02 09:33:30 -08001747 } else {
1748 // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType);
1749 return suggestedStreamType;
Joe Onoratoc7fcba42011-01-05 16:53:11 -08001750 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001751 }
1752 }
1753
1754 private void broadcastRingerMode() {
1755 // Send sticky broadcast
Dianne Hackborn1c633fc2009-12-08 19:45:14 -08001756 Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION);
1757 broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, mRingerMode);
1758 broadcast.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1759 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
1760 long origCallerIdentityToken = Binder.clearCallingIdentity();
1761 mContext.sendStickyBroadcast(broadcast);
1762 Binder.restoreCallingIdentity(origCallerIdentityToken);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001763 }
1764
1765 private void broadcastVibrateSetting(int vibrateType) {
1766 // Send broadcast
1767 if (ActivityManagerNative.isSystemReady()) {
1768 Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
1769 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType);
1770 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType));
1771 mContext.sendBroadcast(broadcast);
1772 }
1773 }
1774
1775 // Message helper methods
1776 private static int getMsg(int baseMsg, int streamType) {
1777 return (baseMsg & 0xffff) | streamType << 16;
1778 }
1779
1780 private static int getMsgBase(int msg) {
1781 return msg & 0xffff;
1782 }
1783
1784 private static void sendMsg(Handler handler, int baseMsg, int streamType,
1785 int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {
1786 int msg = (streamType == SHARED_MSG) ? baseMsg : getMsg(baseMsg, streamType);
1787
1788 if (existingMsgPolicy == SENDMSG_REPLACE) {
1789 handler.removeMessages(msg);
1790 } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
1791 return;
1792 }
1793
1794 handler
1795 .sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
1796 }
1797
1798 boolean checkAudioSettingsPermission(String method) {
1799 if (mContext.checkCallingOrSelfPermission("android.permission.MODIFY_AUDIO_SETTINGS")
1800 == PackageManager.PERMISSION_GRANTED) {
1801 return true;
1802 }
1803 String msg = "Audio Settings Permission Denial: " + method + " from pid="
1804 + Binder.getCallingPid()
1805 + ", uid=" + Binder.getCallingUid();
1806 Log.w(TAG, msg);
1807 return false;
1808 }
1809
1810
1811 ///////////////////////////////////////////////////////////////////////////
1812 // Inner classes
1813 ///////////////////////////////////////////////////////////////////////////
1814
1815 public class VolumeStreamState {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001816 private final int mStreamType;
1817
Jean-Michel Trivi11a74a72009-10-27 17:39:30 -07001818 private String mVolumeIndexSettingName;
1819 private String mLastAudibleVolumeIndexSettingName;
Eric Laurenta553c252009-07-17 12:17:14 -07001820 private int mIndexMax;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001821 private int mIndex;
1822 private int mLastAudibleIndex;
1823 private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo requests client death
1824
Eric Laurenta553c252009-07-17 12:17:14 -07001825 private VolumeStreamState(String settingName, int streamType) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001826
Jean-Michel Trivi11a74a72009-10-27 17:39:30 -07001827 setVolumeIndexSettingName(settingName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001828
1829 mStreamType = streamType;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001830
1831 final ContentResolver cr = mContentResolver;
Jared Suttles59820132009-08-13 21:50:52 -05001832 mIndexMax = MAX_STREAM_VOLUME[streamType];
Eric Laurenta553c252009-07-17 12:17:14 -07001833 mIndex = Settings.System.getInt(cr,
1834 mVolumeIndexSettingName,
1835 AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
1836 mLastAudibleIndex = Settings.System.getInt(cr,
1837 mLastAudibleVolumeIndexSettingName,
1838 (mIndex > 0) ? mIndex : AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
1839 AudioSystem.initStreamVolume(streamType, 0, mIndexMax);
1840 mIndexMax *= 10;
1841 mIndex = getValidIndex(10 * mIndex);
1842 mLastAudibleIndex = getValidIndex(10 * mLastAudibleIndex);
1843 setStreamVolumeIndex(streamType, mIndex);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001844 mDeathHandlers = new ArrayList<VolumeDeathHandler>();
1845 }
1846
Jean-Michel Trivi11a74a72009-10-27 17:39:30 -07001847 public void setVolumeIndexSettingName(String settingName) {
1848 mVolumeIndexSettingName = settingName;
1849 mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
1850 }
1851
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001852 public boolean adjustIndex(int deltaIndex) {
Eric Laurenta553c252009-07-17 12:17:14 -07001853 return setIndex(mIndex + deltaIndex * 10, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001854 }
1855
Eric Laurent9bcf4012009-06-12 06:09:28 -07001856 public boolean setIndex(int index, boolean lastAudible) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001857 int oldIndex = mIndex;
1858 mIndex = getValidIndex(index);
1859
1860 if (oldIndex != mIndex) {
Eric Laurent9bcf4012009-06-12 06:09:28 -07001861 if (lastAudible) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001862 mLastAudibleIndex = mIndex;
1863 }
Eric Laurenta553c252009-07-17 12:17:14 -07001864 // Apply change to all streams using this one as alias
1865 int numStreamTypes = AudioSystem.getNumStreamTypes();
1866 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
1867 if (streamType != mStreamType && STREAM_VOLUME_ALIAS[streamType] == mStreamType) {
1868 mStreamStates[streamType].setIndex(rescaleIndex(mIndex, mStreamType, streamType), lastAudible);
1869 }
1870 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001871 return true;
1872 } else {
1873 return false;
1874 }
1875 }
1876
Eric Laurent5b4e6542010-03-19 20:02:21 -07001877 public void setLastAudibleIndex(int index) {
1878 mLastAudibleIndex = getValidIndex(index);
1879 }
1880
1881 public void adjustLastAudibleIndex(int deltaIndex) {
1882 setLastAudibleIndex(mLastAudibleIndex + deltaIndex * 10);
1883 }
1884
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001885 public int getMaxIndex() {
Eric Laurenta553c252009-07-17 12:17:14 -07001886 return mIndexMax;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001887 }
1888
1889 public void mute(IBinder cb, boolean state) {
1890 VolumeDeathHandler handler = getDeathHandler(cb, state);
1891 if (handler == null) {
1892 Log.e(TAG, "Could not get client death handler for stream: "+mStreamType);
1893 return;
1894 }
1895 handler.mute(state);
1896 }
1897
1898 private int getValidIndex(int index) {
1899 if (index < 0) {
1900 return 0;
Eric Laurenta553c252009-07-17 12:17:14 -07001901 } else if (index > mIndexMax) {
1902 return mIndexMax;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001903 }
1904
1905 return index;
1906 }
1907
1908 private class VolumeDeathHandler implements IBinder.DeathRecipient {
1909 private IBinder mICallback; // To be notified of client's death
1910 private int mMuteCount; // Number of active mutes for this client
1911
1912 VolumeDeathHandler(IBinder cb) {
1913 mICallback = cb;
1914 }
1915
1916 public void mute(boolean state) {
1917 synchronized(mDeathHandlers) {
1918 if (state) {
1919 if (mMuteCount == 0) {
1920 // Register for client death notification
1921 try {
Eric Laurent5b4e6542010-03-19 20:02:21 -07001922 // mICallback can be 0 if muted by AudioService
1923 if (mICallback != null) {
1924 mICallback.linkToDeath(this, 0);
1925 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001926 mDeathHandlers.add(this);
1927 // If the stream is not yet muted by any client, set lvel to 0
1928 if (muteCount() == 0) {
Eric Laurent9bcf4012009-06-12 06:09:28 -07001929 setIndex(0, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001930 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0,
1931 VolumeStreamState.this, 0);
1932 }
1933 } catch (RemoteException e) {
1934 // Client has died!
1935 binderDied();
1936 mDeathHandlers.notify();
1937 return;
1938 }
1939 } else {
1940 Log.w(TAG, "stream: "+mStreamType+" was already muted by this client");
1941 }
1942 mMuteCount++;
1943 } else {
1944 if (mMuteCount == 0) {
1945 Log.e(TAG, "unexpected unmute for stream: "+mStreamType);
1946 } else {
1947 mMuteCount--;
1948 if (mMuteCount == 0) {
1949 // Unregistr from client death notification
1950 mDeathHandlers.remove(this);
Eric Laurent5b4e6542010-03-19 20:02:21 -07001951 // mICallback can be 0 if muted by AudioService
1952 if (mICallback != null) {
1953 mICallback.unlinkToDeath(this, 0);
1954 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001955 if (muteCount() == 0) {
Eric Laurent5b4e6542010-03-19 20:02:21 -07001956 // If the stream is not muted any more, restore it's volume if
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001957 // ringer mode allows it
1958 if (!isStreamAffectedByRingerMode(mStreamType) || mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
Eric Laurent9bcf4012009-06-12 06:09:28 -07001959 setIndex(mLastAudibleIndex, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001960 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0,
1961 VolumeStreamState.this, 0);
1962 }
1963 }
1964 }
1965 }
1966 }
1967 mDeathHandlers.notify();
1968 }
1969 }
1970
1971 public void binderDied() {
1972 Log.w(TAG, "Volume service client died for stream: "+mStreamType);
1973 if (mMuteCount != 0) {
1974 // Reset all active mute requests from this client.
1975 mMuteCount = 1;
1976 mute(false);
1977 }
1978 }
1979 }
1980
1981 private int muteCount() {
1982 int count = 0;
1983 int size = mDeathHandlers.size();
1984 for (int i = 0; i < size; i++) {
1985 count += mDeathHandlers.get(i).mMuteCount;
1986 }
1987 return count;
1988 }
1989
1990 private VolumeDeathHandler getDeathHandler(IBinder cb, boolean state) {
1991 synchronized(mDeathHandlers) {
1992 VolumeDeathHandler handler;
1993 int size = mDeathHandlers.size();
1994 for (int i = 0; i < size; i++) {
1995 handler = mDeathHandlers.get(i);
Eric Laurent5b4e6542010-03-19 20:02:21 -07001996 if (cb == handler.mICallback) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001997 return handler;
1998 }
1999 }
2000 // If this is the first mute request for this client, create a new
2001 // client death handler. Otherwise, it is an out of sequence unmute request.
2002 if (state) {
2003 handler = new VolumeDeathHandler(cb);
2004 } else {
2005 Log.w(TAG, "stream was not muted by this client");
2006 handler = null;
2007 }
2008 return handler;
2009 }
2010 }
2011 }
2012
2013 /** Thread that handles native AudioSystem control. */
2014 private class AudioSystemThread extends Thread {
2015 AudioSystemThread() {
2016 super("AudioService");
2017 }
2018
2019 @Override
2020 public void run() {
2021 // Set this thread up so the handler will work on it
2022 Looper.prepare();
2023
2024 synchronized(AudioService.this) {
2025 mAudioHandler = new AudioHandler();
2026
2027 // Notify that the handler has been created
2028 AudioService.this.notify();
2029 }
2030
2031 // Listen for volume change requests that are set by VolumePanel
2032 Looper.loop();
2033 }
2034 }
2035
2036 /** Handles internal volume messages in separate volume thread. */
2037 private class AudioHandler extends Handler {
2038
2039 private void setSystemVolume(VolumeStreamState streamState) {
2040
2041 // Adjust volume
Eric Laurenta553c252009-07-17 12:17:14 -07002042 setStreamVolumeIndex(streamState.mStreamType, streamState.mIndex);
2043
2044 // Apply change to all streams using this one as alias
2045 int numStreamTypes = AudioSystem.getNumStreamTypes();
2046 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
2047 if (streamType != streamState.mStreamType &&
2048 STREAM_VOLUME_ALIAS[streamType] == streamState.mStreamType) {
2049 setStreamVolumeIndex(streamType, mStreamStates[streamType].mIndex);
2050 }
2051 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002052
2053 // Post a persist volume msg
2054 sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamState.mStreamType,
Eric Laurent31951ca2010-03-02 18:54:45 -08002055 SENDMSG_REPLACE, 1, 1, streamState, PERSIST_DELAY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002056 }
2057
Eric Laurent31951ca2010-03-02 18:54:45 -08002058 private void persistVolume(VolumeStreamState streamState, boolean current, boolean lastAudible) {
2059 if (current) {
2060 System.putInt(mContentResolver, streamState.mVolumeIndexSettingName,
2061 (streamState.mIndex + 5)/ 10);
2062 }
2063 if (lastAudible) {
2064 System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName,
Eric Laurenta553c252009-07-17 12:17:14 -07002065 (streamState.mLastAudibleIndex + 5) / 10);
Eric Laurent31951ca2010-03-02 18:54:45 -08002066 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002067 }
2068
2069 private void persistRingerMode() {
2070 System.putInt(mContentResolver, System.MODE_RINGER, mRingerMode);
2071 }
2072
2073 private void persistVibrateSetting() {
2074 System.putInt(mContentResolver, System.VIBRATE_ON, mVibrateSetting);
2075 }
2076
2077 private void playSoundEffect(int effectType, int volume) {
2078 synchronized (mSoundEffectsLock) {
2079 if (mSoundPool == null) {
2080 return;
2081 }
Eric Laurenta2ef57d2009-09-28 04:46:10 -07002082 float volFloat;
Eric Laurent25101b02011-02-02 09:33:30 -08002083 // use default if volume is not specified by caller
Eric Laurenta2ef57d2009-09-28 04:46:10 -07002084 if (volume < 0) {
Eric Laurent25101b02011-02-02 09:33:30 -08002085 volFloat = (float)Math.pow(10, SOUND_EFFECT_VOLUME_DB/20);
Eric Laurenta2ef57d2009-09-28 04:46:10 -07002086 } else {
2087 volFloat = (float) volume / 1000.0f;
2088 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002089
2090 if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) {
Eric Laurenta2ef57d2009-09-28 04:46:10 -07002091 mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002092 } else {
2093 MediaPlayer mediaPlayer = new MediaPlayer();
2094 if (mediaPlayer != null) {
2095 try {
2096 String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]];
2097 mediaPlayer.setDataSource(filePath);
2098 mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM);
2099 mediaPlayer.prepare();
Eric Laurenta2ef57d2009-09-28 04:46:10 -07002100 mediaPlayer.setVolume(volFloat, volFloat);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002101 mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
2102 public void onCompletion(MediaPlayer mp) {
2103 cleanupPlayer(mp);
2104 }
2105 });
2106 mediaPlayer.setOnErrorListener(new OnErrorListener() {
2107 public boolean onError(MediaPlayer mp, int what, int extra) {
2108 cleanupPlayer(mp);
2109 return true;
2110 }
2111 });
2112 mediaPlayer.start();
2113 } catch (IOException ex) {
2114 Log.w(TAG, "MediaPlayer IOException: "+ex);
2115 } catch (IllegalArgumentException ex) {
2116 Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex);
2117 } catch (IllegalStateException ex) {
2118 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
2119 }
2120 }
2121 }
2122 }
2123 }
2124
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07002125 private void onHandlePersistMediaButtonReceiver(ComponentName receiver) {
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07002126 Settings.System.putString(mContentResolver, Settings.System.MEDIA_BUTTON_RECEIVER,
2127 receiver == null ? "" : receiver.flattenToString());
2128 }
2129
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002130 private void cleanupPlayer(MediaPlayer mp) {
2131 if (mp != null) {
2132 try {
2133 mp.stop();
2134 mp.release();
2135 } catch (IllegalStateException ex) {
2136 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
2137 }
2138 }
2139 }
2140
Eric Laurentfa640152011-03-12 15:59:51 -08002141 private void setForceUse(int usage, int config) {
2142 AudioSystem.setForceUse(usage, config);
2143 }
2144
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002145 @Override
2146 public void handleMessage(Message msg) {
2147 int baseMsgWhat = getMsgBase(msg.what);
2148
2149 switch (baseMsgWhat) {
2150
2151 case MSG_SET_SYSTEM_VOLUME:
2152 setSystemVolume((VolumeStreamState) msg.obj);
2153 break;
2154
2155 case MSG_PERSIST_VOLUME:
Eric Laurent31951ca2010-03-02 18:54:45 -08002156 persistVolume((VolumeStreamState) msg.obj, (msg.arg1 != 0), (msg.arg2 != 0));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002157 break;
2158
2159 case MSG_PERSIST_RINGER_MODE:
2160 persistRingerMode();
2161 break;
2162
2163 case MSG_PERSIST_VIBRATE_SETTING:
2164 persistVibrateSetting();
2165 break;
2166
2167 case MSG_MEDIA_SERVER_DIED:
Eric Laurent89e74ba2009-09-30 18:26:36 -07002168 if (!mMediaServerOk) {
2169 Log.e(TAG, "Media server died.");
Eric Laurenta25c5d82011-02-10 11:04:34 -08002170 // Force creation of new IAudioFlinger interface so that we are notified
2171 // when new media_server process is back to life.
2172 AudioSystem.setErrorCallback(mAudioSystemCallback);
Eric Laurent89e74ba2009-09-30 18:26:36 -07002173 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
2174 null, 500);
2175 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002176 break;
2177
2178 case MSG_MEDIA_SERVER_STARTED:
2179 Log.e(TAG, "Media server started.");
Eric Laurent3c652ca2010-06-21 20:46:26 -07002180 // indicate to audio HAL that we start the reconfiguration phase after a media
2181 // server crash
2182 // Note that MSG_MEDIA_SERVER_STARTED message is only received when the media server
2183 // process restarts after a crash, not the first time it is started.
2184 AudioSystem.setParameters("restarting=true");
2185
Eric Laurentc42ac9d2009-07-29 08:53:03 -07002186 // Restore device connection states
2187 Set set = mConnectedDevices.entrySet();
2188 Iterator i = set.iterator();
2189 while(i.hasNext()){
2190 Map.Entry device = (Map.Entry)i.next();
2191 AudioSystem.setDeviceConnectionState(((Integer)device.getKey()).intValue(),
2192 AudioSystem.DEVICE_STATE_AVAILABLE,
2193 (String)device.getValue());
2194 }
2195
2196 // Restore call state
2197 AudioSystem.setPhoneState(mMode);
2198
Eric Laurentd5603c12009-08-06 08:49:39 -07002199 // Restore forced usage for communcations and record
Eric Laurentc42ac9d2009-07-29 08:53:03 -07002200 AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm);
Eric Laurentd5603c12009-08-06 08:49:39 -07002201 AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm);
Eric Laurentc42ac9d2009-07-29 08:53:03 -07002202
Eric Laurenta553c252009-07-17 12:17:14 -07002203 // Restore stream volumes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002204 int numStreamTypes = AudioSystem.getNumStreamTypes();
2205 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
Eric Laurenta553c252009-07-17 12:17:14 -07002206 int index;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002207 VolumeStreamState streamState = mStreamStates[streamType];
Eric Laurentc42ac9d2009-07-29 08:53:03 -07002208 AudioSystem.initStreamVolume(streamType, 0, (streamState.mIndexMax + 5) / 10);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002209 if (streamState.muteCount() == 0) {
Eric Laurenta553c252009-07-17 12:17:14 -07002210 index = streamState.mIndex;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002211 } else {
Eric Laurenta553c252009-07-17 12:17:14 -07002212 index = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002213 }
Eric Laurenta553c252009-07-17 12:17:14 -07002214 setStreamVolumeIndex(streamType, index);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002215 }
Eric Laurentc42ac9d2009-07-29 08:53:03 -07002216
2217 // Restore ringer mode
2218 setRingerModeInt(getRingerMode(), false);
Eric Laurent3c652ca2010-06-21 20:46:26 -07002219
2220 // indicate the end of reconfiguration phase to audio HAL
2221 AudioSystem.setParameters("restarting=false");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002222 break;
2223
Eric Laurent117b7bb2011-01-16 17:07:27 -08002224 case MSG_LOAD_SOUND_EFFECTS:
2225 loadSoundEffects();
2226 break;
2227
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002228 case MSG_PLAY_SOUND_EFFECT:
2229 playSoundEffect(msg.arg1, msg.arg2);
2230 break;
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07002231
2232 case MSG_BTA2DP_DOCK_TIMEOUT:
2233 // msg.obj == address of BTA2DP device
2234 makeA2dpDeviceUnavailableNow( (String) msg.obj );
2235 break;
Eric Laurentfa640152011-03-12 15:59:51 -08002236
2237 case MSG_SET_FORCE_USE:
2238 setForceUse(msg.arg1, msg.arg2);
2239 break;
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07002240
2241 case MSG_PERSIST_MEDIABUTTONRECEIVER:
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07002242 onHandlePersistMediaButtonReceiver( (ComponentName) msg.obj );
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07002243 break;
Eric Laurentdc03c612011-04-01 10:59:41 -07002244
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07002245 case MSG_RCDISPLAY_CLEAR:
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07002246 onRcDisplayClear();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07002247 break;
2248
2249 case MSG_RCDISPLAY_UPDATE:
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07002250 // msg.obj is guaranteed to be non null
2251 onRcDisplayUpdate( (RemoteControlStackEntry) msg.obj, msg.arg1);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07002252 break;
2253
Eric Laurentdc03c612011-04-01 10:59:41 -07002254 case MSG_BT_HEADSET_CNCT_FAILED:
2255 resetBluetoothSco();
2256 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002257 }
2258 }
2259 }
2260
Jason Parekhb1096152009-03-24 17:48:25 -07002261 private class SettingsObserver extends ContentObserver {
Eric Laurenta553c252009-07-17 12:17:14 -07002262
Jason Parekhb1096152009-03-24 17:48:25 -07002263 SettingsObserver() {
2264 super(new Handler());
2265 mContentResolver.registerContentObserver(Settings.System.getUriFor(
2266 Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this);
2267 }
2268
2269 @Override
2270 public void onChange(boolean selfChange) {
2271 super.onChange(selfChange);
Eric Laurenta553c252009-07-17 12:17:14 -07002272 synchronized (mSettingsLock) {
2273 int ringerModeAffectedStreams = Settings.System.getInt(mContentResolver,
Eric Laurentc1d41662011-07-19 11:21:13 -07002274 Settings.System.MODE_RINGER_STREAMS_AFFECTED,
2275 ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
2276 (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)));
2277 if (mVoiceCapable) {
2278 ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
2279 } else {
Eric Laurent402f7f22011-02-04 12:30:32 -08002280 ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
2281 }
Eric Laurenta553c252009-07-17 12:17:14 -07002282 if (ringerModeAffectedStreams != mRingerModeAffectedStreams) {
2283 /*
2284 * Ensure all stream types that should be affected by ringer mode
2285 * are in the proper state.
2286 */
2287 mRingerModeAffectedStreams = ringerModeAffectedStreams;
2288 setRingerModeInt(getRingerMode(), false);
2289 }
Eric Laurenta553c252009-07-17 12:17:14 -07002290 }
Jason Parekhb1096152009-03-24 17:48:25 -07002291 }
Jason Parekhb1096152009-03-24 17:48:25 -07002292 }
Eric Laurenta553c252009-07-17 12:17:14 -07002293
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07002294 private void makeA2dpDeviceAvailable(String address) {
2295 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
2296 AudioSystem.DEVICE_STATE_AVAILABLE,
2297 address);
2298 // Reset A2DP suspend state each time a new sink is connected
2299 AudioSystem.setParameters("A2dpSuspended=false");
2300 mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP),
2301 address);
2302 }
2303
2304 private void makeA2dpDeviceUnavailableNow(String address) {
2305 Intent noisyIntent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
2306 mContext.sendBroadcast(noisyIntent);
2307 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
2308 AudioSystem.DEVICE_STATE_UNAVAILABLE,
2309 address);
2310 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
2311 }
2312
2313 private void makeA2dpDeviceUnavailableLater(String address) {
Eric Laurent3b591262010-04-20 07:01:00 -07002314 // prevent any activity on the A2DP audio output to avoid unwanted
2315 // reconnection of the sink.
2316 AudioSystem.setParameters("A2dpSuspended=true");
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07002317 // the device will be made unavailable later, so consider it disconnected right away
2318 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
2319 // send the delayed message to make the device unavailable later
2320 Message msg = mAudioHandler.obtainMessage(MSG_BTA2DP_DOCK_TIMEOUT, address);
2321 mAudioHandler.sendMessageDelayed(msg, BTA2DP_DOCK_TIMEOUT_MILLIS);
2322
2323 }
2324
Jean-Michel Trivia847ba42010-04-23 11:49:29 -07002325 private void cancelA2dpDeviceTimeout() {
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07002326 mAudioHandler.removeMessages(MSG_BTA2DP_DOCK_TIMEOUT);
2327 }
2328
Jean-Michel Trivia847ba42010-04-23 11:49:29 -07002329 private boolean hasScheduledA2dpDockTimeout() {
2330 return mAudioHandler.hasMessages(MSG_BTA2DP_DOCK_TIMEOUT);
2331 }
2332
2333 /* cache of the address of the last dock the device was connected to */
2334 private String mDockAddress;
2335
Eric Laurenta553c252009-07-17 12:17:14 -07002336 /**
2337 * Receiver for misc intent broadcasts the Phone app cares about.
2338 */
2339 private class AudioServiceBroadcastReceiver extends BroadcastReceiver {
2340 @Override
2341 public void onReceive(Context context, Intent intent) {
2342 String action = intent.getAction();
2343
Jean-Michel Trivi758559e2010-03-09 09:26:08 -08002344 if (action.equals(Intent.ACTION_DOCK_EVENT)) {
2345 int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
2346 Intent.EXTRA_DOCK_STATE_UNDOCKED);
2347 int config;
2348 switch (dockState) {
2349 case Intent.EXTRA_DOCK_STATE_DESK:
2350 config = AudioSystem.FORCE_BT_DESK_DOCK;
2351 break;
2352 case Intent.EXTRA_DOCK_STATE_CAR:
2353 config = AudioSystem.FORCE_BT_CAR_DOCK;
2354 break;
Praveen Bharathi21e941b2010-10-06 15:23:14 -05002355 case Intent.EXTRA_DOCK_STATE_LE_DESK:
2356 config = AudioSystem.FORCE_ANALOG_DOCK;
2357 break;
2358 case Intent.EXTRA_DOCK_STATE_HE_DESK:
2359 config = AudioSystem.FORCE_DIGITAL_DOCK;
2360 break;
Jean-Michel Trivi758559e2010-03-09 09:26:08 -08002361 case Intent.EXTRA_DOCK_STATE_UNDOCKED:
2362 default:
2363 config = AudioSystem.FORCE_NONE;
2364 }
2365 AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07002366 } else if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
2367 int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
2368 BluetoothProfile.STATE_DISCONNECTED);
Nick Pelly005b2282009-09-10 10:21:56 -07002369 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Eric Laurentdca56b92011-09-02 14:20:56 -07002370 if (btDevice == null) {
2371 return;
2372 }
Nick Pellybd022f42009-08-14 18:33:38 -07002373 String address = btDevice.getAddress();
Eric Laurentdca56b92011-09-02 14:20:56 -07002374 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
2375 address = "";
2376 }
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07002377 boolean isConnected =
2378 (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
2379 mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP).equals(address));
Eric Laurentd5603c12009-08-06 08:49:39 -07002380
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07002381 if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07002382 if (btDevice.isBluetoothDock()) {
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07002383 if (state == BluetoothProfile.STATE_DISCONNECTED) {
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07002384 // introduction of a delay for transient disconnections of docks when
2385 // power is rapidly turned off/on, this message will be canceled if
2386 // we reconnect the dock under a preset delay
2387 makeA2dpDeviceUnavailableLater(address);
2388 // the next time isConnected is evaluated, it will be false for the dock
2389 }
2390 } else {
2391 makeA2dpDeviceUnavailableNow(address);
2392 }
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07002393 } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07002394 if (btDevice.isBluetoothDock()) {
2395 // this could be a reconnection after a transient disconnection
Jean-Michel Trivia847ba42010-04-23 11:49:29 -07002396 cancelA2dpDeviceTimeout();
2397 mDockAddress = address;
2398 } else {
2399 // this could be a connection of another A2DP device before the timeout of
2400 // a dock: cancel the dock timeout, and make the dock unavailable now
2401 if(hasScheduledA2dpDockTimeout()) {
2402 cancelA2dpDeviceTimeout();
2403 makeA2dpDeviceUnavailableNow(mDockAddress);
2404 }
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07002405 }
2406 makeA2dpDeviceAvailable(address);
Eric Laurenta553c252009-07-17 12:17:14 -07002407 }
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07002408 } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
2409 int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
2410 BluetoothProfile.STATE_DISCONNECTED);
Eric Laurentd5603c12009-08-06 08:49:39 -07002411 int device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
Nick Pellya56d1c72009-08-19 14:49:29 -07002412 String address = null;
Eric Laurentdca56b92011-09-02 14:20:56 -07002413
2414 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
2415 if (btDevice == null) {
2416 return;
2417 }
2418
2419 address = btDevice.getAddress();
2420 BluetoothClass btClass = btDevice.getBluetoothClass();
2421 if (btClass != null) {
2422 switch (btClass.getDeviceClass()) {
2423 case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
2424 case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
2425 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
2426 break;
2427 case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
2428 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
2429 break;
Eric Laurentd5603c12009-08-06 08:49:39 -07002430 }
2431 }
2432
Eric Laurentdca56b92011-09-02 14:20:56 -07002433 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
2434 address = "";
2435 }
Eric Laurentd5603c12009-08-06 08:49:39 -07002436 boolean isConnected = (mConnectedDevices.containsKey(device) &&
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07002437 mConnectedDevices.get(device).equals(address));
Eric Laurentd5603c12009-08-06 08:49:39 -07002438
Eric Laurent62ef7672010-11-24 10:58:32 -08002439 synchronized (mScoClients) {
2440 if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
2441 AudioSystem.setDeviceConnectionState(device,
2442 AudioSystem.DEVICE_STATE_UNAVAILABLE,
2443 address);
2444 mConnectedDevices.remove(device);
2445 mBluetoothHeadsetDevice = null;
Eric Laurentdc03c612011-04-01 10:59:41 -07002446 resetBluetoothSco();
Eric Laurent62ef7672010-11-24 10:58:32 -08002447 } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
2448 AudioSystem.setDeviceConnectionState(device,
2449 AudioSystem.DEVICE_STATE_AVAILABLE,
2450 address);
2451 mConnectedDevices.put(new Integer(device), address);
2452 mBluetoothHeadsetDevice = btDevice;
2453 }
Eric Laurenta553c252009-07-17 12:17:14 -07002454 }
2455 } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
2456 int state = intent.getIntExtra("state", 0);
Eric Laurent923d7d72009-11-12 12:09:06 -08002457 int microphone = intent.getIntExtra("microphone", 0);
2458
2459 if (microphone != 0) {
Eric Laurent2c61bee2010-12-14 16:31:33 -08002460 boolean isConnected =
2461 mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
Eric Laurent923d7d72009-11-12 12:09:06 -08002462 if (state == 0 && isConnected) {
2463 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
2464 AudioSystem.DEVICE_STATE_UNAVAILABLE,
2465 "");
2466 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
2467 } else if (state == 1 && !isConnected) {
2468 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
2469 AudioSystem.DEVICE_STATE_AVAILABLE,
2470 "");
Eric Laurent2c61bee2010-12-14 16:31:33 -08002471 mConnectedDevices.put(
2472 new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADSET), "");
Eric Laurent923d7d72009-11-12 12:09:06 -08002473 }
2474 } else {
Eric Laurent2c61bee2010-12-14 16:31:33 -08002475 boolean isConnected =
2476 mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
Eric Laurent923d7d72009-11-12 12:09:06 -08002477 if (state == 0 && isConnected) {
2478 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
2479 AudioSystem.DEVICE_STATE_UNAVAILABLE,
2480 "");
2481 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
2482 } else if (state == 1 && !isConnected) {
2483 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
2484 AudioSystem.DEVICE_STATE_AVAILABLE,
2485 "");
Eric Laurent2c61bee2010-12-14 16:31:33 -08002486 mConnectedDevices.put(
2487 new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), "");
Eric Laurent923d7d72009-11-12 12:09:06 -08002488 }
Eric Laurenta553c252009-07-17 12:17:14 -07002489 }
Praveen Bharathi21e941b2010-10-06 15:23:14 -05002490 } else if (action.equals(Intent.ACTION_USB_ANLG_HEADSET_PLUG)) {
2491 int state = intent.getIntExtra("state", 0);
2492 Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_ANLG_HEADSET_PLUG, state = "+state);
Eric Laurent2c61bee2010-12-14 16:31:33 -08002493 boolean isConnected =
2494 mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
Praveen Bharathi21e941b2010-10-06 15:23:14 -05002495 if (state == 0 && isConnected) {
2496 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
2497 AudioSystem.DEVICE_STATE_UNAVAILABLE, "");
2498 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
2499 } else if (state == 1 && !isConnected) {
2500 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
2501 AudioSystem.DEVICE_STATE_AVAILABLE, "");
Eric Laurent2c61bee2010-12-14 16:31:33 -08002502 mConnectedDevices.put(
2503 new Integer(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET), "");
Praveen Bharathi21e941b2010-10-06 15:23:14 -05002504 }
Eric Laurent2c61bee2010-12-14 16:31:33 -08002505 } else if (action.equals(Intent.ACTION_HDMI_AUDIO_PLUG)) {
2506 int state = intent.getIntExtra("state", 0);
2507 Log.v(TAG, "Broadcast Receiver: Got ACTION_HDMI_AUDIO_PLUG, state = "+state);
2508 boolean isConnected =
2509 mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_AUX_DIGITAL);
2510 if (state == 0 && isConnected) {
2511 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL,
2512 AudioSystem.DEVICE_STATE_UNAVAILABLE, "");
2513 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_AUX_DIGITAL);
2514 } else if (state == 1 && !isConnected) {
2515 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL,
2516 AudioSystem.DEVICE_STATE_AVAILABLE, "");
2517 mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_AUX_DIGITAL), "");
2518 }
2519 } else if (action.equals(Intent.ACTION_USB_DGTL_HEADSET_PLUG)) {
Praveen Bharathi21e941b2010-10-06 15:23:14 -05002520 int state = intent.getIntExtra("state", 0);
2521 Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_DGTL_HEADSET_PLUG, state = "+state);
Eric Laurent2c61bee2010-12-14 16:31:33 -08002522 boolean isConnected =
2523 mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
Praveen Bharathi21e941b2010-10-06 15:23:14 -05002524 if (state == 0 && isConnected) {
2525 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
2526 AudioSystem.DEVICE_STATE_UNAVAILABLE, "");
2527 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
2528 } else if (state == 1 && !isConnected) {
2529 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
2530 AudioSystem.DEVICE_STATE_AVAILABLE, "");
Eric Laurent2c61bee2010-12-14 16:31:33 -08002531 mConnectedDevices.put(
2532 new Integer(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET), "");
Praveen Bharathi21e941b2010-10-06 15:23:14 -05002533 }
Eric Laurent3def1ee2010-03-17 23:26:26 -07002534 } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
Eric Laurent62ef7672010-11-24 10:58:32 -08002535 boolean broadcast = false;
Eric Laurentdc03c612011-04-01 10:59:41 -07002536 int state = AudioManager.SCO_AUDIO_STATE_ERROR;
Eric Laurent3def1ee2010-03-17 23:26:26 -07002537 synchronized (mScoClients) {
Eric Laurent62ef7672010-11-24 10:58:32 -08002538 int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
Eric Laurentdc03c612011-04-01 10:59:41 -07002539 // broadcast intent if the connection was initated by AudioService
2540 if (!mScoClients.isEmpty() &&
2541 (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
2542 mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
2543 mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
Eric Laurent62ef7672010-11-24 10:58:32 -08002544 broadcast = true;
2545 }
2546 switch (btState) {
2547 case BluetoothHeadset.STATE_AUDIO_CONNECTED:
Eric Laurentdc03c612011-04-01 10:59:41 -07002548 state = AudioManager.SCO_AUDIO_STATE_CONNECTED;
2549 if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
2550 mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
2551 mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
Eric Laurent62ef7672010-11-24 10:58:32 -08002552 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
Eric Laurent3def1ee2010-03-17 23:26:26 -07002553 }
Eric Laurent62ef7672010-11-24 10:58:32 -08002554 break;
2555 case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
Eric Laurentdc03c612011-04-01 10:59:41 -07002556 state = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
Eric Laurent62ef7672010-11-24 10:58:32 -08002557 mScoAudioState = SCO_STATE_INACTIVE;
Eric Laurentd7454be2011-09-14 08:45:58 -07002558 clearAllScoClients(0, false);
Eric Laurent62ef7672010-11-24 10:58:32 -08002559 break;
2560 case BluetoothHeadset.STATE_AUDIO_CONNECTING:
Eric Laurentdc03c612011-04-01 10:59:41 -07002561 if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
2562 mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
2563 mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
Eric Laurent62ef7672010-11-24 10:58:32 -08002564 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
Eric Laurent3def1ee2010-03-17 23:26:26 -07002565 }
Eric Laurent62ef7672010-11-24 10:58:32 -08002566 default:
2567 // do not broadcast CONNECTING or invalid state
2568 broadcast = false;
2569 break;
Eric Laurent3def1ee2010-03-17 23:26:26 -07002570 }
2571 }
Eric Laurent62ef7672010-11-24 10:58:32 -08002572 if (broadcast) {
Eric Laurentdc03c612011-04-01 10:59:41 -07002573 broadcastScoConnectionState(state);
2574 //FIXME: this is to maintain compatibility with deprecated intent
2575 // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
Eric Laurent62ef7672010-11-24 10:58:32 -08002576 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
Eric Laurentdc03c612011-04-01 10:59:41 -07002577 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
Eric Laurent62ef7672010-11-24 10:58:32 -08002578 mContext.sendStickyBroadcast(newIntent);
2579 }
2580 } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
Eric Laurenta60e2122010-12-28 16:49:07 -08002581 mBootCompleted = true;
Eric Laurent117b7bb2011-01-16 17:07:27 -08002582 sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SHARED_MSG, SENDMSG_NOOP,
2583 0, 0, null, 0);
Eric Laurentdc03c612011-04-01 10:59:41 -07002584
Eric Laurent96f218e2011-08-15 15:55:21 -07002585 mKeyguardManager =
2586 (KeyguardManager)mContext.getSystemService(Context.KEYGUARD_SERVICE);
Eric Laurentdc03c612011-04-01 10:59:41 -07002587 mScoConnectionState = AudioManager.SCO_AUDIO_STATE_ERROR;
2588 resetBluetoothSco();
2589 getBluetoothHeadset();
2590 //FIXME: this is to maintain compatibility with deprecated intent
2591 // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
Eric Laurent62ef7672010-11-24 10:58:32 -08002592 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
2593 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
2594 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
2595 mContext.sendStickyBroadcast(newIntent);
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07002596 } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) {
2597 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
2598 // a package is being removed, not replaced
2599 String packageName = intent.getData().getSchemeSpecificPart();
2600 if (packageName != null) {
2601 removeMediaButtonReceiverForPackage(packageName);
2602 }
2603 }
Eric Laurent950e8cb2011-10-13 08:57:54 -07002604 } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
2605 AudioSystem.setParameters("screen_state=on");
2606 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
2607 AudioSystem.setParameters("screen_state=off");
Eric Laurenta553c252009-07-17 12:17:14 -07002608 }
2609 }
2610 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002611
2612 //==========================================================================================
2613 // AudioFocus
2614 //==========================================================================================
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07002615
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -07002616 /* constant to identify focus stack entry that is used to hold the focus while the phone
2617 * is ringing or during a call
2618 */
2619 private final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls";
2620
Jean-Michel Trivi392a2bb2010-05-10 20:02:46 -07002621 private final static Object mAudioFocusLock = new Object();
2622
Jean-Michel Trivie73131a2010-06-14 09:53:30 -07002623 private final static Object mRingingLock = new Object();
2624
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -07002625 private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
2626 @Override
2627 public void onCallStateChanged(int state, String incomingNumber) {
2628 if (state == TelephonyManager.CALL_STATE_RINGING) {
2629 //Log.v(TAG, " CALL_STATE_RINGING");
Jean-Michel Trivie73131a2010-06-14 09:53:30 -07002630 synchronized(mRingingLock) {
2631 mIsRinging = true;
2632 }
Jean-Michel Trivi2ade5762010-12-11 13:18:30 -08002633 } else if ((state == TelephonyManager.CALL_STATE_OFFHOOK)
2634 || (state == TelephonyManager.CALL_STATE_IDLE)) {
Jean-Michel Trivie73131a2010-06-14 09:53:30 -07002635 synchronized(mRingingLock) {
2636 mIsRinging = false;
2637 }
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07002638 }
2639 }
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -07002640 };
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07002641
2642 private void notifyTopOfAudioFocusStack() {
2643 // notify the top of the stack it gained focus
2644 if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
2645 if (canReassignAudioFocus()) {
2646 try {
2647 mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
2648 AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId);
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07002649 } catch (RemoteException e) {
2650 Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e);
2651 e.printStackTrace();
2652 }
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07002653 }
2654 }
2655 }
2656
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002657 private static class FocusStackEntry {
2658 public int mStreamType = -1;// no stream type
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002659 public IAudioFocusDispatcher mFocusDispatcher = null;
2660 public IBinder mSourceRef = null;
2661 public String mClientId;
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07002662 public int mFocusChangeType;
Jean-Michel Trivi061214b2011-08-14 11:37:24 -07002663 public AudioFocusDeathHandler mHandler;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07002664 public String mPackageName;
2665 public int mCallingUid;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002666
2667 public FocusStackEntry() {
2668 }
2669
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07002670 public FocusStackEntry(int streamType, int duration,
Jean-Michel Trivi061214b2011-08-14 11:37:24 -07002671 IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr,
2672 String pn, int uid) {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002673 mStreamType = streamType;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002674 mFocusDispatcher = afl;
2675 mSourceRef = source;
2676 mClientId = id;
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07002677 mFocusChangeType = duration;
Jean-Michel Trivi061214b2011-08-14 11:37:24 -07002678 mHandler = hdlr;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07002679 mPackageName = pn;
2680 mCallingUid = uid;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002681 }
Jean-Michel Trivi061214b2011-08-14 11:37:24 -07002682
2683 public void unlinkToDeath() {
2684 if (mSourceRef != null && mHandler != null) {
2685 mSourceRef.unlinkToDeath(mHandler, 0);
2686 }
2687 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002688 }
2689
2690 private Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>();
2691
2692 /**
2693 * Helper function:
2694 * Display in the log the current entries in the audio focus stack
2695 */
2696 private void dumpFocusStack(PrintWriter pw) {
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -07002697 pw.println("\nAudio Focus stack entries:");
Jean-Michel Trivi392a2bb2010-05-10 20:02:46 -07002698 synchronized(mAudioFocusLock) {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002699 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
2700 while(stackIterator.hasNext()) {
2701 FocusStackEntry fse = stackIterator.next();
2702 pw.println(" source:" + fse.mSourceRef + " -- client: " + fse.mClientId
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07002703 + " -- duration: " + fse.mFocusChangeType
2704 + " -- uid: " + fse.mCallingUid);
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002705 }
2706 }
2707 }
2708
2709 /**
2710 * Helper function:
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07002711 * Called synchronized on mAudioFocusLock
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002712 * Remove a focus listener from the focus stack.
2713 * @param focusListenerToRemove the focus listener
2714 * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
2715 * focus, notify the next item in the stack it gained focus.
2716 */
2717 private void removeFocusStackEntry(String clientToRemove, boolean signal) {
2718 // is the current top of the focus stack abandoning focus? (because of death or request)
2719 if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove))
2720 {
2721 //Log.i(TAG, " removeFocusStackEntry() removing top of stack");
Jean-Michel Trivi061214b2011-08-14 11:37:24 -07002722 FocusStackEntry fse = mFocusStack.pop();
2723 fse.unlinkToDeath();
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002724 if (signal) {
2725 // notify the new top of the stack it gained focus
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07002726 notifyTopOfAudioFocusStack();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07002727 // there's a new top of the stack, let the remote control know
2728 synchronized(mRCStack) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07002729 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07002730 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002731 }
2732 } else {
2733 // focus is abandoned by a client that's not at the top of the stack,
2734 // no need to update focus.
2735 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
2736 while(stackIterator.hasNext()) {
2737 FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
2738 if(fse.mClientId.equals(clientToRemove)) {
2739 Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for "
2740 + fse.mClientId);
Jean-Michel Trivi72843372011-04-18 10:18:37 -07002741 stackIterator.remove();
Jean-Michel Trivi061214b2011-08-14 11:37:24 -07002742 fse.unlinkToDeath();
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002743 }
2744 }
2745 }
2746 }
2747
2748 /**
2749 * Helper function:
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07002750 * Called synchronized on mAudioFocusLock
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002751 * Remove focus listeners from the focus stack for a particular client.
2752 */
2753 private void removeFocusStackEntryForClient(IBinder cb) {
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07002754 // is the owner of the audio focus part of the client to remove?
2755 boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
2756 mFocusStack.peek().mSourceRef.equals(cb);
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002757 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
2758 while(stackIterator.hasNext()) {
2759 FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
2760 if(fse.mSourceRef.equals(cb)) {
2761 Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for "
2762 + fse.mClientId);
Jean-Michel Trivi72843372011-04-18 10:18:37 -07002763 stackIterator.remove();
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002764 }
2765 }
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07002766 if (isTopOfStackForClientToRemove) {
2767 // we removed an entry at the top of the stack:
2768 // notify the new top of the stack it gained focus.
2769 notifyTopOfAudioFocusStack();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07002770 // there's a new top of the stack, let the remote control know
2771 synchronized(mRCStack) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07002772 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07002773 }
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07002774 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002775 }
2776
2777 /**
2778 * Helper function:
2779 * Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
2780 */
Jean-Michel Trivid327f212010-03-16 21:44:33 -07002781 private boolean canReassignAudioFocus() {
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -07002782 // focus requests are rejected during a phone call or when the phone is ringing
2783 // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus
2784 if (!mFocusStack.isEmpty() && IN_VOICE_COMM_FOCUS_ID.equals(mFocusStack.peek().mClientId)) {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002785 return false;
2786 }
2787 return true;
2788 }
2789
2790 /**
2791 * Inner class to monitor audio focus client deaths, and remove them from the audio focus
2792 * stack if necessary.
2793 */
2794 private class AudioFocusDeathHandler implements IBinder.DeathRecipient {
2795 private IBinder mCb; // To be notified of client's death
2796
2797 AudioFocusDeathHandler(IBinder cb) {
2798 mCb = cb;
2799 }
2800
2801 public void binderDied() {
Jean-Michel Trivi392a2bb2010-05-10 20:02:46 -07002802 synchronized(mAudioFocusLock) {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002803 Log.w(TAG, " AudioFocus audio focus client died");
2804 removeFocusStackEntryForClient(mCb);
2805 }
2806 }
2807
2808 public IBinder getBinder() {
2809 return mCb;
2810 }
2811 }
2812
2813
Jean-Michel Trivid327f212010-03-16 21:44:33 -07002814 /** @see AudioManager#requestAudioFocus(IAudioFocusDispatcher, int, int) */
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07002815 public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb,
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07002816 IAudioFocusDispatcher fd, String clientId, String callingPackageName) {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002817 Log.i(TAG, " AudioFocus requestAudioFocus() from " + clientId);
2818 // the main stream type for the audio focus request is currently not used. It may
2819 // potentially be used to handle multiple stream type-dependent audio focuses.
2820
Jean-Michel Trivi382f4e6e2011-03-08 14:38:59 -08002821 // we need a valid binder callback for clients
2822 if (!cb.pingBinder()) {
2823 Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002824 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
2825 }
2826
Jean-Michel Trivi392a2bb2010-05-10 20:02:46 -07002827 synchronized(mAudioFocusLock) {
2828 if (!canReassignAudioFocus()) {
2829 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
2830 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002831
Jean-Michel Trivibaf674e2011-08-14 15:16:21 -07002832 // handle the potential premature death of the new holder of the focus
2833 // (premature death == death before abandoning focus)
2834 // Register for client death notification
2835 AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
2836 try {
2837 cb.linkToDeath(afdh, 0);
2838 } catch (RemoteException e) {
2839 // client has already died!
2840 Log.w(TAG, "AudioFocus requestAudioFocus() could not link to "+cb+" binder death");
2841 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
2842 }
2843
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002844 if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) {
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07002845 // if focus is already owned by this client and the reason for acquiring the focus
2846 // hasn't changed, don't do anything
2847 if (mFocusStack.peek().mFocusChangeType == focusChangeHint) {
2848 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
2849 }
2850 // the reason for the audio focus request has changed: remove the current top of
2851 // stack and respond as if we had a new focus owner
2852 mFocusStack.pop();
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002853 }
2854
2855 // notify current top of stack it is losing focus
2856 if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
2857 try {
2858 mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07002859 -1 * focusChangeHint, // loss and gain codes are inverse of each other
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002860 mFocusStack.peek().mClientId);
2861 } catch (RemoteException e) {
2862 Log.e(TAG, " Failure to signal loss of focus due to "+ e);
2863 e.printStackTrace();
2864 }
2865 }
2866
Jean-Michel Trivi55d1bb32010-04-01 17:40:58 -07002867 // focus requester might already be somewhere below in the stack, remove it
Jean-Michel Trivibaf674e2011-08-14 15:16:21 -07002868 removeFocusStackEntry(clientId, false /* signal */);
Jean-Michel Trivi061214b2011-08-14 11:37:24 -07002869
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002870 // push focus requester at the top of the audio focus stack
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07002871 mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, fd, cb,
Jean-Michel Trivi061214b2011-08-14 11:37:24 -07002872 clientId, afdh, callingPackageName, Binder.getCallingUid()));
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07002873
2874 // there's a new top of the stack, let the remote control know
2875 synchronized(mRCStack) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07002876 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07002877 }
Jean-Michel Trivi392a2bb2010-05-10 20:02:46 -07002878 }//synchronized(mAudioFocusLock)
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002879
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002880 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
2881 }
2882
Jean-Michel Trivid327f212010-03-16 21:44:33 -07002883 /** @see AudioManager#abandonAudioFocus(IAudioFocusDispatcher) */
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002884 public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) {
2885 Log.i(TAG, " AudioFocus abandonAudioFocus() from " + clientId);
Jean-Michel Trivi392a2bb2010-05-10 20:02:46 -07002886 try {
2887 // this will take care of notifying the new focus owner if needed
2888 synchronized(mAudioFocusLock) {
2889 removeFocusStackEntry(clientId, true);
2890 }
2891 } catch (java.util.ConcurrentModificationException cme) {
2892 // Catching this exception here is temporary. It is here just to prevent
2893 // a crash seen when the "Silent" notification is played. This is believed to be fixed
2894 // but this try catch block is left just to be safe.
2895 Log.e(TAG, "FATAL EXCEPTION AudioFocus abandonAudioFocus() caused " + cme);
2896 cme.printStackTrace();
Jean-Michel Trivi2930bb22010-04-09 19:27:58 -07002897 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002898
2899 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
2900 }
2901
2902
Jean-Michel Trivid327f212010-03-16 21:44:33 -07002903 public void unregisterAudioFocusClient(String clientId) {
Jean-Michel Trivi392a2bb2010-05-10 20:02:46 -07002904 synchronized(mAudioFocusLock) {
Jean-Michel Trivi2930bb22010-04-09 19:27:58 -07002905 removeFocusStackEntry(clientId, false);
2906 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002907 }
2908
2909
Jean-Michel Trivid327f212010-03-16 21:44:33 -07002910 //==========================================================================================
2911 // RemoteControl
2912 //==========================================================================================
2913 /**
2914 * Receiver for media button intents. Handles the dispatching of the media button event
2915 * to one of the registered listeners, or if there was none, resumes the intent broadcast
2916 * to the rest of the system.
2917 */
2918 private class MediaButtonBroadcastReceiver extends BroadcastReceiver {
2919 @Override
2920 public void onReceive(Context context, Intent intent) {
2921 String action = intent.getAction();
2922 if (!Intent.ACTION_MEDIA_BUTTON.equals(action)) {
2923 return;
2924 }
2925 KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
2926 if (event != null) {
2927 // if in a call or ringing, do not break the current phone app behavior
2928 // TODO modify this to let the phone app specifically get the RC focus
2929 // add modify the phone app to take advantage of the new API
Jean-Michel Trivie73131a2010-06-14 09:53:30 -07002930 synchronized(mRingingLock) {
2931 if (mIsRinging || (getMode() == AudioSystem.MODE_IN_CALL) ||
Jean-Michel Trivi8f677d62010-11-15 12:11:32 -08002932 (getMode() == AudioSystem.MODE_IN_COMMUNICATION) ||
Jean-Michel Trivie73131a2010-06-14 09:53:30 -07002933 (getMode() == AudioSystem.MODE_RINGTONE) ) {
2934 return;
2935 }
Jean-Michel Trivid327f212010-03-16 21:44:33 -07002936 }
2937 synchronized(mRCStack) {
2938 if (!mRCStack.empty()) {
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07002939 // create a new intent to fill in the extras of the registered PendingIntent
Jean-Michel Trivid327f212010-03-16 21:44:33 -07002940 Intent targetedIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07002941 Bundle extras = intent.getExtras();
2942 if (extras != null) {
2943 targetedIntent.putExtras(extras);
2944 // trap the current broadcast
2945 abortBroadcast();
2946 //Log.v(TAG, " Sending intent" + targetedIntent);
2947 // send the intent that was registered by the client
2948 try {
2949 mRCStack.peek().mMediaIntent.send(context, 0, targetedIntent);
2950 } catch (CanceledException e) {
2951 Log.e(TAG, "Error sending pending intent " + mRCStack.peek());
2952 e.printStackTrace();
2953 }
2954 }
Jean-Michel Trivid327f212010-03-16 21:44:33 -07002955 }
2956 }
2957 }
2958 }
2959 }
2960
Neel Parekhced110e2011-08-05 14:32:49 -07002961 private final Object mCurrentRcLock = new Object();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07002962 /**
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07002963 * The one remote control client which will receive a request for display information.
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07002964 * This object may be null.
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07002965 * Access protected by mCurrentRcLock.
2966 */
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07002967 private IRemoteControlClient mCurrentRcClient = null;
Jean-Michel Trivid327f212010-03-16 21:44:33 -07002968
Jean-Michel Trivi7b6198c2011-08-07 11:27:50 -07002969 private final static int RC_INFO_NONE = 0;
2970 private final static int RC_INFO_ALL =
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07002971 RemoteControlClient.FLAG_INFORMATION_REQUEST_ALBUM_ART |
2972 RemoteControlClient.FLAG_INFORMATION_REQUEST_KEY_MEDIA |
2973 RemoteControlClient.FLAG_INFORMATION_REQUEST_METADATA |
2974 RemoteControlClient.FLAG_INFORMATION_REQUEST_PLAYSTATE;
Jean-Michel Trivi7b6198c2011-08-07 11:27:50 -07002975
2976 /**
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07002977 * A monotonically increasing generation counter for mCurrentRcClient.
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07002978 * Only accessed with a lock on mCurrentRcLock.
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07002979 * No value wrap-around issues as we only act on equal values.
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07002980 */
Neel Parekhced110e2011-08-05 14:32:49 -07002981 private int mCurrentRcClientGen = 0;
Jean-Michel Trivid327f212010-03-16 21:44:33 -07002982
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07002983 /**
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07002984 * Inner class to monitor remote control client deaths, and remove the client for the
2985 * remote control stack if necessary.
2986 */
2987 private class RcClientDeathHandler implements IBinder.DeathRecipient {
2988 private IBinder mCb; // To be notified of client's death
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07002989 private PendingIntent mMediaIntent;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07002990
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07002991 RcClientDeathHandler(IBinder cb, PendingIntent pi) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07002992 mCb = cb;
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07002993 mMediaIntent = pi;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07002994 }
2995
2996 public void binderDied() {
2997 Log.w(TAG, " RemoteControlClient died");
2998 // remote control client died, make sure the displays don't use it anymore
2999 // by setting its remote control client to null
Jean-Michel Trivi26b3d1f2011-09-28 11:05:15 -07003000 registerRemoteControlClient(mMediaIntent, null/*rcClient*/, null/*ignored*/);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003001 }
3002
3003 public IBinder getBinder() {
3004 return mCb;
3005 }
3006 }
3007
3008 private static class RemoteControlStackEntry {
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003009 /**
3010 * The target for the ACTION_MEDIA_BUTTON events.
3011 * Always non null.
3012 */
3013 public PendingIntent mMediaIntent;
3014 /**
3015 * The registered media button event receiver.
3016 * Always non null.
3017 */
3018 public ComponentName mReceiverComponent;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003019 public String mCallingPackageName;
3020 public int mCallingUid;
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003021 /**
3022 * Provides access to the information to display on the remote control.
3023 * May be null (when a media button event receiver is registered,
3024 * but no remote control client has been registered) */
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003025 public IRemoteControlClient mRcClient;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003026 public RcClientDeathHandler mRcClientDeathHandler;
3027
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003028 /** precondition: mediaIntent != null, eventReceiver != null */
3029 public RemoteControlStackEntry(PendingIntent mediaIntent, ComponentName eventReceiver) {
3030 mMediaIntent = mediaIntent;
3031 mReceiverComponent = eventReceiver;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003032 mCallingUid = -1;
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07003033 mRcClient = null;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003034 }
3035
3036 public void unlinkToRcClientDeath() {
3037 if ((mRcClientDeathHandler != null) && (mRcClientDeathHandler.mCb != null)) {
3038 try {
3039 mRcClientDeathHandler.mCb.unlinkToDeath(mRcClientDeathHandler, 0);
3040 } catch (java.util.NoSuchElementException e) {
3041 // not much we can do here
3042 Log.e(TAG, "Encountered " + e + " in unlinkToRcClientDeath()");
3043 e.printStackTrace();
3044 }
3045 }
3046 }
3047 }
3048
3049 /**
3050 * The stack of remote control event receivers.
3051 * Code sections and methods that modify the remote control event receiver stack are
3052 * synchronized on mRCStack, but also BEFORE on mFocusLock as any change in either
3053 * stack, audio focus or RC, can lead to a change in the remote control display
3054 */
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003055 private Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>();
3056
3057 /**
3058 * Helper function:
3059 * Display in the log the current entries in the remote control focus stack
3060 */
3061 private void dumpRCStack(PrintWriter pw) {
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -07003062 pw.println("\nRemote Control stack entries:");
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003063 synchronized(mRCStack) {
3064 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3065 while(stackIterator.hasNext()) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003066 RemoteControlStackEntry rcse = stackIterator.next();
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003067 pw.println(" pi: " + rcse.mMediaIntent +
3068 " -- ercvr: " + rcse.mReceiverComponent +
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07003069 " -- client: " + rcse.mRcClient +
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003070 " -- uid: " + rcse.mCallingUid);
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003071 }
3072 }
3073 }
3074
3075 /**
3076 * Helper function:
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07003077 * Remove any entry in the remote control stack that has the same package name as packageName
3078 * Pre-condition: packageName != null
3079 */
3080 private void removeMediaButtonReceiverForPackage(String packageName) {
3081 synchronized(mRCStack) {
3082 if (mRCStack.empty()) {
3083 return;
3084 } else {
3085 RemoteControlStackEntry oldTop = mRCStack.peek();
3086 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3087 // iterate over the stack entries
3088 while(stackIterator.hasNext()) {
3089 RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
3090 if (packageName.equalsIgnoreCase(rcse.mReceiverComponent.getPackageName())) {
3091 // a stack entry is from the package being removed, remove it from the stack
3092 stackIterator.remove();
3093 }
3094 }
3095 if (mRCStack.empty()) {
3096 // no saved media button receiver
3097 mAudioHandler.sendMessage(
3098 mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
3099 null));
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07003100 } else if (oldTop != mRCStack.peek()) {
3101 // the top of the stack has changed, save it in the system settings
3102 // by posting a message to persist it
3103 mAudioHandler.sendMessage(
3104 mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
3105 mRCStack.peek().mReceiverComponent));
3106 }
3107 }
3108 }
3109 }
3110
3111 /**
3112 * Helper function:
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003113 * Restore remote control receiver from the system settings.
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07003114 */
3115 private void restoreMediaButtonReceiver() {
3116 String receiverName = Settings.System.getString(mContentResolver,
3117 Settings.System.MEDIA_BUTTON_RECEIVER);
3118 if ((null != receiverName) && !receiverName.isEmpty()) {
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003119 ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
3120 // construct a PendingIntent targeted to the restored component name
3121 // for the media button and register it
3122 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
3123 // the associated intent will be handled by the component being registered
3124 mediaButtonIntent.setComponent(eventReceiver);
3125 PendingIntent pi = PendingIntent.getBroadcast(mContext,
3126 0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
3127 registerMediaButtonIntent(pi, eventReceiver);
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07003128 }
3129 }
3130
3131 /**
3132 * Helper function:
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003133 * Set the new remote control receiver at the top of the RC focus stack.
3134 * precondition: mediaIntent != null, target != null
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003135 */
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003136 private void pushMediaButtonReceiver(PendingIntent mediaIntent, ComponentName target) {
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003137 // already at top of stack?
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003138 if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(mediaIntent)) {
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003139 return;
3140 }
3141 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003142 RemoteControlStackEntry rcse = null;
3143 boolean wasInsideStack = false;
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003144 while(stackIterator.hasNext()) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003145 rcse = (RemoteControlStackEntry)stackIterator.next();
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003146 if(rcse.mMediaIntent.equals(mediaIntent)) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003147 wasInsideStack = true;
Jean-Michel Trivi72843372011-04-18 10:18:37 -07003148 stackIterator.remove();
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003149 break;
3150 }
3151 }
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003152 if (!wasInsideStack) {
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003153 rcse = new RemoteControlStackEntry(mediaIntent, target);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003154 }
3155 mRCStack.push(rcse);
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07003156
3157 // post message to persist the default media button receiver
3158 mAudioHandler.sendMessage( mAudioHandler.obtainMessage(
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003159 MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) );
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003160 }
3161
3162 /**
3163 * Helper function:
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003164 * Remove the remote control receiver from the RC focus stack.
3165 * precondition: pi != null
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003166 */
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003167 private void removeMediaButtonReceiver(PendingIntent pi) {
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003168 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3169 while(stackIterator.hasNext()) {
3170 RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003171 if(rcse.mMediaIntent.equals(pi)) {
Jean-Michel Trivi72843372011-04-18 10:18:37 -07003172 stackIterator.remove();
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003173 break;
3174 }
3175 }
3176 }
3177
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003178 /**
3179 * Helper function:
3180 * Called synchronized on mRCStack
3181 */
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003182 private boolean isCurrentRcController(PendingIntent pi) {
3183 if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(pi)) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003184 return true;
3185 }
3186 return false;
3187 }
3188
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003189 //==========================================================================================
3190 // Remote control display / client
3191 //==========================================================================================
3192 /**
3193 * Update the remote control displays with the new "focused" client generation
3194 */
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07003195 private void setNewRcClientOnDisplays_syncRcsCurrc(int newClientGeneration,
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003196 PendingIntent newMediaIntent, boolean clearing) {
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003197 // NOTE: Only one IRemoteControlDisplay supported in this implementation
3198 if (mRcDisplay != null) {
3199 try {
Jean-Michel Trivi4b6df6a32011-08-24 10:31:13 -07003200 mRcDisplay.setCurrentClientId(
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003201 newClientGeneration, newMediaIntent, clearing);
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003202 } catch (RemoteException e) {
Jean-Michel Trividb8a3612011-08-26 19:10:51 -07003203 Log.e(TAG, "Dead display in setNewRcClientOnDisplays_syncRcsCurrc() "+e);
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003204 // if we had a display before, stop monitoring its death
3205 rcDisplay_stopDeathMonitor_syncRcStack();
3206 mRcDisplay = null;
3207 }
3208 }
3209 }
3210
3211 /**
3212 * Update the remote control clients with the new "focused" client generation
3213 */
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07003214 private void setNewRcClientGenerationOnClients_syncRcsCurrc(int newClientGeneration) {
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003215 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3216 while(stackIterator.hasNext()) {
3217 RemoteControlStackEntry se = stackIterator.next();
3218 if ((se != null) && (se.mRcClient != null)) {
3219 try {
3220 se.mRcClient.setCurrentClientGenerationId(newClientGeneration);
3221 } catch (RemoteException e) {
Jean-Michel Trividb8a3612011-08-26 19:10:51 -07003222 Log.w(TAG, "Dead client in setNewRcClientGenerationOnClients_syncRcsCurrc()"+e);
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003223 stackIterator.remove();
3224 se.unlinkToRcClientDeath();
3225 }
3226 }
3227 }
3228 }
3229
3230 /**
Jean-Michel Trivi4b6df6a32011-08-24 10:31:13 -07003231 * Update the displays and clients with the new "focused" client generation and name
3232 * @param newClientGeneration the new generation value matching a client update
3233 * @param newClientEventReceiver the media button event receiver associated with the client.
3234 * May be null, which implies there is no registered media button event receiver.
3235 * @param clearing true if the new client generation value maps to a remote control update
3236 * where the display should be cleared.
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003237 */
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07003238 private void setNewRcClient_syncRcsCurrc(int newClientGeneration,
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003239 PendingIntent newMediaIntent, boolean clearing) {
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07003240 // send the new valid client generation ID to all displays
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003241 setNewRcClientOnDisplays_syncRcsCurrc(newClientGeneration, newMediaIntent, clearing);
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07003242 // send the new valid client generation ID to all clients
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07003243 setNewRcClientGenerationOnClients_syncRcsCurrc(newClientGeneration);
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003244 }
3245
3246 /**
3247 * Called when processing MSG_RCDISPLAY_CLEAR event
3248 */
3249 private void onRcDisplayClear() {
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07003250 if (DEBUG_RC) Log.i(TAG, "Clear remote control display");
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003251
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07003252 synchronized(mRCStack) {
3253 synchronized(mCurrentRcLock) {
3254 mCurrentRcClientGen++;
3255 // synchronously update the displays and clients with the new client generation
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07003256 setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003257 null /*newMediaIntent*/, true /*clearing*/);
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07003258 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003259 }
3260 }
3261
3262 /**
3263 * Called when processing MSG_RCDISPLAY_UPDATE event
3264 */
3265 private void onRcDisplayUpdate(RemoteControlStackEntry rcse, int flags /* USED ?*/) {
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07003266 synchronized(mRCStack) {
3267 synchronized(mCurrentRcLock) {
3268 if ((mCurrentRcClient != null) && (mCurrentRcClient.equals(rcse.mRcClient))) {
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07003269 if (DEBUG_RC) Log.i(TAG, "Display/update remote control ");
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003270
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07003271 mCurrentRcClientGen++;
3272 // synchronously update the displays and clients with
3273 // the new client generation
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07003274 setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003275 rcse.mMediaIntent /*newMediaIntent*/,
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07003276 false /*clearing*/);
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003277
Jean-Michel Trividb8a3612011-08-26 19:10:51 -07003278 // tell the current client that it needs to send info
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07003279 try {
3280 mCurrentRcClient.onInformationRequested(mCurrentRcClientGen,
3281 flags, mArtworkExpectedWidth, mArtworkExpectedHeight);
3282 } catch (RemoteException e) {
3283 Log.e(TAG, "Current valid remote client is dead: "+e);
3284 mCurrentRcClient = null;
3285 }
3286 } else {
3287 // the remote control display owner has changed between the
3288 // the message to update the display was sent, and the time it
3289 // gets to be processed (now)
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003290 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003291 }
3292 }
3293 }
3294
3295
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003296 /**
3297 * Helper function:
3298 * Called synchronized on mRCStack
3299 */
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003300 private void clearRemoteControlDisplay_syncAfRcs() {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003301 synchronized(mCurrentRcLock) {
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07003302 mCurrentRcClient = null;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003303 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003304 // will cause onRcDisplayClear() to be called in AudioService's handler thread
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003305 mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_CLEAR) );
3306 }
3307
3308 /**
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003309 * Helper function for code readability: only to be called from
3310 * checkUpdateRemoteControlDisplay_syncAfRcs() which checks the preconditions for
3311 * this method.
3312 * Preconditions:
3313 * - called synchronized mAudioFocusLock then on mRCStack
3314 * - mRCStack.isEmpty() is false
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003315 */
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003316 private void updateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003317 RemoteControlStackEntry rcse = mRCStack.peek();
Jean-Michel Trivib5f04d62011-08-09 19:12:36 -07003318 int infoFlagsAboutToBeUsed = infoChangedFlags;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003319 // this is where we enforce opt-in for information display on the remote controls
3320 // with the new AudioManager.registerRemoteControlClient() API
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07003321 if (rcse.mRcClient == null) {
3322 //Log.w(TAG, "Can't update remote control display with null remote control client");
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003323 clearRemoteControlDisplay_syncAfRcs();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003324 return;
3325 }
3326 synchronized(mCurrentRcLock) {
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07003327 if (!rcse.mRcClient.equals(mCurrentRcClient)) {
Jean-Michel Trivi7b6198c2011-08-07 11:27:50 -07003328 // new RC client, assume every type of information shall be queried
Jean-Michel Trivib5f04d62011-08-09 19:12:36 -07003329 infoFlagsAboutToBeUsed = RC_INFO_ALL;
Jean-Michel Trivi7b6198c2011-08-07 11:27:50 -07003330 }
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07003331 mCurrentRcClient = rcse.mRcClient;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003332 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003333 // will cause onRcDisplayUpdate() to be called in AudioService's handler thread
Jean-Michel Trivib5f04d62011-08-09 19:12:36 -07003334 mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_UPDATE,
Jean-Michel Trivi0a9faa82011-08-14 17:36:42 -07003335 infoFlagsAboutToBeUsed /* arg1 */, 0, rcse /* obj, != null */) );
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003336 }
3337
3338 /**
3339 * Helper function:
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003340 * Called synchronized on mAudioFocusLock, then mRCStack
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003341 * Check whether the remote control display should be updated, triggers the update if required
Jean-Michel Trivib5f04d62011-08-09 19:12:36 -07003342 * @param infoChangedFlags the flags corresponding to the remote control client information
3343 * that has changed, if applicable (checking for the update conditions might trigger a
3344 * clear, rather than an update event).
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003345 */
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003346 private void checkUpdateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003347 // determine whether the remote control display should be refreshed
3348 // if either stack is empty, there is a mismatch, so clear the RC display
3349 if (mRCStack.isEmpty() || mFocusStack.isEmpty()) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003350 clearRemoteControlDisplay_syncAfRcs();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003351 return;
3352 }
3353 // if the top of the two stacks belong to different packages, there is a mismatch, clear
3354 if ((mRCStack.peek().mCallingPackageName != null)
3355 && (mFocusStack.peek().mPackageName != null)
3356 && !(mRCStack.peek().mCallingPackageName.compareTo(
3357 mFocusStack.peek().mPackageName) == 0)) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003358 clearRemoteControlDisplay_syncAfRcs();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003359 return;
3360 }
3361 // if the audio focus didn't originate from the same Uid as the one in which the remote
3362 // control information will be retrieved, clear
3363 if (mRCStack.peek().mCallingUid != mFocusStack.peek().mCallingUid) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003364 clearRemoteControlDisplay_syncAfRcs();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003365 return;
3366 }
3367 // refresh conditions were verified: update the remote controls
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003368 // ok to call: synchronized mAudioFocusLock then on mRCStack, mRCStack is not empty
3369 updateRemoteControlDisplay_syncAfRcs(infoChangedFlags);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003370 }
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003371
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003372 /**
3373 * see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
3374 * precondition: mediaIntent != null, target != null
3375 */
3376 public void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver) {
3377 Log.i(TAG, " Remote Control registerMediaButtonIntent() for " + mediaIntent);
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003378
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003379 synchronized(mAudioFocusLock) {
3380 synchronized(mRCStack) {
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003381 pushMediaButtonReceiver(mediaIntent, eventReceiver);
Jean-Michel Trivib5f04d62011-08-09 19:12:36 -07003382 // new RC client, assume every type of information shall be queried
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003383 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003384 }
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003385 }
3386 }
3387
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003388 /**
3389 * see AudioManager.unregisterMediaButtonIntent(PendingIntent mediaIntent)
3390 * precondition: mediaIntent != null, eventReceiver != null
3391 */
3392 public void unregisterMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver)
3393 {
3394 Log.i(TAG, " Remote Control unregisterMediaButtonIntent() for " + mediaIntent);
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003395
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003396 synchronized(mAudioFocusLock) {
3397 synchronized(mRCStack) {
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003398 boolean topOfStackWillChange = isCurrentRcController(mediaIntent);
3399 removeMediaButtonReceiver(mediaIntent);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003400 if (topOfStackWillChange) {
Jean-Michel Trivib5f04d62011-08-09 19:12:36 -07003401 // current RC client will change, assume every type of info needs to be queried
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003402 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003403 }
3404 }
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003405 }
3406 }
3407
Jean-Michel Trivi26b3d1f2011-09-28 11:05:15 -07003408 /**
3409 * see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...)
3410 * Note: using this method with rcClient == null is a way to "disable" the IRemoteControlClient
3411 * without modifying the RC stack, but while still causing the display to refresh (will
3412 * become blank as a result of this)
3413 */
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003414 public void registerRemoteControlClient(PendingIntent mediaIntent,
3415 IRemoteControlClient rcClient, String callingPackageName) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003416 if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003417 synchronized(mAudioFocusLock) {
3418 synchronized(mRCStack) {
3419 // store the new display information
3420 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3421 while(stackIterator.hasNext()) {
3422 RemoteControlStackEntry rcse = stackIterator.next();
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003423 if(rcse.mMediaIntent.equals(mediaIntent)) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003424 // already had a remote control client?
3425 if (rcse.mRcClientDeathHandler != null) {
3426 // stop monitoring the old client's death
3427 rcse.unlinkToRcClientDeath();
3428 }
3429 // save the new remote control client
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07003430 rcse.mRcClient = rcClient;
Jean-Michel Trivi26b3d1f2011-09-28 11:05:15 -07003431 rcse.mCallingPackageName = callingPackageName;
3432 rcse.mCallingUid = Binder.getCallingUid();
3433 if (rcClient == null) {
3434 rcse.mRcClientDeathHandler = null;
3435 break;
3436 }
3437
3438 // there is a new (non-null) client:
3439 // 1/ give the new client the current display (if any)
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003440 if (mRcDisplay != null) {
3441 try {
3442 rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
3443 } catch (RemoteException e) {
3444 Log.e(TAG, "Error connecting remote control display to client: "+e);
3445 e.printStackTrace();
3446 }
3447 }
Jean-Michel Trivi26b3d1f2011-09-28 11:05:15 -07003448 // 2/ monitor the new client's death
3449 IBinder b = rcse.mRcClient.asBinder();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003450 RcClientDeathHandler rcdh =
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003451 new RcClientDeathHandler(b, rcse.mMediaIntent);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003452 try {
3453 b.linkToDeath(rcdh, 0);
3454 } catch (RemoteException e) {
3455 // remote control client is DOA, disqualify it
3456 Log.w(TAG, "registerRemoteControlClient() has a dead client " + b);
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07003457 rcse.mRcClient = null;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003458 }
3459 rcse.mRcClientDeathHandler = rcdh;
3460 break;
3461 }
3462 }
3463 // if the eventReceiver is at the top of the stack
3464 // then check for potential refresh of the remote controls
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003465 if (isCurrentRcController(mediaIntent)) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003466 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003467 }
3468 }
3469 }
3470 }
3471
Jean-Michel Trivi4b6df6a32011-08-24 10:31:13 -07003472 /**
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003473 * see AudioManager.unregisterRemoteControlClient(PendingIntent pi, ...)
Jean-Michel Trivi4b6df6a32011-08-24 10:31:13 -07003474 * rcClient is guaranteed non-null
3475 */
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003476 public void unregisterRemoteControlClient(PendingIntent mediaIntent,
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003477 IRemoteControlClient rcClient) {
Jean-Michel Trivi4b6df6a32011-08-24 10:31:13 -07003478 synchronized(mAudioFocusLock) {
3479 synchronized(mRCStack) {
3480 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3481 while(stackIterator.hasNext()) {
3482 RemoteControlStackEntry rcse = stackIterator.next();
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003483 if ((rcse.mMediaIntent.equals(mediaIntent))
Jean-Michel Trivi4b6df6a32011-08-24 10:31:13 -07003484 && rcClient.equals(rcse.mRcClient)) {
3485 // we found the IRemoteControlClient to unregister
3486 // stop monitoring its death
3487 rcse.unlinkToRcClientDeath();
3488 // reset the client-related fields
3489 rcse.mRcClient = null;
Jean-Michel Trivi4b6df6a32011-08-24 10:31:13 -07003490 rcse.mRcClientDeathHandler = null;
3491 rcse.mCallingPackageName = null;
3492 }
3493 }
3494 }
3495 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003496 }
3497
3498 /**
3499 * The remote control displays.
3500 * Access synchronized on mRCStack
3501 * NOTE: Only one IRemoteControlDisplay supported in this implementation
3502 */
3503 private IRemoteControlDisplay mRcDisplay;
3504 private RcDisplayDeathHandler mRcDisplayDeathHandler;
3505 private int mArtworkExpectedWidth = -1;
3506 private int mArtworkExpectedHeight = -1;
3507 /**
3508 * Inner class to monitor remote control display deaths, and unregister them from the list
3509 * of displays if necessary.
3510 */
3511 private class RcDisplayDeathHandler implements IBinder.DeathRecipient {
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07003512 private IBinder mCb; // To be notified of client's death
3513
3514 public RcDisplayDeathHandler(IBinder b) {
3515 if (DEBUG_RC) Log.i(TAG, "new RcDisplayDeathHandler for "+b);
3516 mCb = b;
3517 }
3518
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003519 public void binderDied() {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003520 synchronized(mRCStack) {
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07003521 Log.w(TAG, "RemoteControl: display died");
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003522 mRcDisplay = null;
3523 }
3524 }
3525
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07003526 public void unlinkToRcDisplayDeath() {
3527 if (DEBUG_RC) Log.i(TAG, "unlinkToRcDisplayDeath for "+mCb);
3528 try {
3529 mCb.unlinkToDeath(this, 0);
3530 } catch (java.util.NoSuchElementException e) {
3531 // not much we can do here, the display was being unregistered anyway
3532 Log.e(TAG, "Encountered " + e + " in unlinkToRcDisplayDeath()");
3533 e.printStackTrace();
3534 }
3535 }
3536
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003537 }
3538
3539 private void rcDisplay_stopDeathMonitor_syncRcStack() {
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07003540 if (mRcDisplay != null) { // implies (mRcDisplayDeathHandler != null)
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003541 // we had a display before, stop monitoring its death
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07003542 mRcDisplayDeathHandler.unlinkToRcDisplayDeath();
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003543 }
3544 }
3545
3546 private void rcDisplay_startDeathMonitor_syncRcStack() {
3547 if (mRcDisplay != null) {
3548 // new non-null display, monitor its death
3549 IBinder b = mRcDisplay.asBinder();
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07003550 mRcDisplayDeathHandler = new RcDisplayDeathHandler(b);
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003551 try {
3552 b.linkToDeath(mRcDisplayDeathHandler, 0);
3553 } catch (RemoteException e) {
3554 // remote control display is DOA, disqualify it
3555 Log.w(TAG, "registerRemoteControlDisplay() has a dead client " + b);
3556 mRcDisplay = null;
3557 }
3558 }
3559 }
3560
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07003561 /**
Jean-Michel Trivi958bba62011-08-30 10:52:59 -07003562 * Register an IRemoteControlDisplay.
3563 * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient
3564 * at the top of the stack to update the new display with its information.
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07003565 * Since only one IRemoteControlDisplay is supported, this will unregister the previous display.
3566 * @param rcd the IRemoteControlDisplay to register. No effect if null.
3567 */
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003568 public void registerRemoteControlDisplay(IRemoteControlDisplay rcd) {
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07003569 if (DEBUG_RC) Log.d(TAG, ">>> registerRemoteControlDisplay("+rcd+")");
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003570 synchronized(mAudioFocusLock) {
3571 synchronized(mRCStack) {
3572 if ((mRcDisplay == rcd) || (rcd == null)) {
3573 return;
3574 }
3575 // if we had a display before, stop monitoring its death
3576 rcDisplay_stopDeathMonitor_syncRcStack();
3577 mRcDisplay = rcd;
3578 // new display, start monitoring its death
3579 rcDisplay_startDeathMonitor_syncRcStack();
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003580
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003581 // let all the remote control clients there is a new display
3582 // no need to unplug the previous because we only support one display
3583 // and the clients don't track the death of the display
3584 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3585 while(stackIterator.hasNext()) {
3586 RemoteControlStackEntry rcse = stackIterator.next();
3587 if(rcse.mRcClient != null) {
3588 try {
3589 rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
3590 } catch (RemoteException e) {
3591 Log.e(TAG, "Error connecting remote control display to client: " + e);
3592 e.printStackTrace();
3593 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003594 }
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003595 }
Jean-Michel Trividb8a3612011-08-26 19:10:51 -07003596
Jean-Michel Trivi33879452011-08-31 11:42:17 -07003597 // we have a new display, of which all the clients are now aware: have it be updated
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003598 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
Jean-Michel Trivi33879452011-08-31 11:42:17 -07003599 }
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003600 }
3601 }
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003602
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07003603 /**
3604 * Unregister an IRemoteControlDisplay.
3605 * Since only one IRemoteControlDisplay is supported, this has no effect if the one to
3606 * unregister is not the current one.
3607 * @param rcd the IRemoteControlDisplay to unregister. No effect if null.
3608 */
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003609 public void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) {
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07003610 if (DEBUG_RC) Log.d(TAG, "<<< unregisterRemoteControlDisplay("+rcd+")");
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003611 synchronized(mRCStack) {
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07003612 // only one display here, so you can only unregister the current display
3613 if ((rcd == null) || (rcd != mRcDisplay)) {
3614 if (DEBUG_RC) Log.w(TAG, " trying to unregister unregistered RCD");
3615 return;
3616 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003617 // if we had a display before, stop monitoring its death
3618 rcDisplay_stopDeathMonitor_syncRcStack();
3619 mRcDisplay = null;
3620
3621 // disconnect this remote control display from all the clients
3622 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3623 while(stackIterator.hasNext()) {
3624 RemoteControlStackEntry rcse = stackIterator.next();
3625 if(rcse.mRcClient != null) {
3626 try {
3627 rcse.mRcClient.unplugRemoteControlDisplay(rcd);
3628 } catch (RemoteException e) {
3629 Log.e(TAG, "Error disconnecting remote control display to client: " + e);
3630 e.printStackTrace();
3631 }
3632 }
3633 }
3634 }
3635 }
3636
3637 public void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) {
3638 synchronized(mRCStack) {
3639 // NOTE: Only one IRemoteControlDisplay supported in this implementation
3640 mArtworkExpectedWidth = w;
3641 mArtworkExpectedHeight = h;
3642 }
3643 }
3644
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003645 @Override
3646 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003647 // TODO probably a lot more to do here than just the audio focus and remote control stacks
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003648 dumpFocusStack(pw);
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003649 dumpRCStack(pw);
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003650 }
3651
3652
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003653}