blob: fdb6818d3912e2d0665865ce6cdd1cefb6a0cf06 [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
Jean-Michel Trivic6802222012-04-30 11:15:03 -070024import android.app.Activity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.app.ActivityManagerNative;
Amith Yamasani6243edd2011-12-05 19:58:48 -080026import android.app.KeyguardManager;
Jean-Michel Trivif0cff042011-09-14 18:11:09 -070027import android.app.PendingIntent;
28import android.app.PendingIntent.CanceledException;
Jean-Michel Trivic6802222012-04-30 11:15:03 -070029import android.app.PendingIntent.OnFinished;
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -070030import android.bluetooth.BluetoothA2dp;
31import android.bluetooth.BluetoothAdapter;
32import android.bluetooth.BluetoothClass;
33import android.bluetooth.BluetoothDevice;
34import android.bluetooth.BluetoothHeadset;
35import android.bluetooth.BluetoothProfile;
Jean-Michel Trivi4294b832012-04-30 17:39:29 -070036import android.content.ActivityNotFoundException;
Nick Pellybd022f42009-08-14 18:33:38 -070037import android.content.BroadcastReceiver;
Jean-Michel Trivid327f212010-03-16 21:44:33 -070038import android.content.ComponentName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.content.ContentResolver;
40import android.content.Context;
41import android.content.Intent;
Eric Laurenta553c252009-07-17 12:17:14 -070042import android.content.IntentFilter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.content.pm.PackageManager;
Jean-Michel Trivif26f0172012-04-25 16:23:20 -070044import android.content.res.Configuration;
Jason Parekhb1096152009-03-24 17:48:25 -070045import android.database.ContentObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.media.MediaPlayer.OnCompletionListener;
47import android.media.MediaPlayer.OnErrorListener;
48import android.os.Binder;
Jean-Michel Trivif0cff042011-09-14 18:11:09 -070049import android.os.Bundle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import android.os.Environment;
51import android.os.Handler;
52import android.os.IBinder;
53import android.os.Looper;
54import android.os.Message;
Jean-Michel Trivic6802222012-04-30 11:15:03 -070055import android.os.PowerManager;
Dianne Hackborn632ca412012-06-14 19:34:10 -070056import android.os.RemoteCallbackList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057import android.os.RemoteException;
58import android.os.ServiceManager;
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -070059import android.os.SystemProperties;
Dianne Hackborn5ac72a22012-08-29 18:32:08 -070060import android.os.UserHandle;
Eric Laurentbffc3d12012-05-07 17:43:49 -070061import android.os.Vibrator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062import android.provider.Settings;
63import android.provider.Settings.System;
Jean-Michel Trivi3c2711f2012-05-20 12:07:03 -070064import android.speech.RecognizerIntent;
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -070065import android.telephony.PhoneStateListener;
Eric Laurentd640bd32012-09-28 18:01:48 -070066import android.telephony.ServiceState;
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -070067import android.telephony.TelephonyManager;
Dianne Hackborn632ca412012-06-14 19:34:10 -070068import android.text.TextUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069import android.util.Log;
Jean-Michel Trivid327f212010-03-16 21:44:33 -070070import android.view.KeyEvent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071import android.view.VolumePanel;
72
73import com.android.internal.telephony.ITelephony;
74
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -080075import java.io.FileDescriptor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076import java.io.IOException;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -080077import java.io.PrintWriter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078import java.util.ArrayList;
Eric Laurent3172d5e2012-05-09 11:38:16 -070079import java.util.concurrent.ConcurrentHashMap;
Eric Laurentc42ac9d2009-07-29 08:53:03 -070080import java.util.HashMap;
81import java.util.Iterator;
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -070082import java.util.List;
Eric Laurentc42ac9d2009-07-29 08:53:03 -070083import java.util.Map;
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -070084import java.util.NoSuchElementException;
Eric Laurentc42ac9d2009-07-29 08:53:03 -070085import java.util.Set;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -080086import java.util.Stack;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087
88/**
89 * The implementation of the volume manager service.
90 * <p>
91 * This implementation focuses on delivering a responsive UI. Most methods are
92 * asynchronous to external calls. For example, the task of setting a volume
93 * will update our internal state, but in a separate thread will set the system
94 * volume and later persist to the database. Similarly, setting the ringer mode
95 * will update the state and broadcast a change and in a separate thread later
96 * persist the ringer mode.
97 *
98 * @hide
99 */
Jean-Michel Trivic6802222012-04-30 11:15:03 -0700100public class AudioService extends IAudioService.Stub implements OnFinished {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101
102 private static final String TAG = "AudioService";
103
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -0700104 /** Debug remote control client/display feature */
Jean-Michel Trivi7ff866e2011-10-13 18:09:26 -0700105 protected static final boolean DEBUG_RC = false;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700106 /** Debug volumes */
107 protected static final boolean DEBUG_VOL = false;
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -0700108
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 /** How long to delay before persisting a change in volume/ringer mode. */
RoboErik45edba12012-03-27 17:54:36 -0700110 private static final int PERSIST_DELAY = 500;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111
112 private Context mContext;
113 private ContentResolver mContentResolver;
Joe Onoratoc7fcba42011-01-05 16:53:11 -0800114 private boolean mVoiceCapable;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -0800115
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800116 /** The UI */
117 private VolumePanel mVolumePanel;
118
119 // sendMsg() flags
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120 /** If the msg is already queued, replace it with this one. */
121 private static final int SENDMSG_REPLACE = 0;
122 /** If the msg is already queued, ignore this one and leave the old. */
123 private static final int SENDMSG_NOOP = 1;
124 /** If the msg is already queued, queue this one and leave the old. */
125 private static final int SENDMSG_QUEUE = 2;
126
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700127 // AudioHandler messages
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800128 private static final int MSG_SET_DEVICE_VOLUME = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129 private static final int MSG_PERSIST_VOLUME = 1;
Mike Lockwood5c55a052011-12-15 17:21:44 -0500130 private static final int MSG_PERSIST_MASTER_VOLUME = 2;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131 private static final int MSG_PERSIST_RINGER_MODE = 3;
Eric Laurentbffc3d12012-05-07 17:43:49 -0700132 private static final int MSG_MEDIA_SERVER_DIED = 4;
133 private static final int MSG_MEDIA_SERVER_STARTED = 5;
134 private static final int MSG_PLAY_SOUND_EFFECT = 6;
135 private static final int MSG_BTA2DP_DOCK_TIMEOUT = 7;
136 private static final int MSG_LOAD_SOUND_EFFECTS = 8;
137 private static final int MSG_SET_FORCE_USE = 9;
138 private static final int MSG_PERSIST_MEDIABUTTONRECEIVER = 10;
139 private static final int MSG_BT_HEADSET_CNCT_FAILED = 11;
140 private static final int MSG_RCDISPLAY_CLEAR = 12;
141 private static final int MSG_RCDISPLAY_UPDATE = 13;
142 private static final int MSG_SET_ALL_VOLUMES = 14;
143 private static final int MSG_PERSIST_MASTER_VOLUME_MUTE = 15;
Dianne Hackborn632ca412012-06-14 19:34:10 -0700144 private static final int MSG_REPORT_NEW_ROUTES = 16;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700145 private static final int MSG_REEVALUATE_REMOTE = 17;
Jean-Michel Trivif98de1e2012-06-20 13:40:22 -0700146 private static final int MSG_RCC_NEW_PLAYBACK_INFO = 18;
147 private static final int MSG_RCC_NEW_VOLUME_OBS = 19;
Eric Laurentc390bed2012-07-03 12:24:05 -0700148 private static final int MSG_SET_FORCE_BT_A2DP_USE = 20;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700149 // start of messages handled under wakelock
150 // these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
Jean-Michel Trivie12c39b2012-06-06 10:51:58 -0700151 // and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
Eric Laurentc390bed2012-07-03 12:24:05 -0700152 private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 21;
153 private static final int MSG_SET_A2DP_CONNECTION_STATE = 22;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700154 // end of messages handled under wakelock
Jean-Michel Trivi2ac2afe2012-08-21 11:16:55 -0700155 private static final int MSG_SET_RSX_CONNECTION_STATE = 23; // change remote submix connection
156 private static final int MSG_SET_FORCE_RSX_USE = 24; // force remote submix audio routing
Eric Laurentc34dcc12012-09-10 13:51:52 -0700157 private static final int MSG_CHECK_MUSIC_ACTIVE = 25;
Eric Laurent5bfaeae2012-09-21 18:44:48 -0700158 private static final int MSG_BROADCAST_AUDIO_BECOMING_NOISY = 26;
Eric Laurentd640bd32012-09-28 18:01:48 -0700159 private static final int MSG_CONFIGURE_SAFE_MEDIA_VOLUME = 27;
160 private static final int MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED = 28;
Eric Laurentafbb0472011-12-15 09:04:23 -0800161
162 // flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be
163 // persisted
164 private static final int PERSIST_CURRENT = 0x1;
165 private static final int PERSIST_LAST_AUDIBLE = 0x2;
166
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -0700167 private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
Eric Laurentdc03c612011-04-01 10:59:41 -0700168 // Timeout for connection to bluetooth headset service
169 private static final int BT_HEADSET_CNCT_TIMEOUT_MS = 3000;
170
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 /** @see AudioSystemThread */
172 private AudioSystemThread mAudioSystemThread;
173 /** @see AudioHandler */
174 private AudioHandler mAudioHandler;
175 /** @see VolumeStreamState */
176 private VolumeStreamState[] mStreamStates;
Jason Parekhb1096152009-03-24 17:48:25 -0700177 private SettingsObserver mSettingsObserver;
Eric Laurenta553c252009-07-17 12:17:14 -0700178
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 private int mMode;
Glenn Kastenba195eb2011-12-13 09:30:40 -0800180 // protects mRingerMode
181 private final Object mSettingsLock = new Object();
Eric Laurent45c90ce2012-04-24 18:44:22 -0700182
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183 private boolean mMediaServerOk;
184
185 private SoundPool mSoundPool;
Glenn Kasten30c918c2011-11-10 17:56:41 -0800186 private final Object mSoundEffectsLock = new Object();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800187 private static final int NUM_SOUNDPOOL_CHANNELS = 4;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188
Mike Lockwood47676902011-11-08 10:31:21 -0800189 // Internally master volume is a float in the 0.0 - 1.0 range,
190 // but to support integer based AudioManager API we translate it to 0 - 100
191 private static final int MAX_MASTER_VOLUME = 100;
192
Lei Zhang6c798972012-03-02 11:40:12 -0800193 // Maximum volume adjust steps allowed in a single batch call.
194 private static final int MAX_BATCH_VOLUME_ADJUST_STEPS = 4;
195
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 /* Sound effect file names */
197 private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/";
198 private static final String[] SOUND_EFFECT_FILES = new String[] {
199 "Effect_Tick.ogg",
200 "KeypressStandard.ogg",
201 "KeypressSpacebar.ogg",
202 "KeypressDelete.ogg",
203 "KeypressReturn.ogg"
204 };
205
206 /* Sound effect file name mapping sound effect id (AudioManager.FX_xxx) to
207 * file index in SOUND_EFFECT_FILES[] (first column) and indicating if effect
208 * uses soundpool (second column) */
Glenn Kasten30c918c2011-11-10 17:56:41 -0800209 private final int[][] SOUND_EFFECT_FILES_MAP = new int[][] {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210 {0, -1}, // FX_KEY_CLICK
211 {0, -1}, // FX_FOCUS_NAVIGATION_UP
212 {0, -1}, // FX_FOCUS_NAVIGATION_DOWN
213 {0, -1}, // FX_FOCUS_NAVIGATION_LEFT
214 {0, -1}, // FX_FOCUS_NAVIGATION_RIGHT
215 {1, -1}, // FX_KEYPRESS_STANDARD
216 {2, -1}, // FX_KEYPRESS_SPACEBAR
217 {3, -1}, // FX_FOCUS_DELETE
218 {4, -1} // FX_FOCUS_RETURN
219 };
220
Jared Suttles59820132009-08-13 21:50:52 -0500221 /** @hide Maximum volume index values for audio streams */
Glenn Kasten30c918c2011-11-10 17:56:41 -0800222 private final int[] MAX_STREAM_VOLUME = new int[] {
Eric Laurent6ee99522009-08-25 06:30:59 -0700223 5, // STREAM_VOICE_CALL
224 7, // STREAM_SYSTEM
225 7, // STREAM_RING
226 15, // STREAM_MUSIC
227 7, // STREAM_ALARM
228 7, // STREAM_NOTIFICATION
229 15, // STREAM_BLUETOOTH_SCO
230 7, // STREAM_SYSTEM_ENFORCED
231 15, // STREAM_DTMF
232 15 // STREAM_TTS
Jared Suttles59820132009-08-13 21:50:52 -0500233 };
Eric Laurent6d517662012-04-23 18:42:39 -0700234 /* mStreamVolumeAlias[] indicates for each stream if it uses the volume settings
Eric Laurenta553c252009-07-17 12:17:14 -0700235 * of another stream: This avoids multiplying the volume settings for hidden
236 * stream types that follow other stream behavior for volume settings
Eric Laurent6d517662012-04-23 18:42:39 -0700237 * NOTE: do not create loops in aliases!
238 * Some streams alias to different streams according to device category (phone or tablet) or
239 * use case (in call s off call...).See updateStreamVolumeAlias() for more details
240 * mStreamVolumeAlias contains the default aliases for a voice capable device (phone) and
241 * STREAM_VOLUME_ALIAS_NON_VOICE for a non voice capable device (tablet).*/
Glenn Kasten30c918c2011-11-10 17:56:41 -0800242 private final int[] STREAM_VOLUME_ALIAS = new int[] {
Eric Laurent6d517662012-04-23 18:42:39 -0700243 AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL
244 AudioSystem.STREAM_RING, // STREAM_SYSTEM
245 AudioSystem.STREAM_RING, // STREAM_RING
246 AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
247 AudioSystem.STREAM_ALARM, // STREAM_ALARM
248 AudioSystem.STREAM_RING, // STREAM_NOTIFICATION
249 AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO
250 AudioSystem.STREAM_RING, // STREAM_SYSTEM_ENFORCED
251 AudioSystem.STREAM_RING, // STREAM_DTMF
252 AudioSystem.STREAM_MUSIC // STREAM_TTS
Eric Laurenta553c252009-07-17 12:17:14 -0700253 };
Eric Laurent6d517662012-04-23 18:42:39 -0700254 private final int[] STREAM_VOLUME_ALIAS_NON_VOICE = new int[] {
255 AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL
256 AudioSystem.STREAM_MUSIC, // STREAM_SYSTEM
257 AudioSystem.STREAM_RING, // STREAM_RING
258 AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
259 AudioSystem.STREAM_ALARM, // STREAM_ALARM
260 AudioSystem.STREAM_RING, // STREAM_NOTIFICATION
261 AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO
262 AudioSystem.STREAM_MUSIC, // STREAM_SYSTEM_ENFORCED
263 AudioSystem.STREAM_MUSIC, // STREAM_DTMF
264 AudioSystem.STREAM_MUSIC // STREAM_TTS
265 };
266 private int[] mStreamVolumeAlias;
Eric Laurenta553c252009-07-17 12:17:14 -0700267
Eric Laurentbffc3d12012-05-07 17:43:49 -0700268 // stream names used by dumpStreamStates()
269 private final String[] STREAM_NAMES = new String[] {
270 "STREAM_VOICE_CALL",
271 "STREAM_SYSTEM",
272 "STREAM_RING",
273 "STREAM_MUSIC",
274 "STREAM_ALARM",
275 "STREAM_NOTIFICATION",
276 "STREAM_BLUETOOTH_SCO",
277 "STREAM_SYSTEM_ENFORCED",
278 "STREAM_DTMF",
279 "STREAM_TTS"
280 };
281
Glenn Kasten30c918c2011-11-10 17:56:41 -0800282 private final AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800283 public void onError(int error) {
284 switch (error) {
285 case AudioSystem.AUDIO_STATUS_SERVER_DIED:
286 if (mMediaServerOk) {
Eric Laurentafbb0472011-12-15 09:04:23 -0800287 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SENDMSG_NOOP, 0, 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288 null, 1500);
Eric Laurent89e74ba2009-09-30 18:26:36 -0700289 mMediaServerOk = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800290 }
291 break;
292 case AudioSystem.AUDIO_STATUS_OK:
293 if (!mMediaServerOk) {
Eric Laurentafbb0472011-12-15 09:04:23 -0800294 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_STARTED, SENDMSG_NOOP, 0, 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295 null, 0);
Eric Laurent89e74ba2009-09-30 18:26:36 -0700296 mMediaServerOk = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 }
298 break;
299 default:
300 break;
301 }
302 }
303 };
304
305 /**
306 * Current ringer mode from one of {@link AudioManager#RINGER_MODE_NORMAL},
307 * {@link AudioManager#RINGER_MODE_SILENT}, or
308 * {@link AudioManager#RINGER_MODE_VIBRATE}.
309 */
Glenn Kastenba195eb2011-12-13 09:30:40 -0800310 // protected by mSettingsLock
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800311 private int mRingerMode;
312
Eric Laurent9bcf4012009-06-12 06:09:28 -0700313 /** @see System#MODE_RINGER_STREAMS_AFFECTED */
314 private int mRingerModeAffectedStreams;
315
Eric Laurent5b4e6542010-03-19 20:02:21 -0700316 // Streams currently muted by ringer mode
317 private int mRingerModeMutedStreams;
318
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800319 /** @see System#MUTE_STREAMS_AFFECTED */
320 private int mMuteAffectedStreams;
321
322 /**
Eric Laurentbffc3d12012-05-07 17:43:49 -0700323 * NOTE: setVibrateSetting(), getVibrateSetting(), shouldVibrate() are deprecated.
324 * mVibrateSetting is just maintained during deprecation period but vibration policy is
325 * now only controlled by mHasVibrator and mRingerMode
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800326 */
327 private int mVibrateSetting;
328
Eric Laurentbffc3d12012-05-07 17:43:49 -0700329 // Is there a vibrator
330 private final boolean mHasVibrator;
331
Eric Laurenta553c252009-07-17 12:17:14 -0700332 // Broadcast receiver for device connections intent broadcasts
333 private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
334
Jean-Michel Trivie73131a2010-06-14 09:53:30 -0700335 // Used to alter media button redirection when the phone is ringing.
336 private boolean mIsRinging = false;
337
Eric Laurentc42ac9d2009-07-29 08:53:03 -0700338 // Devices currently connected
Glenn Kasten30c918c2011-11-10 17:56:41 -0800339 private final HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>();
Eric Laurentc42ac9d2009-07-29 08:53:03 -0700340
341 // Forced device usage for communications
342 private int mForcedUseForComm;
343
Mike Lockwood0dc37cc2011-12-01 16:14:19 -0500344 // True if we have master volume support
345 private final boolean mUseMasterVolume;
346
Mike Lockwood97606472012-02-09 11:24:10 -0800347 private final int[] mMasterVolumeRamp;
348
Eric Laurent9272b4b2010-01-23 17:12:59 -0800349 // List of binder death handlers for setMode() client processes.
350 // The last process to have called setMode() is at the top of the list.
Glenn Kasten30c918c2011-11-10 17:56:41 -0800351 private final ArrayList <SetModeDeathHandler> mSetModeDeathHandlers = new ArrayList <SetModeDeathHandler>();
Eric Laurenteb14a782009-12-17 03:12:59 -0800352
Eric Laurent3def1ee2010-03-17 23:26:26 -0700353 // List of clients having issued a SCO start request
Glenn Kasten30c918c2011-11-10 17:56:41 -0800354 private final ArrayList <ScoClient> mScoClients = new ArrayList <ScoClient>();
Eric Laurent3def1ee2010-03-17 23:26:26 -0700355
356 // BluetoothHeadset API to control SCO connection
357 private BluetoothHeadset mBluetoothHeadset;
358
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -0700359 // Bluetooth headset device
360 private BluetoothDevice mBluetoothHeadsetDevice;
Eric Laurent3def1ee2010-03-17 23:26:26 -0700361
Eric Laurent62ef7672010-11-24 10:58:32 -0800362 // Indicate if SCO audio connection is currently active and if the initiator is
363 // audio service (internal) or bluetooth headset (external)
364 private int mScoAudioState;
365 // SCO audio state is not active
366 private static final int SCO_STATE_INACTIVE = 0;
Eric Laurentdc03c612011-04-01 10:59:41 -0700367 // SCO audio activation request waiting for headset service to connect
368 private static final int SCO_STATE_ACTIVATE_REQ = 1;
Eric Laurent62ef7672010-11-24 10:58:32 -0800369 // SCO audio state is active or starting due to a local request to start a virtual call
Eric Laurentdc03c612011-04-01 10:59:41 -0700370 private static final int SCO_STATE_ACTIVE_INTERNAL = 3;
371 // SCO audio deactivation request waiting for headset service to connect
372 private static final int SCO_STATE_DEACTIVATE_REQ = 5;
373
Eric Laurent62ef7672010-11-24 10:58:32 -0800374 // SCO audio state is active due to an action in BT handsfree (either voice recognition or
375 // in call audio)
376 private static final int SCO_STATE_ACTIVE_EXTERNAL = 2;
Eric Laurentdc03c612011-04-01 10:59:41 -0700377 // Deactivation request for all SCO connections (initiated by audio mode change)
378 // waiting for headset service to connect
379 private static final int SCO_STATE_DEACTIVATE_EXT_REQ = 4;
380
381 // Current connection state indicated by bluetooth headset
382 private int mScoConnectionState;
Eric Laurent62ef7672010-11-24 10:58:32 -0800383
Eric Laurenta60e2122010-12-28 16:49:07 -0800384 // true if boot sequence has been completed
385 private boolean mBootCompleted;
386 // listener for SoundPool sample load completion indication
387 private SoundPoolCallback mSoundPoolCallBack;
388 // thread for SoundPool listener
389 private SoundPoolListenerThread mSoundPoolListenerThread;
390 // message looper for SoundPool listener
391 private Looper mSoundPoolLooper = null;
Jean-Michel Trivic55b3932012-06-05 11:57:59 -0700392 // volume applied to sound played with playSoundEffect()
Jean-Michel Trivif2b0c112012-07-09 11:59:11 -0700393 private static int sSoundEffectVolumeDb;
Eric Laurent9903e262012-09-21 18:10:32 -0700394 // getActiveStreamType() will return:
395 // - STREAM_NOTIFICATION on tablets during this period after a notification stopped
396 // - STREAM_MUSIC on phones during this period after music or talkback/voice search prompt
Eric Laurent25101b02011-02-02 09:33:30 -0800397 // stopped
Eric Laurent9903e262012-09-21 18:10:32 -0700398 private static final int DEFAULT_STREAM_TYPE_OVERRIDE_DELAY_MS = 5000;
Eric Laurent25101b02011-02-02 09:33:30 -0800399 // previous volume adjustment direction received by checkForRingerModeChange()
400 private int mPrevVolDirection = AudioManager.ADJUST_SAME;
Amith Yamasani6243edd2011-12-05 19:58:48 -0800401 // Keyguard manager proxy
402 private KeyguardManager mKeyguardManager;
Eric Laurent45c90ce2012-04-24 18:44:22 -0700403 // mVolumeControlStream is set by VolumePanel to temporarily force the stream type which volume
404 // is controlled by Vol keys.
405 private int mVolumeControlStream = -1;
406 private final Object mForceControlStreamLock = new Object();
407 // VolumePanel is currently the only client of forceVolumeControlStream() and runs in system
408 // server process so in theory it is not necessary to monitor the client death.
409 // However it is good to be ready for future evolutions.
410 private ForceControlStreamClient mForceControlStreamClient = null;
Jeff Sharkey098d5802012-04-26 17:30:34 -0700411 // Used to play ringtones outside system_server
412 private volatile IRingtonePlayer mRingtonePlayer;
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800413
Jean-Michel Trivif26f0172012-04-25 16:23:20 -0700414 private int mDeviceOrientation = Configuration.ORIENTATION_UNDEFINED;
415
Eric Laurent78472112012-05-21 08:57:21 -0700416 // Request to override default use of A2DP for media.
417 private boolean mBluetoothA2dpEnabled;
418 private final Object mBluetoothA2dpEnabledLock = new Object();
419
Dianne Hackborn632ca412012-06-14 19:34:10 -0700420 // Monitoring of audio routes. Protected by mCurAudioRoutes.
421 final AudioRoutesInfo mCurAudioRoutes = new AudioRoutesInfo();
422 final RemoteCallbackList<IAudioRoutesObserver> mRoutesObservers
423 = new RemoteCallbackList<IAudioRoutesObserver>();
424
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700425 /**
426 * A fake stream type to match the notion of remote media playback
427 */
428 public final static int STREAM_REMOTE_MUSIC = -200;
429
Eric Laurent4bbcc652012-09-24 14:26:30 -0700430 // Devices for which the volume is fixed and VolumePanel slider should be disabled
431 final int mFixedVolumeDevices = AudioSystem.DEVICE_OUT_AUX_DIGITAL |
432 AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET |
433 AudioSystem.DEVICE_OUT_ALL_USB;
434
Eric Laurentd640bd32012-09-28 18:01:48 -0700435 private final boolean mMonitorOrientation;
436
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800437 ///////////////////////////////////////////////////////////////////////////
438 // Construction
439 ///////////////////////////////////////////////////////////////////////////
440
441 /** @hide */
442 public AudioService(Context context) {
443 mContext = context;
444 mContentResolver = context.getContentResolver();
Joe Onoratoc7fcba42011-01-05 16:53:11 -0800445 mVoiceCapable = mContext.getResources().getBoolean(
446 com.android.internal.R.bool.config_voice_capable);
Jared Suttles59820132009-08-13 21:50:52 -0500447
Jean-Michel Trivic6802222012-04-30 11:15:03 -0700448 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
Jean-Michel Trivi2d8dab52012-05-30 18:13:59 -0700449 mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
Jean-Michel Trivic6802222012-04-30 11:15:03 -0700450
Eric Laurentbffc3d12012-05-07 17:43:49 -0700451 Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
452 mHasVibrator = vibrator == null ? false : vibrator.hasVibrator();
453
Jared Suttles59820132009-08-13 21:50:52 -0500454 // Intialized volume
455 MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = SystemProperties.getInt(
456 "ro.config.vc_call_vol_steps",
457 MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL]);
458
Jean-Michel Trivif2b0c112012-07-09 11:59:11 -0700459 sSoundEffectVolumeDb = context.getResources().getInteger(
Jean-Michel Trivic55b3932012-06-05 11:57:59 -0700460 com.android.internal.R.integer.config_soundEffectVolumeDb);
Eric Laurent25101b02011-02-02 09:33:30 -0800461
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800462 mVolumePanel = new VolumePanel(context, this);
Eric Laurent6d517662012-04-23 18:42:39 -0700463 mMode = AudioSystem.MODE_NORMAL;
Eric Laurentc42ac9d2009-07-29 08:53:03 -0700464 mForcedUseForComm = AudioSystem.FORCE_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800465 createAudioSystemThread();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800466 readPersistedSettings();
Eric Laurentc1d41662011-07-19 11:21:13 -0700467 mSettingsObserver = new SettingsObserver();
Eric Laurent6d517662012-04-23 18:42:39 -0700468 updateStreamVolumeAlias(false /*updateVolumes*/);
Eric Laurenta553c252009-07-17 12:17:14 -0700469 createStreamStates();
Eric Laurent9f103de2011-09-08 15:04:23 -0700470
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800471 mMediaServerOk = true;
Eric Laurent3891c4c2010-04-20 09:40:57 -0700472
Eric Laurentd640bd32012-09-28 18:01:48 -0700473 mSafeMediaVolumeState = new Integer(SAFE_MEDIA_VOLUME_NOT_CONFIGURED);
474
Eric Laurent3891c4c2010-04-20 09:40:57 -0700475 // Call setRingerModeInt() to apply correct mute
476 // state on streams affected by ringer mode.
477 mRingerModeMutedStreams = 0;
478 setRingerModeInt(getRingerMode(), false);
479
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800480 AudioSystem.setErrorCallback(mAudioSystemCallback);
Eric Laurenta553c252009-07-17 12:17:14 -0700481
482 // Register for device connection intent broadcasts.
483 IntentFilter intentFilter =
Eric Laurentb1fbaac2012-05-29 09:24:28 -0700484 new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -0700485 intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
486 intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
Eric Laurent59f48272012-04-05 19:42:21 -0700487 intentFilter.addAction(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG);
488 intentFilter.addAction(Intent.ACTION_USB_AUDIO_DEVICE_PLUG);
Eric Laurent62ef7672010-11-24 10:58:32 -0800489 intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
Eric Laurent950e8cb2011-10-13 08:57:54 -0700490 intentFilter.addAction(Intent.ACTION_SCREEN_ON);
491 intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
Eric Laurentbc0fab1f2012-09-19 11:24:41 -0700492 intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
Jean-Michel Trivif26f0172012-04-25 16:23:20 -0700493
Eric Laurentd640bd32012-09-28 18:01:48 -0700494 intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
Jean-Michel Trivif26f0172012-04-25 16:23:20 -0700495 // Register a configuration change listener only if requested by system properties
496 // to monitor orientation changes (off by default)
Eric Laurentd640bd32012-09-28 18:01:48 -0700497 mMonitorOrientation = SystemProperties.getBoolean("ro.audio.monitorOrientation", false);
498 if (mMonitorOrientation) {
Jean-Michel Trivif26f0172012-04-25 16:23:20 -0700499 Log.v(TAG, "monitoring device orientation");
Jean-Michel Trivif26f0172012-04-25 16:23:20 -0700500 // initialize orientation in AudioSystem
501 setOrientationForAudioSystem();
502 }
503
Eric Laurenta553c252009-07-17 12:17:14 -0700504 context.registerReceiver(mReceiver, intentFilter);
Jared Suttles59820132009-08-13 21:50:52 -0500505
Jean-Michel Trivid589fea2011-04-15 11:28:10 -0700506 // Register for package removal intent broadcasts for media button receiver persistence
507 IntentFilter pkgFilter = new IntentFilter();
508 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
509 pkgFilter.addDataScheme("package");
510 context.registerReceiver(mReceiver, pkgFilter);
511
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -0700512 // Register for phone state monitoring
513 TelephonyManager tmgr = (TelephonyManager)
514 context.getSystemService(Context.TELEPHONY_SERVICE);
515 tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
Mike Lockwood8517e462011-10-25 14:47:19 -0400516
Mike Lockwood0dc37cc2011-12-01 16:14:19 -0500517 mUseMasterVolume = context.getResources().getBoolean(
518 com.android.internal.R.bool.config_useMasterVolume);
Mike Lockwood90631542012-01-06 11:20:37 -0500519 restoreMasterVolume();
Mike Lockwood97606472012-02-09 11:24:10 -0800520
521 mMasterVolumeRamp = context.getResources().getIntArray(
522 com.android.internal.R.array.config_masterVolumeRamp);
Eric Laurent78472112012-05-21 08:57:21 -0700523
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700524 mMainRemote = new RemotePlaybackState(-1, MAX_STREAM_VOLUME[AudioManager.STREAM_MUSIC],
525 MAX_STREAM_VOLUME[AudioManager.STREAM_MUSIC]);
526 mHasRemotePlayback = false;
527 mMainRemoteIsActive = false;
528 postReevaluateRemote();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 }
530
531 private void createAudioSystemThread() {
532 mAudioSystemThread = new AudioSystemThread();
533 mAudioSystemThread.start();
534 waitForAudioHandlerCreation();
535 }
536
537 /** Waits for the volume handler to be created by the other thread. */
538 private void waitForAudioHandlerCreation() {
539 synchronized(this) {
540 while (mAudioHandler == null) {
541 try {
542 // Wait for mAudioHandler to be set by the other thread
543 wait();
544 } catch (InterruptedException e) {
545 Log.e(TAG, "Interrupted while waiting on volume handler.");
546 }
547 }
548 }
549 }
550
Eric Laurent24482012012-05-10 09:41:17 -0700551 private void checkAllAliasStreamVolumes() {
552 int numStreamTypes = AudioSystem.getNumStreamTypes();
553 for (int streamType = 0; streamType < numStreamTypes; streamType++) {
554 if (streamType != mStreamVolumeAlias[streamType]) {
555 mStreamStates[streamType].
556 setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]],
557 false /*lastAudible*/);
558 mStreamStates[streamType].
559 setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]],
560 true /*lastAudible*/);
561 }
562 // apply stream volume
563 if (mStreamStates[streamType].muteCount() == 0) {
564 mStreamStates[streamType].applyAllVolumes();
565 }
566 }
567 }
568
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800569 private void createStreamStates() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800570 int numStreamTypes = AudioSystem.getNumStreamTypes();
571 VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
572
573 for (int i = 0; i < numStreamTypes; i++) {
Eric Laurent6d517662012-04-23 18:42:39 -0700574 streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[mStreamVolumeAlias[i]], i);
Eric Laurenta553c252009-07-17 12:17:14 -0700575 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800576
Eric Laurent24482012012-05-10 09:41:17 -0700577 checkAllAliasStreamVolumes();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800578 }
579
Eric Laurentbffc3d12012-05-07 17:43:49 -0700580 private void dumpStreamStates(PrintWriter pw) {
581 pw.println("\nStream volumes (device: index)");
582 int numStreamTypes = AudioSystem.getNumStreamTypes();
583 for (int i = 0; i < numStreamTypes; i++) {
584 pw.println("- "+STREAM_NAMES[i]+":");
585 mStreamStates[i].dump(pw);
586 pw.println("");
587 }
588 }
589
Eric Laurent6d517662012-04-23 18:42:39 -0700590
591 private void updateStreamVolumeAlias(boolean updateVolumes) {
592 int dtmfStreamAlias;
593 if (mVoiceCapable) {
594 mStreamVolumeAlias = STREAM_VOLUME_ALIAS;
595 dtmfStreamAlias = AudioSystem.STREAM_RING;
596 } else {
597 mStreamVolumeAlias = STREAM_VOLUME_ALIAS_NON_VOICE;
598 dtmfStreamAlias = AudioSystem.STREAM_MUSIC;
599 }
600 if (isInCommunication()) {
601 dtmfStreamAlias = AudioSystem.STREAM_VOICE_CALL;
602 }
603 mStreamVolumeAlias[AudioSystem.STREAM_DTMF] = dtmfStreamAlias;
604 if (updateVolumes) {
605 mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias],
606 false /*lastAudible*/);
607 mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias],
608 true /*lastAudible*/);
609 sendMsg(mAudioHandler,
610 MSG_SET_ALL_VOLUMES,
611 SENDMSG_QUEUE,
612 0,
613 0,
614 mStreamStates[AudioSystem.STREAM_DTMF], 0);
615 }
616 }
617
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800618 private void readPersistedSettings() {
619 final ContentResolver cr = mContentResolver;
620
Eric Laurentbffc3d12012-05-07 17:43:49 -0700621 int ringerModeFromSettings =
Eric Laurentbc0fab1f2012-09-19 11:24:41 -0700622 Settings.Global.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);
Eric Laurentbffc3d12012-05-07 17:43:49 -0700623 int ringerMode = ringerModeFromSettings;
Eric Laurent72668b22011-07-19 16:04:27 -0700624 // sanity check in case the settings are restored from a device with incompatible
625 // ringer modes
Glenn Kastenba195eb2011-12-13 09:30:40 -0800626 if (!AudioManager.isValidRingerMode(ringerMode)) {
627 ringerMode = AudioManager.RINGER_MODE_NORMAL;
Eric Laurentbffc3d12012-05-07 17:43:49 -0700628 }
629 if ((ringerMode == AudioManager.RINGER_MODE_VIBRATE) && !mHasVibrator) {
630 ringerMode = AudioManager.RINGER_MODE_SILENT;
631 }
632 if (ringerMode != ringerModeFromSettings) {
Eric Laurentbc0fab1f2012-09-19 11:24:41 -0700633 Settings.Global.putInt(cr, System.MODE_RINGER, ringerMode);
Glenn Kastenba195eb2011-12-13 09:30:40 -0800634 }
635 synchronized(mSettingsLock) {
636 mRingerMode = ringerMode;
Eric Laurent72668b22011-07-19 16:04:27 -0700637 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800638
Eric Laurentbffc3d12012-05-07 17:43:49 -0700639 // System.VIBRATE_ON is not used any more but defaults for mVibrateSetting
640 // are still needed while setVibrateSetting() and getVibrateSetting() are being deprecated.
641 mVibrateSetting = getValueForVibrateSetting(0,
642 AudioManager.VIBRATE_TYPE_NOTIFICATION,
643 mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT
644 : AudioManager.VIBRATE_SETTING_OFF);
645 mVibrateSetting = getValueForVibrateSetting(mVibrateSetting,
646 AudioManager.VIBRATE_TYPE_RINGER,
647 mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT
648 : AudioManager.VIBRATE_SETTING_OFF);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800649
Eric Laurentc1d41662011-07-19 11:21:13 -0700650 // make sure settings for ringer mode are consistent with device type: non voice capable
651 // devices (tablets) include media stream in silent mode whereas phones don't.
Eric Laurentbc0fab1f2012-09-19 11:24:41 -0700652 mRingerModeAffectedStreams = Settings.System.getIntForUser(cr,
Eric Laurent9bcf4012009-06-12 06:09:28 -0700653 Settings.System.MODE_RINGER_STREAMS_AFFECTED,
Eric Laurenta553c252009-07-17 12:17:14 -0700654 ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
Eric Laurentbc0fab1f2012-09-19 11:24:41 -0700655 (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)),
656 UserHandle.USER_CURRENT);
Eric Laurentc1d41662011-07-19 11:21:13 -0700657 if (mVoiceCapable) {
658 mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
659 } else {
Eric Laurent402f7f22011-02-04 12:30:32 -0800660 mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
661 }
Eric Laurentbc0fab1f2012-09-19 11:24:41 -0700662 Settings.System.putIntForUser(cr,
663 Settings.System.MODE_RINGER_STREAMS_AFFECTED,
664 mRingerModeAffectedStreams,
665 UserHandle.USER_CURRENT);
Eric Laurentc1d41662011-07-19 11:21:13 -0700666
Eric Laurentbc0fab1f2012-09-19 11:24:41 -0700667 mMuteAffectedStreams = System.getIntForUser(cr,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800668 System.MUTE_STREAMS_AFFECTED,
Eric Laurentbc0fab1f2012-09-19 11:24:41 -0700669 ((1 << AudioSystem.STREAM_MUSIC)|
670 (1 << AudioSystem.STREAM_RING)|
671 (1 << AudioSystem.STREAM_SYSTEM)),
672 UserHandle.USER_CURRENT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800673
Eric Laurentbc0fab1f2012-09-19 11:24:41 -0700674 boolean masterMute = System.getIntForUser(cr, System.VOLUME_MASTER_MUTE,
675 0, UserHandle.USER_CURRENT) == 1;
Justin Koh57978ed2012-04-03 17:37:58 -0700676 AudioSystem.setMasterMute(masterMute);
677 broadcastMasterMuteStatus(masterMute);
678
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800679 // Each stream will read its own persisted settings
680
681 // Broadcast the sticky intent
Glenn Kastenba195eb2011-12-13 09:30:40 -0800682 broadcastRingerMode(ringerMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800683
684 // Broadcast vibrate settings
685 broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
686 broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION);
Jean-Michel Trivid589fea2011-04-15 11:28:10 -0700687
688 // Restore the default media button receiver from the system settings
689 restoreMediaButtonReceiver();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800690 }
691
Eric Laurenta553c252009-07-17 12:17:14 -0700692 private int rescaleIndex(int index, int srcStream, int dstStream) {
693 return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex();
694 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800695
696 ///////////////////////////////////////////////////////////////////////////
697 // IPC methods
698 ///////////////////////////////////////////////////////////////////////////
699
700 /** @see AudioManager#adjustVolume(int, int) */
701 public void adjustVolume(int direction, int flags) {
702 adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags);
703 }
704
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700705 /** @see AudioManager#adjustLocalOrRemoteStreamVolume(int, int) with current assumption
706 * on streamType: fixed to STREAM_MUSIC */
707 public void adjustLocalOrRemoteStreamVolume(int streamType, int direction) {
708 if (DEBUG_VOL) Log.d(TAG, "adjustLocalOrRemoteStreamVolume(dir="+direction+")");
709 if (checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
710 adjustRemoteVolume(AudioSystem.STREAM_MUSIC, direction, 0);
711 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
712 adjustStreamVolume(AudioSystem.STREAM_MUSIC, direction, 0);
713 }
714 }
715
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800716 /** @see AudioManager#adjustVolume(int, int, int) */
717 public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700718 if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream="+suggestedStreamType);
Eric Laurent402f7f22011-02-04 12:30:32 -0800719 int streamType;
Eric Laurent45c90ce2012-04-24 18:44:22 -0700720 if (mVolumeControlStream != -1) {
721 streamType = mVolumeControlStream;
Eric Laurent402f7f22011-02-04 12:30:32 -0800722 } else {
723 streamType = getActiveStreamType(suggestedStreamType);
724 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800725
Amith Yamasani6243edd2011-12-05 19:58:48 -0800726 // Play sounds on STREAM_RING only and if lock screen is not on.
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700727 if ((streamType != STREAM_REMOTE_MUSIC) &&
728 (flags & AudioManager.FLAG_PLAY_SOUND) != 0 &&
Eric Laurent6d517662012-04-23 18:42:39 -0700729 ((mStreamVolumeAlias[streamType] != AudioSystem.STREAM_RING)
Amith Yamasani6243edd2011-12-05 19:58:48 -0800730 || (mKeyguardManager != null && mKeyguardManager.isKeyguardLocked()))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800731 flags &= ~AudioManager.FLAG_PLAY_SOUND;
732 }
733
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700734 if (streamType == STREAM_REMOTE_MUSIC) {
735 // don't play sounds for remote
736 flags &= ~AudioManager.FLAG_PLAY_SOUND;
737 //if (DEBUG_VOL) Log.i(TAG, "Need to adjust remote volume: calling adjustRemoteVolume()");
738 adjustRemoteVolume(AudioSystem.STREAM_MUSIC, direction, flags);
739 } else {
740 adjustStreamVolume(streamType, direction, flags);
741 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800742 }
743
744 /** @see AudioManager#adjustStreamVolume(int, int, int) */
745 public void adjustStreamVolume(int streamType, int direction, int flags) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700746 if (DEBUG_VOL) Log.d(TAG, "adjustStreamVolume() stream="+streamType+", dir="+direction);
747
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800748 ensureValidDirection(direction);
749 ensureValidStreamType(streamType);
750
Eric Laurent96a33d12011-11-08 10:31:57 -0800751 // use stream type alias here so that streams with same alias have the same behavior,
752 // including with regard to silent mode control (e.g the use of STREAM_RING below and in
753 // checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION)
Eric Laurent6d517662012-04-23 18:42:39 -0700754 int streamTypeAlias = mStreamVolumeAlias[streamType];
Eric Laurentb024c302011-10-14 17:19:27 -0700755 VolumeStreamState streamState = mStreamStates[streamTypeAlias];
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800756
757 final int device = getDeviceForStream(streamTypeAlias);
758 // get last audible index if stream is muted, current index otherwise
Eric Laurent24482012012-05-10 09:41:17 -0700759 final int aliasIndex = streamState.getIndex(device,
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800760 (streamState.muteCount() != 0) /* lastAudible */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800761 boolean adjustVolume = true;
762
Eric Laurent24482012012-05-10 09:41:17 -0700763 // convert one UI step (+/-1) into a number of internal units on the stream alias
764 int step = rescaleIndex(10, streamType, streamTypeAlias);
765
Eric Laurentc34dcc12012-09-10 13:51:52 -0700766 if ((direction == AudioManager.ADJUST_RAISE) &&
767 !checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) {
768 return;
769 }
770
Eric Laurent5b4e6542010-03-19 20:02:21 -0700771 int index;
Eric Laurent4bbcc652012-09-24 14:26:30 -0700772 int oldIndex;
Eric Laurent24482012012-05-10 09:41:17 -0700773
Eric Laurent4bbcc652012-09-24 14:26:30 -0700774 if ((streamTypeAlias == AudioSystem.STREAM_MUSIC) &&
775 ((device & mFixedVolumeDevices) != 0)) {
776 flags |= AudioManager.FLAG_FIXED_VOLUME;
777 index = mStreamStates[streamType].getMaxIndex();
778 oldIndex = index;
Eric Laurent5b4e6542010-03-19 20:02:21 -0700779 } else {
Eric Laurent4bbcc652012-09-24 14:26:30 -0700780 // If either the client forces allowing ringer modes for this adjustment,
781 // or the stream type is one that is affected by ringer modes
782 if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
783 (streamTypeAlias == getMasterStreamType())) {
784 int ringerMode = getRingerMode();
785 // do not vibrate if already in vibrate mode
786 if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
787 flags &= ~AudioManager.FLAG_VIBRATE;
788 }
789 // Check if the ringer mode changes with this volume adjustment. If
790 // it does, it will handle adjusting the volume, so we won't below
791 adjustVolume = checkForRingerModeChange(aliasIndex, direction, step);
792 if ((streamTypeAlias == getMasterStreamType()) &&
793 (mRingerMode == AudioManager.RINGER_MODE_SILENT)) {
794 streamState.setLastAudibleIndex(0, device);
795 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800796 }
Eric Laurent25101b02011-02-02 09:33:30 -0800797
Eric Laurent4bbcc652012-09-24 14:26:30 -0700798 // If stream is muted, adjust last audible index only
799 oldIndex = mStreamStates[streamType].getIndex(device,
800 (mStreamStates[streamType].muteCount() != 0) /* lastAudible */);
801
802 if (streamState.muteCount() != 0) {
803 if (adjustVolume) {
804 // Post a persist volume msg
805 // no need to persist volume on all streams sharing the same alias
806 streamState.adjustLastAudibleIndex(direction * step, device);
807 sendMsg(mAudioHandler,
808 MSG_PERSIST_VOLUME,
809 SENDMSG_QUEUE,
810 PERSIST_LAST_AUDIBLE,
811 device,
812 streamState,
813 PERSIST_DELAY);
814 }
815 index = mStreamStates[streamType].getIndex(device, true /* lastAudible */);
816 } else {
817 if (adjustVolume && streamState.adjustIndex(direction * step, device)) {
818 // Post message to set system volume (it in turn will post a message
819 // to persist). Do not change volume if stream is muted.
820 sendMsg(mAudioHandler,
821 MSG_SET_DEVICE_VOLUME,
822 SENDMSG_QUEUE,
823 device,
824 0,
825 streamState,
826 0);
827 }
828 index = mStreamStates[streamType].getIndex(device, false /* lastAudible */);
829 }
830 }
Eric Laurent25101b02011-02-02 09:33:30 -0800831 sendVolumeUpdate(streamType, oldIndex, index, flags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800832 }
833
Mike Lockwoodcbdb49d2011-10-20 12:54:05 -0400834 /** @see AudioManager#adjustMasterVolume(int) */
Lei Zhang6c798972012-03-02 11:40:12 -0800835 public void adjustMasterVolume(int steps, int flags) {
836 ensureValidSteps(steps);
Mike Lockwood97606472012-02-09 11:24:10 -0800837 int volume = Math.round(AudioSystem.getMasterVolume() * MAX_MASTER_VOLUME);
838 int delta = 0;
Lei Zhang6c798972012-03-02 11:40:12 -0800839 int numSteps = Math.abs(steps);
840 int direction = steps > 0 ? AudioManager.ADJUST_RAISE : AudioManager.ADJUST_LOWER;
841 for (int i = 0; i < numSteps; ++i) {
842 delta = findVolumeDelta(direction, volume);
843 volume += delta;
Mike Lockwoodcbdb49d2011-10-20 12:54:05 -0400844 }
RoboErik24b082f2012-02-24 14:21:16 -0800845
Lei Zhang6c798972012-03-02 11:40:12 -0800846 //Log.d(TAG, "adjustMasterVolume volume: " + volume + " steps: " + steps);
847 setMasterVolume(volume, flags);
Mike Lockwoodcbdb49d2011-10-20 12:54:05 -0400848 }
849
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800850 /** @see AudioManager#setStreamVolume(int, int, int) */
851 public void setStreamVolume(int streamType, int index, int flags) {
852 ensureValidStreamType(streamType);
Eric Laurent6d517662012-04-23 18:42:39 -0700853 VolumeStreamState streamState = mStreamStates[mStreamVolumeAlias[streamType]];
Eric Laurent9ce379a2010-02-16 06:00:26 -0800854
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800855 final int device = getDeviceForStream(streamType);
Eric Laurent4bbcc652012-09-24 14:26:30 -0700856 int oldIndex;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700857
Eric Laurent4bbcc652012-09-24 14:26:30 -0700858 if ((mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) &&
859 ((device & mFixedVolumeDevices) != 0)) {
860 flags |= AudioManager.FLAG_FIXED_VOLUME;
861 index = mStreamStates[streamType].getMaxIndex();
862 oldIndex = index;
863 } else {
864 // get last audible index if stream is muted, current index otherwise
865 oldIndex = streamState.getIndex(device,
866 (streamState.muteCount() != 0) /* lastAudible */);
Eric Laurent9ce379a2010-02-16 06:00:26 -0800867
Eric Laurent4bbcc652012-09-24 14:26:30 -0700868 index = rescaleIndex(index * 10, streamType, mStreamVolumeAlias[streamType]);
Eric Laurent24482012012-05-10 09:41:17 -0700869
Eric Laurent4bbcc652012-09-24 14:26:30 -0700870 if (!checkSafeMediaVolume(mStreamVolumeAlias[streamType], index, device)) {
871 return;
Eric Laurentb024c302011-10-14 17:19:27 -0700872 }
Eric Laurent4bbcc652012-09-24 14:26:30 -0700873
874 // setting volume on master stream type also controls silent mode
875 if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
876 (mStreamVolumeAlias[streamType] == getMasterStreamType())) {
877 int newRingerMode;
878 if (index == 0) {
879 newRingerMode = mHasVibrator ? AudioManager.RINGER_MODE_VIBRATE
880 : AudioManager.RINGER_MODE_SILENT;
881 setStreamVolumeInt(mStreamVolumeAlias[streamType],
882 index,
883 device,
884 false,
885 true);
886 } else {
887 newRingerMode = AudioManager.RINGER_MODE_NORMAL;
888 }
889 setRingerMode(newRingerMode);
890 }
891
892 setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, false, true);
893 // get last audible index if stream is muted, current index otherwise
894 index = mStreamStates[streamType].getIndex(device,
895 (mStreamStates[streamType].muteCount() != 0) /* lastAudible */);
Eric Laurentb024c302011-10-14 17:19:27 -0700896 }
Eric Laurent25101b02011-02-02 09:33:30 -0800897 sendVolumeUpdate(streamType, oldIndex, index, flags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800898 }
899
Eric Laurent45c90ce2012-04-24 18:44:22 -0700900 /** @see AudioManager#forceVolumeControlStream(int) */
901 public void forceVolumeControlStream(int streamType, IBinder cb) {
902 synchronized(mForceControlStreamLock) {
903 mVolumeControlStream = streamType;
904 if (mVolumeControlStream == -1) {
905 if (mForceControlStreamClient != null) {
906 mForceControlStreamClient.release();
907 mForceControlStreamClient = null;
908 }
909 } else {
910 mForceControlStreamClient = new ForceControlStreamClient(cb);
911 }
912 }
913 }
914
915 private class ForceControlStreamClient implements IBinder.DeathRecipient {
916 private IBinder mCb; // To be notified of client's death
917
918 ForceControlStreamClient(IBinder cb) {
919 if (cb != null) {
920 try {
921 cb.linkToDeath(this, 0);
922 } catch (RemoteException e) {
923 // Client has died!
924 Log.w(TAG, "ForceControlStreamClient() could not link to "+cb+" binder death");
925 cb = null;
926 }
927 }
928 mCb = cb;
929 }
930
931 public void binderDied() {
932 synchronized(mForceControlStreamLock) {
933 Log.w(TAG, "SCO client died");
934 if (mForceControlStreamClient != this) {
935 Log.w(TAG, "unregistered control stream client died");
936 } else {
937 mForceControlStreamClient = null;
938 mVolumeControlStream = -1;
939 }
940 }
941 }
942
943 public void release() {
944 if (mCb != null) {
945 mCb.unlinkToDeath(this, 0);
946 mCb = null;
947 }
948 }
949 }
950
Lei Zhang6c798972012-03-02 11:40:12 -0800951 private int findVolumeDelta(int direction, int volume) {
952 int delta = 0;
953 if (direction == AudioManager.ADJUST_RAISE) {
954 if (volume == MAX_MASTER_VOLUME) {
955 return 0;
956 }
957 // This is the default value if we make it to the end
958 delta = mMasterVolumeRamp[1];
959 // If we're raising the volume move down the ramp array until we
960 // find the volume we're above and use that groups delta.
961 for (int i = mMasterVolumeRamp.length - 1; i > 1; i -= 2) {
962 if (volume >= mMasterVolumeRamp[i - 1]) {
963 delta = mMasterVolumeRamp[i];
964 break;
965 }
966 }
967 } else if (direction == AudioManager.ADJUST_LOWER){
968 if (volume == 0) {
969 return 0;
970 }
971 int length = mMasterVolumeRamp.length;
972 // This is the default value if we make it to the end
973 delta = -mMasterVolumeRamp[length - 1];
974 // If we're lowering the volume move up the ramp array until we
975 // find the volume we're below and use the group below it's delta
976 for (int i = 2; i < length; i += 2) {
977 if (volume <= mMasterVolumeRamp[i]) {
978 delta = -mMasterVolumeRamp[i - 1];
979 break;
980 }
981 }
982 }
983 return delta;
984 }
985
Dianne Hackborn5ac72a22012-08-29 18:32:08 -0700986 private void sendBroadcastToAll(Intent intent) {
987 final long ident = Binder.clearCallingIdentity();
988 try {
989 mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
990 } finally {
991 Binder.restoreCallingIdentity(ident);
992 }
993 }
994
995 private void sendStickyBroadcastToAll(Intent intent) {
996 final long ident = Binder.clearCallingIdentity();
997 try {
998 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
999 } finally {
1000 Binder.restoreCallingIdentity(ident);
1001 }
1002 }
1003
Eric Laurent25101b02011-02-02 09:33:30 -08001004 // UI update and Broadcast Intent
1005 private void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags) {
1006 if (!mVoiceCapable && (streamType == AudioSystem.STREAM_RING)) {
1007 streamType = AudioSystem.STREAM_NOTIFICATION;
1008 }
1009
1010 mVolumePanel.postVolumeChanged(streamType, flags);
1011
Eric Laurent4bbcc652012-09-24 14:26:30 -07001012 if ((flags & AudioManager.FLAG_FIXED_VOLUME) == 0) {
1013 oldIndex = (oldIndex + 5) / 10;
1014 index = (index + 5) / 10;
1015 Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
1016 intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
1017 intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index);
1018 intent.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex);
1019 sendBroadcastToAll(intent);
1020 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001021 }
1022
Mike Lockwood0dc37cc2011-12-01 16:14:19 -05001023 // UI update and Broadcast Intent
1024 private void sendMasterVolumeUpdate(int flags, int oldVolume, int newVolume) {
1025 mVolumePanel.postMasterVolumeChanged(flags);
1026
1027 Intent intent = new Intent(AudioManager.MASTER_VOLUME_CHANGED_ACTION);
1028 intent.putExtra(AudioManager.EXTRA_PREV_MASTER_VOLUME_VALUE, oldVolume);
1029 intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_VALUE, newVolume);
Dianne Hackborn5ac72a22012-08-29 18:32:08 -07001030 sendBroadcastToAll(intent);
Mike Lockwood0dc37cc2011-12-01 16:14:19 -05001031 }
1032
1033 // UI update and Broadcast Intent
1034 private void sendMasterMuteUpdate(boolean muted, int flags) {
1035 mVolumePanel.postMasterMuteChanged(flags);
Justin Koh57978ed2012-04-03 17:37:58 -07001036 broadcastMasterMuteStatus(muted);
1037 }
Mike Lockwood0dc37cc2011-12-01 16:14:19 -05001038
Justin Koh57978ed2012-04-03 17:37:58 -07001039 private void broadcastMasterMuteStatus(boolean muted) {
Mike Lockwood0dc37cc2011-12-01 16:14:19 -05001040 Intent intent = new Intent(AudioManager.MASTER_MUTE_CHANGED_ACTION);
1041 intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_MUTED, muted);
Justin Koh57978ed2012-04-03 17:37:58 -07001042 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1043 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
Dianne Hackborn5ac72a22012-08-29 18:32:08 -07001044 sendStickyBroadcastToAll(intent);
Mike Lockwood0dc37cc2011-12-01 16:14:19 -05001045 }
1046
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001047 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001048 * Sets the stream state's index, and posts a message to set system volume.
1049 * This will not call out to the UI. Assumes a valid stream type.
1050 *
1051 * @param streamType Type of the stream
1052 * @param index Desired volume index of the stream
Eric Laurent9bc8358d2011-11-18 16:43:31 -08001053 * @param device the device whose volume must be changed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001054 * @param force If true, set the volume even if the desired volume is same
1055 * as the current volume.
Eric Laurent9bcf4012009-06-12 06:09:28 -07001056 * @param lastAudible If true, stores new index as last audible one
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001057 */
Eric Laurent9bc8358d2011-11-18 16:43:31 -08001058 private void setStreamVolumeInt(int streamType,
1059 int index,
1060 int device,
1061 boolean force,
1062 boolean lastAudible) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001063 VolumeStreamState streamState = mStreamStates[streamType];
Eric Laurent5b4e6542010-03-19 20:02:21 -07001064
1065 // If stream is muted, set last audible index only
1066 if (streamState.muteCount() != 0) {
Eric Laurent758dd522010-04-13 10:38:05 -07001067 // Do not allow last audible index to be 0
1068 if (index != 0) {
Eric Laurent9bc8358d2011-11-18 16:43:31 -08001069 streamState.setLastAudibleIndex(index, device);
Eric Laurent758dd522010-04-13 10:38:05 -07001070 // Post a persist volume msg
Eric Laurentafbb0472011-12-15 09:04:23 -08001071 sendMsg(mAudioHandler,
1072 MSG_PERSIST_VOLUME,
Eric Laurent98ad9b92012-02-15 17:21:37 -08001073 SENDMSG_QUEUE,
Eric Laurentafbb0472011-12-15 09:04:23 -08001074 PERSIST_LAST_AUDIBLE,
Eric Laurent9bc8358d2011-11-18 16:43:31 -08001075 device,
Eric Laurentafbb0472011-12-15 09:04:23 -08001076 streamState,
1077 PERSIST_DELAY);
Eric Laurent758dd522010-04-13 10:38:05 -07001078 }
Eric Laurent5b4e6542010-03-19 20:02:21 -07001079 } else {
Eric Laurent9bc8358d2011-11-18 16:43:31 -08001080 if (streamState.setIndex(index, device, lastAudible) || force) {
Eric Laurent5b4e6542010-03-19 20:02:21 -07001081 // Post message to set system volume (it in turn will post a message
1082 // to persist).
Eric Laurentafbb0472011-12-15 09:04:23 -08001083 sendMsg(mAudioHandler,
Eric Laurent9bc8358d2011-11-18 16:43:31 -08001084 MSG_SET_DEVICE_VOLUME,
Eric Laurent98ad9b92012-02-15 17:21:37 -08001085 SENDMSG_QUEUE,
Eric Laurent9bc8358d2011-11-18 16:43:31 -08001086 device,
Eric Laurentafbb0472011-12-15 09:04:23 -08001087 0,
1088 streamState,
1089 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001090 }
1091 }
1092 }
1093
1094 /** @see AudioManager#setStreamSolo(int, boolean) */
1095 public void setStreamSolo(int streamType, boolean state, IBinder cb) {
1096 for (int stream = 0; stream < mStreamStates.length; stream++) {
1097 if (!isStreamAffectedByMute(stream) || stream == streamType) continue;
1098 // Bring back last audible volume
1099 mStreamStates[stream].mute(cb, state);
1100 }
1101 }
1102
1103 /** @see AudioManager#setStreamMute(int, boolean) */
1104 public void setStreamMute(int streamType, boolean state, IBinder cb) {
1105 if (isStreamAffectedByMute(streamType)) {
1106 mStreamStates[streamType].mute(cb, state);
1107 }
1108 }
1109
Eric Laurent25101b02011-02-02 09:33:30 -08001110 /** get stream mute state. */
1111 public boolean isStreamMute(int streamType) {
1112 return (mStreamStates[streamType].muteCount() != 0);
1113 }
1114
Mike Lockwoodce952c82011-11-14 10:47:42 -08001115 /** @see AudioManager#setMasterMute(boolean, IBinder) */
Justin Koh0273af52012-04-04 18:29:50 -07001116 public void setMasterMute(boolean state, int flags, IBinder cb) {
Jason Simmons1ce5b262012-02-02 13:00:17 -08001117 if (state != AudioSystem.getMasterMute()) {
1118 AudioSystem.setMasterMute(state);
Justin Koh57978ed2012-04-03 17:37:58 -07001119 // Post a persist master volume msg
Justin Koh75cf9e12012-04-04 15:27:37 -07001120 sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME_MUTE, SENDMSG_REPLACE, state ? 1
Justin Koh57978ed2012-04-03 17:37:58 -07001121 : 0, 0, null, PERSIST_DELAY);
Justin Koh0273af52012-04-04 18:29:50 -07001122 sendMasterMuteUpdate(state, flags);
Jason Simmons1ce5b262012-02-02 13:00:17 -08001123 }
Mike Lockwoodce952c82011-11-14 10:47:42 -08001124 }
1125
1126 /** get master mute state. */
1127 public boolean isMasterMute() {
Mike Lockwood3194ea92011-12-07 11:47:31 -08001128 return AudioSystem.getMasterMute();
Mike Lockwoodce952c82011-11-14 10:47:42 -08001129 }
1130
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001131 /** @see AudioManager#getStreamVolume(int) */
1132 public int getStreamVolume(int streamType) {
1133 ensureValidStreamType(streamType);
Eric Laurent9bc8358d2011-11-18 16:43:31 -08001134 int device = getDeviceForStream(streamType);
Eric Laurent4bbcc652012-09-24 14:26:30 -07001135 int index;
1136
1137 if ((mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) &&
1138 (device & mFixedVolumeDevices) != 0) {
1139 index = mStreamStates[streamType].getMaxIndex();
1140 } else {
1141 index = mStreamStates[streamType].getIndex(device, false /* lastAudible */);
1142 }
1143 return (index + 5) / 10;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001144 }
1145
Mike Lockwood47676902011-11-08 10:31:21 -08001146 public int getMasterVolume() {
Mike Lockwoodce952c82011-11-14 10:47:42 -08001147 if (isMasterMute()) return 0;
1148 return getLastAudibleMasterVolume();
Mike Lockwood8dc1dab2011-10-27 09:52:41 -04001149 }
1150
Mike Lockwood47676902011-11-08 10:31:21 -08001151 public void setMasterVolume(int volume, int flags) {
Mike Lockwood97606472012-02-09 11:24:10 -08001152 if (volume < 0) {
1153 volume = 0;
1154 } else if (volume > MAX_MASTER_VOLUME) {
1155 volume = MAX_MASTER_VOLUME;
1156 }
Mike Lockwood5c55a052011-12-15 17:21:44 -05001157 doSetMasterVolume((float)volume / MAX_MASTER_VOLUME, flags);
1158 }
1159
1160 private void doSetMasterVolume(float volume, int flags) {
1161 // don't allow changing master volume when muted
1162 if (!AudioSystem.getMasterMute()) {
1163 int oldVolume = getMasterVolume();
1164 AudioSystem.setMasterVolume(volume);
1165
1166 int newVolume = getMasterVolume();
1167 if (newVolume != oldVolume) {
1168 // Post a persist master volume msg
1169 sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME, SENDMSG_REPLACE,
1170 Math.round(volume * (float)1000.0), 0, null, PERSIST_DELAY);
Mike Lockwood5c55a052011-12-15 17:21:44 -05001171 }
Justin Koh3caba512012-04-02 15:32:18 -07001172 // Send the volume update regardless whether there was a change.
1173 sendMasterVolumeUpdate(flags, oldVolume, newVolume);
Mike Lockwood5c55a052011-12-15 17:21:44 -05001174 }
Mike Lockwood8dc1dab2011-10-27 09:52:41 -04001175 }
1176
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001177 /** @see AudioManager#getStreamMaxVolume(int) */
1178 public int getStreamMaxVolume(int streamType) {
1179 ensureValidStreamType(streamType);
Eric Laurenta553c252009-07-17 12:17:14 -07001180 return (mStreamStates[streamType].getMaxIndex() + 5) / 10;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001181 }
1182
Mike Lockwood47676902011-11-08 10:31:21 -08001183 public int getMasterMaxVolume() {
1184 return MAX_MASTER_VOLUME;
1185 }
Eric Laurent25101b02011-02-02 09:33:30 -08001186
1187 /** Get last audible volume before stream was muted. */
1188 public int getLastAudibleStreamVolume(int streamType) {
1189 ensureValidStreamType(streamType);
Eric Laurent9bc8358d2011-11-18 16:43:31 -08001190 int device = getDeviceForStream(streamType);
1191 return (mStreamStates[streamType].getIndex(device, true /* lastAudible */) + 5) / 10;
Eric Laurent25101b02011-02-02 09:33:30 -08001192 }
1193
Mike Lockwoodce952c82011-11-14 10:47:42 -08001194 /** Get last audible master volume before it was muted. */
1195 public int getLastAudibleMasterVolume() {
1196 return Math.round(AudioSystem.getMasterVolume() * MAX_MASTER_VOLUME);
1197 }
1198
Eric Laurent6d517662012-04-23 18:42:39 -07001199 /** @see AudioManager#getMasterStreamType(int) */
1200 public int getMasterStreamType() {
1201 if (mVoiceCapable) {
1202 return AudioSystem.STREAM_RING;
1203 } else {
1204 return AudioSystem.STREAM_MUSIC;
1205 }
1206 }
1207
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001208 /** @see AudioManager#getRingerMode() */
1209 public int getRingerMode() {
Glenn Kastenba195eb2011-12-13 09:30:40 -08001210 synchronized(mSettingsLock) {
1211 return mRingerMode;
1212 }
1213 }
1214
1215 private void ensureValidRingerMode(int ringerMode) {
1216 if (!AudioManager.isValidRingerMode(ringerMode)) {
1217 throw new IllegalArgumentException("Bad ringer mode " + ringerMode);
1218 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001219 }
1220
1221 /** @see AudioManager#setRingerMode(int) */
1222 public void setRingerMode(int ringerMode) {
Eric Laurent24482012012-05-10 09:41:17 -07001223 if ((ringerMode == AudioManager.RINGER_MODE_VIBRATE) && !mHasVibrator) {
1224 ringerMode = AudioManager.RINGER_MODE_SILENT;
1225 }
Glenn Kastenba195eb2011-12-13 09:30:40 -08001226 if (ringerMode != getRingerMode()) {
1227 setRingerModeInt(ringerMode, true);
1228 // Send sticky broadcast
1229 broadcastRingerMode(ringerMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001230 }
1231 }
1232
Eric Laurent4050c932009-07-08 02:52:14 -07001233 private void setRingerModeInt(int ringerMode, boolean persist) {
Glenn Kastenba195eb2011-12-13 09:30:40 -08001234 synchronized(mSettingsLock) {
1235 mRingerMode = ringerMode;
1236 }
Jason Parekhb1096152009-03-24 17:48:25 -07001237
Eric Laurent5b4e6542010-03-19 20:02:21 -07001238 // Mute stream if not previously muted by ringer mode and ringer mode
1239 // is not RINGER_MODE_NORMAL and stream is affected by ringer mode.
1240 // Unmute stream if previously muted by ringer mode and ringer mode
1241 // is RINGER_MODE_NORMAL or stream is not affected by ringer mode.
Jason Parekhb1096152009-03-24 17:48:25 -07001242 int numStreamTypes = AudioSystem.getNumStreamTypes();
Eric Laurent5b4e6542010-03-19 20:02:21 -07001243 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
1244 if (isStreamMutedByRingerMode(streamType)) {
1245 if (!isStreamAffectedByRingerMode(streamType) ||
Glenn Kastenba195eb2011-12-13 09:30:40 -08001246 ringerMode == AudioManager.RINGER_MODE_NORMAL) {
Eric Laurentb024c302011-10-14 17:19:27 -07001247 // ring and notifications volume should never be 0 when not silenced
1248 // on voice capable devices
1249 if (mVoiceCapable &&
Eric Laurent6d517662012-04-23 18:42:39 -07001250 mStreamVolumeAlias[streamType] == AudioSystem.STREAM_RING) {
Eric Laurent3172d5e2012-05-09 11:38:16 -07001251 synchronized (mStreamStates[streamType]) {
1252 Set set = mStreamStates[streamType].mLastAudibleIndex.entrySet();
1253 Iterator i = set.iterator();
1254 while (i.hasNext()) {
1255 Map.Entry entry = (Map.Entry)i.next();
1256 if ((Integer)entry.getValue() == 0) {
1257 entry.setValue(10);
1258 }
Eric Laurent9bc8358d2011-11-18 16:43:31 -08001259 }
1260 }
Eric Laurentb024c302011-10-14 17:19:27 -07001261 }
Eric Laurent5b4e6542010-03-19 20:02:21 -07001262 mStreamStates[streamType].mute(null, false);
1263 mRingerModeMutedStreams &= ~(1 << streamType);
Eric Laurent9bcf4012009-06-12 06:09:28 -07001264 }
Eric Laurent5b4e6542010-03-19 20:02:21 -07001265 } else {
1266 if (isStreamAffectedByRingerMode(streamType) &&
Glenn Kastenba195eb2011-12-13 09:30:40 -08001267 ringerMode != AudioManager.RINGER_MODE_NORMAL) {
Eric Laurent5b4e6542010-03-19 20:02:21 -07001268 mStreamStates[streamType].mute(null, true);
1269 mRingerModeMutedStreams |= (1 << streamType);
1270 }
Jason Parekhb1096152009-03-24 17:48:25 -07001271 }
1272 }
Eric Laurenta553c252009-07-17 12:17:14 -07001273
Jason Parekhb1096152009-03-24 17:48:25 -07001274 // Post a persist ringer mode msg
Eric Laurent4050c932009-07-08 02:52:14 -07001275 if (persist) {
Eric Laurentafbb0472011-12-15 09:04:23 -08001276 sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE,
Eric Laurent4050c932009-07-08 02:52:14 -07001277 SENDMSG_REPLACE, 0, 0, null, PERSIST_DELAY);
1278 }
Jason Parekhb1096152009-03-24 17:48:25 -07001279 }
1280
Mike Lockwood90631542012-01-06 11:20:37 -05001281 private void restoreMasterVolume() {
1282 if (mUseMasterVolume) {
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07001283 float volume = Settings.System.getFloatForUser(mContentResolver,
1284 Settings.System.VOLUME_MASTER, -1.0f, UserHandle.USER_CURRENT);
Mike Lockwood90631542012-01-06 11:20:37 -05001285 if (volume >= 0.0f) {
1286 AudioSystem.setMasterVolume(volume);
1287 }
1288 }
1289 }
1290
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001291 /** @see AudioManager#shouldVibrate(int) */
1292 public boolean shouldVibrate(int vibrateType) {
Eric Laurentbffc3d12012-05-07 17:43:49 -07001293 if (!mHasVibrator) return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001294
1295 switch (getVibrateSetting(vibrateType)) {
1296
1297 case AudioManager.VIBRATE_SETTING_ON:
Glenn Kastenba195eb2011-12-13 09:30:40 -08001298 return getRingerMode() != AudioManager.RINGER_MODE_SILENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001299
1300 case AudioManager.VIBRATE_SETTING_ONLY_SILENT:
Glenn Kastenba195eb2011-12-13 09:30:40 -08001301 return getRingerMode() == AudioManager.RINGER_MODE_VIBRATE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001302
1303 case AudioManager.VIBRATE_SETTING_OFF:
Daniel Sandlerbcac4962010-04-12 13:23:57 -04001304 // return false, even for incoming calls
1305 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001306
1307 default:
1308 return false;
1309 }
1310 }
1311
1312 /** @see AudioManager#getVibrateSetting(int) */
1313 public int getVibrateSetting(int vibrateType) {
Eric Laurentbffc3d12012-05-07 17:43:49 -07001314 if (!mHasVibrator) return AudioManager.VIBRATE_SETTING_OFF;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001315 return (mVibrateSetting >> (vibrateType * 2)) & 3;
1316 }
1317
1318 /** @see AudioManager#setVibrateSetting(int, int) */
1319 public void setVibrateSetting(int vibrateType, int vibrateSetting) {
1320
Eric Laurentbffc3d12012-05-07 17:43:49 -07001321 if (!mHasVibrator) return;
1322
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001323 mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, vibrateType, vibrateSetting);
1324
1325 // Broadcast change
1326 broadcastVibrateSetting(vibrateType);
1327
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001328 }
1329
1330 /**
1331 * @see #setVibrateSetting(int, int)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001332 */
1333 public static int getValueForVibrateSetting(int existingValue, int vibrateType,
1334 int vibrateSetting) {
1335
1336 // First clear the existing setting. Each vibrate type has two bits in
1337 // the value. Note '3' is '11' in binary.
1338 existingValue &= ~(3 << (vibrateType * 2));
1339
1340 // Set into the old value
1341 existingValue |= (vibrateSetting & 3) << (vibrateType * 2);
1342
1343 return existingValue;
1344 }
1345
Eric Laurent9272b4b2010-01-23 17:12:59 -08001346 private class SetModeDeathHandler implements IBinder.DeathRecipient {
1347 private IBinder mCb; // To be notified of client's death
Marco Nelissenf1ddd512011-08-10 14:15:44 -07001348 private int mPid;
Eric Laurent9272b4b2010-01-23 17:12:59 -08001349 private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client
1350
Eric Laurent9f103de2011-09-08 15:04:23 -07001351 SetModeDeathHandler(IBinder cb, int pid) {
Eric Laurent9272b4b2010-01-23 17:12:59 -08001352 mCb = cb;
Eric Laurent9f103de2011-09-08 15:04:23 -07001353 mPid = pid;
Eric Laurent9272b4b2010-01-23 17:12:59 -08001354 }
1355
1356 public void binderDied() {
Eric Laurentd7454be2011-09-14 08:45:58 -07001357 int newModeOwnerPid = 0;
Eric Laurent9272b4b2010-01-23 17:12:59 -08001358 synchronized(mSetModeDeathHandlers) {
1359 Log.w(TAG, "setMode() client died");
1360 int index = mSetModeDeathHandlers.indexOf(this);
1361 if (index < 0) {
1362 Log.w(TAG, "unregistered setMode() client died");
1363 } else {
Eric Laurentd7454be2011-09-14 08:45:58 -07001364 newModeOwnerPid = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid);
Eric Laurent9272b4b2010-01-23 17:12:59 -08001365 }
1366 }
Eric Laurent9f103de2011-09-08 15:04:23 -07001367 // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
1368 // SCO connections not started by the application changing the mode
Eric Laurentd7454be2011-09-14 08:45:58 -07001369 if (newModeOwnerPid != 0) {
1370 disconnectBluetoothSco(newModeOwnerPid);
Eric Laurent9f103de2011-09-08 15:04:23 -07001371 }
Eric Laurent9272b4b2010-01-23 17:12:59 -08001372 }
1373
Marco Nelissenf1ddd512011-08-10 14:15:44 -07001374 public int getPid() {
1375 return mPid;
1376 }
1377
Eric Laurent9272b4b2010-01-23 17:12:59 -08001378 public void setMode(int mode) {
1379 mMode = mode;
1380 }
1381
1382 public int getMode() {
1383 return mMode;
1384 }
1385
1386 public IBinder getBinder() {
1387 return mCb;
1388 }
1389 }
1390
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001391 /** @see AudioManager#setMode(int) */
Eric Laurent9272b4b2010-01-23 17:12:59 -08001392 public void setMode(int mode, IBinder cb) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001393 if (!checkAudioSettingsPermission("setMode()")) {
1394 return;
1395 }
Eric Laurenta553c252009-07-17 12:17:14 -07001396
Jean-Michel Trivi8f677d62010-11-15 12:11:32 -08001397 if (mode < AudioSystem.MODE_CURRENT || mode >= AudioSystem.NUM_MODES) {
Eric Laurenta553c252009-07-17 12:17:14 -07001398 return;
1399 }
1400
Eric Laurentd7454be2011-09-14 08:45:58 -07001401 int newModeOwnerPid = 0;
Eric Laurent9f103de2011-09-08 15:04:23 -07001402 synchronized(mSetModeDeathHandlers) {
Eric Laurenta553c252009-07-17 12:17:14 -07001403 if (mode == AudioSystem.MODE_CURRENT) {
1404 mode = mMode;
1405 }
Eric Laurentd7454be2011-09-14 08:45:58 -07001406 newModeOwnerPid = setModeInt(mode, cb, Binder.getCallingPid());
Eric Laurent9f103de2011-09-08 15:04:23 -07001407 }
1408 // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
1409 // SCO connections not started by the application changing the mode
Eric Laurentd7454be2011-09-14 08:45:58 -07001410 if (newModeOwnerPid != 0) {
1411 disconnectBluetoothSco(newModeOwnerPid);
Eric Laurent9f103de2011-09-08 15:04:23 -07001412 }
1413 }
Jean-Michel Trivi2ade5762010-12-11 13:18:30 -08001414
Eric Laurent9f103de2011-09-08 15:04:23 -07001415 // must be called synchronized on mSetModeDeathHandlers
Eric Laurentd7454be2011-09-14 08:45:58 -07001416 // setModeInt() returns a valid PID if the audio mode was successfully set to
Eric Laurent9f103de2011-09-08 15:04:23 -07001417 // any mode other than NORMAL.
Eric Laurentd7454be2011-09-14 08:45:58 -07001418 int setModeInt(int mode, IBinder cb, int pid) {
1419 int newModeOwnerPid = 0;
Eric Laurent9f103de2011-09-08 15:04:23 -07001420 if (cb == null) {
1421 Log.e(TAG, "setModeInt() called with null binder");
Eric Laurentd7454be2011-09-14 08:45:58 -07001422 return newModeOwnerPid;
Eric Laurent9f103de2011-09-08 15:04:23 -07001423 }
Jean-Michel Trivi2ade5762010-12-11 13:18:30 -08001424
Eric Laurent9f103de2011-09-08 15:04:23 -07001425 SetModeDeathHandler hdlr = null;
1426 Iterator iter = mSetModeDeathHandlers.iterator();
1427 while (iter.hasNext()) {
1428 SetModeDeathHandler h = (SetModeDeathHandler)iter.next();
1429 if (h.getPid() == pid) {
1430 hdlr = h;
1431 // Remove from client list so that it is re-inserted at top of list
1432 iter.remove();
1433 hdlr.getBinder().unlinkToDeath(hdlr, 0);
1434 break;
1435 }
1436 }
1437 int status = AudioSystem.AUDIO_STATUS_OK;
1438 do {
1439 if (mode == AudioSystem.MODE_NORMAL) {
1440 // get new mode from client at top the list if any
1441 if (!mSetModeDeathHandlers.isEmpty()) {
1442 hdlr = mSetModeDeathHandlers.get(0);
1443 cb = hdlr.getBinder();
1444 mode = hdlr.getMode();
Eric Laurentb9c9d262009-05-06 08:13:20 -07001445 }
Eric Laurent9f103de2011-09-08 15:04:23 -07001446 } else {
1447 if (hdlr == null) {
1448 hdlr = new SetModeDeathHandler(cb, pid);
1449 }
1450 // Register for client death notification
1451 try {
1452 cb.linkToDeath(hdlr, 0);
1453 } catch (RemoteException e) {
1454 // Client has died!
1455 Log.w(TAG, "setMode() could not link to "+cb+" binder death");
1456 }
1457
1458 // Last client to call setMode() is always at top of client list
1459 // as required by SetModeDeathHandler.binderDied()
1460 mSetModeDeathHandlers.add(0, hdlr);
1461 hdlr.setMode(mode);
1462 }
1463
1464 if (mode != mMode) {
1465 status = AudioSystem.setPhoneState(mode);
1466 if (status == AudioSystem.AUDIO_STATUS_OK) {
Eric Laurent9f103de2011-09-08 15:04:23 -07001467 mMode = mode;
1468 } else {
1469 if (hdlr != null) {
1470 mSetModeDeathHandlers.remove(hdlr);
1471 cb.unlinkToDeath(hdlr, 0);
1472 }
1473 // force reading new top of mSetModeDeathHandlers stack
1474 mode = AudioSystem.MODE_NORMAL;
1475 }
1476 } else {
1477 status = AudioSystem.AUDIO_STATUS_OK;
1478 }
1479 } while (status != AudioSystem.AUDIO_STATUS_OK && !mSetModeDeathHandlers.isEmpty());
1480
1481 if (status == AudioSystem.AUDIO_STATUS_OK) {
1482 if (mode != AudioSystem.MODE_NORMAL) {
Eric Laurentd7454be2011-09-14 08:45:58 -07001483 if (mSetModeDeathHandlers.isEmpty()) {
1484 Log.e(TAG, "setMode() different from MODE_NORMAL with empty mode client stack");
1485 } else {
1486 newModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
1487 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001488 }
1489 int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001490 if (streamType == STREAM_REMOTE_MUSIC) {
1491 // here handle remote media playback the same way as local playback
1492 streamType = AudioManager.STREAM_MUSIC;
1493 }
Eric Laurent9bc8358d2011-11-18 16:43:31 -08001494 int device = getDeviceForStream(streamType);
Eric Laurent6d517662012-04-23 18:42:39 -07001495 int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device, false);
1496 setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true, false);
1497
1498 updateStreamVolumeAlias(true /*updateVolumes*/);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001499 }
Eric Laurentd7454be2011-09-14 08:45:58 -07001500 return newModeOwnerPid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001501 }
1502
1503 /** @see AudioManager#getMode() */
1504 public int getMode() {
1505 return mMode;
1506 }
1507
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001508 /** @see AudioManager#playSoundEffect(int) */
1509 public void playSoundEffect(int effectType) {
Eric Laurentafbb0472011-12-15 09:04:23 -08001510 sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_NOOP,
Eric Laurenta2ef57d2009-09-28 04:46:10 -07001511 effectType, -1, null, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001512 }
1513
1514 /** @see AudioManager#playSoundEffect(int, float) */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001515 public void playSoundEffectVolume(int effectType, float volume) {
Eric Laurenta2ef57d2009-09-28 04:46:10 -07001516 loadSoundEffects();
Eric Laurentafbb0472011-12-15 09:04:23 -08001517 sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_NOOP,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001518 effectType, (int) (volume * 1000), null, 0);
1519 }
1520
1521 /**
1522 * Loads samples into the soundpool.
Glenn Kasten5c17a822011-11-30 09:41:01 -08001523 * This method must be called at first when sound effects are enabled
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001524 */
1525 public boolean loadSoundEffects() {
Eric Laurenta60e2122010-12-28 16:49:07 -08001526 int status;
1527
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001528 synchronized (mSoundEffectsLock) {
Eric Laurenta60e2122010-12-28 16:49:07 -08001529 if (!mBootCompleted) {
1530 Log.w(TAG, "loadSoundEffects() called before boot complete");
1531 return false;
1532 }
1533
Eric Laurenta2ef57d2009-09-28 04:46:10 -07001534 if (mSoundPool != null) {
1535 return true;
1536 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001537 mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
Eric Laurenta60e2122010-12-28 16:49:07 -08001538
1539 try {
1540 mSoundPoolCallBack = null;
1541 mSoundPoolListenerThread = new SoundPoolListenerThread();
1542 mSoundPoolListenerThread.start();
1543 // Wait for mSoundPoolCallBack to be set by the other thread
1544 mSoundEffectsLock.wait();
1545 } catch (InterruptedException e) {
1546 Log.w(TAG, "Interrupted while waiting sound pool listener thread.");
1547 }
1548
1549 if (mSoundPoolCallBack == null) {
1550 Log.w(TAG, "loadSoundEffects() could not create SoundPool listener or thread");
1551 if (mSoundPoolLooper != null) {
1552 mSoundPoolLooper.quit();
1553 mSoundPoolLooper = null;
1554 }
1555 mSoundPoolListenerThread = null;
1556 mSoundPool.release();
1557 mSoundPool = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001558 return false;
1559 }
1560 /*
1561 * poolId table: The value -1 in this table indicates that corresponding
1562 * file (same index in SOUND_EFFECT_FILES[] has not been loaded.
1563 * Once loaded, the value in poolId is the sample ID and the same
1564 * sample can be reused for another effect using the same file.
1565 */
1566 int[] poolId = new int[SOUND_EFFECT_FILES.length];
1567 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
1568 poolId[fileIdx] = -1;
1569 }
1570 /*
1571 * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded.
1572 * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0:
1573 * this indicates we have a valid sample loaded for this effect.
1574 */
Eric Laurenta60e2122010-12-28 16:49:07 -08001575
Eric Laurent117b7bb2011-01-16 17:07:27 -08001576 int lastSample = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001577 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
1578 // Do not load sample if this effect uses the MediaPlayer
1579 if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) {
1580 continue;
1581 }
1582 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
Eric Laurenta60e2122010-12-28 16:49:07 -08001583 String filePath = Environment.getRootDirectory()
1584 + SOUND_EFFECTS_PATH
1585 + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effect][0]];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001586 int sampleId = mSoundPool.load(filePath, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001587 if (sampleId <= 0) {
1588 Log.w(TAG, "Soundpool could not load file: "+filePath);
Eric Laurent117b7bb2011-01-16 17:07:27 -08001589 } else {
1590 SOUND_EFFECT_FILES_MAP[effect][1] = sampleId;
1591 poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId;
1592 lastSample = sampleId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001593 }
1594 } else {
1595 SOUND_EFFECT_FILES_MAP[effect][1] = poolId[SOUND_EFFECT_FILES_MAP[effect][0]];
1596 }
1597 }
Eric Laurenta60e2122010-12-28 16:49:07 -08001598 // wait for all samples to be loaded
Eric Laurent117b7bb2011-01-16 17:07:27 -08001599 if (lastSample != 0) {
1600 mSoundPoolCallBack.setLastSample(lastSample);
1601
1602 try {
1603 mSoundEffectsLock.wait();
1604 status = mSoundPoolCallBack.status();
1605 } catch (java.lang.InterruptedException e) {
1606 Log.w(TAG, "Interrupted while waiting sound pool callback.");
1607 status = -1;
1608 }
1609 } else {
Eric Laurenta60e2122010-12-28 16:49:07 -08001610 status = -1;
1611 }
Eric Laurent117b7bb2011-01-16 17:07:27 -08001612
Eric Laurenta60e2122010-12-28 16:49:07 -08001613 if (mSoundPoolLooper != null) {
1614 mSoundPoolLooper.quit();
1615 mSoundPoolLooper = null;
1616 }
1617 mSoundPoolListenerThread = null;
1618 if (status != 0) {
1619 Log.w(TAG,
1620 "loadSoundEffects(), Error "
Eric Laurent117b7bb2011-01-16 17:07:27 -08001621 + ((lastSample != 0) ? mSoundPoolCallBack.status() : -1)
Eric Laurenta60e2122010-12-28 16:49:07 -08001622 + " while loading samples");
Eric Laurent117b7bb2011-01-16 17:07:27 -08001623 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
1624 if (SOUND_EFFECT_FILES_MAP[effect][1] > 0) {
1625 SOUND_EFFECT_FILES_MAP[effect][1] = -1;
1626 }
1627 }
1628
Eric Laurenta60e2122010-12-28 16:49:07 -08001629 mSoundPool.release();
1630 mSoundPool = null;
1631 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001632 }
Eric Laurenta60e2122010-12-28 16:49:07 -08001633 return (status == 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001634 }
1635
1636 /**
1637 * Unloads samples from the sound pool.
1638 * This method can be called to free some memory when
1639 * sound effects are disabled.
1640 */
1641 public void unloadSoundEffects() {
1642 synchronized (mSoundEffectsLock) {
1643 if (mSoundPool == null) {
1644 return;
1645 }
Eric Laurenta60e2122010-12-28 16:49:07 -08001646
Eric Laurent117b7bb2011-01-16 17:07:27 -08001647 mAudioHandler.removeMessages(MSG_LOAD_SOUND_EFFECTS);
Eric Laurenta60e2122010-12-28 16:49:07 -08001648 mAudioHandler.removeMessages(MSG_PLAY_SOUND_EFFECT);
1649
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001650 int[] poolId = new int[SOUND_EFFECT_FILES.length];
1651 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
1652 poolId[fileIdx] = 0;
1653 }
1654
1655 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
1656 if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) {
1657 continue;
1658 }
1659 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) {
1660 mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]);
1661 SOUND_EFFECT_FILES_MAP[effect][1] = -1;
1662 poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1;
1663 }
1664 }
Eric Laurenta60e2122010-12-28 16:49:07 -08001665 mSoundPool.release();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001666 mSoundPool = null;
1667 }
1668 }
1669
Eric Laurenta60e2122010-12-28 16:49:07 -08001670 class SoundPoolListenerThread extends Thread {
1671 public SoundPoolListenerThread() {
1672 super("SoundPoolListenerThread");
1673 }
1674
1675 @Override
1676 public void run() {
1677
1678 Looper.prepare();
1679 mSoundPoolLooper = Looper.myLooper();
1680
1681 synchronized (mSoundEffectsLock) {
1682 if (mSoundPool != null) {
1683 mSoundPoolCallBack = new SoundPoolCallback();
1684 mSoundPool.setOnLoadCompleteListener(mSoundPoolCallBack);
1685 }
1686 mSoundEffectsLock.notify();
1687 }
1688 Looper.loop();
1689 }
1690 }
1691
1692 private final class SoundPoolCallback implements
1693 android.media.SoundPool.OnLoadCompleteListener {
1694
1695 int mStatus;
1696 int mLastSample;
1697
1698 public int status() {
1699 return mStatus;
1700 }
1701
1702 public void setLastSample(int sample) {
1703 mLastSample = sample;
1704 }
1705
1706 public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
1707 synchronized (mSoundEffectsLock) {
1708 if (status != 0) {
1709 mStatus = status;
1710 }
1711 if (sampleId == mLastSample) {
Eric Laurenta60e2122010-12-28 16:49:07 -08001712 mSoundEffectsLock.notify();
1713 }
1714 }
1715 }
1716 }
1717
Eric Laurent4050c932009-07-08 02:52:14 -07001718 /** @see AudioManager#reloadAudioSettings() */
1719 public void reloadAudioSettings() {
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07001720 readAudioSettings(false /*userSwitch*/);
1721 }
1722
1723 private void readAudioSettings(boolean userSwitch) {
Eric Laurent4050c932009-07-08 02:52:14 -07001724 // restore ringer mode, ringer mode affected streams, mute affected streams and vibrate settings
1725 readPersistedSettings();
1726
1727 // restore volume settings
1728 int numStreamTypes = AudioSystem.getNumStreamTypes();
1729 for (int streamType = 0; streamType < numStreamTypes; streamType++) {
1730 VolumeStreamState streamState = mStreamStates[streamType];
1731
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07001732 if (userSwitch && mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) {
1733 continue;
1734 }
1735
Eric Laurent3172d5e2012-05-09 11:38:16 -07001736 synchronized (streamState) {
1737 streamState.readSettings();
Eric Laurenta553c252009-07-17 12:17:14 -07001738
Eric Laurent3172d5e2012-05-09 11:38:16 -07001739 // unmute stream that was muted but is not affect by mute anymore
1740 if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType)) {
1741 int size = streamState.mDeathHandlers.size();
1742 for (int i = 0; i < size; i++) {
1743 streamState.mDeathHandlers.get(i).mMuteCount = 1;
1744 streamState.mDeathHandlers.get(i).mute(false);
1745 }
Eric Laurent4050c932009-07-08 02:52:14 -07001746 }
Eric Laurent4050c932009-07-08 02:52:14 -07001747 }
1748 }
1749
Eric Laurent24482012012-05-10 09:41:17 -07001750 checkAllAliasStreamVolumes();
1751
Eric Laurentd640bd32012-09-28 18:01:48 -07001752 synchronized (mSafeMediaVolumeState) {
1753 if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE) {
Eric Laurentf1a457d2012-09-20 16:27:23 -07001754 enforceSafeMediaVolume();
1755 }
Eric Laurentc34dcc12012-09-10 13:51:52 -07001756 }
1757
Eric Laurent4050c932009-07-08 02:52:14 -07001758 // apply new ringer mode
1759 setRingerModeInt(getRingerMode(), false);
1760 }
1761
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001762 /** @see AudioManager#setSpeakerphoneOn() */
1763 public void setSpeakerphoneOn(boolean on){
Eric Laurentdc1d17a2009-09-10 00:48:21 -07001764 if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
1765 return;
1766 }
Eric Laurentfa640152011-03-12 15:59:51 -08001767 mForcedUseForComm = on ? AudioSystem.FORCE_SPEAKER : AudioSystem.FORCE_NONE;
1768
Eric Laurentafbb0472011-12-15 09:04:23 -08001769 sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
Eric Laurentfa640152011-03-12 15:59:51 -08001770 AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0);
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001771 }
1772
1773 /** @see AudioManager#isSpeakerphoneOn() */
1774 public boolean isSpeakerphoneOn() {
Eric Laurentfa640152011-03-12 15:59:51 -08001775 return (mForcedUseForComm == AudioSystem.FORCE_SPEAKER);
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001776 }
1777
1778 /** @see AudioManager#setBluetoothScoOn() */
1779 public void setBluetoothScoOn(boolean on){
Eric Laurentdc1d17a2009-09-10 00:48:21 -07001780 if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
1781 return;
1782 }
Eric Laurentfa640152011-03-12 15:59:51 -08001783 mForcedUseForComm = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE;
1784
Eric Laurentafbb0472011-12-15 09:04:23 -08001785 sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
Eric Laurentfa640152011-03-12 15:59:51 -08001786 AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0);
Eric Laurentafbb0472011-12-15 09:04:23 -08001787 sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
Eric Laurentfa640152011-03-12 15:59:51 -08001788 AudioSystem.FOR_RECORD, mForcedUseForComm, null, 0);
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001789 }
1790
1791 /** @see AudioManager#isBluetoothScoOn() */
1792 public boolean isBluetoothScoOn() {
Eric Laurentfa640152011-03-12 15:59:51 -08001793 return (mForcedUseForComm == AudioSystem.FORCE_BT_SCO);
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001794 }
1795
Eric Laurent78472112012-05-21 08:57:21 -07001796 /** @see AudioManager#setBluetoothA2dpOn() */
1797 public void setBluetoothA2dpOn(boolean on) {
Eric Laurentc390bed2012-07-03 12:24:05 -07001798 synchronized (mBluetoothA2dpEnabledLock) {
1799 mBluetoothA2dpEnabled = on;
1800 sendMsg(mAudioHandler, MSG_SET_FORCE_BT_A2DP_USE, SENDMSG_QUEUE,
1801 AudioSystem.FOR_MEDIA,
1802 mBluetoothA2dpEnabled ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP,
1803 null, 0);
1804 }
Eric Laurent78472112012-05-21 08:57:21 -07001805 }
1806
1807 /** @see AudioManager#isBluetoothA2dpOn() */
1808 public boolean isBluetoothA2dpOn() {
1809 synchronized (mBluetoothA2dpEnabledLock) {
1810 return mBluetoothA2dpEnabled;
1811 }
1812 }
1813
Eric Laurent3def1ee2010-03-17 23:26:26 -07001814 /** @see AudioManager#startBluetoothSco() */
1815 public void startBluetoothSco(IBinder cb){
Eric Laurentdc03c612011-04-01 10:59:41 -07001816 if (!checkAudioSettingsPermission("startBluetoothSco()") ||
1817 !mBootCompleted) {
Eric Laurent3def1ee2010-03-17 23:26:26 -07001818 return;
1819 }
Eric Laurent854938a2011-02-22 12:05:20 -08001820 ScoClient client = getScoClient(cb, true);
Eric Laurent3def1ee2010-03-17 23:26:26 -07001821 client.incCount();
1822 }
1823
1824 /** @see AudioManager#stopBluetoothSco() */
1825 public void stopBluetoothSco(IBinder cb){
Eric Laurentdc03c612011-04-01 10:59:41 -07001826 if (!checkAudioSettingsPermission("stopBluetoothSco()") ||
1827 !mBootCompleted) {
Eric Laurent3def1ee2010-03-17 23:26:26 -07001828 return;
1829 }
Eric Laurent854938a2011-02-22 12:05:20 -08001830 ScoClient client = getScoClient(cb, false);
1831 if (client != null) {
1832 client.decCount();
1833 }
Eric Laurent3def1ee2010-03-17 23:26:26 -07001834 }
1835
Eric Laurent78472112012-05-21 08:57:21 -07001836
Eric Laurent3def1ee2010-03-17 23:26:26 -07001837 private class ScoClient implements IBinder.DeathRecipient {
1838 private IBinder mCb; // To be notified of client's death
Marco Nelissenf1ddd512011-08-10 14:15:44 -07001839 private int mCreatorPid;
Eric Laurent3def1ee2010-03-17 23:26:26 -07001840 private int mStartcount; // number of SCO connections started by this client
1841
1842 ScoClient(IBinder cb) {
1843 mCb = cb;
Marco Nelissenf1ddd512011-08-10 14:15:44 -07001844 mCreatorPid = Binder.getCallingPid();
Eric Laurent3def1ee2010-03-17 23:26:26 -07001845 mStartcount = 0;
1846 }
1847
1848 public void binderDied() {
1849 synchronized(mScoClients) {
1850 Log.w(TAG, "SCO client died");
1851 int index = mScoClients.indexOf(this);
1852 if (index < 0) {
1853 Log.w(TAG, "unregistered SCO client died");
1854 } else {
1855 clearCount(true);
1856 mScoClients.remove(this);
1857 }
1858 }
1859 }
1860
1861 public void incCount() {
1862 synchronized(mScoClients) {
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07001863 requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED);
Eric Laurent3def1ee2010-03-17 23:26:26 -07001864 if (mStartcount == 0) {
1865 try {
1866 mCb.linkToDeath(this, 0);
1867 } catch (RemoteException e) {
1868 // client has already died!
1869 Log.w(TAG, "ScoClient incCount() could not link to "+mCb+" binder death");
1870 }
1871 }
1872 mStartcount++;
1873 }
1874 }
1875
1876 public void decCount() {
1877 synchronized(mScoClients) {
1878 if (mStartcount == 0) {
1879 Log.w(TAG, "ScoClient.decCount() already 0");
1880 } else {
1881 mStartcount--;
1882 if (mStartcount == 0) {
Eric Laurente2dd8c42010-06-30 19:41:56 -07001883 try {
1884 mCb.unlinkToDeath(this, 0);
1885 } catch (NoSuchElementException e) {
1886 Log.w(TAG, "decCount() going to 0 but not registered to binder");
1887 }
Eric Laurent3def1ee2010-03-17 23:26:26 -07001888 }
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07001889 requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
Eric Laurent3def1ee2010-03-17 23:26:26 -07001890 }
1891 }
1892 }
1893
1894 public void clearCount(boolean stopSco) {
1895 synchronized(mScoClients) {
Eric Laurente2dd8c42010-06-30 19:41:56 -07001896 if (mStartcount != 0) {
1897 try {
1898 mCb.unlinkToDeath(this, 0);
1899 } catch (NoSuchElementException e) {
1900 Log.w(TAG, "clearCount() mStartcount: "+mStartcount+" != 0 but not registered to binder");
1901 }
1902 }
Eric Laurent3def1ee2010-03-17 23:26:26 -07001903 mStartcount = 0;
Eric Laurent3def1ee2010-03-17 23:26:26 -07001904 if (stopSco) {
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07001905 requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
Eric Laurent3def1ee2010-03-17 23:26:26 -07001906 }
1907 }
1908 }
1909
1910 public int getCount() {
1911 return mStartcount;
1912 }
1913
1914 public IBinder getBinder() {
1915 return mCb;
1916 }
1917
Eric Laurentd7454be2011-09-14 08:45:58 -07001918 public int getPid() {
1919 return mCreatorPid;
1920 }
1921
Eric Laurent3def1ee2010-03-17 23:26:26 -07001922 public int totalCount() {
1923 synchronized(mScoClients) {
1924 int count = 0;
1925 int size = mScoClients.size();
1926 for (int i = 0; i < size; i++) {
1927 count += mScoClients.get(i).getCount();
1928 }
1929 return count;
1930 }
1931 }
1932
1933 private void requestScoState(int state) {
Eric Laurent62ef7672010-11-24 10:58:32 -08001934 checkScoAudioState();
Eric Laurentdc03c612011-04-01 10:59:41 -07001935 if (totalCount() == 0) {
1936 if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
1937 // Make sure that the state transitions to CONNECTING even if we cannot initiate
1938 // the connection.
1939 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
1940 // Accept SCO audio activation only in NORMAL audio mode or if the mode is
Marco Nelissenf1ddd512011-08-10 14:15:44 -07001941 // currently controlled by the same client process.
Eric Laurent9f103de2011-09-08 15:04:23 -07001942 synchronized(mSetModeDeathHandlers) {
1943 if ((mSetModeDeathHandlers.isEmpty() ||
1944 mSetModeDeathHandlers.get(0).getPid() == mCreatorPid) &&
1945 (mScoAudioState == SCO_STATE_INACTIVE ||
1946 mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
1947 if (mScoAudioState == SCO_STATE_INACTIVE) {
1948 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
1949 if (mBluetoothHeadset.startScoUsingVirtualVoiceCall(
1950 mBluetoothHeadsetDevice)) {
1951 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
1952 } else {
1953 broadcastScoConnectionState(
1954 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1955 }
1956 } else if (getBluetoothHeadset()) {
1957 mScoAudioState = SCO_STATE_ACTIVATE_REQ;
Eric Laurentdc03c612011-04-01 10:59:41 -07001958 }
Eric Laurent9f103de2011-09-08 15:04:23 -07001959 } else {
1960 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
1961 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
Eric Laurentdc03c612011-04-01 10:59:41 -07001962 }
1963 } else {
Eric Laurent9f103de2011-09-08 15:04:23 -07001964 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
Eric Laurentdc03c612011-04-01 10:59:41 -07001965 }
Eric Laurentdc03c612011-04-01 10:59:41 -07001966 }
Eric Laurent62ef7672010-11-24 10:58:32 -08001967 } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED &&
Eric Laurentdc03c612011-04-01 10:59:41 -07001968 (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
1969 mScoAudioState == SCO_STATE_ACTIVATE_REQ)) {
1970 if (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) {
Marco Nelissen671db6f2011-09-06 16:29:12 -07001971 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
Eric Laurentdc03c612011-04-01 10:59:41 -07001972 if (!mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
1973 mBluetoothHeadsetDevice)) {
1974 mScoAudioState = SCO_STATE_INACTIVE;
1975 broadcastScoConnectionState(
1976 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1977 }
1978 } else if (getBluetoothHeadset()) {
1979 mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
1980 }
1981 } else {
1982 mScoAudioState = SCO_STATE_INACTIVE;
1983 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1984 }
Eric Laurent3def1ee2010-03-17 23:26:26 -07001985 }
1986 }
1987 }
1988 }
1989
Eric Laurent62ef7672010-11-24 10:58:32 -08001990 private void checkScoAudioState() {
1991 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null &&
Eric Laurentdc03c612011-04-01 10:59:41 -07001992 mScoAudioState == SCO_STATE_INACTIVE &&
Eric Laurent62ef7672010-11-24 10:58:32 -08001993 mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
1994 != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1995 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
1996 }
1997 }
1998
Eric Laurent854938a2011-02-22 12:05:20 -08001999 private ScoClient getScoClient(IBinder cb, boolean create) {
Eric Laurent3def1ee2010-03-17 23:26:26 -07002000 synchronized(mScoClients) {
Eric Laurent854938a2011-02-22 12:05:20 -08002001 ScoClient client = null;
Eric Laurent3def1ee2010-03-17 23:26:26 -07002002 int size = mScoClients.size();
2003 for (int i = 0; i < size; i++) {
2004 client = mScoClients.get(i);
2005 if (client.getBinder() == cb)
2006 return client;
2007 }
Eric Laurent854938a2011-02-22 12:05:20 -08002008 if (create) {
2009 client = new ScoClient(cb);
2010 mScoClients.add(client);
2011 }
Eric Laurent3def1ee2010-03-17 23:26:26 -07002012 return client;
2013 }
2014 }
2015
Eric Laurentd7454be2011-09-14 08:45:58 -07002016 public void clearAllScoClients(int exceptPid, boolean stopSco) {
Eric Laurent3def1ee2010-03-17 23:26:26 -07002017 synchronized(mScoClients) {
Eric Laurent854938a2011-02-22 12:05:20 -08002018 ScoClient savedClient = null;
Eric Laurent3def1ee2010-03-17 23:26:26 -07002019 int size = mScoClients.size();
2020 for (int i = 0; i < size; i++) {
Eric Laurent854938a2011-02-22 12:05:20 -08002021 ScoClient cl = mScoClients.get(i);
Eric Laurentd7454be2011-09-14 08:45:58 -07002022 if (cl.getPid() != exceptPid) {
Eric Laurent854938a2011-02-22 12:05:20 -08002023 cl.clearCount(stopSco);
2024 } else {
2025 savedClient = cl;
2026 }
2027 }
2028 mScoClients.clear();
2029 if (savedClient != null) {
2030 mScoClients.add(savedClient);
Eric Laurent3def1ee2010-03-17 23:26:26 -07002031 }
2032 }
2033 }
2034
Eric Laurentdc03c612011-04-01 10:59:41 -07002035 private boolean getBluetoothHeadset() {
2036 boolean result = false;
2037 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
2038 if (adapter != null) {
2039 result = adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
2040 BluetoothProfile.HEADSET);
2041 }
2042 // If we could not get a bluetooth headset proxy, send a failure message
2043 // without delay to reset the SCO audio state and clear SCO clients.
2044 // If we could get a proxy, send a delayed failure message that will reset our state
2045 // in case we don't receive onServiceConnected().
Eric Laurentafbb0472011-12-15 09:04:23 -08002046 sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED,
Eric Laurentdc03c612011-04-01 10:59:41 -07002047 SENDMSG_REPLACE, 0, 0, null, result ? BT_HEADSET_CNCT_TIMEOUT_MS : 0);
2048 return result;
2049 }
2050
Eric Laurentd7454be2011-09-14 08:45:58 -07002051 private void disconnectBluetoothSco(int exceptPid) {
Eric Laurentdc03c612011-04-01 10:59:41 -07002052 synchronized(mScoClients) {
2053 checkScoAudioState();
2054 if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL ||
2055 mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
2056 if (mBluetoothHeadsetDevice != null) {
2057 if (mBluetoothHeadset != null) {
2058 if (!mBluetoothHeadset.stopVoiceRecognition(
Eric Laurentb06ac832011-05-25 15:55:18 -07002059 mBluetoothHeadsetDevice)) {
Eric Laurentafbb0472011-12-15 09:04:23 -08002060 sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED,
Eric Laurentdc03c612011-04-01 10:59:41 -07002061 SENDMSG_REPLACE, 0, 0, null, 0);
2062 }
2063 } else if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL &&
2064 getBluetoothHeadset()) {
2065 mScoAudioState = SCO_STATE_DEACTIVATE_EXT_REQ;
2066 }
2067 }
2068 } else {
Eric Laurentd7454be2011-09-14 08:45:58 -07002069 clearAllScoClients(exceptPid, true);
Eric Laurentdc03c612011-04-01 10:59:41 -07002070 }
2071 }
2072 }
2073
2074 private void resetBluetoothSco() {
2075 synchronized(mScoClients) {
Eric Laurentd7454be2011-09-14 08:45:58 -07002076 clearAllScoClients(0, false);
Eric Laurentdc03c612011-04-01 10:59:41 -07002077 mScoAudioState = SCO_STATE_INACTIVE;
2078 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
2079 }
2080 }
2081
2082 private void broadcastScoConnectionState(int state) {
2083 if (state != mScoConnectionState) {
2084 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
2085 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
2086 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE,
2087 mScoConnectionState);
Dianne Hackborn5ac72a22012-08-29 18:32:08 -07002088 sendStickyBroadcastToAll(newIntent);
Eric Laurentdc03c612011-04-01 10:59:41 -07002089 mScoConnectionState = state;
2090 }
2091 }
2092
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07002093 private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
2094 new BluetoothProfile.ServiceListener() {
2095 public void onServiceConnected(int profile, BluetoothProfile proxy) {
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08002096 BluetoothDevice btDevice;
2097 List<BluetoothDevice> deviceList;
2098 switch(profile) {
2099 case BluetoothProfile.A2DP:
2100 BluetoothA2dp a2dp = (BluetoothA2dp) proxy;
2101 deviceList = a2dp.getConnectedDevices();
Eric Laurent62ef7672010-11-24 10:58:32 -08002102 if (deviceList.size() > 0) {
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08002103 btDevice = deviceList.get(0);
Eric Laurentb1fbaac2012-05-29 09:24:28 -07002104 synchronized (mConnectedDevices) {
2105 int state = a2dp.getConnectionState(btDevice);
2106 int delay = checkSendBecomingNoisyIntent(
2107 AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
2108 (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0);
Jean-Michel Trivie12c39b2012-06-06 10:51:58 -07002109 queueMsgUnderWakeLock(mAudioHandler,
Eric Laurentb1fbaac2012-05-29 09:24:28 -07002110 MSG_SET_A2DP_CONNECTION_STATE,
Eric Laurentb1fbaac2012-05-29 09:24:28 -07002111 state,
2112 0,
2113 btDevice,
2114 delay);
2115 }
Eric Laurent62ef7672010-11-24 10:58:32 -08002116 }
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08002117 break;
2118
2119 case BluetoothProfile.HEADSET:
2120 synchronized (mScoClients) {
2121 // Discard timeout message
2122 mAudioHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
2123 mBluetoothHeadset = (BluetoothHeadset) proxy;
2124 deviceList = mBluetoothHeadset.getConnectedDevices();
2125 if (deviceList.size() > 0) {
2126 mBluetoothHeadsetDevice = deviceList.get(0);
2127 } else {
2128 mBluetoothHeadsetDevice = null;
2129 }
2130 // Refresh SCO audio state
2131 checkScoAudioState();
2132 // Continue pending action if any
2133 if (mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
2134 mScoAudioState == SCO_STATE_DEACTIVATE_REQ ||
2135 mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
2136 boolean status = false;
2137 if (mBluetoothHeadsetDevice != null) {
2138 switch (mScoAudioState) {
2139 case SCO_STATE_ACTIVATE_REQ:
2140 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
2141 status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
2142 mBluetoothHeadsetDevice);
2143 break;
2144 case SCO_STATE_DEACTIVATE_REQ:
2145 status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
2146 mBluetoothHeadsetDevice);
2147 break;
2148 case SCO_STATE_DEACTIVATE_EXT_REQ:
2149 status = mBluetoothHeadset.stopVoiceRecognition(
2150 mBluetoothHeadsetDevice);
2151 }
2152 }
2153 if (!status) {
Eric Laurentafbb0472011-12-15 09:04:23 -08002154 sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED,
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08002155 SENDMSG_REPLACE, 0, 0, null, 0);
Eric Laurentdc03c612011-04-01 10:59:41 -07002156 }
2157 }
Eric Laurentdc03c612011-04-01 10:59:41 -07002158 }
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08002159 break;
2160
2161 default:
2162 break;
Eric Laurent3def1ee2010-03-17 23:26:26 -07002163 }
2164 }
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07002165 public void onServiceDisconnected(int profile) {
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08002166 switch(profile) {
2167 case BluetoothProfile.A2DP:
2168 synchronized (mConnectedDevices) {
2169 if (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)) {
2170 makeA2dpDeviceUnavailableNow(
2171 mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
2172 }
2173 }
2174 break;
2175
2176 case BluetoothProfile.HEADSET:
2177 synchronized (mScoClients) {
2178 mBluetoothHeadset = null;
2179 }
2180 break;
2181
2182 default:
2183 break;
Eric Laurent3def1ee2010-03-17 23:26:26 -07002184 }
2185 }
2186 };
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002187
Jean-Michel Trivi2ac2afe2012-08-21 11:16:55 -07002188 /** see AudioManager.setRemoteSubmixOn(boolean on) */
2189 public void setRemoteSubmixOn(boolean on, int address) {
2190 sendMsg(mAudioHandler, MSG_SET_RSX_CONNECTION_STATE,
2191 SENDMSG_REPLACE /* replace with QUEUE when multiple addresses are supported */,
2192 on ? 1 : 0 /*arg1*/,
2193 address /*arg2*/,
2194 null/*obj*/, 0/*delay*/);
2195
2196 // Note that we are currently forcing use of remote submix as soon as corresponding device
2197 // is made available
2198 sendMsg(mAudioHandler, MSG_SET_FORCE_RSX_USE, SENDMSG_REPLACE,
2199 AudioSystem.FOR_MEDIA,
2200 on ? AudioSystem.FORCE_REMOTE_SUBMIX : AudioSystem.FORCE_NONE,
2201 null/*obj*/, 0/*delay*/);
2202 }
2203
2204 private void onSetRsxConnectionState(int available, int address) {
2205 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_REMOTE_SUBMIX,
2206 available == 1 ?
2207 AudioSystem.DEVICE_STATE_AVAILABLE : AudioSystem.DEVICE_STATE_UNAVAILABLE,
2208 String.valueOf(address) /*device_address*/);
2209 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_REMOTE_SUBMIX,
2210 available == 1 ?
2211 AudioSystem.DEVICE_STATE_AVAILABLE : AudioSystem.DEVICE_STATE_UNAVAILABLE,
2212 String.valueOf(address) /*device_address*/);
2213 }
2214
Eric Laurentc34dcc12012-09-10 13:51:52 -07002215 private void onCheckMusicActive() {
Eric Laurentd640bd32012-09-28 18:01:48 -07002216 synchronized (mSafeMediaVolumeState) {
2217 if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_INACTIVE) {
Eric Laurentc34dcc12012-09-10 13:51:52 -07002218 int device = getDeviceForStream(AudioSystem.STREAM_MUSIC);
2219
2220 if ((device & mSafeMediaVolumeDevices) != 0) {
2221 sendMsg(mAudioHandler,
2222 MSG_CHECK_MUSIC_ACTIVE,
2223 SENDMSG_REPLACE,
Eric Laurentf1a457d2012-09-20 16:27:23 -07002224 0,
Eric Laurentc34dcc12012-09-10 13:51:52 -07002225 0,
2226 null,
2227 MUSIC_ACTIVE_POLL_PERIOD_MS);
Eric Laurentf1a457d2012-09-20 16:27:23 -07002228 int index = mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(device,
2229 false /*lastAudible*/);
2230 if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0) &&
2231 (index > mSafeMediaVolumeIndex)) {
Eric Laurentc34dcc12012-09-10 13:51:52 -07002232 // Approximate cumulative active music time
2233 mMusicActiveMs += MUSIC_ACTIVE_POLL_PERIOD_MS;
2234 if (mMusicActiveMs > UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX) {
2235 setSafeMediaVolumeEnabled(true);
2236 mMusicActiveMs = 0;
2237 mVolumePanel.postDisplaySafeVolumeWarning();
2238 }
2239 }
2240 }
2241 }
2242 }
2243 }
2244
Eric Laurentd640bd32012-09-28 18:01:48 -07002245 private void onConfigureSafeVolume(boolean force) {
2246 synchronized (mSafeMediaVolumeState) {
2247 int mcc = mContext.getResources().getConfiguration().mcc;
2248 if ((mMcc != mcc) || ((mMcc == 0) && force)) {
2249 mSafeMediaVolumeIndex = mContext.getResources().getInteger(
2250 com.android.internal.R.integer.config_safe_media_volume_index) * 10;
2251 boolean safeMediaVolumeEnabled = mContext.getResources().getBoolean(
2252 com.android.internal.R.bool.config_safe_media_volume_enabled);
2253 if (safeMediaVolumeEnabled) {
2254 mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_ACTIVE;
2255 enforceSafeMediaVolume();
2256 } else {
2257 mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_DISABLED;
2258 }
2259 mMcc = mcc;
2260 }
2261 }
2262 }
2263
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002264 ///////////////////////////////////////////////////////////////////////////
2265 // Internal methods
2266 ///////////////////////////////////////////////////////////////////////////
2267
2268 /**
2269 * Checks if the adjustment should change ringer mode instead of just
2270 * adjusting volume. If so, this will set the proper ringer mode and volume
2271 * indices on the stream states.
2272 */
Eric Laurent24482012012-05-10 09:41:17 -07002273 private boolean checkForRingerModeChange(int oldIndex, int direction, int step) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002274 boolean adjustVolumeIndex = true;
Glenn Kastenba195eb2011-12-13 09:30:40 -08002275 int ringerMode = getRingerMode();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002276
Eric Laurentbffc3d12012-05-07 17:43:49 -07002277 switch (ringerMode) {
2278 case RINGER_MODE_NORMAL:
2279 if (direction == AudioManager.ADJUST_LOWER) {
2280 if (mHasVibrator) {
Eric Laurent24482012012-05-10 09:41:17 -07002281 // "step" is the delta in internal index units corresponding to a
2282 // change of 1 in UI index units.
2283 // Because of rounding when rescaling from one stream index range to its alias
2284 // index range, we cannot simply test oldIndex == step:
2285 // (step <= oldIndex < 2 * step) is equivalent to: (old UI index == 1)
2286 if (step <= oldIndex && oldIndex < 2 * step) {
Eric Laurentbffc3d12012-05-07 17:43:49 -07002287 ringerMode = RINGER_MODE_VIBRATE;
2288 }
2289 } else {
Eric Laurent24482012012-05-10 09:41:17 -07002290 // (oldIndex < step) is equivalent to (old UI index == 0)
2291 if ((oldIndex < step) && mPrevVolDirection != AudioManager.ADJUST_LOWER) {
Eric Laurentbffc3d12012-05-07 17:43:49 -07002292 ringerMode = RINGER_MODE_SILENT;
2293 }
Eric Laurent3d4c06f2011-08-15 19:58:28 -07002294 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002295 }
Eric Laurentbffc3d12012-05-07 17:43:49 -07002296 break;
2297 case RINGER_MODE_VIBRATE:
2298 if (!mHasVibrator) {
2299 Log.e(TAG, "checkForRingerModeChange() current ringer mode is vibrate" +
2300 "but no vibrator is present");
2301 break;
2302 }
Amith Yamasanic696a532011-10-28 17:02:37 -07002303 if ((direction == AudioManager.ADJUST_LOWER)) {
Amith Yamasanic696a532011-10-28 17:02:37 -07002304 if (mPrevVolDirection != AudioManager.ADJUST_LOWER) {
Eric Laurentbffc3d12012-05-07 17:43:49 -07002305 ringerMode = RINGER_MODE_SILENT;
Amith Yamasanic696a532011-10-28 17:02:37 -07002306 }
2307 } else if (direction == AudioManager.ADJUST_RAISE) {
Eric Laurentbffc3d12012-05-07 17:43:49 -07002308 ringerMode = RINGER_MODE_NORMAL;
Amith Yamasanic696a532011-10-28 17:02:37 -07002309 }
2310 adjustVolumeIndex = false;
Eric Laurentbffc3d12012-05-07 17:43:49 -07002311 break;
2312 case RINGER_MODE_SILENT:
Daniel Sandler6329bf72010-02-26 15:17:44 -05002313 if (direction == AudioManager.ADJUST_RAISE) {
Eric Laurentbffc3d12012-05-07 17:43:49 -07002314 if (mHasVibrator) {
2315 ringerMode = RINGER_MODE_VIBRATE;
2316 } else {
2317 ringerMode = RINGER_MODE_NORMAL;
2318 }
Daniel Sandler6329bf72010-02-26 15:17:44 -05002319 }
Eric Laurentb024c302011-10-14 17:19:27 -07002320 adjustVolumeIndex = false;
Eric Laurentbffc3d12012-05-07 17:43:49 -07002321 break;
2322 default:
2323 Log.e(TAG, "checkForRingerModeChange() wrong ringer mode: "+ringerMode);
2324 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002325 }
2326
Eric Laurentbffc3d12012-05-07 17:43:49 -07002327 setRingerMode(ringerMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002328
Eric Laurent25101b02011-02-02 09:33:30 -08002329 mPrevVolDirection = direction;
2330
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002331 return adjustVolumeIndex;
2332 }
2333
2334 public boolean isStreamAffectedByRingerMode(int streamType) {
Eric Laurent9bcf4012009-06-12 06:09:28 -07002335 return (mRingerModeAffectedStreams & (1 << streamType)) != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002336 }
2337
Eric Laurent5b4e6542010-03-19 20:02:21 -07002338 private boolean isStreamMutedByRingerMode(int streamType) {
2339 return (mRingerModeMutedStreams & (1 << streamType)) != 0;
2340 }
2341
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002342 public boolean isStreamAffectedByMute(int streamType) {
2343 return (mMuteAffectedStreams & (1 << streamType)) != 0;
2344 }
2345
2346 private void ensureValidDirection(int direction) {
2347 if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) {
2348 throw new IllegalArgumentException("Bad direction " + direction);
2349 }
2350 }
2351
Lei Zhang6c798972012-03-02 11:40:12 -08002352 private void ensureValidSteps(int steps) {
2353 if (Math.abs(steps) > MAX_BATCH_VOLUME_ADJUST_STEPS) {
2354 throw new IllegalArgumentException("Bad volume adjust steps " + steps);
2355 }
2356 }
2357
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002358 private void ensureValidStreamType(int streamType) {
2359 if (streamType < 0 || streamType >= mStreamStates.length) {
2360 throw new IllegalArgumentException("Bad stream type " + streamType);
2361 }
2362 }
2363
Eric Laurent6d517662012-04-23 18:42:39 -07002364 private boolean isInCommunication() {
2365 boolean isOffhook = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002366
Eric Laurent25101b02011-02-02 09:33:30 -08002367 if (mVoiceCapable) {
Eric Laurent25101b02011-02-02 09:33:30 -08002368 try {
2369 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
2370 if (phone != null) isOffhook = phone.isOffhook();
2371 } catch (RemoteException e) {
2372 Log.w(TAG, "Couldn't connect to phone service", e);
2373 }
Eric Laurent6d517662012-04-23 18:42:39 -07002374 }
2375 return (isOffhook || getMode() == AudioManager.MODE_IN_COMMUNICATION);
2376 }
Eric Laurent25101b02011-02-02 09:33:30 -08002377
Eric Laurent6d517662012-04-23 18:42:39 -07002378 private int getActiveStreamType(int suggestedStreamType) {
2379 if (mVoiceCapable) {
2380 if (isInCommunication()) {
Eric Laurent25101b02011-02-02 09:33:30 -08002381 if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
2382 == AudioSystem.FORCE_BT_SCO) {
2383 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
2384 return AudioSystem.STREAM_BLUETOOTH_SCO;
2385 } else {
2386 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
2387 return AudioSystem.STREAM_VOICE_CALL;
2388 }
Eric Laurent25101b02011-02-02 09:33:30 -08002389 } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07002390 // Having the suggested stream be USE_DEFAULT_STREAM_TYPE is how remote control
2391 // volume can have priority over STREAM_MUSIC
2392 if (checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
2393 if (DEBUG_VOL)
2394 Log.v(TAG, "getActiveStreamType: Forcing STREAM_REMOTE_MUSIC");
2395 return STREAM_REMOTE_MUSIC;
Eric Laurent9903e262012-09-21 18:10:32 -07002396 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC,
2397 DEFAULT_STREAM_TYPE_OVERRIDE_DELAY_MS)) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07002398 if (DEBUG_VOL)
2399 Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC stream active");
2400 return AudioSystem.STREAM_MUSIC;
2401 } else {
2402 if (DEBUG_VOL)
2403 Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING b/c default");
2404 return AudioSystem.STREAM_RING;
2405 }
2406 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
2407 if (DEBUG_VOL)
2408 Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC stream active");
2409 return AudioSystem.STREAM_MUSIC;
Joe Onoratoc7fcba42011-01-05 16:53:11 -08002410 } else {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07002411 if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Returning suggested type "
2412 + suggestedStreamType);
Eric Laurent25101b02011-02-02 09:33:30 -08002413 return suggestedStreamType;
2414 }
2415 } else {
Eric Laurent6d517662012-04-23 18:42:39 -07002416 if (isInCommunication()) {
Eric Laurent25101b02011-02-02 09:33:30 -08002417 if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
2418 == AudioSystem.FORCE_BT_SCO) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07002419 if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO");
Eric Laurent25101b02011-02-02 09:33:30 -08002420 return AudioSystem.STREAM_BLUETOOTH_SCO;
2421 } else {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07002422 if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL");
Eric Laurent25101b02011-02-02 09:33:30 -08002423 return AudioSystem.STREAM_VOICE_CALL;
2424 }
2425 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_NOTIFICATION,
Eric Laurent9903e262012-09-21 18:10:32 -07002426 DEFAULT_STREAM_TYPE_OVERRIDE_DELAY_MS) ||
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07002427 AudioSystem.isStreamActive(AudioSystem.STREAM_RING,
Eric Laurent9903e262012-09-21 18:10:32 -07002428 DEFAULT_STREAM_TYPE_OVERRIDE_DELAY_MS)) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07002429 if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION");
Eric Laurent25101b02011-02-02 09:33:30 -08002430 return AudioSystem.STREAM_NOTIFICATION;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07002431 } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
2432 if (checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
2433 // Having the suggested stream be USE_DEFAULT_STREAM_TYPE is how remote control
2434 // volume can have priority over STREAM_MUSIC
2435 if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_REMOTE_MUSIC");
2436 return STREAM_REMOTE_MUSIC;
2437 } else {
2438 if (DEBUG_VOL)
2439 Log.v(TAG, "getActiveStreamType: using STREAM_MUSIC as default");
2440 return AudioSystem.STREAM_MUSIC;
2441 }
Eric Laurent25101b02011-02-02 09:33:30 -08002442 } else {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07002443 if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Returning suggested type "
2444 + suggestedStreamType);
Eric Laurent25101b02011-02-02 09:33:30 -08002445 return suggestedStreamType;
Joe Onoratoc7fcba42011-01-05 16:53:11 -08002446 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002447 }
2448 }
2449
Glenn Kastenba195eb2011-12-13 09:30:40 -08002450 private void broadcastRingerMode(int ringerMode) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002451 // Send sticky broadcast
Dianne Hackborn1c633fc2009-12-08 19:45:14 -08002452 Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION);
Glenn Kastenba195eb2011-12-13 09:30:40 -08002453 broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, ringerMode);
Dianne Hackborn1c633fc2009-12-08 19:45:14 -08002454 broadcast.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
2455 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
Dianne Hackborn5ac72a22012-08-29 18:32:08 -07002456 sendStickyBroadcastToAll(broadcast);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002457 }
2458
2459 private void broadcastVibrateSetting(int vibrateType) {
2460 // Send broadcast
2461 if (ActivityManagerNative.isSystemReady()) {
2462 Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
2463 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType);
2464 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType));
Dianne Hackborn5ac72a22012-08-29 18:32:08 -07002465 sendBroadcastToAll(broadcast);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002466 }
2467 }
2468
2469 // Message helper methods
Jean-Michel Trivi2d8dab52012-05-30 18:13:59 -07002470 /**
2471 * Queue a message on the given handler's message queue, after acquiring the service wake lock.
2472 * Note that the wake lock needs to be released after the message has been handled.
2473 */
2474 private void queueMsgUnderWakeLock(Handler handler, int msg,
2475 int arg1, int arg2, Object obj, int delay) {
2476 mMediaEventWakeLock.acquire();
2477 sendMsg(handler, msg, SENDMSG_QUEUE, arg1, arg2, obj, delay);
2478 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002479
Eric Laurentafbb0472011-12-15 09:04:23 -08002480 private static void sendMsg(Handler handler, int msg,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002481 int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002482
2483 if (existingMsgPolicy == SENDMSG_REPLACE) {
2484 handler.removeMessages(msg);
2485 } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
2486 return;
2487 }
2488
Eric Laurentafbb0472011-12-15 09:04:23 -08002489 handler.sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002490 }
2491
2492 boolean checkAudioSettingsPermission(String method) {
2493 if (mContext.checkCallingOrSelfPermission("android.permission.MODIFY_AUDIO_SETTINGS")
2494 == PackageManager.PERMISSION_GRANTED) {
2495 return true;
2496 }
2497 String msg = "Audio Settings Permission Denial: " + method + " from pid="
2498 + Binder.getCallingPid()
2499 + ", uid=" + Binder.getCallingUid();
2500 Log.w(TAG, msg);
2501 return false;
2502 }
2503
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002504 private int getDeviceForStream(int stream) {
2505 int device = AudioSystem.getDevicesForStream(stream);
2506 if ((device & (device - 1)) != 0) {
2507 // Multiple device selection is either:
2508 // - speaker + one other device: give priority to speaker in this case.
2509 // - one A2DP device + another device: happens with duplicated output. In this case
2510 // retain the device on the A2DP output as the other must not correspond to an active
2511 // selection if not the speaker.
2512 if ((device & AudioSystem.DEVICE_OUT_SPEAKER) != 0) {
2513 device = AudioSystem.DEVICE_OUT_SPEAKER;
2514 } else {
2515 device &= AudioSystem.DEVICE_OUT_ALL_A2DP;
2516 }
2517 }
2518 return device;
2519 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002520
Eric Laurentb1fbaac2012-05-29 09:24:28 -07002521 public void setWiredDeviceConnectionState(int device, int state, String name) {
2522 synchronized (mConnectedDevices) {
2523 int delay = checkSendBecomingNoisyIntent(device, state);
Jean-Michel Trivi2d8dab52012-05-30 18:13:59 -07002524 queueMsgUnderWakeLock(mAudioHandler,
Eric Laurentb1fbaac2012-05-29 09:24:28 -07002525 MSG_SET_WIRED_DEVICE_CONNECTION_STATE,
Eric Laurentb1fbaac2012-05-29 09:24:28 -07002526 device,
2527 state,
2528 name,
2529 delay);
2530 }
2531 }
2532
2533 public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state)
2534 {
2535 int delay;
2536 synchronized (mConnectedDevices) {
2537 delay = checkSendBecomingNoisyIntent(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
2538 (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0);
Jean-Michel Trivi2d8dab52012-05-30 18:13:59 -07002539 queueMsgUnderWakeLock(mAudioHandler,
Eric Laurentb1fbaac2012-05-29 09:24:28 -07002540 MSG_SET_A2DP_CONNECTION_STATE,
Eric Laurentb1fbaac2012-05-29 09:24:28 -07002541 state,
2542 0,
2543 device,
2544 delay);
2545 }
2546 return delay;
2547 }
2548
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002549 ///////////////////////////////////////////////////////////////////////////
2550 // Inner classes
2551 ///////////////////////////////////////////////////////////////////////////
2552
2553 public class VolumeStreamState {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002554 private final int mStreamType;
2555
Jean-Michel Trivi11a74a72009-10-27 17:39:30 -07002556 private String mVolumeIndexSettingName;
2557 private String mLastAudibleVolumeIndexSettingName;
Eric Laurenta553c252009-07-17 12:17:14 -07002558 private int mIndexMax;
Eric Laurent3172d5e2012-05-09 11:38:16 -07002559 private final ConcurrentHashMap<Integer, Integer> mIndex =
2560 new ConcurrentHashMap<Integer, Integer>(8, 0.75f, 4);
2561 private final ConcurrentHashMap<Integer, Integer> mLastAudibleIndex =
2562 new ConcurrentHashMap<Integer, Integer>(8, 0.75f, 4);
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002563 private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo clients death
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002564
Eric Laurenta553c252009-07-17 12:17:14 -07002565 private VolumeStreamState(String settingName, int streamType) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002566
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002567 mVolumeIndexSettingName = settingName;
2568 mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002569
2570 mStreamType = streamType;
Jared Suttles59820132009-08-13 21:50:52 -05002571 mIndexMax = MAX_STREAM_VOLUME[streamType];
Eric Laurenta553c252009-07-17 12:17:14 -07002572 AudioSystem.initStreamVolume(streamType, 0, mIndexMax);
2573 mIndexMax *= 10;
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002574
2575 readSettings();
2576
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002577 mDeathHandlers = new ArrayList<VolumeDeathHandler>();
2578 }
2579
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002580 public String getSettingNameForDevice(boolean lastAudible, int device) {
2581 String name = lastAudible ?
2582 mLastAudibleVolumeIndexSettingName :
2583 mVolumeIndexSettingName;
2584 String suffix = AudioSystem.getDeviceName(device);
2585 if (suffix.isEmpty()) {
2586 return name;
2587 }
2588 return name + "_" + suffix;
Jean-Michel Trivi11a74a72009-10-27 17:39:30 -07002589 }
2590
Eric Laurent3172d5e2012-05-09 11:38:16 -07002591 public synchronized void readSettings() {
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002592 int remainingDevices = AudioSystem.DEVICE_OUT_ALL;
Eric Laurent9ac8d0c2012-03-07 10:45:36 -08002593
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002594 for (int i = 0; remainingDevices != 0; i++) {
2595 int device = (1 << i);
2596 if ((device & remainingDevices) == 0) {
2597 continue;
2598 }
2599 remainingDevices &= ~device;
2600
2601 // retrieve current volume for device
Eric Laurent9ac8d0c2012-03-07 10:45:36 -08002602 String name = getSettingNameForDevice(false /* lastAudible */, device);
2603 // if no volume stored for current stream and device, use default volume if default
2604 // device, continue otherwise
2605 int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT) ?
2606 AudioManager.DEFAULT_STREAM_VOLUME[mStreamType] : -1;
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07002607 int index = Settings.System.getIntForUser(
2608 mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002609 if (index == -1) {
2610 continue;
2611 }
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002612
2613 // retrieve last audible volume for device
Eric Laurent9ac8d0c2012-03-07 10:45:36 -08002614 name = getSettingNameForDevice(true /* lastAudible */, device);
2615 // use stored last audible index if present, otherwise use current index if not 0
2616 // or default index
2617 defaultIndex = (index > 0) ?
2618 index : AudioManager.DEFAULT_STREAM_VOLUME[mStreamType];
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07002619 int lastAudibleIndex = Settings.System.getIntForUser(
2620 mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
Eric Laurent9ac8d0c2012-03-07 10:45:36 -08002621
Eric Laurentbffc3d12012-05-07 17:43:49 -07002622 // a last audible index of 0 should never be stored for ring and notification
2623 // streams on phones (voice capable devices).
2624 // same for system stream on phones and tablets
Eric Laurent9ac8d0c2012-03-07 10:45:36 -08002625 if ((lastAudibleIndex == 0) &&
Eric Laurentbffc3d12012-05-07 17:43:49 -07002626 ((mVoiceCapable &&
2627 (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_RING)) ||
2628 (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_SYSTEM))) {
Eric Laurent9ac8d0c2012-03-07 10:45:36 -08002629 lastAudibleIndex = AudioManager.DEFAULT_STREAM_VOLUME[mStreamType];
2630 // Correct the data base
2631 sendMsg(mAudioHandler,
2632 MSG_PERSIST_VOLUME,
2633 SENDMSG_QUEUE,
2634 PERSIST_LAST_AUDIBLE,
2635 device,
2636 this,
2637 PERSIST_DELAY);
2638 }
2639 mLastAudibleIndex.put(device, getValidIndex(10 * lastAudibleIndex));
Eric Laurentbffc3d12012-05-07 17:43:49 -07002640 // the initial index should never be 0 for ring and notification streams on phones
2641 // (voice capable devices) if not in silent or vibrate mode.
2642 // same for system stream on phones and tablets
2643 if ((index == 0) && (mRingerMode == AudioManager.RINGER_MODE_NORMAL) &&
2644 ((mVoiceCapable &&
2645 (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_RING)) ||
2646 (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_SYSTEM))) {
Eric Laurent9ac8d0c2012-03-07 10:45:36 -08002647 index = lastAudibleIndex;
2648 // Correct the data base
2649 sendMsg(mAudioHandler,
2650 MSG_PERSIST_VOLUME,
2651 SENDMSG_QUEUE,
2652 PERSIST_CURRENT,
2653 device,
2654 this,
2655 PERSIST_DELAY);
2656 }
2657 mIndex.put(device, getValidIndex(10 * index));
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002658 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002659 }
2660
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002661 public void applyDeviceVolume(int device) {
2662 AudioSystem.setStreamVolumeIndex(mStreamType,
2663 (getIndex(device, false /* lastAudible */) + 5)/10,
2664 device);
2665 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002666
Eric Laurent3172d5e2012-05-09 11:38:16 -07002667 public synchronized void applyAllVolumes() {
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002668 // apply default volume first: by convention this will reset all
2669 // devices volumes in audio policy manager to the supplied value
2670 AudioSystem.setStreamVolumeIndex(mStreamType,
2671 (getIndex(AudioSystem.DEVICE_OUT_DEFAULT, false /* lastAudible */) + 5)/10,
2672 AudioSystem.DEVICE_OUT_DEFAULT);
2673 // then apply device specific volumes
2674 Set set = mIndex.entrySet();
2675 Iterator i = set.iterator();
2676 while (i.hasNext()) {
2677 Map.Entry entry = (Map.Entry)i.next();
2678 int device = ((Integer)entry.getKey()).intValue();
2679 if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
2680 AudioSystem.setStreamVolumeIndex(mStreamType,
2681 ((Integer)entry.getValue() + 5)/10,
2682 device);
2683 }
2684 }
2685 }
2686
2687 public boolean adjustIndex(int deltaIndex, int device) {
2688 return setIndex(getIndex(device,
Eric Laurent24482012012-05-10 09:41:17 -07002689 false /* lastAudible */) + deltaIndex,
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002690 device,
2691 true /* lastAudible */);
2692 }
2693
Eric Laurent3172d5e2012-05-09 11:38:16 -07002694 public synchronized boolean setIndex(int index, int device, boolean lastAudible) {
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002695 int oldIndex = getIndex(device, false /* lastAudible */);
2696 index = getValidIndex(index);
2697 mIndex.put(device, getValidIndex(index));
2698
2699 if (oldIndex != index) {
Eric Laurent9bcf4012009-06-12 06:09:28 -07002700 if (lastAudible) {
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002701 mLastAudibleIndex.put(device, index);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002702 }
Eric Laurenta553c252009-07-17 12:17:14 -07002703 // Apply change to all streams using this one as alias
Eric Laurentbffc3d12012-05-07 17:43:49 -07002704 // if changing volume of current device, also change volume of current
2705 // device on aliased stream
2706 boolean currentDevice = (device == getDeviceForStream(mStreamType));
Eric Laurenta553c252009-07-17 12:17:14 -07002707 int numStreamTypes = AudioSystem.getNumStreamTypes();
2708 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
Eric Laurentbffc3d12012-05-07 17:43:49 -07002709 if (streamType != mStreamType &&
2710 mStreamVolumeAlias[streamType] == mStreamType) {
2711 int scaledIndex = rescaleIndex(index, mStreamType, streamType);
2712 mStreamStates[streamType].setIndex(scaledIndex,
2713 device,
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002714 lastAudible);
Eric Laurentbffc3d12012-05-07 17:43:49 -07002715 if (currentDevice) {
2716 mStreamStates[streamType].setIndex(scaledIndex,
2717 getDeviceForStream(streamType),
2718 lastAudible);
2719 }
Eric Laurenta553c252009-07-17 12:17:14 -07002720 }
2721 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002722 return true;
2723 } else {
2724 return false;
2725 }
2726 }
2727
Eric Laurent3172d5e2012-05-09 11:38:16 -07002728 public synchronized int getIndex(int device, boolean lastAudible) {
2729 ConcurrentHashMap <Integer, Integer> indexes;
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002730 if (lastAudible) {
2731 indexes = mLastAudibleIndex;
2732 } else {
2733 indexes = mIndex;
2734 }
2735 Integer index = indexes.get(device);
2736 if (index == null) {
2737 // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
2738 index = indexes.get(AudioSystem.DEVICE_OUT_DEFAULT);
2739 }
2740 return index.intValue();
Eric Laurent5b4e6542010-03-19 20:02:21 -07002741 }
2742
Eric Laurent3172d5e2012-05-09 11:38:16 -07002743 public synchronized void setLastAudibleIndex(int index, int device) {
Eric Laurent24482012012-05-10 09:41:17 -07002744 // Apply change to all streams using this one as alias
2745 // if changing volume of current device, also change volume of current
2746 // device on aliased stream
2747 boolean currentDevice = (device == getDeviceForStream(mStreamType));
2748 int numStreamTypes = AudioSystem.getNumStreamTypes();
2749 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
2750 if (streamType != mStreamType &&
2751 mStreamVolumeAlias[streamType] == mStreamType) {
2752 int scaledIndex = rescaleIndex(index, mStreamType, streamType);
2753 mStreamStates[streamType].setLastAudibleIndex(scaledIndex, device);
2754 if (currentDevice) {
2755 mStreamStates[streamType].setLastAudibleIndex(scaledIndex,
2756 getDeviceForStream(streamType));
2757 }
2758 }
2759 }
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002760 mLastAudibleIndex.put(device, getValidIndex(index));
2761 }
2762
Eric Laurent3172d5e2012-05-09 11:38:16 -07002763 public synchronized void adjustLastAudibleIndex(int deltaIndex, int device) {
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002764 setLastAudibleIndex(getIndex(device,
Eric Laurent24482012012-05-10 09:41:17 -07002765 true /* lastAudible */) + deltaIndex,
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002766 device);
Eric Laurent5b4e6542010-03-19 20:02:21 -07002767 }
2768
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002769 public int getMaxIndex() {
Eric Laurenta553c252009-07-17 12:17:14 -07002770 return mIndexMax;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002771 }
2772
Eric Laurent3172d5e2012-05-09 11:38:16 -07002773 // only called by setAllIndexes() which is already synchronized
2774 public ConcurrentHashMap <Integer, Integer> getAllIndexes(boolean lastAudible) {
Eric Laurent6d517662012-04-23 18:42:39 -07002775 if (lastAudible) {
2776 return mLastAudibleIndex;
2777 } else {
2778 return mIndex;
2779 }
2780 }
2781
Eric Laurent3172d5e2012-05-09 11:38:16 -07002782 public synchronized void setAllIndexes(VolumeStreamState srcStream, boolean lastAudible) {
2783 ConcurrentHashMap <Integer, Integer> indexes = srcStream.getAllIndexes(lastAudible);
Eric Laurent6d517662012-04-23 18:42:39 -07002784 Set set = indexes.entrySet();
2785 Iterator i = set.iterator();
2786 while (i.hasNext()) {
2787 Map.Entry entry = (Map.Entry)i.next();
2788 int device = ((Integer)entry.getKey()).intValue();
2789 int index = ((Integer)entry.getValue()).intValue();
2790 index = rescaleIndex(index, srcStream.getStreamType(), mStreamType);
2791 setIndex(index, device, lastAudible);
2792 }
2793 }
2794
Eric Laurent3172d5e2012-05-09 11:38:16 -07002795 public synchronized void mute(IBinder cb, boolean state) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002796 VolumeDeathHandler handler = getDeathHandler(cb, state);
2797 if (handler == null) {
2798 Log.e(TAG, "Could not get client death handler for stream: "+mStreamType);
2799 return;
2800 }
2801 handler.mute(state);
2802 }
2803
Eric Laurent6d517662012-04-23 18:42:39 -07002804 public int getStreamType() {
2805 return mStreamType;
2806 }
2807
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002808 private int getValidIndex(int index) {
2809 if (index < 0) {
2810 return 0;
Eric Laurenta553c252009-07-17 12:17:14 -07002811 } else if (index > mIndexMax) {
2812 return mIndexMax;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002813 }
2814
2815 return index;
2816 }
2817
2818 private class VolumeDeathHandler implements IBinder.DeathRecipient {
2819 private IBinder mICallback; // To be notified of client's death
2820 private int mMuteCount; // Number of active mutes for this client
2821
2822 VolumeDeathHandler(IBinder cb) {
2823 mICallback = cb;
2824 }
2825
Eric Laurent3172d5e2012-05-09 11:38:16 -07002826 // must be called while synchronized on parent VolumeStreamState
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002827 public void mute(boolean state) {
Eric Laurent3172d5e2012-05-09 11:38:16 -07002828 if (state) {
2829 if (mMuteCount == 0) {
2830 // Register for client death notification
2831 try {
2832 // mICallback can be 0 if muted by AudioService
2833 if (mICallback != null) {
2834 mICallback.linkToDeath(this, 0);
2835 }
2836 mDeathHandlers.add(this);
2837 // If the stream is not yet muted by any client, set level to 0
2838 if (muteCount() == 0) {
2839 Set set = mIndex.entrySet();
2840 Iterator i = set.iterator();
2841 while (i.hasNext()) {
2842 Map.Entry entry = (Map.Entry)i.next();
2843 int device = ((Integer)entry.getKey()).intValue();
2844 setIndex(0, device, false /* lastAudible */);
Eric Laurent5b4e6542010-03-19 20:02:21 -07002845 }
Eric Laurent3172d5e2012-05-09 11:38:16 -07002846 sendMsg(mAudioHandler,
2847 MSG_SET_ALL_VOLUMES,
2848 SENDMSG_QUEUE,
2849 0,
2850 0,
2851 VolumeStreamState.this, 0);
2852 }
2853 } catch (RemoteException e) {
2854 // Client has died!
2855 binderDied();
2856 return;
2857 }
2858 } else {
2859 Log.w(TAG, "stream: "+mStreamType+" was already muted by this client");
2860 }
2861 mMuteCount++;
2862 } else {
2863 if (mMuteCount == 0) {
2864 Log.e(TAG, "unexpected unmute for stream: "+mStreamType);
2865 } else {
2866 mMuteCount--;
2867 if (mMuteCount == 0) {
2868 // Unregister from client death notification
2869 mDeathHandlers.remove(this);
2870 // mICallback can be 0 if muted by AudioService
2871 if (mICallback != null) {
2872 mICallback.unlinkToDeath(this, 0);
2873 }
2874 if (muteCount() == 0) {
2875 // If the stream is not muted any more, restore its volume if
2876 // ringer mode allows it
2877 if (!isStreamAffectedByRingerMode(mStreamType) ||
2878 mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002879 Set set = mIndex.entrySet();
2880 Iterator i = set.iterator();
2881 while (i.hasNext()) {
2882 Map.Entry entry = (Map.Entry)i.next();
2883 int device = ((Integer)entry.getKey()).intValue();
Eric Laurent3172d5e2012-05-09 11:38:16 -07002884 setIndex(getIndex(device,
2885 true /* lastAudible */),
2886 device,
2887 false /* lastAudible */);
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002888 }
2889 sendMsg(mAudioHandler,
2890 MSG_SET_ALL_VOLUMES,
Eric Laurent98ad9b92012-02-15 17:21:37 -08002891 SENDMSG_QUEUE,
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002892 0,
2893 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002894 VolumeStreamState.this, 0);
2895 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002896 }
2897 }
2898 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002899 }
2900 }
2901
2902 public void binderDied() {
2903 Log.w(TAG, "Volume service client died for stream: "+mStreamType);
2904 if (mMuteCount != 0) {
2905 // Reset all active mute requests from this client.
2906 mMuteCount = 1;
2907 mute(false);
2908 }
2909 }
2910 }
2911
Eric Laurent3172d5e2012-05-09 11:38:16 -07002912 private synchronized int muteCount() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002913 int count = 0;
2914 int size = mDeathHandlers.size();
2915 for (int i = 0; i < size; i++) {
2916 count += mDeathHandlers.get(i).mMuteCount;
2917 }
2918 return count;
2919 }
2920
Eric Laurent3172d5e2012-05-09 11:38:16 -07002921 // only called by mute() which is already synchronized
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002922 private VolumeDeathHandler getDeathHandler(IBinder cb, boolean state) {
Eric Laurent3172d5e2012-05-09 11:38:16 -07002923 VolumeDeathHandler handler;
2924 int size = mDeathHandlers.size();
2925 for (int i = 0; i < size; i++) {
2926 handler = mDeathHandlers.get(i);
2927 if (cb == handler.mICallback) {
2928 return handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002929 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002930 }
Eric Laurent3172d5e2012-05-09 11:38:16 -07002931 // If this is the first mute request for this client, create a new
2932 // client death handler. Otherwise, it is an out of sequence unmute request.
2933 if (state) {
2934 handler = new VolumeDeathHandler(cb);
2935 } else {
2936 Log.w(TAG, "stream was not muted by this client");
2937 handler = null;
2938 }
2939 return handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002940 }
Eric Laurentbffc3d12012-05-07 17:43:49 -07002941
2942 private void dump(PrintWriter pw) {
2943 pw.print(" Current: ");
2944 Set set = mIndex.entrySet();
2945 Iterator i = set.iterator();
2946 while (i.hasNext()) {
2947 Map.Entry entry = (Map.Entry)i.next();
2948 pw.print(Integer.toHexString(((Integer)entry.getKey()).intValue())
2949 + ": " + ((((Integer)entry.getValue()).intValue() + 5) / 10)+", ");
2950 }
2951 pw.print("\n Last audible: ");
2952 set = mLastAudibleIndex.entrySet();
2953 i = set.iterator();
2954 while (i.hasNext()) {
2955 Map.Entry entry = (Map.Entry)i.next();
2956 pw.print(Integer.toHexString(((Integer)entry.getKey()).intValue())
2957 + ": " + ((((Integer)entry.getValue()).intValue() + 5) / 10)+", ");
2958 }
2959 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002960 }
2961
2962 /** Thread that handles native AudioSystem control. */
2963 private class AudioSystemThread extends Thread {
2964 AudioSystemThread() {
2965 super("AudioService");
2966 }
2967
2968 @Override
2969 public void run() {
2970 // Set this thread up so the handler will work on it
2971 Looper.prepare();
2972
2973 synchronized(AudioService.this) {
2974 mAudioHandler = new AudioHandler();
2975
2976 // Notify that the handler has been created
2977 AudioService.this.notify();
2978 }
2979
2980 // Listen for volume change requests that are set by VolumePanel
2981 Looper.loop();
2982 }
2983 }
2984
2985 /** Handles internal volume messages in separate volume thread. */
2986 private class AudioHandler extends Handler {
2987
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002988 private void setDeviceVolume(VolumeStreamState streamState, int device) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002989
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002990 // Apply volume
2991 streamState.applyDeviceVolume(device);
Eric Laurenta553c252009-07-17 12:17:14 -07002992
2993 // Apply change to all streams using this one as alias
2994 int numStreamTypes = AudioSystem.getNumStreamTypes();
2995 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
2996 if (streamType != streamState.mStreamType &&
Eric Laurent6d517662012-04-23 18:42:39 -07002997 mStreamVolumeAlias[streamType] == streamState.mStreamType) {
2998 mStreamStates[streamType].applyDeviceVolume(getDeviceForStream(streamType));
Eric Laurenta553c252009-07-17 12:17:14 -07002999 }
3000 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003001
3002 // Post a persist volume msg
Eric Laurentafbb0472011-12-15 09:04:23 -08003003 sendMsg(mAudioHandler,
3004 MSG_PERSIST_VOLUME,
Eric Laurent98ad9b92012-02-15 17:21:37 -08003005 SENDMSG_QUEUE,
Eric Laurentafbb0472011-12-15 09:04:23 -08003006 PERSIST_CURRENT|PERSIST_LAST_AUDIBLE,
Eric Laurent9bc8358d2011-11-18 16:43:31 -08003007 device,
Eric Laurentafbb0472011-12-15 09:04:23 -08003008 streamState,
3009 PERSIST_DELAY);
3010
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003011 }
3012
Eric Laurent9bc8358d2011-11-18 16:43:31 -08003013 private void setAllVolumes(VolumeStreamState streamState) {
3014
3015 // Apply volume
3016 streamState.applyAllVolumes();
3017
3018 // Apply change to all streams using this one as alias
3019 int numStreamTypes = AudioSystem.getNumStreamTypes();
3020 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
3021 if (streamType != streamState.mStreamType &&
Eric Laurent6d517662012-04-23 18:42:39 -07003022 mStreamVolumeAlias[streamType] == streamState.mStreamType) {
Eric Laurent9bc8358d2011-11-18 16:43:31 -08003023 mStreamStates[streamType].applyAllVolumes();
3024 }
3025 }
3026 }
3027
Eric Laurentafbb0472011-12-15 09:04:23 -08003028 private void persistVolume(VolumeStreamState streamState,
Eric Laurent9bc8358d2011-11-18 16:43:31 -08003029 int persistType,
3030 int device) {
Eric Laurentafbb0472011-12-15 09:04:23 -08003031 if ((persistType & PERSIST_CURRENT) != 0) {
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07003032 System.putIntForUser(mContentResolver,
Eric Laurent9bc8358d2011-11-18 16:43:31 -08003033 streamState.getSettingNameForDevice(false /* lastAudible */, device),
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07003034 (streamState.getIndex(device, false /* lastAudible */) + 5)/ 10,
3035 UserHandle.USER_CURRENT);
Eric Laurent31951ca2010-03-02 18:54:45 -08003036 }
Eric Laurentafbb0472011-12-15 09:04:23 -08003037 if ((persistType & PERSIST_LAST_AUDIBLE) != 0) {
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07003038 System.putIntForUser(mContentResolver,
Eric Laurent9bc8358d2011-11-18 16:43:31 -08003039 streamState.getSettingNameForDevice(true /* lastAudible */, device),
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07003040 (streamState.getIndex(device, true /* lastAudible */) + 5) / 10,
3041 UserHandle.USER_CURRENT);
Eric Laurent31951ca2010-03-02 18:54:45 -08003042 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003043 }
3044
Glenn Kastenba195eb2011-12-13 09:30:40 -08003045 private void persistRingerMode(int ringerMode) {
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07003046 Settings.Global.putInt(mContentResolver, System.MODE_RINGER, ringerMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003047 }
3048
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003049 private void playSoundEffect(int effectType, int volume) {
3050 synchronized (mSoundEffectsLock) {
3051 if (mSoundPool == null) {
3052 return;
3053 }
Eric Laurenta2ef57d2009-09-28 04:46:10 -07003054 float volFloat;
Eric Laurent25101b02011-02-02 09:33:30 -08003055 // use default if volume is not specified by caller
Eric Laurenta2ef57d2009-09-28 04:46:10 -07003056 if (volume < 0) {
Jean-Michel Trivif2b0c112012-07-09 11:59:11 -07003057 volFloat = (float)Math.pow(10, (float)sSoundEffectVolumeDb/20);
Eric Laurenta2ef57d2009-09-28 04:46:10 -07003058 } else {
3059 volFloat = (float) volume / 1000.0f;
3060 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003061
3062 if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) {
Eric Laurenta2ef57d2009-09-28 04:46:10 -07003063 mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003064 } else {
3065 MediaPlayer mediaPlayer = new MediaPlayer();
Glenn Kasten62b9aec2011-11-07 11:10:16 -08003066 try {
3067 String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]];
3068 mediaPlayer.setDataSource(filePath);
3069 mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM);
3070 mediaPlayer.prepare();
3071 mediaPlayer.setVolume(volFloat, volFloat);
3072 mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
3073 public void onCompletion(MediaPlayer mp) {
3074 cleanupPlayer(mp);
3075 }
3076 });
3077 mediaPlayer.setOnErrorListener(new OnErrorListener() {
3078 public boolean onError(MediaPlayer mp, int what, int extra) {
3079 cleanupPlayer(mp);
3080 return true;
3081 }
3082 });
3083 mediaPlayer.start();
3084 } catch (IOException ex) {
3085 Log.w(TAG, "MediaPlayer IOException: "+ex);
3086 } catch (IllegalArgumentException ex) {
3087 Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex);
3088 } catch (IllegalStateException ex) {
3089 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003090 }
3091 }
3092 }
3093 }
3094
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003095 private void onHandlePersistMediaButtonReceiver(ComponentName receiver) {
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07003096 Settings.System.putStringForUser(mContentResolver,
3097 Settings.System.MEDIA_BUTTON_RECEIVER,
3098 receiver == null ? "" : receiver.flattenToString(),
3099 UserHandle.USER_CURRENT);
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07003100 }
3101
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003102 private void cleanupPlayer(MediaPlayer mp) {
3103 if (mp != null) {
3104 try {
3105 mp.stop();
3106 mp.release();
3107 } catch (IllegalStateException ex) {
3108 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
3109 }
3110 }
3111 }
3112
Eric Laurentfa640152011-03-12 15:59:51 -08003113 private void setForceUse(int usage, int config) {
3114 AudioSystem.setForceUse(usage, config);
3115 }
3116
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003117 @Override
3118 public void handleMessage(Message msg) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003119
Eric Laurentafbb0472011-12-15 09:04:23 -08003120 switch (msg.what) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003121
Eric Laurent9bc8358d2011-11-18 16:43:31 -08003122 case MSG_SET_DEVICE_VOLUME:
3123 setDeviceVolume((VolumeStreamState) msg.obj, msg.arg1);
3124 break;
3125
3126 case MSG_SET_ALL_VOLUMES:
3127 setAllVolumes((VolumeStreamState) msg.obj);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003128 break;
3129
3130 case MSG_PERSIST_VOLUME:
Eric Laurent9bc8358d2011-11-18 16:43:31 -08003131 persistVolume((VolumeStreamState) msg.obj, msg.arg1, msg.arg2);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003132 break;
3133
Mike Lockwood5c55a052011-12-15 17:21:44 -05003134 case MSG_PERSIST_MASTER_VOLUME:
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07003135 Settings.System.putFloatForUser(mContentResolver,
3136 Settings.System.VOLUME_MASTER,
3137 (float)msg.arg1 / (float)1000.0,
3138 UserHandle.USER_CURRENT);
Mike Lockwood5c55a052011-12-15 17:21:44 -05003139 break;
3140
Justin Koh57978ed2012-04-03 17:37:58 -07003141 case MSG_PERSIST_MASTER_VOLUME_MUTE:
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07003142 Settings.System.putIntForUser(mContentResolver,
3143 Settings.System.VOLUME_MASTER_MUTE,
3144 msg.arg1,
3145 UserHandle.USER_CURRENT);
Justin Koh57978ed2012-04-03 17:37:58 -07003146 break;
3147
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003148 case MSG_PERSIST_RINGER_MODE:
Glenn Kastenba195eb2011-12-13 09:30:40 -08003149 // note that the value persisted is the current ringer mode, not the
3150 // value of ringer mode as of the time the request was made to persist
3151 persistRingerMode(getRingerMode());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003152 break;
3153
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003154 case MSG_MEDIA_SERVER_DIED:
Eric Laurent89e74ba2009-09-30 18:26:36 -07003155 if (!mMediaServerOk) {
3156 Log.e(TAG, "Media server died.");
Eric Laurenta25c5d82011-02-10 11:04:34 -08003157 // Force creation of new IAudioFlinger interface so that we are notified
3158 // when new media_server process is back to life.
3159 AudioSystem.setErrorCallback(mAudioSystemCallback);
Eric Laurentafbb0472011-12-15 09:04:23 -08003160 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SENDMSG_NOOP, 0, 0,
Eric Laurent89e74ba2009-09-30 18:26:36 -07003161 null, 500);
3162 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003163 break;
3164
3165 case MSG_MEDIA_SERVER_STARTED:
3166 Log.e(TAG, "Media server started.");
Eric Laurent3c652ca2010-06-21 20:46:26 -07003167 // indicate to audio HAL that we start the reconfiguration phase after a media
3168 // server crash
3169 // Note that MSG_MEDIA_SERVER_STARTED message is only received when the media server
3170 // process restarts after a crash, not the first time it is started.
3171 AudioSystem.setParameters("restarting=true");
3172
Eric Laurentc42ac9d2009-07-29 08:53:03 -07003173 // Restore device connection states
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003174 synchronized (mConnectedDevices) {
3175 Set set = mConnectedDevices.entrySet();
3176 Iterator i = set.iterator();
Eric Laurent9bc8358d2011-11-18 16:43:31 -08003177 while (i.hasNext()) {
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003178 Map.Entry device = (Map.Entry)i.next();
3179 AudioSystem.setDeviceConnectionState(
3180 ((Integer)device.getKey()).intValue(),
3181 AudioSystem.DEVICE_STATE_AVAILABLE,
3182 (String)device.getValue());
3183 }
Eric Laurentc42ac9d2009-07-29 08:53:03 -07003184 }
Eric Laurentc42ac9d2009-07-29 08:53:03 -07003185 // Restore call state
3186 AudioSystem.setPhoneState(mMode);
3187
Eric Laurentd5603c12009-08-06 08:49:39 -07003188 // Restore forced usage for communcations and record
Eric Laurentc42ac9d2009-07-29 08:53:03 -07003189 AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm);
Eric Laurentd5603c12009-08-06 08:49:39 -07003190 AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm);
Eric Laurentc42ac9d2009-07-29 08:53:03 -07003191
Eric Laurenta553c252009-07-17 12:17:14 -07003192 // Restore stream volumes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003193 int numStreamTypes = AudioSystem.getNumStreamTypes();
3194 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003195 VolumeStreamState streamState = mStreamStates[streamType];
Eric Laurentc42ac9d2009-07-29 08:53:03 -07003196 AudioSystem.initStreamVolume(streamType, 0, (streamState.mIndexMax + 5) / 10);
Eric Laurent9bc8358d2011-11-18 16:43:31 -08003197
3198 streamState.applyAllVolumes();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003199 }
Eric Laurentc42ac9d2009-07-29 08:53:03 -07003200
3201 // Restore ringer mode
3202 setRingerModeInt(getRingerMode(), false);
Eric Laurent3c652ca2010-06-21 20:46:26 -07003203
Mike Lockwood90631542012-01-06 11:20:37 -05003204 // Restore master volume
3205 restoreMasterVolume();
3206
Jean-Michel Trivif26f0172012-04-25 16:23:20 -07003207 // Reset device orientation (if monitored for this device)
Eric Laurentd640bd32012-09-28 18:01:48 -07003208 if (mMonitorOrientation) {
Jean-Michel Trivif26f0172012-04-25 16:23:20 -07003209 setOrientationForAudioSystem();
3210 }
3211
Eric Laurent78472112012-05-21 08:57:21 -07003212 synchronized (mBluetoothA2dpEnabledLock) {
3213 AudioSystem.setForceUse(AudioSystem.FOR_MEDIA,
3214 mBluetoothA2dpEnabled ?
3215 AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP);
3216 }
Eric Laurent3c652ca2010-06-21 20:46:26 -07003217 // indicate the end of reconfiguration phase to audio HAL
3218 AudioSystem.setParameters("restarting=false");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003219 break;
3220
Eric Laurent117b7bb2011-01-16 17:07:27 -08003221 case MSG_LOAD_SOUND_EFFECTS:
3222 loadSoundEffects();
3223 break;
3224
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003225 case MSG_PLAY_SOUND_EFFECT:
3226 playSoundEffect(msg.arg1, msg.arg2);
3227 break;
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07003228
3229 case MSG_BTA2DP_DOCK_TIMEOUT:
3230 // msg.obj == address of BTA2DP device
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003231 synchronized (mConnectedDevices) {
3232 makeA2dpDeviceUnavailableNow( (String) msg.obj );
3233 }
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07003234 break;
Eric Laurentfa640152011-03-12 15:59:51 -08003235
3236 case MSG_SET_FORCE_USE:
Eric Laurentc390bed2012-07-03 12:24:05 -07003237 case MSG_SET_FORCE_BT_A2DP_USE:
Jean-Michel Trivi2ac2afe2012-08-21 11:16:55 -07003238 case MSG_SET_FORCE_RSX_USE:
Eric Laurentfa640152011-03-12 15:59:51 -08003239 setForceUse(msg.arg1, msg.arg2);
3240 break;
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07003241
3242 case MSG_PERSIST_MEDIABUTTONRECEIVER:
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003243 onHandlePersistMediaButtonReceiver( (ComponentName) msg.obj );
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07003244 break;
Eric Laurentdc03c612011-04-01 10:59:41 -07003245
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003246 case MSG_RCDISPLAY_CLEAR:
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003247 onRcDisplayClear();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003248 break;
3249
3250 case MSG_RCDISPLAY_UPDATE:
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003251 // msg.obj is guaranteed to be non null
3252 onRcDisplayUpdate( (RemoteControlStackEntry) msg.obj, msg.arg1);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003253 break;
3254
Eric Laurentdc03c612011-04-01 10:59:41 -07003255 case MSG_BT_HEADSET_CNCT_FAILED:
3256 resetBluetoothSco();
3257 break;
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003258
3259 case MSG_SET_WIRED_DEVICE_CONNECTION_STATE:
3260 onSetWiredDeviceConnectionState(msg.arg1, msg.arg2, (String)msg.obj);
Jean-Michel Trivi2d8dab52012-05-30 18:13:59 -07003261 mMediaEventWakeLock.release();
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003262 break;
3263
3264 case MSG_SET_A2DP_CONNECTION_STATE:
3265 onSetA2dpConnectionState((BluetoothDevice)msg.obj, msg.arg1);
Jean-Michel Trivi2d8dab52012-05-30 18:13:59 -07003266 mMediaEventWakeLock.release();
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003267 break;
Dianne Hackborn632ca412012-06-14 19:34:10 -07003268
3269 case MSG_REPORT_NEW_ROUTES: {
3270 int N = mRoutesObservers.beginBroadcast();
3271 if (N > 0) {
3272 AudioRoutesInfo routes;
3273 synchronized (mCurAudioRoutes) {
3274 routes = new AudioRoutesInfo(mCurAudioRoutes);
3275 }
3276 while (N > 0) {
3277 N--;
3278 IAudioRoutesObserver obs = mRoutesObservers.getBroadcastItem(N);
3279 try {
3280 obs.dispatchAudioRoutesChanged(routes);
3281 } catch (RemoteException e) {
3282 }
3283 }
3284 }
3285 mRoutesObservers.finishBroadcast();
3286 break;
3287 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07003288
3289 case MSG_REEVALUATE_REMOTE:
3290 onReevaluateRemote();
3291 break;
Jean-Michel Trivif98de1e2012-06-20 13:40:22 -07003292
3293 case MSG_RCC_NEW_PLAYBACK_INFO:
3294 onNewPlaybackInfoForRcc(msg.arg1 /* rccId */, msg.arg2 /* key */,
3295 ((Integer)msg.obj).intValue() /* value */);
3296 break;
3297 case MSG_RCC_NEW_VOLUME_OBS:
3298 onRegisterVolumeObserverForRcc(msg.arg1 /* rccId */,
3299 (IRemoteVolumeObserver)msg.obj /* rvo */);
3300 break;
Jean-Michel Trivi2ac2afe2012-08-21 11:16:55 -07003301
3302 case MSG_SET_RSX_CONNECTION_STATE:
3303 onSetRsxConnectionState(msg.arg1/*available*/, msg.arg2/*address*/);
3304 break;
Eric Laurentc34dcc12012-09-10 13:51:52 -07003305
3306 case MSG_CHECK_MUSIC_ACTIVE:
3307 onCheckMusicActive();
3308 break;
Eric Laurent5bfaeae2012-09-21 18:44:48 -07003309
3310 case MSG_BROADCAST_AUDIO_BECOMING_NOISY:
3311 onSendBecomingNoisyIntent();
3312 break;
Eric Laurentd640bd32012-09-28 18:01:48 -07003313
3314 case MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED:
3315 case MSG_CONFIGURE_SAFE_MEDIA_VOLUME:
3316 onConfigureSafeVolume((msg.what == MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED));
3317 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003318 }
3319 }
3320 }
3321
Jason Parekhb1096152009-03-24 17:48:25 -07003322 private class SettingsObserver extends ContentObserver {
Eric Laurenta553c252009-07-17 12:17:14 -07003323
Jason Parekhb1096152009-03-24 17:48:25 -07003324 SettingsObserver() {
3325 super(new Handler());
3326 mContentResolver.registerContentObserver(Settings.System.getUriFor(
3327 Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this);
3328 }
3329
3330 @Override
3331 public void onChange(boolean selfChange) {
3332 super.onChange(selfChange);
Glenn Kastenba195eb2011-12-13 09:30:40 -08003333 // FIXME This synchronized is not necessary if mSettingsLock only protects mRingerMode.
3334 // However there appear to be some missing locks around mRingerModeMutedStreams
3335 // and mRingerModeAffectedStreams, so will leave this synchronized for now.
3336 // mRingerModeMutedStreams and mMuteAffectedStreams are safe (only accessed once).
Eric Laurenta553c252009-07-17 12:17:14 -07003337 synchronized (mSettingsLock) {
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07003338 int ringerModeAffectedStreams = Settings.System.getIntForUser(mContentResolver,
Eric Laurentc1d41662011-07-19 11:21:13 -07003339 Settings.System.MODE_RINGER_STREAMS_AFFECTED,
3340 ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07003341 (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)),
3342 UserHandle.USER_CURRENT);
Eric Laurentc1d41662011-07-19 11:21:13 -07003343 if (mVoiceCapable) {
3344 ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
3345 } else {
Eric Laurent402f7f22011-02-04 12:30:32 -08003346 ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
3347 }
Eric Laurenta553c252009-07-17 12:17:14 -07003348 if (ringerModeAffectedStreams != mRingerModeAffectedStreams) {
3349 /*
3350 * Ensure all stream types that should be affected by ringer mode
3351 * are in the proper state.
3352 */
3353 mRingerModeAffectedStreams = ringerModeAffectedStreams;
3354 setRingerModeInt(getRingerMode(), false);
3355 }
Eric Laurenta553c252009-07-17 12:17:14 -07003356 }
Jason Parekhb1096152009-03-24 17:48:25 -07003357 }
Jason Parekhb1096152009-03-24 17:48:25 -07003358 }
Eric Laurenta553c252009-07-17 12:17:14 -07003359
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003360 // must be called synchronized on mConnectedDevices
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07003361 private void makeA2dpDeviceAvailable(String address) {
Eric Laurent78472112012-05-21 08:57:21 -07003362 // enable A2DP before notifying A2DP connection to avoid unecessary processing in
3363 // audio policy manager
3364 setBluetoothA2dpOnInt(true);
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07003365 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
3366 AudioSystem.DEVICE_STATE_AVAILABLE,
3367 address);
3368 // Reset A2DP suspend state each time a new sink is connected
3369 AudioSystem.setParameters("A2dpSuspended=false");
3370 mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP),
3371 address);
3372 }
3373
Eric Laurent5bfaeae2012-09-21 18:44:48 -07003374 private void onSendBecomingNoisyIntent() {
Dianne Hackborn5ac72a22012-08-29 18:32:08 -07003375 sendBroadcastToAll(new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
Mike Lockwood98418182012-05-10 17:13:20 -07003376 }
3377
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003378 // must be called synchronized on mConnectedDevices
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07003379 private void makeA2dpDeviceUnavailableNow(String address) {
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07003380 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
3381 AudioSystem.DEVICE_STATE_UNAVAILABLE,
3382 address);
3383 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
3384 }
3385
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003386 // must be called synchronized on mConnectedDevices
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07003387 private void makeA2dpDeviceUnavailableLater(String address) {
Eric Laurent3b591262010-04-20 07:01:00 -07003388 // prevent any activity on the A2DP audio output to avoid unwanted
3389 // reconnection of the sink.
3390 AudioSystem.setParameters("A2dpSuspended=true");
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07003391 // the device will be made unavailable later, so consider it disconnected right away
3392 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
3393 // send the delayed message to make the device unavailable later
3394 Message msg = mAudioHandler.obtainMessage(MSG_BTA2DP_DOCK_TIMEOUT, address);
3395 mAudioHandler.sendMessageDelayed(msg, BTA2DP_DOCK_TIMEOUT_MILLIS);
3396
3397 }
3398
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003399 // must be called synchronized on mConnectedDevices
Jean-Michel Trivia847ba42010-04-23 11:49:29 -07003400 private void cancelA2dpDeviceTimeout() {
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07003401 mAudioHandler.removeMessages(MSG_BTA2DP_DOCK_TIMEOUT);
3402 }
3403
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003404 // must be called synchronized on mConnectedDevices
Jean-Michel Trivia847ba42010-04-23 11:49:29 -07003405 private boolean hasScheduledA2dpDockTimeout() {
3406 return mAudioHandler.hasMessages(MSG_BTA2DP_DOCK_TIMEOUT);
3407 }
3408
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003409 private void onSetA2dpConnectionState(BluetoothDevice btDevice, int state)
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003410 {
3411 if (btDevice == null) {
3412 return;
3413 }
3414 String address = btDevice.getAddress();
3415 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
3416 address = "";
3417 }
3418 synchronized (mConnectedDevices) {
3419 boolean isConnected =
3420 (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
3421 mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP).equals(address));
3422
3423 if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
3424 if (btDevice.isBluetoothDock()) {
3425 if (state == BluetoothProfile.STATE_DISCONNECTED) {
3426 // introduction of a delay for transient disconnections of docks when
3427 // power is rapidly turned off/on, this message will be canceled if
3428 // we reconnect the dock under a preset delay
3429 makeA2dpDeviceUnavailableLater(address);
3430 // the next time isConnected is evaluated, it will be false for the dock
3431 }
3432 } else {
3433 makeA2dpDeviceUnavailableNow(address);
3434 }
Dianne Hackborn632ca412012-06-14 19:34:10 -07003435 synchronized (mCurAudioRoutes) {
3436 if (mCurAudioRoutes.mBluetoothName != null) {
3437 mCurAudioRoutes.mBluetoothName = null;
3438 sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
3439 SENDMSG_NOOP, 0, 0, null, 0);
3440 }
3441 }
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003442 } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
3443 if (btDevice.isBluetoothDock()) {
3444 // this could be a reconnection after a transient disconnection
3445 cancelA2dpDeviceTimeout();
3446 mDockAddress = address;
3447 } else {
3448 // this could be a connection of another A2DP device before the timeout of
3449 // a dock: cancel the dock timeout, and make the dock unavailable now
3450 if(hasScheduledA2dpDockTimeout()) {
3451 cancelA2dpDeviceTimeout();
3452 makeA2dpDeviceUnavailableNow(mDockAddress);
3453 }
3454 }
3455 makeA2dpDeviceAvailable(address);
Dianne Hackborn632ca412012-06-14 19:34:10 -07003456 synchronized (mCurAudioRoutes) {
3457 String name = btDevice.getAliasName();
3458 if (!TextUtils.equals(mCurAudioRoutes.mBluetoothName, name)) {
3459 mCurAudioRoutes.mBluetoothName = name;
3460 sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
3461 SENDMSG_NOOP, 0, 0, null, 0);
3462 }
3463 }
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003464 }
3465 }
3466 }
3467
Eric Laurent59f48272012-04-05 19:42:21 -07003468 private boolean handleDeviceConnection(boolean connected, int device, String params) {
3469 synchronized (mConnectedDevices) {
3470 boolean isConnected = (mConnectedDevices.containsKey(device) &&
Mike Lockwood98418182012-05-10 17:13:20 -07003471 (params.isEmpty() || mConnectedDevices.get(device).equals(params)));
Eric Laurent59f48272012-04-05 19:42:21 -07003472
3473 if (isConnected && !connected) {
3474 AudioSystem.setDeviceConnectionState(device,
3475 AudioSystem.DEVICE_STATE_UNAVAILABLE,
Mike Lockwood98418182012-05-10 17:13:20 -07003476 mConnectedDevices.get(device));
Eric Laurent59f48272012-04-05 19:42:21 -07003477 mConnectedDevices.remove(device);
3478 return true;
3479 } else if (!isConnected && connected) {
3480 AudioSystem.setDeviceConnectionState(device,
3481 AudioSystem.DEVICE_STATE_AVAILABLE,
3482 params);
3483 mConnectedDevices.put(new Integer(device), params);
3484 return true;
3485 }
3486 }
3487 return false;
3488 }
3489
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003490 // Devices which removal triggers intent ACTION_AUDIO_BECOMING_NOISY. The intent is only
3491 // sent if none of these devices is connected.
3492 int mBecomingNoisyIntentDevices =
3493 AudioSystem.DEVICE_OUT_WIRED_HEADSET | AudioSystem.DEVICE_OUT_WIRED_HEADPHONE |
Eric Laurent794da7a2012-08-30 11:30:16 -07003494 AudioSystem.DEVICE_OUT_ALL_A2DP | AudioSystem.DEVICE_OUT_AUX_DIGITAL |
3495 AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET | AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET |
3496 AudioSystem.DEVICE_OUT_ALL_USB;
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003497
3498 // must be called before removing the device from mConnectedDevices
3499 private int checkSendBecomingNoisyIntent(int device, int state) {
3500 int delay = 0;
3501 if ((state == 0) && ((device & mBecomingNoisyIntentDevices) != 0)) {
3502 int devices = 0;
3503 for (int dev : mConnectedDevices.keySet()) {
3504 if ((dev & mBecomingNoisyIntentDevices) != 0) {
3505 devices |= dev;
3506 }
3507 }
3508 if (devices == device) {
Eric Laurent5bfaeae2012-09-21 18:44:48 -07003509 sendMsg(mAudioHandler,
3510 MSG_BROADCAST_AUDIO_BECOMING_NOISY,
3511 SENDMSG_REPLACE,
3512 0,
3513 0,
3514 null,
3515 0);
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003516 delay = 1000;
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003517 }
3518 }
3519
3520 if (mAudioHandler.hasMessages(MSG_SET_A2DP_CONNECTION_STATE) ||
3521 mAudioHandler.hasMessages(MSG_SET_WIRED_DEVICE_CONNECTION_STATE)) {
3522 delay = 1000;
3523 }
3524 return delay;
3525 }
3526
3527 private void sendDeviceConnectionIntent(int device, int state, String name)
3528 {
3529 Intent intent = new Intent();
3530
3531 intent.putExtra("state", state);
3532 intent.putExtra("name", name);
3533 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
3534
Dianne Hackborn632ca412012-06-14 19:34:10 -07003535 int connType = 0;
3536
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003537 if (device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) {
Dianne Hackborn632ca412012-06-14 19:34:10 -07003538 connType = AudioRoutesInfo.MAIN_HEADSET;
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003539 intent.setAction(Intent.ACTION_HEADSET_PLUG);
3540 intent.putExtra("microphone", 1);
3541 } else if (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) {
Dianne Hackborn632ca412012-06-14 19:34:10 -07003542 connType = AudioRoutesInfo.MAIN_HEADPHONES;
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003543 intent.setAction(Intent.ACTION_HEADSET_PLUG);
3544 intent.putExtra("microphone", 0);
3545 } else if (device == AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET) {
Dianne Hackborn632ca412012-06-14 19:34:10 -07003546 connType = AudioRoutesInfo.MAIN_DOCK_SPEAKERS;
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003547 intent.setAction(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG);
3548 } else if (device == AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET) {
Dianne Hackborn632ca412012-06-14 19:34:10 -07003549 connType = AudioRoutesInfo.MAIN_DOCK_SPEAKERS;
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003550 intent.setAction(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG);
3551 } else if (device == AudioSystem.DEVICE_OUT_AUX_DIGITAL) {
Dianne Hackborn632ca412012-06-14 19:34:10 -07003552 connType = AudioRoutesInfo.MAIN_HDMI;
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003553 intent.setAction(Intent.ACTION_HDMI_AUDIO_PLUG);
3554 }
3555
Dianne Hackborn632ca412012-06-14 19:34:10 -07003556 synchronized (mCurAudioRoutes) {
3557 if (connType != 0) {
3558 int newConn = mCurAudioRoutes.mMainType;
3559 if (state != 0) {
3560 newConn |= connType;
3561 } else {
3562 newConn &= ~connType;
3563 }
3564 if (newConn != mCurAudioRoutes.mMainType) {
3565 mCurAudioRoutes.mMainType = newConn;
3566 sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
3567 SENDMSG_NOOP, 0, 0, null, 0);
3568 }
3569 }
3570 }
3571
Dianne Hackborn5ac72a22012-08-29 18:32:08 -07003572 final long ident = Binder.clearCallingIdentity();
3573 try {
3574 ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);
3575 } finally {
3576 Binder.restoreCallingIdentity(ident);
3577 }
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003578 }
3579
3580 private void onSetWiredDeviceConnectionState(int device, int state, String name)
3581 {
3582 synchronized (mConnectedDevices) {
3583 if ((state == 0) && ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
3584 (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE))) {
3585 setBluetoothA2dpOnInt(true);
3586 }
Mike Lockwooddb454842012-09-18 11:16:57 -07003587 boolean isUsb = ((device & AudioSystem.DEVICE_OUT_ALL_USB) != 0);
3588 handleDeviceConnection((state == 1), device, (isUsb ? name : ""));
Eric Laurentf1a457d2012-09-20 16:27:23 -07003589 if (state != 0) {
3590 if ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
3591 (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE)) {
3592 setBluetoothA2dpOnInt(false);
3593 }
3594 if ((device & mSafeMediaVolumeDevices) != 0) {
3595 sendMsg(mAudioHandler,
3596 MSG_CHECK_MUSIC_ACTIVE,
3597 SENDMSG_REPLACE,
3598 0,
3599 0,
3600 null,
3601 MUSIC_ACTIVE_POLL_PERIOD_MS);
3602 }
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003603 }
Mike Lockwooddb454842012-09-18 11:16:57 -07003604 if (!isUsb) {
3605 sendDeviceConnectionIntent(device, state, name);
3606 }
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003607 }
3608 }
3609
Jean-Michel Trivia847ba42010-04-23 11:49:29 -07003610 /* cache of the address of the last dock the device was connected to */
3611 private String mDockAddress;
3612
Eric Laurenta553c252009-07-17 12:17:14 -07003613 /**
3614 * Receiver for misc intent broadcasts the Phone app cares about.
3615 */
3616 private class AudioServiceBroadcastReceiver extends BroadcastReceiver {
3617 @Override
3618 public void onReceive(Context context, Intent intent) {
3619 String action = intent.getAction();
Eric Laurent59f48272012-04-05 19:42:21 -07003620 int device;
3621 int state;
Eric Laurenta553c252009-07-17 12:17:14 -07003622
Jean-Michel Trivi758559e2010-03-09 09:26:08 -08003623 if (action.equals(Intent.ACTION_DOCK_EVENT)) {
3624 int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
3625 Intent.EXTRA_DOCK_STATE_UNDOCKED);
3626 int config;
3627 switch (dockState) {
3628 case Intent.EXTRA_DOCK_STATE_DESK:
3629 config = AudioSystem.FORCE_BT_DESK_DOCK;
3630 break;
3631 case Intent.EXTRA_DOCK_STATE_CAR:
3632 config = AudioSystem.FORCE_BT_CAR_DOCK;
3633 break;
Praveen Bharathi21e941b2010-10-06 15:23:14 -05003634 case Intent.EXTRA_DOCK_STATE_LE_DESK:
3635 config = AudioSystem.FORCE_ANALOG_DOCK;
3636 break;
3637 case Intent.EXTRA_DOCK_STATE_HE_DESK:
3638 config = AudioSystem.FORCE_DIGITAL_DOCK;
3639 break;
Jean-Michel Trivi758559e2010-03-09 09:26:08 -08003640 case Intent.EXTRA_DOCK_STATE_UNDOCKED:
3641 default:
3642 config = AudioSystem.FORCE_NONE;
3643 }
3644 AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07003645 } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
Eric Laurent59f48272012-04-05 19:42:21 -07003646 state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07003647 BluetoothProfile.STATE_DISCONNECTED);
Eric Laurent59f48272012-04-05 19:42:21 -07003648 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
Nick Pellya56d1c72009-08-19 14:49:29 -07003649 String address = null;
Eric Laurentdca56b92011-09-02 14:20:56 -07003650
3651 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
3652 if (btDevice == null) {
3653 return;
3654 }
3655
3656 address = btDevice.getAddress();
3657 BluetoothClass btClass = btDevice.getBluetoothClass();
3658 if (btClass != null) {
3659 switch (btClass.getDeviceClass()) {
3660 case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
3661 case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
3662 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
3663 break;
3664 case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
3665 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
3666 break;
Eric Laurentd5603c12009-08-06 08:49:39 -07003667 }
3668 }
3669
Eric Laurentdca56b92011-09-02 14:20:56 -07003670 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
3671 address = "";
3672 }
Eric Laurentd5603c12009-08-06 08:49:39 -07003673
Eric Laurent59f48272012-04-05 19:42:21 -07003674 boolean connected = (state == BluetoothProfile.STATE_CONNECTED);
3675 if (handleDeviceConnection(connected, device, address)) {
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003676 synchronized (mScoClients) {
Eric Laurent59f48272012-04-05 19:42:21 -07003677 if (connected) {
3678 mBluetoothHeadsetDevice = btDevice;
3679 } else {
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003680 mBluetoothHeadsetDevice = null;
3681 resetBluetoothSco();
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003682 }
Eric Laurent62ef7672010-11-24 10:58:32 -08003683 }
Eric Laurenta553c252009-07-17 12:17:14 -07003684 }
Eric Laurent59f48272012-04-05 19:42:21 -07003685 } else if (action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ||
3686 action.equals(Intent.ACTION_USB_AUDIO_DEVICE_PLUG)) {
3687 state = intent.getIntExtra("state", 0);
3688 int alsaCard = intent.getIntExtra("card", -1);
3689 int alsaDevice = intent.getIntExtra("device", -1);
Mike Lockwood98418182012-05-10 17:13:20 -07003690 String params = (alsaCard == -1 && alsaDevice == -1 ? ""
3691 : "card=" + alsaCard + ";device=" + alsaDevice);
Eric Laurent59f48272012-04-05 19:42:21 -07003692 device = action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ?
3693 AudioSystem.DEVICE_OUT_USB_ACCESSORY : AudioSystem.DEVICE_OUT_USB_DEVICE;
3694 Log.v(TAG, "Broadcast Receiver: Got "
3695 + (action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ?
3696 "ACTION_USB_AUDIO_ACCESSORY_PLUG" : "ACTION_USB_AUDIO_DEVICE_PLUG")
3697 + ", state = " + state + ", card: " + alsaCard + ", device: " + alsaDevice);
Mike Lockwooddb454842012-09-18 11:16:57 -07003698 setWiredDeviceConnectionState(device, state, params);
Eric Laurent3def1ee2010-03-17 23:26:26 -07003699 } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
Eric Laurent62ef7672010-11-24 10:58:32 -08003700 boolean broadcast = false;
Eric Laurent59f48272012-04-05 19:42:21 -07003701 int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
Eric Laurent3def1ee2010-03-17 23:26:26 -07003702 synchronized (mScoClients) {
Eric Laurent62ef7672010-11-24 10:58:32 -08003703 int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
Eric Laurentdc03c612011-04-01 10:59:41 -07003704 // broadcast intent if the connection was initated by AudioService
3705 if (!mScoClients.isEmpty() &&
3706 (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
3707 mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
3708 mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
Eric Laurent62ef7672010-11-24 10:58:32 -08003709 broadcast = true;
3710 }
3711 switch (btState) {
3712 case BluetoothHeadset.STATE_AUDIO_CONNECTED:
Eric Laurent59f48272012-04-05 19:42:21 -07003713 scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
Eric Laurentdc03c612011-04-01 10:59:41 -07003714 if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
3715 mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
3716 mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
Eric Laurent62ef7672010-11-24 10:58:32 -08003717 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
Eric Laurent3def1ee2010-03-17 23:26:26 -07003718 }
Eric Laurent62ef7672010-11-24 10:58:32 -08003719 break;
3720 case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
Eric Laurent59f48272012-04-05 19:42:21 -07003721 scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
Eric Laurent62ef7672010-11-24 10:58:32 -08003722 mScoAudioState = SCO_STATE_INACTIVE;
Eric Laurentd7454be2011-09-14 08:45:58 -07003723 clearAllScoClients(0, false);
Eric Laurent62ef7672010-11-24 10:58:32 -08003724 break;
3725 case BluetoothHeadset.STATE_AUDIO_CONNECTING:
Eric Laurentdc03c612011-04-01 10:59:41 -07003726 if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
3727 mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
3728 mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
Eric Laurent62ef7672010-11-24 10:58:32 -08003729 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
Eric Laurent3def1ee2010-03-17 23:26:26 -07003730 }
Eric Laurent62ef7672010-11-24 10:58:32 -08003731 default:
3732 // do not broadcast CONNECTING or invalid state
3733 broadcast = false;
3734 break;
Eric Laurent3def1ee2010-03-17 23:26:26 -07003735 }
3736 }
Eric Laurent62ef7672010-11-24 10:58:32 -08003737 if (broadcast) {
Eric Laurent59f48272012-04-05 19:42:21 -07003738 broadcastScoConnectionState(scoAudioState);
Eric Laurentdc03c612011-04-01 10:59:41 -07003739 //FIXME: this is to maintain compatibility with deprecated intent
3740 // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
Eric Laurent62ef7672010-11-24 10:58:32 -08003741 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
Eric Laurent59f48272012-04-05 19:42:21 -07003742 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, scoAudioState);
Dianne Hackborn5ac72a22012-08-29 18:32:08 -07003743 sendStickyBroadcastToAll(newIntent);
Eric Laurent62ef7672010-11-24 10:58:32 -08003744 }
3745 } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
Eric Laurenta60e2122010-12-28 16:49:07 -08003746 mBootCompleted = true;
Eric Laurentafbb0472011-12-15 09:04:23 -08003747 sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_NOOP,
Eric Laurent117b7bb2011-01-16 17:07:27 -08003748 0, 0, null, 0);
Eric Laurentdc03c612011-04-01 10:59:41 -07003749
Amith Yamasani6243edd2011-12-05 19:58:48 -08003750 mKeyguardManager =
3751 (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
Eric Laurentdc03c612011-04-01 10:59:41 -07003752 mScoConnectionState = AudioManager.SCO_AUDIO_STATE_ERROR;
3753 resetBluetoothSco();
3754 getBluetoothHeadset();
3755 //FIXME: this is to maintain compatibility with deprecated intent
3756 // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
Eric Laurent62ef7672010-11-24 10:58:32 -08003757 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
3758 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
3759 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
Dianne Hackborn5ac72a22012-08-29 18:32:08 -07003760 sendStickyBroadcastToAll(newIntent);
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003761
3762 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
3763 if (adapter != null) {
3764 adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
3765 BluetoothProfile.A2DP);
3766 }
Eric Laurentd640bd32012-09-28 18:01:48 -07003767
3768 sendMsg(mAudioHandler,
3769 MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED,
3770 SENDMSG_REPLACE,
3771 0,
3772 0,
3773 null,
3774 SAFE_VOLUME_CONFIGURE_TIMEOUT_MS);
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07003775 } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) {
3776 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
3777 // a package is being removed, not replaced
3778 String packageName = intent.getData().getSchemeSpecificPart();
3779 if (packageName != null) {
3780 removeMediaButtonReceiverForPackage(packageName);
3781 }
3782 }
Eric Laurent950e8cb2011-10-13 08:57:54 -07003783 } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
3784 AudioSystem.setParameters("screen_state=on");
3785 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
3786 AudioSystem.setParameters("screen_state=off");
Jean-Michel Trivif26f0172012-04-25 16:23:20 -07003787 } else if (action.equalsIgnoreCase(Intent.ACTION_CONFIGURATION_CHANGED)) {
3788 handleConfigurationChanged(context);
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07003789 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
Eric Laurent5bfaeae2012-09-21 18:44:48 -07003790 // attempt to stop music playabck for background user
3791 sendMsg(mAudioHandler,
3792 MSG_BROADCAST_AUDIO_BECOMING_NOISY,
3793 SENDMSG_REPLACE,
3794 0,
3795 0,
3796 null,
3797 0);
3798 // load volume settings for new user
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07003799 readAudioSettings(true /*userSwitch*/);
3800 // preserve STREAM_MUSIC volume from one user to the next.
3801 sendMsg(mAudioHandler,
3802 MSG_SET_ALL_VOLUMES,
3803 SENDMSG_QUEUE,
3804 0,
3805 0,
3806 mStreamStates[AudioSystem.STREAM_MUSIC], 0);
Eric Laurenta553c252009-07-17 12:17:14 -07003807 }
3808 }
3809 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003810
3811 //==========================================================================================
3812 // AudioFocus
3813 //==========================================================================================
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07003814
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -07003815 /* constant to identify focus stack entry that is used to hold the focus while the phone
Jean-Michel Trivi4dd36742012-01-24 09:52:39 -08003816 * is ringing or during a call. Used by com.android.internal.telephony.CallManager when
3817 * entering and exiting calls.
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -07003818 */
Jean-Michel Trivi4dd36742012-01-24 09:52:39 -08003819 public final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls";
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -07003820
Jean-Michel Trivi392a2bb2010-05-10 20:02:46 -07003821 private final static Object mAudioFocusLock = new Object();
3822
Jean-Michel Trivie73131a2010-06-14 09:53:30 -07003823 private final static Object mRingingLock = new Object();
3824
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -07003825 private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
3826 @Override
3827 public void onCallStateChanged(int state, String incomingNumber) {
3828 if (state == TelephonyManager.CALL_STATE_RINGING) {
3829 //Log.v(TAG, " CALL_STATE_RINGING");
Jean-Michel Trivie73131a2010-06-14 09:53:30 -07003830 synchronized(mRingingLock) {
3831 mIsRinging = true;
3832 }
Jean-Michel Trivi2ade5762010-12-11 13:18:30 -08003833 } else if ((state == TelephonyManager.CALL_STATE_OFFHOOK)
3834 || (state == TelephonyManager.CALL_STATE_IDLE)) {
Jean-Michel Trivie73131a2010-06-14 09:53:30 -07003835 synchronized(mRingingLock) {
3836 mIsRinging = false;
3837 }
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07003838 }
3839 }
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -07003840 };
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07003841
3842 private void notifyTopOfAudioFocusStack() {
3843 // notify the top of the stack it gained focus
3844 if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
3845 if (canReassignAudioFocus()) {
3846 try {
3847 mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
3848 AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId);
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07003849 } catch (RemoteException e) {
3850 Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e);
3851 e.printStackTrace();
3852 }
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07003853 }
3854 }
3855 }
3856
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003857 private static class FocusStackEntry {
3858 public int mStreamType = -1;// no stream type
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003859 public IAudioFocusDispatcher mFocusDispatcher = null;
3860 public IBinder mSourceRef = null;
3861 public String mClientId;
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07003862 public int mFocusChangeType;
Jean-Michel Trivi061214b2011-08-14 11:37:24 -07003863 public AudioFocusDeathHandler mHandler;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003864 public String mPackageName;
3865 public int mCallingUid;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003866
3867 public FocusStackEntry() {
3868 }
3869
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003870 public FocusStackEntry(int streamType, int duration,
Jean-Michel Trivi061214b2011-08-14 11:37:24 -07003871 IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr,
3872 String pn, int uid) {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003873 mStreamType = streamType;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003874 mFocusDispatcher = afl;
3875 mSourceRef = source;
3876 mClientId = id;
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07003877 mFocusChangeType = duration;
Jean-Michel Trivi061214b2011-08-14 11:37:24 -07003878 mHandler = hdlr;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003879 mPackageName = pn;
3880 mCallingUid = uid;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003881 }
Jean-Michel Trivi061214b2011-08-14 11:37:24 -07003882
Jean-Michel Trivie7edc862011-11-15 16:27:05 -08003883 public void unlinkToDeath() {
3884 try {
3885 if (mSourceRef != null && mHandler != null) {
3886 mSourceRef.unlinkToDeath(mHandler, 0);
3887 mHandler = null;
3888 }
3889 } catch (java.util.NoSuchElementException e) {
3890 Log.e(TAG, "Encountered " + e + " in FocusStackEntry.unlinkToDeath()");
Jean-Michel Trivi061214b2011-08-14 11:37:24 -07003891 }
3892 }
Jean-Michel Trivide1f0652011-11-11 16:13:48 -08003893
3894 @Override
3895 protected void finalize() throws Throwable {
Jean-Michel Trivie7edc862011-11-15 16:27:05 -08003896 unlinkToDeath(); // unlink exception handled inside method
3897 super.finalize();
Jean-Michel Trivide1f0652011-11-11 16:13:48 -08003898 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003899 }
3900
Glenn Kasten30c918c2011-11-10 17:56:41 -08003901 private final Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>();
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003902
3903 /**
3904 * Helper function:
3905 * Display in the log the current entries in the audio focus stack
3906 */
3907 private void dumpFocusStack(PrintWriter pw) {
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -07003908 pw.println("\nAudio Focus stack entries:");
Jean-Michel Trivi392a2bb2010-05-10 20:02:46 -07003909 synchronized(mAudioFocusLock) {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003910 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
3911 while(stackIterator.hasNext()) {
3912 FocusStackEntry fse = stackIterator.next();
Jean-Michel Trivi1f9196a2012-07-26 15:11:07 -07003913 pw.println(" source:" + fse.mSourceRef + " -- client: " + fse.mClientId
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003914 + " -- duration: " + fse.mFocusChangeType
Jean-Michel Trivi1f9196a2012-07-26 15:11:07 -07003915 + " -- uid: " + fse.mCallingUid
3916 + " -- stream: " + fse.mStreamType);
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003917 }
3918 }
3919 }
3920
3921 /**
3922 * Helper function:
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003923 * Called synchronized on mAudioFocusLock
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003924 * Remove a focus listener from the focus stack.
3925 * @param focusListenerToRemove the focus listener
3926 * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
3927 * focus, notify the next item in the stack it gained focus.
3928 */
3929 private void removeFocusStackEntry(String clientToRemove, boolean signal) {
Jean-Michel Trivie7edc862011-11-15 16:27:05 -08003930 // is the current top of the focus stack abandoning focus? (because of request, not death)
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003931 if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove))
3932 {
3933 //Log.i(TAG, " removeFocusStackEntry() removing top of stack");
Jean-Michel Trivie7edc862011-11-15 16:27:05 -08003934 FocusStackEntry fse = mFocusStack.pop();
3935 fse.unlinkToDeath();
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003936 if (signal) {
3937 // notify the new top of the stack it gained focus
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07003938 notifyTopOfAudioFocusStack();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003939 // there's a new top of the stack, let the remote control know
3940 synchronized(mRCStack) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003941 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003942 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003943 }
3944 } else {
3945 // focus is abandoned by a client that's not at the top of the stack,
3946 // no need to update focus.
3947 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
3948 while(stackIterator.hasNext()) {
3949 FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
3950 if(fse.mClientId.equals(clientToRemove)) {
3951 Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for "
3952 + fse.mClientId);
Jean-Michel Trivi72843372011-04-18 10:18:37 -07003953 stackIterator.remove();
Jean-Michel Trivie7edc862011-11-15 16:27:05 -08003954 fse.unlinkToDeath();
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003955 }
3956 }
3957 }
3958 }
3959
3960 /**
3961 * Helper function:
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003962 * Called synchronized on mAudioFocusLock
Jean-Michel Trivie7edc862011-11-15 16:27:05 -08003963 * Remove focus listeners from the focus stack for a particular client when it has died.
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003964 */
3965 private void removeFocusStackEntryForClient(IBinder cb) {
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07003966 // is the owner of the audio focus part of the client to remove?
3967 boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
3968 mFocusStack.peek().mSourceRef.equals(cb);
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003969 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
3970 while(stackIterator.hasNext()) {
3971 FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
3972 if(fse.mSourceRef.equals(cb)) {
3973 Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for "
3974 + fse.mClientId);
Jean-Michel Trivi72843372011-04-18 10:18:37 -07003975 stackIterator.remove();
Jean-Michel Trivie7edc862011-11-15 16:27:05 -08003976 // the client just died, no need to unlink to its death
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003977 }
3978 }
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07003979 if (isTopOfStackForClientToRemove) {
3980 // we removed an entry at the top of the stack:
3981 // notify the new top of the stack it gained focus.
3982 notifyTopOfAudioFocusStack();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003983 // there's a new top of the stack, let the remote control know
3984 synchronized(mRCStack) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07003985 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003986 }
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07003987 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003988 }
3989
3990 /**
3991 * Helper function:
3992 * Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
3993 */
Jean-Michel Trivid327f212010-03-16 21:44:33 -07003994 private boolean canReassignAudioFocus() {
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -07003995 // focus requests are rejected during a phone call or when the phone is ringing
3996 // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus
3997 if (!mFocusStack.isEmpty() && IN_VOICE_COMM_FOCUS_ID.equals(mFocusStack.peek().mClientId)) {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08003998 return false;
3999 }
4000 return true;
4001 }
4002
4003 /**
4004 * Inner class to monitor audio focus client deaths, and remove them from the audio focus
4005 * stack if necessary.
4006 */
4007 private class AudioFocusDeathHandler implements IBinder.DeathRecipient {
4008 private IBinder mCb; // To be notified of client's death
4009
4010 AudioFocusDeathHandler(IBinder cb) {
4011 mCb = cb;
4012 }
4013
4014 public void binderDied() {
Jean-Michel Trivi392a2bb2010-05-10 20:02:46 -07004015 synchronized(mAudioFocusLock) {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004016 Log.w(TAG, " AudioFocus audio focus client died");
4017 removeFocusStackEntryForClient(mCb);
4018 }
4019 }
4020
4021 public IBinder getBinder() {
4022 return mCb;
4023 }
4024 }
4025
4026
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004027 /** @see AudioManager#requestAudioFocus(IAudioFocusDispatcher, int, int) */
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07004028 public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb,
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004029 IAudioFocusDispatcher fd, String clientId, String callingPackageName) {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004030 Log.i(TAG, " AudioFocus requestAudioFocus() from " + clientId);
4031 // the main stream type for the audio focus request is currently not used. It may
4032 // potentially be used to handle multiple stream type-dependent audio focuses.
4033
Jean-Michel Trivi382f4e6e2011-03-08 14:38:59 -08004034 // we need a valid binder callback for clients
4035 if (!cb.pingBinder()) {
4036 Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004037 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
4038 }
4039
Jean-Michel Trivi392a2bb2010-05-10 20:02:46 -07004040 synchronized(mAudioFocusLock) {
4041 if (!canReassignAudioFocus()) {
4042 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
4043 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004044
Jean-Michel Trivibaf674e2011-08-14 15:16:21 -07004045 // handle the potential premature death of the new holder of the focus
4046 // (premature death == death before abandoning focus)
4047 // Register for client death notification
4048 AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
4049 try {
4050 cb.linkToDeath(afdh, 0);
4051 } catch (RemoteException e) {
4052 // client has already died!
4053 Log.w(TAG, "AudioFocus requestAudioFocus() could not link to "+cb+" binder death");
4054 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
4055 }
4056
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004057 if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) {
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07004058 // if focus is already owned by this client and the reason for acquiring the focus
4059 // hasn't changed, don't do anything
4060 if (mFocusStack.peek().mFocusChangeType == focusChangeHint) {
Jean-Michel Trivide1f0652011-11-11 16:13:48 -08004061 // unlink death handler so it can be gc'ed.
4062 // linkToDeath() creates a JNI global reference preventing collection.
4063 cb.unlinkToDeath(afdh, 0);
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07004064 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
4065 }
4066 // the reason for the audio focus request has changed: remove the current top of
4067 // stack and respond as if we had a new focus owner
Jean-Michel Trivie7edc862011-11-15 16:27:05 -08004068 FocusStackEntry fse = mFocusStack.pop();
4069 fse.unlinkToDeath();
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004070 }
4071
4072 // notify current top of stack it is losing focus
4073 if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
4074 try {
4075 mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07004076 -1 * focusChangeHint, // loss and gain codes are inverse of each other
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004077 mFocusStack.peek().mClientId);
4078 } catch (RemoteException e) {
4079 Log.e(TAG, " Failure to signal loss of focus due to "+ e);
4080 e.printStackTrace();
4081 }
4082 }
4083
Jean-Michel Trivi55d1bb32010-04-01 17:40:58 -07004084 // focus requester might already be somewhere below in the stack, remove it
Jean-Michel Trivibaf674e2011-08-14 15:16:21 -07004085 removeFocusStackEntry(clientId, false /* signal */);
Jean-Michel Trivi061214b2011-08-14 11:37:24 -07004086
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004087 // push focus requester at the top of the audio focus stack
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004088 mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, fd, cb,
Jean-Michel Trivi061214b2011-08-14 11:37:24 -07004089 clientId, afdh, callingPackageName, Binder.getCallingUid()));
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004090
4091 // there's a new top of the stack, let the remote control know
4092 synchronized(mRCStack) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07004093 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004094 }
Jean-Michel Trivi392a2bb2010-05-10 20:02:46 -07004095 }//synchronized(mAudioFocusLock)
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004096
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004097 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
4098 }
4099
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004100 /** @see AudioManager#abandonAudioFocus(IAudioFocusDispatcher) */
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004101 public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) {
4102 Log.i(TAG, " AudioFocus abandonAudioFocus() from " + clientId);
Jean-Michel Trivi392a2bb2010-05-10 20:02:46 -07004103 try {
4104 // this will take care of notifying the new focus owner if needed
4105 synchronized(mAudioFocusLock) {
4106 removeFocusStackEntry(clientId, true);
4107 }
4108 } catch (java.util.ConcurrentModificationException cme) {
4109 // Catching this exception here is temporary. It is here just to prevent
4110 // a crash seen when the "Silent" notification is played. This is believed to be fixed
4111 // but this try catch block is left just to be safe.
4112 Log.e(TAG, "FATAL EXCEPTION AudioFocus abandonAudioFocus() caused " + cme);
4113 cme.printStackTrace();
Jean-Michel Trivi2930bb22010-04-09 19:27:58 -07004114 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004115
4116 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
4117 }
4118
4119
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004120 public void unregisterAudioFocusClient(String clientId) {
Jean-Michel Trivi392a2bb2010-05-10 20:02:46 -07004121 synchronized(mAudioFocusLock) {
Jean-Michel Trivi2930bb22010-04-09 19:27:58 -07004122 removeFocusStackEntry(clientId, false);
4123 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004124 }
4125
4126
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004127 //==========================================================================================
4128 // RemoteControl
4129 //==========================================================================================
Jean-Michel Trivic6802222012-04-30 11:15:03 -07004130 public void dispatchMediaKeyEvent(KeyEvent keyEvent) {
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004131 filterMediaKeyEvent(keyEvent, false /*needWakeLock*/);
Jean-Michel Trivic6802222012-04-30 11:15:03 -07004132 }
4133
4134 public void dispatchMediaKeyEventUnderWakelock(KeyEvent keyEvent) {
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004135 filterMediaKeyEvent(keyEvent, true /*needWakeLock*/);
Jean-Michel Trivic6802222012-04-30 11:15:03 -07004136 }
4137
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004138 private void filterMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
Jean-Michel Trivic6802222012-04-30 11:15:03 -07004139 // sanity check on the incoming key event
4140 if (!isValidMediaKeyEvent(keyEvent)) {
4141 Log.e(TAG, "not dispatching invalid media key event " + keyEvent);
4142 return;
4143 }
Jean-Michel Trivi722b8082012-05-15 15:18:33 -07004144 // event filtering for telephony
Jean-Michel Trivic6802222012-04-30 11:15:03 -07004145 synchronized(mRingingLock) {
Jean-Michel Trivi722b8082012-05-15 15:18:33 -07004146 synchronized(mRCStack) {
4147 if ((mMediaReceiverForCalls != null) &&
4148 (mIsRinging || (getMode() == AudioSystem.MODE_IN_CALL))) {
4149 dispatchMediaKeyEventForCalls(keyEvent, needWakeLock);
4150 return;
4151 }
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004152 }
Jean-Michel Trivic6802222012-04-30 11:15:03 -07004153 }
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004154 // event filtering based on voice-based interactions
4155 if (isValidVoiceInputKeyCode(keyEvent.getKeyCode())) {
4156 filterVoiceInputKeyEvent(keyEvent, needWakeLock);
4157 } else {
4158 dispatchMediaKeyEvent(keyEvent, needWakeLock);
4159 }
4160 }
4161
4162 /**
Jean-Michel Trivi722b8082012-05-15 15:18:33 -07004163 * Handles the dispatching of the media button events to the telephony package.
4164 * Precondition: mMediaReceiverForCalls != null
4165 * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons
4166 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
4167 * is dispatched.
4168 */
4169 private void dispatchMediaKeyEventForCalls(KeyEvent keyEvent, boolean needWakeLock) {
4170 Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
4171 keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
4172 keyIntent.setPackage(mMediaReceiverForCalls.getPackageName());
4173 if (needWakeLock) {
4174 mMediaEventWakeLock.acquire();
4175 keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
4176 }
Dianne Hackborn5ac72a22012-08-29 18:32:08 -07004177 final long ident = Binder.clearCallingIdentity();
4178 try {
4179 mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
4180 null, mKeyEventDone, mAudioHandler, Activity.RESULT_OK, null, null);
4181 } finally {
4182 Binder.restoreCallingIdentity(ident);
4183 }
Jean-Michel Trivi722b8082012-05-15 15:18:33 -07004184 }
4185
4186 /**
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004187 * Handles the dispatching of the media button events to one of the registered listeners,
4188 * or if there was none, broadcast an ACTION_MEDIA_BUTTON intent to the rest of the system.
4189 * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons
4190 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
4191 * is dispatched.
4192 */
4193 private void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
Jean-Michel Trivic6802222012-04-30 11:15:03 -07004194 if (needWakeLock) {
4195 mMediaEventWakeLock.acquire();
4196 }
4197 Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
4198 keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
4199 synchronized(mRCStack) {
4200 if (!mRCStack.empty()) {
4201 // send the intent that was registered by the client
4202 try {
4203 mRCStack.peek().mMediaIntent.send(mContext,
4204 needWakeLock ? WAKELOCK_RELEASE_ON_FINISHED : 0 /*code*/,
4205 keyIntent, AudioService.this, mAudioHandler);
4206 } catch (CanceledException e) {
4207 Log.e(TAG, "Error sending pending intent " + mRCStack.peek());
4208 e.printStackTrace();
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004209 }
Jean-Michel Trivic6802222012-04-30 11:15:03 -07004210 } else {
4211 // legacy behavior when nobody registered their media button event receiver
4212 // through AudioManager
4213 if (needWakeLock) {
4214 keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004215 }
Dianne Hackborn5ac72a22012-08-29 18:32:08 -07004216 final long ident = Binder.clearCallingIdentity();
4217 try {
4218 mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
4219 null, mKeyEventDone,
4220 mAudioHandler, Activity.RESULT_OK, null, null);
4221 } finally {
4222 Binder.restoreCallingIdentity(ident);
4223 }
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004224 }
4225 }
4226 }
4227
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004228 /**
Jeff Brownab624c22012-05-17 11:08:31 -07004229 * The different actions performed in response to a voice button key event.
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004230 */
4231 private final static int VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS = 1;
4232 private final static int VOICEBUTTON_ACTION_START_VOICE_INPUT = 2;
4233 private final static int VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS = 3;
4234
4235 private final Object mVoiceEventLock = new Object();
Jeff Brownab624c22012-05-17 11:08:31 -07004236 private boolean mVoiceButtonDown;
4237 private boolean mVoiceButtonHandled;
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004238
4239 /**
4240 * Filter key events that may be used for voice-based interactions
4241 * @param keyEvent a non-null KeyEvent whose key code is that of one of the supported
4242 * media buttons that can be used to trigger voice-based interactions.
4243 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
4244 * is dispatched.
4245 */
4246 private void filterVoiceInputKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
Jeff Brownab624c22012-05-17 11:08:31 -07004247 if (DEBUG_RC) {
4248 Log.v(TAG, "voice input key event: " + keyEvent + ", needWakeLock=" + needWakeLock);
4249 }
4250
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004251 int voiceButtonAction = VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS;
4252 int keyAction = keyEvent.getAction();
4253 synchronized (mVoiceEventLock) {
Jeff Brownab624c22012-05-17 11:08:31 -07004254 if (keyAction == KeyEvent.ACTION_DOWN) {
4255 if (keyEvent.getRepeatCount() == 0) {
4256 // initial down
4257 mVoiceButtonDown = true;
4258 mVoiceButtonHandled = false;
4259 } else if (mVoiceButtonDown && !mVoiceButtonHandled
4260 && (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
4261 // long-press, start voice-based interactions
4262 mVoiceButtonHandled = true;
4263 voiceButtonAction = VOICEBUTTON_ACTION_START_VOICE_INPUT;
4264 }
4265 } else if (keyAction == KeyEvent.ACTION_UP) {
4266 if (mVoiceButtonDown) {
4267 // voice button up
4268 mVoiceButtonDown = false;
4269 if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
4270 voiceButtonAction = VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS;
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004271 }
Jeff Brownab624c22012-05-17 11:08:31 -07004272 }
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004273 }
4274 }//synchronized (mVoiceEventLock)
4275
4276 // take action after media button event filtering for voice-based interactions
4277 switch (voiceButtonAction) {
4278 case VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS:
4279 if (DEBUG_RC) Log.v(TAG, " ignore key event");
4280 break;
4281 case VOICEBUTTON_ACTION_START_VOICE_INPUT:
4282 if (DEBUG_RC) Log.v(TAG, " start voice-based interactions");
4283 // then start the voice-based interactions
4284 startVoiceBasedInteractions(needWakeLock);
4285 break;
4286 case VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS:
Jean-Michel Trivi1f9196a2012-07-26 15:11:07 -07004287 if (DEBUG_RC) Log.v(TAG, " send simulated key event, wakelock=" + needWakeLock);
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004288 sendSimulatedMediaButtonEvent(keyEvent, needWakeLock);
4289 break;
4290 }
4291 }
4292
4293 private void sendSimulatedMediaButtonEvent(KeyEvent originalKeyEvent, boolean needWakeLock) {
4294 // send DOWN event
4295 KeyEvent keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_DOWN);
4296 dispatchMediaKeyEvent(keyEvent, needWakeLock);
4297 // send UP event
4298 keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_UP);
4299 dispatchMediaKeyEvent(keyEvent, needWakeLock);
4300
4301 }
4302
4303
Jean-Michel Trivic6802222012-04-30 11:15:03 -07004304 private static boolean isValidMediaKeyEvent(KeyEvent keyEvent) {
4305 if (keyEvent == null) {
4306 return false;
4307 }
4308 final int keyCode = keyEvent.getKeyCode();
4309 switch (keyCode) {
4310 case KeyEvent.KEYCODE_MUTE:
4311 case KeyEvent.KEYCODE_HEADSETHOOK:
4312 case KeyEvent.KEYCODE_MEDIA_PLAY:
4313 case KeyEvent.KEYCODE_MEDIA_PAUSE:
4314 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
4315 case KeyEvent.KEYCODE_MEDIA_STOP:
4316 case KeyEvent.KEYCODE_MEDIA_NEXT:
4317 case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
4318 case KeyEvent.KEYCODE_MEDIA_REWIND:
4319 case KeyEvent.KEYCODE_MEDIA_RECORD:
4320 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
4321 case KeyEvent.KEYCODE_MEDIA_CLOSE:
4322 case KeyEvent.KEYCODE_MEDIA_EJECT:
4323 break;
4324 default:
4325 return false;
4326 }
4327 return true;
4328 }
4329
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004330 /**
4331 * Checks whether the given key code is one that can trigger the launch of voice-based
4332 * interactions.
4333 * @param keyCode the key code associated with the key event
4334 * @return true if the key is one of the supported voice-based interaction triggers
4335 */
4336 private static boolean isValidVoiceInputKeyCode(int keyCode) {
4337 if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK) {
4338 return true;
4339 } else {
4340 return false;
4341 }
4342 }
4343
4344 /**
4345 * Tell the system to start voice-based interactions / voice commands
4346 */
4347 private void startVoiceBasedInteractions(boolean needWakeLock) {
Jean-Michel Trivi3c2711f2012-05-20 12:07:03 -07004348 Intent voiceIntent = null;
4349 // select which type of search to launch:
4350 // - screen on and device unlocked: action is ACTION_WEB_SEARCH
4351 // - device locked or screen off: action is ACTION_VOICE_SEARCH_HANDS_FREE
4352 // with EXTRA_SECURE set to true if the device is securely locked
4353 PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
4354 boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
4355 if (!isLocked && pm.isScreenOn()) {
4356 voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
4357 } else {
4358 voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
4359 voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
4360 isLocked && mKeyguardManager.isKeyguardSecure());
4361 }
4362 // start the search activity
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004363 if (needWakeLock) {
4364 mMediaEventWakeLock.acquire();
4365 }
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004366 try {
Jean-Michel Trivi3c2711f2012-05-20 12:07:03 -07004367 if (voiceIntent != null) {
4368 voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
4369 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
4370 mContext.startActivity(voiceIntent);
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004371 }
4372 } catch (ActivityNotFoundException e) {
Jean-Michel Trivi3c2711f2012-05-20 12:07:03 -07004373 Log.w(TAG, "No activity for search: " + e);
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004374 } finally {
4375 if (needWakeLock) {
4376 mMediaEventWakeLock.release();
4377 }
4378 }
4379 }
4380
Jean-Michel Trivic6802222012-04-30 11:15:03 -07004381 private PowerManager.WakeLock mMediaEventWakeLock;
4382
4383 private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; //magic number
4384
4385 // only set when wakelock was acquired, no need to check value when received
4386 private static final String EXTRA_WAKELOCK_ACQUIRED =
4387 "android.media.AudioService.WAKELOCK_ACQUIRED";
4388
4389 public void onSendFinished(PendingIntent pendingIntent, Intent intent,
4390 int resultCode, String resultData, Bundle resultExtras) {
4391 if (resultCode == WAKELOCK_RELEASE_ON_FINISHED) {
4392 mMediaEventWakeLock.release();
4393 }
4394 }
4395
4396 BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
4397 public void onReceive(Context context, Intent intent) {
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004398 if (intent == null) {
4399 return;
4400 }
4401 Bundle extras = intent.getExtras();
4402 if (extras == null) {
4403 return;
4404 }
4405 if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)) {
Jean-Michel Trivic6802222012-04-30 11:15:03 -07004406 mMediaEventWakeLock.release();
4407 }
4408 }
4409 };
4410
Neel Parekhced110e2011-08-05 14:32:49 -07004411 private final Object mCurrentRcLock = new Object();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004412 /**
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004413 * The one remote control client which will receive a request for display information.
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07004414 * This object may be null.
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004415 * Access protected by mCurrentRcLock.
4416 */
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004417 private IRemoteControlClient mCurrentRcClient = null;
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004418
Jean-Michel Trivi7b6198c2011-08-07 11:27:50 -07004419 private final static int RC_INFO_NONE = 0;
4420 private final static int RC_INFO_ALL =
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004421 RemoteControlClient.FLAG_INFORMATION_REQUEST_ALBUM_ART |
4422 RemoteControlClient.FLAG_INFORMATION_REQUEST_KEY_MEDIA |
4423 RemoteControlClient.FLAG_INFORMATION_REQUEST_METADATA |
4424 RemoteControlClient.FLAG_INFORMATION_REQUEST_PLAYSTATE;
Jean-Michel Trivi7b6198c2011-08-07 11:27:50 -07004425
4426 /**
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07004427 * A monotonically increasing generation counter for mCurrentRcClient.
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004428 * Only accessed with a lock on mCurrentRcLock.
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07004429 * No value wrap-around issues as we only act on equal values.
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004430 */
Neel Parekhced110e2011-08-05 14:32:49 -07004431 private int mCurrentRcClientGen = 0;
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004432
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004433 /**
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004434 * Inner class to monitor remote control client deaths, and remove the client for the
4435 * remote control stack if necessary.
4436 */
4437 private class RcClientDeathHandler implements IBinder.DeathRecipient {
4438 private IBinder mCb; // To be notified of client's death
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004439 private PendingIntent mMediaIntent;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004440
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004441 RcClientDeathHandler(IBinder cb, PendingIntent pi) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004442 mCb = cb;
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004443 mMediaIntent = pi;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004444 }
4445
4446 public void binderDied() {
4447 Log.w(TAG, " RemoteControlClient died");
4448 // remote control client died, make sure the displays don't use it anymore
4449 // by setting its remote control client to null
Jean-Michel Trivi26b3d1f2011-09-28 11:05:15 -07004450 registerRemoteControlClient(mMediaIntent, null/*rcClient*/, null/*ignored*/);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07004451 // the dead client was maybe handling remote playback, reevaluate
4452 postReevaluateRemote();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004453 }
4454
4455 public IBinder getBinder() {
4456 return mCb;
4457 }
4458 }
4459
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07004460 /**
4461 * A global counter for RemoteControlClient identifiers
4462 */
4463 private static int sLastRccId = 0;
4464
4465 private class RemotePlaybackState {
4466 int mRccId;
4467 int mVolume;
4468 int mVolumeMax;
4469 int mVolumeHandling;
4470
4471 private RemotePlaybackState(int id, int vol, int volMax) {
4472 mRccId = id;
4473 mVolume = vol;
4474 mVolumeMax = volMax;
4475 mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
4476 }
4477 }
4478
4479 /**
4480 * Internal cache for the playback information of the RemoteControlClient whose volume gets to
4481 * be controlled by the volume keys ("main"), so we don't have to iterate over the RC stack
4482 * every time we need this info.
4483 */
4484 private RemotePlaybackState mMainRemote;
4485 /**
4486 * Indicates whether the "main" RemoteControlClient is considered active.
4487 * Use synchronized on mMainRemote.
4488 */
4489 private boolean mMainRemoteIsActive;
4490 /**
4491 * Indicates whether there is remote playback going on. True even if there is no "active"
4492 * remote playback (mMainRemoteIsActive is false), but a RemoteControlClient has declared it
4493 * handles remote playback.
4494 * Use synchronized on mMainRemote.
4495 */
4496 private boolean mHasRemotePlayback;
4497
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004498 private static class RemoteControlStackEntry {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07004499 public int mRccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004500 /**
4501 * The target for the ACTION_MEDIA_BUTTON events.
4502 * Always non null.
4503 */
4504 public PendingIntent mMediaIntent;
4505 /**
4506 * The registered media button event receiver.
4507 * Always non null.
4508 */
4509 public ComponentName mReceiverComponent;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004510 public String mCallingPackageName;
4511 public int mCallingUid;
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004512 /**
4513 * Provides access to the information to display on the remote control.
4514 * May be null (when a media button event receiver is registered,
4515 * but no remote control client has been registered) */
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004516 public IRemoteControlClient mRcClient;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004517 public RcClientDeathHandler mRcClientDeathHandler;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07004518 /**
4519 * Information only used for non-local playback
4520 */
4521 public int mPlaybackType;
4522 public int mPlaybackVolume;
4523 public int mPlaybackVolumeMax;
4524 public int mPlaybackVolumeHandling;
4525 public int mPlaybackStream;
4526 public int mPlaybackState;
Jean-Michel Trivi13570122012-06-19 14:03:09 -07004527 public IRemoteVolumeObserver mRemoteVolumeObs;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07004528
4529 public void resetPlaybackInfo() {
4530 mPlaybackType = RemoteControlClient.PLAYBACK_TYPE_LOCAL;
4531 mPlaybackVolume = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
4532 mPlaybackVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
4533 mPlaybackVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
4534 mPlaybackStream = AudioManager.STREAM_MUSIC;
4535 mPlaybackState = RemoteControlClient.PLAYSTATE_STOPPED;
Jean-Michel Trivi13570122012-06-19 14:03:09 -07004536 mRemoteVolumeObs = null;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07004537 }
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004538
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004539 /** precondition: mediaIntent != null, eventReceiver != null */
4540 public RemoteControlStackEntry(PendingIntent mediaIntent, ComponentName eventReceiver) {
4541 mMediaIntent = mediaIntent;
4542 mReceiverComponent = eventReceiver;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004543 mCallingUid = -1;
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07004544 mRcClient = null;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07004545 mRccId = ++sLastRccId;
4546
4547 resetPlaybackInfo();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004548 }
4549
4550 public void unlinkToRcClientDeath() {
4551 if ((mRcClientDeathHandler != null) && (mRcClientDeathHandler.mCb != null)) {
4552 try {
4553 mRcClientDeathHandler.mCb.unlinkToDeath(mRcClientDeathHandler, 0);
Jean-Michel Trivia9509732011-11-14 15:23:04 -08004554 mRcClientDeathHandler = null;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004555 } catch (java.util.NoSuchElementException e) {
4556 // not much we can do here
4557 Log.e(TAG, "Encountered " + e + " in unlinkToRcClientDeath()");
4558 e.printStackTrace();
4559 }
4560 }
4561 }
Jean-Michel Trivia9509732011-11-14 15:23:04 -08004562
4563 @Override
4564 protected void finalize() throws Throwable {
4565 unlinkToRcClientDeath();// unlink exception handled inside method
4566 super.finalize();
4567 }
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004568 }
4569
4570 /**
4571 * The stack of remote control event receivers.
4572 * Code sections and methods that modify the remote control event receiver stack are
4573 * synchronized on mRCStack, but also BEFORE on mFocusLock as any change in either
4574 * stack, audio focus or RC, can lead to a change in the remote control display
4575 */
Glenn Kasten30c918c2011-11-10 17:56:41 -08004576 private final Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>();
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004577
4578 /**
Jean-Michel Trivi722b8082012-05-15 15:18:33 -07004579 * The component the telephony package can register so telephony calls have priority to
4580 * handle media button events
4581 */
4582 private ComponentName mMediaReceiverForCalls = null;
4583
4584 /**
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004585 * Helper function:
4586 * Display in the log the current entries in the remote control focus stack
4587 */
4588 private void dumpRCStack(PrintWriter pw) {
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -07004589 pw.println("\nRemote Control stack entries:");
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004590 synchronized(mRCStack) {
4591 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4592 while(stackIterator.hasNext()) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004593 RemoteControlStackEntry rcse = stackIterator.next();
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004594 pw.println(" pi: " + rcse.mMediaIntent +
4595 " -- ercvr: " + rcse.mReceiverComponent +
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07004596 " -- client: " + rcse.mRcClient +
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07004597 " -- uid: " + rcse.mCallingUid +
4598 " -- type: " + rcse.mPlaybackType +
4599 " state: " + rcse.mPlaybackState);
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004600 }
4601 }
4602 }
4603
4604 /**
4605 * Helper function:
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07004606 * Display in the log the current entries in the remote control stack, focusing
4607 * on RemoteControlClient data
4608 */
4609 private void dumpRCCStack(PrintWriter pw) {
4610 pw.println("\nRemote Control Client stack entries:");
4611 synchronized(mRCStack) {
4612 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4613 while(stackIterator.hasNext()) {
4614 RemoteControlStackEntry rcse = stackIterator.next();
4615 pw.println(" uid: " + rcse.mCallingUid +
4616 " -- id: " + rcse.mRccId +
4617 " -- type: " + rcse.mPlaybackType +
4618 " -- state: " + rcse.mPlaybackState +
4619 " -- vol handling: " + rcse.mPlaybackVolumeHandling +
4620 " -- vol: " + rcse.mPlaybackVolume +
Jean-Michel Trivi13570122012-06-19 14:03:09 -07004621 " -- volMax: " + rcse.mPlaybackVolumeMax +
4622 " -- volObs: " + rcse.mRemoteVolumeObs);
Eric Laurentc34dcc12012-09-10 13:51:52 -07004623
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07004624 }
4625 }
4626 synchronized (mMainRemote) {
4627 pw.println("\nRemote Volume State:");
4628 pw.println(" has remote: " + mHasRemotePlayback);
4629 pw.println(" is remote active: " + mMainRemoteIsActive);
4630 pw.println(" rccId: " + mMainRemote.mRccId);
4631 pw.println(" volume handling: "
4632 + ((mMainRemote.mVolumeHandling == RemoteControlClient.PLAYBACK_VOLUME_FIXED) ?
4633 "PLAYBACK_VOLUME_FIXED(0)" : "PLAYBACK_VOLUME_VARIABLE(1)"));
4634 pw.println(" volume: " + mMainRemote.mVolume);
4635 pw.println(" volume steps: " + mMainRemote.mVolumeMax);
4636 }
4637 }
4638
4639 /**
4640 * Helper function:
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07004641 * Remove any entry in the remote control stack that has the same package name as packageName
4642 * Pre-condition: packageName != null
4643 */
4644 private void removeMediaButtonReceiverForPackage(String packageName) {
4645 synchronized(mRCStack) {
4646 if (mRCStack.empty()) {
4647 return;
4648 } else {
4649 RemoteControlStackEntry oldTop = mRCStack.peek();
4650 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4651 // iterate over the stack entries
4652 while(stackIterator.hasNext()) {
4653 RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
4654 if (packageName.equalsIgnoreCase(rcse.mReceiverComponent.getPackageName())) {
4655 // a stack entry is from the package being removed, remove it from the stack
4656 stackIterator.remove();
Jean-Michel Trivia9509732011-11-14 15:23:04 -08004657 rcse.unlinkToRcClientDeath();
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07004658 }
4659 }
4660 if (mRCStack.empty()) {
4661 // no saved media button receiver
4662 mAudioHandler.sendMessage(
4663 mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
4664 null));
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07004665 } else if (oldTop != mRCStack.peek()) {
4666 // the top of the stack has changed, save it in the system settings
4667 // by posting a message to persist it
4668 mAudioHandler.sendMessage(
4669 mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
4670 mRCStack.peek().mReceiverComponent));
4671 }
4672 }
4673 }
4674 }
4675
4676 /**
4677 * Helper function:
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004678 * Restore remote control receiver from the system settings.
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07004679 */
4680 private void restoreMediaButtonReceiver() {
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07004681 String receiverName = Settings.System.getStringForUser(mContentResolver,
4682 Settings.System.MEDIA_BUTTON_RECEIVER, UserHandle.USER_CURRENT);
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07004683 if ((null != receiverName) && !receiverName.isEmpty()) {
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004684 ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
4685 // construct a PendingIntent targeted to the restored component name
4686 // for the media button and register it
4687 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
4688 // the associated intent will be handled by the component being registered
4689 mediaButtonIntent.setComponent(eventReceiver);
4690 PendingIntent pi = PendingIntent.getBroadcast(mContext,
4691 0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
4692 registerMediaButtonIntent(pi, eventReceiver);
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07004693 }
4694 }
4695
4696 /**
4697 * Helper function:
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004698 * Set the new remote control receiver at the top of the RC focus stack.
4699 * precondition: mediaIntent != null, target != null
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004700 */
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004701 private void pushMediaButtonReceiver(PendingIntent mediaIntent, ComponentName target) {
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004702 // already at top of stack?
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004703 if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(mediaIntent)) {
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004704 return;
4705 }
4706 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004707 RemoteControlStackEntry rcse = null;
4708 boolean wasInsideStack = false;
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004709 while(stackIterator.hasNext()) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004710 rcse = (RemoteControlStackEntry)stackIterator.next();
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004711 if(rcse.mMediaIntent.equals(mediaIntent)) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004712 wasInsideStack = true;
Jean-Michel Trivi72843372011-04-18 10:18:37 -07004713 stackIterator.remove();
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004714 break;
4715 }
4716 }
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004717 if (!wasInsideStack) {
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004718 rcse = new RemoteControlStackEntry(mediaIntent, target);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004719 }
4720 mRCStack.push(rcse);
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07004721
4722 // post message to persist the default media button receiver
4723 mAudioHandler.sendMessage( mAudioHandler.obtainMessage(
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004724 MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) );
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004725 }
4726
4727 /**
4728 * Helper function:
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004729 * Remove the remote control receiver from the RC focus stack.
4730 * precondition: pi != null
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004731 */
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004732 private void removeMediaButtonReceiver(PendingIntent pi) {
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004733 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4734 while(stackIterator.hasNext()) {
4735 RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004736 if(rcse.mMediaIntent.equals(pi)) {
Jean-Michel Trivi72843372011-04-18 10:18:37 -07004737 stackIterator.remove();
Jean-Michel Trivia9509732011-11-14 15:23:04 -08004738 rcse.unlinkToRcClientDeath();
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004739 break;
4740 }
4741 }
4742 }
4743
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004744 /**
4745 * Helper function:
4746 * Called synchronized on mRCStack
4747 */
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004748 private boolean isCurrentRcController(PendingIntent pi) {
4749 if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(pi)) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004750 return true;
4751 }
4752 return false;
4753 }
4754
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004755 //==========================================================================================
4756 // Remote control display / client
4757 //==========================================================================================
4758 /**
4759 * Update the remote control displays with the new "focused" client generation
4760 */
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07004761 private void setNewRcClientOnDisplays_syncRcsCurrc(int newClientGeneration,
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004762 PendingIntent newMediaIntent, boolean clearing) {
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004763 // NOTE: Only one IRemoteControlDisplay supported in this implementation
4764 if (mRcDisplay != null) {
4765 try {
Jean-Michel Trivi4b6df6a32011-08-24 10:31:13 -07004766 mRcDisplay.setCurrentClientId(
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004767 newClientGeneration, newMediaIntent, clearing);
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004768 } catch (RemoteException e) {
Jean-Michel Trividb8a3612011-08-26 19:10:51 -07004769 Log.e(TAG, "Dead display in setNewRcClientOnDisplays_syncRcsCurrc() "+e);
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004770 // if we had a display before, stop monitoring its death
4771 rcDisplay_stopDeathMonitor_syncRcStack();
4772 mRcDisplay = null;
4773 }
4774 }
4775 }
4776
4777 /**
4778 * Update the remote control clients with the new "focused" client generation
4779 */
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07004780 private void setNewRcClientGenerationOnClients_syncRcsCurrc(int newClientGeneration) {
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004781 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4782 while(stackIterator.hasNext()) {
4783 RemoteControlStackEntry se = stackIterator.next();
4784 if ((se != null) && (se.mRcClient != null)) {
4785 try {
4786 se.mRcClient.setCurrentClientGenerationId(newClientGeneration);
4787 } catch (RemoteException e) {
Jean-Michel Trividb8a3612011-08-26 19:10:51 -07004788 Log.w(TAG, "Dead client in setNewRcClientGenerationOnClients_syncRcsCurrc()"+e);
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004789 stackIterator.remove();
4790 se.unlinkToRcClientDeath();
4791 }
4792 }
4793 }
4794 }
4795
4796 /**
Jean-Michel Trivi4b6df6a32011-08-24 10:31:13 -07004797 * Update the displays and clients with the new "focused" client generation and name
4798 * @param newClientGeneration the new generation value matching a client update
4799 * @param newClientEventReceiver the media button event receiver associated with the client.
4800 * May be null, which implies there is no registered media button event receiver.
4801 * @param clearing true if the new client generation value maps to a remote control update
4802 * where the display should be cleared.
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004803 */
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07004804 private void setNewRcClient_syncRcsCurrc(int newClientGeneration,
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004805 PendingIntent newMediaIntent, boolean clearing) {
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07004806 // send the new valid client generation ID to all displays
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004807 setNewRcClientOnDisplays_syncRcsCurrc(newClientGeneration, newMediaIntent, clearing);
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07004808 // send the new valid client generation ID to all clients
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07004809 setNewRcClientGenerationOnClients_syncRcsCurrc(newClientGeneration);
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004810 }
4811
4812 /**
4813 * Called when processing MSG_RCDISPLAY_CLEAR event
4814 */
4815 private void onRcDisplayClear() {
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07004816 if (DEBUG_RC) Log.i(TAG, "Clear remote control display");
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004817
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07004818 synchronized(mRCStack) {
4819 synchronized(mCurrentRcLock) {
4820 mCurrentRcClientGen++;
4821 // synchronously update the displays and clients with the new client generation
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07004822 setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004823 null /*newMediaIntent*/, true /*clearing*/);
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07004824 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004825 }
4826 }
4827
4828 /**
4829 * Called when processing MSG_RCDISPLAY_UPDATE event
4830 */
4831 private void onRcDisplayUpdate(RemoteControlStackEntry rcse, int flags /* USED ?*/) {
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07004832 synchronized(mRCStack) {
4833 synchronized(mCurrentRcLock) {
4834 if ((mCurrentRcClient != null) && (mCurrentRcClient.equals(rcse.mRcClient))) {
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07004835 if (DEBUG_RC) Log.i(TAG, "Display/update remote control ");
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004836
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07004837 mCurrentRcClientGen++;
4838 // synchronously update the displays and clients with
4839 // the new client generation
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07004840 setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004841 rcse.mMediaIntent /*newMediaIntent*/,
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07004842 false /*clearing*/);
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004843
Jean-Michel Trividb8a3612011-08-26 19:10:51 -07004844 // tell the current client that it needs to send info
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07004845 try {
4846 mCurrentRcClient.onInformationRequested(mCurrentRcClientGen,
4847 flags, mArtworkExpectedWidth, mArtworkExpectedHeight);
4848 } catch (RemoteException e) {
4849 Log.e(TAG, "Current valid remote client is dead: "+e);
4850 mCurrentRcClient = null;
4851 }
4852 } else {
4853 // the remote control display owner has changed between the
4854 // the message to update the display was sent, and the time it
4855 // gets to be processed (now)
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004856 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004857 }
4858 }
4859 }
4860
4861
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004862 /**
4863 * Helper function:
4864 * Called synchronized on mRCStack
4865 */
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07004866 private void clearRemoteControlDisplay_syncAfRcs() {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004867 synchronized(mCurrentRcLock) {
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07004868 mCurrentRcClient = null;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004869 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004870 // will cause onRcDisplayClear() to be called in AudioService's handler thread
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004871 mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_CLEAR) );
4872 }
4873
4874 /**
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07004875 * Helper function for code readability: only to be called from
4876 * checkUpdateRemoteControlDisplay_syncAfRcs() which checks the preconditions for
4877 * this method.
4878 * Preconditions:
4879 * - called synchronized mAudioFocusLock then on mRCStack
4880 * - mRCStack.isEmpty() is false
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004881 */
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07004882 private void updateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004883 RemoteControlStackEntry rcse = mRCStack.peek();
Jean-Michel Trivib5f04d62011-08-09 19:12:36 -07004884 int infoFlagsAboutToBeUsed = infoChangedFlags;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004885 // this is where we enforce opt-in for information display on the remote controls
4886 // with the new AudioManager.registerRemoteControlClient() API
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07004887 if (rcse.mRcClient == null) {
4888 //Log.w(TAG, "Can't update remote control display with null remote control client");
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07004889 clearRemoteControlDisplay_syncAfRcs();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004890 return;
4891 }
4892 synchronized(mCurrentRcLock) {
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07004893 if (!rcse.mRcClient.equals(mCurrentRcClient)) {
Jean-Michel Trivi7b6198c2011-08-07 11:27:50 -07004894 // new RC client, assume every type of information shall be queried
Jean-Michel Trivib5f04d62011-08-09 19:12:36 -07004895 infoFlagsAboutToBeUsed = RC_INFO_ALL;
Jean-Michel Trivi7b6198c2011-08-07 11:27:50 -07004896 }
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07004897 mCurrentRcClient = rcse.mRcClient;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004898 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004899 // will cause onRcDisplayUpdate() to be called in AudioService's handler thread
Jean-Michel Trivib5f04d62011-08-09 19:12:36 -07004900 mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_UPDATE,
Jean-Michel Trivi0a9faa82011-08-14 17:36:42 -07004901 infoFlagsAboutToBeUsed /* arg1 */, 0, rcse /* obj, != null */) );
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004902 }
4903
4904 /**
4905 * Helper function:
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07004906 * Called synchronized on mAudioFocusLock, then mRCStack
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004907 * Check whether the remote control display should be updated, triggers the update if required
Jean-Michel Trivib5f04d62011-08-09 19:12:36 -07004908 * @param infoChangedFlags the flags corresponding to the remote control client information
4909 * that has changed, if applicable (checking for the update conditions might trigger a
4910 * clear, rather than an update event).
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004911 */
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07004912 private void checkUpdateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004913 // determine whether the remote control display should be refreshed
4914 // if either stack is empty, there is a mismatch, so clear the RC display
4915 if (mRCStack.isEmpty() || mFocusStack.isEmpty()) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07004916 clearRemoteControlDisplay_syncAfRcs();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004917 return;
4918 }
Jean-Michel Trivi1f9196a2012-07-26 15:11:07 -07004919
4920 // determine which entry in the AudioFocus stack to consider, and compare against the
4921 // top of the stack for the media button event receivers : simply using the top of the
4922 // stack would make the entry disappear from the RemoteControlDisplay in conditions such as
4923 // notifications playing during music playback.
4924 // crawl the AudioFocus stack until an entry is found with the following characteristics:
4925 // - focus gain on STREAM_MUSIC stream
4926 // - non-transient focus gain on a stream other than music
4927 FocusStackEntry af = null;
4928 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
4929 while(stackIterator.hasNext()) {
4930 FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
4931 if ((fse.mStreamType == AudioManager.STREAM_MUSIC)
4932 || (fse.mFocusChangeType == AudioManager.AUDIOFOCUS_GAIN)) {
4933 af = fse;
4934 break;
4935 }
4936 }
4937 if (af == null) {
4938 clearRemoteControlDisplay_syncAfRcs();
4939 return;
4940 }
4941
4942 // if the audio focus and RC owners belong to different packages, there is a mismatch, clear
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004943 if ((mRCStack.peek().mCallingPackageName != null)
Jean-Michel Trivi1f9196a2012-07-26 15:11:07 -07004944 && (af.mPackageName != null)
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004945 && !(mRCStack.peek().mCallingPackageName.compareTo(
Jean-Michel Trivi1f9196a2012-07-26 15:11:07 -07004946 af.mPackageName) == 0)) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07004947 clearRemoteControlDisplay_syncAfRcs();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004948 return;
4949 }
4950 // if the audio focus didn't originate from the same Uid as the one in which the remote
4951 // control information will be retrieved, clear
Jean-Michel Trivi1f9196a2012-07-26 15:11:07 -07004952 if (mRCStack.peek().mCallingUid != af.mCallingUid) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07004953 clearRemoteControlDisplay_syncAfRcs();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004954 return;
4955 }
4956 // refresh conditions were verified: update the remote controls
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07004957 // ok to call: synchronized mAudioFocusLock then on mRCStack, mRCStack is not empty
4958 updateRemoteControlDisplay_syncAfRcs(infoChangedFlags);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004959 }
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004960
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08004961 /**
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004962 * see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
4963 * precondition: mediaIntent != null, target != null
4964 */
4965 public void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver) {
4966 Log.i(TAG, " Remote Control registerMediaButtonIntent() for " + mediaIntent);
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004967
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004968 synchronized(mAudioFocusLock) {
4969 synchronized(mRCStack) {
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004970 pushMediaButtonReceiver(mediaIntent, eventReceiver);
Jean-Michel Trivib5f04d62011-08-09 19:12:36 -07004971 // new RC client, assume every type of information shall be queried
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07004972 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004973 }
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004974 }
4975 }
4976
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08004977 /**
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004978 * see AudioManager.unregisterMediaButtonIntent(PendingIntent mediaIntent)
4979 * precondition: mediaIntent != null, eventReceiver != null
4980 */
4981 public void unregisterMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver)
4982 {
4983 Log.i(TAG, " Remote Control unregisterMediaButtonIntent() for " + mediaIntent);
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004984
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004985 synchronized(mAudioFocusLock) {
4986 synchronized(mRCStack) {
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004987 boolean topOfStackWillChange = isCurrentRcController(mediaIntent);
4988 removeMediaButtonReceiver(mediaIntent);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004989 if (topOfStackWillChange) {
Jean-Michel Trivib5f04d62011-08-09 19:12:36 -07004990 // current RC client will change, assume every type of info needs to be queried
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07004991 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004992 }
4993 }
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004994 }
4995 }
4996
Jean-Michel Trivi26b3d1f2011-09-28 11:05:15 -07004997 /**
Jean-Michel Trivi722b8082012-05-15 15:18:33 -07004998 * see AudioManager.registerMediaButtonEventReceiverForCalls(ComponentName c)
4999 * precondition: c != null
5000 */
5001 public void registerMediaButtonEventReceiverForCalls(ComponentName c) {
5002 if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE")
5003 != PackageManager.PERMISSION_GRANTED) {
5004 Log.e(TAG, "Invalid permissions to register media button receiver for calls");
5005 return;
5006 }
5007 synchronized(mRCStack) {
5008 mMediaReceiverForCalls = c;
5009 }
5010 }
5011
5012 /**
5013 * see AudioManager.unregisterMediaButtonEventReceiverForCalls()
5014 */
5015 public void unregisterMediaButtonEventReceiverForCalls() {
5016 if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE")
5017 != PackageManager.PERMISSION_GRANTED) {
5018 Log.e(TAG, "Invalid permissions to unregister media button receiver for calls");
5019 return;
5020 }
5021 synchronized(mRCStack) {
5022 mMediaReceiverForCalls = null;
5023 }
5024 }
5025
5026 /**
Jean-Michel Trivi26b3d1f2011-09-28 11:05:15 -07005027 * see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...)
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005028 * @return the unique ID of the RemoteControlStackEntry associated with the RemoteControlClient
Jean-Michel Trivi26b3d1f2011-09-28 11:05:15 -07005029 * Note: using this method with rcClient == null is a way to "disable" the IRemoteControlClient
5030 * without modifying the RC stack, but while still causing the display to refresh (will
5031 * become blank as a result of this)
5032 */
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005033 public int registerRemoteControlClient(PendingIntent mediaIntent,
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005034 IRemoteControlClient rcClient, String callingPackageName) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07005035 if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005036 int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005037 synchronized(mAudioFocusLock) {
5038 synchronized(mRCStack) {
5039 // store the new display information
5040 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
5041 while(stackIterator.hasNext()) {
5042 RemoteControlStackEntry rcse = stackIterator.next();
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005043 if(rcse.mMediaIntent.equals(mediaIntent)) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005044 // already had a remote control client?
5045 if (rcse.mRcClientDeathHandler != null) {
5046 // stop monitoring the old client's death
5047 rcse.unlinkToRcClientDeath();
5048 }
5049 // save the new remote control client
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07005050 rcse.mRcClient = rcClient;
Jean-Michel Trivi26b3d1f2011-09-28 11:05:15 -07005051 rcse.mCallingPackageName = callingPackageName;
5052 rcse.mCallingUid = Binder.getCallingUid();
5053 if (rcClient == null) {
Jean-Michel Trivia9509732011-11-14 15:23:04 -08005054 // here rcse.mRcClientDeathHandler is null;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005055 rcse.resetPlaybackInfo();
Jean-Michel Trivi26b3d1f2011-09-28 11:05:15 -07005056 break;
5057 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005058 rccId = rcse.mRccId;
Jean-Michel Trivi26b3d1f2011-09-28 11:05:15 -07005059
5060 // there is a new (non-null) client:
5061 // 1/ give the new client the current display (if any)
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005062 if (mRcDisplay != null) {
5063 try {
5064 rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
5065 } catch (RemoteException e) {
5066 Log.e(TAG, "Error connecting remote control display to client: "+e);
5067 e.printStackTrace();
5068 }
5069 }
Jean-Michel Trivi26b3d1f2011-09-28 11:05:15 -07005070 // 2/ monitor the new client's death
5071 IBinder b = rcse.mRcClient.asBinder();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005072 RcClientDeathHandler rcdh =
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005073 new RcClientDeathHandler(b, rcse.mMediaIntent);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005074 try {
5075 b.linkToDeath(rcdh, 0);
5076 } catch (RemoteException e) {
5077 // remote control client is DOA, disqualify it
5078 Log.w(TAG, "registerRemoteControlClient() has a dead client " + b);
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07005079 rcse.mRcClient = null;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005080 }
5081 rcse.mRcClientDeathHandler = rcdh;
5082 break;
5083 }
5084 }
5085 // if the eventReceiver is at the top of the stack
5086 // then check for potential refresh of the remote controls
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005087 if (isCurrentRcController(mediaIntent)) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07005088 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005089 }
5090 }
5091 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005092 return rccId;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005093 }
5094
Jean-Michel Trivi4b6df6a32011-08-24 10:31:13 -07005095 /**
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005096 * see AudioManager.unregisterRemoteControlClient(PendingIntent pi, ...)
Jean-Michel Trivi4b6df6a32011-08-24 10:31:13 -07005097 * rcClient is guaranteed non-null
5098 */
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005099 public void unregisterRemoteControlClient(PendingIntent mediaIntent,
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005100 IRemoteControlClient rcClient) {
Jean-Michel Trivi4b6df6a32011-08-24 10:31:13 -07005101 synchronized(mAudioFocusLock) {
5102 synchronized(mRCStack) {
5103 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
5104 while(stackIterator.hasNext()) {
5105 RemoteControlStackEntry rcse = stackIterator.next();
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005106 if ((rcse.mMediaIntent.equals(mediaIntent))
Jean-Michel Trivi4b6df6a32011-08-24 10:31:13 -07005107 && rcClient.equals(rcse.mRcClient)) {
5108 // we found the IRemoteControlClient to unregister
5109 // stop monitoring its death
5110 rcse.unlinkToRcClientDeath();
5111 // reset the client-related fields
5112 rcse.mRcClient = null;
Jean-Michel Trivi4b6df6a32011-08-24 10:31:13 -07005113 rcse.mCallingPackageName = null;
5114 }
5115 }
5116 }
5117 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005118 }
5119
5120 /**
5121 * The remote control displays.
5122 * Access synchronized on mRCStack
5123 * NOTE: Only one IRemoteControlDisplay supported in this implementation
5124 */
5125 private IRemoteControlDisplay mRcDisplay;
5126 private RcDisplayDeathHandler mRcDisplayDeathHandler;
5127 private int mArtworkExpectedWidth = -1;
5128 private int mArtworkExpectedHeight = -1;
5129 /**
5130 * Inner class to monitor remote control display deaths, and unregister them from the list
5131 * of displays if necessary.
5132 */
5133 private class RcDisplayDeathHandler implements IBinder.DeathRecipient {
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005134 private IBinder mCb; // To be notified of client's death
5135
5136 public RcDisplayDeathHandler(IBinder b) {
5137 if (DEBUG_RC) Log.i(TAG, "new RcDisplayDeathHandler for "+b);
5138 mCb = b;
5139 }
5140
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005141 public void binderDied() {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005142 synchronized(mRCStack) {
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005143 Log.w(TAG, "RemoteControl: display died");
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005144 mRcDisplay = null;
5145 }
5146 }
5147
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005148 public void unlinkToRcDisplayDeath() {
5149 if (DEBUG_RC) Log.i(TAG, "unlinkToRcDisplayDeath for "+mCb);
5150 try {
5151 mCb.unlinkToDeath(this, 0);
5152 } catch (java.util.NoSuchElementException e) {
5153 // not much we can do here, the display was being unregistered anyway
5154 Log.e(TAG, "Encountered " + e + " in unlinkToRcDisplayDeath()");
5155 e.printStackTrace();
5156 }
5157 }
5158
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005159 }
5160
5161 private void rcDisplay_stopDeathMonitor_syncRcStack() {
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005162 if (mRcDisplay != null) { // implies (mRcDisplayDeathHandler != null)
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005163 // we had a display before, stop monitoring its death
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005164 mRcDisplayDeathHandler.unlinkToRcDisplayDeath();
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005165 }
5166 }
5167
5168 private void rcDisplay_startDeathMonitor_syncRcStack() {
5169 if (mRcDisplay != null) {
5170 // new non-null display, monitor its death
5171 IBinder b = mRcDisplay.asBinder();
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005172 mRcDisplayDeathHandler = new RcDisplayDeathHandler(b);
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005173 try {
5174 b.linkToDeath(mRcDisplayDeathHandler, 0);
5175 } catch (RemoteException e) {
5176 // remote control display is DOA, disqualify it
5177 Log.w(TAG, "registerRemoteControlDisplay() has a dead client " + b);
5178 mRcDisplay = null;
5179 }
5180 }
5181 }
5182
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005183 /**
Jean-Michel Trivi958bba62011-08-30 10:52:59 -07005184 * Register an IRemoteControlDisplay.
5185 * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient
5186 * at the top of the stack to update the new display with its information.
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005187 * Since only one IRemoteControlDisplay is supported, this will unregister the previous display.
5188 * @param rcd the IRemoteControlDisplay to register. No effect if null.
5189 */
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005190 public void registerRemoteControlDisplay(IRemoteControlDisplay rcd) {
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005191 if (DEBUG_RC) Log.d(TAG, ">>> registerRemoteControlDisplay("+rcd+")");
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07005192 synchronized(mAudioFocusLock) {
5193 synchronized(mRCStack) {
5194 if ((mRcDisplay == rcd) || (rcd == null)) {
5195 return;
5196 }
5197 // if we had a display before, stop monitoring its death
5198 rcDisplay_stopDeathMonitor_syncRcStack();
5199 mRcDisplay = rcd;
5200 // new display, start monitoring its death
5201 rcDisplay_startDeathMonitor_syncRcStack();
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005202
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07005203 // let all the remote control clients there is a new display
5204 // no need to unplug the previous because we only support one display
5205 // and the clients don't track the death of the display
5206 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
5207 while(stackIterator.hasNext()) {
5208 RemoteControlStackEntry rcse = stackIterator.next();
5209 if(rcse.mRcClient != null) {
5210 try {
5211 rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
5212 } catch (RemoteException e) {
5213 Log.e(TAG, "Error connecting remote control display to client: " + e);
5214 e.printStackTrace();
5215 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005216 }
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005217 }
Jean-Michel Trividb8a3612011-08-26 19:10:51 -07005218
Jean-Michel Trivi33879452011-08-31 11:42:17 -07005219 // 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 -07005220 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
Jean-Michel Trivi33879452011-08-31 11:42:17 -07005221 }
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005222 }
5223 }
Jean-Michel Trivid327f212010-03-16 21:44:33 -07005224
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005225 /**
5226 * Unregister an IRemoteControlDisplay.
5227 * Since only one IRemoteControlDisplay is supported, this has no effect if the one to
5228 * unregister is not the current one.
5229 * @param rcd the IRemoteControlDisplay to unregister. No effect if null.
5230 */
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005231 public void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) {
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005232 if (DEBUG_RC) Log.d(TAG, "<<< unregisterRemoteControlDisplay("+rcd+")");
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005233 synchronized(mRCStack) {
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005234 // only one display here, so you can only unregister the current display
5235 if ((rcd == null) || (rcd != mRcDisplay)) {
5236 if (DEBUG_RC) Log.w(TAG, " trying to unregister unregistered RCD");
5237 return;
5238 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005239 // if we had a display before, stop monitoring its death
5240 rcDisplay_stopDeathMonitor_syncRcStack();
5241 mRcDisplay = null;
5242
5243 // disconnect this remote control display from all the clients
5244 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
5245 while(stackIterator.hasNext()) {
5246 RemoteControlStackEntry rcse = stackIterator.next();
5247 if(rcse.mRcClient != null) {
5248 try {
5249 rcse.mRcClient.unplugRemoteControlDisplay(rcd);
5250 } catch (RemoteException e) {
5251 Log.e(TAG, "Error disconnecting remote control display to client: " + e);
5252 e.printStackTrace();
5253 }
5254 }
5255 }
5256 }
5257 }
5258
5259 public void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) {
5260 synchronized(mRCStack) {
5261 // NOTE: Only one IRemoteControlDisplay supported in this implementation
5262 mArtworkExpectedWidth = w;
5263 mArtworkExpectedHeight = h;
5264 }
5265 }
5266
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005267 public void setPlaybackInfoForRcc(int rccId, int what, int value) {
Jean-Michel Trivif98de1e2012-06-20 13:40:22 -07005268 sendMsg(mAudioHandler, MSG_RCC_NEW_PLAYBACK_INFO, SENDMSG_QUEUE,
5269 rccId /* arg1 */, what /* arg2 */, Integer.valueOf(value) /* obj */, 0 /* delay */);
5270 }
5271
5272 // handler for MSG_RCC_NEW_PLAYBACK_INFO
5273 private void onNewPlaybackInfoForRcc(int rccId, int key, int value) {
5274 if(DEBUG_RC) Log.d(TAG, "onNewPlaybackInfoForRcc(id=" + rccId +
5275 ", what=" + key + ",val=" + value + ")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005276 synchronized(mRCStack) {
5277 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
5278 while(stackIterator.hasNext()) {
5279 RemoteControlStackEntry rcse = stackIterator.next();
5280 if (rcse.mRccId == rccId) {
Jean-Michel Trivif98de1e2012-06-20 13:40:22 -07005281 switch (key) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005282 case RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE:
5283 rcse.mPlaybackType = value;
5284 postReevaluateRemote();
5285 break;
5286 case RemoteControlClient.PLAYBACKINFO_VOLUME:
5287 rcse.mPlaybackVolume = value;
5288 synchronized (mMainRemote) {
5289 if (rccId == mMainRemote.mRccId) {
5290 mMainRemote.mVolume = value;
5291 mVolumePanel.postHasNewRemotePlaybackInfo();
5292 }
5293 }
5294 break;
5295 case RemoteControlClient.PLAYBACKINFO_VOLUME_MAX:
5296 rcse.mPlaybackVolumeMax = value;
5297 synchronized (mMainRemote) {
5298 if (rccId == mMainRemote.mRccId) {
5299 mMainRemote.mVolumeMax = value;
5300 mVolumePanel.postHasNewRemotePlaybackInfo();
5301 }
5302 }
5303 break;
5304 case RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING:
5305 rcse.mPlaybackVolumeHandling = value;
5306 synchronized (mMainRemote) {
5307 if (rccId == mMainRemote.mRccId) {
5308 mMainRemote.mVolumeHandling = value;
5309 mVolumePanel.postHasNewRemotePlaybackInfo();
5310 }
5311 }
5312 break;
5313 case RemoteControlClient.PLAYBACKINFO_USES_STREAM:
5314 rcse.mPlaybackStream = value;
5315 break;
5316 case RemoteControlClient.PLAYBACKINFO_PLAYSTATE:
5317 rcse.mPlaybackState = value;
5318 synchronized (mMainRemote) {
5319 if (rccId == mMainRemote.mRccId) {
5320 mMainRemoteIsActive = isPlaystateActive(value);
5321 postReevaluateRemote();
5322 }
5323 }
5324 break;
5325 default:
Jean-Michel Trivif98de1e2012-06-20 13:40:22 -07005326 Log.e(TAG, "unhandled key " + key + " for RCC " + rccId);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005327 break;
5328 }
5329 return;
5330 }
5331 }
5332 }
5333 }
5334
Jean-Michel Trivi13570122012-06-19 14:03:09 -07005335 public void registerRemoteVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
Jean-Michel Trivif98de1e2012-06-20 13:40:22 -07005336 sendMsg(mAudioHandler, MSG_RCC_NEW_VOLUME_OBS, SENDMSG_QUEUE,
5337 rccId /* arg1 */, 0, rvo /* obj */, 0 /* delay */);
5338 }
5339
5340 // handler for MSG_RCC_NEW_VOLUME_OBS
5341 private void onRegisterVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
Jean-Michel Trivi13570122012-06-19 14:03:09 -07005342 synchronized(mRCStack) {
5343 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
5344 while(stackIterator.hasNext()) {
5345 RemoteControlStackEntry rcse = stackIterator.next();
5346 if (rcse.mRccId == rccId) {
5347 rcse.mRemoteVolumeObs = rvo;
5348 break;
5349 }
5350 }
5351 }
5352 }
5353
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005354 /**
5355 * Checks if a remote client is active on the supplied stream type. Update the remote stream
5356 * volume state if found and playing
5357 * @param streamType
5358 * @return false if no remote playing is currently playing
5359 */
5360 private boolean checkUpdateRemoteStateIfActive(int streamType) {
5361 synchronized(mRCStack) {
5362 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
5363 while(stackIterator.hasNext()) {
5364 RemoteControlStackEntry rcse = stackIterator.next();
5365 if ((rcse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE)
5366 && isPlaystateActive(rcse.mPlaybackState)
5367 && (rcse.mPlaybackStream == streamType)) {
5368 if (DEBUG_RC) Log.d(TAG, "remote playback active on stream " + streamType
5369 + ", vol =" + rcse.mPlaybackVolume);
5370 synchronized (mMainRemote) {
5371 mMainRemote.mRccId = rcse.mRccId;
5372 mMainRemote.mVolume = rcse.mPlaybackVolume;
5373 mMainRemote.mVolumeMax = rcse.mPlaybackVolumeMax;
5374 mMainRemote.mVolumeHandling = rcse.mPlaybackVolumeHandling;
5375 mMainRemoteIsActive = true;
5376 }
5377 return true;
5378 }
5379 }
5380 }
5381 synchronized (mMainRemote) {
5382 mMainRemoteIsActive = false;
5383 }
5384 return false;
5385 }
5386
5387 /**
5388 * Returns true if the given playback state is considered "active", i.e. it describes a state
5389 * where playback is happening, or about to
5390 * @param playState the playback state to evaluate
5391 * @return true if active, false otherwise (inactive or unknown)
5392 */
5393 private static boolean isPlaystateActive(int playState) {
5394 switch (playState) {
5395 case RemoteControlClient.PLAYSTATE_PLAYING:
5396 case RemoteControlClient.PLAYSTATE_BUFFERING:
5397 case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
5398 case RemoteControlClient.PLAYSTATE_REWINDING:
5399 case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
5400 case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
5401 return true;
5402 default:
5403 return false;
5404 }
5405 }
5406
5407 private void adjustRemoteVolume(int streamType, int direction, int flags) {
5408 int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
5409 boolean volFixed = false;
5410 synchronized (mMainRemote) {
5411 if (!mMainRemoteIsActive) {
5412 if (DEBUG_VOL) Log.w(TAG, "adjustRemoteVolume didn't find an active client");
5413 return;
5414 }
5415 rccId = mMainRemote.mRccId;
5416 volFixed = (mMainRemote.mVolumeHandling ==
5417 RemoteControlClient.PLAYBACK_VOLUME_FIXED);
5418 }
5419 // unlike "local" stream volumes, we can't compute the new volume based on the direction,
5420 // we can only notify the remote that volume needs to be updated, and we'll get an async'
5421 // update through setPlaybackInfoForRcc()
5422 if (!volFixed) {
5423 sendVolumeUpdateToRemote(rccId, direction);
5424 }
5425
5426 // fire up the UI
5427 mVolumePanel.postRemoteVolumeChanged(streamType, flags);
5428 }
5429
5430 private void sendVolumeUpdateToRemote(int rccId, int direction) {
5431 if (DEBUG_VOL) { Log.d(TAG, "sendVolumeUpdateToRemote(rccId="+rccId+" , dir="+direction); }
5432 if (direction == 0) {
5433 // only handling discrete events
5434 return;
5435 }
Jean-Michel Trivi13570122012-06-19 14:03:09 -07005436 IRemoteVolumeObserver rvo = null;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005437 synchronized (mRCStack) {
5438 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
5439 while(stackIterator.hasNext()) {
5440 RemoteControlStackEntry rcse = stackIterator.next();
5441 //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
5442 if (rcse.mRccId == rccId) {
Jean-Michel Trivi13570122012-06-19 14:03:09 -07005443 rvo = rcse.mRemoteVolumeObs;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005444 break;
5445 }
5446 }
5447 }
Jean-Michel Trivi13570122012-06-19 14:03:09 -07005448 if (rvo != null) {
5449 try {
5450 rvo.dispatchRemoteVolumeUpdate(direction, -1);
5451 } catch (RemoteException e) {
5452 Log.e(TAG, "Error dispatching relative volume update", e);
5453 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005454 }
5455 }
5456
5457 public int getRemoteStreamMaxVolume() {
5458 synchronized (mMainRemote) {
5459 if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
5460 return 0;
5461 }
5462 return mMainRemote.mVolumeMax;
5463 }
5464 }
5465
5466 public int getRemoteStreamVolume() {
5467 synchronized (mMainRemote) {
5468 if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
5469 return 0;
5470 }
5471 return mMainRemote.mVolume;
5472 }
5473 }
5474
5475 public void setRemoteStreamVolume(int vol) {
5476 if (DEBUG_VOL) { Log.d(TAG, "setRemoteStreamVolume(vol="+vol+")"); }
5477 int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
5478 synchronized (mMainRemote) {
5479 if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
5480 return;
5481 }
5482 rccId = mMainRemote.mRccId;
5483 }
Jean-Michel Trivi13570122012-06-19 14:03:09 -07005484 IRemoteVolumeObserver rvo = null;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005485 synchronized (mRCStack) {
5486 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
5487 while(stackIterator.hasNext()) {
5488 RemoteControlStackEntry rcse = stackIterator.next();
5489 if (rcse.mRccId == rccId) {
5490 //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
Jean-Michel Trivi13570122012-06-19 14:03:09 -07005491 rvo = rcse.mRemoteVolumeObs;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005492 break;
5493 }
5494 }
5495 }
Jean-Michel Trivi13570122012-06-19 14:03:09 -07005496 if (rvo != null) {
5497 try {
5498 rvo.dispatchRemoteVolumeUpdate(0, vol);
5499 } catch (RemoteException e) {
5500 Log.e(TAG, "Error dispatching absolute volume update", e);
5501 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005502 }
5503 }
5504
5505 /**
5506 * Call to make AudioService reevaluate whether it's in a mode where remote players should
5507 * have their volume controlled. In this implementation this is only to reset whether
5508 * VolumePanel should display remote volumes
5509 */
5510 private void postReevaluateRemote() {
5511 sendMsg(mAudioHandler, MSG_REEVALUATE_REMOTE, SENDMSG_QUEUE, 0, 0, null, 0);
5512 }
5513
5514 private void onReevaluateRemote() {
5515 if (DEBUG_VOL) { Log.w(TAG, "onReevaluateRemote()"); }
5516 // is there a registered RemoteControlClient that is handling remote playback
5517 boolean hasRemotePlayback = false;
5518 synchronized (mRCStack) {
5519 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
5520 while(stackIterator.hasNext()) {
5521 RemoteControlStackEntry rcse = stackIterator.next();
5522 if (rcse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) {
5523 hasRemotePlayback = true;
5524 break;
5525 }
5526 }
5527 }
5528 synchronized (mMainRemote) {
5529 if (mHasRemotePlayback != hasRemotePlayback) {
5530 mHasRemotePlayback = hasRemotePlayback;
5531 mVolumePanel.postRemoteSliderVisibility(hasRemotePlayback);
5532 }
5533 }
5534 }
5535
Jean-Michel Trivif26f0172012-04-25 16:23:20 -07005536 //==========================================================================================
5537 // Device orientation
5538 //==========================================================================================
5539 /**
5540 * Handles device configuration changes that may map to a change in the orientation.
5541 * This feature is optional, and is defined by the definition and value of the
5542 * "ro.audio.monitorOrientation" system property.
5543 */
5544 private void handleConfigurationChanged(Context context) {
5545 try {
5546 // reading new orientation "safely" (i.e. under try catch) in case anything
5547 // goes wrong when obtaining resources and configuration
Eric Laurentd640bd32012-09-28 18:01:48 -07005548 Configuration config = context.getResources().getConfiguration();
5549 if (mMonitorOrientation) {
5550 int newOrientation = config.orientation;
5551 if (newOrientation != mDeviceOrientation) {
5552 mDeviceOrientation = newOrientation;
5553 setOrientationForAudioSystem();
5554 }
Jean-Michel Trivif26f0172012-04-25 16:23:20 -07005555 }
Eric Laurentd640bd32012-09-28 18:01:48 -07005556 sendMsg(mAudioHandler,
5557 MSG_CONFIGURE_SAFE_MEDIA_VOLUME,
5558 SENDMSG_REPLACE,
5559 0,
5560 0,
5561 null,
5562 0);
Jean-Michel Trivif26f0172012-04-25 16:23:20 -07005563 } catch (Exception e) {
5564 Log.e(TAG, "Error retrieving device orientation: " + e);
5565 }
5566 }
5567
5568 private void setOrientationForAudioSystem() {
5569 switch (mDeviceOrientation) {
5570 case Configuration.ORIENTATION_LANDSCAPE:
5571 //Log.i(TAG, "orientation is landscape");
5572 AudioSystem.setParameters("orientation=landscape");
5573 break;
5574 case Configuration.ORIENTATION_PORTRAIT:
5575 //Log.i(TAG, "orientation is portrait");
5576 AudioSystem.setParameters("orientation=portrait");
5577 break;
5578 case Configuration.ORIENTATION_SQUARE:
5579 //Log.i(TAG, "orientation is square");
5580 AudioSystem.setParameters("orientation=square");
5581 break;
5582 case Configuration.ORIENTATION_UNDEFINED:
5583 //Log.i(TAG, "orientation is undefined");
5584 AudioSystem.setParameters("orientation=undefined");
5585 break;
5586 default:
5587 Log.e(TAG, "Unknown orientation");
5588 }
5589 }
5590
5591
Eric Laurent78472112012-05-21 08:57:21 -07005592 // Handles request to override default use of A2DP for media.
5593 public void setBluetoothA2dpOnInt(boolean on) {
5594 synchronized (mBluetoothA2dpEnabledLock) {
5595 mBluetoothA2dpEnabled = on;
Eric Laurentc390bed2012-07-03 12:24:05 -07005596 mAudioHandler.removeMessages(MSG_SET_FORCE_BT_A2DP_USE);
5597 AudioSystem.setForceUse(AudioSystem.FOR_MEDIA,
5598 mBluetoothA2dpEnabled ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP);
Eric Laurent78472112012-05-21 08:57:21 -07005599 }
5600 }
5601
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08005602 @Override
Jeff Sharkey098d5802012-04-26 17:30:34 -07005603 public void setRingtonePlayer(IRingtonePlayer player) {
5604 mContext.enforceCallingOrSelfPermission(REMOTE_AUDIO_PLAYBACK, null);
5605 mRingtonePlayer = player;
5606 }
5607
5608 @Override
5609 public IRingtonePlayer getRingtonePlayer() {
5610 return mRingtonePlayer;
5611 }
5612
5613 @Override
Dianne Hackborn632ca412012-06-14 19:34:10 -07005614 public AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
5615 synchronized (mCurAudioRoutes) {
5616 AudioRoutesInfo routes = new AudioRoutesInfo(mCurAudioRoutes);
5617 mRoutesObservers.register(observer);
5618 return routes;
5619 }
5620 }
5621
Eric Laurentc34dcc12012-09-10 13:51:52 -07005622
5623 //==========================================================================================
5624 // Safe media volume management.
5625 // MUSIC stream volume level is limited when headphones are connected according to safety
5626 // regulation. When the user attempts to raise the volume above the limit, a warning is
5627 // displayed and the user has to acknowlegde before the volume is actually changed.
5628 // The volume index corresponding to the limit is stored in config_safe_media_volume_index
5629 // property. Platforms with a different limit must set this property accordingly in their
5630 // overlay.
5631 //==========================================================================================
5632
Eric Laurentd640bd32012-09-28 18:01:48 -07005633 // mSafeMediaVolumeState indicates whether the media volume is limited over headphones.
5634 // It is SAFE_MEDIA_VOLUME_NOT_CONFIGURED at boot time until a network service is connected
5635 // or the configure time is elapsed. It is then set to SAFE_MEDIA_VOLUME_ACTIVE or
5636 // SAFE_MEDIA_VOLUME_DISABLED according to country option. If not SAFE_MEDIA_VOLUME_DISABLED, it
5637 // can be set to SAFE_MEDIA_VOLUME_INACTIVE by calling AudioService.disableSafeMediaVolume()
5638 // (when user opts out).
5639 private final int SAFE_MEDIA_VOLUME_NOT_CONFIGURED = 0;
5640 private final int SAFE_MEDIA_VOLUME_DISABLED = 1;
5641 private final int SAFE_MEDIA_VOLUME_INACTIVE = 2;
5642 private final int SAFE_MEDIA_VOLUME_ACTIVE = 3;
5643 private Integer mSafeMediaVolumeState;
5644
5645 private int mMcc = 0;
Eric Laurentc34dcc12012-09-10 13:51:52 -07005646 // mSafeMediaVolumeIndex is the cached value of config_safe_media_volume_index property
Eric Laurentd640bd32012-09-28 18:01:48 -07005647 private int mSafeMediaVolumeIndex;
Eric Laurentc34dcc12012-09-10 13:51:52 -07005648 // mSafeMediaVolumeDevices lists the devices for which safe media volume is enforced,
5649 private final int mSafeMediaVolumeDevices = AudioSystem.DEVICE_OUT_WIRED_HEADSET |
5650 AudioSystem.DEVICE_OUT_WIRED_HEADPHONE;
5651 // mMusicActiveMs is the cumulative time of music activity since safe volume was disabled.
5652 // When this time reaches UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX, the safe media volume is re-enabled
5653 // automatically. mMusicActiveMs is rounded to a multiple of MUSIC_ACTIVE_POLL_PERIOD_MS.
5654 private int mMusicActiveMs;
5655 private static final int UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX = (20 * 3600 * 1000); // 20 hours
5656 private static final int MUSIC_ACTIVE_POLL_PERIOD_MS = 60000; // 1 minute polling interval
Eric Laurentd640bd32012-09-28 18:01:48 -07005657 private static final int SAFE_VOLUME_CONFIGURE_TIMEOUT_MS = 30000; // 30s after boot completed
Eric Laurentc34dcc12012-09-10 13:51:52 -07005658
5659 private void setSafeMediaVolumeEnabled(boolean on) {
Eric Laurentd640bd32012-09-28 18:01:48 -07005660 synchronized (mSafeMediaVolumeState) {
5661 if ((mSafeMediaVolumeState != SAFE_MEDIA_VOLUME_NOT_CONFIGURED) &&
5662 (mSafeMediaVolumeState != SAFE_MEDIA_VOLUME_DISABLED)) {
5663 if (on && (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_INACTIVE)) {
5664 mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_ACTIVE;
5665 enforceSafeMediaVolume();
5666 } else if (!on && (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE)) {
5667 mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_INACTIVE;
5668 mMusicActiveMs = 0;
5669 sendMsg(mAudioHandler,
5670 MSG_CHECK_MUSIC_ACTIVE,
5671 SENDMSG_REPLACE,
5672 0,
5673 0,
5674 null,
5675 MUSIC_ACTIVE_POLL_PERIOD_MS);
5676 }
Eric Laurentc34dcc12012-09-10 13:51:52 -07005677 }
Eric Laurentc34dcc12012-09-10 13:51:52 -07005678 }
5679 }
5680
5681 private void enforceSafeMediaVolume() {
5682 VolumeStreamState streamState = mStreamStates[AudioSystem.STREAM_MUSIC];
5683 boolean lastAudible = (streamState.muteCount() != 0);
5684 int devices = mSafeMediaVolumeDevices;
5685 int i = 0;
5686
5687 while (devices != 0) {
5688 int device = 1 << i++;
5689 if ((device & devices) == 0) {
5690 continue;
5691 }
5692 int index = streamState.getIndex(device, lastAudible);
5693 if (index > mSafeMediaVolumeIndex) {
5694 if (lastAudible) {
5695 streamState.setLastAudibleIndex(mSafeMediaVolumeIndex, device);
5696 sendMsg(mAudioHandler,
5697 MSG_PERSIST_VOLUME,
5698 SENDMSG_QUEUE,
5699 PERSIST_LAST_AUDIBLE,
5700 device,
5701 streamState,
5702 PERSIST_DELAY);
5703 } else {
5704 streamState.setIndex(mSafeMediaVolumeIndex, device, true);
5705 sendMsg(mAudioHandler,
5706 MSG_SET_DEVICE_VOLUME,
5707 SENDMSG_QUEUE,
5708 device,
5709 0,
5710 streamState,
5711 0);
5712 }
5713 }
5714 devices &= ~device;
5715 }
5716 }
5717
5718 private boolean checkSafeMediaVolume(int streamType, int index, int device) {
Eric Laurentd640bd32012-09-28 18:01:48 -07005719 synchronized (mSafeMediaVolumeState) {
5720 if ((mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE) &&
Eric Laurentc34dcc12012-09-10 13:51:52 -07005721 (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) &&
5722 ((device & mSafeMediaVolumeDevices) != 0) &&
5723 (index > mSafeMediaVolumeIndex)) {
5724 mVolumePanel.postDisplaySafeVolumeWarning();
5725 return false;
5726 }
5727 return true;
5728 }
5729 }
5730
5731 public void disableSafeMediaVolume() {
Eric Laurentd640bd32012-09-28 18:01:48 -07005732 synchronized (mSafeMediaVolumeState) {
Eric Laurentc34dcc12012-09-10 13:51:52 -07005733 setSafeMediaVolumeEnabled(false);
5734 }
5735 }
5736
5737
Dianne Hackborn632ca412012-06-14 19:34:10 -07005738 @Override
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08005739 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkeyeb4cc4922012-04-26 18:17:29 -07005740 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
5741
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08005742 dumpFocusStack(pw);
Jean-Michel Trivid327f212010-03-16 21:44:33 -07005743 dumpRCStack(pw);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005744 dumpRCCStack(pw);
Eric Laurentbffc3d12012-05-07 17:43:49 -07005745 dumpStreamStates(pw);
Dianne Hackborn632ca412012-06-14 19:34:10 -07005746 pw.println("\nAudio routes:");
5747 pw.print(" mMainType=0x"); pw.println(Integer.toHexString(mCurAudioRoutes.mMainType));
5748 pw.print(" mBluetoothName="); pw.println(mCurAudioRoutes.mBluetoothName);
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08005749 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005750}