blob: dc9496fe2282845533265c0d7c23617aad14735f [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
Jeff Sharkey098d5802012-04-26 17:30:34 -070019import static android.Manifest.permission.REMOTE_AUDIO_PLAYBACK;
Amith Yamasanic696a532011-10-28 17:02:37 -070020import static android.media.AudioManager.RINGER_MODE_NORMAL;
21import static android.media.AudioManager.RINGER_MODE_SILENT;
22import static android.media.AudioManager.RINGER_MODE_VIBRATE;
23
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.app.ActivityManagerNative;
Amith Yamasani6243edd2011-12-05 19:58:48 -080025import android.app.KeyguardManager;
Jean-Michel Trivif0cff042011-09-14 18:11:09 -070026import android.app.PendingIntent;
27import android.app.PendingIntent.CanceledException;
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -070028import android.bluetooth.BluetoothA2dp;
29import android.bluetooth.BluetoothAdapter;
30import android.bluetooth.BluetoothClass;
31import android.bluetooth.BluetoothDevice;
32import android.bluetooth.BluetoothHeadset;
33import android.bluetooth.BluetoothProfile;
Nick Pellybd022f42009-08-14 18:33:38 -070034import android.content.BroadcastReceiver;
Jean-Michel Trivid327f212010-03-16 21:44:33 -070035import android.content.ComponentName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.content.ContentResolver;
37import android.content.Context;
38import android.content.Intent;
Eric Laurenta553c252009-07-17 12:17:14 -070039import android.content.IntentFilter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.content.pm.PackageManager;
Jason Parekhb1096152009-03-24 17:48:25 -070041import android.database.ContentObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.media.MediaPlayer.OnCompletionListener;
43import android.media.MediaPlayer.OnErrorListener;
44import android.os.Binder;
Jean-Michel Trivif0cff042011-09-14 18:11:09 -070045import android.os.Bundle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.os.Environment;
47import android.os.Handler;
48import android.os.IBinder;
49import android.os.Looper;
50import android.os.Message;
51import android.os.RemoteException;
52import android.os.ServiceManager;
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -070053import android.os.SystemProperties;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054import android.provider.Settings;
55import android.provider.Settings.System;
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -070056import android.telephony.PhoneStateListener;
57import android.telephony.TelephonyManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058import android.util.Log;
Jean-Michel Trivid327f212010-03-16 21:44:33 -070059import android.view.KeyEvent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060import android.view.VolumePanel;
61
62import com.android.internal.telephony.ITelephony;
63
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -080064import java.io.FileDescriptor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065import java.io.IOException;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -080066import java.io.PrintWriter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067import java.util.ArrayList;
Eric Laurentc42ac9d2009-07-29 08:53:03 -070068import java.util.HashMap;
69import java.util.Iterator;
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -070070import java.util.List;
Eric Laurentc42ac9d2009-07-29 08:53:03 -070071import java.util.Map;
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -070072import java.util.NoSuchElementException;
Eric Laurentc42ac9d2009-07-29 08:53:03 -070073import java.util.Set;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -080074import java.util.Stack;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075
76/**
77 * The implementation of the volume manager service.
78 * <p>
79 * This implementation focuses on delivering a responsive UI. Most methods are
80 * asynchronous to external calls. For example, the task of setting a volume
81 * will update our internal state, but in a separate thread will set the system
82 * volume and later persist to the database. Similarly, setting the ringer mode
83 * will update the state and broadcast a change and in a separate thread later
84 * persist the ringer mode.
85 *
86 * @hide
87 */
88public class AudioService extends IAudioService.Stub {
89
90 private static final String TAG = "AudioService";
91
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -070092 /** Debug remote control client/display feature */
Jean-Michel Trivi7ff866e2011-10-13 18:09:26 -070093 protected static final boolean DEBUG_RC = false;
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -070094
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 /** How long to delay before persisting a change in volume/ringer mode. */
RoboErik45edba12012-03-27 17:54:36 -070096 private static final int PERSIST_DELAY = 500;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097
98 private Context mContext;
99 private ContentResolver mContentResolver;
Joe Onoratoc7fcba42011-01-05 16:53:11 -0800100 private boolean mVoiceCapable;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -0800101
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102 /** The UI */
103 private VolumePanel mVolumePanel;
104
105 // sendMsg() flags
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106 /** If the msg is already queued, replace it with this one. */
107 private static final int SENDMSG_REPLACE = 0;
108 /** If the msg is already queued, ignore this one and leave the old. */
109 private static final int SENDMSG_NOOP = 1;
110 /** If the msg is already queued, queue this one and leave the old. */
111 private static final int SENDMSG_QUEUE = 2;
112
113 // AudioHandler message.whats
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800114 private static final int MSG_SET_DEVICE_VOLUME = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 private static final int MSG_PERSIST_VOLUME = 1;
Mike Lockwood5c55a052011-12-15 17:21:44 -0500116 private static final int MSG_PERSIST_MASTER_VOLUME = 2;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117 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;
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800129 private static final int MSG_SET_ALL_VOLUMES = 15;
Justin Koh6a8096d2012-04-04 14:50:27 -0700130 private static final int MSG_PERSIST_MASTER_VOLUME_MUTE = 16;
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -0700131
Eric Laurentafbb0472011-12-15 09:04:23 -0800132
133 // flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be
134 // persisted
135 private static final int PERSIST_CURRENT = 0x1;
136 private static final int PERSIST_LAST_AUDIBLE = 0x2;
137
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -0700138 private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
Eric Laurentdc03c612011-04-01 10:59:41 -0700139 // Timeout for connection to bluetooth headset service
140 private static final int BT_HEADSET_CNCT_TIMEOUT_MS = 3000;
141
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800142 /** @see AudioSystemThread */
143 private AudioSystemThread mAudioSystemThread;
144 /** @see AudioHandler */
145 private AudioHandler mAudioHandler;
146 /** @see VolumeStreamState */
147 private VolumeStreamState[] mStreamStates;
Jason Parekhb1096152009-03-24 17:48:25 -0700148 private SettingsObserver mSettingsObserver;
Eric Laurenta553c252009-07-17 12:17:14 -0700149
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800150 private int mMode;
Glenn Kastenba195eb2011-12-13 09:30:40 -0800151 // protects mRingerMode
152 private final Object mSettingsLock = new Object();
Eric Laurent45c90ce2012-04-24 18:44:22 -0700153
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800154 private boolean mMediaServerOk;
155
156 private SoundPool mSoundPool;
Glenn Kasten30c918c2011-11-10 17:56:41 -0800157 private final Object mSoundEffectsLock = new Object();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800158 private static final int NUM_SOUNDPOOL_CHANNELS = 4;
159 private static final int SOUND_EFFECT_VOLUME = 1000;
160
Mike Lockwood47676902011-11-08 10:31:21 -0800161 // Internally master volume is a float in the 0.0 - 1.0 range,
162 // but to support integer based AudioManager API we translate it to 0 - 100
163 private static final int MAX_MASTER_VOLUME = 100;
164
Lei Zhang6c798972012-03-02 11:40:12 -0800165 // Maximum volume adjust steps allowed in a single batch call.
166 private static final int MAX_BATCH_VOLUME_ADJUST_STEPS = 4;
167
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 /* Sound effect file names */
169 private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/";
170 private static final String[] SOUND_EFFECT_FILES = new String[] {
171 "Effect_Tick.ogg",
172 "KeypressStandard.ogg",
173 "KeypressSpacebar.ogg",
174 "KeypressDelete.ogg",
175 "KeypressReturn.ogg"
176 };
177
178 /* Sound effect file name mapping sound effect id (AudioManager.FX_xxx) to
179 * file index in SOUND_EFFECT_FILES[] (first column) and indicating if effect
180 * uses soundpool (second column) */
Glenn Kasten30c918c2011-11-10 17:56:41 -0800181 private final int[][] SOUND_EFFECT_FILES_MAP = new int[][] {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800182 {0, -1}, // FX_KEY_CLICK
183 {0, -1}, // FX_FOCUS_NAVIGATION_UP
184 {0, -1}, // FX_FOCUS_NAVIGATION_DOWN
185 {0, -1}, // FX_FOCUS_NAVIGATION_LEFT
186 {0, -1}, // FX_FOCUS_NAVIGATION_RIGHT
187 {1, -1}, // FX_KEYPRESS_STANDARD
188 {2, -1}, // FX_KEYPRESS_SPACEBAR
189 {3, -1}, // FX_FOCUS_DELETE
190 {4, -1} // FX_FOCUS_RETURN
191 };
192
Jared Suttles59820132009-08-13 21:50:52 -0500193 /** @hide Maximum volume index values for audio streams */
Glenn Kasten30c918c2011-11-10 17:56:41 -0800194 private final int[] MAX_STREAM_VOLUME = new int[] {
Eric Laurent6ee99522009-08-25 06:30:59 -0700195 5, // STREAM_VOICE_CALL
196 7, // STREAM_SYSTEM
197 7, // STREAM_RING
198 15, // STREAM_MUSIC
199 7, // STREAM_ALARM
200 7, // STREAM_NOTIFICATION
201 15, // STREAM_BLUETOOTH_SCO
202 7, // STREAM_SYSTEM_ENFORCED
203 15, // STREAM_DTMF
204 15 // STREAM_TTS
Jared Suttles59820132009-08-13 21:50:52 -0500205 };
Eric Laurent6d517662012-04-23 18:42:39 -0700206 /* mStreamVolumeAlias[] indicates for each stream if it uses the volume settings
Eric Laurenta553c252009-07-17 12:17:14 -0700207 * of another stream: This avoids multiplying the volume settings for hidden
208 * stream types that follow other stream behavior for volume settings
Eric Laurent6d517662012-04-23 18:42:39 -0700209 * NOTE: do not create loops in aliases!
210 * Some streams alias to different streams according to device category (phone or tablet) or
211 * use case (in call s off call...).See updateStreamVolumeAlias() for more details
212 * mStreamVolumeAlias contains the default aliases for a voice capable device (phone) and
213 * STREAM_VOLUME_ALIAS_NON_VOICE for a non voice capable device (tablet).*/
Glenn Kasten30c918c2011-11-10 17:56:41 -0800214 private final int[] STREAM_VOLUME_ALIAS = new int[] {
Eric Laurent6d517662012-04-23 18:42:39 -0700215 AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL
216 AudioSystem.STREAM_RING, // STREAM_SYSTEM
217 AudioSystem.STREAM_RING, // STREAM_RING
218 AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
219 AudioSystem.STREAM_ALARM, // STREAM_ALARM
220 AudioSystem.STREAM_RING, // STREAM_NOTIFICATION
221 AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO
222 AudioSystem.STREAM_RING, // STREAM_SYSTEM_ENFORCED
223 AudioSystem.STREAM_RING, // STREAM_DTMF
224 AudioSystem.STREAM_MUSIC // STREAM_TTS
Eric Laurenta553c252009-07-17 12:17:14 -0700225 };
Eric Laurent6d517662012-04-23 18:42:39 -0700226 private final int[] STREAM_VOLUME_ALIAS_NON_VOICE = new int[] {
227 AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL
228 AudioSystem.STREAM_MUSIC, // STREAM_SYSTEM
229 AudioSystem.STREAM_RING, // STREAM_RING
230 AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
231 AudioSystem.STREAM_ALARM, // STREAM_ALARM
232 AudioSystem.STREAM_RING, // STREAM_NOTIFICATION
233 AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO
234 AudioSystem.STREAM_MUSIC, // STREAM_SYSTEM_ENFORCED
235 AudioSystem.STREAM_MUSIC, // STREAM_DTMF
236 AudioSystem.STREAM_MUSIC // STREAM_TTS
237 };
238 private int[] mStreamVolumeAlias;
Eric Laurenta553c252009-07-17 12:17:14 -0700239
Glenn Kasten30c918c2011-11-10 17:56:41 -0800240 private final AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800241 public void onError(int error) {
242 switch (error) {
243 case AudioSystem.AUDIO_STATUS_SERVER_DIED:
244 if (mMediaServerOk) {
Eric Laurentafbb0472011-12-15 09:04:23 -0800245 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SENDMSG_NOOP, 0, 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800246 null, 1500);
Eric Laurent89e74ba2009-09-30 18:26:36 -0700247 mMediaServerOk = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800248 }
249 break;
250 case AudioSystem.AUDIO_STATUS_OK:
251 if (!mMediaServerOk) {
Eric Laurentafbb0472011-12-15 09:04:23 -0800252 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_STARTED, SENDMSG_NOOP, 0, 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800253 null, 0);
Eric Laurent89e74ba2009-09-30 18:26:36 -0700254 mMediaServerOk = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800255 }
256 break;
257 default:
258 break;
259 }
260 }
261 };
262
263 /**
264 * Current ringer mode from one of {@link AudioManager#RINGER_MODE_NORMAL},
265 * {@link AudioManager#RINGER_MODE_SILENT}, or
266 * {@link AudioManager#RINGER_MODE_VIBRATE}.
267 */
Glenn Kastenba195eb2011-12-13 09:30:40 -0800268 // protected by mSettingsLock
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800269 private int mRingerMode;
270
Eric Laurent9bcf4012009-06-12 06:09:28 -0700271 /** @see System#MODE_RINGER_STREAMS_AFFECTED */
272 private int mRingerModeAffectedStreams;
273
Eric Laurent5b4e6542010-03-19 20:02:21 -0700274 // Streams currently muted by ringer mode
275 private int mRingerModeMutedStreams;
276
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277 /** @see System#MUTE_STREAMS_AFFECTED */
278 private int mMuteAffectedStreams;
279
280 /**
281 * Has multiple bits per vibrate type to indicate the type's vibrate
282 * setting. See {@link #setVibrateSetting(int, int)}.
283 * <p>
284 * NOTE: This is not the final decision of whether vibrate is on/off for the
285 * type since it depends on the ringer mode. See {@link #shouldVibrate(int)}.
286 */
287 private int mVibrateSetting;
288
Eric Laurenta553c252009-07-17 12:17:14 -0700289 // Broadcast receiver for device connections intent broadcasts
290 private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
291
Jean-Michel Trivid327f212010-03-16 21:44:33 -0700292 // Broadcast receiver for media button broadcasts (separate from mReceiver to
293 // independently change its priority)
294 private final BroadcastReceiver mMediaButtonReceiver = new MediaButtonBroadcastReceiver();
295
Jean-Michel Trivie73131a2010-06-14 09:53:30 -0700296 // Used to alter media button redirection when the phone is ringing.
297 private boolean mIsRinging = false;
298
Eric Laurentc42ac9d2009-07-29 08:53:03 -0700299 // Devices currently connected
Glenn Kasten30c918c2011-11-10 17:56:41 -0800300 private final HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>();
Eric Laurentc42ac9d2009-07-29 08:53:03 -0700301
302 // Forced device usage for communications
303 private int mForcedUseForComm;
304
Mike Lockwood0dc37cc2011-12-01 16:14:19 -0500305 // True if we have master volume support
306 private final boolean mUseMasterVolume;
307
Mike Lockwood97606472012-02-09 11:24:10 -0800308 private final int[] mMasterVolumeRamp;
309
Eric Laurent9272b4b2010-01-23 17:12:59 -0800310 // List of binder death handlers for setMode() client processes.
311 // The last process to have called setMode() is at the top of the list.
Glenn Kasten30c918c2011-11-10 17:56:41 -0800312 private final ArrayList <SetModeDeathHandler> mSetModeDeathHandlers = new ArrayList <SetModeDeathHandler>();
Eric Laurenteb14a782009-12-17 03:12:59 -0800313
Eric Laurent3def1ee2010-03-17 23:26:26 -0700314 // List of clients having issued a SCO start request
Glenn Kasten30c918c2011-11-10 17:56:41 -0800315 private final ArrayList <ScoClient> mScoClients = new ArrayList <ScoClient>();
Eric Laurent3def1ee2010-03-17 23:26:26 -0700316
317 // BluetoothHeadset API to control SCO connection
318 private BluetoothHeadset mBluetoothHeadset;
319
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -0700320 // Bluetooth headset device
321 private BluetoothDevice mBluetoothHeadsetDevice;
Eric Laurent3def1ee2010-03-17 23:26:26 -0700322
Eric Laurent62ef7672010-11-24 10:58:32 -0800323 // Indicate if SCO audio connection is currently active and if the initiator is
324 // audio service (internal) or bluetooth headset (external)
325 private int mScoAudioState;
326 // SCO audio state is not active
327 private static final int SCO_STATE_INACTIVE = 0;
Eric Laurentdc03c612011-04-01 10:59:41 -0700328 // SCO audio activation request waiting for headset service to connect
329 private static final int SCO_STATE_ACTIVATE_REQ = 1;
Eric Laurent62ef7672010-11-24 10:58:32 -0800330 // SCO audio state is active or starting due to a local request to start a virtual call
Eric Laurentdc03c612011-04-01 10:59:41 -0700331 private static final int SCO_STATE_ACTIVE_INTERNAL = 3;
332 // SCO audio deactivation request waiting for headset service to connect
333 private static final int SCO_STATE_DEACTIVATE_REQ = 5;
334
Eric Laurent62ef7672010-11-24 10:58:32 -0800335 // SCO audio state is active due to an action in BT handsfree (either voice recognition or
336 // in call audio)
337 private static final int SCO_STATE_ACTIVE_EXTERNAL = 2;
Eric Laurentdc03c612011-04-01 10:59:41 -0700338 // Deactivation request for all SCO connections (initiated by audio mode change)
339 // waiting for headset service to connect
340 private static final int SCO_STATE_DEACTIVATE_EXT_REQ = 4;
341
342 // Current connection state indicated by bluetooth headset
343 private int mScoConnectionState;
Eric Laurent62ef7672010-11-24 10:58:32 -0800344
Eric Laurenta60e2122010-12-28 16:49:07 -0800345 // true if boot sequence has been completed
346 private boolean mBootCompleted;
347 // listener for SoundPool sample load completion indication
348 private SoundPoolCallback mSoundPoolCallBack;
349 // thread for SoundPool listener
350 private SoundPoolListenerThread mSoundPoolListenerThread;
351 // message looper for SoundPool listener
352 private Looper mSoundPoolLooper = null;
Eric Laurent25101b02011-02-02 09:33:30 -0800353 // default volume applied to sound played with playSoundEffect()
Eric Laurent6d517662012-04-23 18:42:39 -0700354 private static final int SOUND_EFFECT_DEFAULT_VOLUME_DB = 0;
Eric Laurent25101b02011-02-02 09:33:30 -0800355 // volume applied to sound played with playSoundEffect() read from ro.config.sound_fx_volume
356 private int SOUND_EFFECT_VOLUME_DB;
357 // getActiveStreamType() will return STREAM_NOTIFICATION during this period after a notification
358 // stopped
359 private static final int NOTIFICATION_VOLUME_DELAY_MS = 5000;
360 // previous volume adjustment direction received by checkForRingerModeChange()
361 private int mPrevVolDirection = AudioManager.ADJUST_SAME;
Amith Yamasani6243edd2011-12-05 19:58:48 -0800362 // Keyguard manager proxy
363 private KeyguardManager mKeyguardManager;
Eric Laurent45c90ce2012-04-24 18:44:22 -0700364 // mVolumeControlStream is set by VolumePanel to temporarily force the stream type which volume
365 // is controlled by Vol keys.
366 private int mVolumeControlStream = -1;
367 private final Object mForceControlStreamLock = new Object();
368 // VolumePanel is currently the only client of forceVolumeControlStream() and runs in system
369 // server process so in theory it is not necessary to monitor the client death.
370 // However it is good to be ready for future evolutions.
371 private ForceControlStreamClient mForceControlStreamClient = null;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700372 // Used to play ringtones outside system_server
373 private volatile IRingtonePlayer mRingtonePlayer;
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800374
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375 ///////////////////////////////////////////////////////////////////////////
376 // Construction
377 ///////////////////////////////////////////////////////////////////////////
378
379 /** @hide */
380 public AudioService(Context context) {
381 mContext = context;
382 mContentResolver = context.getContentResolver();
Joe Onoratoc7fcba42011-01-05 16:53:11 -0800383 mVoiceCapable = mContext.getResources().getBoolean(
384 com.android.internal.R.bool.config_voice_capable);
Jared Suttles59820132009-08-13 21:50:52 -0500385
386 // Intialized volume
387 MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = SystemProperties.getInt(
388 "ro.config.vc_call_vol_steps",
389 MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL]);
390
Eric Laurent25101b02011-02-02 09:33:30 -0800391 SOUND_EFFECT_VOLUME_DB = SystemProperties.getInt(
392 "ro.config.sound_fx_volume",
393 SOUND_EFFECT_DEFAULT_VOLUME_DB);
394
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800395 mVolumePanel = new VolumePanel(context, this);
Eric Laurent6d517662012-04-23 18:42:39 -0700396 mMode = AudioSystem.MODE_NORMAL;
Eric Laurentc42ac9d2009-07-29 08:53:03 -0700397 mForcedUseForComm = AudioSystem.FORCE_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 createAudioSystemThread();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399 readPersistedSettings();
Eric Laurentc1d41662011-07-19 11:21:13 -0700400 mSettingsObserver = new SettingsObserver();
Eric Laurent6d517662012-04-23 18:42:39 -0700401 updateStreamVolumeAlias(false /*updateVolumes*/);
Eric Laurenta553c252009-07-17 12:17:14 -0700402 createStreamStates();
Eric Laurent9f103de2011-09-08 15:04:23 -0700403
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800404 mMediaServerOk = true;
Eric Laurent3891c4c2010-04-20 09:40:57 -0700405
406 // Call setRingerModeInt() to apply correct mute
407 // state on streams affected by ringer mode.
408 mRingerModeMutedStreams = 0;
409 setRingerModeInt(getRingerMode(), false);
410
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800411 AudioSystem.setErrorCallback(mAudioSystemCallback);
Eric Laurenta553c252009-07-17 12:17:14 -0700412
413 // Register for device connection intent broadcasts.
414 IntentFilter intentFilter =
415 new IntentFilter(Intent.ACTION_HEADSET_PLUG);
Praveen Bharathi21e941b2010-10-06 15:23:14 -0500416
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -0700417 intentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
Eric Laurent3def1ee2010-03-17 23:26:26 -0700418 intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -0700419 intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
420 intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
Eric Laurent59f48272012-04-05 19:42:21 -0700421 intentFilter.addAction(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG);
422 intentFilter.addAction(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG);
Praveen Bharathi26e37342010-11-02 19:23:30 -0700423 intentFilter.addAction(Intent.ACTION_HDMI_AUDIO_PLUG);
Eric Laurent59f48272012-04-05 19:42:21 -0700424 intentFilter.addAction(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG);
425 intentFilter.addAction(Intent.ACTION_USB_AUDIO_DEVICE_PLUG);
Eric Laurent62ef7672010-11-24 10:58:32 -0800426 intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
Eric Laurent950e8cb2011-10-13 08:57:54 -0700427 intentFilter.addAction(Intent.ACTION_SCREEN_ON);
428 intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
Eric Laurenta553c252009-07-17 12:17:14 -0700429 context.registerReceiver(mReceiver, intentFilter);
Jared Suttles59820132009-08-13 21:50:52 -0500430
Jean-Michel Trivid589fea2011-04-15 11:28:10 -0700431 // Register for package removal intent broadcasts for media button receiver persistence
432 IntentFilter pkgFilter = new IntentFilter();
433 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
434 pkgFilter.addDataScheme("package");
435 context.registerReceiver(mReceiver, pkgFilter);
436
Jean-Michel Trivid327f212010-03-16 21:44:33 -0700437 // Register for media button intent broadcasts.
438 intentFilter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -0700439 // Workaround for bug on priority setting
440 //intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
441 intentFilter.setPriority(Integer.MAX_VALUE);
Jean-Michel Trivid327f212010-03-16 21:44:33 -0700442 context.registerReceiver(mMediaButtonReceiver, intentFilter);
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -0700443
444 // Register for phone state monitoring
445 TelephonyManager tmgr = (TelephonyManager)
446 context.getSystemService(Context.TELEPHONY_SERVICE);
447 tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
Mike Lockwood8517e462011-10-25 14:47:19 -0400448
Mike Lockwood0dc37cc2011-12-01 16:14:19 -0500449 mUseMasterVolume = context.getResources().getBoolean(
450 com.android.internal.R.bool.config_useMasterVolume);
Mike Lockwood90631542012-01-06 11:20:37 -0500451 restoreMasterVolume();
Mike Lockwood97606472012-02-09 11:24:10 -0800452
453 mMasterVolumeRamp = context.getResources().getIntArray(
454 com.android.internal.R.array.config_masterVolumeRamp);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455 }
456
457 private void createAudioSystemThread() {
458 mAudioSystemThread = new AudioSystemThread();
459 mAudioSystemThread.start();
460 waitForAudioHandlerCreation();
461 }
462
463 /** Waits for the volume handler to be created by the other thread. */
464 private void waitForAudioHandlerCreation() {
465 synchronized(this) {
466 while (mAudioHandler == null) {
467 try {
468 // Wait for mAudioHandler to be set by the other thread
469 wait();
470 } catch (InterruptedException e) {
471 Log.e(TAG, "Interrupted while waiting on volume handler.");
472 }
473 }
474 }
475 }
476
477 private void createStreamStates() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478 int numStreamTypes = AudioSystem.getNumStreamTypes();
479 VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
480
481 for (int i = 0; i < numStreamTypes; i++) {
Eric Laurent6d517662012-04-23 18:42:39 -0700482 streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[mStreamVolumeAlias[i]], i);
Eric Laurenta553c252009-07-17 12:17:14 -0700483 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800484
Eric Laurenta553c252009-07-17 12:17:14 -0700485 // Correct stream index values for streams with aliases
486 for (int i = 0; i < numStreamTypes; i++) {
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800487 int device = getDeviceForStream(i);
Eric Laurent6d517662012-04-23 18:42:39 -0700488 if (mStreamVolumeAlias[i] != i) {
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800489 int index = rescaleIndex(streams[i].getIndex(device, false /* lastAudible */),
Eric Laurent6d517662012-04-23 18:42:39 -0700490 mStreamVolumeAlias[i],
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800491 i);
492 streams[i].mIndex.put(device, streams[i].getValidIndex(index));
493 streams[i].applyDeviceVolume(device);
494 index = rescaleIndex(streams[i].getIndex(device, true /* lastAudible */),
Eric Laurent6d517662012-04-23 18:42:39 -0700495 mStreamVolumeAlias[i],
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800496 i);
497 streams[i].mLastAudibleIndex.put(device, streams[i].getValidIndex(index));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800498 }
499 }
500 }
501
Eric Laurent6d517662012-04-23 18:42:39 -0700502
503 private void updateStreamVolumeAlias(boolean updateVolumes) {
504 int dtmfStreamAlias;
505 if (mVoiceCapable) {
506 mStreamVolumeAlias = STREAM_VOLUME_ALIAS;
507 dtmfStreamAlias = AudioSystem.STREAM_RING;
508 } else {
509 mStreamVolumeAlias = STREAM_VOLUME_ALIAS_NON_VOICE;
510 dtmfStreamAlias = AudioSystem.STREAM_MUSIC;
511 }
512 if (isInCommunication()) {
513 dtmfStreamAlias = AudioSystem.STREAM_VOICE_CALL;
514 }
515 mStreamVolumeAlias[AudioSystem.STREAM_DTMF] = dtmfStreamAlias;
516 if (updateVolumes) {
517 mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias],
518 false /*lastAudible*/);
519 mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias],
520 true /*lastAudible*/);
521 sendMsg(mAudioHandler,
522 MSG_SET_ALL_VOLUMES,
523 SENDMSG_QUEUE,
524 0,
525 0,
526 mStreamStates[AudioSystem.STREAM_DTMF], 0);
527 }
528 }
529
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800530 private void readPersistedSettings() {
531 final ContentResolver cr = mContentResolver;
532
Glenn Kastenba195eb2011-12-13 09:30:40 -0800533 int ringerMode = System.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);
Eric Laurent72668b22011-07-19 16:04:27 -0700534 // sanity check in case the settings are restored from a device with incompatible
535 // ringer modes
Glenn Kastenba195eb2011-12-13 09:30:40 -0800536 if (!AudioManager.isValidRingerMode(ringerMode)) {
537 ringerMode = AudioManager.RINGER_MODE_NORMAL;
538 System.putInt(cr, System.MODE_RINGER, ringerMode);
539 }
540 synchronized(mSettingsLock) {
541 mRingerMode = ringerMode;
Eric Laurent72668b22011-07-19 16:04:27 -0700542 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800543
544 mVibrateSetting = System.getInt(cr, System.VIBRATE_ON, 0);
545
Eric Laurentc1d41662011-07-19 11:21:13 -0700546 // make sure settings for ringer mode are consistent with device type: non voice capable
547 // devices (tablets) include media stream in silent mode whereas phones don't.
Eric Laurent9bcf4012009-06-12 06:09:28 -0700548 mRingerModeAffectedStreams = Settings.System.getInt(cr,
549 Settings.System.MODE_RINGER_STREAMS_AFFECTED,
Eric Laurenta553c252009-07-17 12:17:14 -0700550 ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
Eric Laurentc1d41662011-07-19 11:21:13 -0700551 (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)));
552 if (mVoiceCapable) {
553 mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
554 } else {
Eric Laurent402f7f22011-02-04 12:30:32 -0800555 mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
556 }
Eric Laurentc1d41662011-07-19 11:21:13 -0700557 Settings.System.putInt(cr,
558 Settings.System.MODE_RINGER_STREAMS_AFFECTED, mRingerModeAffectedStreams);
559
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800560 mMuteAffectedStreams = System.getInt(cr,
561 System.MUTE_STREAMS_AFFECTED,
562 ((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM)));
563
Justin Koh57978ed2012-04-03 17:37:58 -0700564 boolean masterMute = System.getInt(cr, System.VOLUME_MASTER_MUTE, 0) == 1;
565 AudioSystem.setMasterMute(masterMute);
566 broadcastMasterMuteStatus(masterMute);
567
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800568 // Each stream will read its own persisted settings
569
570 // Broadcast the sticky intent
Glenn Kastenba195eb2011-12-13 09:30:40 -0800571 broadcastRingerMode(ringerMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800572
573 // Broadcast vibrate settings
574 broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
575 broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION);
Jean-Michel Trivid589fea2011-04-15 11:28:10 -0700576
577 // Restore the default media button receiver from the system settings
578 restoreMediaButtonReceiver();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800579 }
580
Eric Laurenta553c252009-07-17 12:17:14 -0700581 private int rescaleIndex(int index, int srcStream, int dstStream) {
582 return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex();
583 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800584
585 ///////////////////////////////////////////////////////////////////////////
586 // IPC methods
587 ///////////////////////////////////////////////////////////////////////////
588
589 /** @see AudioManager#adjustVolume(int, int) */
590 public void adjustVolume(int direction, int flags) {
591 adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags);
592 }
593
594 /** @see AudioManager#adjustVolume(int, int, int) */
595 public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
596
Eric Laurent402f7f22011-02-04 12:30:32 -0800597 int streamType;
Eric Laurent45c90ce2012-04-24 18:44:22 -0700598 if (mVolumeControlStream != -1) {
599 streamType = mVolumeControlStream;
Eric Laurent402f7f22011-02-04 12:30:32 -0800600 } else {
601 streamType = getActiveStreamType(suggestedStreamType);
602 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800603
Amith Yamasani6243edd2011-12-05 19:58:48 -0800604 // Play sounds on STREAM_RING only and if lock screen is not on.
Eric Laurent96f218e2011-08-15 15:55:21 -0700605 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 &&
Eric Laurent6d517662012-04-23 18:42:39 -0700606 ((mStreamVolumeAlias[streamType] != AudioSystem.STREAM_RING)
Amith Yamasani6243edd2011-12-05 19:58:48 -0800607 || (mKeyguardManager != null && mKeyguardManager.isKeyguardLocked()))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800608 flags &= ~AudioManager.FLAG_PLAY_SOUND;
609 }
610
611 adjustStreamVolume(streamType, direction, flags);
612 }
613
614 /** @see AudioManager#adjustStreamVolume(int, int, int) */
615 public void adjustStreamVolume(int streamType, int direction, int flags) {
616 ensureValidDirection(direction);
617 ensureValidStreamType(streamType);
618
Eric Laurent96a33d12011-11-08 10:31:57 -0800619 // use stream type alias here so that streams with same alias have the same behavior,
620 // including with regard to silent mode control (e.g the use of STREAM_RING below and in
621 // checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION)
Eric Laurent6d517662012-04-23 18:42:39 -0700622 int streamTypeAlias = mStreamVolumeAlias[streamType];
Eric Laurentb024c302011-10-14 17:19:27 -0700623 VolumeStreamState streamState = mStreamStates[streamTypeAlias];
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800624
625 final int device = getDeviceForStream(streamTypeAlias);
626 // get last audible index if stream is muted, current index otherwise
627 final int oldIndex = streamState.getIndex(device,
628 (streamState.muteCount() != 0) /* lastAudible */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800629 boolean adjustVolume = true;
630
631 // If either the client forces allowing ringer modes for this adjustment,
632 // or the stream type is one that is affected by ringer modes
Eric Laurent25101b02011-02-02 09:33:30 -0800633 if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
Eric Laurent96a33d12011-11-08 10:31:57 -0800634 streamTypeAlias == AudioSystem.STREAM_RING ||
635 (!mVoiceCapable && streamTypeAlias == AudioSystem.STREAM_MUSIC)) {
Glenn Kastenba195eb2011-12-13 09:30:40 -0800636 int ringerMode = getRingerMode();
Amith Yamasanic696a532011-10-28 17:02:37 -0700637 // do not vibrate if already in vibrate mode
Glenn Kastenba195eb2011-12-13 09:30:40 -0800638 if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
Eric Laurentcc11b1b2011-08-17 12:42:07 -0700639 flags &= ~AudioManager.FLAG_VIBRATE;
640 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800641 // Check if the ringer mode changes with this volume adjustment. If
642 // it does, it will handle adjusting the volume, so we won't below
Eric Laurentb024c302011-10-14 17:19:27 -0700643 adjustVolume = checkForRingerModeChange(oldIndex, direction, streamTypeAlias);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800644 }
645
Eric Laurent5b4e6542010-03-19 20:02:21 -0700646 // If stream is muted, adjust last audible index only
647 int index;
648 if (streamState.muteCount() != 0) {
649 if (adjustVolume) {
Eric Laurent96a33d12011-11-08 10:31:57 -0800650 // adjust volume on all stream types sharing the same alias otherwise a query
651 // on last audible index for an alias would not give the correct value
652 int numStreamTypes = AudioSystem.getNumStreamTypes();
653 for (int i = numStreamTypes - 1; i >= 0; i--) {
Eric Laurent6d517662012-04-23 18:42:39 -0700654 if (mStreamVolumeAlias[i] == streamTypeAlias) {
Eric Laurent96a33d12011-11-08 10:31:57 -0800655 VolumeStreamState s = mStreamStates[i];
656
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800657 s.adjustLastAudibleIndex(direction, device);
Eric Laurent96a33d12011-11-08 10:31:57 -0800658 // Post a persist volume msg
Eric Laurentafbb0472011-12-15 09:04:23 -0800659 sendMsg(mAudioHandler,
660 MSG_PERSIST_VOLUME,
Eric Laurent98ad9b92012-02-15 17:21:37 -0800661 SENDMSG_QUEUE,
Eric Laurentafbb0472011-12-15 09:04:23 -0800662 PERSIST_LAST_AUDIBLE,
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800663 device,
Eric Laurentafbb0472011-12-15 09:04:23 -0800664 s,
665 PERSIST_DELAY);
Eric Laurent96a33d12011-11-08 10:31:57 -0800666 }
667 }
Eric Laurent5b4e6542010-03-19 20:02:21 -0700668 }
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800669 index = streamState.getIndex(device, true /* lastAudible */);
Eric Laurent5b4e6542010-03-19 20:02:21 -0700670 } else {
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800671 if (adjustVolume && streamState.adjustIndex(direction, device)) {
Eric Laurent5b4e6542010-03-19 20:02:21 -0700672 // Post message to set system volume (it in turn will post a message
673 // to persist). Do not change volume if stream is muted.
Eric Laurentafbb0472011-12-15 09:04:23 -0800674 sendMsg(mAudioHandler,
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800675 MSG_SET_DEVICE_VOLUME,
Eric Laurent98ad9b92012-02-15 17:21:37 -0800676 SENDMSG_QUEUE,
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800677 device,
Eric Laurentafbb0472011-12-15 09:04:23 -0800678 0,
679 streamState,
680 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800681 }
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800682 index = streamState.getIndex(device, false /* lastAudible */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800683 }
Eric Laurent25101b02011-02-02 09:33:30 -0800684
685 sendVolumeUpdate(streamType, oldIndex, index, flags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800686 }
687
Mike Lockwoodcbdb49d2011-10-20 12:54:05 -0400688 /** @see AudioManager#adjustMasterVolume(int) */
Lei Zhang6c798972012-03-02 11:40:12 -0800689 public void adjustMasterVolume(int steps, int flags) {
690 ensureValidSteps(steps);
Mike Lockwood97606472012-02-09 11:24:10 -0800691 int volume = Math.round(AudioSystem.getMasterVolume() * MAX_MASTER_VOLUME);
692 int delta = 0;
Lei Zhang6c798972012-03-02 11:40:12 -0800693 int numSteps = Math.abs(steps);
694 int direction = steps > 0 ? AudioManager.ADJUST_RAISE : AudioManager.ADJUST_LOWER;
695 for (int i = 0; i < numSteps; ++i) {
696 delta = findVolumeDelta(direction, volume);
697 volume += delta;
Mike Lockwoodcbdb49d2011-10-20 12:54:05 -0400698 }
RoboErik24b082f2012-02-24 14:21:16 -0800699
Lei Zhang6c798972012-03-02 11:40:12 -0800700 //Log.d(TAG, "adjustMasterVolume volume: " + volume + " steps: " + steps);
701 setMasterVolume(volume, flags);
Mike Lockwoodcbdb49d2011-10-20 12:54:05 -0400702 }
703
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800704 /** @see AudioManager#setStreamVolume(int, int, int) */
705 public void setStreamVolume(int streamType, int index, int flags) {
706 ensureValidStreamType(streamType);
Eric Laurent6d517662012-04-23 18:42:39 -0700707 VolumeStreamState streamState = mStreamStates[mStreamVolumeAlias[streamType]];
Eric Laurent9ce379a2010-02-16 06:00:26 -0800708
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800709 final int device = getDeviceForStream(streamType);
710 // get last audible index if stream is muted, current index otherwise
711 final int oldIndex = streamState.getIndex(device,
712 (streamState.muteCount() != 0) /* lastAudible */);
Eric Laurent9ce379a2010-02-16 06:00:26 -0800713
Eric Laurentb024c302011-10-14 17:19:27 -0700714 // setting ring or notifications volume to 0 on voice capable devices enters silent mode
715 if (mVoiceCapable && (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
Eric Laurent6d517662012-04-23 18:42:39 -0700716 (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_RING))) {
Glenn Kastenba195eb2011-12-13 09:30:40 -0800717 int newRingerMode;
Eric Laurentb024c302011-10-14 17:19:27 -0700718 if (index == 0) {
719 newRingerMode = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1
720 ? AudioManager.RINGER_MODE_VIBRATE
721 : AudioManager.RINGER_MODE_SILENT;
Eric Laurent6d517662012-04-23 18:42:39 -0700722 setStreamVolumeInt(mStreamVolumeAlias[streamType],
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800723 index,
724 device,
725 false,
726 true);
Eric Laurentb024c302011-10-14 17:19:27 -0700727 } else {
728 newRingerMode = AudioManager.RINGER_MODE_NORMAL;
729 }
Glenn Kastenba195eb2011-12-13 09:30:40 -0800730 setRingerMode(newRingerMode);
Eric Laurentb024c302011-10-14 17:19:27 -0700731 }
732
Eric Laurent6d517662012-04-23 18:42:39 -0700733 index = rescaleIndex(index * 10, streamType, mStreamVolumeAlias[streamType]);
734 setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, false, true);
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800735 // get last audible index if stream is muted, current index otherwise
736 index = streamState.getIndex(device,
737 (streamState.muteCount() != 0) /* lastAudible */);
Eric Laurent5b4e6542010-03-19 20:02:21 -0700738
Eric Laurent25101b02011-02-02 09:33:30 -0800739 sendVolumeUpdate(streamType, oldIndex, index, flags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800740 }
741
Eric Laurent45c90ce2012-04-24 18:44:22 -0700742 /** @see AudioManager#forceVolumeControlStream(int) */
743 public void forceVolumeControlStream(int streamType, IBinder cb) {
744 synchronized(mForceControlStreamLock) {
745 mVolumeControlStream = streamType;
746 if (mVolumeControlStream == -1) {
747 if (mForceControlStreamClient != null) {
748 mForceControlStreamClient.release();
749 mForceControlStreamClient = null;
750 }
751 } else {
752 mForceControlStreamClient = new ForceControlStreamClient(cb);
753 }
754 }
755 }
756
757 private class ForceControlStreamClient implements IBinder.DeathRecipient {
758 private IBinder mCb; // To be notified of client's death
759
760 ForceControlStreamClient(IBinder cb) {
761 if (cb != null) {
762 try {
763 cb.linkToDeath(this, 0);
764 } catch (RemoteException e) {
765 // Client has died!
766 Log.w(TAG, "ForceControlStreamClient() could not link to "+cb+" binder death");
767 cb = null;
768 }
769 }
770 mCb = cb;
771 }
772
773 public void binderDied() {
774 synchronized(mForceControlStreamLock) {
775 Log.w(TAG, "SCO client died");
776 if (mForceControlStreamClient != this) {
777 Log.w(TAG, "unregistered control stream client died");
778 } else {
779 mForceControlStreamClient = null;
780 mVolumeControlStream = -1;
781 }
782 }
783 }
784
785 public void release() {
786 if (mCb != null) {
787 mCb.unlinkToDeath(this, 0);
788 mCb = null;
789 }
790 }
791 }
792
Lei Zhang6c798972012-03-02 11:40:12 -0800793 private int findVolumeDelta(int direction, int volume) {
794 int delta = 0;
795 if (direction == AudioManager.ADJUST_RAISE) {
796 if (volume == MAX_MASTER_VOLUME) {
797 return 0;
798 }
799 // This is the default value if we make it to the end
800 delta = mMasterVolumeRamp[1];
801 // If we're raising the volume move down the ramp array until we
802 // find the volume we're above and use that groups delta.
803 for (int i = mMasterVolumeRamp.length - 1; i > 1; i -= 2) {
804 if (volume >= mMasterVolumeRamp[i - 1]) {
805 delta = mMasterVolumeRamp[i];
806 break;
807 }
808 }
809 } else if (direction == AudioManager.ADJUST_LOWER){
810 if (volume == 0) {
811 return 0;
812 }
813 int length = mMasterVolumeRamp.length;
814 // This is the default value if we make it to the end
815 delta = -mMasterVolumeRamp[length - 1];
816 // If we're lowering the volume move up the ramp array until we
817 // find the volume we're below and use the group below it's delta
818 for (int i = 2; i < length; i += 2) {
819 if (volume <= mMasterVolumeRamp[i]) {
820 delta = -mMasterVolumeRamp[i - 1];
821 break;
822 }
823 }
824 }
825 return delta;
826 }
827
Eric Laurent25101b02011-02-02 09:33:30 -0800828 // UI update and Broadcast Intent
829 private void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags) {
830 if (!mVoiceCapable && (streamType == AudioSystem.STREAM_RING)) {
831 streamType = AudioSystem.STREAM_NOTIFICATION;
832 }
833
834 mVolumePanel.postVolumeChanged(streamType, flags);
835
Eric Laurent9ce379a2010-02-16 06:00:26 -0800836 oldIndex = (oldIndex + 5) / 10;
837 index = (index + 5) / 10;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800838 Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
839 intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
Eric Laurent9ce379a2010-02-16 06:00:26 -0800840 intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index);
841 intent.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex);
Eric Laurent9ce379a2010-02-16 06:00:26 -0800842 mContext.sendBroadcast(intent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800843 }
844
Mike Lockwood0dc37cc2011-12-01 16:14:19 -0500845 // UI update and Broadcast Intent
846 private void sendMasterVolumeUpdate(int flags, int oldVolume, int newVolume) {
847 mVolumePanel.postMasterVolumeChanged(flags);
848
849 Intent intent = new Intent(AudioManager.MASTER_VOLUME_CHANGED_ACTION);
850 intent.putExtra(AudioManager.EXTRA_PREV_MASTER_VOLUME_VALUE, oldVolume);
851 intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_VALUE, newVolume);
852 mContext.sendBroadcast(intent);
853 }
854
855 // UI update and Broadcast Intent
856 private void sendMasterMuteUpdate(boolean muted, int flags) {
857 mVolumePanel.postMasterMuteChanged(flags);
Justin Koh57978ed2012-04-03 17:37:58 -0700858 broadcastMasterMuteStatus(muted);
859 }
Mike Lockwood0dc37cc2011-12-01 16:14:19 -0500860
Justin Koh57978ed2012-04-03 17:37:58 -0700861 private void broadcastMasterMuteStatus(boolean muted) {
Mike Lockwood0dc37cc2011-12-01 16:14:19 -0500862 Intent intent = new Intent(AudioManager.MASTER_MUTE_CHANGED_ACTION);
863 intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_MUTED, muted);
Justin Koh57978ed2012-04-03 17:37:58 -0700864 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
865 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
Mike Lockwood0dc37cc2011-12-01 16:14:19 -0500866 long origCallerIdentityToken = Binder.clearCallingIdentity();
867 mContext.sendStickyBroadcast(intent);
868 Binder.restoreCallingIdentity(origCallerIdentityToken);
869 }
870
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800871 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800872 * Sets the stream state's index, and posts a message to set system volume.
873 * This will not call out to the UI. Assumes a valid stream type.
874 *
875 * @param streamType Type of the stream
876 * @param index Desired volume index of the stream
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800877 * @param device the device whose volume must be changed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878 * @param force If true, set the volume even if the desired volume is same
879 * as the current volume.
Eric Laurent9bcf4012009-06-12 06:09:28 -0700880 * @param lastAudible If true, stores new index as last audible one
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800881 */
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800882 private void setStreamVolumeInt(int streamType,
883 int index,
884 int device,
885 boolean force,
886 boolean lastAudible) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800887 VolumeStreamState streamState = mStreamStates[streamType];
Eric Laurent5b4e6542010-03-19 20:02:21 -0700888
889 // If stream is muted, set last audible index only
890 if (streamState.muteCount() != 0) {
Eric Laurent758dd522010-04-13 10:38:05 -0700891 // Do not allow last audible index to be 0
892 if (index != 0) {
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800893 streamState.setLastAudibleIndex(index, device);
Eric Laurent758dd522010-04-13 10:38:05 -0700894 // Post a persist volume msg
Eric Laurentafbb0472011-12-15 09:04:23 -0800895 sendMsg(mAudioHandler,
896 MSG_PERSIST_VOLUME,
Eric Laurent98ad9b92012-02-15 17:21:37 -0800897 SENDMSG_QUEUE,
Eric Laurentafbb0472011-12-15 09:04:23 -0800898 PERSIST_LAST_AUDIBLE,
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800899 device,
Eric Laurentafbb0472011-12-15 09:04:23 -0800900 streamState,
901 PERSIST_DELAY);
Eric Laurent758dd522010-04-13 10:38:05 -0700902 }
Eric Laurent5b4e6542010-03-19 20:02:21 -0700903 } else {
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800904 if (streamState.setIndex(index, device, lastAudible) || force) {
Eric Laurent5b4e6542010-03-19 20:02:21 -0700905 // Post message to set system volume (it in turn will post a message
906 // to persist).
Eric Laurentafbb0472011-12-15 09:04:23 -0800907 sendMsg(mAudioHandler,
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800908 MSG_SET_DEVICE_VOLUME,
Eric Laurent98ad9b92012-02-15 17:21:37 -0800909 SENDMSG_QUEUE,
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800910 device,
Eric Laurentafbb0472011-12-15 09:04:23 -0800911 0,
912 streamState,
913 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800914 }
915 }
916 }
917
918 /** @see AudioManager#setStreamSolo(int, boolean) */
919 public void setStreamSolo(int streamType, boolean state, IBinder cb) {
920 for (int stream = 0; stream < mStreamStates.length; stream++) {
921 if (!isStreamAffectedByMute(stream) || stream == streamType) continue;
922 // Bring back last audible volume
923 mStreamStates[stream].mute(cb, state);
924 }
925 }
926
927 /** @see AudioManager#setStreamMute(int, boolean) */
928 public void setStreamMute(int streamType, boolean state, IBinder cb) {
929 if (isStreamAffectedByMute(streamType)) {
930 mStreamStates[streamType].mute(cb, state);
931 }
932 }
933
Eric Laurent25101b02011-02-02 09:33:30 -0800934 /** get stream mute state. */
935 public boolean isStreamMute(int streamType) {
936 return (mStreamStates[streamType].muteCount() != 0);
937 }
938
Mike Lockwoodce952c82011-11-14 10:47:42 -0800939 /** @see AudioManager#setMasterMute(boolean, IBinder) */
Justin Koh0273af52012-04-04 18:29:50 -0700940 public void setMasterMute(boolean state, int flags, IBinder cb) {
Jason Simmons1ce5b262012-02-02 13:00:17 -0800941 if (state != AudioSystem.getMasterMute()) {
942 AudioSystem.setMasterMute(state);
Justin Koh57978ed2012-04-03 17:37:58 -0700943 // Post a persist master volume msg
Justin Koh75cf9e12012-04-04 15:27:37 -0700944 sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME_MUTE, SENDMSG_REPLACE, state ? 1
Justin Koh57978ed2012-04-03 17:37:58 -0700945 : 0, 0, null, PERSIST_DELAY);
Justin Koh0273af52012-04-04 18:29:50 -0700946 sendMasterMuteUpdate(state, flags);
Jason Simmons1ce5b262012-02-02 13:00:17 -0800947 }
Mike Lockwoodce952c82011-11-14 10:47:42 -0800948 }
949
950 /** get master mute state. */
951 public boolean isMasterMute() {
Mike Lockwood3194ea92011-12-07 11:47:31 -0800952 return AudioSystem.getMasterMute();
Mike Lockwoodce952c82011-11-14 10:47:42 -0800953 }
954
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800955 /** @see AudioManager#getStreamVolume(int) */
956 public int getStreamVolume(int streamType) {
957 ensureValidStreamType(streamType);
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800958 int device = getDeviceForStream(streamType);
959 return (mStreamStates[streamType].getIndex(device, false /* lastAudible */) + 5) / 10;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800960 }
961
Mike Lockwood47676902011-11-08 10:31:21 -0800962 public int getMasterVolume() {
Mike Lockwoodce952c82011-11-14 10:47:42 -0800963 if (isMasterMute()) return 0;
964 return getLastAudibleMasterVolume();
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400965 }
966
Mike Lockwood47676902011-11-08 10:31:21 -0800967 public void setMasterVolume(int volume, int flags) {
Mike Lockwood97606472012-02-09 11:24:10 -0800968 if (volume < 0) {
969 volume = 0;
970 } else if (volume > MAX_MASTER_VOLUME) {
971 volume = MAX_MASTER_VOLUME;
972 }
Mike Lockwood5c55a052011-12-15 17:21:44 -0500973 doSetMasterVolume((float)volume / MAX_MASTER_VOLUME, flags);
974 }
975
976 private void doSetMasterVolume(float volume, int flags) {
977 // don't allow changing master volume when muted
978 if (!AudioSystem.getMasterMute()) {
979 int oldVolume = getMasterVolume();
980 AudioSystem.setMasterVolume(volume);
981
982 int newVolume = getMasterVolume();
983 if (newVolume != oldVolume) {
984 // Post a persist master volume msg
985 sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME, SENDMSG_REPLACE,
986 Math.round(volume * (float)1000.0), 0, null, PERSIST_DELAY);
Mike Lockwood5c55a052011-12-15 17:21:44 -0500987 }
Justin Koh3caba512012-04-02 15:32:18 -0700988 // Send the volume update regardless whether there was a change.
989 sendMasterVolumeUpdate(flags, oldVolume, newVolume);
Mike Lockwood5c55a052011-12-15 17:21:44 -0500990 }
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400991 }
992
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800993 /** @see AudioManager#getStreamMaxVolume(int) */
994 public int getStreamMaxVolume(int streamType) {
995 ensureValidStreamType(streamType);
Eric Laurenta553c252009-07-17 12:17:14 -0700996 return (mStreamStates[streamType].getMaxIndex() + 5) / 10;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800997 }
998
Mike Lockwood47676902011-11-08 10:31:21 -0800999 public int getMasterMaxVolume() {
1000 return MAX_MASTER_VOLUME;
1001 }
Eric Laurent25101b02011-02-02 09:33:30 -08001002
1003 /** Get last audible volume before stream was muted. */
1004 public int getLastAudibleStreamVolume(int streamType) {
1005 ensureValidStreamType(streamType);
Eric Laurent9bc8358d2011-11-18 16:43:31 -08001006 int device = getDeviceForStream(streamType);
1007 return (mStreamStates[streamType].getIndex(device, true /* lastAudible */) + 5) / 10;
Eric Laurent25101b02011-02-02 09:33:30 -08001008 }
1009
Mike Lockwoodce952c82011-11-14 10:47:42 -08001010 /** Get last audible master volume before it was muted. */
1011 public int getLastAudibleMasterVolume() {
1012 return Math.round(AudioSystem.getMasterVolume() * MAX_MASTER_VOLUME);
1013 }
1014
Eric Laurent6d517662012-04-23 18:42:39 -07001015 /** @see AudioManager#getMasterStreamType(int) */
1016 public int getMasterStreamType() {
1017 if (mVoiceCapable) {
1018 return AudioSystem.STREAM_RING;
1019 } else {
1020 return AudioSystem.STREAM_MUSIC;
1021 }
1022 }
1023
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001024 /** @see AudioManager#getRingerMode() */
1025 public int getRingerMode() {
Glenn Kastenba195eb2011-12-13 09:30:40 -08001026 synchronized(mSettingsLock) {
1027 return mRingerMode;
1028 }
1029 }
1030
1031 private void ensureValidRingerMode(int ringerMode) {
1032 if (!AudioManager.isValidRingerMode(ringerMode)) {
1033 throw new IllegalArgumentException("Bad ringer mode " + ringerMode);
1034 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001035 }
1036
1037 /** @see AudioManager#setRingerMode(int) */
1038 public void setRingerMode(int ringerMode) {
Glenn Kastenba195eb2011-12-13 09:30:40 -08001039 ensureValidRingerMode(ringerMode);
1040 if (ringerMode != getRingerMode()) {
1041 setRingerModeInt(ringerMode, true);
1042 // Send sticky broadcast
1043 broadcastRingerMode(ringerMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001044 }
1045 }
1046
Eric Laurent4050c932009-07-08 02:52:14 -07001047 private void setRingerModeInt(int ringerMode, boolean persist) {
Glenn Kastenba195eb2011-12-13 09:30:40 -08001048 synchronized(mSettingsLock) {
1049 mRingerMode = ringerMode;
1050 }
Jason Parekhb1096152009-03-24 17:48:25 -07001051
Eric Laurent5b4e6542010-03-19 20:02:21 -07001052 // Mute stream if not previously muted by ringer mode and ringer mode
1053 // is not RINGER_MODE_NORMAL and stream is affected by ringer mode.
1054 // Unmute stream if previously muted by ringer mode and ringer mode
1055 // is RINGER_MODE_NORMAL or stream is not affected by ringer mode.
Jason Parekhb1096152009-03-24 17:48:25 -07001056 int numStreamTypes = AudioSystem.getNumStreamTypes();
Eric Laurent5b4e6542010-03-19 20:02:21 -07001057 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
1058 if (isStreamMutedByRingerMode(streamType)) {
1059 if (!isStreamAffectedByRingerMode(streamType) ||
Glenn Kastenba195eb2011-12-13 09:30:40 -08001060 ringerMode == AudioManager.RINGER_MODE_NORMAL) {
Eric Laurentb024c302011-10-14 17:19:27 -07001061 // ring and notifications volume should never be 0 when not silenced
1062 // on voice capable devices
1063 if (mVoiceCapable &&
Eric Laurent6d517662012-04-23 18:42:39 -07001064 mStreamVolumeAlias[streamType] == AudioSystem.STREAM_RING) {
Eric Laurent9bc8358d2011-11-18 16:43:31 -08001065
1066 Set set = mStreamStates[streamType].mLastAudibleIndex.entrySet();
1067 Iterator i = set.iterator();
1068 while (i.hasNext()) {
1069 Map.Entry entry = (Map.Entry)i.next();
1070 if ((Integer)entry.getValue() == 0) {
1071 entry.setValue(10);
1072 }
1073 }
Eric Laurentb024c302011-10-14 17:19:27 -07001074 }
Eric Laurent5b4e6542010-03-19 20:02:21 -07001075 mStreamStates[streamType].mute(null, false);
1076 mRingerModeMutedStreams &= ~(1 << streamType);
Eric Laurent9bcf4012009-06-12 06:09:28 -07001077 }
Eric Laurent5b4e6542010-03-19 20:02:21 -07001078 } else {
1079 if (isStreamAffectedByRingerMode(streamType) &&
Glenn Kastenba195eb2011-12-13 09:30:40 -08001080 ringerMode != AudioManager.RINGER_MODE_NORMAL) {
Eric Laurent5b4e6542010-03-19 20:02:21 -07001081 mStreamStates[streamType].mute(null, true);
1082 mRingerModeMutedStreams |= (1 << streamType);
1083 }
Jason Parekhb1096152009-03-24 17:48:25 -07001084 }
1085 }
Eric Laurenta553c252009-07-17 12:17:14 -07001086
Jason Parekhb1096152009-03-24 17:48:25 -07001087 // Post a persist ringer mode msg
Eric Laurent4050c932009-07-08 02:52:14 -07001088 if (persist) {
Eric Laurentafbb0472011-12-15 09:04:23 -08001089 sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE,
Eric Laurent4050c932009-07-08 02:52:14 -07001090 SENDMSG_REPLACE, 0, 0, null, PERSIST_DELAY);
1091 }
Jason Parekhb1096152009-03-24 17:48:25 -07001092 }
1093
Mike Lockwood90631542012-01-06 11:20:37 -05001094 private void restoreMasterVolume() {
1095 if (mUseMasterVolume) {
1096 float volume = Settings.System.getFloat(mContentResolver,
1097 Settings.System.VOLUME_MASTER, -1.0f);
1098 if (volume >= 0.0f) {
1099 AudioSystem.setMasterVolume(volume);
1100 }
1101 }
1102 }
1103
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001104 /** @see AudioManager#shouldVibrate(int) */
1105 public boolean shouldVibrate(int vibrateType) {
1106
1107 switch (getVibrateSetting(vibrateType)) {
1108
1109 case AudioManager.VIBRATE_SETTING_ON:
Glenn Kastenba195eb2011-12-13 09:30:40 -08001110 return getRingerMode() != AudioManager.RINGER_MODE_SILENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001111
1112 case AudioManager.VIBRATE_SETTING_ONLY_SILENT:
Glenn Kastenba195eb2011-12-13 09:30:40 -08001113 return getRingerMode() == AudioManager.RINGER_MODE_VIBRATE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001114
1115 case AudioManager.VIBRATE_SETTING_OFF:
Daniel Sandlerbcac4962010-04-12 13:23:57 -04001116 // return false, even for incoming calls
1117 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001118
1119 default:
1120 return false;
1121 }
1122 }
1123
1124 /** @see AudioManager#getVibrateSetting(int) */
1125 public int getVibrateSetting(int vibrateType) {
1126 return (mVibrateSetting >> (vibrateType * 2)) & 3;
1127 }
1128
1129 /** @see AudioManager#setVibrateSetting(int, int) */
1130 public void setVibrateSetting(int vibrateType, int vibrateSetting) {
1131
1132 mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, vibrateType, vibrateSetting);
1133
1134 // Broadcast change
1135 broadcastVibrateSetting(vibrateType);
1136
1137 // Post message to set ringer mode (it in turn will post a message
1138 // to persist)
Eric Laurentafbb0472011-12-15 09:04:23 -08001139 sendMsg(mAudioHandler, MSG_PERSIST_VIBRATE_SETTING, SENDMSG_NOOP, 0, 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001140 null, 0);
1141 }
1142
1143 /**
1144 * @see #setVibrateSetting(int, int)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001145 */
1146 public static int getValueForVibrateSetting(int existingValue, int vibrateType,
1147 int vibrateSetting) {
1148
1149 // First clear the existing setting. Each vibrate type has two bits in
1150 // the value. Note '3' is '11' in binary.
1151 existingValue &= ~(3 << (vibrateType * 2));
1152
1153 // Set into the old value
1154 existingValue |= (vibrateSetting & 3) << (vibrateType * 2);
1155
1156 return existingValue;
1157 }
1158
Eric Laurent9272b4b2010-01-23 17:12:59 -08001159 private class SetModeDeathHandler implements IBinder.DeathRecipient {
1160 private IBinder mCb; // To be notified of client's death
Marco Nelissenf1ddd512011-08-10 14:15:44 -07001161 private int mPid;
Eric Laurent9272b4b2010-01-23 17:12:59 -08001162 private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client
1163
Eric Laurent9f103de2011-09-08 15:04:23 -07001164 SetModeDeathHandler(IBinder cb, int pid) {
Eric Laurent9272b4b2010-01-23 17:12:59 -08001165 mCb = cb;
Eric Laurent9f103de2011-09-08 15:04:23 -07001166 mPid = pid;
Eric Laurent9272b4b2010-01-23 17:12:59 -08001167 }
1168
1169 public void binderDied() {
Eric Laurentd7454be2011-09-14 08:45:58 -07001170 int newModeOwnerPid = 0;
Eric Laurent9272b4b2010-01-23 17:12:59 -08001171 synchronized(mSetModeDeathHandlers) {
1172 Log.w(TAG, "setMode() client died");
1173 int index = mSetModeDeathHandlers.indexOf(this);
1174 if (index < 0) {
1175 Log.w(TAG, "unregistered setMode() client died");
1176 } else {
Eric Laurentd7454be2011-09-14 08:45:58 -07001177 newModeOwnerPid = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid);
Eric Laurent9272b4b2010-01-23 17:12:59 -08001178 }
1179 }
Eric Laurent9f103de2011-09-08 15:04:23 -07001180 // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
1181 // SCO connections not started by the application changing the mode
Eric Laurentd7454be2011-09-14 08:45:58 -07001182 if (newModeOwnerPid != 0) {
1183 disconnectBluetoothSco(newModeOwnerPid);
Eric Laurent9f103de2011-09-08 15:04:23 -07001184 }
Eric Laurent9272b4b2010-01-23 17:12:59 -08001185 }
1186
Marco Nelissenf1ddd512011-08-10 14:15:44 -07001187 public int getPid() {
1188 return mPid;
1189 }
1190
Eric Laurent9272b4b2010-01-23 17:12:59 -08001191 public void setMode(int mode) {
1192 mMode = mode;
1193 }
1194
1195 public int getMode() {
1196 return mMode;
1197 }
1198
1199 public IBinder getBinder() {
1200 return mCb;
1201 }
1202 }
1203
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001204 /** @see AudioManager#setMode(int) */
Eric Laurent9272b4b2010-01-23 17:12:59 -08001205 public void setMode(int mode, IBinder cb) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001206 if (!checkAudioSettingsPermission("setMode()")) {
1207 return;
1208 }
Eric Laurenta553c252009-07-17 12:17:14 -07001209
Jean-Michel Trivi8f677d62010-11-15 12:11:32 -08001210 if (mode < AudioSystem.MODE_CURRENT || mode >= AudioSystem.NUM_MODES) {
Eric Laurenta553c252009-07-17 12:17:14 -07001211 return;
1212 }
1213
Eric Laurentd7454be2011-09-14 08:45:58 -07001214 int newModeOwnerPid = 0;
Eric Laurent9f103de2011-09-08 15:04:23 -07001215 synchronized(mSetModeDeathHandlers) {
Eric Laurenta553c252009-07-17 12:17:14 -07001216 if (mode == AudioSystem.MODE_CURRENT) {
1217 mode = mMode;
1218 }
Eric Laurentd7454be2011-09-14 08:45:58 -07001219 newModeOwnerPid = setModeInt(mode, cb, Binder.getCallingPid());
Eric Laurent9f103de2011-09-08 15:04:23 -07001220 }
1221 // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
1222 // SCO connections not started by the application changing the mode
Eric Laurentd7454be2011-09-14 08:45:58 -07001223 if (newModeOwnerPid != 0) {
1224 disconnectBluetoothSco(newModeOwnerPid);
Eric Laurent9f103de2011-09-08 15:04:23 -07001225 }
1226 }
Jean-Michel Trivi2ade5762010-12-11 13:18:30 -08001227
Eric Laurent9f103de2011-09-08 15:04:23 -07001228 // must be called synchronized on mSetModeDeathHandlers
Eric Laurentd7454be2011-09-14 08:45:58 -07001229 // setModeInt() returns a valid PID if the audio mode was successfully set to
Eric Laurent9f103de2011-09-08 15:04:23 -07001230 // any mode other than NORMAL.
Eric Laurentd7454be2011-09-14 08:45:58 -07001231 int setModeInt(int mode, IBinder cb, int pid) {
1232 int newModeOwnerPid = 0;
Eric Laurent9f103de2011-09-08 15:04:23 -07001233 if (cb == null) {
1234 Log.e(TAG, "setModeInt() called with null binder");
Eric Laurentd7454be2011-09-14 08:45:58 -07001235 return newModeOwnerPid;
Eric Laurent9f103de2011-09-08 15:04:23 -07001236 }
Jean-Michel Trivi2ade5762010-12-11 13:18:30 -08001237
Eric Laurent9f103de2011-09-08 15:04:23 -07001238 SetModeDeathHandler hdlr = null;
1239 Iterator iter = mSetModeDeathHandlers.iterator();
1240 while (iter.hasNext()) {
1241 SetModeDeathHandler h = (SetModeDeathHandler)iter.next();
1242 if (h.getPid() == pid) {
1243 hdlr = h;
1244 // Remove from client list so that it is re-inserted at top of list
1245 iter.remove();
1246 hdlr.getBinder().unlinkToDeath(hdlr, 0);
1247 break;
1248 }
1249 }
1250 int status = AudioSystem.AUDIO_STATUS_OK;
1251 do {
1252 if (mode == AudioSystem.MODE_NORMAL) {
1253 // get new mode from client at top the list if any
1254 if (!mSetModeDeathHandlers.isEmpty()) {
1255 hdlr = mSetModeDeathHandlers.get(0);
1256 cb = hdlr.getBinder();
1257 mode = hdlr.getMode();
Eric Laurentb9c9d262009-05-06 08:13:20 -07001258 }
Eric Laurent9f103de2011-09-08 15:04:23 -07001259 } else {
1260 if (hdlr == null) {
1261 hdlr = new SetModeDeathHandler(cb, pid);
1262 }
1263 // Register for client death notification
1264 try {
1265 cb.linkToDeath(hdlr, 0);
1266 } catch (RemoteException e) {
1267 // Client has died!
1268 Log.w(TAG, "setMode() could not link to "+cb+" binder death");
1269 }
1270
1271 // Last client to call setMode() is always at top of client list
1272 // as required by SetModeDeathHandler.binderDied()
1273 mSetModeDeathHandlers.add(0, hdlr);
1274 hdlr.setMode(mode);
1275 }
1276
1277 if (mode != mMode) {
1278 status = AudioSystem.setPhoneState(mode);
1279 if (status == AudioSystem.AUDIO_STATUS_OK) {
Eric Laurent9f103de2011-09-08 15:04:23 -07001280 mMode = mode;
1281 } else {
1282 if (hdlr != null) {
1283 mSetModeDeathHandlers.remove(hdlr);
1284 cb.unlinkToDeath(hdlr, 0);
1285 }
1286 // force reading new top of mSetModeDeathHandlers stack
1287 mode = AudioSystem.MODE_NORMAL;
1288 }
1289 } else {
1290 status = AudioSystem.AUDIO_STATUS_OK;
1291 }
1292 } while (status != AudioSystem.AUDIO_STATUS_OK && !mSetModeDeathHandlers.isEmpty());
1293
1294 if (status == AudioSystem.AUDIO_STATUS_OK) {
1295 if (mode != AudioSystem.MODE_NORMAL) {
Eric Laurentd7454be2011-09-14 08:45:58 -07001296 if (mSetModeDeathHandlers.isEmpty()) {
1297 Log.e(TAG, "setMode() different from MODE_NORMAL with empty mode client stack");
1298 } else {
1299 newModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
1300 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001301 }
1302 int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
Eric Laurent9bc8358d2011-11-18 16:43:31 -08001303 int device = getDeviceForStream(streamType);
Eric Laurent6d517662012-04-23 18:42:39 -07001304 int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device, false);
1305 setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true, false);
1306
1307 updateStreamVolumeAlias(true /*updateVolumes*/);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001308 }
Eric Laurentd7454be2011-09-14 08:45:58 -07001309 return newModeOwnerPid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001310 }
1311
1312 /** @see AudioManager#getMode() */
1313 public int getMode() {
1314 return mMode;
1315 }
1316
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001317 /** @see AudioManager#playSoundEffect(int) */
1318 public void playSoundEffect(int effectType) {
Eric Laurentafbb0472011-12-15 09:04:23 -08001319 sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_NOOP,
Eric Laurenta2ef57d2009-09-28 04:46:10 -07001320 effectType, -1, null, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001321 }
1322
1323 /** @see AudioManager#playSoundEffect(int, float) */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001324 public void playSoundEffectVolume(int effectType, float volume) {
Eric Laurenta2ef57d2009-09-28 04:46:10 -07001325 loadSoundEffects();
Eric Laurentafbb0472011-12-15 09:04:23 -08001326 sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_NOOP,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001327 effectType, (int) (volume * 1000), null, 0);
1328 }
1329
1330 /**
1331 * Loads samples into the soundpool.
Glenn Kasten5c17a822011-11-30 09:41:01 -08001332 * This method must be called at first when sound effects are enabled
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001333 */
1334 public boolean loadSoundEffects() {
Eric Laurenta60e2122010-12-28 16:49:07 -08001335 int status;
1336
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001337 synchronized (mSoundEffectsLock) {
Eric Laurenta60e2122010-12-28 16:49:07 -08001338 if (!mBootCompleted) {
1339 Log.w(TAG, "loadSoundEffects() called before boot complete");
1340 return false;
1341 }
1342
Eric Laurenta2ef57d2009-09-28 04:46:10 -07001343 if (mSoundPool != null) {
1344 return true;
1345 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001346 mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
Eric Laurenta60e2122010-12-28 16:49:07 -08001347
1348 try {
1349 mSoundPoolCallBack = null;
1350 mSoundPoolListenerThread = new SoundPoolListenerThread();
1351 mSoundPoolListenerThread.start();
1352 // Wait for mSoundPoolCallBack to be set by the other thread
1353 mSoundEffectsLock.wait();
1354 } catch (InterruptedException e) {
1355 Log.w(TAG, "Interrupted while waiting sound pool listener thread.");
1356 }
1357
1358 if (mSoundPoolCallBack == null) {
1359 Log.w(TAG, "loadSoundEffects() could not create SoundPool listener or thread");
1360 if (mSoundPoolLooper != null) {
1361 mSoundPoolLooper.quit();
1362 mSoundPoolLooper = null;
1363 }
1364 mSoundPoolListenerThread = null;
1365 mSoundPool.release();
1366 mSoundPool = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001367 return false;
1368 }
1369 /*
1370 * poolId table: The value -1 in this table indicates that corresponding
1371 * file (same index in SOUND_EFFECT_FILES[] has not been loaded.
1372 * Once loaded, the value in poolId is the sample ID and the same
1373 * sample can be reused for another effect using the same file.
1374 */
1375 int[] poolId = new int[SOUND_EFFECT_FILES.length];
1376 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
1377 poolId[fileIdx] = -1;
1378 }
1379 /*
1380 * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded.
1381 * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0:
1382 * this indicates we have a valid sample loaded for this effect.
1383 */
Eric Laurenta60e2122010-12-28 16:49:07 -08001384
Eric Laurent117b7bb2011-01-16 17:07:27 -08001385 int lastSample = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001386 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
1387 // Do not load sample if this effect uses the MediaPlayer
1388 if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) {
1389 continue;
1390 }
1391 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
Eric Laurenta60e2122010-12-28 16:49:07 -08001392 String filePath = Environment.getRootDirectory()
1393 + SOUND_EFFECTS_PATH
1394 + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effect][0]];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001395 int sampleId = mSoundPool.load(filePath, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001396 if (sampleId <= 0) {
1397 Log.w(TAG, "Soundpool could not load file: "+filePath);
Eric Laurent117b7bb2011-01-16 17:07:27 -08001398 } else {
1399 SOUND_EFFECT_FILES_MAP[effect][1] = sampleId;
1400 poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId;
1401 lastSample = sampleId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001402 }
1403 } else {
1404 SOUND_EFFECT_FILES_MAP[effect][1] = poolId[SOUND_EFFECT_FILES_MAP[effect][0]];
1405 }
1406 }
Eric Laurenta60e2122010-12-28 16:49:07 -08001407 // wait for all samples to be loaded
Eric Laurent117b7bb2011-01-16 17:07:27 -08001408 if (lastSample != 0) {
1409 mSoundPoolCallBack.setLastSample(lastSample);
1410
1411 try {
1412 mSoundEffectsLock.wait();
1413 status = mSoundPoolCallBack.status();
1414 } catch (java.lang.InterruptedException e) {
1415 Log.w(TAG, "Interrupted while waiting sound pool callback.");
1416 status = -1;
1417 }
1418 } else {
Eric Laurenta60e2122010-12-28 16:49:07 -08001419 status = -1;
1420 }
Eric Laurent117b7bb2011-01-16 17:07:27 -08001421
Eric Laurenta60e2122010-12-28 16:49:07 -08001422 if (mSoundPoolLooper != null) {
1423 mSoundPoolLooper.quit();
1424 mSoundPoolLooper = null;
1425 }
1426 mSoundPoolListenerThread = null;
1427 if (status != 0) {
1428 Log.w(TAG,
1429 "loadSoundEffects(), Error "
Eric Laurent117b7bb2011-01-16 17:07:27 -08001430 + ((lastSample != 0) ? mSoundPoolCallBack.status() : -1)
Eric Laurenta60e2122010-12-28 16:49:07 -08001431 + " while loading samples");
Eric Laurent117b7bb2011-01-16 17:07:27 -08001432 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
1433 if (SOUND_EFFECT_FILES_MAP[effect][1] > 0) {
1434 SOUND_EFFECT_FILES_MAP[effect][1] = -1;
1435 }
1436 }
1437
Eric Laurenta60e2122010-12-28 16:49:07 -08001438 mSoundPool.release();
1439 mSoundPool = null;
1440 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001441 }
Eric Laurenta60e2122010-12-28 16:49:07 -08001442 return (status == 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001443 }
1444
1445 /**
1446 * Unloads samples from the sound pool.
1447 * This method can be called to free some memory when
1448 * sound effects are disabled.
1449 */
1450 public void unloadSoundEffects() {
1451 synchronized (mSoundEffectsLock) {
1452 if (mSoundPool == null) {
1453 return;
1454 }
Eric Laurenta60e2122010-12-28 16:49:07 -08001455
Eric Laurent117b7bb2011-01-16 17:07:27 -08001456 mAudioHandler.removeMessages(MSG_LOAD_SOUND_EFFECTS);
Eric Laurenta60e2122010-12-28 16:49:07 -08001457 mAudioHandler.removeMessages(MSG_PLAY_SOUND_EFFECT);
1458
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001459 int[] poolId = new int[SOUND_EFFECT_FILES.length];
1460 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
1461 poolId[fileIdx] = 0;
1462 }
1463
1464 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
1465 if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) {
1466 continue;
1467 }
1468 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) {
1469 mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]);
1470 SOUND_EFFECT_FILES_MAP[effect][1] = -1;
1471 poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1;
1472 }
1473 }
Eric Laurenta60e2122010-12-28 16:49:07 -08001474 mSoundPool.release();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001475 mSoundPool = null;
1476 }
1477 }
1478
Eric Laurenta60e2122010-12-28 16:49:07 -08001479 class SoundPoolListenerThread extends Thread {
1480 public SoundPoolListenerThread() {
1481 super("SoundPoolListenerThread");
1482 }
1483
1484 @Override
1485 public void run() {
1486
1487 Looper.prepare();
1488 mSoundPoolLooper = Looper.myLooper();
1489
1490 synchronized (mSoundEffectsLock) {
1491 if (mSoundPool != null) {
1492 mSoundPoolCallBack = new SoundPoolCallback();
1493 mSoundPool.setOnLoadCompleteListener(mSoundPoolCallBack);
1494 }
1495 mSoundEffectsLock.notify();
1496 }
1497 Looper.loop();
1498 }
1499 }
1500
1501 private final class SoundPoolCallback implements
1502 android.media.SoundPool.OnLoadCompleteListener {
1503
1504 int mStatus;
1505 int mLastSample;
1506
1507 public int status() {
1508 return mStatus;
1509 }
1510
1511 public void setLastSample(int sample) {
1512 mLastSample = sample;
1513 }
1514
1515 public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
1516 synchronized (mSoundEffectsLock) {
1517 if (status != 0) {
1518 mStatus = status;
1519 }
1520 if (sampleId == mLastSample) {
Eric Laurenta60e2122010-12-28 16:49:07 -08001521 mSoundEffectsLock.notify();
1522 }
1523 }
1524 }
1525 }
1526
Eric Laurent4050c932009-07-08 02:52:14 -07001527 /** @see AudioManager#reloadAudioSettings() */
1528 public void reloadAudioSettings() {
1529 // restore ringer mode, ringer mode affected streams, mute affected streams and vibrate settings
1530 readPersistedSettings();
1531
1532 // restore volume settings
1533 int numStreamTypes = AudioSystem.getNumStreamTypes();
1534 for (int streamType = 0; streamType < numStreamTypes; streamType++) {
1535 VolumeStreamState streamState = mStreamStates[streamType];
1536
Eric Laurent9bc8358d2011-11-18 16:43:31 -08001537 streamState.readSettings();
Eric Laurenta553c252009-07-17 12:17:14 -07001538
Eric Laurent5b4e6542010-03-19 20:02:21 -07001539 // unmute stream that was muted but is not affect by mute anymore
Eric Laurent4050c932009-07-08 02:52:14 -07001540 if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType)) {
1541 int size = streamState.mDeathHandlers.size();
1542 for (int i = 0; i < size; i++) {
1543 streamState.mDeathHandlers.get(i).mMuteCount = 1;
1544 streamState.mDeathHandlers.get(i).mute(false);
1545 }
1546 }
1547 // apply stream volume
1548 if (streamState.muteCount() == 0) {
Eric Laurent9bc8358d2011-11-18 16:43:31 -08001549 streamState.applyAllVolumes();
Eric Laurent4050c932009-07-08 02:52:14 -07001550 }
1551 }
1552
1553 // apply new ringer mode
1554 setRingerModeInt(getRingerMode(), false);
1555 }
1556
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001557 /** @see AudioManager#setSpeakerphoneOn() */
1558 public void setSpeakerphoneOn(boolean on){
Eric Laurentdc1d17a2009-09-10 00:48:21 -07001559 if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
1560 return;
1561 }
Eric Laurentfa640152011-03-12 15:59:51 -08001562 mForcedUseForComm = on ? AudioSystem.FORCE_SPEAKER : AudioSystem.FORCE_NONE;
1563
Eric Laurentafbb0472011-12-15 09:04:23 -08001564 sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
Eric Laurentfa640152011-03-12 15:59:51 -08001565 AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0);
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001566 }
1567
1568 /** @see AudioManager#isSpeakerphoneOn() */
1569 public boolean isSpeakerphoneOn() {
Eric Laurentfa640152011-03-12 15:59:51 -08001570 return (mForcedUseForComm == AudioSystem.FORCE_SPEAKER);
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001571 }
1572
1573 /** @see AudioManager#setBluetoothScoOn() */
1574 public void setBluetoothScoOn(boolean on){
Eric Laurentdc1d17a2009-09-10 00:48:21 -07001575 if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
1576 return;
1577 }
Eric Laurentfa640152011-03-12 15:59:51 -08001578 mForcedUseForComm = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE;
1579
Eric Laurentafbb0472011-12-15 09:04:23 -08001580 sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
Eric Laurentfa640152011-03-12 15:59:51 -08001581 AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0);
Eric Laurentafbb0472011-12-15 09:04:23 -08001582 sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
Eric Laurentfa640152011-03-12 15:59:51 -08001583 AudioSystem.FOR_RECORD, mForcedUseForComm, null, 0);
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001584 }
1585
1586 /** @see AudioManager#isBluetoothScoOn() */
1587 public boolean isBluetoothScoOn() {
Eric Laurentfa640152011-03-12 15:59:51 -08001588 return (mForcedUseForComm == AudioSystem.FORCE_BT_SCO);
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001589 }
1590
Eric Laurent3def1ee2010-03-17 23:26:26 -07001591 /** @see AudioManager#startBluetoothSco() */
1592 public void startBluetoothSco(IBinder cb){
Eric Laurentdc03c612011-04-01 10:59:41 -07001593 if (!checkAudioSettingsPermission("startBluetoothSco()") ||
1594 !mBootCompleted) {
Eric Laurent3def1ee2010-03-17 23:26:26 -07001595 return;
1596 }
Eric Laurent854938a2011-02-22 12:05:20 -08001597 ScoClient client = getScoClient(cb, true);
Eric Laurent3def1ee2010-03-17 23:26:26 -07001598 client.incCount();
1599 }
1600
1601 /** @see AudioManager#stopBluetoothSco() */
1602 public void stopBluetoothSco(IBinder cb){
Eric Laurentdc03c612011-04-01 10:59:41 -07001603 if (!checkAudioSettingsPermission("stopBluetoothSco()") ||
1604 !mBootCompleted) {
Eric Laurent3def1ee2010-03-17 23:26:26 -07001605 return;
1606 }
Eric Laurent854938a2011-02-22 12:05:20 -08001607 ScoClient client = getScoClient(cb, false);
1608 if (client != null) {
1609 client.decCount();
1610 }
Eric Laurent3def1ee2010-03-17 23:26:26 -07001611 }
1612
1613 private class ScoClient implements IBinder.DeathRecipient {
1614 private IBinder mCb; // To be notified of client's death
Marco Nelissenf1ddd512011-08-10 14:15:44 -07001615 private int mCreatorPid;
Eric Laurent3def1ee2010-03-17 23:26:26 -07001616 private int mStartcount; // number of SCO connections started by this client
1617
1618 ScoClient(IBinder cb) {
1619 mCb = cb;
Marco Nelissenf1ddd512011-08-10 14:15:44 -07001620 mCreatorPid = Binder.getCallingPid();
Eric Laurent3def1ee2010-03-17 23:26:26 -07001621 mStartcount = 0;
1622 }
1623
1624 public void binderDied() {
1625 synchronized(mScoClients) {
1626 Log.w(TAG, "SCO client died");
1627 int index = mScoClients.indexOf(this);
1628 if (index < 0) {
1629 Log.w(TAG, "unregistered SCO client died");
1630 } else {
1631 clearCount(true);
1632 mScoClients.remove(this);
1633 }
1634 }
1635 }
1636
1637 public void incCount() {
1638 synchronized(mScoClients) {
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07001639 requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED);
Eric Laurent3def1ee2010-03-17 23:26:26 -07001640 if (mStartcount == 0) {
1641 try {
1642 mCb.linkToDeath(this, 0);
1643 } catch (RemoteException e) {
1644 // client has already died!
1645 Log.w(TAG, "ScoClient incCount() could not link to "+mCb+" binder death");
1646 }
1647 }
1648 mStartcount++;
1649 }
1650 }
1651
1652 public void decCount() {
1653 synchronized(mScoClients) {
1654 if (mStartcount == 0) {
1655 Log.w(TAG, "ScoClient.decCount() already 0");
1656 } else {
1657 mStartcount--;
1658 if (mStartcount == 0) {
Eric Laurente2dd8c42010-06-30 19:41:56 -07001659 try {
1660 mCb.unlinkToDeath(this, 0);
1661 } catch (NoSuchElementException e) {
1662 Log.w(TAG, "decCount() going to 0 but not registered to binder");
1663 }
Eric Laurent3def1ee2010-03-17 23:26:26 -07001664 }
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07001665 requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
Eric Laurent3def1ee2010-03-17 23:26:26 -07001666 }
1667 }
1668 }
1669
1670 public void clearCount(boolean stopSco) {
1671 synchronized(mScoClients) {
Eric Laurente2dd8c42010-06-30 19:41:56 -07001672 if (mStartcount != 0) {
1673 try {
1674 mCb.unlinkToDeath(this, 0);
1675 } catch (NoSuchElementException e) {
1676 Log.w(TAG, "clearCount() mStartcount: "+mStartcount+" != 0 but not registered to binder");
1677 }
1678 }
Eric Laurent3def1ee2010-03-17 23:26:26 -07001679 mStartcount = 0;
Eric Laurent3def1ee2010-03-17 23:26:26 -07001680 if (stopSco) {
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07001681 requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
Eric Laurent3def1ee2010-03-17 23:26:26 -07001682 }
1683 }
1684 }
1685
1686 public int getCount() {
1687 return mStartcount;
1688 }
1689
1690 public IBinder getBinder() {
1691 return mCb;
1692 }
1693
Eric Laurentd7454be2011-09-14 08:45:58 -07001694 public int getPid() {
1695 return mCreatorPid;
1696 }
1697
Eric Laurent3def1ee2010-03-17 23:26:26 -07001698 public int totalCount() {
1699 synchronized(mScoClients) {
1700 int count = 0;
1701 int size = mScoClients.size();
1702 for (int i = 0; i < size; i++) {
1703 count += mScoClients.get(i).getCount();
1704 }
1705 return count;
1706 }
1707 }
1708
1709 private void requestScoState(int state) {
Eric Laurent62ef7672010-11-24 10:58:32 -08001710 checkScoAudioState();
Eric Laurentdc03c612011-04-01 10:59:41 -07001711 if (totalCount() == 0) {
1712 if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
1713 // Make sure that the state transitions to CONNECTING even if we cannot initiate
1714 // the connection.
1715 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
1716 // Accept SCO audio activation only in NORMAL audio mode or if the mode is
Marco Nelissenf1ddd512011-08-10 14:15:44 -07001717 // currently controlled by the same client process.
Eric Laurent9f103de2011-09-08 15:04:23 -07001718 synchronized(mSetModeDeathHandlers) {
1719 if ((mSetModeDeathHandlers.isEmpty() ||
1720 mSetModeDeathHandlers.get(0).getPid() == mCreatorPid) &&
1721 (mScoAudioState == SCO_STATE_INACTIVE ||
1722 mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
1723 if (mScoAudioState == SCO_STATE_INACTIVE) {
1724 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
1725 if (mBluetoothHeadset.startScoUsingVirtualVoiceCall(
1726 mBluetoothHeadsetDevice)) {
1727 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
1728 } else {
1729 broadcastScoConnectionState(
1730 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1731 }
1732 } else if (getBluetoothHeadset()) {
1733 mScoAudioState = SCO_STATE_ACTIVATE_REQ;
Eric Laurentdc03c612011-04-01 10:59:41 -07001734 }
Eric Laurent9f103de2011-09-08 15:04:23 -07001735 } else {
1736 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
1737 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
Eric Laurentdc03c612011-04-01 10:59:41 -07001738 }
1739 } else {
Eric Laurent9f103de2011-09-08 15:04:23 -07001740 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
Eric Laurentdc03c612011-04-01 10:59:41 -07001741 }
Eric Laurentdc03c612011-04-01 10:59:41 -07001742 }
Eric Laurent62ef7672010-11-24 10:58:32 -08001743 } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED &&
Eric Laurentdc03c612011-04-01 10:59:41 -07001744 (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
1745 mScoAudioState == SCO_STATE_ACTIVATE_REQ)) {
1746 if (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) {
Marco Nelissen671db6f2011-09-06 16:29:12 -07001747 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
Eric Laurentdc03c612011-04-01 10:59:41 -07001748 if (!mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
1749 mBluetoothHeadsetDevice)) {
1750 mScoAudioState = SCO_STATE_INACTIVE;
1751 broadcastScoConnectionState(
1752 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1753 }
1754 } else if (getBluetoothHeadset()) {
1755 mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
1756 }
1757 } else {
1758 mScoAudioState = SCO_STATE_INACTIVE;
1759 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1760 }
Eric Laurent3def1ee2010-03-17 23:26:26 -07001761 }
1762 }
1763 }
1764 }
1765
Eric Laurent62ef7672010-11-24 10:58:32 -08001766 private void checkScoAudioState() {
1767 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null &&
Eric Laurentdc03c612011-04-01 10:59:41 -07001768 mScoAudioState == SCO_STATE_INACTIVE &&
Eric Laurent62ef7672010-11-24 10:58:32 -08001769 mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
1770 != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1771 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
1772 }
1773 }
1774
Eric Laurent854938a2011-02-22 12:05:20 -08001775 private ScoClient getScoClient(IBinder cb, boolean create) {
Eric Laurent3def1ee2010-03-17 23:26:26 -07001776 synchronized(mScoClients) {
Eric Laurent854938a2011-02-22 12:05:20 -08001777 ScoClient client = null;
Eric Laurent3def1ee2010-03-17 23:26:26 -07001778 int size = mScoClients.size();
1779 for (int i = 0; i < size; i++) {
1780 client = mScoClients.get(i);
1781 if (client.getBinder() == cb)
1782 return client;
1783 }
Eric Laurent854938a2011-02-22 12:05:20 -08001784 if (create) {
1785 client = new ScoClient(cb);
1786 mScoClients.add(client);
1787 }
Eric Laurent3def1ee2010-03-17 23:26:26 -07001788 return client;
1789 }
1790 }
1791
Eric Laurentd7454be2011-09-14 08:45:58 -07001792 public void clearAllScoClients(int exceptPid, boolean stopSco) {
Eric Laurent3def1ee2010-03-17 23:26:26 -07001793 synchronized(mScoClients) {
Eric Laurent854938a2011-02-22 12:05:20 -08001794 ScoClient savedClient = null;
Eric Laurent3def1ee2010-03-17 23:26:26 -07001795 int size = mScoClients.size();
1796 for (int i = 0; i < size; i++) {
Eric Laurent854938a2011-02-22 12:05:20 -08001797 ScoClient cl = mScoClients.get(i);
Eric Laurentd7454be2011-09-14 08:45:58 -07001798 if (cl.getPid() != exceptPid) {
Eric Laurent854938a2011-02-22 12:05:20 -08001799 cl.clearCount(stopSco);
1800 } else {
1801 savedClient = cl;
1802 }
1803 }
1804 mScoClients.clear();
1805 if (savedClient != null) {
1806 mScoClients.add(savedClient);
Eric Laurent3def1ee2010-03-17 23:26:26 -07001807 }
1808 }
1809 }
1810
Eric Laurentdc03c612011-04-01 10:59:41 -07001811 private boolean getBluetoothHeadset() {
1812 boolean result = false;
1813 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
1814 if (adapter != null) {
1815 result = adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
1816 BluetoothProfile.HEADSET);
1817 }
1818 // If we could not get a bluetooth headset proxy, send a failure message
1819 // without delay to reset the SCO audio state and clear SCO clients.
1820 // If we could get a proxy, send a delayed failure message that will reset our state
1821 // in case we don't receive onServiceConnected().
Eric Laurentafbb0472011-12-15 09:04:23 -08001822 sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED,
Eric Laurentdc03c612011-04-01 10:59:41 -07001823 SENDMSG_REPLACE, 0, 0, null, result ? BT_HEADSET_CNCT_TIMEOUT_MS : 0);
1824 return result;
1825 }
1826
Eric Laurentd7454be2011-09-14 08:45:58 -07001827 private void disconnectBluetoothSco(int exceptPid) {
Eric Laurentdc03c612011-04-01 10:59:41 -07001828 synchronized(mScoClients) {
1829 checkScoAudioState();
1830 if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL ||
1831 mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
1832 if (mBluetoothHeadsetDevice != null) {
1833 if (mBluetoothHeadset != null) {
1834 if (!mBluetoothHeadset.stopVoiceRecognition(
Eric Laurentb06ac832011-05-25 15:55:18 -07001835 mBluetoothHeadsetDevice)) {
Eric Laurentafbb0472011-12-15 09:04:23 -08001836 sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED,
Eric Laurentdc03c612011-04-01 10:59:41 -07001837 SENDMSG_REPLACE, 0, 0, null, 0);
1838 }
1839 } else if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL &&
1840 getBluetoothHeadset()) {
1841 mScoAudioState = SCO_STATE_DEACTIVATE_EXT_REQ;
1842 }
1843 }
1844 } else {
Eric Laurentd7454be2011-09-14 08:45:58 -07001845 clearAllScoClients(exceptPid, true);
Eric Laurentdc03c612011-04-01 10:59:41 -07001846 }
1847 }
1848 }
1849
1850 private void resetBluetoothSco() {
1851 synchronized(mScoClients) {
Eric Laurentd7454be2011-09-14 08:45:58 -07001852 clearAllScoClients(0, false);
Eric Laurentdc03c612011-04-01 10:59:41 -07001853 mScoAudioState = SCO_STATE_INACTIVE;
1854 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1855 }
1856 }
1857
1858 private void broadcastScoConnectionState(int state) {
1859 if (state != mScoConnectionState) {
1860 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
1861 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
1862 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE,
1863 mScoConnectionState);
1864 mContext.sendStickyBroadcast(newIntent);
1865 mScoConnectionState = state;
1866 }
1867 }
1868
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07001869 private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
1870 new BluetoothProfile.ServiceListener() {
1871 public void onServiceConnected(int profile, BluetoothProfile proxy) {
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08001872 BluetoothDevice btDevice;
1873 List<BluetoothDevice> deviceList;
1874 switch(profile) {
1875 case BluetoothProfile.A2DP:
1876 BluetoothA2dp a2dp = (BluetoothA2dp) proxy;
1877 deviceList = a2dp.getConnectedDevices();
Eric Laurent62ef7672010-11-24 10:58:32 -08001878 if (deviceList.size() > 0) {
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08001879 btDevice = deviceList.get(0);
1880 handleA2dpConnectionStateChange(btDevice, a2dp.getConnectionState(btDevice));
Eric Laurent62ef7672010-11-24 10:58:32 -08001881 }
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08001882 break;
1883
1884 case BluetoothProfile.HEADSET:
1885 synchronized (mScoClients) {
1886 // Discard timeout message
1887 mAudioHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
1888 mBluetoothHeadset = (BluetoothHeadset) proxy;
1889 deviceList = mBluetoothHeadset.getConnectedDevices();
1890 if (deviceList.size() > 0) {
1891 mBluetoothHeadsetDevice = deviceList.get(0);
1892 } else {
1893 mBluetoothHeadsetDevice = null;
1894 }
1895 // Refresh SCO audio state
1896 checkScoAudioState();
1897 // Continue pending action if any
1898 if (mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
1899 mScoAudioState == SCO_STATE_DEACTIVATE_REQ ||
1900 mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
1901 boolean status = false;
1902 if (mBluetoothHeadsetDevice != null) {
1903 switch (mScoAudioState) {
1904 case SCO_STATE_ACTIVATE_REQ:
1905 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
1906 status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
1907 mBluetoothHeadsetDevice);
1908 break;
1909 case SCO_STATE_DEACTIVATE_REQ:
1910 status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
1911 mBluetoothHeadsetDevice);
1912 break;
1913 case SCO_STATE_DEACTIVATE_EXT_REQ:
1914 status = mBluetoothHeadset.stopVoiceRecognition(
1915 mBluetoothHeadsetDevice);
1916 }
1917 }
1918 if (!status) {
Eric Laurentafbb0472011-12-15 09:04:23 -08001919 sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED,
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08001920 SENDMSG_REPLACE, 0, 0, null, 0);
Eric Laurentdc03c612011-04-01 10:59:41 -07001921 }
1922 }
Eric Laurentdc03c612011-04-01 10:59:41 -07001923 }
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08001924 break;
1925
1926 default:
1927 break;
Eric Laurent3def1ee2010-03-17 23:26:26 -07001928 }
1929 }
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07001930 public void onServiceDisconnected(int profile) {
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08001931 switch(profile) {
1932 case BluetoothProfile.A2DP:
1933 synchronized (mConnectedDevices) {
1934 if (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)) {
1935 makeA2dpDeviceUnavailableNow(
1936 mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
1937 }
1938 }
1939 break;
1940
1941 case BluetoothProfile.HEADSET:
1942 synchronized (mScoClients) {
1943 mBluetoothHeadset = null;
1944 }
1945 break;
1946
1947 default:
1948 break;
Eric Laurent3def1ee2010-03-17 23:26:26 -07001949 }
1950 }
1951 };
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08001952
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001953 ///////////////////////////////////////////////////////////////////////////
1954 // Internal methods
1955 ///////////////////////////////////////////////////////////////////////////
1956
1957 /**
1958 * Checks if the adjustment should change ringer mode instead of just
1959 * adjusting volume. If so, this will set the proper ringer mode and volume
1960 * indices on the stream states.
1961 */
Eric Laurentb024c302011-10-14 17:19:27 -07001962 private boolean checkForRingerModeChange(int oldIndex, int direction, int streamType) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001963 boolean adjustVolumeIndex = true;
Glenn Kastenba195eb2011-12-13 09:30:40 -08001964 int ringerMode = getRingerMode();
1965 int newRingerMode = ringerMode;
Eric Laurent3d4c06f2011-08-15 19:58:28 -07001966 int uiIndex = (oldIndex + 5) / 10;
Amith Yamasanic696a532011-10-28 17:02:37 -07001967 boolean vibeInSilent = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001968
Glenn Kastenba195eb2011-12-13 09:30:40 -08001969 if (ringerMode == RINGER_MODE_NORMAL) {
Eric Laurent3d4c06f2011-08-15 19:58:28 -07001970 if ((direction == AudioManager.ADJUST_LOWER) && (uiIndex <= 1)) {
1971 // enter silent mode if current index is the last audible one and not repeating a
1972 // volume key down
Amith Yamasanic696a532011-10-28 17:02:37 -07001973 if (vibeInSilent || mPrevVolDirection != AudioManager.ADJUST_LOWER) {
Eric Laurent3d4c06f2011-08-15 19:58:28 -07001974 // "silent mode", but which one?
Amith Yamasanic696a532011-10-28 17:02:37 -07001975 newRingerMode = vibeInSilent ? RINGER_MODE_VIBRATE : RINGER_MODE_SILENT;
Eric Laurent3d4c06f2011-08-15 19:58:28 -07001976 }
Eric Laurent8ca9a1c2011-10-31 12:12:37 -07001977 if (uiIndex == 0 ||
1978 (!vibeInSilent &&
1979 mPrevVolDirection == AudioManager.ADJUST_LOWER &&
1980 mVoiceCapable && streamType == AudioSystem.STREAM_RING)) {
Eric Laurent3d4c06f2011-08-15 19:58:28 -07001981 adjustVolumeIndex = false;
1982 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001983 }
Glenn Kastenba195eb2011-12-13 09:30:40 -08001984 } else if (ringerMode == RINGER_MODE_VIBRATE) {
Amith Yamasanic696a532011-10-28 17:02:37 -07001985 if ((direction == AudioManager.ADJUST_LOWER)) {
1986 // Set it to silent, if it wasn't a long-press
1987 if (mPrevVolDirection != AudioManager.ADJUST_LOWER) {
1988 newRingerMode = RINGER_MODE_SILENT;
1989 }
1990 } else if (direction == AudioManager.ADJUST_RAISE) {
1991 newRingerMode = RINGER_MODE_NORMAL;
1992 }
1993 adjustVolumeIndex = false;
Daniel Sandler6329bf72010-02-26 15:17:44 -05001994 } else {
1995 if (direction == AudioManager.ADJUST_RAISE) {
1996 // exiting silent mode
Amith Yamasanic696a532011-10-28 17:02:37 -07001997 // If VIBRATE_IN_SILENT, then go into vibrate mode
1998 newRingerMode = vibeInSilent ? RINGER_MODE_VIBRATE : RINGER_MODE_NORMAL;
Daniel Sandler6329bf72010-02-26 15:17:44 -05001999 }
Eric Laurentb024c302011-10-14 17:19:27 -07002000 adjustVolumeIndex = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002001 }
2002
Glenn Kastenba195eb2011-12-13 09:30:40 -08002003 setRingerMode(newRingerMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002004
Eric Laurent25101b02011-02-02 09:33:30 -08002005 mPrevVolDirection = direction;
2006
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002007 return adjustVolumeIndex;
2008 }
2009
2010 public boolean isStreamAffectedByRingerMode(int streamType) {
Eric Laurent9bcf4012009-06-12 06:09:28 -07002011 return (mRingerModeAffectedStreams & (1 << streamType)) != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002012 }
2013
Eric Laurent5b4e6542010-03-19 20:02:21 -07002014 private boolean isStreamMutedByRingerMode(int streamType) {
2015 return (mRingerModeMutedStreams & (1 << streamType)) != 0;
2016 }
2017
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002018 public boolean isStreamAffectedByMute(int streamType) {
2019 return (mMuteAffectedStreams & (1 << streamType)) != 0;
2020 }
2021
2022 private void ensureValidDirection(int direction) {
2023 if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) {
2024 throw new IllegalArgumentException("Bad direction " + direction);
2025 }
2026 }
2027
Lei Zhang6c798972012-03-02 11:40:12 -08002028 private void ensureValidSteps(int steps) {
2029 if (Math.abs(steps) > MAX_BATCH_VOLUME_ADJUST_STEPS) {
2030 throw new IllegalArgumentException("Bad volume adjust steps " + steps);
2031 }
2032 }
2033
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002034 private void ensureValidStreamType(int streamType) {
2035 if (streamType < 0 || streamType >= mStreamStates.length) {
2036 throw new IllegalArgumentException("Bad stream type " + streamType);
2037 }
2038 }
2039
Eric Laurent6d517662012-04-23 18:42:39 -07002040 private boolean isInCommunication() {
2041 boolean isOffhook = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002042
Eric Laurent25101b02011-02-02 09:33:30 -08002043 if (mVoiceCapable) {
Eric Laurent25101b02011-02-02 09:33:30 -08002044 try {
2045 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
2046 if (phone != null) isOffhook = phone.isOffhook();
2047 } catch (RemoteException e) {
2048 Log.w(TAG, "Couldn't connect to phone service", e);
2049 }
Eric Laurent6d517662012-04-23 18:42:39 -07002050 }
2051 return (isOffhook || getMode() == AudioManager.MODE_IN_COMMUNICATION);
2052 }
Eric Laurent25101b02011-02-02 09:33:30 -08002053
Eric Laurent6d517662012-04-23 18:42:39 -07002054 private int getActiveStreamType(int suggestedStreamType) {
2055 if (mVoiceCapable) {
2056 if (isInCommunication()) {
Eric Laurent25101b02011-02-02 09:33:30 -08002057 if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
2058 == AudioSystem.FORCE_BT_SCO) {
2059 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
2060 return AudioSystem.STREAM_BLUETOOTH_SCO;
2061 } else {
2062 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
2063 return AudioSystem.STREAM_VOICE_CALL;
2064 }
2065 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
2066 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC...");
2067 return AudioSystem.STREAM_MUSIC;
2068 } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
Joe Onoratoc7fcba42011-01-05 16:53:11 -08002069 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING..."
2070 // + " b/c USE_DEFAULT_STREAM_TYPE...");
2071 return AudioSystem.STREAM_RING;
2072 } else {
Eric Laurent25101b02011-02-02 09:33:30 -08002073 // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType);
2074 return suggestedStreamType;
2075 }
2076 } else {
Eric Laurent6d517662012-04-23 18:42:39 -07002077 if (isInCommunication()) {
Eric Laurent25101b02011-02-02 09:33:30 -08002078 if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
2079 == AudioSystem.FORCE_BT_SCO) {
2080 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
2081 return AudioSystem.STREAM_BLUETOOTH_SCO;
2082 } else {
2083 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
2084 return AudioSystem.STREAM_VOICE_CALL;
2085 }
2086 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_NOTIFICATION,
2087 NOTIFICATION_VOLUME_DELAY_MS) ||
2088 AudioSystem.isStreamActive(AudioSystem.STREAM_RING,
2089 NOTIFICATION_VOLUME_DELAY_MS)) {
2090 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION...");
2091 return AudioSystem.STREAM_NOTIFICATION;
2092 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0) ||
2093 (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE)) {
Joe Onoratoc7fcba42011-01-05 16:53:11 -08002094 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC "
2095 // + " b/c USE_DEFAULT_STREAM_TYPE...");
2096 return AudioSystem.STREAM_MUSIC;
Eric Laurent25101b02011-02-02 09:33:30 -08002097 } else {
2098 // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType);
2099 return suggestedStreamType;
Joe Onoratoc7fcba42011-01-05 16:53:11 -08002100 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002101 }
2102 }
2103
Glenn Kastenba195eb2011-12-13 09:30:40 -08002104 private void broadcastRingerMode(int ringerMode) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002105 // Send sticky broadcast
Dianne Hackborn1c633fc2009-12-08 19:45:14 -08002106 Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION);
Glenn Kastenba195eb2011-12-13 09:30:40 -08002107 broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, ringerMode);
Dianne Hackborn1c633fc2009-12-08 19:45:14 -08002108 broadcast.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
2109 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
2110 long origCallerIdentityToken = Binder.clearCallingIdentity();
2111 mContext.sendStickyBroadcast(broadcast);
2112 Binder.restoreCallingIdentity(origCallerIdentityToken);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002113 }
2114
2115 private void broadcastVibrateSetting(int vibrateType) {
2116 // Send broadcast
2117 if (ActivityManagerNative.isSystemReady()) {
2118 Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
2119 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType);
2120 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType));
2121 mContext.sendBroadcast(broadcast);
2122 }
2123 }
2124
2125 // Message helper methods
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002126
Eric Laurentafbb0472011-12-15 09:04:23 -08002127 private static void sendMsg(Handler handler, int msg,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002128 int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002129
2130 if (existingMsgPolicy == SENDMSG_REPLACE) {
2131 handler.removeMessages(msg);
2132 } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
2133 return;
2134 }
2135
Eric Laurentafbb0472011-12-15 09:04:23 -08002136 handler.sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002137 }
2138
2139 boolean checkAudioSettingsPermission(String method) {
2140 if (mContext.checkCallingOrSelfPermission("android.permission.MODIFY_AUDIO_SETTINGS")
2141 == PackageManager.PERMISSION_GRANTED) {
2142 return true;
2143 }
2144 String msg = "Audio Settings Permission Denial: " + method + " from pid="
2145 + Binder.getCallingPid()
2146 + ", uid=" + Binder.getCallingUid();
2147 Log.w(TAG, msg);
2148 return false;
2149 }
2150
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002151 private int getDeviceForStream(int stream) {
2152 int device = AudioSystem.getDevicesForStream(stream);
2153 if ((device & (device - 1)) != 0) {
2154 // Multiple device selection is either:
2155 // - speaker + one other device: give priority to speaker in this case.
2156 // - one A2DP device + another device: happens with duplicated output. In this case
2157 // retain the device on the A2DP output as the other must not correspond to an active
2158 // selection if not the speaker.
2159 if ((device & AudioSystem.DEVICE_OUT_SPEAKER) != 0) {
2160 device = AudioSystem.DEVICE_OUT_SPEAKER;
2161 } else {
2162 device &= AudioSystem.DEVICE_OUT_ALL_A2DP;
2163 }
2164 }
2165 return device;
2166 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002167
2168 ///////////////////////////////////////////////////////////////////////////
2169 // Inner classes
2170 ///////////////////////////////////////////////////////////////////////////
2171
2172 public class VolumeStreamState {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002173 private final int mStreamType;
2174
Jean-Michel Trivi11a74a72009-10-27 17:39:30 -07002175 private String mVolumeIndexSettingName;
2176 private String mLastAudibleVolumeIndexSettingName;
Eric Laurenta553c252009-07-17 12:17:14 -07002177 private int mIndexMax;
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002178 private final HashMap <Integer, Integer> mIndex = new HashMap <Integer, Integer>();
2179 private final HashMap <Integer, Integer> mLastAudibleIndex =
2180 new HashMap <Integer, Integer>();
2181 private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo clients death
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002182
Eric Laurenta553c252009-07-17 12:17:14 -07002183 private VolumeStreamState(String settingName, int streamType) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002184
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002185 mVolumeIndexSettingName = settingName;
2186 mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002187
2188 mStreamType = streamType;
Jared Suttles59820132009-08-13 21:50:52 -05002189 mIndexMax = MAX_STREAM_VOLUME[streamType];
Eric Laurenta553c252009-07-17 12:17:14 -07002190 AudioSystem.initStreamVolume(streamType, 0, mIndexMax);
2191 mIndexMax *= 10;
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002192
2193 readSettings();
2194
2195 applyAllVolumes();
2196
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002197 mDeathHandlers = new ArrayList<VolumeDeathHandler>();
2198 }
2199
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002200 public String getSettingNameForDevice(boolean lastAudible, int device) {
2201 String name = lastAudible ?
2202 mLastAudibleVolumeIndexSettingName :
2203 mVolumeIndexSettingName;
2204 String suffix = AudioSystem.getDeviceName(device);
2205 if (suffix.isEmpty()) {
2206 return name;
2207 }
2208 return name + "_" + suffix;
Jean-Michel Trivi11a74a72009-10-27 17:39:30 -07002209 }
2210
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002211 public void readSettings() {
Eric Laurent9ac8d0c2012-03-07 10:45:36 -08002212 boolean checkSilentVolume = (mRingerMode == AudioManager.RINGER_MODE_NORMAL) &&
2213 isStreamAffectedByRingerMode(mStreamType);
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002214
2215 int remainingDevices = AudioSystem.DEVICE_OUT_ALL;
Eric Laurent9ac8d0c2012-03-07 10:45:36 -08002216
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002217 for (int i = 0; remainingDevices != 0; i++) {
2218 int device = (1 << i);
2219 if ((device & remainingDevices) == 0) {
2220 continue;
2221 }
2222 remainingDevices &= ~device;
2223
2224 // retrieve current volume for device
Eric Laurent9ac8d0c2012-03-07 10:45:36 -08002225 String name = getSettingNameForDevice(false /* lastAudible */, device);
2226 // if no volume stored for current stream and device, use default volume if default
2227 // device, continue otherwise
2228 int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT) ?
2229 AudioManager.DEFAULT_STREAM_VOLUME[mStreamType] : -1;
2230 int index = Settings.System.getInt(mContentResolver, name, defaultIndex);
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002231 if (index == -1) {
2232 continue;
2233 }
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002234
2235 // retrieve last audible volume for device
Eric Laurent9ac8d0c2012-03-07 10:45:36 -08002236 name = getSettingNameForDevice(true /* lastAudible */, device);
2237 // use stored last audible index if present, otherwise use current index if not 0
2238 // or default index
2239 defaultIndex = (index > 0) ?
2240 index : AudioManager.DEFAULT_STREAM_VOLUME[mStreamType];
2241 int lastAudibleIndex = Settings.System.getInt(mContentResolver, name, defaultIndex);
2242
2243 // a last audible index of 0 is never stored, except on non-voice capable devices
2244 // (e.g. tablets) for the music stream type, where the music stream volume can reach
2245 // 0 without the device being in silent mode
2246 if ((lastAudibleIndex == 0) &&
2247 (mVoiceCapable ||
Eric Laurent6d517662012-04-23 18:42:39 -07002248 (mStreamVolumeAlias[mStreamType] != AudioSystem.STREAM_MUSIC))) {
Eric Laurent9ac8d0c2012-03-07 10:45:36 -08002249 lastAudibleIndex = AudioManager.DEFAULT_STREAM_VOLUME[mStreamType];
2250 // Correct the data base
2251 sendMsg(mAudioHandler,
2252 MSG_PERSIST_VOLUME,
2253 SENDMSG_QUEUE,
2254 PERSIST_LAST_AUDIBLE,
2255 device,
2256 this,
2257 PERSIST_DELAY);
2258 }
2259 mLastAudibleIndex.put(device, getValidIndex(10 * lastAudibleIndex));
2260 // the initial index should never be 0 for a stream affected by ringer mode if not
2261 // in silent or vibrate mode.
2262 // this is permitted on tablets for music stream type.
2263 if (checkSilentVolume && (index == 0) &&
2264 (mVoiceCapable ||
Eric Laurent6d517662012-04-23 18:42:39 -07002265 (mStreamVolumeAlias[mStreamType] != AudioSystem.STREAM_MUSIC))) {
Eric Laurent9ac8d0c2012-03-07 10:45:36 -08002266 index = lastAudibleIndex;
2267 // Correct the data base
2268 sendMsg(mAudioHandler,
2269 MSG_PERSIST_VOLUME,
2270 SENDMSG_QUEUE,
2271 PERSIST_CURRENT,
2272 device,
2273 this,
2274 PERSIST_DELAY);
2275 }
2276 mIndex.put(device, getValidIndex(10 * index));
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002277 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002278 }
2279
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002280 public void applyDeviceVolume(int device) {
2281 AudioSystem.setStreamVolumeIndex(mStreamType,
2282 (getIndex(device, false /* lastAudible */) + 5)/10,
2283 device);
2284 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002285
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002286 public void applyAllVolumes() {
2287 // apply default volume first: by convention this will reset all
2288 // devices volumes in audio policy manager to the supplied value
2289 AudioSystem.setStreamVolumeIndex(mStreamType,
2290 (getIndex(AudioSystem.DEVICE_OUT_DEFAULT, false /* lastAudible */) + 5)/10,
2291 AudioSystem.DEVICE_OUT_DEFAULT);
2292 // then apply device specific volumes
2293 Set set = mIndex.entrySet();
2294 Iterator i = set.iterator();
2295 while (i.hasNext()) {
2296 Map.Entry entry = (Map.Entry)i.next();
2297 int device = ((Integer)entry.getKey()).intValue();
2298 if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
2299 AudioSystem.setStreamVolumeIndex(mStreamType,
2300 ((Integer)entry.getValue() + 5)/10,
2301 device);
2302 }
2303 }
2304 }
2305
2306 public boolean adjustIndex(int deltaIndex, int device) {
2307 return setIndex(getIndex(device,
2308 false /* lastAudible */) + deltaIndex * 10,
2309 device,
2310 true /* lastAudible */);
2311 }
2312
2313 public boolean setIndex(int index, int device, boolean lastAudible) {
2314 int oldIndex = getIndex(device, false /* lastAudible */);
2315 index = getValidIndex(index);
2316 mIndex.put(device, getValidIndex(index));
2317
2318 if (oldIndex != index) {
Eric Laurent9bcf4012009-06-12 06:09:28 -07002319 if (lastAudible) {
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002320 mLastAudibleIndex.put(device, index);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002321 }
Eric Laurenta553c252009-07-17 12:17:14 -07002322 // Apply change to all streams using this one as alias
2323 int numStreamTypes = AudioSystem.getNumStreamTypes();
2324 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
Eric Laurent6d517662012-04-23 18:42:39 -07002325 if (streamType != mStreamType && mStreamVolumeAlias[streamType] == mStreamType) {
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002326 mStreamStates[streamType].setIndex(rescaleIndex(index,
2327 mStreamType,
2328 streamType),
Eric Laurent6d517662012-04-23 18:42:39 -07002329 getDeviceForStream(streamType),
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002330 lastAudible);
Eric Laurenta553c252009-07-17 12:17:14 -07002331 }
2332 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002333 return true;
2334 } else {
2335 return false;
2336 }
2337 }
2338
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002339 public int getIndex(int device, boolean lastAudible) {
2340 HashMap <Integer, Integer> indexes;
2341 if (lastAudible) {
2342 indexes = mLastAudibleIndex;
2343 } else {
2344 indexes = mIndex;
2345 }
2346 Integer index = indexes.get(device);
2347 if (index == null) {
2348 // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
2349 index = indexes.get(AudioSystem.DEVICE_OUT_DEFAULT);
2350 }
2351 return index.intValue();
Eric Laurent5b4e6542010-03-19 20:02:21 -07002352 }
2353
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002354 public void setLastAudibleIndex(int index, int device) {
2355 mLastAudibleIndex.put(device, getValidIndex(index));
2356 }
2357
2358 public void adjustLastAudibleIndex(int deltaIndex, int device) {
2359 setLastAudibleIndex(getIndex(device,
2360 true /* lastAudible */) + deltaIndex * 10,
2361 device);
Eric Laurent5b4e6542010-03-19 20:02:21 -07002362 }
2363
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002364 public int getMaxIndex() {
Eric Laurenta553c252009-07-17 12:17:14 -07002365 return mIndexMax;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002366 }
2367
Eric Laurent6d517662012-04-23 18:42:39 -07002368 public HashMap <Integer, Integer> getAllIndexes(boolean lastAudible) {
2369 if (lastAudible) {
2370 return mLastAudibleIndex;
2371 } else {
2372 return mIndex;
2373 }
2374 }
2375
2376 public void setAllIndexes(VolumeStreamState srcStream, boolean lastAudible) {
2377 HashMap <Integer, Integer> indexes = srcStream.getAllIndexes(lastAudible);
2378 Set set = indexes.entrySet();
2379 Iterator i = set.iterator();
2380 while (i.hasNext()) {
2381 Map.Entry entry = (Map.Entry)i.next();
2382 int device = ((Integer)entry.getKey()).intValue();
2383 int index = ((Integer)entry.getValue()).intValue();
2384 index = rescaleIndex(index, srcStream.getStreamType(), mStreamType);
2385 setIndex(index, device, lastAudible);
2386 }
2387 }
2388
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002389 public void mute(IBinder cb, boolean state) {
2390 VolumeDeathHandler handler = getDeathHandler(cb, state);
2391 if (handler == null) {
2392 Log.e(TAG, "Could not get client death handler for stream: "+mStreamType);
2393 return;
2394 }
2395 handler.mute(state);
2396 }
2397
Eric Laurent6d517662012-04-23 18:42:39 -07002398 public int getStreamType() {
2399 return mStreamType;
2400 }
2401
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002402 private int getValidIndex(int index) {
2403 if (index < 0) {
2404 return 0;
Eric Laurenta553c252009-07-17 12:17:14 -07002405 } else if (index > mIndexMax) {
2406 return mIndexMax;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002407 }
2408
2409 return index;
2410 }
2411
2412 private class VolumeDeathHandler implements IBinder.DeathRecipient {
2413 private IBinder mICallback; // To be notified of client's death
2414 private int mMuteCount; // Number of active mutes for this client
2415
2416 VolumeDeathHandler(IBinder cb) {
2417 mICallback = cb;
2418 }
2419
2420 public void mute(boolean state) {
2421 synchronized(mDeathHandlers) {
2422 if (state) {
2423 if (mMuteCount == 0) {
2424 // Register for client death notification
2425 try {
Eric Laurent5b4e6542010-03-19 20:02:21 -07002426 // mICallback can be 0 if muted by AudioService
2427 if (mICallback != null) {
2428 mICallback.linkToDeath(this, 0);
2429 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002430 mDeathHandlers.add(this);
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002431 // If the stream is not yet muted by any client, set level to 0
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002432 if (muteCount() == 0) {
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002433 Set set = mIndex.entrySet();
2434 Iterator i = set.iterator();
2435 while (i.hasNext()) {
2436 Map.Entry entry = (Map.Entry)i.next();
2437 int device = ((Integer)entry.getKey()).intValue();
2438 setIndex(0, device, false /* lastAudible */);
2439 }
2440 sendMsg(mAudioHandler,
2441 MSG_SET_ALL_VOLUMES,
Eric Laurent98ad9b92012-02-15 17:21:37 -08002442 SENDMSG_QUEUE,
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002443 0,
2444 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002445 VolumeStreamState.this, 0);
2446 }
2447 } catch (RemoteException e) {
2448 // Client has died!
2449 binderDied();
2450 mDeathHandlers.notify();
2451 return;
2452 }
2453 } else {
2454 Log.w(TAG, "stream: "+mStreamType+" was already muted by this client");
2455 }
2456 mMuteCount++;
2457 } else {
2458 if (mMuteCount == 0) {
2459 Log.e(TAG, "unexpected unmute for stream: "+mStreamType);
2460 } else {
2461 mMuteCount--;
2462 if (mMuteCount == 0) {
2463 // Unregistr from client death notification
2464 mDeathHandlers.remove(this);
Eric Laurent5b4e6542010-03-19 20:02:21 -07002465 // mICallback can be 0 if muted by AudioService
2466 if (mICallback != null) {
2467 mICallback.unlinkToDeath(this, 0);
2468 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002469 if (muteCount() == 0) {
Eric Laurent5b4e6542010-03-19 20:02:21 -07002470 // If the stream is not muted any more, restore it's volume if
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002471 // ringer mode allows it
Glenn Kastenba195eb2011-12-13 09:30:40 -08002472 if (!isStreamAffectedByRingerMode(mStreamType) ||
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002473 mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
2474 Set set = mIndex.entrySet();
2475 Iterator i = set.iterator();
2476 while (i.hasNext()) {
2477 Map.Entry entry = (Map.Entry)i.next();
2478 int device = ((Integer)entry.getKey()).intValue();
2479 setIndex(getIndex(device,
2480 true /* lastAudible */),
2481 device,
2482 false /* lastAudible */);
2483 }
2484 sendMsg(mAudioHandler,
2485 MSG_SET_ALL_VOLUMES,
Eric Laurent98ad9b92012-02-15 17:21:37 -08002486 SENDMSG_QUEUE,
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002487 0,
2488 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002489 VolumeStreamState.this, 0);
2490 }
2491 }
2492 }
2493 }
2494 }
2495 mDeathHandlers.notify();
2496 }
2497 }
2498
2499 public void binderDied() {
2500 Log.w(TAG, "Volume service client died for stream: "+mStreamType);
2501 if (mMuteCount != 0) {
2502 // Reset all active mute requests from this client.
2503 mMuteCount = 1;
2504 mute(false);
2505 }
2506 }
2507 }
2508
2509 private int muteCount() {
2510 int count = 0;
2511 int size = mDeathHandlers.size();
2512 for (int i = 0; i < size; i++) {
2513 count += mDeathHandlers.get(i).mMuteCount;
2514 }
2515 return count;
2516 }
2517
2518 private VolumeDeathHandler getDeathHandler(IBinder cb, boolean state) {
2519 synchronized(mDeathHandlers) {
2520 VolumeDeathHandler handler;
2521 int size = mDeathHandlers.size();
2522 for (int i = 0; i < size; i++) {
2523 handler = mDeathHandlers.get(i);
Eric Laurent5b4e6542010-03-19 20:02:21 -07002524 if (cb == handler.mICallback) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002525 return handler;
2526 }
2527 }
2528 // If this is the first mute request for this client, create a new
2529 // client death handler. Otherwise, it is an out of sequence unmute request.
2530 if (state) {
2531 handler = new VolumeDeathHandler(cb);
2532 } else {
2533 Log.w(TAG, "stream was not muted by this client");
2534 handler = null;
2535 }
2536 return handler;
2537 }
2538 }
2539 }
2540
2541 /** Thread that handles native AudioSystem control. */
2542 private class AudioSystemThread extends Thread {
2543 AudioSystemThread() {
2544 super("AudioService");
2545 }
2546
2547 @Override
2548 public void run() {
2549 // Set this thread up so the handler will work on it
2550 Looper.prepare();
2551
2552 synchronized(AudioService.this) {
2553 mAudioHandler = new AudioHandler();
2554
2555 // Notify that the handler has been created
2556 AudioService.this.notify();
2557 }
2558
2559 // Listen for volume change requests that are set by VolumePanel
2560 Looper.loop();
2561 }
2562 }
2563
2564 /** Handles internal volume messages in separate volume thread. */
2565 private class AudioHandler extends Handler {
2566
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002567 private void setDeviceVolume(VolumeStreamState streamState, int device) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002568
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002569 // Apply volume
2570 streamState.applyDeviceVolume(device);
Eric Laurenta553c252009-07-17 12:17:14 -07002571
2572 // Apply change to all streams using this one as alias
2573 int numStreamTypes = AudioSystem.getNumStreamTypes();
2574 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
2575 if (streamType != streamState.mStreamType &&
Eric Laurent6d517662012-04-23 18:42:39 -07002576 mStreamVolumeAlias[streamType] == streamState.mStreamType) {
2577 mStreamStates[streamType].applyDeviceVolume(getDeviceForStream(streamType));
Eric Laurenta553c252009-07-17 12:17:14 -07002578 }
2579 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002580
2581 // Post a persist volume msg
Eric Laurentafbb0472011-12-15 09:04:23 -08002582 sendMsg(mAudioHandler,
2583 MSG_PERSIST_VOLUME,
Eric Laurent98ad9b92012-02-15 17:21:37 -08002584 SENDMSG_QUEUE,
Eric Laurentafbb0472011-12-15 09:04:23 -08002585 PERSIST_CURRENT|PERSIST_LAST_AUDIBLE,
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002586 device,
Eric Laurentafbb0472011-12-15 09:04:23 -08002587 streamState,
2588 PERSIST_DELAY);
2589
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002590 }
2591
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002592 private void setAllVolumes(VolumeStreamState streamState) {
2593
2594 // Apply volume
2595 streamState.applyAllVolumes();
2596
2597 // Apply change to all streams using this one as alias
2598 int numStreamTypes = AudioSystem.getNumStreamTypes();
2599 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
2600 if (streamType != streamState.mStreamType &&
Eric Laurent6d517662012-04-23 18:42:39 -07002601 mStreamVolumeAlias[streamType] == streamState.mStreamType) {
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002602 mStreamStates[streamType].applyAllVolumes();
2603 }
2604 }
2605 }
2606
Eric Laurentafbb0472011-12-15 09:04:23 -08002607 private void persistVolume(VolumeStreamState streamState,
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002608 int persistType,
2609 int device) {
Eric Laurentafbb0472011-12-15 09:04:23 -08002610 if ((persistType & PERSIST_CURRENT) != 0) {
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002611 System.putInt(mContentResolver,
2612 streamState.getSettingNameForDevice(false /* lastAudible */, device),
2613 (streamState.getIndex(device, false /* lastAudible */) + 5)/ 10);
Eric Laurent31951ca2010-03-02 18:54:45 -08002614 }
Eric Laurentafbb0472011-12-15 09:04:23 -08002615 if ((persistType & PERSIST_LAST_AUDIBLE) != 0) {
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002616 System.putInt(mContentResolver,
2617 streamState.getSettingNameForDevice(true /* lastAudible */, device),
2618 (streamState.getIndex(device, true /* lastAudible */) + 5) / 10);
Eric Laurent31951ca2010-03-02 18:54:45 -08002619 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002620 }
2621
Glenn Kastenba195eb2011-12-13 09:30:40 -08002622 private void persistRingerMode(int ringerMode) {
2623 System.putInt(mContentResolver, System.MODE_RINGER, ringerMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002624 }
2625
2626 private void persistVibrateSetting() {
2627 System.putInt(mContentResolver, System.VIBRATE_ON, mVibrateSetting);
2628 }
2629
2630 private void playSoundEffect(int effectType, int volume) {
2631 synchronized (mSoundEffectsLock) {
2632 if (mSoundPool == null) {
2633 return;
2634 }
Eric Laurenta2ef57d2009-09-28 04:46:10 -07002635 float volFloat;
Eric Laurent25101b02011-02-02 09:33:30 -08002636 // use default if volume is not specified by caller
Eric Laurenta2ef57d2009-09-28 04:46:10 -07002637 if (volume < 0) {
Eric Laurent25101b02011-02-02 09:33:30 -08002638 volFloat = (float)Math.pow(10, SOUND_EFFECT_VOLUME_DB/20);
Eric Laurenta2ef57d2009-09-28 04:46:10 -07002639 } else {
2640 volFloat = (float) volume / 1000.0f;
2641 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002642
2643 if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) {
Eric Laurenta2ef57d2009-09-28 04:46:10 -07002644 mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002645 } else {
2646 MediaPlayer mediaPlayer = new MediaPlayer();
Glenn Kasten62b9aec2011-11-07 11:10:16 -08002647 try {
2648 String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]];
2649 mediaPlayer.setDataSource(filePath);
2650 mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM);
2651 mediaPlayer.prepare();
2652 mediaPlayer.setVolume(volFloat, volFloat);
2653 mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
2654 public void onCompletion(MediaPlayer mp) {
2655 cleanupPlayer(mp);
2656 }
2657 });
2658 mediaPlayer.setOnErrorListener(new OnErrorListener() {
2659 public boolean onError(MediaPlayer mp, int what, int extra) {
2660 cleanupPlayer(mp);
2661 return true;
2662 }
2663 });
2664 mediaPlayer.start();
2665 } catch (IOException ex) {
2666 Log.w(TAG, "MediaPlayer IOException: "+ex);
2667 } catch (IllegalArgumentException ex) {
2668 Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex);
2669 } catch (IllegalStateException ex) {
2670 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002671 }
2672 }
2673 }
2674 }
2675
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07002676 private void onHandlePersistMediaButtonReceiver(ComponentName receiver) {
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07002677 Settings.System.putString(mContentResolver, Settings.System.MEDIA_BUTTON_RECEIVER,
2678 receiver == null ? "" : receiver.flattenToString());
2679 }
2680
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002681 private void cleanupPlayer(MediaPlayer mp) {
2682 if (mp != null) {
2683 try {
2684 mp.stop();
2685 mp.release();
2686 } catch (IllegalStateException ex) {
2687 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
2688 }
2689 }
2690 }
2691
Eric Laurentfa640152011-03-12 15:59:51 -08002692 private void setForceUse(int usage, int config) {
2693 AudioSystem.setForceUse(usage, config);
2694 }
2695
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002696 @Override
2697 public void handleMessage(Message msg) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002698
Eric Laurentafbb0472011-12-15 09:04:23 -08002699 switch (msg.what) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002700
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002701 case MSG_SET_DEVICE_VOLUME:
2702 setDeviceVolume((VolumeStreamState) msg.obj, msg.arg1);
2703 break;
2704
2705 case MSG_SET_ALL_VOLUMES:
2706 setAllVolumes((VolumeStreamState) msg.obj);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002707 break;
2708
2709 case MSG_PERSIST_VOLUME:
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002710 persistVolume((VolumeStreamState) msg.obj, msg.arg1, msg.arg2);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002711 break;
2712
Mike Lockwood5c55a052011-12-15 17:21:44 -05002713 case MSG_PERSIST_MASTER_VOLUME:
2714 Settings.System.putFloat(mContentResolver, Settings.System.VOLUME_MASTER,
2715 (float)msg.arg1 / (float)1000.0);
2716 break;
2717
Justin Koh57978ed2012-04-03 17:37:58 -07002718 case MSG_PERSIST_MASTER_VOLUME_MUTE:
2719 Settings.System.putInt(mContentResolver, Settings.System.VOLUME_MASTER_MUTE,
2720 msg.arg1);
2721 break;
2722
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002723 case MSG_PERSIST_RINGER_MODE:
Glenn Kastenba195eb2011-12-13 09:30:40 -08002724 // note that the value persisted is the current ringer mode, not the
2725 // value of ringer mode as of the time the request was made to persist
2726 persistRingerMode(getRingerMode());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002727 break;
2728
2729 case MSG_PERSIST_VIBRATE_SETTING:
2730 persistVibrateSetting();
2731 break;
2732
2733 case MSG_MEDIA_SERVER_DIED:
Eric Laurent89e74ba2009-09-30 18:26:36 -07002734 if (!mMediaServerOk) {
2735 Log.e(TAG, "Media server died.");
Eric Laurenta25c5d82011-02-10 11:04:34 -08002736 // Force creation of new IAudioFlinger interface so that we are notified
2737 // when new media_server process is back to life.
2738 AudioSystem.setErrorCallback(mAudioSystemCallback);
Eric Laurentafbb0472011-12-15 09:04:23 -08002739 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SENDMSG_NOOP, 0, 0,
Eric Laurent89e74ba2009-09-30 18:26:36 -07002740 null, 500);
2741 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002742 break;
2743
2744 case MSG_MEDIA_SERVER_STARTED:
2745 Log.e(TAG, "Media server started.");
Eric Laurent3c652ca2010-06-21 20:46:26 -07002746 // indicate to audio HAL that we start the reconfiguration phase after a media
2747 // server crash
2748 // Note that MSG_MEDIA_SERVER_STARTED message is only received when the media server
2749 // process restarts after a crash, not the first time it is started.
2750 AudioSystem.setParameters("restarting=true");
2751
Eric Laurentc42ac9d2009-07-29 08:53:03 -07002752 // Restore device connection states
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08002753 synchronized (mConnectedDevices) {
2754 Set set = mConnectedDevices.entrySet();
2755 Iterator i = set.iterator();
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002756 while (i.hasNext()) {
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08002757 Map.Entry device = (Map.Entry)i.next();
2758 AudioSystem.setDeviceConnectionState(
2759 ((Integer)device.getKey()).intValue(),
2760 AudioSystem.DEVICE_STATE_AVAILABLE,
2761 (String)device.getValue());
2762 }
Eric Laurentc42ac9d2009-07-29 08:53:03 -07002763 }
Eric Laurentc42ac9d2009-07-29 08:53:03 -07002764 // Restore call state
2765 AudioSystem.setPhoneState(mMode);
2766
Eric Laurentd5603c12009-08-06 08:49:39 -07002767 // Restore forced usage for communcations and record
Eric Laurentc42ac9d2009-07-29 08:53:03 -07002768 AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm);
Eric Laurentd5603c12009-08-06 08:49:39 -07002769 AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm);
Eric Laurentc42ac9d2009-07-29 08:53:03 -07002770
Eric Laurenta553c252009-07-17 12:17:14 -07002771 // Restore stream volumes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002772 int numStreamTypes = AudioSystem.getNumStreamTypes();
2773 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002774 VolumeStreamState streamState = mStreamStates[streamType];
Eric Laurentc42ac9d2009-07-29 08:53:03 -07002775 AudioSystem.initStreamVolume(streamType, 0, (streamState.mIndexMax + 5) / 10);
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002776
2777 streamState.applyAllVolumes();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002778 }
Eric Laurentc42ac9d2009-07-29 08:53:03 -07002779
2780 // Restore ringer mode
2781 setRingerModeInt(getRingerMode(), false);
Eric Laurent3c652ca2010-06-21 20:46:26 -07002782
Mike Lockwood90631542012-01-06 11:20:37 -05002783 // Restore master volume
2784 restoreMasterVolume();
2785
Eric Laurent3c652ca2010-06-21 20:46:26 -07002786 // indicate the end of reconfiguration phase to audio HAL
2787 AudioSystem.setParameters("restarting=false");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002788 break;
2789
Eric Laurent117b7bb2011-01-16 17:07:27 -08002790 case MSG_LOAD_SOUND_EFFECTS:
2791 loadSoundEffects();
2792 break;
2793
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002794 case MSG_PLAY_SOUND_EFFECT:
2795 playSoundEffect(msg.arg1, msg.arg2);
2796 break;
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07002797
2798 case MSG_BTA2DP_DOCK_TIMEOUT:
2799 // msg.obj == address of BTA2DP device
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08002800 synchronized (mConnectedDevices) {
2801 makeA2dpDeviceUnavailableNow( (String) msg.obj );
2802 }
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07002803 break;
Eric Laurentfa640152011-03-12 15:59:51 -08002804
2805 case MSG_SET_FORCE_USE:
2806 setForceUse(msg.arg1, msg.arg2);
2807 break;
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07002808
2809 case MSG_PERSIST_MEDIABUTTONRECEIVER:
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07002810 onHandlePersistMediaButtonReceiver( (ComponentName) msg.obj );
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07002811 break;
Eric Laurentdc03c612011-04-01 10:59:41 -07002812
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07002813 case MSG_RCDISPLAY_CLEAR:
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07002814 onRcDisplayClear();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07002815 break;
2816
2817 case MSG_RCDISPLAY_UPDATE:
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07002818 // msg.obj is guaranteed to be non null
2819 onRcDisplayUpdate( (RemoteControlStackEntry) msg.obj, msg.arg1);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07002820 break;
2821
Eric Laurentdc03c612011-04-01 10:59:41 -07002822 case MSG_BT_HEADSET_CNCT_FAILED:
2823 resetBluetoothSco();
2824 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002825 }
2826 }
2827 }
2828
Jason Parekhb1096152009-03-24 17:48:25 -07002829 private class SettingsObserver extends ContentObserver {
Eric Laurenta553c252009-07-17 12:17:14 -07002830
Jason Parekhb1096152009-03-24 17:48:25 -07002831 SettingsObserver() {
2832 super(new Handler());
2833 mContentResolver.registerContentObserver(Settings.System.getUriFor(
2834 Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this);
2835 }
2836
2837 @Override
2838 public void onChange(boolean selfChange) {
2839 super.onChange(selfChange);
Glenn Kastenba195eb2011-12-13 09:30:40 -08002840 // FIXME This synchronized is not necessary if mSettingsLock only protects mRingerMode.
2841 // However there appear to be some missing locks around mRingerModeMutedStreams
2842 // and mRingerModeAffectedStreams, so will leave this synchronized for now.
2843 // mRingerModeMutedStreams and mMuteAffectedStreams are safe (only accessed once).
Eric Laurenta553c252009-07-17 12:17:14 -07002844 synchronized (mSettingsLock) {
2845 int ringerModeAffectedStreams = Settings.System.getInt(mContentResolver,
Eric Laurentc1d41662011-07-19 11:21:13 -07002846 Settings.System.MODE_RINGER_STREAMS_AFFECTED,
2847 ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
2848 (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)));
2849 if (mVoiceCapable) {
2850 ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
2851 } else {
Eric Laurent402f7f22011-02-04 12:30:32 -08002852 ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
2853 }
Eric Laurenta553c252009-07-17 12:17:14 -07002854 if (ringerModeAffectedStreams != mRingerModeAffectedStreams) {
2855 /*
2856 * Ensure all stream types that should be affected by ringer mode
2857 * are in the proper state.
2858 */
2859 mRingerModeAffectedStreams = ringerModeAffectedStreams;
2860 setRingerModeInt(getRingerMode(), false);
2861 }
Eric Laurenta553c252009-07-17 12:17:14 -07002862 }
Jason Parekhb1096152009-03-24 17:48:25 -07002863 }
Jason Parekhb1096152009-03-24 17:48:25 -07002864 }
Eric Laurenta553c252009-07-17 12:17:14 -07002865
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08002866 // must be called synchronized on mConnectedDevices
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07002867 private void makeA2dpDeviceAvailable(String address) {
2868 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
2869 AudioSystem.DEVICE_STATE_AVAILABLE,
2870 address);
2871 // Reset A2DP suspend state each time a new sink is connected
2872 AudioSystem.setParameters("A2dpSuspended=false");
2873 mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP),
2874 address);
2875 }
2876
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08002877 // must be called synchronized on mConnectedDevices
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07002878 private void makeA2dpDeviceUnavailableNow(String address) {
2879 Intent noisyIntent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
2880 mContext.sendBroadcast(noisyIntent);
2881 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
2882 AudioSystem.DEVICE_STATE_UNAVAILABLE,
2883 address);
2884 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
2885 }
2886
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08002887 // must be called synchronized on mConnectedDevices
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07002888 private void makeA2dpDeviceUnavailableLater(String address) {
Eric Laurent3b591262010-04-20 07:01:00 -07002889 // prevent any activity on the A2DP audio output to avoid unwanted
2890 // reconnection of the sink.
2891 AudioSystem.setParameters("A2dpSuspended=true");
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07002892 // the device will be made unavailable later, so consider it disconnected right away
2893 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
2894 // send the delayed message to make the device unavailable later
2895 Message msg = mAudioHandler.obtainMessage(MSG_BTA2DP_DOCK_TIMEOUT, address);
2896 mAudioHandler.sendMessageDelayed(msg, BTA2DP_DOCK_TIMEOUT_MILLIS);
2897
2898 }
2899
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08002900 // must be called synchronized on mConnectedDevices
Jean-Michel Trivia847ba42010-04-23 11:49:29 -07002901 private void cancelA2dpDeviceTimeout() {
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07002902 mAudioHandler.removeMessages(MSG_BTA2DP_DOCK_TIMEOUT);
2903 }
2904
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08002905 // must be called synchronized on mConnectedDevices
Jean-Michel Trivia847ba42010-04-23 11:49:29 -07002906 private boolean hasScheduledA2dpDockTimeout() {
2907 return mAudioHandler.hasMessages(MSG_BTA2DP_DOCK_TIMEOUT);
2908 }
2909
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08002910 private void handleA2dpConnectionStateChange(BluetoothDevice btDevice, int state)
2911 {
2912 if (btDevice == null) {
2913 return;
2914 }
2915 String address = btDevice.getAddress();
2916 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
2917 address = "";
2918 }
2919 synchronized (mConnectedDevices) {
2920 boolean isConnected =
2921 (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
2922 mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP).equals(address));
2923
2924 if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
2925 if (btDevice.isBluetoothDock()) {
2926 if (state == BluetoothProfile.STATE_DISCONNECTED) {
2927 // introduction of a delay for transient disconnections of docks when
2928 // power is rapidly turned off/on, this message will be canceled if
2929 // we reconnect the dock under a preset delay
2930 makeA2dpDeviceUnavailableLater(address);
2931 // the next time isConnected is evaluated, it will be false for the dock
2932 }
2933 } else {
2934 makeA2dpDeviceUnavailableNow(address);
2935 }
2936 } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
2937 if (btDevice.isBluetoothDock()) {
2938 // this could be a reconnection after a transient disconnection
2939 cancelA2dpDeviceTimeout();
2940 mDockAddress = address;
2941 } else {
2942 // this could be a connection of another A2DP device before the timeout of
2943 // a dock: cancel the dock timeout, and make the dock unavailable now
2944 if(hasScheduledA2dpDockTimeout()) {
2945 cancelA2dpDeviceTimeout();
2946 makeA2dpDeviceUnavailableNow(mDockAddress);
2947 }
2948 }
2949 makeA2dpDeviceAvailable(address);
2950 }
2951 }
2952 }
2953
Eric Laurent59f48272012-04-05 19:42:21 -07002954 private boolean handleDeviceConnection(boolean connected, int device, String params) {
2955 synchronized (mConnectedDevices) {
2956 boolean isConnected = (mConnectedDevices.containsKey(device) &&
2957 mConnectedDevices.get(device).equals(params));
2958
2959 if (isConnected && !connected) {
2960 AudioSystem.setDeviceConnectionState(device,
2961 AudioSystem.DEVICE_STATE_UNAVAILABLE,
2962 params);
2963 mConnectedDevices.remove(device);
2964 return true;
2965 } else if (!isConnected && connected) {
2966 AudioSystem.setDeviceConnectionState(device,
2967 AudioSystem.DEVICE_STATE_AVAILABLE,
2968 params);
2969 mConnectedDevices.put(new Integer(device), params);
2970 return true;
2971 }
2972 }
2973 return false;
2974 }
2975
Jean-Michel Trivia847ba42010-04-23 11:49:29 -07002976 /* cache of the address of the last dock the device was connected to */
2977 private String mDockAddress;
2978
Eric Laurenta553c252009-07-17 12:17:14 -07002979 /**
2980 * Receiver for misc intent broadcasts the Phone app cares about.
2981 */
2982 private class AudioServiceBroadcastReceiver extends BroadcastReceiver {
2983 @Override
2984 public void onReceive(Context context, Intent intent) {
2985 String action = intent.getAction();
Eric Laurent59f48272012-04-05 19:42:21 -07002986 int device;
2987 int state;
Eric Laurenta553c252009-07-17 12:17:14 -07002988
Jean-Michel Trivi758559e2010-03-09 09:26:08 -08002989 if (action.equals(Intent.ACTION_DOCK_EVENT)) {
2990 int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
2991 Intent.EXTRA_DOCK_STATE_UNDOCKED);
2992 int config;
2993 switch (dockState) {
2994 case Intent.EXTRA_DOCK_STATE_DESK:
2995 config = AudioSystem.FORCE_BT_DESK_DOCK;
2996 break;
2997 case Intent.EXTRA_DOCK_STATE_CAR:
2998 config = AudioSystem.FORCE_BT_CAR_DOCK;
2999 break;
Praveen Bharathi21e941b2010-10-06 15:23:14 -05003000 case Intent.EXTRA_DOCK_STATE_LE_DESK:
3001 config = AudioSystem.FORCE_ANALOG_DOCK;
3002 break;
3003 case Intent.EXTRA_DOCK_STATE_HE_DESK:
3004 config = AudioSystem.FORCE_DIGITAL_DOCK;
3005 break;
Jean-Michel Trivi758559e2010-03-09 09:26:08 -08003006 case Intent.EXTRA_DOCK_STATE_UNDOCKED:
3007 default:
3008 config = AudioSystem.FORCE_NONE;
3009 }
3010 AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07003011 } else if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
Eric Laurent59f48272012-04-05 19:42:21 -07003012 state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07003013 BluetoothProfile.STATE_DISCONNECTED);
Nick Pelly005b2282009-09-10 10:21:56 -07003014 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Eric Laurentd5603c12009-08-06 08:49:39 -07003015
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003016 handleA2dpConnectionStateChange(btDevice, state);
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07003017 } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
Eric Laurent59f48272012-04-05 19:42:21 -07003018 state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07003019 BluetoothProfile.STATE_DISCONNECTED);
Eric Laurent59f48272012-04-05 19:42:21 -07003020 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
Nick Pellya56d1c72009-08-19 14:49:29 -07003021 String address = null;
Eric Laurentdca56b92011-09-02 14:20:56 -07003022
3023 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
3024 if (btDevice == null) {
3025 return;
3026 }
3027
3028 address = btDevice.getAddress();
3029 BluetoothClass btClass = btDevice.getBluetoothClass();
3030 if (btClass != null) {
3031 switch (btClass.getDeviceClass()) {
3032 case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
3033 case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
3034 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
3035 break;
3036 case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
3037 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
3038 break;
Eric Laurentd5603c12009-08-06 08:49:39 -07003039 }
3040 }
3041
Eric Laurentdca56b92011-09-02 14:20:56 -07003042 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
3043 address = "";
3044 }
Eric Laurentd5603c12009-08-06 08:49:39 -07003045
Eric Laurent59f48272012-04-05 19:42:21 -07003046 boolean connected = (state == BluetoothProfile.STATE_CONNECTED);
3047 if (handleDeviceConnection(connected, device, address)) {
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003048 synchronized (mScoClients) {
Eric Laurent59f48272012-04-05 19:42:21 -07003049 if (connected) {
3050 mBluetoothHeadsetDevice = btDevice;
3051 } else {
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003052 mBluetoothHeadsetDevice = null;
3053 resetBluetoothSco();
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003054 }
Eric Laurent62ef7672010-11-24 10:58:32 -08003055 }
Eric Laurenta553c252009-07-17 12:17:14 -07003056 }
3057 } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
Eric Laurent59f48272012-04-05 19:42:21 -07003058 state = intent.getIntExtra("state", 0);
Eric Laurent923d7d72009-11-12 12:09:06 -08003059 int microphone = intent.getIntExtra("microphone", 0);
3060
Eric Laurent59f48272012-04-05 19:42:21 -07003061 if (microphone != 0) {
3062 device = AudioSystem.DEVICE_OUT_WIRED_HEADSET;
3063 } else {
3064 device = AudioSystem.DEVICE_OUT_WIRED_HEADPHONE;
Eric Laurenta553c252009-07-17 12:17:14 -07003065 }
Eric Laurent59f48272012-04-05 19:42:21 -07003066 handleDeviceConnection((state == 1), device, "");
3067 } else if (action.equals(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG)) {
3068 state = intent.getIntExtra("state", 0);
3069 Log.v(TAG, "Broadcast Receiver: Got ACTION_ANALOG_AUDIO_DOCK_PLUG, state = "+state);
3070 handleDeviceConnection((state == 1), AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET, "");
Eric Laurent2c61bee2010-12-14 16:31:33 -08003071 } else if (action.equals(Intent.ACTION_HDMI_AUDIO_PLUG)) {
Eric Laurent59f48272012-04-05 19:42:21 -07003072 state = intent.getIntExtra("state", 0);
Eric Laurent2c61bee2010-12-14 16:31:33 -08003073 Log.v(TAG, "Broadcast Receiver: Got ACTION_HDMI_AUDIO_PLUG, state = "+state);
Eric Laurent59f48272012-04-05 19:42:21 -07003074 handleDeviceConnection((state == 1), AudioSystem.DEVICE_OUT_AUX_DIGITAL, "");
3075 } else if (action.equals(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG)) {
3076 state = intent.getIntExtra("state", 0);
3077 Log.v(TAG,
3078 "Broadcast Receiver Got ACTION_DIGITAL_AUDIO_DOCK_PLUG, state = " + state);
3079 handleDeviceConnection((state == 1), AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET, "");
3080 } else if (action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ||
3081 action.equals(Intent.ACTION_USB_AUDIO_DEVICE_PLUG)) {
3082 state = intent.getIntExtra("state", 0);
3083 int alsaCard = intent.getIntExtra("card", -1);
3084 int alsaDevice = intent.getIntExtra("device", -1);
3085 String params = "card=" + alsaCard + ";device=" + alsaDevice;
3086 device = action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ?
3087 AudioSystem.DEVICE_OUT_USB_ACCESSORY : AudioSystem.DEVICE_OUT_USB_DEVICE;
3088 Log.v(TAG, "Broadcast Receiver: Got "
3089 + (action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ?
3090 "ACTION_USB_AUDIO_ACCESSORY_PLUG" : "ACTION_USB_AUDIO_DEVICE_PLUG")
3091 + ", state = " + state + ", card: " + alsaCard + ", device: " + alsaDevice);
3092 handleDeviceConnection((state == 1), device, params);
Eric Laurent3def1ee2010-03-17 23:26:26 -07003093 } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
Eric Laurent62ef7672010-11-24 10:58:32 -08003094 boolean broadcast = false;
Eric Laurent59f48272012-04-05 19:42:21 -07003095 int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
Eric Laurent3def1ee2010-03-17 23:26:26 -07003096 synchronized (mScoClients) {
Eric Laurent62ef7672010-11-24 10:58:32 -08003097 int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
Eric Laurentdc03c612011-04-01 10:59:41 -07003098 // broadcast intent if the connection was initated by AudioService
3099 if (!mScoClients.isEmpty() &&
3100 (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
3101 mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
3102 mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
Eric Laurent62ef7672010-11-24 10:58:32 -08003103 broadcast = true;
3104 }
3105 switch (btState) {
3106 case BluetoothHeadset.STATE_AUDIO_CONNECTED:
Eric Laurent59f48272012-04-05 19:42:21 -07003107 scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
Eric Laurentdc03c612011-04-01 10:59:41 -07003108 if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
3109 mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
3110 mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
Eric Laurent62ef7672010-11-24 10:58:32 -08003111 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
Eric Laurent3def1ee2010-03-17 23:26:26 -07003112 }
Eric Laurent62ef7672010-11-24 10:58:32 -08003113 break;
3114 case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
Eric Laurent59f48272012-04-05 19:42:21 -07003115 scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
Eric Laurent62ef7672010-11-24 10:58:32 -08003116 mScoAudioState = SCO_STATE_INACTIVE;
Eric Laurentd7454be2011-09-14 08:45:58 -07003117 clearAllScoClients(0, false);
Eric Laurent62ef7672010-11-24 10:58:32 -08003118 break;
3119 case BluetoothHeadset.STATE_AUDIO_CONNECTING:
Eric Laurentdc03c612011-04-01 10:59:41 -07003120 if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
3121 mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
3122 mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
Eric Laurent62ef7672010-11-24 10:58:32 -08003123 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
Eric Laurent3def1ee2010-03-17 23:26:26 -07003124 }
Eric Laurent62ef7672010-11-24 10:58:32 -08003125 default:
3126 // do not broadcast CONNECTING or invalid state
3127 broadcast = false;
3128 break;
Eric Laurent3def1ee2010-03-17 23:26:26 -07003129 }
3130 }
Eric Laurent62ef7672010-11-24 10:58:32 -08003131 if (broadcast) {
Eric Laurent59f48272012-04-05 19:42:21 -07003132 broadcastScoConnectionState(scoAudioState);
Eric Laurentdc03c612011-04-01 10:59:41 -07003133 //FIXME: this is to maintain compatibility with deprecated intent
3134 // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
Eric Laurent62ef7672010-11-24 10:58:32 -08003135 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
Eric Laurent59f48272012-04-05 19:42:21 -07003136 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, scoAudioState);
Eric Laurent62ef7672010-11-24 10:58:32 -08003137 mContext.sendStickyBroadcast(newIntent);
3138 }
3139 } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
Eric Laurenta60e2122010-12-28 16:49:07 -08003140 mBootCompleted = true;
Eric Laurentafbb0472011-12-15 09:04:23 -08003141 sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_NOOP,
Eric Laurent117b7bb2011-01-16 17:07:27 -08003142 0, 0, null, 0);
Eric Laurentdc03c612011-04-01 10:59:41 -07003143
Amith Yamasani6243edd2011-12-05 19:58:48 -08003144 mKeyguardManager =
3145 (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
Eric Laurentdc03c612011-04-01 10:59:41 -07003146 mScoConnectionState = AudioManager.SCO_AUDIO_STATE_ERROR;
3147 resetBluetoothSco();
3148 getBluetoothHeadset();
3149 //FIXME: this is to maintain compatibility with deprecated intent
3150 // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
Eric Laurent62ef7672010-11-24 10:58:32 -08003151 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
3152 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
3153 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
3154 mContext.sendStickyBroadcast(newIntent);
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003155
3156 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
3157 if (adapter != null) {
3158 adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
3159 BluetoothProfile.A2DP);
3160 }
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07003161 } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) {
3162 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
3163 // a package is being removed, not replaced
3164 String packageName = intent.getData().getSchemeSpecificPart();
3165 if (packageName != null) {
3166 removeMediaButtonReceiverForPackage(packageName);
3167 }
3168 }
Eric Laurent950e8cb2011-10-13 08:57:54 -07003169 } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
3170 AudioSystem.setParameters("screen_state=on");
3171 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
3172 AudioSystem.setParameters("screen_state=off");
Eric Laurenta553c252009-07-17 12:17:14 -07003173 }
3174 }
3175 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003176
3177 //==========================================================================================
3178 // AudioFocus
3179 //==========================================================================================
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07003180
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -07003181 /* constant to identify focus stack entry that is used to hold the focus while the phone
Jean-Michel Trivi4dd36742012-01-24 09:52:39 -08003182 * is ringing or during a call. Used by com.android.internal.telephony.CallManager when
3183 * entering and exiting calls.
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -07003184 */
Jean-Michel Trivi4dd36742012-01-24 09:52:39 -08003185 public final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls";
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -07003186
Jean-Michel Trivi392a2bb2010-05-10 20:02:46 -07003187 private final static Object mAudioFocusLock = new Object();
3188
Jean-Michel Trivie73131a2010-06-14 09:53:30 -07003189 private final static Object mRingingLock = new Object();
3190
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -07003191 private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
3192 @Override
3193 public void onCallStateChanged(int state, String incomingNumber) {
3194 if (state == TelephonyManager.CALL_STATE_RINGING) {
3195 //Log.v(TAG, " CALL_STATE_RINGING");
Jean-Michel Trivie73131a2010-06-14 09:53:30 -07003196 synchronized(mRingingLock) {
3197 mIsRinging = true;
3198 }
Jean-Michel Trivi2ade5762010-12-11 13:18:30 -08003199 } else if ((state == TelephonyManager.CALL_STATE_OFFHOOK)
3200 || (state == TelephonyManager.CALL_STATE_IDLE)) {
Jean-Michel Trivie73131a2010-06-14 09:53:30 -07003201 synchronized(mRingingLock) {
3202 mIsRinging = false;
3203 }
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07003204 }
3205 }
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -07003206 };
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07003207
3208 private void notifyTopOfAudioFocusStack() {
3209 // notify the top of the stack it gained focus
3210 if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
3211 if (canReassignAudioFocus()) {
3212 try {
3213 mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
3214 AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId);
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07003215 } catch (RemoteException e) {
3216 Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e);
3217 e.printStackTrace();
3218 }
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07003219 }
3220 }
3221 }
3222
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003223 private static class FocusStackEntry {
3224 public int mStreamType = -1;// no stream type
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003225 public IAudioFocusDispatcher mFocusDispatcher = null;
3226 public IBinder mSourceRef = null;
3227 public String mClientId;
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07003228 public int mFocusChangeType;
Jean-Michel Trivi061214b2011-08-14 11:37:24 -07003229 public AudioFocusDeathHandler mHandler;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003230 public String mPackageName;
3231 public int mCallingUid;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003232
3233 public FocusStackEntry() {
3234 }
3235
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003236 public FocusStackEntry(int streamType, int duration,
Jean-Michel Trivi061214b2011-08-14 11:37:24 -07003237 IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr,
3238 String pn, int uid) {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003239 mStreamType = streamType;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003240 mFocusDispatcher = afl;
3241 mSourceRef = source;
3242 mClientId = id;
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07003243 mFocusChangeType = duration;
Jean-Michel Trivi061214b2011-08-14 11:37:24 -07003244 mHandler = hdlr;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003245 mPackageName = pn;
3246 mCallingUid = uid;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003247 }
Jean-Michel Trivi061214b2011-08-14 11:37:24 -07003248
Jean-Michel Trivie7edc862011-11-15 16:27:05 -08003249 public void unlinkToDeath() {
3250 try {
3251 if (mSourceRef != null && mHandler != null) {
3252 mSourceRef.unlinkToDeath(mHandler, 0);
3253 mHandler = null;
3254 }
3255 } catch (java.util.NoSuchElementException e) {
3256 Log.e(TAG, "Encountered " + e + " in FocusStackEntry.unlinkToDeath()");
Jean-Michel Trivi061214b2011-08-14 11:37:24 -07003257 }
3258 }
Jean-Michel Trivide1f0652011-11-11 16:13:48 -08003259
3260 @Override
3261 protected void finalize() throws Throwable {
Jean-Michel Trivie7edc862011-11-15 16:27:05 -08003262 unlinkToDeath(); // unlink exception handled inside method
3263 super.finalize();
Jean-Michel Trivide1f0652011-11-11 16:13:48 -08003264 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003265 }
3266
Glenn Kasten30c918c2011-11-10 17:56:41 -08003267 private final Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>();
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003268
3269 /**
3270 * Helper function:
3271 * Display in the log the current entries in the audio focus stack
3272 */
3273 private void dumpFocusStack(PrintWriter pw) {
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -07003274 pw.println("\nAudio Focus stack entries:");
Jean-Michel Trivi392a2bb2010-05-10 20:02:46 -07003275 synchronized(mAudioFocusLock) {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003276 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
3277 while(stackIterator.hasNext()) {
3278 FocusStackEntry fse = stackIterator.next();
3279 pw.println(" source:" + fse.mSourceRef + " -- client: " + fse.mClientId
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003280 + " -- duration: " + fse.mFocusChangeType
3281 + " -- uid: " + fse.mCallingUid);
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003282 }
3283 }
3284 }
3285
3286 /**
3287 * Helper function:
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003288 * Called synchronized on mAudioFocusLock
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003289 * Remove a focus listener from the focus stack.
3290 * @param focusListenerToRemove the focus listener
3291 * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
3292 * focus, notify the next item in the stack it gained focus.
3293 */
3294 private void removeFocusStackEntry(String clientToRemove, boolean signal) {
Jean-Michel Trivie7edc862011-11-15 16:27:05 -08003295 // is the current top of the focus stack abandoning focus? (because of request, not death)
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003296 if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove))
3297 {
3298 //Log.i(TAG, " removeFocusStackEntry() removing top of stack");
Jean-Michel Trivie7edc862011-11-15 16:27:05 -08003299 FocusStackEntry fse = mFocusStack.pop();
3300 fse.unlinkToDeath();
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003301 if (signal) {
3302 // notify the new top of the stack it gained focus
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07003303 notifyTopOfAudioFocusStack();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003304 // there's a new top of the stack, let the remote control know
3305 synchronized(mRCStack) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003306 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003307 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003308 }
3309 } else {
3310 // focus is abandoned by a client that's not at the top of the stack,
3311 // no need to update focus.
3312 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
3313 while(stackIterator.hasNext()) {
3314 FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
3315 if(fse.mClientId.equals(clientToRemove)) {
3316 Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for "
3317 + fse.mClientId);
Jean-Michel Trivi72843372011-04-18 10:18:37 -07003318 stackIterator.remove();
Jean-Michel Trivie7edc862011-11-15 16:27:05 -08003319 fse.unlinkToDeath();
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003320 }
3321 }
3322 }
3323 }
3324
3325 /**
3326 * Helper function:
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003327 * Called synchronized on mAudioFocusLock
Jean-Michel Trivie7edc862011-11-15 16:27:05 -08003328 * Remove focus listeners from the focus stack for a particular client when it has died.
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003329 */
3330 private void removeFocusStackEntryForClient(IBinder cb) {
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07003331 // is the owner of the audio focus part of the client to remove?
3332 boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
3333 mFocusStack.peek().mSourceRef.equals(cb);
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003334 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
3335 while(stackIterator.hasNext()) {
3336 FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
3337 if(fse.mSourceRef.equals(cb)) {
3338 Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for "
3339 + fse.mClientId);
Jean-Michel Trivi72843372011-04-18 10:18:37 -07003340 stackIterator.remove();
Jean-Michel Trivie7edc862011-11-15 16:27:05 -08003341 // the client just died, no need to unlink to its death
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003342 }
3343 }
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07003344 if (isTopOfStackForClientToRemove) {
3345 // we removed an entry at the top of the stack:
3346 // notify the new top of the stack it gained focus.
3347 notifyTopOfAudioFocusStack();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003348 // there's a new top of the stack, let the remote control know
3349 synchronized(mRCStack) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003350 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003351 }
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07003352 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003353 }
3354
3355 /**
3356 * Helper function:
3357 * Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
3358 */
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003359 private boolean canReassignAudioFocus() {
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -07003360 // focus requests are rejected during a phone call or when the phone is ringing
3361 // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus
3362 if (!mFocusStack.isEmpty() && IN_VOICE_COMM_FOCUS_ID.equals(mFocusStack.peek().mClientId)) {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003363 return false;
3364 }
3365 return true;
3366 }
3367
3368 /**
3369 * Inner class to monitor audio focus client deaths, and remove them from the audio focus
3370 * stack if necessary.
3371 */
3372 private class AudioFocusDeathHandler implements IBinder.DeathRecipient {
3373 private IBinder mCb; // To be notified of client's death
3374
3375 AudioFocusDeathHandler(IBinder cb) {
3376 mCb = cb;
3377 }
3378
3379 public void binderDied() {
Jean-Michel Trivi392a2bb2010-05-10 20:02:46 -07003380 synchronized(mAudioFocusLock) {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003381 Log.w(TAG, " AudioFocus audio focus client died");
3382 removeFocusStackEntryForClient(mCb);
3383 }
3384 }
3385
3386 public IBinder getBinder() {
3387 return mCb;
3388 }
3389 }
3390
3391
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003392 /** @see AudioManager#requestAudioFocus(IAudioFocusDispatcher, int, int) */
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07003393 public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb,
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003394 IAudioFocusDispatcher fd, String clientId, String callingPackageName) {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003395 Log.i(TAG, " AudioFocus requestAudioFocus() from " + clientId);
3396 // the main stream type for the audio focus request is currently not used. It may
3397 // potentially be used to handle multiple stream type-dependent audio focuses.
3398
Jean-Michel Trivi382f4e6e2011-03-08 14:38:59 -08003399 // we need a valid binder callback for clients
3400 if (!cb.pingBinder()) {
3401 Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003402 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
3403 }
3404
Jean-Michel Trivi392a2bb2010-05-10 20:02:46 -07003405 synchronized(mAudioFocusLock) {
3406 if (!canReassignAudioFocus()) {
3407 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
3408 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003409
Jean-Michel Trivibaf674e2011-08-14 15:16:21 -07003410 // handle the potential premature death of the new holder of the focus
3411 // (premature death == death before abandoning focus)
3412 // Register for client death notification
3413 AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
3414 try {
3415 cb.linkToDeath(afdh, 0);
3416 } catch (RemoteException e) {
3417 // client has already died!
3418 Log.w(TAG, "AudioFocus requestAudioFocus() could not link to "+cb+" binder death");
3419 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
3420 }
3421
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003422 if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) {
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07003423 // if focus is already owned by this client and the reason for acquiring the focus
3424 // hasn't changed, don't do anything
3425 if (mFocusStack.peek().mFocusChangeType == focusChangeHint) {
Jean-Michel Trivide1f0652011-11-11 16:13:48 -08003426 // unlink death handler so it can be gc'ed.
3427 // linkToDeath() creates a JNI global reference preventing collection.
3428 cb.unlinkToDeath(afdh, 0);
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07003429 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
3430 }
3431 // the reason for the audio focus request has changed: remove the current top of
3432 // stack and respond as if we had a new focus owner
Jean-Michel Trivie7edc862011-11-15 16:27:05 -08003433 FocusStackEntry fse = mFocusStack.pop();
3434 fse.unlinkToDeath();
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003435 }
3436
3437 // notify current top of stack it is losing focus
3438 if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
3439 try {
3440 mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07003441 -1 * focusChangeHint, // loss and gain codes are inverse of each other
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003442 mFocusStack.peek().mClientId);
3443 } catch (RemoteException e) {
3444 Log.e(TAG, " Failure to signal loss of focus due to "+ e);
3445 e.printStackTrace();
3446 }
3447 }
3448
Jean-Michel Trivi55d1bb32010-04-01 17:40:58 -07003449 // focus requester might already be somewhere below in the stack, remove it
Jean-Michel Trivibaf674e2011-08-14 15:16:21 -07003450 removeFocusStackEntry(clientId, false /* signal */);
Jean-Michel Trivi061214b2011-08-14 11:37:24 -07003451
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003452 // push focus requester at the top of the audio focus stack
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003453 mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, fd, cb,
Jean-Michel Trivi061214b2011-08-14 11:37:24 -07003454 clientId, afdh, callingPackageName, Binder.getCallingUid()));
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003455
3456 // there's a new top of the stack, let the remote control know
3457 synchronized(mRCStack) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003458 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003459 }
Jean-Michel Trivi392a2bb2010-05-10 20:02:46 -07003460 }//synchronized(mAudioFocusLock)
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003461
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003462 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
3463 }
3464
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003465 /** @see AudioManager#abandonAudioFocus(IAudioFocusDispatcher) */
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003466 public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) {
3467 Log.i(TAG, " AudioFocus abandonAudioFocus() from " + clientId);
Jean-Michel Trivi392a2bb2010-05-10 20:02:46 -07003468 try {
3469 // this will take care of notifying the new focus owner if needed
3470 synchronized(mAudioFocusLock) {
3471 removeFocusStackEntry(clientId, true);
3472 }
3473 } catch (java.util.ConcurrentModificationException cme) {
3474 // Catching this exception here is temporary. It is here just to prevent
3475 // a crash seen when the "Silent" notification is played. This is believed to be fixed
3476 // but this try catch block is left just to be safe.
3477 Log.e(TAG, "FATAL EXCEPTION AudioFocus abandonAudioFocus() caused " + cme);
3478 cme.printStackTrace();
Jean-Michel Trivi2930bb22010-04-09 19:27:58 -07003479 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003480
3481 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
3482 }
3483
3484
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003485 public void unregisterAudioFocusClient(String clientId) {
Jean-Michel Trivi392a2bb2010-05-10 20:02:46 -07003486 synchronized(mAudioFocusLock) {
Jean-Michel Trivi2930bb22010-04-09 19:27:58 -07003487 removeFocusStackEntry(clientId, false);
3488 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003489 }
3490
3491
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003492 //==========================================================================================
3493 // RemoteControl
3494 //==========================================================================================
3495 /**
3496 * Receiver for media button intents. Handles the dispatching of the media button event
3497 * to one of the registered listeners, or if there was none, resumes the intent broadcast
3498 * to the rest of the system.
3499 */
3500 private class MediaButtonBroadcastReceiver extends BroadcastReceiver {
3501 @Override
3502 public void onReceive(Context context, Intent intent) {
3503 String action = intent.getAction();
3504 if (!Intent.ACTION_MEDIA_BUTTON.equals(action)) {
3505 return;
3506 }
3507 KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
3508 if (event != null) {
3509 // if in a call or ringing, do not break the current phone app behavior
3510 // TODO modify this to let the phone app specifically get the RC focus
3511 // add modify the phone app to take advantage of the new API
Jean-Michel Trivie73131a2010-06-14 09:53:30 -07003512 synchronized(mRingingLock) {
3513 if (mIsRinging || (getMode() == AudioSystem.MODE_IN_CALL) ||
Jean-Michel Trivi8f677d62010-11-15 12:11:32 -08003514 (getMode() == AudioSystem.MODE_IN_COMMUNICATION) ||
Jean-Michel Trivie73131a2010-06-14 09:53:30 -07003515 (getMode() == AudioSystem.MODE_RINGTONE) ) {
3516 return;
3517 }
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003518 }
3519 synchronized(mRCStack) {
3520 if (!mRCStack.empty()) {
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003521 // create a new intent to fill in the extras of the registered PendingIntent
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003522 Intent targetedIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003523 Bundle extras = intent.getExtras();
3524 if (extras != null) {
3525 targetedIntent.putExtras(extras);
3526 // trap the current broadcast
3527 abortBroadcast();
3528 //Log.v(TAG, " Sending intent" + targetedIntent);
3529 // send the intent that was registered by the client
3530 try {
3531 mRCStack.peek().mMediaIntent.send(context, 0, targetedIntent);
3532 } catch (CanceledException e) {
3533 Log.e(TAG, "Error sending pending intent " + mRCStack.peek());
3534 e.printStackTrace();
3535 }
3536 }
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003537 }
3538 }
3539 }
3540 }
3541 }
3542
Neel Parekhced110e2011-08-05 14:32:49 -07003543 private final Object mCurrentRcLock = new Object();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003544 /**
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003545 * The one remote control client which will receive a request for display information.
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07003546 * This object may be null.
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003547 * Access protected by mCurrentRcLock.
3548 */
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003549 private IRemoteControlClient mCurrentRcClient = null;
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003550
Jean-Michel Trivi7b6198c2011-08-07 11:27:50 -07003551 private final static int RC_INFO_NONE = 0;
3552 private final static int RC_INFO_ALL =
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003553 RemoteControlClient.FLAG_INFORMATION_REQUEST_ALBUM_ART |
3554 RemoteControlClient.FLAG_INFORMATION_REQUEST_KEY_MEDIA |
3555 RemoteControlClient.FLAG_INFORMATION_REQUEST_METADATA |
3556 RemoteControlClient.FLAG_INFORMATION_REQUEST_PLAYSTATE;
Jean-Michel Trivi7b6198c2011-08-07 11:27:50 -07003557
3558 /**
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07003559 * A monotonically increasing generation counter for mCurrentRcClient.
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003560 * Only accessed with a lock on mCurrentRcLock.
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07003561 * No value wrap-around issues as we only act on equal values.
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003562 */
Neel Parekhced110e2011-08-05 14:32:49 -07003563 private int mCurrentRcClientGen = 0;
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003564
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003565 /**
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003566 * Inner class to monitor remote control client deaths, and remove the client for the
3567 * remote control stack if necessary.
3568 */
3569 private class RcClientDeathHandler implements IBinder.DeathRecipient {
3570 private IBinder mCb; // To be notified of client's death
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003571 private PendingIntent mMediaIntent;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003572
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003573 RcClientDeathHandler(IBinder cb, PendingIntent pi) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003574 mCb = cb;
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003575 mMediaIntent = pi;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003576 }
3577
3578 public void binderDied() {
3579 Log.w(TAG, " RemoteControlClient died");
3580 // remote control client died, make sure the displays don't use it anymore
3581 // by setting its remote control client to null
Jean-Michel Trivi26b3d1f2011-09-28 11:05:15 -07003582 registerRemoteControlClient(mMediaIntent, null/*rcClient*/, null/*ignored*/);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003583 }
3584
3585 public IBinder getBinder() {
3586 return mCb;
3587 }
3588 }
3589
3590 private static class RemoteControlStackEntry {
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003591 /**
3592 * The target for the ACTION_MEDIA_BUTTON events.
3593 * Always non null.
3594 */
3595 public PendingIntent mMediaIntent;
3596 /**
3597 * The registered media button event receiver.
3598 * Always non null.
3599 */
3600 public ComponentName mReceiverComponent;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003601 public String mCallingPackageName;
3602 public int mCallingUid;
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003603 /**
3604 * Provides access to the information to display on the remote control.
3605 * May be null (when a media button event receiver is registered,
3606 * but no remote control client has been registered) */
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003607 public IRemoteControlClient mRcClient;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003608 public RcClientDeathHandler mRcClientDeathHandler;
3609
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003610 /** precondition: mediaIntent != null, eventReceiver != null */
3611 public RemoteControlStackEntry(PendingIntent mediaIntent, ComponentName eventReceiver) {
3612 mMediaIntent = mediaIntent;
3613 mReceiverComponent = eventReceiver;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003614 mCallingUid = -1;
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07003615 mRcClient = null;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003616 }
3617
3618 public void unlinkToRcClientDeath() {
3619 if ((mRcClientDeathHandler != null) && (mRcClientDeathHandler.mCb != null)) {
3620 try {
3621 mRcClientDeathHandler.mCb.unlinkToDeath(mRcClientDeathHandler, 0);
Jean-Michel Trivia9509732011-11-14 15:23:04 -08003622 mRcClientDeathHandler = null;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003623 } catch (java.util.NoSuchElementException e) {
3624 // not much we can do here
3625 Log.e(TAG, "Encountered " + e + " in unlinkToRcClientDeath()");
3626 e.printStackTrace();
3627 }
3628 }
3629 }
Jean-Michel Trivia9509732011-11-14 15:23:04 -08003630
3631 @Override
3632 protected void finalize() throws Throwable {
3633 unlinkToRcClientDeath();// unlink exception handled inside method
3634 super.finalize();
3635 }
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003636 }
3637
3638 /**
3639 * The stack of remote control event receivers.
3640 * Code sections and methods that modify the remote control event receiver stack are
3641 * synchronized on mRCStack, but also BEFORE on mFocusLock as any change in either
3642 * stack, audio focus or RC, can lead to a change in the remote control display
3643 */
Glenn Kasten30c918c2011-11-10 17:56:41 -08003644 private final Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>();
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003645
3646 /**
3647 * Helper function:
3648 * Display in the log the current entries in the remote control focus stack
3649 */
3650 private void dumpRCStack(PrintWriter pw) {
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -07003651 pw.println("\nRemote Control stack entries:");
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003652 synchronized(mRCStack) {
3653 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3654 while(stackIterator.hasNext()) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003655 RemoteControlStackEntry rcse = stackIterator.next();
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003656 pw.println(" pi: " + rcse.mMediaIntent +
3657 " -- ercvr: " + rcse.mReceiverComponent +
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07003658 " -- client: " + rcse.mRcClient +
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003659 " -- uid: " + rcse.mCallingUid);
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003660 }
3661 }
3662 }
3663
3664 /**
3665 * Helper function:
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07003666 * Remove any entry in the remote control stack that has the same package name as packageName
3667 * Pre-condition: packageName != null
3668 */
3669 private void removeMediaButtonReceiverForPackage(String packageName) {
3670 synchronized(mRCStack) {
3671 if (mRCStack.empty()) {
3672 return;
3673 } else {
3674 RemoteControlStackEntry oldTop = mRCStack.peek();
3675 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3676 // iterate over the stack entries
3677 while(stackIterator.hasNext()) {
3678 RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
3679 if (packageName.equalsIgnoreCase(rcse.mReceiverComponent.getPackageName())) {
3680 // a stack entry is from the package being removed, remove it from the stack
3681 stackIterator.remove();
Jean-Michel Trivia9509732011-11-14 15:23:04 -08003682 rcse.unlinkToRcClientDeath();
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07003683 }
3684 }
3685 if (mRCStack.empty()) {
3686 // no saved media button receiver
3687 mAudioHandler.sendMessage(
3688 mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
3689 null));
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07003690 } else if (oldTop != mRCStack.peek()) {
3691 // the top of the stack has changed, save it in the system settings
3692 // by posting a message to persist it
3693 mAudioHandler.sendMessage(
3694 mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
3695 mRCStack.peek().mReceiverComponent));
3696 }
3697 }
3698 }
3699 }
3700
3701 /**
3702 * Helper function:
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003703 * Restore remote control receiver from the system settings.
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07003704 */
3705 private void restoreMediaButtonReceiver() {
3706 String receiverName = Settings.System.getString(mContentResolver,
3707 Settings.System.MEDIA_BUTTON_RECEIVER);
3708 if ((null != receiverName) && !receiverName.isEmpty()) {
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003709 ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
3710 // construct a PendingIntent targeted to the restored component name
3711 // for the media button and register it
3712 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
3713 // the associated intent will be handled by the component being registered
3714 mediaButtonIntent.setComponent(eventReceiver);
3715 PendingIntent pi = PendingIntent.getBroadcast(mContext,
3716 0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
3717 registerMediaButtonIntent(pi, eventReceiver);
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07003718 }
3719 }
3720
3721 /**
3722 * Helper function:
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003723 * Set the new remote control receiver at the top of the RC focus stack.
3724 * precondition: mediaIntent != null, target != null
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003725 */
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003726 private void pushMediaButtonReceiver(PendingIntent mediaIntent, ComponentName target) {
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003727 // already at top of stack?
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003728 if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(mediaIntent)) {
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003729 return;
3730 }
3731 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003732 RemoteControlStackEntry rcse = null;
3733 boolean wasInsideStack = false;
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003734 while(stackIterator.hasNext()) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003735 rcse = (RemoteControlStackEntry)stackIterator.next();
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003736 if(rcse.mMediaIntent.equals(mediaIntent)) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003737 wasInsideStack = true;
Jean-Michel Trivi72843372011-04-18 10:18:37 -07003738 stackIterator.remove();
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003739 break;
3740 }
3741 }
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003742 if (!wasInsideStack) {
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003743 rcse = new RemoteControlStackEntry(mediaIntent, target);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003744 }
3745 mRCStack.push(rcse);
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07003746
3747 // post message to persist the default media button receiver
3748 mAudioHandler.sendMessage( mAudioHandler.obtainMessage(
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003749 MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) );
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003750 }
3751
3752 /**
3753 * Helper function:
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003754 * Remove the remote control receiver from the RC focus stack.
3755 * precondition: pi != null
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003756 */
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003757 private void removeMediaButtonReceiver(PendingIntent pi) {
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003758 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3759 while(stackIterator.hasNext()) {
3760 RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003761 if(rcse.mMediaIntent.equals(pi)) {
Jean-Michel Trivi72843372011-04-18 10:18:37 -07003762 stackIterator.remove();
Jean-Michel Trivia9509732011-11-14 15:23:04 -08003763 rcse.unlinkToRcClientDeath();
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003764 break;
3765 }
3766 }
3767 }
3768
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003769 /**
3770 * Helper function:
3771 * Called synchronized on mRCStack
3772 */
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003773 private boolean isCurrentRcController(PendingIntent pi) {
3774 if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(pi)) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003775 return true;
3776 }
3777 return false;
3778 }
3779
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003780 //==========================================================================================
3781 // Remote control display / client
3782 //==========================================================================================
3783 /**
3784 * Update the remote control displays with the new "focused" client generation
3785 */
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07003786 private void setNewRcClientOnDisplays_syncRcsCurrc(int newClientGeneration,
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003787 PendingIntent newMediaIntent, boolean clearing) {
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003788 // NOTE: Only one IRemoteControlDisplay supported in this implementation
3789 if (mRcDisplay != null) {
3790 try {
Jean-Michel Trivi4b6df6a32011-08-24 10:31:13 -07003791 mRcDisplay.setCurrentClientId(
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003792 newClientGeneration, newMediaIntent, clearing);
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003793 } catch (RemoteException e) {
Jean-Michel Trividb8a3612011-08-26 19:10:51 -07003794 Log.e(TAG, "Dead display in setNewRcClientOnDisplays_syncRcsCurrc() "+e);
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003795 // if we had a display before, stop monitoring its death
3796 rcDisplay_stopDeathMonitor_syncRcStack();
3797 mRcDisplay = null;
3798 }
3799 }
3800 }
3801
3802 /**
3803 * Update the remote control clients with the new "focused" client generation
3804 */
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07003805 private void setNewRcClientGenerationOnClients_syncRcsCurrc(int newClientGeneration) {
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003806 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3807 while(stackIterator.hasNext()) {
3808 RemoteControlStackEntry se = stackIterator.next();
3809 if ((se != null) && (se.mRcClient != null)) {
3810 try {
3811 se.mRcClient.setCurrentClientGenerationId(newClientGeneration);
3812 } catch (RemoteException e) {
Jean-Michel Trividb8a3612011-08-26 19:10:51 -07003813 Log.w(TAG, "Dead client in setNewRcClientGenerationOnClients_syncRcsCurrc()"+e);
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003814 stackIterator.remove();
3815 se.unlinkToRcClientDeath();
3816 }
3817 }
3818 }
3819 }
3820
3821 /**
Jean-Michel Trivi4b6df6a32011-08-24 10:31:13 -07003822 * Update the displays and clients with the new "focused" client generation and name
3823 * @param newClientGeneration the new generation value matching a client update
3824 * @param newClientEventReceiver the media button event receiver associated with the client.
3825 * May be null, which implies there is no registered media button event receiver.
3826 * @param clearing true if the new client generation value maps to a remote control update
3827 * where the display should be cleared.
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003828 */
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07003829 private void setNewRcClient_syncRcsCurrc(int newClientGeneration,
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003830 PendingIntent newMediaIntent, boolean clearing) {
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07003831 // send the new valid client generation ID to all displays
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003832 setNewRcClientOnDisplays_syncRcsCurrc(newClientGeneration, newMediaIntent, clearing);
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07003833 // send the new valid client generation ID to all clients
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07003834 setNewRcClientGenerationOnClients_syncRcsCurrc(newClientGeneration);
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003835 }
3836
3837 /**
3838 * Called when processing MSG_RCDISPLAY_CLEAR event
3839 */
3840 private void onRcDisplayClear() {
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07003841 if (DEBUG_RC) Log.i(TAG, "Clear remote control display");
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003842
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07003843 synchronized(mRCStack) {
3844 synchronized(mCurrentRcLock) {
3845 mCurrentRcClientGen++;
3846 // synchronously update the displays and clients with the new client generation
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07003847 setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003848 null /*newMediaIntent*/, true /*clearing*/);
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07003849 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003850 }
3851 }
3852
3853 /**
3854 * Called when processing MSG_RCDISPLAY_UPDATE event
3855 */
3856 private void onRcDisplayUpdate(RemoteControlStackEntry rcse, int flags /* USED ?*/) {
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07003857 synchronized(mRCStack) {
3858 synchronized(mCurrentRcLock) {
3859 if ((mCurrentRcClient != null) && (mCurrentRcClient.equals(rcse.mRcClient))) {
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07003860 if (DEBUG_RC) Log.i(TAG, "Display/update remote control ");
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003861
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07003862 mCurrentRcClientGen++;
3863 // synchronously update the displays and clients with
3864 // the new client generation
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07003865 setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003866 rcse.mMediaIntent /*newMediaIntent*/,
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07003867 false /*clearing*/);
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003868
Jean-Michel Trividb8a3612011-08-26 19:10:51 -07003869 // tell the current client that it needs to send info
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07003870 try {
3871 mCurrentRcClient.onInformationRequested(mCurrentRcClientGen,
3872 flags, mArtworkExpectedWidth, mArtworkExpectedHeight);
3873 } catch (RemoteException e) {
3874 Log.e(TAG, "Current valid remote client is dead: "+e);
3875 mCurrentRcClient = null;
3876 }
3877 } else {
3878 // the remote control display owner has changed between the
3879 // the message to update the display was sent, and the time it
3880 // gets to be processed (now)
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003881 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003882 }
3883 }
3884 }
3885
3886
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003887 /**
3888 * Helper function:
3889 * Called synchronized on mRCStack
3890 */
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003891 private void clearRemoteControlDisplay_syncAfRcs() {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003892 synchronized(mCurrentRcLock) {
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07003893 mCurrentRcClient = null;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003894 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003895 // will cause onRcDisplayClear() to be called in AudioService's handler thread
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003896 mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_CLEAR) );
3897 }
3898
3899 /**
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003900 * Helper function for code readability: only to be called from
3901 * checkUpdateRemoteControlDisplay_syncAfRcs() which checks the preconditions for
3902 * this method.
3903 * Preconditions:
3904 * - called synchronized mAudioFocusLock then on mRCStack
3905 * - mRCStack.isEmpty() is false
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003906 */
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003907 private void updateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003908 RemoteControlStackEntry rcse = mRCStack.peek();
Jean-Michel Trivib5f04d62011-08-09 19:12:36 -07003909 int infoFlagsAboutToBeUsed = infoChangedFlags;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003910 // this is where we enforce opt-in for information display on the remote controls
3911 // with the new AudioManager.registerRemoteControlClient() API
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07003912 if (rcse.mRcClient == null) {
3913 //Log.w(TAG, "Can't update remote control display with null remote control client");
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003914 clearRemoteControlDisplay_syncAfRcs();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003915 return;
3916 }
3917 synchronized(mCurrentRcLock) {
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07003918 if (!rcse.mRcClient.equals(mCurrentRcClient)) {
Jean-Michel Trivi7b6198c2011-08-07 11:27:50 -07003919 // new RC client, assume every type of information shall be queried
Jean-Michel Trivib5f04d62011-08-09 19:12:36 -07003920 infoFlagsAboutToBeUsed = RC_INFO_ALL;
Jean-Michel Trivi7b6198c2011-08-07 11:27:50 -07003921 }
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07003922 mCurrentRcClient = rcse.mRcClient;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003923 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003924 // will cause onRcDisplayUpdate() to be called in AudioService's handler thread
Jean-Michel Trivib5f04d62011-08-09 19:12:36 -07003925 mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_UPDATE,
Jean-Michel Trivi0a9faa82011-08-14 17:36:42 -07003926 infoFlagsAboutToBeUsed /* arg1 */, 0, rcse /* obj, != null */) );
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003927 }
3928
3929 /**
3930 * Helper function:
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003931 * Called synchronized on mAudioFocusLock, then mRCStack
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003932 * Check whether the remote control display should be updated, triggers the update if required
Jean-Michel Trivib5f04d62011-08-09 19:12:36 -07003933 * @param infoChangedFlags the flags corresponding to the remote control client information
3934 * that has changed, if applicable (checking for the update conditions might trigger a
3935 * clear, rather than an update event).
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003936 */
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003937 private void checkUpdateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003938 // determine whether the remote control display should be refreshed
3939 // if either stack is empty, there is a mismatch, so clear the RC display
3940 if (mRCStack.isEmpty() || mFocusStack.isEmpty()) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003941 clearRemoteControlDisplay_syncAfRcs();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003942 return;
3943 }
3944 // if the top of the two stacks belong to different packages, there is a mismatch, clear
3945 if ((mRCStack.peek().mCallingPackageName != null)
3946 && (mFocusStack.peek().mPackageName != null)
3947 && !(mRCStack.peek().mCallingPackageName.compareTo(
3948 mFocusStack.peek().mPackageName) == 0)) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003949 clearRemoteControlDisplay_syncAfRcs();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003950 return;
3951 }
3952 // if the audio focus didn't originate from the same Uid as the one in which the remote
3953 // control information will be retrieved, clear
3954 if (mRCStack.peek().mCallingUid != mFocusStack.peek().mCallingUid) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003955 clearRemoteControlDisplay_syncAfRcs();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003956 return;
3957 }
3958 // refresh conditions were verified: update the remote controls
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003959 // ok to call: synchronized mAudioFocusLock then on mRCStack, mRCStack is not empty
3960 updateRemoteControlDisplay_syncAfRcs(infoChangedFlags);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003961 }
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003962
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003963 /**
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003964 * see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
3965 * precondition: mediaIntent != null, target != null
3966 */
3967 public void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver) {
3968 Log.i(TAG, " Remote Control registerMediaButtonIntent() for " + mediaIntent);
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003969
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003970 synchronized(mAudioFocusLock) {
3971 synchronized(mRCStack) {
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003972 pushMediaButtonReceiver(mediaIntent, eventReceiver);
Jean-Michel Trivib5f04d62011-08-09 19:12:36 -07003973 // new RC client, assume every type of information shall be queried
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003974 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003975 }
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003976 }
3977 }
3978
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003979 /**
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003980 * see AudioManager.unregisterMediaButtonIntent(PendingIntent mediaIntent)
3981 * precondition: mediaIntent != null, eventReceiver != null
3982 */
3983 public void unregisterMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver)
3984 {
3985 Log.i(TAG, " Remote Control unregisterMediaButtonIntent() for " + mediaIntent);
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003986
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003987 synchronized(mAudioFocusLock) {
3988 synchronized(mRCStack) {
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003989 boolean topOfStackWillChange = isCurrentRcController(mediaIntent);
3990 removeMediaButtonReceiver(mediaIntent);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003991 if (topOfStackWillChange) {
Jean-Michel Trivib5f04d62011-08-09 19:12:36 -07003992 // current RC client will change, assume every type of info needs to be queried
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003993 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003994 }
3995 }
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003996 }
3997 }
3998
Jean-Michel Trivi26b3d1f2011-09-28 11:05:15 -07003999 /**
4000 * see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...)
4001 * Note: using this method with rcClient == null is a way to "disable" the IRemoteControlClient
4002 * without modifying the RC stack, but while still causing the display to refresh (will
4003 * become blank as a result of this)
4004 */
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004005 public void registerRemoteControlClient(PendingIntent mediaIntent,
4006 IRemoteControlClient rcClient, String callingPackageName) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07004007 if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004008 synchronized(mAudioFocusLock) {
4009 synchronized(mRCStack) {
4010 // store the new display information
4011 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4012 while(stackIterator.hasNext()) {
4013 RemoteControlStackEntry rcse = stackIterator.next();
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004014 if(rcse.mMediaIntent.equals(mediaIntent)) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004015 // already had a remote control client?
4016 if (rcse.mRcClientDeathHandler != null) {
4017 // stop monitoring the old client's death
4018 rcse.unlinkToRcClientDeath();
4019 }
4020 // save the new remote control client
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07004021 rcse.mRcClient = rcClient;
Jean-Michel Trivi26b3d1f2011-09-28 11:05:15 -07004022 rcse.mCallingPackageName = callingPackageName;
4023 rcse.mCallingUid = Binder.getCallingUid();
4024 if (rcClient == null) {
Jean-Michel Trivia9509732011-11-14 15:23:04 -08004025 // here rcse.mRcClientDeathHandler is null;
Jean-Michel Trivi26b3d1f2011-09-28 11:05:15 -07004026 break;
4027 }
4028
4029 // there is a new (non-null) client:
4030 // 1/ give the new client the current display (if any)
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004031 if (mRcDisplay != null) {
4032 try {
4033 rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
4034 } catch (RemoteException e) {
4035 Log.e(TAG, "Error connecting remote control display to client: "+e);
4036 e.printStackTrace();
4037 }
4038 }
Jean-Michel Trivi26b3d1f2011-09-28 11:05:15 -07004039 // 2/ monitor the new client's death
4040 IBinder b = rcse.mRcClient.asBinder();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004041 RcClientDeathHandler rcdh =
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004042 new RcClientDeathHandler(b, rcse.mMediaIntent);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004043 try {
4044 b.linkToDeath(rcdh, 0);
4045 } catch (RemoteException e) {
4046 // remote control client is DOA, disqualify it
4047 Log.w(TAG, "registerRemoteControlClient() has a dead client " + b);
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07004048 rcse.mRcClient = null;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004049 }
4050 rcse.mRcClientDeathHandler = rcdh;
4051 break;
4052 }
4053 }
4054 // if the eventReceiver is at the top of the stack
4055 // then check for potential refresh of the remote controls
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004056 if (isCurrentRcController(mediaIntent)) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07004057 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004058 }
4059 }
4060 }
4061 }
4062
Jean-Michel Trivi4b6df6a32011-08-24 10:31:13 -07004063 /**
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004064 * see AudioManager.unregisterRemoteControlClient(PendingIntent pi, ...)
Jean-Michel Trivi4b6df6a32011-08-24 10:31:13 -07004065 * rcClient is guaranteed non-null
4066 */
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004067 public void unregisterRemoteControlClient(PendingIntent mediaIntent,
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004068 IRemoteControlClient rcClient) {
Jean-Michel Trivi4b6df6a32011-08-24 10:31:13 -07004069 synchronized(mAudioFocusLock) {
4070 synchronized(mRCStack) {
4071 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4072 while(stackIterator.hasNext()) {
4073 RemoteControlStackEntry rcse = stackIterator.next();
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004074 if ((rcse.mMediaIntent.equals(mediaIntent))
Jean-Michel Trivi4b6df6a32011-08-24 10:31:13 -07004075 && rcClient.equals(rcse.mRcClient)) {
4076 // we found the IRemoteControlClient to unregister
4077 // stop monitoring its death
4078 rcse.unlinkToRcClientDeath();
4079 // reset the client-related fields
4080 rcse.mRcClient = null;
Jean-Michel Trivi4b6df6a32011-08-24 10:31:13 -07004081 rcse.mCallingPackageName = null;
4082 }
4083 }
4084 }
4085 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004086 }
4087
4088 /**
4089 * The remote control displays.
4090 * Access synchronized on mRCStack
4091 * NOTE: Only one IRemoteControlDisplay supported in this implementation
4092 */
4093 private IRemoteControlDisplay mRcDisplay;
4094 private RcDisplayDeathHandler mRcDisplayDeathHandler;
4095 private int mArtworkExpectedWidth = -1;
4096 private int mArtworkExpectedHeight = -1;
4097 /**
4098 * Inner class to monitor remote control display deaths, and unregister them from the list
4099 * of displays if necessary.
4100 */
4101 private class RcDisplayDeathHandler implements IBinder.DeathRecipient {
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07004102 private IBinder mCb; // To be notified of client's death
4103
4104 public RcDisplayDeathHandler(IBinder b) {
4105 if (DEBUG_RC) Log.i(TAG, "new RcDisplayDeathHandler for "+b);
4106 mCb = b;
4107 }
4108
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004109 public void binderDied() {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004110 synchronized(mRCStack) {
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07004111 Log.w(TAG, "RemoteControl: display died");
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004112 mRcDisplay = null;
4113 }
4114 }
4115
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07004116 public void unlinkToRcDisplayDeath() {
4117 if (DEBUG_RC) Log.i(TAG, "unlinkToRcDisplayDeath for "+mCb);
4118 try {
4119 mCb.unlinkToDeath(this, 0);
4120 } catch (java.util.NoSuchElementException e) {
4121 // not much we can do here, the display was being unregistered anyway
4122 Log.e(TAG, "Encountered " + e + " in unlinkToRcDisplayDeath()");
4123 e.printStackTrace();
4124 }
4125 }
4126
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004127 }
4128
4129 private void rcDisplay_stopDeathMonitor_syncRcStack() {
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07004130 if (mRcDisplay != null) { // implies (mRcDisplayDeathHandler != null)
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004131 // we had a display before, stop monitoring its death
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07004132 mRcDisplayDeathHandler.unlinkToRcDisplayDeath();
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004133 }
4134 }
4135
4136 private void rcDisplay_startDeathMonitor_syncRcStack() {
4137 if (mRcDisplay != null) {
4138 // new non-null display, monitor its death
4139 IBinder b = mRcDisplay.asBinder();
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07004140 mRcDisplayDeathHandler = new RcDisplayDeathHandler(b);
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004141 try {
4142 b.linkToDeath(mRcDisplayDeathHandler, 0);
4143 } catch (RemoteException e) {
4144 // remote control display is DOA, disqualify it
4145 Log.w(TAG, "registerRemoteControlDisplay() has a dead client " + b);
4146 mRcDisplay = null;
4147 }
4148 }
4149 }
4150
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07004151 /**
Jean-Michel Trivi958bba62011-08-30 10:52:59 -07004152 * Register an IRemoteControlDisplay.
4153 * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient
4154 * at the top of the stack to update the new display with its information.
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07004155 * Since only one IRemoteControlDisplay is supported, this will unregister the previous display.
4156 * @param rcd the IRemoteControlDisplay to register. No effect if null.
4157 */
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004158 public void registerRemoteControlDisplay(IRemoteControlDisplay rcd) {
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07004159 if (DEBUG_RC) Log.d(TAG, ">>> registerRemoteControlDisplay("+rcd+")");
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07004160 synchronized(mAudioFocusLock) {
4161 synchronized(mRCStack) {
4162 if ((mRcDisplay == rcd) || (rcd == null)) {
4163 return;
4164 }
4165 // if we had a display before, stop monitoring its death
4166 rcDisplay_stopDeathMonitor_syncRcStack();
4167 mRcDisplay = rcd;
4168 // new display, start monitoring its death
4169 rcDisplay_startDeathMonitor_syncRcStack();
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004170
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07004171 // let all the remote control clients there is a new display
4172 // no need to unplug the previous because we only support one display
4173 // and the clients don't track the death of the display
4174 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4175 while(stackIterator.hasNext()) {
4176 RemoteControlStackEntry rcse = stackIterator.next();
4177 if(rcse.mRcClient != null) {
4178 try {
4179 rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
4180 } catch (RemoteException e) {
4181 Log.e(TAG, "Error connecting remote control display to client: " + e);
4182 e.printStackTrace();
4183 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004184 }
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004185 }
Jean-Michel Trividb8a3612011-08-26 19:10:51 -07004186
Jean-Michel Trivi33879452011-08-31 11:42:17 -07004187 // 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 -07004188 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
Jean-Michel Trivi33879452011-08-31 11:42:17 -07004189 }
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004190 }
4191 }
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004192
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07004193 /**
4194 * Unregister an IRemoteControlDisplay.
4195 * Since only one IRemoteControlDisplay is supported, this has no effect if the one to
4196 * unregister is not the current one.
4197 * @param rcd the IRemoteControlDisplay to unregister. No effect if null.
4198 */
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004199 public void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) {
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07004200 if (DEBUG_RC) Log.d(TAG, "<<< unregisterRemoteControlDisplay("+rcd+")");
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004201 synchronized(mRCStack) {
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07004202 // only one display here, so you can only unregister the current display
4203 if ((rcd == null) || (rcd != mRcDisplay)) {
4204 if (DEBUG_RC) Log.w(TAG, " trying to unregister unregistered RCD");
4205 return;
4206 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004207 // if we had a display before, stop monitoring its death
4208 rcDisplay_stopDeathMonitor_syncRcStack();
4209 mRcDisplay = null;
4210
4211 // disconnect this remote control display from all the clients
4212 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4213 while(stackIterator.hasNext()) {
4214 RemoteControlStackEntry rcse = stackIterator.next();
4215 if(rcse.mRcClient != null) {
4216 try {
4217 rcse.mRcClient.unplugRemoteControlDisplay(rcd);
4218 } catch (RemoteException e) {
4219 Log.e(TAG, "Error disconnecting remote control display to client: " + e);
4220 e.printStackTrace();
4221 }
4222 }
4223 }
4224 }
4225 }
4226
4227 public void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) {
4228 synchronized(mRCStack) {
4229 // NOTE: Only one IRemoteControlDisplay supported in this implementation
4230 mArtworkExpectedWidth = w;
4231 mArtworkExpectedHeight = h;
4232 }
4233 }
4234
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004235 @Override
Jeff Sharkey098d5802012-04-26 17:30:34 -07004236 public void setRingtonePlayer(IRingtonePlayer player) {
4237 mContext.enforceCallingOrSelfPermission(REMOTE_AUDIO_PLAYBACK, null);
4238 mRingtonePlayer = player;
4239 }
4240
4241 @Override
4242 public IRingtonePlayer getRingtonePlayer() {
4243 return mRingtonePlayer;
4244 }
4245
4246 @Override
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004247 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkeyeb4cc4922012-04-26 18:17:29 -07004248 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
4249
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004250 // 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 -08004251 dumpFocusStack(pw);
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004252 dumpRCStack(pw);
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004253 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004254}