blob: 0df4f822393bc2dd22c39bedcc3cefa4e1c03c05 [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;
Eric Laurente78fced2013-03-15 16:03:47 -070045import android.content.res.Resources;
46import android.content.res.XmlResourceParser;
Jason Parekhb1096152009-03-24 17:48:25 -070047import android.database.ContentObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import android.media.MediaPlayer.OnCompletionListener;
49import android.media.MediaPlayer.OnErrorListener;
50import android.os.Binder;
Eric Laurentc18c9132013-04-12 17:24:56 -070051import android.os.Build;
Jean-Michel Trivif0cff042011-09-14 18:11:09 -070052import android.os.Bundle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.os.Environment;
54import android.os.Handler;
55import android.os.IBinder;
56import android.os.Looper;
57import android.os.Message;
Jean-Michel Trivic6802222012-04-30 11:15:03 -070058import android.os.PowerManager;
Dianne Hackborn632ca412012-06-14 19:34:10 -070059import android.os.RemoteCallbackList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060import android.os.RemoteException;
61import android.os.ServiceManager;
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -070062import android.os.SystemProperties;
Dianne Hackborn5ac72a22012-08-29 18:32:08 -070063import android.os.UserHandle;
Eric Laurentbffc3d12012-05-07 17:43:49 -070064import android.os.Vibrator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065import android.provider.Settings;
66import android.provider.Settings.System;
Jean-Michel Trivi3c2711f2012-05-20 12:07:03 -070067import android.speech.RecognizerIntent;
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -070068import android.telephony.PhoneStateListener;
69import android.telephony.TelephonyManager;
Dianne Hackborn632ca412012-06-14 19:34:10 -070070import android.text.TextUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071import android.util.Log;
Jean-Michel Trivid327f212010-03-16 21:44:33 -070072import android.view.KeyEvent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073import android.view.VolumePanel;
74
75import com.android.internal.telephony.ITelephony;
Eric Laurente78fced2013-03-15 16:03:47 -070076import com.android.internal.util.XmlUtils;
77
78import org.xmlpull.v1.XmlPullParserException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -080080import java.io.FileDescriptor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081import java.io.IOException;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -080082import java.io.PrintWriter;
Eric Laurente78fced2013-03-15 16:03:47 -070083import java.lang.reflect.Field;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084import java.util.ArrayList;
Eric Laurent3172d5e2012-05-09 11:38:16 -070085import java.util.concurrent.ConcurrentHashMap;
Eric Laurentc42ac9d2009-07-29 08:53:03 -070086import java.util.HashMap;
87import java.util.Iterator;
Jaikumar Ganesh5a1e4cf2010-10-18 17:05:09 -070088import java.util.List;
Eric Laurentc42ac9d2009-07-29 08:53:03 -070089import java.util.Map;
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -070090import java.util.NoSuchElementException;
Eric Laurentc42ac9d2009-07-29 08:53:03 -070091import java.util.Set;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -080092import java.util.Stack;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093
94/**
95 * The implementation of the volume manager service.
96 * <p>
97 * This implementation focuses on delivering a responsive UI. Most methods are
98 * asynchronous to external calls. For example, the task of setting a volume
99 * will update our internal state, but in a separate thread will set the system
100 * volume and later persist to the database. Similarly, setting the ringer mode
101 * will update the state and broadcast a change and in a separate thread later
102 * persist the ringer mode.
103 *
104 * @hide
105 */
Jean-Michel Trivic6802222012-04-30 11:15:03 -0700106public class AudioService extends IAudioService.Stub implements OnFinished {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107
108 private static final String TAG = "AudioService";
109
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -0700110 /** Debug remote control client/display feature */
Jean-Michel Trivi7ff866e2011-10-13 18:09:26 -0700111 protected static final boolean DEBUG_RC = false;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700112 /** Debug volumes */
113 protected static final boolean DEBUG_VOL = false;
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -0700114
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115 /** How long to delay before persisting a change in volume/ringer mode. */
RoboErik45edba12012-03-27 17:54:36 -0700116 private static final int PERSIST_DELAY = 500;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117
118 private Context mContext;
119 private ContentResolver mContentResolver;
Joe Onoratoc7fcba42011-01-05 16:53:11 -0800120 private boolean mVoiceCapable;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -0800121
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122 /** The UI */
123 private VolumePanel mVolumePanel;
124
125 // sendMsg() flags
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126 /** If the msg is already queued, replace it with this one. */
127 private static final int SENDMSG_REPLACE = 0;
128 /** If the msg is already queued, ignore this one and leave the old. */
129 private static final int SENDMSG_NOOP = 1;
130 /** If the msg is already queued, queue this one and leave the old. */
131 private static final int SENDMSG_QUEUE = 2;
132
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700133 // AudioHandler messages
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800134 private static final int MSG_SET_DEVICE_VOLUME = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 private static final int MSG_PERSIST_VOLUME = 1;
Mike Lockwood5c55a052011-12-15 17:21:44 -0500136 private static final int MSG_PERSIST_MASTER_VOLUME = 2;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 private static final int MSG_PERSIST_RINGER_MODE = 3;
Eric Laurentbffc3d12012-05-07 17:43:49 -0700138 private static final int MSG_MEDIA_SERVER_DIED = 4;
139 private static final int MSG_MEDIA_SERVER_STARTED = 5;
140 private static final int MSG_PLAY_SOUND_EFFECT = 6;
141 private static final int MSG_BTA2DP_DOCK_TIMEOUT = 7;
142 private static final int MSG_LOAD_SOUND_EFFECTS = 8;
143 private static final int MSG_SET_FORCE_USE = 9;
144 private static final int MSG_PERSIST_MEDIABUTTONRECEIVER = 10;
145 private static final int MSG_BT_HEADSET_CNCT_FAILED = 11;
146 private static final int MSG_RCDISPLAY_CLEAR = 12;
147 private static final int MSG_RCDISPLAY_UPDATE = 13;
148 private static final int MSG_SET_ALL_VOLUMES = 14;
149 private static final int MSG_PERSIST_MASTER_VOLUME_MUTE = 15;
Dianne Hackborn632ca412012-06-14 19:34:10 -0700150 private static final int MSG_REPORT_NEW_ROUTES = 16;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700151 private static final int MSG_REEVALUATE_REMOTE = 17;
Jean-Michel Trivif98de1e2012-06-20 13:40:22 -0700152 private static final int MSG_RCC_NEW_PLAYBACK_INFO = 18;
153 private static final int MSG_RCC_NEW_VOLUME_OBS = 19;
Eric Laurentc390bed2012-07-03 12:24:05 -0700154 private static final int MSG_SET_FORCE_BT_A2DP_USE = 20;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700155 // start of messages handled under wakelock
156 // these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
Jean-Michel Trivie12c39b2012-06-06 10:51:58 -0700157 // and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
Eric Laurentc390bed2012-07-03 12:24:05 -0700158 private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 21;
159 private static final int MSG_SET_A2DP_CONNECTION_STATE = 22;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700160 // end of messages handled under wakelock
Jean-Michel Trivi2ac2afe2012-08-21 11:16:55 -0700161 private static final int MSG_SET_RSX_CONNECTION_STATE = 23; // change remote submix connection
Jean-Michel Trivic203ecf2012-10-11 10:13:50 -0700162 private static final int MSG_CHECK_MUSIC_ACTIVE = 24;
163 private static final int MSG_BROADCAST_AUDIO_BECOMING_NOISY = 25;
164 private static final int MSG_CONFIGURE_SAFE_MEDIA_VOLUME = 26;
165 private static final int MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED = 27;
Eric Laurent05274f32012-11-29 12:48:18 -0800166 private static final int MSG_PERSIST_SAFE_VOLUME_STATE = 28;
Jean-Michel Trivia578c482012-12-28 11:19:49 -0800167 private static final int MSG_PROMOTE_RCC = 29;
Eric Laurent2a57ca92013-03-07 17:29:27 -0800168 private static final int MSG_BROADCAST_BT_CONNECTION_STATE = 30;
Eric Laurent5d3eb44a2013-03-21 15:35:10 -0700169 private static final int MSG_UNLOAD_SOUND_EFFECTS = 31;
Jean-Michel Trivibc43b4c2013-03-22 09:30:50 -0700170 private static final int MSG_RCC_NEW_PLAYBACK_STATE = 32;
Jean-Michel Trivi3261b532013-04-01 14:59:39 -0700171 private static final int MSG_RCC_SEEK_REQUEST = 33;
Eric Laurentafbb0472011-12-15 09:04:23 -0800172
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -0700173 private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
Eric Laurentdc03c612011-04-01 10:59:41 -0700174 // Timeout for connection to bluetooth headset service
175 private static final int BT_HEADSET_CNCT_TIMEOUT_MS = 3000;
176
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800177 /** @see AudioSystemThread */
178 private AudioSystemThread mAudioSystemThread;
179 /** @see AudioHandler */
180 private AudioHandler mAudioHandler;
181 /** @see VolumeStreamState */
182 private VolumeStreamState[] mStreamStates;
Jason Parekhb1096152009-03-24 17:48:25 -0700183 private SettingsObserver mSettingsObserver;
Eric Laurenta553c252009-07-17 12:17:14 -0700184
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800185 private int mMode;
Glenn Kastenba195eb2011-12-13 09:30:40 -0800186 // protects mRingerMode
187 private final Object mSettingsLock = new Object();
Eric Laurent45c90ce2012-04-24 18:44:22 -0700188
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800189 private boolean mMediaServerOk;
190
191 private SoundPool mSoundPool;
Glenn Kasten30c918c2011-11-10 17:56:41 -0800192 private final Object mSoundEffectsLock = new Object();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 private static final int NUM_SOUNDPOOL_CHANNELS = 4;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194
Mike Lockwood47676902011-11-08 10:31:21 -0800195 // Internally master volume is a float in the 0.0 - 1.0 range,
196 // but to support integer based AudioManager API we translate it to 0 - 100
197 private static final int MAX_MASTER_VOLUME = 100;
198
Lei Zhang6c798972012-03-02 11:40:12 -0800199 // Maximum volume adjust steps allowed in a single batch call.
200 private static final int MAX_BATCH_VOLUME_ADJUST_STEPS = 4;
201
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 /* Sound effect file names */
203 private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/";
Eric Laurente78fced2013-03-15 16:03:47 -0700204 private static final List<String> SOUND_EFFECT_FILES = new ArrayList<String>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205
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) */
Eric Laurente78fced2013-03-15 16:03:47 -0700209 private final int[][] SOUND_EFFECT_FILES_MAP = new int[AudioManager.NUM_SOUND_EFFECTS][2];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210
Jared Suttles59820132009-08-13 21:50:52 -0500211 /** @hide Maximum volume index values for audio streams */
Glenn Kasten30c918c2011-11-10 17:56:41 -0800212 private final int[] MAX_STREAM_VOLUME = new int[] {
Eric Laurent6ee99522009-08-25 06:30:59 -0700213 5, // STREAM_VOICE_CALL
214 7, // STREAM_SYSTEM
215 7, // STREAM_RING
216 15, // STREAM_MUSIC
217 7, // STREAM_ALARM
218 7, // STREAM_NOTIFICATION
219 15, // STREAM_BLUETOOTH_SCO
220 7, // STREAM_SYSTEM_ENFORCED
221 15, // STREAM_DTMF
222 15 // STREAM_TTS
Jared Suttles59820132009-08-13 21:50:52 -0500223 };
Eric Laurent6d517662012-04-23 18:42:39 -0700224 /* mStreamVolumeAlias[] indicates for each stream if it uses the volume settings
Eric Laurenta553c252009-07-17 12:17:14 -0700225 * of another stream: This avoids multiplying the volume settings for hidden
226 * stream types that follow other stream behavior for volume settings
Eric Laurent6d517662012-04-23 18:42:39 -0700227 * NOTE: do not create loops in aliases!
228 * Some streams alias to different streams according to device category (phone or tablet) or
229 * use case (in call s off call...).See updateStreamVolumeAlias() for more details
230 * mStreamVolumeAlias contains the default aliases for a voice capable device (phone) and
231 * STREAM_VOLUME_ALIAS_NON_VOICE for a non voice capable device (tablet).*/
Glenn Kasten30c918c2011-11-10 17:56:41 -0800232 private final int[] STREAM_VOLUME_ALIAS = new int[] {
Eric Laurent6d517662012-04-23 18:42:39 -0700233 AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL
234 AudioSystem.STREAM_RING, // STREAM_SYSTEM
235 AudioSystem.STREAM_RING, // STREAM_RING
236 AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
237 AudioSystem.STREAM_ALARM, // STREAM_ALARM
238 AudioSystem.STREAM_RING, // STREAM_NOTIFICATION
239 AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO
240 AudioSystem.STREAM_RING, // STREAM_SYSTEM_ENFORCED
241 AudioSystem.STREAM_RING, // STREAM_DTMF
242 AudioSystem.STREAM_MUSIC // STREAM_TTS
Eric Laurenta553c252009-07-17 12:17:14 -0700243 };
Eric Laurent6d517662012-04-23 18:42:39 -0700244 private final int[] STREAM_VOLUME_ALIAS_NON_VOICE = new int[] {
245 AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL
246 AudioSystem.STREAM_MUSIC, // STREAM_SYSTEM
247 AudioSystem.STREAM_RING, // STREAM_RING
248 AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
249 AudioSystem.STREAM_ALARM, // STREAM_ALARM
250 AudioSystem.STREAM_RING, // STREAM_NOTIFICATION
251 AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO
252 AudioSystem.STREAM_MUSIC, // STREAM_SYSTEM_ENFORCED
253 AudioSystem.STREAM_MUSIC, // STREAM_DTMF
254 AudioSystem.STREAM_MUSIC // STREAM_TTS
255 };
256 private int[] mStreamVolumeAlias;
Eric Laurenta553c252009-07-17 12:17:14 -0700257
Eric Laurent83a017b2013-03-19 18:15:31 -0700258 private final boolean mUseFixedVolume;
259
Eric Laurentbffc3d12012-05-07 17:43:49 -0700260 // stream names used by dumpStreamStates()
261 private final String[] STREAM_NAMES = new String[] {
262 "STREAM_VOICE_CALL",
263 "STREAM_SYSTEM",
264 "STREAM_RING",
265 "STREAM_MUSIC",
266 "STREAM_ALARM",
267 "STREAM_NOTIFICATION",
268 "STREAM_BLUETOOTH_SCO",
269 "STREAM_SYSTEM_ENFORCED",
270 "STREAM_DTMF",
271 "STREAM_TTS"
272 };
273
Glenn Kasten30c918c2011-11-10 17:56:41 -0800274 private final AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275 public void onError(int error) {
276 switch (error) {
277 case AudioSystem.AUDIO_STATUS_SERVER_DIED:
278 if (mMediaServerOk) {
Eric Laurentafbb0472011-12-15 09:04:23 -0800279 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SENDMSG_NOOP, 0, 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280 null, 1500);
Eric Laurent89e74ba2009-09-30 18:26:36 -0700281 mMediaServerOk = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282 }
283 break;
284 case AudioSystem.AUDIO_STATUS_OK:
285 if (!mMediaServerOk) {
Eric Laurentafbb0472011-12-15 09:04:23 -0800286 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_STARTED, SENDMSG_NOOP, 0, 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287 null, 0);
Eric Laurent89e74ba2009-09-30 18:26:36 -0700288 mMediaServerOk = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800289 }
290 break;
291 default:
292 break;
293 }
294 }
295 };
296
297 /**
298 * Current ringer mode from one of {@link AudioManager#RINGER_MODE_NORMAL},
299 * {@link AudioManager#RINGER_MODE_SILENT}, or
300 * {@link AudioManager#RINGER_MODE_VIBRATE}.
301 */
Glenn Kastenba195eb2011-12-13 09:30:40 -0800302 // protected by mSettingsLock
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303 private int mRingerMode;
304
Eric Laurent9bcf4012009-06-12 06:09:28 -0700305 /** @see System#MODE_RINGER_STREAMS_AFFECTED */
306 private int mRingerModeAffectedStreams;
307
Eric Laurent5b4e6542010-03-19 20:02:21 -0700308 // Streams currently muted by ringer mode
309 private int mRingerModeMutedStreams;
310
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800311 /** @see System#MUTE_STREAMS_AFFECTED */
312 private int mMuteAffectedStreams;
313
314 /**
Eric Laurentbffc3d12012-05-07 17:43:49 -0700315 * NOTE: setVibrateSetting(), getVibrateSetting(), shouldVibrate() are deprecated.
316 * mVibrateSetting is just maintained during deprecation period but vibration policy is
317 * now only controlled by mHasVibrator and mRingerMode
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800318 */
319 private int mVibrateSetting;
320
Eric Laurentbffc3d12012-05-07 17:43:49 -0700321 // Is there a vibrator
322 private final boolean mHasVibrator;
323
Eric Laurenta553c252009-07-17 12:17:14 -0700324 // Broadcast receiver for device connections intent broadcasts
325 private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
326
Jean-Michel Trivie73131a2010-06-14 09:53:30 -0700327 // Used to alter media button redirection when the phone is ringing.
328 private boolean mIsRinging = false;
329
Eric Laurentc42ac9d2009-07-29 08:53:03 -0700330 // Devices currently connected
Glenn Kasten30c918c2011-11-10 17:56:41 -0800331 private final HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>();
Eric Laurentc42ac9d2009-07-29 08:53:03 -0700332
333 // Forced device usage for communications
334 private int mForcedUseForComm;
335
Mike Lockwood0dc37cc2011-12-01 16:14:19 -0500336 // True if we have master volume support
337 private final boolean mUseMasterVolume;
338
Mike Lockwood97606472012-02-09 11:24:10 -0800339 private final int[] mMasterVolumeRamp;
340
Eric Laurent9272b4b2010-01-23 17:12:59 -0800341 // List of binder death handlers for setMode() client processes.
342 // The last process to have called setMode() is at the top of the list.
Glenn Kasten30c918c2011-11-10 17:56:41 -0800343 private final ArrayList <SetModeDeathHandler> mSetModeDeathHandlers = new ArrayList <SetModeDeathHandler>();
Eric Laurenteb14a782009-12-17 03:12:59 -0800344
Eric Laurent3def1ee2010-03-17 23:26:26 -0700345 // List of clients having issued a SCO start request
Glenn Kasten30c918c2011-11-10 17:56:41 -0800346 private final ArrayList <ScoClient> mScoClients = new ArrayList <ScoClient>();
Eric Laurent3def1ee2010-03-17 23:26:26 -0700347
348 // BluetoothHeadset API to control SCO connection
349 private BluetoothHeadset mBluetoothHeadset;
350
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -0700351 // Bluetooth headset device
352 private BluetoothDevice mBluetoothHeadsetDevice;
Eric Laurent3def1ee2010-03-17 23:26:26 -0700353
Eric Laurent62ef7672010-11-24 10:58:32 -0800354 // Indicate if SCO audio connection is currently active and if the initiator is
355 // audio service (internal) or bluetooth headset (external)
356 private int mScoAudioState;
357 // SCO audio state is not active
358 private static final int SCO_STATE_INACTIVE = 0;
Eric Laurentdc03c612011-04-01 10:59:41 -0700359 // SCO audio activation request waiting for headset service to connect
360 private static final int SCO_STATE_ACTIVATE_REQ = 1;
Eric Laurent25fc29b2013-04-05 12:13:54 -0700361 // SCO audio state is active or starting due to a request from AudioManager API
Eric Laurentdc03c612011-04-01 10:59:41 -0700362 private static final int SCO_STATE_ACTIVE_INTERNAL = 3;
363 // SCO audio deactivation request waiting for headset service to connect
364 private static final int SCO_STATE_DEACTIVATE_REQ = 5;
365
Eric Laurent62ef7672010-11-24 10:58:32 -0800366 // SCO audio state is active due to an action in BT handsfree (either voice recognition or
367 // in call audio)
368 private static final int SCO_STATE_ACTIVE_EXTERNAL = 2;
Eric Laurentdc03c612011-04-01 10:59:41 -0700369 // Deactivation request for all SCO connections (initiated by audio mode change)
370 // waiting for headset service to connect
371 private static final int SCO_STATE_DEACTIVATE_EXT_REQ = 4;
372
Eric Laurentc18c9132013-04-12 17:24:56 -0700373 // Indicates the mode used for SCO audio connection. The mode is virtual call if the request
374 // originated from an app targeting an API version before JB MR2 and raw audio after that.
375 private int mScoAudioMode;
376 // SCO audio mode is virtual voice call (BluetoothHeadset.startScoUsingVirtualVoiceCall())
377 private static final int SCO_MODE_VIRTUAL_CALL = 0;
378 // SCO audio mode is raw audio (BluetoothHeadset.connectAudio())
379 private static final int SCO_MODE_RAW = 1;
380
Eric Laurentdc03c612011-04-01 10:59:41 -0700381 // 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 |
Eric Laurent7ee1e4f2012-10-26 18:11:21 -0700433 AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET |
Eric Laurent4bbcc652012-09-24 14:26:30 -0700434 AudioSystem.DEVICE_OUT_ALL_USB;
435
Eric Laurentd640bd32012-09-28 18:01:48 -0700436 private final boolean mMonitorOrientation;
437
Eric Laurent7ee1e4f2012-10-26 18:11:21 -0700438 private boolean mDockAudioMediaEnabled = true;
439
Eric Laurent08ed1b92012-11-05 14:54:12 -0800440 private int mDockState = Intent.EXTRA_DOCK_STATE_UNDOCKED;
441
Eric Laurentfde16d52012-12-03 14:42:39 -0800442 // Used when safe volume warning message display is requested by setStreamVolume(). In this
443 // case, the new requested volume, stream type and device are stored in mPendingVolumeCommand
444 // and used later when/if disableSafeMediaVolume() is called.
445 private StreamVolumeCommand mPendingVolumeCommand;
446
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800447 ///////////////////////////////////////////////////////////////////////////
448 // Construction
449 ///////////////////////////////////////////////////////////////////////////
450
451 /** @hide */
452 public AudioService(Context context) {
453 mContext = context;
454 mContentResolver = context.getContentResolver();
Joe Onoratoc7fcba42011-01-05 16:53:11 -0800455 mVoiceCapable = mContext.getResources().getBoolean(
456 com.android.internal.R.bool.config_voice_capable);
Jared Suttles59820132009-08-13 21:50:52 -0500457
Jean-Michel Trivic6802222012-04-30 11:15:03 -0700458 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
Jean-Michel Trivi2d8dab52012-05-30 18:13:59 -0700459 mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
Jean-Michel Trivic6802222012-04-30 11:15:03 -0700460
Eric Laurentbffc3d12012-05-07 17:43:49 -0700461 Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
462 mHasVibrator = vibrator == null ? false : vibrator.hasVibrator();
463
Jared Suttles59820132009-08-13 21:50:52 -0500464 // Intialized volume
465 MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = SystemProperties.getInt(
466 "ro.config.vc_call_vol_steps",
467 MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL]);
468
Jean-Michel Trivif2b0c112012-07-09 11:59:11 -0700469 sSoundEffectVolumeDb = context.getResources().getInteger(
Jean-Michel Trivic55b3932012-06-05 11:57:59 -0700470 com.android.internal.R.integer.config_soundEffectVolumeDb);
Eric Laurent25101b02011-02-02 09:33:30 -0800471
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800472 mVolumePanel = new VolumePanel(context, this);
Eric Laurent6d517662012-04-23 18:42:39 -0700473 mMode = AudioSystem.MODE_NORMAL;
Eric Laurentc42ac9d2009-07-29 08:53:03 -0700474 mForcedUseForComm = AudioSystem.FORCE_NONE;
Eric Laurentdd45d012012-10-08 09:04:34 -0700475
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 createAudioSystemThread();
Eric Laurentdd45d012012-10-08 09:04:34 -0700477
478 boolean cameraSoundForced = mContext.getResources().getBoolean(
479 com.android.internal.R.bool.config_camera_sound_forced);
480 mCameraSoundForced = new Boolean(cameraSoundForced);
481 sendMsg(mAudioHandler,
482 MSG_SET_FORCE_USE,
483 SENDMSG_QUEUE,
484 AudioSystem.FOR_SYSTEM,
485 cameraSoundForced ?
486 AudioSystem.FORCE_SYSTEM_ENFORCED : AudioSystem.FORCE_NONE,
487 null,
488 0);
489
Eric Laurent05274f32012-11-29 12:48:18 -0800490 mSafeMediaVolumeState = new Integer(Settings.Global.getInt(mContentResolver,
491 Settings.Global.AUDIO_SAFE_VOLUME_STATE,
492 SAFE_MEDIA_VOLUME_NOT_CONFIGURED));
493 // The default safe volume index read here will be replaced by the actual value when
494 // the mcc is read by onConfigureSafeVolume()
495 mSafeMediaVolumeIndex = mContext.getResources().getInteger(
496 com.android.internal.R.integer.config_safe_media_volume_index) * 10;
497
Eric Laurent83a017b2013-03-19 18:15:31 -0700498 mUseFixedVolume = mContext.getResources().getBoolean(
499 com.android.internal.R.bool.config_useFixedVolume);
500
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800501 readPersistedSettings();
Eric Laurentc1d41662011-07-19 11:21:13 -0700502 mSettingsObserver = new SettingsObserver();
Eric Laurent6d517662012-04-23 18:42:39 -0700503 updateStreamVolumeAlias(false /*updateVolumes*/);
Eric Laurenta553c252009-07-17 12:17:14 -0700504 createStreamStates();
Eric Laurent9f103de2011-09-08 15:04:23 -0700505
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800506 mMediaServerOk = true;
Eric Laurent3891c4c2010-04-20 09:40:57 -0700507
508 // Call setRingerModeInt() to apply correct mute
509 // state on streams affected by ringer mode.
510 mRingerModeMutedStreams = 0;
511 setRingerModeInt(getRingerMode(), false);
512
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800513 AudioSystem.setErrorCallback(mAudioSystemCallback);
Eric Laurenta553c252009-07-17 12:17:14 -0700514
515 // Register for device connection intent broadcasts.
516 IntentFilter intentFilter =
Eric Laurentb1fbaac2012-05-29 09:24:28 -0700517 new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -0700518 intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
519 intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
Eric Laurent59f48272012-04-05 19:42:21 -0700520 intentFilter.addAction(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG);
521 intentFilter.addAction(Intent.ACTION_USB_AUDIO_DEVICE_PLUG);
Eric Laurent62ef7672010-11-24 10:58:32 -0800522 intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
Eric Laurent950e8cb2011-10-13 08:57:54 -0700523 intentFilter.addAction(Intent.ACTION_SCREEN_ON);
524 intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
Eric Laurentbc0fab1f2012-09-19 11:24:41 -0700525 intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
Jean-Michel Trivif26f0172012-04-25 16:23:20 -0700526
Eric Laurentd640bd32012-09-28 18:01:48 -0700527 intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
Jean-Michel Trivif26f0172012-04-25 16:23:20 -0700528 // Register a configuration change listener only if requested by system properties
529 // to monitor orientation changes (off by default)
Eric Laurentd640bd32012-09-28 18:01:48 -0700530 mMonitorOrientation = SystemProperties.getBoolean("ro.audio.monitorOrientation", false);
531 if (mMonitorOrientation) {
Jean-Michel Trivif26f0172012-04-25 16:23:20 -0700532 Log.v(TAG, "monitoring device orientation");
Jean-Michel Trivif26f0172012-04-25 16:23:20 -0700533 // initialize orientation in AudioSystem
534 setOrientationForAudioSystem();
535 }
536
Eric Laurenta553c252009-07-17 12:17:14 -0700537 context.registerReceiver(mReceiver, intentFilter);
Jared Suttles59820132009-08-13 21:50:52 -0500538
Jean-Michel Trivid589fea2011-04-15 11:28:10 -0700539 // Register for package removal intent broadcasts for media button receiver persistence
540 IntentFilter pkgFilter = new IntentFilter();
541 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
Dianne Hackborn79f7ec72013-04-04 18:50:23 -0700542 pkgFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
543 pkgFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
544 pkgFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
Jean-Michel Trivid589fea2011-04-15 11:28:10 -0700545 pkgFilter.addDataScheme("package");
546 context.registerReceiver(mReceiver, pkgFilter);
547
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -0700548 // Register for phone state monitoring
549 TelephonyManager tmgr = (TelephonyManager)
550 context.getSystemService(Context.TELEPHONY_SERVICE);
551 tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
Mike Lockwood8517e462011-10-25 14:47:19 -0400552
Mike Lockwood0dc37cc2011-12-01 16:14:19 -0500553 mUseMasterVolume = context.getResources().getBoolean(
554 com.android.internal.R.bool.config_useMasterVolume);
Mike Lockwood90631542012-01-06 11:20:37 -0500555 restoreMasterVolume();
Mike Lockwood97606472012-02-09 11:24:10 -0800556
557 mMasterVolumeRamp = context.getResources().getIntArray(
558 com.android.internal.R.array.config_masterVolumeRamp);
Eric Laurent78472112012-05-21 08:57:21 -0700559
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700560 mMainRemote = new RemotePlaybackState(-1, MAX_STREAM_VOLUME[AudioManager.STREAM_MUSIC],
561 MAX_STREAM_VOLUME[AudioManager.STREAM_MUSIC]);
562 mHasRemotePlayback = false;
563 mMainRemoteIsActive = false;
564 postReevaluateRemote();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800565 }
566
567 private void createAudioSystemThread() {
568 mAudioSystemThread = new AudioSystemThread();
569 mAudioSystemThread.start();
570 waitForAudioHandlerCreation();
571 }
572
573 /** Waits for the volume handler to be created by the other thread. */
574 private void waitForAudioHandlerCreation() {
575 synchronized(this) {
576 while (mAudioHandler == null) {
577 try {
578 // Wait for mAudioHandler to be set by the other thread
579 wait();
580 } catch (InterruptedException e) {
581 Log.e(TAG, "Interrupted while waiting on volume handler.");
582 }
583 }
584 }
585 }
586
Eric Laurent24482012012-05-10 09:41:17 -0700587 private void checkAllAliasStreamVolumes() {
588 int numStreamTypes = AudioSystem.getNumStreamTypes();
589 for (int streamType = 0; streamType < numStreamTypes; streamType++) {
590 if (streamType != mStreamVolumeAlias[streamType]) {
591 mStreamStates[streamType].
Eric Laurent42b041e2013-03-29 11:36:03 -0700592 setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]]);
Eric Laurent24482012012-05-10 09:41:17 -0700593 }
594 // apply stream volume
Eric Laurent42b041e2013-03-29 11:36:03 -0700595 if (!mStreamStates[streamType].isMuted()) {
Eric Laurent24482012012-05-10 09:41:17 -0700596 mStreamStates[streamType].applyAllVolumes();
597 }
598 }
599 }
600
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800601 private void createStreamStates() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800602 int numStreamTypes = AudioSystem.getNumStreamTypes();
603 VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
604
605 for (int i = 0; i < numStreamTypes; i++) {
Eric Laurent6d517662012-04-23 18:42:39 -0700606 streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[mStreamVolumeAlias[i]], i);
Eric Laurenta553c252009-07-17 12:17:14 -0700607 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800608
Eric Laurent24482012012-05-10 09:41:17 -0700609 checkAllAliasStreamVolumes();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800610 }
611
Eric Laurentbffc3d12012-05-07 17:43:49 -0700612 private void dumpStreamStates(PrintWriter pw) {
613 pw.println("\nStream volumes (device: index)");
614 int numStreamTypes = AudioSystem.getNumStreamTypes();
615 for (int i = 0; i < numStreamTypes; i++) {
616 pw.println("- "+STREAM_NAMES[i]+":");
617 mStreamStates[i].dump(pw);
618 pw.println("");
619 }
Eric Laurentdd45d012012-10-08 09:04:34 -0700620 pw.print("\n- mute affected streams = 0x");
621 pw.println(Integer.toHexString(mMuteAffectedStreams));
Eric Laurentbffc3d12012-05-07 17:43:49 -0700622 }
623
Eric Laurent6d517662012-04-23 18:42:39 -0700624
625 private void updateStreamVolumeAlias(boolean updateVolumes) {
626 int dtmfStreamAlias;
627 if (mVoiceCapable) {
628 mStreamVolumeAlias = STREAM_VOLUME_ALIAS;
629 dtmfStreamAlias = AudioSystem.STREAM_RING;
630 } else {
631 mStreamVolumeAlias = STREAM_VOLUME_ALIAS_NON_VOICE;
632 dtmfStreamAlias = AudioSystem.STREAM_MUSIC;
633 }
634 if (isInCommunication()) {
635 dtmfStreamAlias = AudioSystem.STREAM_VOICE_CALL;
636 }
637 mStreamVolumeAlias[AudioSystem.STREAM_DTMF] = dtmfStreamAlias;
638 if (updateVolumes) {
Eric Laurent42b041e2013-03-29 11:36:03 -0700639 mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias]);
Eric Laurent6d517662012-04-23 18:42:39 -0700640 sendMsg(mAudioHandler,
641 MSG_SET_ALL_VOLUMES,
642 SENDMSG_QUEUE,
643 0,
644 0,
645 mStreamStates[AudioSystem.STREAM_DTMF], 0);
646 }
647 }
648
Eric Laurent7ee1e4f2012-10-26 18:11:21 -0700649 private void readDockAudioSettings(ContentResolver cr)
650 {
651 mDockAudioMediaEnabled = Settings.Global.getInt(
Eric Laurent5ba0ffa02012-10-29 12:31:09 -0700652 cr, Settings.Global.DOCK_AUDIO_MEDIA_ENABLED, 0) == 1;
Eric Laurent7ee1e4f2012-10-26 18:11:21 -0700653
654 if (mDockAudioMediaEnabled) {
655 mBecomingNoisyIntentDevices |= AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET;
656 } else {
657 mBecomingNoisyIntentDevices &= ~AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET;
658 }
659
660 sendMsg(mAudioHandler,
661 MSG_SET_FORCE_USE,
662 SENDMSG_QUEUE,
663 AudioSystem.FOR_DOCK,
664 mDockAudioMediaEnabled ?
665 AudioSystem.FORCE_ANALOG_DOCK : AudioSystem.FORCE_NONE,
666 null,
667 0);
668 }
669
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800670 private void readPersistedSettings() {
671 final ContentResolver cr = mContentResolver;
672
Eric Laurentbffc3d12012-05-07 17:43:49 -0700673 int ringerModeFromSettings =
Jeff Sharkey8d9a1f62012-10-18 15:38:14 -0700674 Settings.Global.getInt(
675 cr, Settings.Global.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);
Eric Laurentbffc3d12012-05-07 17:43:49 -0700676 int ringerMode = ringerModeFromSettings;
Eric Laurent72668b22011-07-19 16:04:27 -0700677 // sanity check in case the settings are restored from a device with incompatible
678 // ringer modes
Glenn Kastenba195eb2011-12-13 09:30:40 -0800679 if (!AudioManager.isValidRingerMode(ringerMode)) {
680 ringerMode = AudioManager.RINGER_MODE_NORMAL;
Eric Laurentbffc3d12012-05-07 17:43:49 -0700681 }
682 if ((ringerMode == AudioManager.RINGER_MODE_VIBRATE) && !mHasVibrator) {
683 ringerMode = AudioManager.RINGER_MODE_SILENT;
684 }
685 if (ringerMode != ringerModeFromSettings) {
Jeff Sharkey8d9a1f62012-10-18 15:38:14 -0700686 Settings.Global.putInt(cr, Settings.Global.MODE_RINGER, ringerMode);
Glenn Kastenba195eb2011-12-13 09:30:40 -0800687 }
Eric Laurent83a017b2013-03-19 18:15:31 -0700688 if (mUseFixedVolume) {
689 ringerMode = AudioManager.RINGER_MODE_NORMAL;
690 }
Glenn Kastenba195eb2011-12-13 09:30:40 -0800691 synchronized(mSettingsLock) {
692 mRingerMode = ringerMode;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800693
Eric Laurentdd45d012012-10-08 09:04:34 -0700694 // System.VIBRATE_ON is not used any more but defaults for mVibrateSetting
695 // are still needed while setVibrateSetting() and getVibrateSetting() are being
696 // deprecated.
697 mVibrateSetting = getValueForVibrateSetting(0,
698 AudioManager.VIBRATE_TYPE_NOTIFICATION,
699 mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT
700 : AudioManager.VIBRATE_SETTING_OFF);
701 mVibrateSetting = getValueForVibrateSetting(mVibrateSetting,
702 AudioManager.VIBRATE_TYPE_RINGER,
703 mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT
704 : AudioManager.VIBRATE_SETTING_OFF);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800705
Eric Laurentdd45d012012-10-08 09:04:34 -0700706 // make sure settings for ringer mode are consistent with device type: non voice capable
707 // devices (tablets) include media stream in silent mode whereas phones don't.
708 mRingerModeAffectedStreams = Settings.System.getIntForUser(cr,
709 Settings.System.MODE_RINGER_STREAMS_AFFECTED,
710 ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
711 (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)),
712 UserHandle.USER_CURRENT);
Eric Laurentd799db42012-10-23 20:14:27 -0700713
714 // ringtone, notification and system streams are always affected by ringer mode
715 mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_RING)|
716 (1 << AudioSystem.STREAM_NOTIFICATION)|
717 (1 << AudioSystem.STREAM_SYSTEM);
718
Eric Laurentdd45d012012-10-08 09:04:34 -0700719 if (mVoiceCapable) {
720 mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
721 } else {
722 mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
723 }
724 synchronized (mCameraSoundForced) {
725 if (mCameraSoundForced) {
726 mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
727 } else {
728 mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
729 }
730 }
731
732 Settings.System.putIntForUser(cr,
733 Settings.System.MODE_RINGER_STREAMS_AFFECTED,
734 mRingerModeAffectedStreams,
735 UserHandle.USER_CURRENT);
Eric Laurent7ee1e4f2012-10-26 18:11:21 -0700736
737 readDockAudioSettings(cr);
Eric Laurent402f7f22011-02-04 12:30:32 -0800738 }
Eric Laurentc1d41662011-07-19 11:21:13 -0700739
Eric Laurentbc0fab1f2012-09-19 11:24:41 -0700740 mMuteAffectedStreams = System.getIntForUser(cr,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800741 System.MUTE_STREAMS_AFFECTED,
Eric Laurentbc0fab1f2012-09-19 11:24:41 -0700742 ((1 << AudioSystem.STREAM_MUSIC)|
743 (1 << AudioSystem.STREAM_RING)|
744 (1 << AudioSystem.STREAM_SYSTEM)),
745 UserHandle.USER_CURRENT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800746
Eric Laurentbc0fab1f2012-09-19 11:24:41 -0700747 boolean masterMute = System.getIntForUser(cr, System.VOLUME_MASTER_MUTE,
748 0, UserHandle.USER_CURRENT) == 1;
Eric Laurent83a017b2013-03-19 18:15:31 -0700749 if (mUseFixedVolume) {
750 masterMute = false;
751 AudioSystem.setMasterVolume(1.0f);
752 }
Justin Koh57978ed2012-04-03 17:37:58 -0700753 AudioSystem.setMasterMute(masterMute);
754 broadcastMasterMuteStatus(masterMute);
755
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800756 // Each stream will read its own persisted settings
757
758 // Broadcast the sticky intent
Glenn Kastenba195eb2011-12-13 09:30:40 -0800759 broadcastRingerMode(ringerMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800760
761 // Broadcast vibrate settings
762 broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
763 broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION);
Jean-Michel Trivid589fea2011-04-15 11:28:10 -0700764
765 // Restore the default media button receiver from the system settings
766 restoreMediaButtonReceiver();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800767 }
768
Eric Laurenta553c252009-07-17 12:17:14 -0700769 private int rescaleIndex(int index, int srcStream, int dstStream) {
770 return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex();
771 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800772
773 ///////////////////////////////////////////////////////////////////////////
774 // IPC methods
775 ///////////////////////////////////////////////////////////////////////////
776
777 /** @see AudioManager#adjustVolume(int, int) */
778 public void adjustVolume(int direction, int flags) {
779 adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags);
780 }
781
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700782 /** @see AudioManager#adjustLocalOrRemoteStreamVolume(int, int) with current assumption
783 * on streamType: fixed to STREAM_MUSIC */
784 public void adjustLocalOrRemoteStreamVolume(int streamType, int direction) {
785 if (DEBUG_VOL) Log.d(TAG, "adjustLocalOrRemoteStreamVolume(dir="+direction+")");
786 if (checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
787 adjustRemoteVolume(AudioSystem.STREAM_MUSIC, direction, 0);
788 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
789 adjustStreamVolume(AudioSystem.STREAM_MUSIC, direction, 0);
790 }
791 }
792
Dianne Hackborn961cae92013-03-20 14:59:43 -0700793 /** @see AudioManager#adjustVolume(int, int) */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800794 public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700795 if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream="+suggestedStreamType);
Eric Laurent402f7f22011-02-04 12:30:32 -0800796 int streamType;
Eric Laurent45c90ce2012-04-24 18:44:22 -0700797 if (mVolumeControlStream != -1) {
798 streamType = mVolumeControlStream;
Eric Laurent402f7f22011-02-04 12:30:32 -0800799 } else {
800 streamType = getActiveStreamType(suggestedStreamType);
801 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800802
Amith Yamasani6243edd2011-12-05 19:58:48 -0800803 // Play sounds on STREAM_RING only and if lock screen is not on.
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700804 if ((streamType != STREAM_REMOTE_MUSIC) &&
805 (flags & AudioManager.FLAG_PLAY_SOUND) != 0 &&
Eric Laurent6d517662012-04-23 18:42:39 -0700806 ((mStreamVolumeAlias[streamType] != AudioSystem.STREAM_RING)
Amith Yamasani6243edd2011-12-05 19:58:48 -0800807 || (mKeyguardManager != null && mKeyguardManager.isKeyguardLocked()))) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800808 flags &= ~AudioManager.FLAG_PLAY_SOUND;
809 }
810
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700811 if (streamType == STREAM_REMOTE_MUSIC) {
812 // don't play sounds for remote
Eric Laurent799448b2012-09-25 14:33:56 -0700813 flags &= ~(AudioManager.FLAG_PLAY_SOUND|AudioManager.FLAG_FIXED_VOLUME);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700814 //if (DEBUG_VOL) Log.i(TAG, "Need to adjust remote volume: calling adjustRemoteVolume()");
815 adjustRemoteVolume(AudioSystem.STREAM_MUSIC, direction, flags);
816 } else {
817 adjustStreamVolume(streamType, direction, flags);
818 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800819 }
820
821 /** @see AudioManager#adjustStreamVolume(int, int, int) */
822 public void adjustStreamVolume(int streamType, int direction, int flags) {
Eric Laurent83a017b2013-03-19 18:15:31 -0700823 if (mUseFixedVolume) {
824 return;
825 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700826 if (DEBUG_VOL) Log.d(TAG, "adjustStreamVolume() stream="+streamType+", dir="+direction);
827
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800828 ensureValidDirection(direction);
829 ensureValidStreamType(streamType);
830
Eric Laurent96a33d12011-11-08 10:31:57 -0800831 // use stream type alias here so that streams with same alias have the same behavior,
832 // including with regard to silent mode control (e.g the use of STREAM_RING below and in
833 // checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION)
Eric Laurent6d517662012-04-23 18:42:39 -0700834 int streamTypeAlias = mStreamVolumeAlias[streamType];
Eric Laurentb024c302011-10-14 17:19:27 -0700835 VolumeStreamState streamState = mStreamStates[streamTypeAlias];
Eric Laurent9bc8358d2011-11-18 16:43:31 -0800836
837 final int device = getDeviceForStream(streamTypeAlias);
Eric Laurent3ef75492012-11-28 12:12:23 -0800838
Eric Laurent42b041e2013-03-29 11:36:03 -0700839 int aliasIndex = streamState.getIndex(device);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800840 boolean adjustVolume = true;
Eric Laurent3ef75492012-11-28 12:12:23 -0800841 int step;
Eric Laurent24482012012-05-10 09:41:17 -0700842
Eric Laurentfde16d52012-12-03 14:42:39 -0800843 // reset any pending volume command
844 synchronized (mSafeMediaVolumeState) {
845 mPendingVolumeCommand = null;
846 }
847
Eric Laurent3ef75492012-11-28 12:12:23 -0800848 flags &= ~AudioManager.FLAG_FIXED_VOLUME;
849 if ((streamTypeAlias == AudioSystem.STREAM_MUSIC) &&
850 ((device & mFixedVolumeDevices) != 0)) {
851 flags |= AudioManager.FLAG_FIXED_VOLUME;
852
853 // Always toggle between max safe volume and 0 for fixed volume devices where safe
854 // volume is enforced, and max and 0 for the others.
855 // This is simulated by stepping by the full allowed volume range
856 if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE &&
857 (device & mSafeMediaVolumeDevices) != 0) {
858 step = mSafeMediaVolumeIndex;
859 } else {
860 step = streamState.getMaxIndex();
861 }
862 if (aliasIndex != 0) {
863 aliasIndex = step;
864 }
865 } else {
866 // convert one UI step (+/-1) into a number of internal units on the stream alias
867 step = rescaleIndex(10, streamType, streamTypeAlias);
868 }
869
Eric Laurent42b041e2013-03-29 11:36:03 -0700870 // If either the client forces allowing ringer modes for this adjustment,
871 // or the stream type is one that is affected by ringer modes
872 if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
873 (streamTypeAlias == getMasterStreamType())) {
874 int ringerMode = getRingerMode();
875 // do not vibrate if already in vibrate mode
876 if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
877 flags &= ~AudioManager.FLAG_VIBRATE;
Eric Laurent3ef75492012-11-28 12:12:23 -0800878 }
Eric Laurent42b041e2013-03-29 11:36:03 -0700879 // Check if the ringer mode changes with this volume adjustment. If
880 // it does, it will handle adjusting the volume, so we won't below
881 adjustVolume = checkForRingerModeChange(aliasIndex, direction, step);
882 }
Eric Laurent3ef75492012-11-28 12:12:23 -0800883
Eric Laurent42b041e2013-03-29 11:36:03 -0700884 int oldIndex = mStreamStates[streamType].getIndex(device);
Eric Laurent3ef75492012-11-28 12:12:23 -0800885
Eric Laurent42b041e2013-03-29 11:36:03 -0700886 if (adjustVolume && (direction != AudioManager.ADJUST_SAME)) {
887 if ((direction == AudioManager.ADJUST_RAISE) &&
888 !checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) {
889 Log.e(TAG, "adjustStreamVolume() safe volume index = "+oldIndex);
890 mVolumePanel.postDisplaySafeVolumeWarning(flags);
891 } else if (streamState.adjustIndex(direction * step, device)) {
892 // Post message to set system volume (it in turn will post a message
893 // to persist). Do not change volume if stream is muted.
894 sendMsg(mAudioHandler,
895 MSG_SET_DEVICE_VOLUME,
896 SENDMSG_QUEUE,
897 device,
898 0,
899 streamState,
900 0);
Eric Laurent4bbcc652012-09-24 14:26:30 -0700901 }
902 }
Eric Laurent42b041e2013-03-29 11:36:03 -0700903 int index = mStreamStates[streamType].getIndex(device);
Eric Laurent25101b02011-02-02 09:33:30 -0800904 sendVolumeUpdate(streamType, oldIndex, index, flags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800905 }
906
Dianne Hackborn961cae92013-03-20 14:59:43 -0700907 /** @see AudioManager#adjustMasterVolume(int, int) */
Lei Zhang6c798972012-03-02 11:40:12 -0800908 public void adjustMasterVolume(int steps, int flags) {
Eric Laurent83a017b2013-03-19 18:15:31 -0700909 if (mUseFixedVolume) {
910 return;
911 }
Lei Zhang6c798972012-03-02 11:40:12 -0800912 ensureValidSteps(steps);
Mike Lockwood97606472012-02-09 11:24:10 -0800913 int volume = Math.round(AudioSystem.getMasterVolume() * MAX_MASTER_VOLUME);
914 int delta = 0;
Lei Zhang6c798972012-03-02 11:40:12 -0800915 int numSteps = Math.abs(steps);
916 int direction = steps > 0 ? AudioManager.ADJUST_RAISE : AudioManager.ADJUST_LOWER;
917 for (int i = 0; i < numSteps; ++i) {
918 delta = findVolumeDelta(direction, volume);
919 volume += delta;
Mike Lockwoodcbdb49d2011-10-20 12:54:05 -0400920 }
RoboErik24b082f2012-02-24 14:21:16 -0800921
Lei Zhang6c798972012-03-02 11:40:12 -0800922 //Log.d(TAG, "adjustMasterVolume volume: " + volume + " steps: " + steps);
923 setMasterVolume(volume, flags);
Mike Lockwoodcbdb49d2011-10-20 12:54:05 -0400924 }
925
Eric Laurentfde16d52012-12-03 14:42:39 -0800926 // StreamVolumeCommand contains the information needed to defer the process of
927 // setStreamVolume() in case the user has to acknowledge the safe volume warning message.
928 class StreamVolumeCommand {
929 public final int mStreamType;
930 public final int mIndex;
931 public final int mFlags;
932 public final int mDevice;
Eric Laurent9ce379a2010-02-16 06:00:26 -0800933
Eric Laurentfde16d52012-12-03 14:42:39 -0800934 StreamVolumeCommand(int streamType, int index, int flags, int device) {
935 mStreamType = streamType;
936 mIndex = index;
937 mFlags = flags;
938 mDevice = device;
Eric Laurentb024c302011-10-14 17:19:27 -0700939 }
Eric Laurentfde16d52012-12-03 14:42:39 -0800940 };
Eric Laurent3ef75492012-11-28 12:12:23 -0800941
Eric Laurentfde16d52012-12-03 14:42:39 -0800942 private void onSetStreamVolume(int streamType, int index, int flags, int device) {
Eric Laurent42b041e2013-03-29 11:36:03 -0700943 setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, false);
Eric Laurent3ef75492012-11-28 12:12:23 -0800944 // setting volume on master stream type also controls silent mode
945 if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
946 (mStreamVolumeAlias[streamType] == getMasterStreamType())) {
947 int newRingerMode;
948 if (index == 0) {
949 newRingerMode = mHasVibrator ? AudioManager.RINGER_MODE_VIBRATE
950 : AudioManager.RINGER_MODE_SILENT;
Eric Laurent3ef75492012-11-28 12:12:23 -0800951 } else {
952 newRingerMode = AudioManager.RINGER_MODE_NORMAL;
953 }
954 setRingerMode(newRingerMode);
955 }
Eric Laurentfde16d52012-12-03 14:42:39 -0800956 }
957
958 /** @see AudioManager#setStreamVolume(int, int, int) */
959 public void setStreamVolume(int streamType, int index, int flags) {
Eric Laurent83a017b2013-03-19 18:15:31 -0700960 if (mUseFixedVolume) {
961 return;
962 }
963
Eric Laurentfde16d52012-12-03 14:42:39 -0800964 ensureValidStreamType(streamType);
965 VolumeStreamState streamState = mStreamStates[mStreamVolumeAlias[streamType]];
966
967 final int device = getDeviceForStream(streamType);
968 int oldIndex;
969
970 synchronized (mSafeMediaVolumeState) {
971 // reset any pending volume command
972 mPendingVolumeCommand = null;
973
Eric Laurent42b041e2013-03-29 11:36:03 -0700974 oldIndex = streamState.getIndex(device);
Eric Laurentfde16d52012-12-03 14:42:39 -0800975
976 index = rescaleIndex(index * 10, streamType, mStreamVolumeAlias[streamType]);
977
978 flags &= ~AudioManager.FLAG_FIXED_VOLUME;
979 if ((mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) &&
980 ((device & mFixedVolumeDevices) != 0)) {
981 flags |= AudioManager.FLAG_FIXED_VOLUME;
982
983 // volume is either 0 or max allowed for fixed volume devices
984 if (index != 0) {
985 if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE &&
986 (device & mSafeMediaVolumeDevices) != 0) {
987 index = mSafeMediaVolumeIndex;
988 } else {
989 index = streamState.getMaxIndex();
990 }
991 }
992 }
993
994 if (!checkSafeMediaVolume(mStreamVolumeAlias[streamType], index, device)) {
995 mVolumePanel.postDisplaySafeVolumeWarning(flags);
996 mPendingVolumeCommand = new StreamVolumeCommand(
997 streamType, index, flags, device);
998 } else {
999 onSetStreamVolume(streamType, index, flags, device);
Eric Laurent42b041e2013-03-29 11:36:03 -07001000 index = mStreamStates[streamType].getIndex(device);
Eric Laurentfde16d52012-12-03 14:42:39 -08001001 }
1002 }
Eric Laurent25101b02011-02-02 09:33:30 -08001003 sendVolumeUpdate(streamType, oldIndex, index, flags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001004 }
1005
Eric Laurent45c90ce2012-04-24 18:44:22 -07001006 /** @see AudioManager#forceVolumeControlStream(int) */
1007 public void forceVolumeControlStream(int streamType, IBinder cb) {
1008 synchronized(mForceControlStreamLock) {
1009 mVolumeControlStream = streamType;
1010 if (mVolumeControlStream == -1) {
1011 if (mForceControlStreamClient != null) {
1012 mForceControlStreamClient.release();
1013 mForceControlStreamClient = null;
1014 }
1015 } else {
1016 mForceControlStreamClient = new ForceControlStreamClient(cb);
1017 }
1018 }
1019 }
1020
1021 private class ForceControlStreamClient implements IBinder.DeathRecipient {
1022 private IBinder mCb; // To be notified of client's death
1023
1024 ForceControlStreamClient(IBinder cb) {
1025 if (cb != null) {
1026 try {
1027 cb.linkToDeath(this, 0);
1028 } catch (RemoteException e) {
1029 // Client has died!
1030 Log.w(TAG, "ForceControlStreamClient() could not link to "+cb+" binder death");
1031 cb = null;
1032 }
1033 }
1034 mCb = cb;
1035 }
1036
1037 public void binderDied() {
1038 synchronized(mForceControlStreamLock) {
1039 Log.w(TAG, "SCO client died");
1040 if (mForceControlStreamClient != this) {
1041 Log.w(TAG, "unregistered control stream client died");
1042 } else {
1043 mForceControlStreamClient = null;
1044 mVolumeControlStream = -1;
1045 }
1046 }
1047 }
1048
1049 public void release() {
1050 if (mCb != null) {
1051 mCb.unlinkToDeath(this, 0);
1052 mCb = null;
1053 }
1054 }
1055 }
1056
Lei Zhang6c798972012-03-02 11:40:12 -08001057 private int findVolumeDelta(int direction, int volume) {
1058 int delta = 0;
1059 if (direction == AudioManager.ADJUST_RAISE) {
1060 if (volume == MAX_MASTER_VOLUME) {
1061 return 0;
1062 }
1063 // This is the default value if we make it to the end
1064 delta = mMasterVolumeRamp[1];
1065 // If we're raising the volume move down the ramp array until we
1066 // find the volume we're above and use that groups delta.
1067 for (int i = mMasterVolumeRamp.length - 1; i > 1; i -= 2) {
1068 if (volume >= mMasterVolumeRamp[i - 1]) {
1069 delta = mMasterVolumeRamp[i];
1070 break;
1071 }
1072 }
1073 } else if (direction == AudioManager.ADJUST_LOWER){
1074 if (volume == 0) {
1075 return 0;
1076 }
1077 int length = mMasterVolumeRamp.length;
1078 // This is the default value if we make it to the end
1079 delta = -mMasterVolumeRamp[length - 1];
1080 // If we're lowering the volume move up the ramp array until we
1081 // find the volume we're below and use the group below it's delta
1082 for (int i = 2; i < length; i += 2) {
1083 if (volume <= mMasterVolumeRamp[i]) {
1084 delta = -mMasterVolumeRamp[i - 1];
1085 break;
1086 }
1087 }
1088 }
1089 return delta;
1090 }
1091
Dianne Hackborn5ac72a22012-08-29 18:32:08 -07001092 private void sendBroadcastToAll(Intent intent) {
1093 final long ident = Binder.clearCallingIdentity();
1094 try {
1095 mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
1096 } finally {
1097 Binder.restoreCallingIdentity(ident);
1098 }
1099 }
1100
1101 private void sendStickyBroadcastToAll(Intent intent) {
1102 final long ident = Binder.clearCallingIdentity();
1103 try {
1104 mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
1105 } finally {
1106 Binder.restoreCallingIdentity(ident);
1107 }
1108 }
1109
Eric Laurent25101b02011-02-02 09:33:30 -08001110 // UI update and Broadcast Intent
1111 private void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags) {
1112 if (!mVoiceCapable && (streamType == AudioSystem.STREAM_RING)) {
1113 streamType = AudioSystem.STREAM_NOTIFICATION;
1114 }
1115
1116 mVolumePanel.postVolumeChanged(streamType, flags);
1117
Eric Laurent4bbcc652012-09-24 14:26:30 -07001118 if ((flags & AudioManager.FLAG_FIXED_VOLUME) == 0) {
1119 oldIndex = (oldIndex + 5) / 10;
1120 index = (index + 5) / 10;
1121 Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
1122 intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
1123 intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index);
1124 intent.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex);
1125 sendBroadcastToAll(intent);
1126 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001127 }
1128
Mike Lockwood0dc37cc2011-12-01 16:14:19 -05001129 // UI update and Broadcast Intent
1130 private void sendMasterVolumeUpdate(int flags, int oldVolume, int newVolume) {
1131 mVolumePanel.postMasterVolumeChanged(flags);
1132
1133 Intent intent = new Intent(AudioManager.MASTER_VOLUME_CHANGED_ACTION);
1134 intent.putExtra(AudioManager.EXTRA_PREV_MASTER_VOLUME_VALUE, oldVolume);
1135 intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_VALUE, newVolume);
Dianne Hackborn5ac72a22012-08-29 18:32:08 -07001136 sendBroadcastToAll(intent);
Mike Lockwood0dc37cc2011-12-01 16:14:19 -05001137 }
1138
1139 // UI update and Broadcast Intent
1140 private void sendMasterMuteUpdate(boolean muted, int flags) {
1141 mVolumePanel.postMasterMuteChanged(flags);
Justin Koh57978ed2012-04-03 17:37:58 -07001142 broadcastMasterMuteStatus(muted);
1143 }
Mike Lockwood0dc37cc2011-12-01 16:14:19 -05001144
Justin Koh57978ed2012-04-03 17:37:58 -07001145 private void broadcastMasterMuteStatus(boolean muted) {
Mike Lockwood0dc37cc2011-12-01 16:14:19 -05001146 Intent intent = new Intent(AudioManager.MASTER_MUTE_CHANGED_ACTION);
1147 intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_MUTED, muted);
Justin Koh57978ed2012-04-03 17:37:58 -07001148 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1149 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
Dianne Hackborn5ac72a22012-08-29 18:32:08 -07001150 sendStickyBroadcastToAll(intent);
Mike Lockwood0dc37cc2011-12-01 16:14:19 -05001151 }
1152
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001153 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001154 * Sets the stream state's index, and posts a message to set system volume.
1155 * This will not call out to the UI. Assumes a valid stream type.
1156 *
1157 * @param streamType Type of the stream
1158 * @param index Desired volume index of the stream
Eric Laurent9bc8358d2011-11-18 16:43:31 -08001159 * @param device the device whose volume must be changed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001160 * @param force If true, set the volume even if the desired volume is same
1161 * as the current volume.
1162 */
Eric Laurent9bc8358d2011-11-18 16:43:31 -08001163 private void setStreamVolumeInt(int streamType,
1164 int index,
1165 int device,
Eric Laurent42b041e2013-03-29 11:36:03 -07001166 boolean force) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001167 VolumeStreamState streamState = mStreamStates[streamType];
Eric Laurent5b4e6542010-03-19 20:02:21 -07001168
Eric Laurent42b041e2013-03-29 11:36:03 -07001169 if (streamState.setIndex(index, device) || force) {
1170 // Post message to set system volume (it in turn will post a message
1171 // to persist).
1172 sendMsg(mAudioHandler,
1173 MSG_SET_DEVICE_VOLUME,
1174 SENDMSG_QUEUE,
1175 device,
1176 0,
1177 streamState,
1178 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001179 }
1180 }
1181
1182 /** @see AudioManager#setStreamSolo(int, boolean) */
1183 public void setStreamSolo(int streamType, boolean state, IBinder cb) {
Eric Laurent83a017b2013-03-19 18:15:31 -07001184 if (mUseFixedVolume) {
1185 return;
1186 }
1187
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001188 for (int stream = 0; stream < mStreamStates.length; stream++) {
1189 if (!isStreamAffectedByMute(stream) || stream == streamType) continue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001190 mStreamStates[stream].mute(cb, state);
1191 }
1192 }
1193
1194 /** @see AudioManager#setStreamMute(int, boolean) */
1195 public void setStreamMute(int streamType, boolean state, IBinder cb) {
Eric Laurent83a017b2013-03-19 18:15:31 -07001196 if (mUseFixedVolume) {
1197 return;
1198 }
1199
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001200 if (isStreamAffectedByMute(streamType)) {
1201 mStreamStates[streamType].mute(cb, state);
1202 }
1203 }
1204
Eric Laurent25101b02011-02-02 09:33:30 -08001205 /** get stream mute state. */
1206 public boolean isStreamMute(int streamType) {
Eric Laurent42b041e2013-03-29 11:36:03 -07001207 return mStreamStates[streamType].isMuted();
Eric Laurent25101b02011-02-02 09:33:30 -08001208 }
1209
Dianne Hackborn961cae92013-03-20 14:59:43 -07001210 /** @see AudioManager#setMasterMute(boolean, int) */
Justin Koh0273af52012-04-04 18:29:50 -07001211 public void setMasterMute(boolean state, int flags, IBinder cb) {
Eric Laurent83a017b2013-03-19 18:15:31 -07001212 if (mUseFixedVolume) {
1213 return;
1214 }
1215
Jason Simmons1ce5b262012-02-02 13:00:17 -08001216 if (state != AudioSystem.getMasterMute()) {
1217 AudioSystem.setMasterMute(state);
Justin Koh57978ed2012-04-03 17:37:58 -07001218 // Post a persist master volume msg
Justin Koh75cf9e12012-04-04 15:27:37 -07001219 sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME_MUTE, SENDMSG_REPLACE, state ? 1
Justin Koh57978ed2012-04-03 17:37:58 -07001220 : 0, 0, null, PERSIST_DELAY);
Justin Koh0273af52012-04-04 18:29:50 -07001221 sendMasterMuteUpdate(state, flags);
Jason Simmons1ce5b262012-02-02 13:00:17 -08001222 }
Mike Lockwoodce952c82011-11-14 10:47:42 -08001223 }
1224
1225 /** get master mute state. */
1226 public boolean isMasterMute() {
Mike Lockwood3194ea92011-12-07 11:47:31 -08001227 return AudioSystem.getMasterMute();
Mike Lockwoodce952c82011-11-14 10:47:42 -08001228 }
1229
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001230 /** @see AudioManager#getStreamVolume(int) */
1231 public int getStreamVolume(int streamType) {
1232 ensureValidStreamType(streamType);
Eric Laurent9bc8358d2011-11-18 16:43:31 -08001233 int device = getDeviceForStream(streamType);
Eric Laurent42b041e2013-03-29 11:36:03 -07001234 int index = mStreamStates[streamType].getIndex(device);
Eric Laurent4bbcc652012-09-24 14:26:30 -07001235
Eric Laurent42b041e2013-03-29 11:36:03 -07001236 // by convention getStreamVolume() returns 0 when a stream is muted.
1237 if (mStreamStates[streamType].isMuted()) {
1238 index = 0;
1239 }
Eric Laurent3ef75492012-11-28 12:12:23 -08001240 if (index != 0 && (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) &&
Eric Laurent4bbcc652012-09-24 14:26:30 -07001241 (device & mFixedVolumeDevices) != 0) {
1242 index = mStreamStates[streamType].getMaxIndex();
Eric Laurent4bbcc652012-09-24 14:26:30 -07001243 }
1244 return (index + 5) / 10;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001245 }
1246
Mike Lockwood47676902011-11-08 10:31:21 -08001247 public int getMasterVolume() {
Mike Lockwoodce952c82011-11-14 10:47:42 -08001248 if (isMasterMute()) return 0;
1249 return getLastAudibleMasterVolume();
Mike Lockwood8dc1dab2011-10-27 09:52:41 -04001250 }
1251
Mike Lockwood47676902011-11-08 10:31:21 -08001252 public void setMasterVolume(int volume, int flags) {
Eric Laurent83a017b2013-03-19 18:15:31 -07001253 if (mUseFixedVolume) {
1254 return;
1255 }
1256
Mike Lockwood97606472012-02-09 11:24:10 -08001257 if (volume < 0) {
1258 volume = 0;
1259 } else if (volume > MAX_MASTER_VOLUME) {
1260 volume = MAX_MASTER_VOLUME;
1261 }
Mike Lockwood5c55a052011-12-15 17:21:44 -05001262 doSetMasterVolume((float)volume / MAX_MASTER_VOLUME, flags);
1263 }
1264
1265 private void doSetMasterVolume(float volume, int flags) {
1266 // don't allow changing master volume when muted
1267 if (!AudioSystem.getMasterMute()) {
1268 int oldVolume = getMasterVolume();
1269 AudioSystem.setMasterVolume(volume);
1270
1271 int newVolume = getMasterVolume();
1272 if (newVolume != oldVolume) {
1273 // Post a persist master volume msg
1274 sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME, SENDMSG_REPLACE,
1275 Math.round(volume * (float)1000.0), 0, null, PERSIST_DELAY);
Mike Lockwood5c55a052011-12-15 17:21:44 -05001276 }
Justin Koh3caba512012-04-02 15:32:18 -07001277 // Send the volume update regardless whether there was a change.
1278 sendMasterVolumeUpdate(flags, oldVolume, newVolume);
Mike Lockwood5c55a052011-12-15 17:21:44 -05001279 }
Mike Lockwood8dc1dab2011-10-27 09:52:41 -04001280 }
1281
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001282 /** @see AudioManager#getStreamMaxVolume(int) */
1283 public int getStreamMaxVolume(int streamType) {
1284 ensureValidStreamType(streamType);
Eric Laurenta553c252009-07-17 12:17:14 -07001285 return (mStreamStates[streamType].getMaxIndex() + 5) / 10;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001286 }
1287
Mike Lockwood47676902011-11-08 10:31:21 -08001288 public int getMasterMaxVolume() {
1289 return MAX_MASTER_VOLUME;
1290 }
Eric Laurent25101b02011-02-02 09:33:30 -08001291
1292 /** Get last audible volume before stream was muted. */
1293 public int getLastAudibleStreamVolume(int streamType) {
1294 ensureValidStreamType(streamType);
Eric Laurent9bc8358d2011-11-18 16:43:31 -08001295 int device = getDeviceForStream(streamType);
Eric Laurent42b041e2013-03-29 11:36:03 -07001296 return (mStreamStates[streamType].getIndex(device) + 5) / 10;
Eric Laurent25101b02011-02-02 09:33:30 -08001297 }
1298
Mike Lockwoodce952c82011-11-14 10:47:42 -08001299 /** Get last audible master volume before it was muted. */
1300 public int getLastAudibleMasterVolume() {
1301 return Math.round(AudioSystem.getMasterVolume() * MAX_MASTER_VOLUME);
1302 }
1303
Dianne Hackborn961cae92013-03-20 14:59:43 -07001304 /** @see AudioManager#getMasterStreamType() */
Eric Laurent6d517662012-04-23 18:42:39 -07001305 public int getMasterStreamType() {
1306 if (mVoiceCapable) {
1307 return AudioSystem.STREAM_RING;
1308 } else {
1309 return AudioSystem.STREAM_MUSIC;
1310 }
1311 }
1312
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001313 /** @see AudioManager#getRingerMode() */
1314 public int getRingerMode() {
Glenn Kastenba195eb2011-12-13 09:30:40 -08001315 synchronized(mSettingsLock) {
1316 return mRingerMode;
1317 }
1318 }
1319
1320 private void ensureValidRingerMode(int ringerMode) {
1321 if (!AudioManager.isValidRingerMode(ringerMode)) {
1322 throw new IllegalArgumentException("Bad ringer mode " + ringerMode);
1323 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001324 }
1325
1326 /** @see AudioManager#setRingerMode(int) */
1327 public void setRingerMode(int ringerMode) {
Eric Laurent83a017b2013-03-19 18:15:31 -07001328 if (mUseFixedVolume) {
1329 return;
1330 }
1331
Eric Laurent24482012012-05-10 09:41:17 -07001332 if ((ringerMode == AudioManager.RINGER_MODE_VIBRATE) && !mHasVibrator) {
1333 ringerMode = AudioManager.RINGER_MODE_SILENT;
1334 }
Glenn Kastenba195eb2011-12-13 09:30:40 -08001335 if (ringerMode != getRingerMode()) {
1336 setRingerModeInt(ringerMode, true);
1337 // Send sticky broadcast
1338 broadcastRingerMode(ringerMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001339 }
1340 }
1341
Eric Laurent4050c932009-07-08 02:52:14 -07001342 private void setRingerModeInt(int ringerMode, boolean persist) {
Glenn Kastenba195eb2011-12-13 09:30:40 -08001343 synchronized(mSettingsLock) {
1344 mRingerMode = ringerMode;
1345 }
Jason Parekhb1096152009-03-24 17:48:25 -07001346
Eric Laurent5b4e6542010-03-19 20:02:21 -07001347 // Mute stream if not previously muted by ringer mode and ringer mode
1348 // is not RINGER_MODE_NORMAL and stream is affected by ringer mode.
1349 // Unmute stream if previously muted by ringer mode and ringer mode
1350 // is RINGER_MODE_NORMAL or stream is not affected by ringer mode.
Jason Parekhb1096152009-03-24 17:48:25 -07001351 int numStreamTypes = AudioSystem.getNumStreamTypes();
Eric Laurent5b4e6542010-03-19 20:02:21 -07001352 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
1353 if (isStreamMutedByRingerMode(streamType)) {
1354 if (!isStreamAffectedByRingerMode(streamType) ||
Glenn Kastenba195eb2011-12-13 09:30:40 -08001355 ringerMode == AudioManager.RINGER_MODE_NORMAL) {
Eric Laurentb024c302011-10-14 17:19:27 -07001356 // ring and notifications volume should never be 0 when not silenced
1357 // on voice capable devices
1358 if (mVoiceCapable &&
Eric Laurent6d517662012-04-23 18:42:39 -07001359 mStreamVolumeAlias[streamType] == AudioSystem.STREAM_RING) {
Eric Laurent3172d5e2012-05-09 11:38:16 -07001360 synchronized (mStreamStates[streamType]) {
Eric Laurent42b041e2013-03-29 11:36:03 -07001361 Set set = mStreamStates[streamType].mIndex.entrySet();
Eric Laurent3172d5e2012-05-09 11:38:16 -07001362 Iterator i = set.iterator();
1363 while (i.hasNext()) {
1364 Map.Entry entry = (Map.Entry)i.next();
1365 if ((Integer)entry.getValue() == 0) {
1366 entry.setValue(10);
1367 }
Eric Laurent9bc8358d2011-11-18 16:43:31 -08001368 }
1369 }
Eric Laurentb024c302011-10-14 17:19:27 -07001370 }
Eric Laurent5b4e6542010-03-19 20:02:21 -07001371 mStreamStates[streamType].mute(null, false);
1372 mRingerModeMutedStreams &= ~(1 << streamType);
Eric Laurent9bcf4012009-06-12 06:09:28 -07001373 }
Eric Laurent5b4e6542010-03-19 20:02:21 -07001374 } else {
1375 if (isStreamAffectedByRingerMode(streamType) &&
Glenn Kastenba195eb2011-12-13 09:30:40 -08001376 ringerMode != AudioManager.RINGER_MODE_NORMAL) {
Eric Laurent5b4e6542010-03-19 20:02:21 -07001377 mStreamStates[streamType].mute(null, true);
1378 mRingerModeMutedStreams |= (1 << streamType);
1379 }
Jason Parekhb1096152009-03-24 17:48:25 -07001380 }
1381 }
Eric Laurenta553c252009-07-17 12:17:14 -07001382
Jason Parekhb1096152009-03-24 17:48:25 -07001383 // Post a persist ringer mode msg
Eric Laurent4050c932009-07-08 02:52:14 -07001384 if (persist) {
Eric Laurentafbb0472011-12-15 09:04:23 -08001385 sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE,
Eric Laurent4050c932009-07-08 02:52:14 -07001386 SENDMSG_REPLACE, 0, 0, null, PERSIST_DELAY);
1387 }
Jason Parekhb1096152009-03-24 17:48:25 -07001388 }
1389
Mike Lockwood90631542012-01-06 11:20:37 -05001390 private void restoreMasterVolume() {
Eric Laurent83a017b2013-03-19 18:15:31 -07001391 if (mUseFixedVolume) {
1392 AudioSystem.setMasterVolume(1.0f);
1393 return;
1394 }
Mike Lockwood90631542012-01-06 11:20:37 -05001395 if (mUseMasterVolume) {
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07001396 float volume = Settings.System.getFloatForUser(mContentResolver,
1397 Settings.System.VOLUME_MASTER, -1.0f, UserHandle.USER_CURRENT);
Mike Lockwood90631542012-01-06 11:20:37 -05001398 if (volume >= 0.0f) {
1399 AudioSystem.setMasterVolume(volume);
1400 }
1401 }
1402 }
1403
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001404 /** @see AudioManager#shouldVibrate(int) */
1405 public boolean shouldVibrate(int vibrateType) {
Eric Laurentbffc3d12012-05-07 17:43:49 -07001406 if (!mHasVibrator) return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001407
1408 switch (getVibrateSetting(vibrateType)) {
1409
1410 case AudioManager.VIBRATE_SETTING_ON:
Glenn Kastenba195eb2011-12-13 09:30:40 -08001411 return getRingerMode() != AudioManager.RINGER_MODE_SILENT;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001412
1413 case AudioManager.VIBRATE_SETTING_ONLY_SILENT:
Glenn Kastenba195eb2011-12-13 09:30:40 -08001414 return getRingerMode() == AudioManager.RINGER_MODE_VIBRATE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001415
1416 case AudioManager.VIBRATE_SETTING_OFF:
Daniel Sandlerbcac4962010-04-12 13:23:57 -04001417 // return false, even for incoming calls
1418 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001419
1420 default:
1421 return false;
1422 }
1423 }
1424
1425 /** @see AudioManager#getVibrateSetting(int) */
1426 public int getVibrateSetting(int vibrateType) {
Eric Laurentbffc3d12012-05-07 17:43:49 -07001427 if (!mHasVibrator) return AudioManager.VIBRATE_SETTING_OFF;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001428 return (mVibrateSetting >> (vibrateType * 2)) & 3;
1429 }
1430
1431 /** @see AudioManager#setVibrateSetting(int, int) */
1432 public void setVibrateSetting(int vibrateType, int vibrateSetting) {
1433
Eric Laurentbffc3d12012-05-07 17:43:49 -07001434 if (!mHasVibrator) return;
1435
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001436 mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, vibrateType, vibrateSetting);
1437
1438 // Broadcast change
1439 broadcastVibrateSetting(vibrateType);
1440
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001441 }
1442
1443 /**
1444 * @see #setVibrateSetting(int, int)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001445 */
1446 public static int getValueForVibrateSetting(int existingValue, int vibrateType,
1447 int vibrateSetting) {
1448
1449 // First clear the existing setting. Each vibrate type has two bits in
1450 // the value. Note '3' is '11' in binary.
1451 existingValue &= ~(3 << (vibrateType * 2));
1452
1453 // Set into the old value
1454 existingValue |= (vibrateSetting & 3) << (vibrateType * 2);
1455
1456 return existingValue;
1457 }
1458
Eric Laurent9272b4b2010-01-23 17:12:59 -08001459 private class SetModeDeathHandler implements IBinder.DeathRecipient {
1460 private IBinder mCb; // To be notified of client's death
Marco Nelissenf1ddd512011-08-10 14:15:44 -07001461 private int mPid;
Eric Laurent9272b4b2010-01-23 17:12:59 -08001462 private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client
1463
Eric Laurent9f103de2011-09-08 15:04:23 -07001464 SetModeDeathHandler(IBinder cb, int pid) {
Eric Laurent9272b4b2010-01-23 17:12:59 -08001465 mCb = cb;
Eric Laurent9f103de2011-09-08 15:04:23 -07001466 mPid = pid;
Eric Laurent9272b4b2010-01-23 17:12:59 -08001467 }
1468
1469 public void binderDied() {
Eric Laurentd7454be2011-09-14 08:45:58 -07001470 int newModeOwnerPid = 0;
Eric Laurent9272b4b2010-01-23 17:12:59 -08001471 synchronized(mSetModeDeathHandlers) {
1472 Log.w(TAG, "setMode() client died");
1473 int index = mSetModeDeathHandlers.indexOf(this);
1474 if (index < 0) {
1475 Log.w(TAG, "unregistered setMode() client died");
1476 } else {
Eric Laurentd7454be2011-09-14 08:45:58 -07001477 newModeOwnerPid = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid);
Eric Laurent9272b4b2010-01-23 17:12:59 -08001478 }
1479 }
Eric Laurent9f103de2011-09-08 15:04:23 -07001480 // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
1481 // SCO connections not started by the application changing the mode
Eric Laurentd7454be2011-09-14 08:45:58 -07001482 if (newModeOwnerPid != 0) {
Eric Laurent6b5e22d2013-03-28 16:10:45 -07001483 final long ident = Binder.clearCallingIdentity();
1484 disconnectBluetoothSco(newModeOwnerPid);
1485 Binder.restoreCallingIdentity(ident);
Eric Laurent9f103de2011-09-08 15:04:23 -07001486 }
Eric Laurent9272b4b2010-01-23 17:12:59 -08001487 }
1488
Marco Nelissenf1ddd512011-08-10 14:15:44 -07001489 public int getPid() {
1490 return mPid;
1491 }
1492
Eric Laurent9272b4b2010-01-23 17:12:59 -08001493 public void setMode(int mode) {
1494 mMode = mode;
1495 }
1496
1497 public int getMode() {
1498 return mMode;
1499 }
1500
1501 public IBinder getBinder() {
1502 return mCb;
1503 }
1504 }
1505
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001506 /** @see AudioManager#setMode(int) */
Eric Laurent9272b4b2010-01-23 17:12:59 -08001507 public void setMode(int mode, IBinder cb) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001508 if (!checkAudioSettingsPermission("setMode()")) {
1509 return;
1510 }
Eric Laurenta553c252009-07-17 12:17:14 -07001511
Jean-Michel Trivi8f677d62010-11-15 12:11:32 -08001512 if (mode < AudioSystem.MODE_CURRENT || mode >= AudioSystem.NUM_MODES) {
Eric Laurenta553c252009-07-17 12:17:14 -07001513 return;
1514 }
1515
Eric Laurentd7454be2011-09-14 08:45:58 -07001516 int newModeOwnerPid = 0;
Eric Laurent9f103de2011-09-08 15:04:23 -07001517 synchronized(mSetModeDeathHandlers) {
Eric Laurenta553c252009-07-17 12:17:14 -07001518 if (mode == AudioSystem.MODE_CURRENT) {
1519 mode = mMode;
1520 }
Eric Laurentd7454be2011-09-14 08:45:58 -07001521 newModeOwnerPid = setModeInt(mode, cb, Binder.getCallingPid());
Eric Laurent9f103de2011-09-08 15:04:23 -07001522 }
1523 // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
1524 // SCO connections not started by the application changing the mode
Eric Laurentd7454be2011-09-14 08:45:58 -07001525 if (newModeOwnerPid != 0) {
1526 disconnectBluetoothSco(newModeOwnerPid);
Eric Laurent9f103de2011-09-08 15:04:23 -07001527 }
1528 }
Jean-Michel Trivi2ade5762010-12-11 13:18:30 -08001529
Eric Laurent9f103de2011-09-08 15:04:23 -07001530 // must be called synchronized on mSetModeDeathHandlers
Eric Laurentd7454be2011-09-14 08:45:58 -07001531 // setModeInt() returns a valid PID if the audio mode was successfully set to
Eric Laurent9f103de2011-09-08 15:04:23 -07001532 // any mode other than NORMAL.
Eric Laurentd7454be2011-09-14 08:45:58 -07001533 int setModeInt(int mode, IBinder cb, int pid) {
1534 int newModeOwnerPid = 0;
Eric Laurent9f103de2011-09-08 15:04:23 -07001535 if (cb == null) {
1536 Log.e(TAG, "setModeInt() called with null binder");
Eric Laurentd7454be2011-09-14 08:45:58 -07001537 return newModeOwnerPid;
Eric Laurent9f103de2011-09-08 15:04:23 -07001538 }
Jean-Michel Trivi2ade5762010-12-11 13:18:30 -08001539
Eric Laurent9f103de2011-09-08 15:04:23 -07001540 SetModeDeathHandler hdlr = null;
1541 Iterator iter = mSetModeDeathHandlers.iterator();
1542 while (iter.hasNext()) {
1543 SetModeDeathHandler h = (SetModeDeathHandler)iter.next();
1544 if (h.getPid() == pid) {
1545 hdlr = h;
1546 // Remove from client list so that it is re-inserted at top of list
1547 iter.remove();
1548 hdlr.getBinder().unlinkToDeath(hdlr, 0);
1549 break;
1550 }
1551 }
1552 int status = AudioSystem.AUDIO_STATUS_OK;
1553 do {
1554 if (mode == AudioSystem.MODE_NORMAL) {
1555 // get new mode from client at top the list if any
1556 if (!mSetModeDeathHandlers.isEmpty()) {
1557 hdlr = mSetModeDeathHandlers.get(0);
1558 cb = hdlr.getBinder();
1559 mode = hdlr.getMode();
Eric Laurentb9c9d262009-05-06 08:13:20 -07001560 }
Eric Laurent9f103de2011-09-08 15:04:23 -07001561 } else {
1562 if (hdlr == null) {
1563 hdlr = new SetModeDeathHandler(cb, pid);
1564 }
1565 // Register for client death notification
1566 try {
1567 cb.linkToDeath(hdlr, 0);
1568 } catch (RemoteException e) {
1569 // Client has died!
1570 Log.w(TAG, "setMode() could not link to "+cb+" binder death");
1571 }
1572
1573 // Last client to call setMode() is always at top of client list
1574 // as required by SetModeDeathHandler.binderDied()
1575 mSetModeDeathHandlers.add(0, hdlr);
1576 hdlr.setMode(mode);
1577 }
1578
1579 if (mode != mMode) {
1580 status = AudioSystem.setPhoneState(mode);
1581 if (status == AudioSystem.AUDIO_STATUS_OK) {
Eric Laurent9f103de2011-09-08 15:04:23 -07001582 mMode = mode;
1583 } else {
1584 if (hdlr != null) {
1585 mSetModeDeathHandlers.remove(hdlr);
1586 cb.unlinkToDeath(hdlr, 0);
1587 }
1588 // force reading new top of mSetModeDeathHandlers stack
1589 mode = AudioSystem.MODE_NORMAL;
1590 }
1591 } else {
1592 status = AudioSystem.AUDIO_STATUS_OK;
1593 }
1594 } while (status != AudioSystem.AUDIO_STATUS_OK && !mSetModeDeathHandlers.isEmpty());
1595
1596 if (status == AudioSystem.AUDIO_STATUS_OK) {
1597 if (mode != AudioSystem.MODE_NORMAL) {
Eric Laurentd7454be2011-09-14 08:45:58 -07001598 if (mSetModeDeathHandlers.isEmpty()) {
1599 Log.e(TAG, "setMode() different from MODE_NORMAL with empty mode client stack");
1600 } else {
1601 newModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
1602 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001603 }
1604 int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001605 if (streamType == STREAM_REMOTE_MUSIC) {
1606 // here handle remote media playback the same way as local playback
1607 streamType = AudioManager.STREAM_MUSIC;
1608 }
Eric Laurent9bc8358d2011-11-18 16:43:31 -08001609 int device = getDeviceForStream(streamType);
Eric Laurent42b041e2013-03-29 11:36:03 -07001610 int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device);
1611 setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true);
Eric Laurent6d517662012-04-23 18:42:39 -07001612
1613 updateStreamVolumeAlias(true /*updateVolumes*/);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001614 }
Eric Laurentd7454be2011-09-14 08:45:58 -07001615 return newModeOwnerPid;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001616 }
1617
1618 /** @see AudioManager#getMode() */
1619 public int getMode() {
1620 return mMode;
1621 }
1622
Eric Laurente78fced2013-03-15 16:03:47 -07001623 //==========================================================================================
1624 // Sound Effects
1625 //==========================================================================================
1626
1627 private static final String TAG_AUDIO_ASSETS = "audio_assets";
1628 private static final String ATTR_VERSION = "version";
1629 private static final String TAG_GROUP = "group";
1630 private static final String ATTR_GROUP_NAME = "name";
1631 private static final String TAG_ASSET = "asset";
1632 private static final String ATTR_ASSET_ID = "id";
1633 private static final String ATTR_ASSET_FILE = "file";
1634
1635 private static final String ASSET_FILE_VERSION = "1.0";
1636 private static final String GROUP_TOUCH_SOUNDS = "touch_sounds";
1637
Eric Laurent5d3eb44a2013-03-21 15:35:10 -07001638 private static final int SOUND_EFECTS_LOAD_TIMEOUT_MS = 5000;
1639
1640 class LoadSoundEffectReply {
1641 public int mStatus = 1;
1642 };
1643
Eric Laurente78fced2013-03-15 16:03:47 -07001644 private void loadTouchSoundAssetDefaults() {
1645 SOUND_EFFECT_FILES.add("Effect_Tick.ogg");
1646 for (int i = 0; i < AudioManager.NUM_SOUND_EFFECTS; i++) {
1647 SOUND_EFFECT_FILES_MAP[i][0] = 0;
1648 SOUND_EFFECT_FILES_MAP[i][1] = -1;
1649 }
1650 }
1651
1652 private void loadTouchSoundAssets() {
1653 XmlResourceParser parser = null;
1654
Eric Laurent5d3eb44a2013-03-21 15:35:10 -07001655 // only load assets once.
1656 if (!SOUND_EFFECT_FILES.isEmpty()) {
1657 return;
1658 }
1659
Eric Laurente78fced2013-03-15 16:03:47 -07001660 loadTouchSoundAssetDefaults();
1661
1662 try {
1663 parser = mContext.getResources().getXml(com.android.internal.R.xml.audio_assets);
1664
1665 XmlUtils.beginDocument(parser, TAG_AUDIO_ASSETS);
1666 String version = parser.getAttributeValue(null, ATTR_VERSION);
1667 boolean inTouchSoundsGroup = false;
1668
1669 if (ASSET_FILE_VERSION.equals(version)) {
1670 while (true) {
1671 XmlUtils.nextElement(parser);
1672 String element = parser.getName();
1673 if (element == null) {
1674 break;
1675 }
1676 if (element.equals(TAG_GROUP)) {
1677 String name = parser.getAttributeValue(null, ATTR_GROUP_NAME);
1678 if (GROUP_TOUCH_SOUNDS.equals(name)) {
1679 inTouchSoundsGroup = true;
1680 break;
1681 }
1682 }
1683 }
1684 while (inTouchSoundsGroup) {
1685 XmlUtils.nextElement(parser);
1686 String element = parser.getName();
1687 if (element == null) {
1688 break;
1689 }
1690 if (element.equals(TAG_ASSET)) {
1691 String id = parser.getAttributeValue(null, ATTR_ASSET_ID);
1692 String file = parser.getAttributeValue(null, ATTR_ASSET_FILE);
1693 int fx;
1694
1695 try {
1696 Field field = AudioManager.class.getField(id);
1697 fx = field.getInt(null);
1698 } catch (Exception e) {
1699 Log.w(TAG, "Invalid touch sound ID: "+id);
1700 continue;
1701 }
1702
1703 int i = SOUND_EFFECT_FILES.indexOf(file);
1704 if (i == -1) {
1705 i = SOUND_EFFECT_FILES.size();
1706 SOUND_EFFECT_FILES.add(file);
1707 }
1708 SOUND_EFFECT_FILES_MAP[fx][0] = i;
1709 } else {
1710 break;
1711 }
1712 }
1713 }
1714 } catch (Resources.NotFoundException e) {
1715 Log.w(TAG, "audio assets file not found", e);
1716 } catch (XmlPullParserException e) {
1717 Log.w(TAG, "XML parser exception reading touch sound assets", e);
1718 } catch (IOException e) {
1719 Log.w(TAG, "I/O exception reading touch sound assets", e);
1720 } finally {
1721 if (parser != null) {
1722 parser.close();
1723 }
1724 }
1725 }
1726
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001727 /** @see AudioManager#playSoundEffect(int) */
1728 public void playSoundEffect(int effectType) {
Eric Laurent5d3eb44a2013-03-21 15:35:10 -07001729 playSoundEffectVolume(effectType, -1.0f);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001730 }
1731
1732 /** @see AudioManager#playSoundEffect(int, float) */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001733 public void playSoundEffectVolume(int effectType, float volume) {
Eric Laurent5d3eb44a2013-03-21 15:35:10 -07001734 sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_QUEUE,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001735 effectType, (int) (volume * 1000), null, 0);
1736 }
1737
1738 /**
1739 * Loads samples into the soundpool.
Glenn Kasten5c17a822011-11-30 09:41:01 -08001740 * This method must be called at first when sound effects are enabled
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001741 */
1742 public boolean loadSoundEffects() {
Eric Laurent5d3eb44a2013-03-21 15:35:10 -07001743 int attempts = 3;
1744 LoadSoundEffectReply reply = new LoadSoundEffectReply();
Eric Laurenta60e2122010-12-28 16:49:07 -08001745
Eric Laurent5d3eb44a2013-03-21 15:35:10 -07001746 synchronized (reply) {
1747 sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_QUEUE, 0, 0, reply, 0);
1748 while ((reply.mStatus == 1) && (attempts-- > 0)) {
Eric Laurent117b7bb2011-01-16 17:07:27 -08001749 try {
Eric Laurent5d3eb44a2013-03-21 15:35:10 -07001750 reply.wait(SOUND_EFECTS_LOAD_TIMEOUT_MS);
1751 } catch (InterruptedException e) {
1752 Log.w(TAG, "loadSoundEffects Interrupted while waiting sound pool loaded.");
Eric Laurent117b7bb2011-01-16 17:07:27 -08001753 }
Eric Laurenta60e2122010-12-28 16:49:07 -08001754 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001755 }
Eric Laurent5d3eb44a2013-03-21 15:35:10 -07001756 return (reply.mStatus == 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001757 }
1758
1759 /**
1760 * Unloads samples from the sound pool.
1761 * This method can be called to free some memory when
1762 * sound effects are disabled.
1763 */
1764 public void unloadSoundEffects() {
Eric Laurent5d3eb44a2013-03-21 15:35:10 -07001765 sendMsg(mAudioHandler, MSG_UNLOAD_SOUND_EFFECTS, SENDMSG_QUEUE, 0, 0, null, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001766 }
1767
Eric Laurenta60e2122010-12-28 16:49:07 -08001768 class SoundPoolListenerThread extends Thread {
1769 public SoundPoolListenerThread() {
1770 super("SoundPoolListenerThread");
1771 }
1772
1773 @Override
1774 public void run() {
1775
1776 Looper.prepare();
1777 mSoundPoolLooper = Looper.myLooper();
1778
1779 synchronized (mSoundEffectsLock) {
1780 if (mSoundPool != null) {
1781 mSoundPoolCallBack = new SoundPoolCallback();
1782 mSoundPool.setOnLoadCompleteListener(mSoundPoolCallBack);
1783 }
1784 mSoundEffectsLock.notify();
1785 }
1786 Looper.loop();
1787 }
1788 }
1789
1790 private final class SoundPoolCallback implements
1791 android.media.SoundPool.OnLoadCompleteListener {
1792
Eric Laurent5d3eb44a2013-03-21 15:35:10 -07001793 int mStatus = 1; // 1 means neither error nor last sample loaded yet
1794 List<Integer> mSamples = new ArrayList<Integer>();
Eric Laurenta60e2122010-12-28 16:49:07 -08001795
1796 public int status() {
1797 return mStatus;
1798 }
1799
Eric Laurent5d3eb44a2013-03-21 15:35:10 -07001800 public void setSamples(int[] samples) {
1801 for (int i = 0; i < samples.length; i++) {
1802 // do not wait ack for samples rejected upfront by SoundPool
1803 if (samples[i] > 0) {
1804 mSamples.add(samples[i]);
1805 }
1806 }
Eric Laurenta60e2122010-12-28 16:49:07 -08001807 }
1808
1809 public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
1810 synchronized (mSoundEffectsLock) {
Eric Laurent5d3eb44a2013-03-21 15:35:10 -07001811 int i = mSamples.indexOf(sampleId);
1812 if (i >= 0) {
1813 mSamples.remove(i);
Eric Laurenta60e2122010-12-28 16:49:07 -08001814 }
Eric Laurent5d3eb44a2013-03-21 15:35:10 -07001815 if ((status != 0) || mSamples. isEmpty()) {
1816 mStatus = status;
Eric Laurenta60e2122010-12-28 16:49:07 -08001817 mSoundEffectsLock.notify();
1818 }
1819 }
1820 }
1821 }
1822
Eric Laurent4050c932009-07-08 02:52:14 -07001823 /** @see AudioManager#reloadAudioSettings() */
1824 public void reloadAudioSettings() {
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07001825 readAudioSettings(false /*userSwitch*/);
1826 }
1827
1828 private void readAudioSettings(boolean userSwitch) {
Eric Laurent4050c932009-07-08 02:52:14 -07001829 // restore ringer mode, ringer mode affected streams, mute affected streams and vibrate settings
1830 readPersistedSettings();
1831
1832 // restore volume settings
1833 int numStreamTypes = AudioSystem.getNumStreamTypes();
1834 for (int streamType = 0; streamType < numStreamTypes; streamType++) {
1835 VolumeStreamState streamState = mStreamStates[streamType];
1836
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07001837 if (userSwitch && mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) {
1838 continue;
1839 }
1840
Eric Laurent3172d5e2012-05-09 11:38:16 -07001841 synchronized (streamState) {
1842 streamState.readSettings();
Eric Laurenta553c252009-07-17 12:17:14 -07001843
Eric Laurent3172d5e2012-05-09 11:38:16 -07001844 // unmute stream that was muted but is not affect by mute anymore
Eric Laurent42b041e2013-03-29 11:36:03 -07001845 if (streamState.isMuted() && ((!isStreamAffectedByMute(streamType) &&
Eric Laurent83a017b2013-03-19 18:15:31 -07001846 !isStreamMutedByRingerMode(streamType)) || mUseFixedVolume)) {
Eric Laurent3172d5e2012-05-09 11:38:16 -07001847 int size = streamState.mDeathHandlers.size();
1848 for (int i = 0; i < size; i++) {
1849 streamState.mDeathHandlers.get(i).mMuteCount = 1;
1850 streamState.mDeathHandlers.get(i).mute(false);
1851 }
Eric Laurent4050c932009-07-08 02:52:14 -07001852 }
Eric Laurent4050c932009-07-08 02:52:14 -07001853 }
1854 }
1855
Eric Laurent33902db2012-10-07 16:15:07 -07001856 // apply new ringer mode before checking volume for alias streams so that streams
1857 // muted by ringer mode have the correct volume
1858 setRingerModeInt(getRingerMode(), false);
1859
Eric Laurent24482012012-05-10 09:41:17 -07001860 checkAllAliasStreamVolumes();
1861
Eric Laurentd640bd32012-09-28 18:01:48 -07001862 synchronized (mSafeMediaVolumeState) {
1863 if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE) {
Eric Laurentf1a457d2012-09-20 16:27:23 -07001864 enforceSafeMediaVolume();
1865 }
Eric Laurentc34dcc12012-09-10 13:51:52 -07001866 }
Eric Laurent4050c932009-07-08 02:52:14 -07001867 }
1868
Dianne Hackborn961cae92013-03-20 14:59:43 -07001869 /** @see AudioManager#setSpeakerphoneOn(boolean) */
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001870 public void setSpeakerphoneOn(boolean on){
Eric Laurentdc1d17a2009-09-10 00:48:21 -07001871 if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
1872 return;
1873 }
Eric Laurentfa640152011-03-12 15:59:51 -08001874 mForcedUseForComm = on ? AudioSystem.FORCE_SPEAKER : AudioSystem.FORCE_NONE;
1875
Eric Laurentafbb0472011-12-15 09:04:23 -08001876 sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
Eric Laurentfa640152011-03-12 15:59:51 -08001877 AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0);
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001878 }
1879
1880 /** @see AudioManager#isSpeakerphoneOn() */
1881 public boolean isSpeakerphoneOn() {
Eric Laurentfa640152011-03-12 15:59:51 -08001882 return (mForcedUseForComm == AudioSystem.FORCE_SPEAKER);
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001883 }
1884
Dianne Hackborn961cae92013-03-20 14:59:43 -07001885 /** @see AudioManager#setBluetoothScoOn(boolean) */
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001886 public void setBluetoothScoOn(boolean on){
Eric Laurentdc1d17a2009-09-10 00:48:21 -07001887 if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
1888 return;
1889 }
Eric Laurentfa640152011-03-12 15:59:51 -08001890 mForcedUseForComm = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE;
1891
Eric Laurentafbb0472011-12-15 09:04:23 -08001892 sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
Eric Laurentfa640152011-03-12 15:59:51 -08001893 AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0);
Eric Laurentafbb0472011-12-15 09:04:23 -08001894 sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
Eric Laurentfa640152011-03-12 15:59:51 -08001895 AudioSystem.FOR_RECORD, mForcedUseForComm, null, 0);
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001896 }
1897
1898 /** @see AudioManager#isBluetoothScoOn() */
1899 public boolean isBluetoothScoOn() {
Eric Laurentfa640152011-03-12 15:59:51 -08001900 return (mForcedUseForComm == AudioSystem.FORCE_BT_SCO);
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001901 }
1902
Dianne Hackborn961cae92013-03-20 14:59:43 -07001903 /** @see AudioManager#setBluetoothA2dpOn(boolean) */
Eric Laurent78472112012-05-21 08:57:21 -07001904 public void setBluetoothA2dpOn(boolean on) {
Eric Laurentc390bed2012-07-03 12:24:05 -07001905 synchronized (mBluetoothA2dpEnabledLock) {
1906 mBluetoothA2dpEnabled = on;
1907 sendMsg(mAudioHandler, MSG_SET_FORCE_BT_A2DP_USE, SENDMSG_QUEUE,
1908 AudioSystem.FOR_MEDIA,
1909 mBluetoothA2dpEnabled ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP,
1910 null, 0);
1911 }
Eric Laurent78472112012-05-21 08:57:21 -07001912 }
1913
1914 /** @see AudioManager#isBluetoothA2dpOn() */
1915 public boolean isBluetoothA2dpOn() {
1916 synchronized (mBluetoothA2dpEnabledLock) {
1917 return mBluetoothA2dpEnabled;
1918 }
1919 }
1920
Eric Laurent3def1ee2010-03-17 23:26:26 -07001921 /** @see AudioManager#startBluetoothSco() */
Eric Laurentc18c9132013-04-12 17:24:56 -07001922 public void startBluetoothSco(IBinder cb, int targetSdkVersion){
Eric Laurentdc03c612011-04-01 10:59:41 -07001923 if (!checkAudioSettingsPermission("startBluetoothSco()") ||
1924 !mBootCompleted) {
Eric Laurent3def1ee2010-03-17 23:26:26 -07001925 return;
1926 }
Eric Laurent854938a2011-02-22 12:05:20 -08001927 ScoClient client = getScoClient(cb, true);
Eric Laurentf5a1fc32013-03-11 18:52:57 -07001928 // The calling identity must be cleared before calling ScoClient.incCount().
1929 // inCount() calls requestScoState() which in turn can call BluetoothHeadset APIs
1930 // and this must be done on behalf of system server to make sure permissions are granted.
1931 // The caller identity must be cleared after getScoClient() because it is needed if a new
1932 // client is created.
1933 final long ident = Binder.clearCallingIdentity();
Eric Laurentc18c9132013-04-12 17:24:56 -07001934 client.incCount(targetSdkVersion);
Eric Laurent2a57ca92013-03-07 17:29:27 -08001935 Binder.restoreCallingIdentity(ident);
Eric Laurent3def1ee2010-03-17 23:26:26 -07001936 }
1937
1938 /** @see AudioManager#stopBluetoothSco() */
1939 public void stopBluetoothSco(IBinder cb){
Eric Laurentdc03c612011-04-01 10:59:41 -07001940 if (!checkAudioSettingsPermission("stopBluetoothSco()") ||
1941 !mBootCompleted) {
Eric Laurent3def1ee2010-03-17 23:26:26 -07001942 return;
1943 }
Eric Laurent854938a2011-02-22 12:05:20 -08001944 ScoClient client = getScoClient(cb, false);
Eric Laurentf5a1fc32013-03-11 18:52:57 -07001945 // The calling identity must be cleared before calling ScoClient.decCount().
1946 // decCount() calls requestScoState() which in turn can call BluetoothHeadset APIs
1947 // and this must be done on behalf of system server to make sure permissions are granted.
1948 final long ident = Binder.clearCallingIdentity();
Eric Laurent854938a2011-02-22 12:05:20 -08001949 if (client != null) {
1950 client.decCount();
1951 }
Eric Laurent2a57ca92013-03-07 17:29:27 -08001952 Binder.restoreCallingIdentity(ident);
Eric Laurent3def1ee2010-03-17 23:26:26 -07001953 }
1954
Eric Laurent78472112012-05-21 08:57:21 -07001955
Eric Laurent3def1ee2010-03-17 23:26:26 -07001956 private class ScoClient implements IBinder.DeathRecipient {
1957 private IBinder mCb; // To be notified of client's death
Marco Nelissenf1ddd512011-08-10 14:15:44 -07001958 private int mCreatorPid;
Eric Laurent3def1ee2010-03-17 23:26:26 -07001959 private int mStartcount; // number of SCO connections started by this client
1960
1961 ScoClient(IBinder cb) {
1962 mCb = cb;
Marco Nelissenf1ddd512011-08-10 14:15:44 -07001963 mCreatorPid = Binder.getCallingPid();
Eric Laurent3def1ee2010-03-17 23:26:26 -07001964 mStartcount = 0;
1965 }
1966
1967 public void binderDied() {
1968 synchronized(mScoClients) {
1969 Log.w(TAG, "SCO client died");
1970 int index = mScoClients.indexOf(this);
1971 if (index < 0) {
1972 Log.w(TAG, "unregistered SCO client died");
1973 } else {
1974 clearCount(true);
1975 mScoClients.remove(this);
1976 }
1977 }
1978 }
1979
Eric Laurentc18c9132013-04-12 17:24:56 -07001980 public void incCount(int targetSdkVersion) {
Eric Laurent3def1ee2010-03-17 23:26:26 -07001981 synchronized(mScoClients) {
Eric Laurentc18c9132013-04-12 17:24:56 -07001982 requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED, targetSdkVersion);
Eric Laurent3def1ee2010-03-17 23:26:26 -07001983 if (mStartcount == 0) {
1984 try {
1985 mCb.linkToDeath(this, 0);
1986 } catch (RemoteException e) {
1987 // client has already died!
1988 Log.w(TAG, "ScoClient incCount() could not link to "+mCb+" binder death");
1989 }
1990 }
1991 mStartcount++;
1992 }
1993 }
1994
1995 public void decCount() {
1996 synchronized(mScoClients) {
1997 if (mStartcount == 0) {
1998 Log.w(TAG, "ScoClient.decCount() already 0");
1999 } else {
2000 mStartcount--;
2001 if (mStartcount == 0) {
Eric Laurente2dd8c42010-06-30 19:41:56 -07002002 try {
2003 mCb.unlinkToDeath(this, 0);
2004 } catch (NoSuchElementException e) {
2005 Log.w(TAG, "decCount() going to 0 but not registered to binder");
2006 }
Eric Laurent3def1ee2010-03-17 23:26:26 -07002007 }
Eric Laurentc18c9132013-04-12 17:24:56 -07002008 requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 0);
Eric Laurent3def1ee2010-03-17 23:26:26 -07002009 }
2010 }
2011 }
2012
2013 public void clearCount(boolean stopSco) {
2014 synchronized(mScoClients) {
Eric Laurente2dd8c42010-06-30 19:41:56 -07002015 if (mStartcount != 0) {
2016 try {
2017 mCb.unlinkToDeath(this, 0);
2018 } catch (NoSuchElementException e) {
2019 Log.w(TAG, "clearCount() mStartcount: "+mStartcount+" != 0 but not registered to binder");
2020 }
2021 }
Eric Laurent3def1ee2010-03-17 23:26:26 -07002022 mStartcount = 0;
Eric Laurent3def1ee2010-03-17 23:26:26 -07002023 if (stopSco) {
Eric Laurentc18c9132013-04-12 17:24:56 -07002024 requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED, 0);
Eric Laurent3def1ee2010-03-17 23:26:26 -07002025 }
2026 }
2027 }
2028
2029 public int getCount() {
2030 return mStartcount;
2031 }
2032
2033 public IBinder getBinder() {
2034 return mCb;
2035 }
2036
Eric Laurentd7454be2011-09-14 08:45:58 -07002037 public int getPid() {
2038 return mCreatorPid;
2039 }
2040
Eric Laurent3def1ee2010-03-17 23:26:26 -07002041 public int totalCount() {
2042 synchronized(mScoClients) {
2043 int count = 0;
2044 int size = mScoClients.size();
2045 for (int i = 0; i < size; i++) {
2046 count += mScoClients.get(i).getCount();
2047 }
2048 return count;
2049 }
2050 }
2051
Eric Laurentc18c9132013-04-12 17:24:56 -07002052 private void requestScoState(int state, int targetSdkVersion) {
Eric Laurent62ef7672010-11-24 10:58:32 -08002053 checkScoAudioState();
Eric Laurentdc03c612011-04-01 10:59:41 -07002054 if (totalCount() == 0) {
2055 if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
2056 // Make sure that the state transitions to CONNECTING even if we cannot initiate
2057 // the connection.
2058 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
2059 // Accept SCO audio activation only in NORMAL audio mode or if the mode is
Marco Nelissenf1ddd512011-08-10 14:15:44 -07002060 // currently controlled by the same client process.
Eric Laurent9f103de2011-09-08 15:04:23 -07002061 synchronized(mSetModeDeathHandlers) {
2062 if ((mSetModeDeathHandlers.isEmpty() ||
2063 mSetModeDeathHandlers.get(0).getPid() == mCreatorPid) &&
2064 (mScoAudioState == SCO_STATE_INACTIVE ||
2065 mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
2066 if (mScoAudioState == SCO_STATE_INACTIVE) {
Eric Laurentc18c9132013-04-12 17:24:56 -07002067 mScoAudioMode =
2068 (targetSdkVersion < Build.VERSION_CODES.JELLY_BEAN_MR2) ?
2069 SCO_MODE_VIRTUAL_CALL : SCO_MODE_RAW;
Eric Laurent9f103de2011-09-08 15:04:23 -07002070 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
Eric Laurentc18c9132013-04-12 17:24:56 -07002071 boolean status;
2072 if (mScoAudioMode == SCO_MODE_RAW) {
2073 status = mBluetoothHeadset.connectAudio();
2074 } else {
2075 status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
2076 mBluetoothHeadsetDevice);
2077 }
2078 if (status) {
Eric Laurent9f103de2011-09-08 15:04:23 -07002079 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
2080 } else {
2081 broadcastScoConnectionState(
2082 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
2083 }
2084 } else if (getBluetoothHeadset()) {
2085 mScoAudioState = SCO_STATE_ACTIVATE_REQ;
Eric Laurentdc03c612011-04-01 10:59:41 -07002086 }
Eric Laurent9f103de2011-09-08 15:04:23 -07002087 } else {
2088 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
2089 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
Eric Laurentdc03c612011-04-01 10:59:41 -07002090 }
2091 } else {
Eric Laurent9f103de2011-09-08 15:04:23 -07002092 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
Eric Laurentdc03c612011-04-01 10:59:41 -07002093 }
Eric Laurentdc03c612011-04-01 10:59:41 -07002094 }
Eric Laurent62ef7672010-11-24 10:58:32 -08002095 } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED &&
Eric Laurentdc03c612011-04-01 10:59:41 -07002096 (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
2097 mScoAudioState == SCO_STATE_ACTIVATE_REQ)) {
2098 if (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) {
Marco Nelissen671db6f2011-09-06 16:29:12 -07002099 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
Eric Laurentc18c9132013-04-12 17:24:56 -07002100 boolean status;
2101 if (mScoAudioMode == SCO_MODE_RAW) {
2102 status = mBluetoothHeadset.disconnectAudio();
2103 } else {
2104 status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
2105 mBluetoothHeadsetDevice);
2106 }
2107 if (!status) {
Eric Laurentdc03c612011-04-01 10:59:41 -07002108 mScoAudioState = SCO_STATE_INACTIVE;
2109 broadcastScoConnectionState(
2110 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
2111 }
2112 } else if (getBluetoothHeadset()) {
2113 mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
2114 }
2115 } else {
2116 mScoAudioState = SCO_STATE_INACTIVE;
2117 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
2118 }
Eric Laurent3def1ee2010-03-17 23:26:26 -07002119 }
2120 }
2121 }
2122 }
2123
Eric Laurent62ef7672010-11-24 10:58:32 -08002124 private void checkScoAudioState() {
2125 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null &&
Eric Laurentdc03c612011-04-01 10:59:41 -07002126 mScoAudioState == SCO_STATE_INACTIVE &&
Eric Laurent62ef7672010-11-24 10:58:32 -08002127 mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
2128 != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
2129 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
2130 }
2131 }
2132
Eric Laurent854938a2011-02-22 12:05:20 -08002133 private ScoClient getScoClient(IBinder cb, boolean create) {
Eric Laurent3def1ee2010-03-17 23:26:26 -07002134 synchronized(mScoClients) {
Eric Laurent854938a2011-02-22 12:05:20 -08002135 ScoClient client = null;
Eric Laurent3def1ee2010-03-17 23:26:26 -07002136 int size = mScoClients.size();
2137 for (int i = 0; i < size; i++) {
2138 client = mScoClients.get(i);
2139 if (client.getBinder() == cb)
2140 return client;
2141 }
Eric Laurent854938a2011-02-22 12:05:20 -08002142 if (create) {
2143 client = new ScoClient(cb);
2144 mScoClients.add(client);
2145 }
Eric Laurent3def1ee2010-03-17 23:26:26 -07002146 return client;
2147 }
2148 }
2149
Eric Laurentd7454be2011-09-14 08:45:58 -07002150 public void clearAllScoClients(int exceptPid, boolean stopSco) {
Eric Laurent3def1ee2010-03-17 23:26:26 -07002151 synchronized(mScoClients) {
Eric Laurent854938a2011-02-22 12:05:20 -08002152 ScoClient savedClient = null;
Eric Laurent3def1ee2010-03-17 23:26:26 -07002153 int size = mScoClients.size();
2154 for (int i = 0; i < size; i++) {
Eric Laurent854938a2011-02-22 12:05:20 -08002155 ScoClient cl = mScoClients.get(i);
Eric Laurentd7454be2011-09-14 08:45:58 -07002156 if (cl.getPid() != exceptPid) {
Eric Laurent854938a2011-02-22 12:05:20 -08002157 cl.clearCount(stopSco);
2158 } else {
2159 savedClient = cl;
2160 }
2161 }
2162 mScoClients.clear();
2163 if (savedClient != null) {
2164 mScoClients.add(savedClient);
Eric Laurent3def1ee2010-03-17 23:26:26 -07002165 }
2166 }
2167 }
2168
Eric Laurentdc03c612011-04-01 10:59:41 -07002169 private boolean getBluetoothHeadset() {
2170 boolean result = false;
2171 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
2172 if (adapter != null) {
2173 result = adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
2174 BluetoothProfile.HEADSET);
2175 }
2176 // If we could not get a bluetooth headset proxy, send a failure message
2177 // without delay to reset the SCO audio state and clear SCO clients.
2178 // If we could get a proxy, send a delayed failure message that will reset our state
2179 // in case we don't receive onServiceConnected().
Eric Laurentafbb0472011-12-15 09:04:23 -08002180 sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED,
Eric Laurentdc03c612011-04-01 10:59:41 -07002181 SENDMSG_REPLACE, 0, 0, null, result ? BT_HEADSET_CNCT_TIMEOUT_MS : 0);
2182 return result;
2183 }
2184
Eric Laurentd7454be2011-09-14 08:45:58 -07002185 private void disconnectBluetoothSco(int exceptPid) {
Eric Laurentdc03c612011-04-01 10:59:41 -07002186 synchronized(mScoClients) {
2187 checkScoAudioState();
2188 if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL ||
2189 mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
2190 if (mBluetoothHeadsetDevice != null) {
2191 if (mBluetoothHeadset != null) {
2192 if (!mBluetoothHeadset.stopVoiceRecognition(
Eric Laurentb06ac832011-05-25 15:55:18 -07002193 mBluetoothHeadsetDevice)) {
Eric Laurentafbb0472011-12-15 09:04:23 -08002194 sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED,
Eric Laurentdc03c612011-04-01 10:59:41 -07002195 SENDMSG_REPLACE, 0, 0, null, 0);
2196 }
2197 } else if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL &&
2198 getBluetoothHeadset()) {
2199 mScoAudioState = SCO_STATE_DEACTIVATE_EXT_REQ;
2200 }
2201 }
2202 } else {
Eric Laurentd7454be2011-09-14 08:45:58 -07002203 clearAllScoClients(exceptPid, true);
Eric Laurentdc03c612011-04-01 10:59:41 -07002204 }
2205 }
2206 }
2207
2208 private void resetBluetoothSco() {
2209 synchronized(mScoClients) {
Eric Laurentd7454be2011-09-14 08:45:58 -07002210 clearAllScoClients(0, false);
Eric Laurentdc03c612011-04-01 10:59:41 -07002211 mScoAudioState = SCO_STATE_INACTIVE;
2212 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
2213 }
2214 }
2215
2216 private void broadcastScoConnectionState(int state) {
Eric Laurent2a57ca92013-03-07 17:29:27 -08002217 sendMsg(mAudioHandler, MSG_BROADCAST_BT_CONNECTION_STATE,
2218 SENDMSG_QUEUE, state, 0, null, 0);
2219 }
2220
2221 private void onBroadcastScoConnectionState(int state) {
Eric Laurentdc03c612011-04-01 10:59:41 -07002222 if (state != mScoConnectionState) {
2223 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
2224 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
2225 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE,
2226 mScoConnectionState);
Dianne Hackborn5ac72a22012-08-29 18:32:08 -07002227 sendStickyBroadcastToAll(newIntent);
Eric Laurentdc03c612011-04-01 10:59:41 -07002228 mScoConnectionState = state;
2229 }
2230 }
2231
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07002232 private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
2233 new BluetoothProfile.ServiceListener() {
2234 public void onServiceConnected(int profile, BluetoothProfile proxy) {
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08002235 BluetoothDevice btDevice;
2236 List<BluetoothDevice> deviceList;
2237 switch(profile) {
2238 case BluetoothProfile.A2DP:
2239 BluetoothA2dp a2dp = (BluetoothA2dp) proxy;
2240 deviceList = a2dp.getConnectedDevices();
Eric Laurent62ef7672010-11-24 10:58:32 -08002241 if (deviceList.size() > 0) {
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08002242 btDevice = deviceList.get(0);
Eric Laurentb1fbaac2012-05-29 09:24:28 -07002243 synchronized (mConnectedDevices) {
2244 int state = a2dp.getConnectionState(btDevice);
2245 int delay = checkSendBecomingNoisyIntent(
2246 AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
2247 (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0);
Jean-Michel Trivie12c39b2012-06-06 10:51:58 -07002248 queueMsgUnderWakeLock(mAudioHandler,
Eric Laurentb1fbaac2012-05-29 09:24:28 -07002249 MSG_SET_A2DP_CONNECTION_STATE,
Eric Laurentb1fbaac2012-05-29 09:24:28 -07002250 state,
2251 0,
2252 btDevice,
2253 delay);
2254 }
Eric Laurent62ef7672010-11-24 10:58:32 -08002255 }
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08002256 break;
2257
2258 case BluetoothProfile.HEADSET:
2259 synchronized (mScoClients) {
2260 // Discard timeout message
2261 mAudioHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
2262 mBluetoothHeadset = (BluetoothHeadset) proxy;
2263 deviceList = mBluetoothHeadset.getConnectedDevices();
2264 if (deviceList.size() > 0) {
2265 mBluetoothHeadsetDevice = deviceList.get(0);
2266 } else {
2267 mBluetoothHeadsetDevice = null;
2268 }
2269 // Refresh SCO audio state
2270 checkScoAudioState();
2271 // Continue pending action if any
2272 if (mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
2273 mScoAudioState == SCO_STATE_DEACTIVATE_REQ ||
2274 mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
2275 boolean status = false;
2276 if (mBluetoothHeadsetDevice != null) {
2277 switch (mScoAudioState) {
2278 case SCO_STATE_ACTIVATE_REQ:
2279 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
Eric Laurentc18c9132013-04-12 17:24:56 -07002280 if (mScoAudioMode == SCO_MODE_RAW) {
2281 status = mBluetoothHeadset.connectAudio();
2282 } else {
2283 status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
2284 mBluetoothHeadsetDevice);
2285 }
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08002286 break;
2287 case SCO_STATE_DEACTIVATE_REQ:
Eric Laurentc18c9132013-04-12 17:24:56 -07002288 if (mScoAudioMode == SCO_MODE_RAW) {
2289 status = mBluetoothHeadset.disconnectAudio();
2290 } else {
2291 status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
2292 mBluetoothHeadsetDevice);
2293 }
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08002294 break;
2295 case SCO_STATE_DEACTIVATE_EXT_REQ:
2296 status = mBluetoothHeadset.stopVoiceRecognition(
2297 mBluetoothHeadsetDevice);
2298 }
2299 }
2300 if (!status) {
Eric Laurentafbb0472011-12-15 09:04:23 -08002301 sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED,
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08002302 SENDMSG_REPLACE, 0, 0, null, 0);
Eric Laurentdc03c612011-04-01 10:59:41 -07002303 }
2304 }
Eric Laurentdc03c612011-04-01 10:59:41 -07002305 }
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08002306 break;
2307
2308 default:
2309 break;
Eric Laurent3def1ee2010-03-17 23:26:26 -07002310 }
2311 }
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07002312 public void onServiceDisconnected(int profile) {
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08002313 switch(profile) {
2314 case BluetoothProfile.A2DP:
2315 synchronized (mConnectedDevices) {
2316 if (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)) {
2317 makeA2dpDeviceUnavailableNow(
2318 mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
2319 }
2320 }
2321 break;
2322
2323 case BluetoothProfile.HEADSET:
2324 synchronized (mScoClients) {
2325 mBluetoothHeadset = null;
2326 }
2327 break;
2328
2329 default:
2330 break;
Eric Laurent3def1ee2010-03-17 23:26:26 -07002331 }
2332 }
2333 };
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08002334
Jean-Michel Trivi2ac2afe2012-08-21 11:16:55 -07002335 /** see AudioManager.setRemoteSubmixOn(boolean on) */
2336 public void setRemoteSubmixOn(boolean on, int address) {
2337 sendMsg(mAudioHandler, MSG_SET_RSX_CONNECTION_STATE,
2338 SENDMSG_REPLACE /* replace with QUEUE when multiple addresses are supported */,
2339 on ? 1 : 0 /*arg1*/,
2340 address /*arg2*/,
2341 null/*obj*/, 0/*delay*/);
Jean-Michel Trivi2ac2afe2012-08-21 11:16:55 -07002342 }
2343
2344 private void onSetRsxConnectionState(int available, int address) {
2345 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_REMOTE_SUBMIX,
2346 available == 1 ?
2347 AudioSystem.DEVICE_STATE_AVAILABLE : AudioSystem.DEVICE_STATE_UNAVAILABLE,
2348 String.valueOf(address) /*device_address*/);
2349 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_REMOTE_SUBMIX,
2350 available == 1 ?
2351 AudioSystem.DEVICE_STATE_AVAILABLE : AudioSystem.DEVICE_STATE_UNAVAILABLE,
2352 String.valueOf(address) /*device_address*/);
2353 }
2354
Eric Laurentc34dcc12012-09-10 13:51:52 -07002355 private void onCheckMusicActive() {
Eric Laurentd640bd32012-09-28 18:01:48 -07002356 synchronized (mSafeMediaVolumeState) {
2357 if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_INACTIVE) {
Eric Laurentc34dcc12012-09-10 13:51:52 -07002358 int device = getDeviceForStream(AudioSystem.STREAM_MUSIC);
2359
2360 if ((device & mSafeMediaVolumeDevices) != 0) {
2361 sendMsg(mAudioHandler,
2362 MSG_CHECK_MUSIC_ACTIVE,
2363 SENDMSG_REPLACE,
Eric Laurentf1a457d2012-09-20 16:27:23 -07002364 0,
Eric Laurentc34dcc12012-09-10 13:51:52 -07002365 0,
2366 null,
2367 MUSIC_ACTIVE_POLL_PERIOD_MS);
Eric Laurent42b041e2013-03-29 11:36:03 -07002368 int index = mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(device);
Eric Laurentf1a457d2012-09-20 16:27:23 -07002369 if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0) &&
2370 (index > mSafeMediaVolumeIndex)) {
Eric Laurentc34dcc12012-09-10 13:51:52 -07002371 // Approximate cumulative active music time
2372 mMusicActiveMs += MUSIC_ACTIVE_POLL_PERIOD_MS;
2373 if (mMusicActiveMs > UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX) {
2374 setSafeMediaVolumeEnabled(true);
2375 mMusicActiveMs = 0;
Eric Laurentc34dcc12012-09-10 13:51:52 -07002376 }
2377 }
2378 }
2379 }
2380 }
2381 }
2382
Eric Laurentd640bd32012-09-28 18:01:48 -07002383 private void onConfigureSafeVolume(boolean force) {
2384 synchronized (mSafeMediaVolumeState) {
2385 int mcc = mContext.getResources().getConfiguration().mcc;
2386 if ((mMcc != mcc) || ((mMcc == 0) && force)) {
2387 mSafeMediaVolumeIndex = mContext.getResources().getInteger(
2388 com.android.internal.R.integer.config_safe_media_volume_index) * 10;
2389 boolean safeMediaVolumeEnabled = mContext.getResources().getBoolean(
2390 com.android.internal.R.bool.config_safe_media_volume_enabled);
Eric Laurent05274f32012-11-29 12:48:18 -08002391
2392 // The persisted state is either "disabled" or "active": this is the state applied
2393 // next time we boot and cannot be "inactive"
2394 int persistedState;
Eric Laurentd640bd32012-09-28 18:01:48 -07002395 if (safeMediaVolumeEnabled) {
Eric Laurent05274f32012-11-29 12:48:18 -08002396 persistedState = SAFE_MEDIA_VOLUME_ACTIVE;
2397 // The state can already be "inactive" here if the user has forced it before
2398 // the 30 seconds timeout for forced configuration. In this case we don't reset
2399 // it to "active".
2400 if (mSafeMediaVolumeState != SAFE_MEDIA_VOLUME_INACTIVE) {
2401 mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_ACTIVE;
2402 enforceSafeMediaVolume();
2403 }
Eric Laurentd640bd32012-09-28 18:01:48 -07002404 } else {
Eric Laurent05274f32012-11-29 12:48:18 -08002405 persistedState = SAFE_MEDIA_VOLUME_DISABLED;
Eric Laurentd640bd32012-09-28 18:01:48 -07002406 mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_DISABLED;
2407 }
2408 mMcc = mcc;
Eric Laurent05274f32012-11-29 12:48:18 -08002409 sendMsg(mAudioHandler,
2410 MSG_PERSIST_SAFE_VOLUME_STATE,
2411 SENDMSG_QUEUE,
2412 persistedState,
2413 0,
2414 null,
2415 0);
Eric Laurentd640bd32012-09-28 18:01:48 -07002416 }
2417 }
2418 }
2419
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002420 ///////////////////////////////////////////////////////////////////////////
2421 // Internal methods
2422 ///////////////////////////////////////////////////////////////////////////
2423
2424 /**
2425 * Checks if the adjustment should change ringer mode instead of just
2426 * adjusting volume. If so, this will set the proper ringer mode and volume
2427 * indices on the stream states.
2428 */
Eric Laurent24482012012-05-10 09:41:17 -07002429 private boolean checkForRingerModeChange(int oldIndex, int direction, int step) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002430 boolean adjustVolumeIndex = true;
Glenn Kastenba195eb2011-12-13 09:30:40 -08002431 int ringerMode = getRingerMode();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002432
Eric Laurentbffc3d12012-05-07 17:43:49 -07002433 switch (ringerMode) {
2434 case RINGER_MODE_NORMAL:
2435 if (direction == AudioManager.ADJUST_LOWER) {
2436 if (mHasVibrator) {
Eric Laurent24482012012-05-10 09:41:17 -07002437 // "step" is the delta in internal index units corresponding to a
2438 // change of 1 in UI index units.
2439 // Because of rounding when rescaling from one stream index range to its alias
2440 // index range, we cannot simply test oldIndex == step:
2441 // (step <= oldIndex < 2 * step) is equivalent to: (old UI index == 1)
2442 if (step <= oldIndex && oldIndex < 2 * step) {
Eric Laurentbffc3d12012-05-07 17:43:49 -07002443 ringerMode = RINGER_MODE_VIBRATE;
2444 }
2445 } else {
Eric Laurent24482012012-05-10 09:41:17 -07002446 // (oldIndex < step) is equivalent to (old UI index == 0)
2447 if ((oldIndex < step) && mPrevVolDirection != AudioManager.ADJUST_LOWER) {
Eric Laurentbffc3d12012-05-07 17:43:49 -07002448 ringerMode = RINGER_MODE_SILENT;
2449 }
Eric Laurent3d4c06f2011-08-15 19:58:28 -07002450 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002451 }
Eric Laurentbffc3d12012-05-07 17:43:49 -07002452 break;
2453 case RINGER_MODE_VIBRATE:
2454 if (!mHasVibrator) {
2455 Log.e(TAG, "checkForRingerModeChange() current ringer mode is vibrate" +
2456 "but no vibrator is present");
2457 break;
2458 }
Amith Yamasanic696a532011-10-28 17:02:37 -07002459 if ((direction == AudioManager.ADJUST_LOWER)) {
Amith Yamasanic696a532011-10-28 17:02:37 -07002460 if (mPrevVolDirection != AudioManager.ADJUST_LOWER) {
Eric Laurentbffc3d12012-05-07 17:43:49 -07002461 ringerMode = RINGER_MODE_SILENT;
Amith Yamasanic696a532011-10-28 17:02:37 -07002462 }
2463 } else if (direction == AudioManager.ADJUST_RAISE) {
Eric Laurentbffc3d12012-05-07 17:43:49 -07002464 ringerMode = RINGER_MODE_NORMAL;
Amith Yamasanic696a532011-10-28 17:02:37 -07002465 }
2466 adjustVolumeIndex = false;
Eric Laurentbffc3d12012-05-07 17:43:49 -07002467 break;
2468 case RINGER_MODE_SILENT:
Daniel Sandler6329bf72010-02-26 15:17:44 -05002469 if (direction == AudioManager.ADJUST_RAISE) {
Eric Laurentbffc3d12012-05-07 17:43:49 -07002470 if (mHasVibrator) {
2471 ringerMode = RINGER_MODE_VIBRATE;
2472 } else {
2473 ringerMode = RINGER_MODE_NORMAL;
2474 }
Daniel Sandler6329bf72010-02-26 15:17:44 -05002475 }
Eric Laurentb024c302011-10-14 17:19:27 -07002476 adjustVolumeIndex = false;
Eric Laurentbffc3d12012-05-07 17:43:49 -07002477 break;
2478 default:
2479 Log.e(TAG, "checkForRingerModeChange() wrong ringer mode: "+ringerMode);
2480 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002481 }
2482
Eric Laurentbffc3d12012-05-07 17:43:49 -07002483 setRingerMode(ringerMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002484
Eric Laurent25101b02011-02-02 09:33:30 -08002485 mPrevVolDirection = direction;
2486
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002487 return adjustVolumeIndex;
2488 }
2489
2490 public boolean isStreamAffectedByRingerMode(int streamType) {
Eric Laurent9bcf4012009-06-12 06:09:28 -07002491 return (mRingerModeAffectedStreams & (1 << streamType)) != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002492 }
2493
Eric Laurent5b4e6542010-03-19 20:02:21 -07002494 private boolean isStreamMutedByRingerMode(int streamType) {
2495 return (mRingerModeMutedStreams & (1 << streamType)) != 0;
2496 }
2497
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002498 public boolean isStreamAffectedByMute(int streamType) {
2499 return (mMuteAffectedStreams & (1 << streamType)) != 0;
2500 }
2501
2502 private void ensureValidDirection(int direction) {
2503 if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) {
2504 throw new IllegalArgumentException("Bad direction " + direction);
2505 }
2506 }
2507
Lei Zhang6c798972012-03-02 11:40:12 -08002508 private void ensureValidSteps(int steps) {
2509 if (Math.abs(steps) > MAX_BATCH_VOLUME_ADJUST_STEPS) {
2510 throw new IllegalArgumentException("Bad volume adjust steps " + steps);
2511 }
2512 }
2513
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002514 private void ensureValidStreamType(int streamType) {
2515 if (streamType < 0 || streamType >= mStreamStates.length) {
2516 throw new IllegalArgumentException("Bad stream type " + streamType);
2517 }
2518 }
2519
Eric Laurent6d517662012-04-23 18:42:39 -07002520 private boolean isInCommunication() {
2521 boolean isOffhook = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002522
Eric Laurent25101b02011-02-02 09:33:30 -08002523 if (mVoiceCapable) {
Eric Laurent25101b02011-02-02 09:33:30 -08002524 try {
2525 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
2526 if (phone != null) isOffhook = phone.isOffhook();
2527 } catch (RemoteException e) {
2528 Log.w(TAG, "Couldn't connect to phone service", e);
2529 }
Eric Laurent6d517662012-04-23 18:42:39 -07002530 }
2531 return (isOffhook || getMode() == AudioManager.MODE_IN_COMMUNICATION);
2532 }
Eric Laurent25101b02011-02-02 09:33:30 -08002533
Eric Laurent6d517662012-04-23 18:42:39 -07002534 private int getActiveStreamType(int suggestedStreamType) {
2535 if (mVoiceCapable) {
2536 if (isInCommunication()) {
Eric Laurent25101b02011-02-02 09:33:30 -08002537 if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
2538 == AudioSystem.FORCE_BT_SCO) {
2539 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
2540 return AudioSystem.STREAM_BLUETOOTH_SCO;
2541 } else {
2542 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
2543 return AudioSystem.STREAM_VOICE_CALL;
2544 }
Eric Laurent25101b02011-02-02 09:33:30 -08002545 } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07002546 // Having the suggested stream be USE_DEFAULT_STREAM_TYPE is how remote control
2547 // volume can have priority over STREAM_MUSIC
2548 if (checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
2549 if (DEBUG_VOL)
2550 Log.v(TAG, "getActiveStreamType: Forcing STREAM_REMOTE_MUSIC");
2551 return STREAM_REMOTE_MUSIC;
Eric Laurent9903e262012-09-21 18:10:32 -07002552 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC,
2553 DEFAULT_STREAM_TYPE_OVERRIDE_DELAY_MS)) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07002554 if (DEBUG_VOL)
2555 Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC stream active");
2556 return AudioSystem.STREAM_MUSIC;
2557 } else {
2558 if (DEBUG_VOL)
2559 Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING b/c default");
2560 return AudioSystem.STREAM_RING;
2561 }
2562 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
2563 if (DEBUG_VOL)
2564 Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC stream active");
2565 return AudioSystem.STREAM_MUSIC;
Joe Onoratoc7fcba42011-01-05 16:53:11 -08002566 } else {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07002567 if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Returning suggested type "
2568 + suggestedStreamType);
Eric Laurent25101b02011-02-02 09:33:30 -08002569 return suggestedStreamType;
2570 }
2571 } else {
Eric Laurent6d517662012-04-23 18:42:39 -07002572 if (isInCommunication()) {
Eric Laurent25101b02011-02-02 09:33:30 -08002573 if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
2574 == AudioSystem.FORCE_BT_SCO) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07002575 if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO");
Eric Laurent25101b02011-02-02 09:33:30 -08002576 return AudioSystem.STREAM_BLUETOOTH_SCO;
2577 } else {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07002578 if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL");
Eric Laurent25101b02011-02-02 09:33:30 -08002579 return AudioSystem.STREAM_VOICE_CALL;
2580 }
2581 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_NOTIFICATION,
Eric Laurent9903e262012-09-21 18:10:32 -07002582 DEFAULT_STREAM_TYPE_OVERRIDE_DELAY_MS) ||
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07002583 AudioSystem.isStreamActive(AudioSystem.STREAM_RING,
Eric Laurent9903e262012-09-21 18:10:32 -07002584 DEFAULT_STREAM_TYPE_OVERRIDE_DELAY_MS)) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07002585 if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION");
Eric Laurent25101b02011-02-02 09:33:30 -08002586 return AudioSystem.STREAM_NOTIFICATION;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07002587 } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
2588 if (checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
2589 // Having the suggested stream be USE_DEFAULT_STREAM_TYPE is how remote control
2590 // volume can have priority over STREAM_MUSIC
2591 if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_REMOTE_MUSIC");
2592 return STREAM_REMOTE_MUSIC;
2593 } else {
2594 if (DEBUG_VOL)
2595 Log.v(TAG, "getActiveStreamType: using STREAM_MUSIC as default");
2596 return AudioSystem.STREAM_MUSIC;
2597 }
Eric Laurent25101b02011-02-02 09:33:30 -08002598 } else {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07002599 if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Returning suggested type "
2600 + suggestedStreamType);
Eric Laurent25101b02011-02-02 09:33:30 -08002601 return suggestedStreamType;
Joe Onoratoc7fcba42011-01-05 16:53:11 -08002602 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002603 }
2604 }
2605
Glenn Kastenba195eb2011-12-13 09:30:40 -08002606 private void broadcastRingerMode(int ringerMode) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002607 // Send sticky broadcast
Dianne Hackborn1c633fc2009-12-08 19:45:14 -08002608 Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION);
Glenn Kastenba195eb2011-12-13 09:30:40 -08002609 broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, ringerMode);
Dianne Hackborn1c633fc2009-12-08 19:45:14 -08002610 broadcast.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
2611 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
Dianne Hackborn5ac72a22012-08-29 18:32:08 -07002612 sendStickyBroadcastToAll(broadcast);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002613 }
2614
2615 private void broadcastVibrateSetting(int vibrateType) {
2616 // Send broadcast
2617 if (ActivityManagerNative.isSystemReady()) {
2618 Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
2619 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType);
2620 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType));
Dianne Hackborn5ac72a22012-08-29 18:32:08 -07002621 sendBroadcastToAll(broadcast);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002622 }
2623 }
2624
2625 // Message helper methods
Jean-Michel Trivi2d8dab52012-05-30 18:13:59 -07002626 /**
2627 * Queue a message on the given handler's message queue, after acquiring the service wake lock.
2628 * Note that the wake lock needs to be released after the message has been handled.
2629 */
2630 private void queueMsgUnderWakeLock(Handler handler, int msg,
2631 int arg1, int arg2, Object obj, int delay) {
2632 mMediaEventWakeLock.acquire();
2633 sendMsg(handler, msg, SENDMSG_QUEUE, arg1, arg2, obj, delay);
2634 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002635
Eric Laurentafbb0472011-12-15 09:04:23 -08002636 private static void sendMsg(Handler handler, int msg,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002637 int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002638
2639 if (existingMsgPolicy == SENDMSG_REPLACE) {
2640 handler.removeMessages(msg);
2641 } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
2642 return;
2643 }
2644
Eric Laurentafbb0472011-12-15 09:04:23 -08002645 handler.sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002646 }
2647
2648 boolean checkAudioSettingsPermission(String method) {
2649 if (mContext.checkCallingOrSelfPermission("android.permission.MODIFY_AUDIO_SETTINGS")
2650 == PackageManager.PERMISSION_GRANTED) {
2651 return true;
2652 }
2653 String msg = "Audio Settings Permission Denial: " + method + " from pid="
2654 + Binder.getCallingPid()
2655 + ", uid=" + Binder.getCallingUid();
2656 Log.w(TAG, msg);
2657 return false;
2658 }
2659
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002660 private int getDeviceForStream(int stream) {
2661 int device = AudioSystem.getDevicesForStream(stream);
2662 if ((device & (device - 1)) != 0) {
2663 // Multiple device selection is either:
2664 // - speaker + one other device: give priority to speaker in this case.
2665 // - one A2DP device + another device: happens with duplicated output. In this case
2666 // retain the device on the A2DP output as the other must not correspond to an active
2667 // selection if not the speaker.
2668 if ((device & AudioSystem.DEVICE_OUT_SPEAKER) != 0) {
2669 device = AudioSystem.DEVICE_OUT_SPEAKER;
2670 } else {
2671 device &= AudioSystem.DEVICE_OUT_ALL_A2DP;
2672 }
2673 }
2674 return device;
2675 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002676
Eric Laurentb1fbaac2012-05-29 09:24:28 -07002677 public void setWiredDeviceConnectionState(int device, int state, String name) {
2678 synchronized (mConnectedDevices) {
2679 int delay = checkSendBecomingNoisyIntent(device, state);
Jean-Michel Trivi2d8dab52012-05-30 18:13:59 -07002680 queueMsgUnderWakeLock(mAudioHandler,
Eric Laurentb1fbaac2012-05-29 09:24:28 -07002681 MSG_SET_WIRED_DEVICE_CONNECTION_STATE,
Eric Laurentb1fbaac2012-05-29 09:24:28 -07002682 device,
2683 state,
2684 name,
2685 delay);
2686 }
2687 }
2688
2689 public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state)
2690 {
2691 int delay;
2692 synchronized (mConnectedDevices) {
2693 delay = checkSendBecomingNoisyIntent(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
2694 (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0);
Jean-Michel Trivi2d8dab52012-05-30 18:13:59 -07002695 queueMsgUnderWakeLock(mAudioHandler,
Eric Laurentb1fbaac2012-05-29 09:24:28 -07002696 MSG_SET_A2DP_CONNECTION_STATE,
Eric Laurentb1fbaac2012-05-29 09:24:28 -07002697 state,
2698 0,
2699 device,
2700 delay);
2701 }
2702 return delay;
2703 }
2704
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002705 ///////////////////////////////////////////////////////////////////////////
2706 // Inner classes
2707 ///////////////////////////////////////////////////////////////////////////
2708
2709 public class VolumeStreamState {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002710 private final int mStreamType;
2711
Jean-Michel Trivi11a74a72009-10-27 17:39:30 -07002712 private String mVolumeIndexSettingName;
Eric Laurenta553c252009-07-17 12:17:14 -07002713 private int mIndexMax;
Eric Laurent3172d5e2012-05-09 11:38:16 -07002714 private final ConcurrentHashMap<Integer, Integer> mIndex =
2715 new ConcurrentHashMap<Integer, Integer>(8, 0.75f, 4);
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002716 private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo clients death
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002717
Eric Laurenta553c252009-07-17 12:17:14 -07002718 private VolumeStreamState(String settingName, int streamType) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002719
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002720 mVolumeIndexSettingName = settingName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002721
2722 mStreamType = streamType;
Jared Suttles59820132009-08-13 21:50:52 -05002723 mIndexMax = MAX_STREAM_VOLUME[streamType];
Eric Laurenta553c252009-07-17 12:17:14 -07002724 AudioSystem.initStreamVolume(streamType, 0, mIndexMax);
2725 mIndexMax *= 10;
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002726
Eric Laurent33902db2012-10-07 16:15:07 -07002727 // mDeathHandlers must be created before calling readSettings()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002728 mDeathHandlers = new ArrayList<VolumeDeathHandler>();
Eric Laurent33902db2012-10-07 16:15:07 -07002729
2730 readSettings();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002731 }
2732
Eric Laurent42b041e2013-03-29 11:36:03 -07002733 public String getSettingNameForDevice(int device) {
2734 String name = mVolumeIndexSettingName;
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002735 String suffix = AudioSystem.getDeviceName(device);
2736 if (suffix.isEmpty()) {
2737 return name;
2738 }
2739 return name + "_" + suffix;
Jean-Michel Trivi11a74a72009-10-27 17:39:30 -07002740 }
2741
Eric Laurent3172d5e2012-05-09 11:38:16 -07002742 public synchronized void readSettings() {
Eric Laurent83a017b2013-03-19 18:15:31 -07002743 // force maximum volume on all streams if fixed volume property is set
2744 if (mUseFixedVolume) {
2745 mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, mIndexMax);
Eric Laurent83a017b2013-03-19 18:15:31 -07002746 return;
2747 }
Eric Laurent002e9d32012-10-01 19:39:04 -07002748 // do not read system stream volume from settings: this stream is always aliased
2749 // to another stream type and its volume is never persisted. Values in settings can
2750 // only be stale values
Eric Laurentdd45d012012-10-08 09:04:34 -07002751 if ((mStreamType == AudioSystem.STREAM_SYSTEM) ||
Eric Laurent002e9d32012-10-01 19:39:04 -07002752 (mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED)) {
Eric Laurentdd45d012012-10-08 09:04:34 -07002753 int index = 10 * AudioManager.DEFAULT_STREAM_VOLUME[mStreamType];
2754 synchronized (mCameraSoundForced) {
2755 if (mCameraSoundForced) {
2756 index = mIndexMax;
2757 }
2758 }
Eric Laurent42b041e2013-03-29 11:36:03 -07002759 mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index);
Eric Laurent002e9d32012-10-01 19:39:04 -07002760 return;
2761 }
2762
Eric Laurent83a017b2013-03-19 18:15:31 -07002763 int remainingDevices = AudioSystem.DEVICE_OUT_ALL;
2764
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002765 for (int i = 0; remainingDevices != 0; i++) {
2766 int device = (1 << i);
2767 if ((device & remainingDevices) == 0) {
2768 continue;
2769 }
2770 remainingDevices &= ~device;
2771
2772 // retrieve current volume for device
Eric Laurent42b041e2013-03-29 11:36:03 -07002773 String name = getSettingNameForDevice(device);
Eric Laurent9ac8d0c2012-03-07 10:45:36 -08002774 // if no volume stored for current stream and device, use default volume if default
2775 // device, continue otherwise
2776 int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT) ?
2777 AudioManager.DEFAULT_STREAM_VOLUME[mStreamType] : -1;
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07002778 int index = Settings.System.getIntForUser(
2779 mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002780 if (index == -1) {
2781 continue;
2782 }
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002783
Eric Laurent3ef75492012-11-28 12:12:23 -08002784 // ignore settings for fixed volume devices: volume should always be at max or 0
2785 if ((mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_MUSIC) &&
2786 ((device & mFixedVolumeDevices) != 0)) {
Eric Laurent42b041e2013-03-29 11:36:03 -07002787 mIndex.put(device, (index != 0) ? mIndexMax : 0);
2788 } else {
Eric Laurentdd45d012012-10-08 09:04:34 -07002789 mIndex.put(device, getValidIndex(10 * index));
2790 }
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002791 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002792 }
2793
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002794 public void applyDeviceVolume(int device) {
Eric Laurent42b041e2013-03-29 11:36:03 -07002795 int index;
2796 if (isMuted()) {
2797 index = 0;
2798 } else {
2799 index = (getIndex(device) + 5)/10;
2800 }
2801 AudioSystem.setStreamVolumeIndex(mStreamType, index, device);
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002802 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002803
Eric Laurent3172d5e2012-05-09 11:38:16 -07002804 public synchronized void applyAllVolumes() {
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002805 // apply default volume first: by convention this will reset all
2806 // devices volumes in audio policy manager to the supplied value
Eric Laurent42b041e2013-03-29 11:36:03 -07002807 int index;
2808 if (isMuted()) {
2809 index = 0;
2810 } else {
2811 index = (getIndex(AudioSystem.DEVICE_OUT_DEFAULT) + 5)/10;
2812 }
2813 AudioSystem.setStreamVolumeIndex(mStreamType, index, AudioSystem.DEVICE_OUT_DEFAULT);
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002814 // then apply device specific volumes
2815 Set set = mIndex.entrySet();
2816 Iterator i = set.iterator();
2817 while (i.hasNext()) {
2818 Map.Entry entry = (Map.Entry)i.next();
2819 int device = ((Integer)entry.getKey()).intValue();
2820 if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
Eric Laurent42b041e2013-03-29 11:36:03 -07002821 if (isMuted()) {
2822 index = 0;
2823 } else {
2824 index = ((Integer)entry.getValue() + 5)/10;
2825 }
2826 AudioSystem.setStreamVolumeIndex(mStreamType, index, device);
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002827 }
2828 }
2829 }
2830
2831 public boolean adjustIndex(int deltaIndex, int device) {
Eric Laurent42b041e2013-03-29 11:36:03 -07002832 return setIndex(getIndex(device) + deltaIndex,
2833 device);
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002834 }
2835
Eric Laurent42b041e2013-03-29 11:36:03 -07002836 public synchronized boolean setIndex(int index, int device) {
2837 int oldIndex = getIndex(device);
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002838 index = getValidIndex(index);
Eric Laurentdd45d012012-10-08 09:04:34 -07002839 synchronized (mCameraSoundForced) {
2840 if ((mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) && mCameraSoundForced) {
2841 index = mIndexMax;
2842 }
2843 }
Eric Laurent83a017b2013-03-19 18:15:31 -07002844 mIndex.put(device, index);
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002845
2846 if (oldIndex != index) {
Eric Laurenta553c252009-07-17 12:17:14 -07002847 // Apply change to all streams using this one as alias
Eric Laurentbffc3d12012-05-07 17:43:49 -07002848 // if changing volume of current device, also change volume of current
2849 // device on aliased stream
2850 boolean currentDevice = (device == getDeviceForStream(mStreamType));
Eric Laurenta553c252009-07-17 12:17:14 -07002851 int numStreamTypes = AudioSystem.getNumStreamTypes();
2852 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
Eric Laurentbffc3d12012-05-07 17:43:49 -07002853 if (streamType != mStreamType &&
2854 mStreamVolumeAlias[streamType] == mStreamType) {
2855 int scaledIndex = rescaleIndex(index, mStreamType, streamType);
2856 mStreamStates[streamType].setIndex(scaledIndex,
Eric Laurent42b041e2013-03-29 11:36:03 -07002857 device);
Eric Laurentbffc3d12012-05-07 17:43:49 -07002858 if (currentDevice) {
2859 mStreamStates[streamType].setIndex(scaledIndex,
Eric Laurent42b041e2013-03-29 11:36:03 -07002860 getDeviceForStream(streamType));
Eric Laurentbffc3d12012-05-07 17:43:49 -07002861 }
Eric Laurenta553c252009-07-17 12:17:14 -07002862 }
2863 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002864 return true;
2865 } else {
2866 return false;
2867 }
2868 }
2869
Eric Laurent42b041e2013-03-29 11:36:03 -07002870 public synchronized int getIndex(int device) {
2871 Integer index = mIndex.get(device);
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002872 if (index == null) {
2873 // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
Eric Laurent42b041e2013-03-29 11:36:03 -07002874 index = mIndex.get(AudioSystem.DEVICE_OUT_DEFAULT);
Eric Laurent9bc8358d2011-11-18 16:43:31 -08002875 }
2876 return index.intValue();
Eric Laurent5b4e6542010-03-19 20:02:21 -07002877 }
2878
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002879 public int getMaxIndex() {
Eric Laurenta553c252009-07-17 12:17:14 -07002880 return mIndexMax;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002881 }
2882
Eric Laurent42b041e2013-03-29 11:36:03 -07002883 public synchronized void setAllIndexes(VolumeStreamState srcStream) {
2884 Set set = srcStream.mIndex.entrySet();
Eric Laurent6d517662012-04-23 18:42:39 -07002885 Iterator i = set.iterator();
2886 while (i.hasNext()) {
2887 Map.Entry entry = (Map.Entry)i.next();
2888 int device = ((Integer)entry.getKey()).intValue();
2889 int index = ((Integer)entry.getValue()).intValue();
2890 index = rescaleIndex(index, srcStream.getStreamType(), mStreamType);
Eric Laurent33902db2012-10-07 16:15:07 -07002891
Eric Laurent42b041e2013-03-29 11:36:03 -07002892 setIndex(index, device);
Eric Laurent6d517662012-04-23 18:42:39 -07002893 }
2894 }
2895
Eric Laurentdd45d012012-10-08 09:04:34 -07002896 public synchronized void setAllIndexesToMax() {
2897 Set set = mIndex.entrySet();
2898 Iterator i = set.iterator();
2899 while (i.hasNext()) {
2900 Map.Entry entry = (Map.Entry)i.next();
2901 entry.setValue(mIndexMax);
2902 }
Eric Laurentdd45d012012-10-08 09:04:34 -07002903 }
2904
Eric Laurent3172d5e2012-05-09 11:38:16 -07002905 public synchronized void mute(IBinder cb, boolean state) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002906 VolumeDeathHandler handler = getDeathHandler(cb, state);
2907 if (handler == null) {
2908 Log.e(TAG, "Could not get client death handler for stream: "+mStreamType);
2909 return;
2910 }
2911 handler.mute(state);
2912 }
2913
Eric Laurent6d517662012-04-23 18:42:39 -07002914 public int getStreamType() {
2915 return mStreamType;
2916 }
2917
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002918 private int getValidIndex(int index) {
2919 if (index < 0) {
2920 return 0;
Eric Laurent83a017b2013-03-19 18:15:31 -07002921 } else if (mUseFixedVolume || index > mIndexMax) {
Eric Laurenta553c252009-07-17 12:17:14 -07002922 return mIndexMax;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002923 }
2924
2925 return index;
2926 }
2927
2928 private class VolumeDeathHandler implements IBinder.DeathRecipient {
2929 private IBinder mICallback; // To be notified of client's death
2930 private int mMuteCount; // Number of active mutes for this client
2931
2932 VolumeDeathHandler(IBinder cb) {
2933 mICallback = cb;
2934 }
2935
Eric Laurent3172d5e2012-05-09 11:38:16 -07002936 // must be called while synchronized on parent VolumeStreamState
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002937 public void mute(boolean state) {
Eric Laurent42b041e2013-03-29 11:36:03 -07002938 boolean updateVolume = false;
Eric Laurent3172d5e2012-05-09 11:38:16 -07002939 if (state) {
2940 if (mMuteCount == 0) {
2941 // Register for client death notification
2942 try {
2943 // mICallback can be 0 if muted by AudioService
2944 if (mICallback != null) {
2945 mICallback.linkToDeath(this, 0);
2946 }
Eric Laurent42b041e2013-03-29 11:36:03 -07002947 VolumeStreamState.this.mDeathHandlers.add(this);
Eric Laurent3172d5e2012-05-09 11:38:16 -07002948 // If the stream is not yet muted by any client, set level to 0
Eric Laurent42b041e2013-03-29 11:36:03 -07002949 if (!VolumeStreamState.this.isMuted()) {
2950 updateVolume = true;
Eric Laurent3172d5e2012-05-09 11:38:16 -07002951 }
2952 } catch (RemoteException e) {
2953 // Client has died!
2954 binderDied();
2955 return;
2956 }
2957 } else {
2958 Log.w(TAG, "stream: "+mStreamType+" was already muted by this client");
2959 }
2960 mMuteCount++;
2961 } else {
2962 if (mMuteCount == 0) {
2963 Log.e(TAG, "unexpected unmute for stream: "+mStreamType);
2964 } else {
2965 mMuteCount--;
2966 if (mMuteCount == 0) {
2967 // Unregister from client death notification
Eric Laurent42b041e2013-03-29 11:36:03 -07002968 VolumeStreamState.this.mDeathHandlers.remove(this);
Eric Laurent3172d5e2012-05-09 11:38:16 -07002969 // mICallback can be 0 if muted by AudioService
2970 if (mICallback != null) {
2971 mICallback.unlinkToDeath(this, 0);
2972 }
Eric Laurent42b041e2013-03-29 11:36:03 -07002973 if (!VolumeStreamState.this.isMuted()) {
2974 updateVolume = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002975 }
2976 }
2977 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002978 }
Eric Laurent42b041e2013-03-29 11:36:03 -07002979 if (updateVolume) {
2980 sendMsg(mAudioHandler,
2981 MSG_SET_ALL_VOLUMES,
2982 SENDMSG_QUEUE,
2983 0,
2984 0,
2985 VolumeStreamState.this, 0);
2986 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002987 }
2988
2989 public void binderDied() {
2990 Log.w(TAG, "Volume service client died for stream: "+mStreamType);
2991 if (mMuteCount != 0) {
2992 // Reset all active mute requests from this client.
2993 mMuteCount = 1;
2994 mute(false);
2995 }
2996 }
2997 }
2998
Eric Laurent3172d5e2012-05-09 11:38:16 -07002999 private synchronized int muteCount() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003000 int count = 0;
3001 int size = mDeathHandlers.size();
3002 for (int i = 0; i < size; i++) {
3003 count += mDeathHandlers.get(i).mMuteCount;
3004 }
3005 return count;
3006 }
3007
Eric Laurent42b041e2013-03-29 11:36:03 -07003008 private synchronized boolean isMuted() {
3009 return muteCount() != 0;
3010 }
3011
Eric Laurent3172d5e2012-05-09 11:38:16 -07003012 // only called by mute() which is already synchronized
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003013 private VolumeDeathHandler getDeathHandler(IBinder cb, boolean state) {
Eric Laurent3172d5e2012-05-09 11:38:16 -07003014 VolumeDeathHandler handler;
3015 int size = mDeathHandlers.size();
3016 for (int i = 0; i < size; i++) {
3017 handler = mDeathHandlers.get(i);
3018 if (cb == handler.mICallback) {
3019 return handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003020 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003021 }
Eric Laurent3172d5e2012-05-09 11:38:16 -07003022 // If this is the first mute request for this client, create a new
3023 // client death handler. Otherwise, it is an out of sequence unmute request.
3024 if (state) {
3025 handler = new VolumeDeathHandler(cb);
3026 } else {
3027 Log.w(TAG, "stream was not muted by this client");
3028 handler = null;
3029 }
3030 return handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003031 }
Eric Laurentbffc3d12012-05-07 17:43:49 -07003032
3033 private void dump(PrintWriter pw) {
Eric Laurentdd45d012012-10-08 09:04:34 -07003034 pw.print(" Mute count: ");
3035 pw.println(muteCount());
Eric Laurentbffc3d12012-05-07 17:43:49 -07003036 pw.print(" Current: ");
3037 Set set = mIndex.entrySet();
3038 Iterator i = set.iterator();
3039 while (i.hasNext()) {
3040 Map.Entry entry = (Map.Entry)i.next();
3041 pw.print(Integer.toHexString(((Integer)entry.getKey()).intValue())
3042 + ": " + ((((Integer)entry.getValue()).intValue() + 5) / 10)+", ");
3043 }
Eric Laurentbffc3d12012-05-07 17:43:49 -07003044 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003045 }
3046
3047 /** Thread that handles native AudioSystem control. */
3048 private class AudioSystemThread extends Thread {
3049 AudioSystemThread() {
3050 super("AudioService");
3051 }
3052
3053 @Override
3054 public void run() {
3055 // Set this thread up so the handler will work on it
3056 Looper.prepare();
3057
3058 synchronized(AudioService.this) {
3059 mAudioHandler = new AudioHandler();
3060
3061 // Notify that the handler has been created
3062 AudioService.this.notify();
3063 }
3064
3065 // Listen for volume change requests that are set by VolumePanel
3066 Looper.loop();
3067 }
3068 }
3069
3070 /** Handles internal volume messages in separate volume thread. */
3071 private class AudioHandler extends Handler {
3072
Eric Laurent9bc8358d2011-11-18 16:43:31 -08003073 private void setDeviceVolume(VolumeStreamState streamState, int device) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003074
Eric Laurent9bc8358d2011-11-18 16:43:31 -08003075 // Apply volume
3076 streamState.applyDeviceVolume(device);
Eric Laurenta553c252009-07-17 12:17:14 -07003077
3078 // Apply change to all streams using this one as alias
3079 int numStreamTypes = AudioSystem.getNumStreamTypes();
3080 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
3081 if (streamType != streamState.mStreamType &&
Eric Laurent6d517662012-04-23 18:42:39 -07003082 mStreamVolumeAlias[streamType] == streamState.mStreamType) {
3083 mStreamStates[streamType].applyDeviceVolume(getDeviceForStream(streamType));
Eric Laurenta553c252009-07-17 12:17:14 -07003084 }
3085 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003086
3087 // Post a persist volume msg
Eric Laurentafbb0472011-12-15 09:04:23 -08003088 sendMsg(mAudioHandler,
3089 MSG_PERSIST_VOLUME,
Eric Laurent98ad9b92012-02-15 17:21:37 -08003090 SENDMSG_QUEUE,
Eric Laurent9bc8358d2011-11-18 16:43:31 -08003091 device,
Eric Laurent42b041e2013-03-29 11:36:03 -07003092 0,
Eric Laurentafbb0472011-12-15 09:04:23 -08003093 streamState,
3094 PERSIST_DELAY);
3095
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003096 }
3097
Eric Laurent9bc8358d2011-11-18 16:43:31 -08003098 private void setAllVolumes(VolumeStreamState streamState) {
3099
3100 // Apply volume
3101 streamState.applyAllVolumes();
3102
3103 // Apply change to all streams using this one as alias
3104 int numStreamTypes = AudioSystem.getNumStreamTypes();
3105 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
3106 if (streamType != streamState.mStreamType &&
Eric Laurent6d517662012-04-23 18:42:39 -07003107 mStreamVolumeAlias[streamType] == streamState.mStreamType) {
Eric Laurent9bc8358d2011-11-18 16:43:31 -08003108 mStreamStates[streamType].applyAllVolumes();
3109 }
3110 }
3111 }
3112
Eric Laurent42b041e2013-03-29 11:36:03 -07003113 private void persistVolume(VolumeStreamState streamState, int device) {
Eric Laurent83a017b2013-03-19 18:15:31 -07003114 if (mUseFixedVolume) {
3115 return;
3116 }
Eric Laurent42b041e2013-03-29 11:36:03 -07003117 System.putIntForUser(mContentResolver,
3118 streamState.getSettingNameForDevice(device),
3119 (streamState.getIndex(device) + 5)/ 10,
3120 UserHandle.USER_CURRENT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003121 }
3122
Glenn Kastenba195eb2011-12-13 09:30:40 -08003123 private void persistRingerMode(int ringerMode) {
Eric Laurent83a017b2013-03-19 18:15:31 -07003124 if (mUseFixedVolume) {
3125 return;
3126 }
Jeff Sharkey8d9a1f62012-10-18 15:38:14 -07003127 Settings.Global.putInt(mContentResolver, Settings.Global.MODE_RINGER, ringerMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003128 }
3129
Eric Laurent5d3eb44a2013-03-21 15:35:10 -07003130 private boolean onLoadSoundEffects() {
3131 int status;
3132
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003133 synchronized (mSoundEffectsLock) {
Eric Laurent5d3eb44a2013-03-21 15:35:10 -07003134 if (!mBootCompleted) {
3135 Log.w(TAG, "onLoadSoundEffects() called before boot complete");
3136 return false;
3137 }
3138
3139 if (mSoundPool != null) {
3140 return true;
3141 }
3142
3143 loadTouchSoundAssets();
3144
3145 mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
3146 mSoundPoolCallBack = null;
3147 mSoundPoolListenerThread = new SoundPoolListenerThread();
3148 mSoundPoolListenerThread.start();
3149 int attempts = 3;
3150 while ((mSoundPoolCallBack == null) && (attempts-- > 0)) {
3151 try {
3152 // Wait for mSoundPoolCallBack to be set by the other thread
3153 mSoundEffectsLock.wait(SOUND_EFECTS_LOAD_TIMEOUT_MS);
3154 } catch (InterruptedException e) {
3155 Log.w(TAG, "Interrupted while waiting sound pool listener thread.");
3156 }
3157 }
3158
3159 if (mSoundPoolCallBack == null) {
3160 Log.w(TAG, "onLoadSoundEffects() SoundPool listener or thread creation error");
3161 if (mSoundPoolLooper != null) {
3162 mSoundPoolLooper.quit();
3163 mSoundPoolLooper = null;
3164 }
3165 mSoundPoolListenerThread = null;
3166 mSoundPool.release();
3167 mSoundPool = null;
3168 return false;
3169 }
3170 /*
3171 * poolId table: The value -1 in this table indicates that corresponding
3172 * file (same index in SOUND_EFFECT_FILES[] has not been loaded.
3173 * Once loaded, the value in poolId is the sample ID and the same
3174 * sample can be reused for another effect using the same file.
3175 */
3176 int[] poolId = new int[SOUND_EFFECT_FILES.size()];
3177 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.size(); fileIdx++) {
3178 poolId[fileIdx] = -1;
3179 }
3180 /*
3181 * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded.
3182 * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0:
3183 * this indicates we have a valid sample loaded for this effect.
3184 */
3185
3186 int numSamples = 0;
3187 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
3188 // Do not load sample if this effect uses the MediaPlayer
3189 if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) {
3190 continue;
3191 }
3192 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
3193 String filePath = Environment.getRootDirectory()
3194 + SOUND_EFFECTS_PATH
3195 + SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effect][0]);
3196 int sampleId = mSoundPool.load(filePath, 0);
3197 if (sampleId <= 0) {
3198 Log.w(TAG, "Soundpool could not load file: "+filePath);
3199 } else {
3200 SOUND_EFFECT_FILES_MAP[effect][1] = sampleId;
3201 poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId;
3202 numSamples++;
3203 }
3204 } else {
3205 SOUND_EFFECT_FILES_MAP[effect][1] =
3206 poolId[SOUND_EFFECT_FILES_MAP[effect][0]];
3207 }
3208 }
3209 // wait for all samples to be loaded
3210 if (numSamples > 0) {
3211 mSoundPoolCallBack.setSamples(poolId);
3212
3213 attempts = 3;
3214 status = 1;
3215 while ((status == 1) && (attempts-- > 0)) {
3216 try {
3217 mSoundEffectsLock.wait(SOUND_EFECTS_LOAD_TIMEOUT_MS);
3218 status = mSoundPoolCallBack.status();
3219 } catch (InterruptedException e) {
3220 Log.w(TAG, "Interrupted while waiting sound pool callback.");
3221 }
3222 }
3223 } else {
3224 status = -1;
3225 }
3226
3227 if (mSoundPoolLooper != null) {
3228 mSoundPoolLooper.quit();
3229 mSoundPoolLooper = null;
3230 }
3231 mSoundPoolListenerThread = null;
3232 if (status != 0) {
3233 Log.w(TAG,
3234 "onLoadSoundEffects(), Error "+status+ " while loading samples");
3235 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
3236 if (SOUND_EFFECT_FILES_MAP[effect][1] > 0) {
3237 SOUND_EFFECT_FILES_MAP[effect][1] = -1;
3238 }
3239 }
3240
3241 mSoundPool.release();
3242 mSoundPool = null;
3243 }
3244 }
3245 return (status == 0);
3246 }
3247
3248 /**
3249 * Unloads samples from the sound pool.
3250 * This method can be called to free some memory when
3251 * sound effects are disabled.
3252 */
3253 private void onUnloadSoundEffects() {
3254 synchronized (mSoundEffectsLock) {
3255 if (mSoundPool == null) {
3256 return;
3257 }
3258
3259 int[] poolId = new int[SOUND_EFFECT_FILES.size()];
3260 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.size(); fileIdx++) {
3261 poolId[fileIdx] = 0;
3262 }
3263
3264 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
3265 if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) {
3266 continue;
3267 }
3268 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) {
3269 mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]);
3270 SOUND_EFFECT_FILES_MAP[effect][1] = -1;
3271 poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1;
3272 }
3273 }
3274 mSoundPool.release();
3275 mSoundPool = null;
3276 }
3277 }
3278
3279 private void onPlaySoundEffect(int effectType, int volume) {
3280 synchronized (mSoundEffectsLock) {
3281
3282 onLoadSoundEffects();
3283
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003284 if (mSoundPool == null) {
3285 return;
3286 }
Eric Laurenta2ef57d2009-09-28 04:46:10 -07003287 float volFloat;
Eric Laurent25101b02011-02-02 09:33:30 -08003288 // use default if volume is not specified by caller
Eric Laurenta2ef57d2009-09-28 04:46:10 -07003289 if (volume < 0) {
Jean-Michel Trivif2b0c112012-07-09 11:59:11 -07003290 volFloat = (float)Math.pow(10, (float)sSoundEffectVolumeDb/20);
Eric Laurenta2ef57d2009-09-28 04:46:10 -07003291 } else {
3292 volFloat = (float) volume / 1000.0f;
3293 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003294
3295 if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) {
Eric Laurent5d3eb44a2013-03-21 15:35:10 -07003296 mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1],
3297 volFloat, volFloat, 0, 0, 1.0f);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003298 } else {
3299 MediaPlayer mediaPlayer = new MediaPlayer();
Glenn Kasten62b9aec2011-11-07 11:10:16 -08003300 try {
Eric Laurente78fced2013-03-15 16:03:47 -07003301 String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH +
3302 SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effectType][0]);
Glenn Kasten62b9aec2011-11-07 11:10:16 -08003303 mediaPlayer.setDataSource(filePath);
3304 mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM);
3305 mediaPlayer.prepare();
Glenn Kasten068225d2012-02-27 16:21:04 -08003306 mediaPlayer.setVolume(volFloat);
Glenn Kasten62b9aec2011-11-07 11:10:16 -08003307 mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
3308 public void onCompletion(MediaPlayer mp) {
3309 cleanupPlayer(mp);
3310 }
3311 });
3312 mediaPlayer.setOnErrorListener(new OnErrorListener() {
3313 public boolean onError(MediaPlayer mp, int what, int extra) {
3314 cleanupPlayer(mp);
3315 return true;
3316 }
3317 });
3318 mediaPlayer.start();
3319 } catch (IOException ex) {
3320 Log.w(TAG, "MediaPlayer IOException: "+ex);
3321 } catch (IllegalArgumentException ex) {
3322 Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex);
3323 } catch (IllegalStateException ex) {
3324 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003325 }
3326 }
3327 }
3328 }
3329
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003330 private void onHandlePersistMediaButtonReceiver(ComponentName receiver) {
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07003331 Settings.System.putStringForUser(mContentResolver,
3332 Settings.System.MEDIA_BUTTON_RECEIVER,
3333 receiver == null ? "" : receiver.flattenToString(),
3334 UserHandle.USER_CURRENT);
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07003335 }
3336
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003337 private void cleanupPlayer(MediaPlayer mp) {
3338 if (mp != null) {
3339 try {
3340 mp.stop();
3341 mp.release();
3342 } catch (IllegalStateException ex) {
3343 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
3344 }
3345 }
3346 }
3347
Eric Laurentfa640152011-03-12 15:59:51 -08003348 private void setForceUse(int usage, int config) {
3349 AudioSystem.setForceUse(usage, config);
3350 }
3351
Eric Laurent05274f32012-11-29 12:48:18 -08003352 private void onPersistSafeVolumeState(int state) {
3353 Settings.Global.putInt(mContentResolver,
3354 Settings.Global.AUDIO_SAFE_VOLUME_STATE,
3355 state);
3356 }
3357
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003358 @Override
3359 public void handleMessage(Message msg) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003360
Eric Laurentafbb0472011-12-15 09:04:23 -08003361 switch (msg.what) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003362
Eric Laurent9bc8358d2011-11-18 16:43:31 -08003363 case MSG_SET_DEVICE_VOLUME:
3364 setDeviceVolume((VolumeStreamState) msg.obj, msg.arg1);
3365 break;
3366
3367 case MSG_SET_ALL_VOLUMES:
3368 setAllVolumes((VolumeStreamState) msg.obj);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003369 break;
3370
3371 case MSG_PERSIST_VOLUME:
Eric Laurent42b041e2013-03-29 11:36:03 -07003372 persistVolume((VolumeStreamState) msg.obj, msg.arg1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003373 break;
3374
Mike Lockwood5c55a052011-12-15 17:21:44 -05003375 case MSG_PERSIST_MASTER_VOLUME:
Eric Laurent83a017b2013-03-19 18:15:31 -07003376 if (mUseFixedVolume) {
3377 return;
3378 }
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07003379 Settings.System.putFloatForUser(mContentResolver,
3380 Settings.System.VOLUME_MASTER,
3381 (float)msg.arg1 / (float)1000.0,
3382 UserHandle.USER_CURRENT);
Mike Lockwood5c55a052011-12-15 17:21:44 -05003383 break;
3384
Justin Koh57978ed2012-04-03 17:37:58 -07003385 case MSG_PERSIST_MASTER_VOLUME_MUTE:
Eric Laurent83a017b2013-03-19 18:15:31 -07003386 if (mUseFixedVolume) {
3387 return;
3388 }
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07003389 Settings.System.putIntForUser(mContentResolver,
3390 Settings.System.VOLUME_MASTER_MUTE,
3391 msg.arg1,
3392 UserHandle.USER_CURRENT);
Justin Koh57978ed2012-04-03 17:37:58 -07003393 break;
3394
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003395 case MSG_PERSIST_RINGER_MODE:
Glenn Kastenba195eb2011-12-13 09:30:40 -08003396 // note that the value persisted is the current ringer mode, not the
3397 // value of ringer mode as of the time the request was made to persist
3398 persistRingerMode(getRingerMode());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003399 break;
3400
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003401 case MSG_MEDIA_SERVER_DIED:
Eric Laurent89e74ba2009-09-30 18:26:36 -07003402 if (!mMediaServerOk) {
3403 Log.e(TAG, "Media server died.");
Eric Laurenta25c5d82011-02-10 11:04:34 -08003404 // Force creation of new IAudioFlinger interface so that we are notified
3405 // when new media_server process is back to life.
3406 AudioSystem.setErrorCallback(mAudioSystemCallback);
Eric Laurentafbb0472011-12-15 09:04:23 -08003407 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SENDMSG_NOOP, 0, 0,
Eric Laurent89e74ba2009-09-30 18:26:36 -07003408 null, 500);
3409 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003410 break;
3411
3412 case MSG_MEDIA_SERVER_STARTED:
3413 Log.e(TAG, "Media server started.");
Eric Laurent3c652ca2010-06-21 20:46:26 -07003414 // indicate to audio HAL that we start the reconfiguration phase after a media
3415 // server crash
3416 // Note that MSG_MEDIA_SERVER_STARTED message is only received when the media server
3417 // process restarts after a crash, not the first time it is started.
3418 AudioSystem.setParameters("restarting=true");
3419
Eric Laurentc42ac9d2009-07-29 08:53:03 -07003420 // Restore device connection states
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003421 synchronized (mConnectedDevices) {
3422 Set set = mConnectedDevices.entrySet();
3423 Iterator i = set.iterator();
Eric Laurent9bc8358d2011-11-18 16:43:31 -08003424 while (i.hasNext()) {
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003425 Map.Entry device = (Map.Entry)i.next();
3426 AudioSystem.setDeviceConnectionState(
3427 ((Integer)device.getKey()).intValue(),
3428 AudioSystem.DEVICE_STATE_AVAILABLE,
3429 (String)device.getValue());
3430 }
Eric Laurentc42ac9d2009-07-29 08:53:03 -07003431 }
Eric Laurentc42ac9d2009-07-29 08:53:03 -07003432 // Restore call state
3433 AudioSystem.setPhoneState(mMode);
3434
Eric Laurentd5603c12009-08-06 08:49:39 -07003435 // Restore forced usage for communcations and record
Eric Laurentc42ac9d2009-07-29 08:53:03 -07003436 AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm);
Eric Laurentd5603c12009-08-06 08:49:39 -07003437 AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm);
Eric Laurentdd45d012012-10-08 09:04:34 -07003438 AudioSystem.setForceUse(AudioSystem.FOR_SYSTEM, mCameraSoundForced ?
3439 AudioSystem.FORCE_SYSTEM_ENFORCED : AudioSystem.FORCE_NONE);
Eric Laurentc42ac9d2009-07-29 08:53:03 -07003440
Eric Laurenta553c252009-07-17 12:17:14 -07003441 // Restore stream volumes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003442 int numStreamTypes = AudioSystem.getNumStreamTypes();
3443 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003444 VolumeStreamState streamState = mStreamStates[streamType];
Eric Laurentc42ac9d2009-07-29 08:53:03 -07003445 AudioSystem.initStreamVolume(streamType, 0, (streamState.mIndexMax + 5) / 10);
Eric Laurent9bc8358d2011-11-18 16:43:31 -08003446
3447 streamState.applyAllVolumes();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003448 }
Eric Laurentc42ac9d2009-07-29 08:53:03 -07003449
3450 // Restore ringer mode
3451 setRingerModeInt(getRingerMode(), false);
Eric Laurent3c652ca2010-06-21 20:46:26 -07003452
Mike Lockwood90631542012-01-06 11:20:37 -05003453 // Restore master volume
3454 restoreMasterVolume();
3455
Jean-Michel Trivif26f0172012-04-25 16:23:20 -07003456 // Reset device orientation (if monitored for this device)
Eric Laurentd640bd32012-09-28 18:01:48 -07003457 if (mMonitorOrientation) {
Jean-Michel Trivif26f0172012-04-25 16:23:20 -07003458 setOrientationForAudioSystem();
3459 }
3460
Eric Laurent78472112012-05-21 08:57:21 -07003461 synchronized (mBluetoothA2dpEnabledLock) {
3462 AudioSystem.setForceUse(AudioSystem.FOR_MEDIA,
3463 mBluetoothA2dpEnabled ?
3464 AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP);
3465 }
Eric Laurentbff5ca52012-11-02 16:48:26 -07003466
3467 synchronized (mSettingsLock) {
3468 AudioSystem.setForceUse(AudioSystem.FOR_DOCK,
3469 mDockAudioMediaEnabled ?
3470 AudioSystem.FORCE_ANALOG_DOCK : AudioSystem.FORCE_NONE);
3471 }
3472
Eric Laurent3c652ca2010-06-21 20:46:26 -07003473 // indicate the end of reconfiguration phase to audio HAL
3474 AudioSystem.setParameters("restarting=false");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003475 break;
3476
Eric Laurent5d3eb44a2013-03-21 15:35:10 -07003477 case MSG_UNLOAD_SOUND_EFFECTS:
3478 onUnloadSoundEffects();
3479 break;
3480
Eric Laurent117b7bb2011-01-16 17:07:27 -08003481 case MSG_LOAD_SOUND_EFFECTS:
Eric Laurent5d3eb44a2013-03-21 15:35:10 -07003482 //FIXME: onLoadSoundEffects() should be executed in a separate thread as it
3483 // can take several dozens of milliseconds to complete
3484 boolean loaded = onLoadSoundEffects();
3485 if (msg.obj != null) {
3486 LoadSoundEffectReply reply = (LoadSoundEffectReply)msg.obj;
3487 synchronized (reply) {
3488 reply.mStatus = loaded ? 0 : -1;
3489 reply.notify();
3490 }
3491 }
Eric Laurent117b7bb2011-01-16 17:07:27 -08003492 break;
3493
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003494 case MSG_PLAY_SOUND_EFFECT:
Eric Laurent5d3eb44a2013-03-21 15:35:10 -07003495 onPlaySoundEffect(msg.arg1, msg.arg2);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003496 break;
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07003497
3498 case MSG_BTA2DP_DOCK_TIMEOUT:
3499 // msg.obj == address of BTA2DP device
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003500 synchronized (mConnectedDevices) {
3501 makeA2dpDeviceUnavailableNow( (String) msg.obj );
3502 }
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07003503 break;
Eric Laurentfa640152011-03-12 15:59:51 -08003504
3505 case MSG_SET_FORCE_USE:
Eric Laurentc390bed2012-07-03 12:24:05 -07003506 case MSG_SET_FORCE_BT_A2DP_USE:
Eric Laurentfa640152011-03-12 15:59:51 -08003507 setForceUse(msg.arg1, msg.arg2);
3508 break;
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07003509
3510 case MSG_PERSIST_MEDIABUTTONRECEIVER:
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07003511 onHandlePersistMediaButtonReceiver( (ComponentName) msg.obj );
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07003512 break;
Eric Laurentdc03c612011-04-01 10:59:41 -07003513
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003514 case MSG_RCDISPLAY_CLEAR:
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003515 onRcDisplayClear();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003516 break;
3517
3518 case MSG_RCDISPLAY_UPDATE:
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07003519 // msg.obj is guaranteed to be non null
3520 onRcDisplayUpdate( (RemoteControlStackEntry) msg.obj, msg.arg1);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07003521 break;
3522
Eric Laurentdc03c612011-04-01 10:59:41 -07003523 case MSG_BT_HEADSET_CNCT_FAILED:
3524 resetBluetoothSco();
3525 break;
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003526
3527 case MSG_SET_WIRED_DEVICE_CONNECTION_STATE:
3528 onSetWiredDeviceConnectionState(msg.arg1, msg.arg2, (String)msg.obj);
Jean-Michel Trivi2d8dab52012-05-30 18:13:59 -07003529 mMediaEventWakeLock.release();
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003530 break;
3531
3532 case MSG_SET_A2DP_CONNECTION_STATE:
3533 onSetA2dpConnectionState((BluetoothDevice)msg.obj, msg.arg1);
Jean-Michel Trivi2d8dab52012-05-30 18:13:59 -07003534 mMediaEventWakeLock.release();
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003535 break;
Dianne Hackborn632ca412012-06-14 19:34:10 -07003536
3537 case MSG_REPORT_NEW_ROUTES: {
3538 int N = mRoutesObservers.beginBroadcast();
3539 if (N > 0) {
3540 AudioRoutesInfo routes;
3541 synchronized (mCurAudioRoutes) {
3542 routes = new AudioRoutesInfo(mCurAudioRoutes);
3543 }
3544 while (N > 0) {
3545 N--;
3546 IAudioRoutesObserver obs = mRoutesObservers.getBroadcastItem(N);
3547 try {
3548 obs.dispatchAudioRoutesChanged(routes);
3549 } catch (RemoteException e) {
3550 }
3551 }
3552 }
3553 mRoutesObservers.finishBroadcast();
3554 break;
3555 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07003556
3557 case MSG_REEVALUATE_REMOTE:
3558 onReevaluateRemote();
3559 break;
Jean-Michel Trivif98de1e2012-06-20 13:40:22 -07003560
3561 case MSG_RCC_NEW_PLAYBACK_INFO:
3562 onNewPlaybackInfoForRcc(msg.arg1 /* rccId */, msg.arg2 /* key */,
3563 ((Integer)msg.obj).intValue() /* value */);
3564 break;
3565 case MSG_RCC_NEW_VOLUME_OBS:
3566 onRegisterVolumeObserverForRcc(msg.arg1 /* rccId */,
3567 (IRemoteVolumeObserver)msg.obj /* rvo */);
3568 break;
Jean-Michel Trivibc43b4c2013-03-22 09:30:50 -07003569 case MSG_RCC_NEW_PLAYBACK_STATE:
3570 onNewPlaybackStateForRcc(msg.arg1 /* rccId */, msg.arg2 /* state */,
3571 (RccPlaybackState)msg.obj /* newState */);
3572 break;
Jean-Michel Trivi3fbf67e2013-04-09 11:10:18 -07003573 case MSG_RCC_SEEK_REQUEST:
3574 onSetRemoteControlClientPlaybackPosition(msg.arg1 /* generationId */,
3575 ((Long)msg.obj).longValue() /* timeMs */);
Jean-Michel Trivi2ac2afe2012-08-21 11:16:55 -07003576
3577 case MSG_SET_RSX_CONNECTION_STATE:
3578 onSetRsxConnectionState(msg.arg1/*available*/, msg.arg2/*address*/);
3579 break;
Eric Laurentc34dcc12012-09-10 13:51:52 -07003580
3581 case MSG_CHECK_MUSIC_ACTIVE:
3582 onCheckMusicActive();
3583 break;
Eric Laurent5bfaeae2012-09-21 18:44:48 -07003584
3585 case MSG_BROADCAST_AUDIO_BECOMING_NOISY:
3586 onSendBecomingNoisyIntent();
3587 break;
Eric Laurentd640bd32012-09-28 18:01:48 -07003588
3589 case MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED:
3590 case MSG_CONFIGURE_SAFE_MEDIA_VOLUME:
3591 onConfigureSafeVolume((msg.what == MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED));
3592 break;
Eric Laurent05274f32012-11-29 12:48:18 -08003593 case MSG_PERSIST_SAFE_VOLUME_STATE:
3594 onPersistSafeVolumeState(msg.arg1);
3595 break;
Jean-Michel Trivia578c482012-12-28 11:19:49 -08003596
3597 case MSG_PROMOTE_RCC:
3598 onPromoteRcc(msg.arg1);
3599 break;
Eric Laurent2a57ca92013-03-07 17:29:27 -08003600
3601 case MSG_BROADCAST_BT_CONNECTION_STATE:
3602 onBroadcastScoConnectionState(msg.arg1);
3603 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003604 }
3605 }
3606 }
3607
Jason Parekhb1096152009-03-24 17:48:25 -07003608 private class SettingsObserver extends ContentObserver {
Eric Laurenta553c252009-07-17 12:17:14 -07003609
Jason Parekhb1096152009-03-24 17:48:25 -07003610 SettingsObserver() {
3611 super(new Handler());
3612 mContentResolver.registerContentObserver(Settings.System.getUriFor(
3613 Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this);
Eric Laurent7ee1e4f2012-10-26 18:11:21 -07003614 mContentResolver.registerContentObserver(Settings.Global.getUriFor(
3615 Settings.Global.DOCK_AUDIO_MEDIA_ENABLED), false, this);
Jason Parekhb1096152009-03-24 17:48:25 -07003616 }
3617
3618 @Override
3619 public void onChange(boolean selfChange) {
3620 super.onChange(selfChange);
Glenn Kastenba195eb2011-12-13 09:30:40 -08003621 // FIXME This synchronized is not necessary if mSettingsLock only protects mRingerMode.
3622 // However there appear to be some missing locks around mRingerModeMutedStreams
3623 // and mRingerModeAffectedStreams, so will leave this synchronized for now.
3624 // mRingerModeMutedStreams and mMuteAffectedStreams are safe (only accessed once).
Eric Laurenta553c252009-07-17 12:17:14 -07003625 synchronized (mSettingsLock) {
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07003626 int ringerModeAffectedStreams = Settings.System.getIntForUser(mContentResolver,
Eric Laurentc1d41662011-07-19 11:21:13 -07003627 Settings.System.MODE_RINGER_STREAMS_AFFECTED,
3628 ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07003629 (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)),
3630 UserHandle.USER_CURRENT);
Eric Laurentc1d41662011-07-19 11:21:13 -07003631 if (mVoiceCapable) {
3632 ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
3633 } else {
Eric Laurent402f7f22011-02-04 12:30:32 -08003634 ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
3635 }
Eric Laurentdd45d012012-10-08 09:04:34 -07003636 synchronized (mCameraSoundForced) {
3637 if (mCameraSoundForced) {
3638 ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
3639 } else {
3640 ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
3641 }
3642 }
Eric Laurenta553c252009-07-17 12:17:14 -07003643 if (ringerModeAffectedStreams != mRingerModeAffectedStreams) {
3644 /*
3645 * Ensure all stream types that should be affected by ringer mode
3646 * are in the proper state.
3647 */
3648 mRingerModeAffectedStreams = ringerModeAffectedStreams;
3649 setRingerModeInt(getRingerMode(), false);
3650 }
Eric Laurent7ee1e4f2012-10-26 18:11:21 -07003651 readDockAudioSettings(mContentResolver);
Eric Laurenta553c252009-07-17 12:17:14 -07003652 }
Jason Parekhb1096152009-03-24 17:48:25 -07003653 }
Jason Parekhb1096152009-03-24 17:48:25 -07003654 }
Eric Laurenta553c252009-07-17 12:17:14 -07003655
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003656 // must be called synchronized on mConnectedDevices
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07003657 private void makeA2dpDeviceAvailable(String address) {
Eric Laurent78472112012-05-21 08:57:21 -07003658 // enable A2DP before notifying A2DP connection to avoid unecessary processing in
3659 // audio policy manager
3660 setBluetoothA2dpOnInt(true);
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07003661 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
3662 AudioSystem.DEVICE_STATE_AVAILABLE,
3663 address);
3664 // Reset A2DP suspend state each time a new sink is connected
3665 AudioSystem.setParameters("A2dpSuspended=false");
3666 mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP),
3667 address);
3668 }
3669
Eric Laurent5bfaeae2012-09-21 18:44:48 -07003670 private void onSendBecomingNoisyIntent() {
Dianne Hackborn5ac72a22012-08-29 18:32:08 -07003671 sendBroadcastToAll(new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
Mike Lockwood98418182012-05-10 17:13:20 -07003672 }
3673
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003674 // must be called synchronized on mConnectedDevices
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07003675 private void makeA2dpDeviceUnavailableNow(String address) {
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07003676 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
3677 AudioSystem.DEVICE_STATE_UNAVAILABLE,
3678 address);
3679 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
3680 }
3681
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003682 // must be called synchronized on mConnectedDevices
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07003683 private void makeA2dpDeviceUnavailableLater(String address) {
Eric Laurent3b591262010-04-20 07:01:00 -07003684 // prevent any activity on the A2DP audio output to avoid unwanted
3685 // reconnection of the sink.
3686 AudioSystem.setParameters("A2dpSuspended=true");
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07003687 // the device will be made unavailable later, so consider it disconnected right away
3688 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
3689 // send the delayed message to make the device unavailable later
3690 Message msg = mAudioHandler.obtainMessage(MSG_BTA2DP_DOCK_TIMEOUT, address);
3691 mAudioHandler.sendMessageDelayed(msg, BTA2DP_DOCK_TIMEOUT_MILLIS);
3692
3693 }
3694
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003695 // must be called synchronized on mConnectedDevices
Jean-Michel Trivia847ba42010-04-23 11:49:29 -07003696 private void cancelA2dpDeviceTimeout() {
Jean-Michel Trivi4c637b92010-04-12 18:44:10 -07003697 mAudioHandler.removeMessages(MSG_BTA2DP_DOCK_TIMEOUT);
3698 }
3699
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003700 // must be called synchronized on mConnectedDevices
Jean-Michel Trivia847ba42010-04-23 11:49:29 -07003701 private boolean hasScheduledA2dpDockTimeout() {
3702 return mAudioHandler.hasMessages(MSG_BTA2DP_DOCK_TIMEOUT);
3703 }
3704
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003705 private void onSetA2dpConnectionState(BluetoothDevice btDevice, int state)
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003706 {
3707 if (btDevice == null) {
3708 return;
3709 }
3710 String address = btDevice.getAddress();
3711 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
3712 address = "";
3713 }
3714 synchronized (mConnectedDevices) {
3715 boolean isConnected =
3716 (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
3717 mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP).equals(address));
3718
3719 if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
3720 if (btDevice.isBluetoothDock()) {
3721 if (state == BluetoothProfile.STATE_DISCONNECTED) {
3722 // introduction of a delay for transient disconnections of docks when
3723 // power is rapidly turned off/on, this message will be canceled if
3724 // we reconnect the dock under a preset delay
3725 makeA2dpDeviceUnavailableLater(address);
3726 // the next time isConnected is evaluated, it will be false for the dock
3727 }
3728 } else {
3729 makeA2dpDeviceUnavailableNow(address);
3730 }
Dianne Hackborn632ca412012-06-14 19:34:10 -07003731 synchronized (mCurAudioRoutes) {
3732 if (mCurAudioRoutes.mBluetoothName != null) {
3733 mCurAudioRoutes.mBluetoothName = null;
3734 sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
3735 SENDMSG_NOOP, 0, 0, null, 0);
3736 }
3737 }
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003738 } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
3739 if (btDevice.isBluetoothDock()) {
3740 // this could be a reconnection after a transient disconnection
3741 cancelA2dpDeviceTimeout();
3742 mDockAddress = address;
3743 } else {
3744 // this could be a connection of another A2DP device before the timeout of
3745 // a dock: cancel the dock timeout, and make the dock unavailable now
3746 if(hasScheduledA2dpDockTimeout()) {
3747 cancelA2dpDeviceTimeout();
3748 makeA2dpDeviceUnavailableNow(mDockAddress);
3749 }
3750 }
3751 makeA2dpDeviceAvailable(address);
Dianne Hackborn632ca412012-06-14 19:34:10 -07003752 synchronized (mCurAudioRoutes) {
3753 String name = btDevice.getAliasName();
3754 if (!TextUtils.equals(mCurAudioRoutes.mBluetoothName, name)) {
3755 mCurAudioRoutes.mBluetoothName = name;
3756 sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
3757 SENDMSG_NOOP, 0, 0, null, 0);
3758 }
3759 }
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003760 }
3761 }
3762 }
3763
Eric Laurent59f48272012-04-05 19:42:21 -07003764 private boolean handleDeviceConnection(boolean connected, int device, String params) {
3765 synchronized (mConnectedDevices) {
3766 boolean isConnected = (mConnectedDevices.containsKey(device) &&
Mike Lockwood98418182012-05-10 17:13:20 -07003767 (params.isEmpty() || mConnectedDevices.get(device).equals(params)));
Eric Laurent59f48272012-04-05 19:42:21 -07003768
3769 if (isConnected && !connected) {
3770 AudioSystem.setDeviceConnectionState(device,
3771 AudioSystem.DEVICE_STATE_UNAVAILABLE,
Mike Lockwood98418182012-05-10 17:13:20 -07003772 mConnectedDevices.get(device));
Eric Laurent59f48272012-04-05 19:42:21 -07003773 mConnectedDevices.remove(device);
3774 return true;
3775 } else if (!isConnected && connected) {
3776 AudioSystem.setDeviceConnectionState(device,
3777 AudioSystem.DEVICE_STATE_AVAILABLE,
3778 params);
3779 mConnectedDevices.put(new Integer(device), params);
3780 return true;
3781 }
3782 }
3783 return false;
3784 }
3785
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003786 // Devices which removal triggers intent ACTION_AUDIO_BECOMING_NOISY. The intent is only
3787 // sent if none of these devices is connected.
3788 int mBecomingNoisyIntentDevices =
3789 AudioSystem.DEVICE_OUT_WIRED_HEADSET | AudioSystem.DEVICE_OUT_WIRED_HEADPHONE |
Eric Laurent794da7a2012-08-30 11:30:16 -07003790 AudioSystem.DEVICE_OUT_ALL_A2DP | AudioSystem.DEVICE_OUT_AUX_DIGITAL |
3791 AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET | AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET |
3792 AudioSystem.DEVICE_OUT_ALL_USB;
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003793
3794 // must be called before removing the device from mConnectedDevices
3795 private int checkSendBecomingNoisyIntent(int device, int state) {
3796 int delay = 0;
3797 if ((state == 0) && ((device & mBecomingNoisyIntentDevices) != 0)) {
3798 int devices = 0;
3799 for (int dev : mConnectedDevices.keySet()) {
3800 if ((dev & mBecomingNoisyIntentDevices) != 0) {
3801 devices |= dev;
3802 }
3803 }
3804 if (devices == device) {
Eric Laurent5bfaeae2012-09-21 18:44:48 -07003805 sendMsg(mAudioHandler,
3806 MSG_BROADCAST_AUDIO_BECOMING_NOISY,
3807 SENDMSG_REPLACE,
3808 0,
3809 0,
3810 null,
3811 0);
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003812 delay = 1000;
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003813 }
3814 }
3815
3816 if (mAudioHandler.hasMessages(MSG_SET_A2DP_CONNECTION_STATE) ||
3817 mAudioHandler.hasMessages(MSG_SET_WIRED_DEVICE_CONNECTION_STATE)) {
3818 delay = 1000;
3819 }
3820 return delay;
3821 }
3822
3823 private void sendDeviceConnectionIntent(int device, int state, String name)
3824 {
3825 Intent intent = new Intent();
3826
3827 intent.putExtra("state", state);
3828 intent.putExtra("name", name);
3829 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
3830
Dianne Hackborn632ca412012-06-14 19:34:10 -07003831 int connType = 0;
3832
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003833 if (device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) {
Dianne Hackborn632ca412012-06-14 19:34:10 -07003834 connType = AudioRoutesInfo.MAIN_HEADSET;
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003835 intent.setAction(Intent.ACTION_HEADSET_PLUG);
3836 intent.putExtra("microphone", 1);
3837 } else if (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) {
Dianne Hackborn632ca412012-06-14 19:34:10 -07003838 connType = AudioRoutesInfo.MAIN_HEADPHONES;
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003839 intent.setAction(Intent.ACTION_HEADSET_PLUG);
3840 intent.putExtra("microphone", 0);
3841 } else if (device == AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET) {
Dianne Hackborn632ca412012-06-14 19:34:10 -07003842 connType = AudioRoutesInfo.MAIN_DOCK_SPEAKERS;
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003843 intent.setAction(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG);
3844 } else if (device == AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET) {
Dianne Hackborn632ca412012-06-14 19:34:10 -07003845 connType = AudioRoutesInfo.MAIN_DOCK_SPEAKERS;
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003846 intent.setAction(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG);
3847 } else if (device == AudioSystem.DEVICE_OUT_AUX_DIGITAL) {
Dianne Hackborn632ca412012-06-14 19:34:10 -07003848 connType = AudioRoutesInfo.MAIN_HDMI;
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003849 intent.setAction(Intent.ACTION_HDMI_AUDIO_PLUG);
3850 }
3851
Dianne Hackborn632ca412012-06-14 19:34:10 -07003852 synchronized (mCurAudioRoutes) {
3853 if (connType != 0) {
3854 int newConn = mCurAudioRoutes.mMainType;
3855 if (state != 0) {
3856 newConn |= connType;
3857 } else {
3858 newConn &= ~connType;
3859 }
3860 if (newConn != mCurAudioRoutes.mMainType) {
3861 mCurAudioRoutes.mMainType = newConn;
3862 sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
3863 SENDMSG_NOOP, 0, 0, null, 0);
3864 }
3865 }
3866 }
3867
Dianne Hackborn5ac72a22012-08-29 18:32:08 -07003868 final long ident = Binder.clearCallingIdentity();
3869 try {
3870 ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);
3871 } finally {
3872 Binder.restoreCallingIdentity(ident);
3873 }
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003874 }
3875
3876 private void onSetWiredDeviceConnectionState(int device, int state, String name)
3877 {
3878 synchronized (mConnectedDevices) {
3879 if ((state == 0) && ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
3880 (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE))) {
3881 setBluetoothA2dpOnInt(true);
3882 }
Mike Lockwooddb454842012-09-18 11:16:57 -07003883 boolean isUsb = ((device & AudioSystem.DEVICE_OUT_ALL_USB) != 0);
3884 handleDeviceConnection((state == 1), device, (isUsb ? name : ""));
Eric Laurentf1a457d2012-09-20 16:27:23 -07003885 if (state != 0) {
3886 if ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
3887 (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE)) {
3888 setBluetoothA2dpOnInt(false);
3889 }
3890 if ((device & mSafeMediaVolumeDevices) != 0) {
3891 sendMsg(mAudioHandler,
3892 MSG_CHECK_MUSIC_ACTIVE,
3893 SENDMSG_REPLACE,
3894 0,
3895 0,
3896 null,
3897 MUSIC_ACTIVE_POLL_PERIOD_MS);
3898 }
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003899 }
Mike Lockwooddb454842012-09-18 11:16:57 -07003900 if (!isUsb) {
3901 sendDeviceConnectionIntent(device, state, name);
3902 }
Eric Laurentb1fbaac2012-05-29 09:24:28 -07003903 }
3904 }
3905
Jean-Michel Trivia847ba42010-04-23 11:49:29 -07003906 /* cache of the address of the last dock the device was connected to */
3907 private String mDockAddress;
3908
Eric Laurenta553c252009-07-17 12:17:14 -07003909 /**
3910 * Receiver for misc intent broadcasts the Phone app cares about.
3911 */
3912 private class AudioServiceBroadcastReceiver extends BroadcastReceiver {
3913 @Override
3914 public void onReceive(Context context, Intent intent) {
3915 String action = intent.getAction();
Eric Laurent59f48272012-04-05 19:42:21 -07003916 int device;
3917 int state;
Eric Laurenta553c252009-07-17 12:17:14 -07003918
Jean-Michel Trivi758559e2010-03-09 09:26:08 -08003919 if (action.equals(Intent.ACTION_DOCK_EVENT)) {
3920 int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
3921 Intent.EXTRA_DOCK_STATE_UNDOCKED);
3922 int config;
3923 switch (dockState) {
3924 case Intent.EXTRA_DOCK_STATE_DESK:
3925 config = AudioSystem.FORCE_BT_DESK_DOCK;
3926 break;
3927 case Intent.EXTRA_DOCK_STATE_CAR:
3928 config = AudioSystem.FORCE_BT_CAR_DOCK;
3929 break;
Praveen Bharathi21e941b2010-10-06 15:23:14 -05003930 case Intent.EXTRA_DOCK_STATE_LE_DESK:
Eric Laurent08ed1b92012-11-05 14:54:12 -08003931 config = AudioSystem.FORCE_ANALOG_DOCK;
Praveen Bharathi21e941b2010-10-06 15:23:14 -05003932 break;
3933 case Intent.EXTRA_DOCK_STATE_HE_DESK:
3934 config = AudioSystem.FORCE_DIGITAL_DOCK;
3935 break;
Jean-Michel Trivi758559e2010-03-09 09:26:08 -08003936 case Intent.EXTRA_DOCK_STATE_UNDOCKED:
3937 default:
3938 config = AudioSystem.FORCE_NONE;
3939 }
Eric Laurent08ed1b92012-11-05 14:54:12 -08003940 // Low end docks have a menu to enable or disable audio
3941 // (see mDockAudioMediaEnabled)
3942 if (!((dockState == Intent.EXTRA_DOCK_STATE_LE_DESK) ||
3943 ((dockState == Intent.EXTRA_DOCK_STATE_UNDOCKED) &&
3944 (mDockState == Intent.EXTRA_DOCK_STATE_LE_DESK)))) {
3945 AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
3946 }
3947 mDockState = dockState;
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07003948 } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
Eric Laurent59f48272012-04-05 19:42:21 -07003949 state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
Jaikumar Ganesh82aa7f02010-09-27 17:05:10 -07003950 BluetoothProfile.STATE_DISCONNECTED);
Eric Laurent59f48272012-04-05 19:42:21 -07003951 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
Nick Pellya56d1c72009-08-19 14:49:29 -07003952 String address = null;
Eric Laurentdca56b92011-09-02 14:20:56 -07003953
3954 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
3955 if (btDevice == null) {
3956 return;
3957 }
3958
3959 address = btDevice.getAddress();
3960 BluetoothClass btClass = btDevice.getBluetoothClass();
3961 if (btClass != null) {
3962 switch (btClass.getDeviceClass()) {
3963 case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
3964 case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
3965 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
3966 break;
3967 case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
3968 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
3969 break;
Eric Laurentd5603c12009-08-06 08:49:39 -07003970 }
3971 }
3972
Eric Laurentdca56b92011-09-02 14:20:56 -07003973 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
3974 address = "";
3975 }
Eric Laurentd5603c12009-08-06 08:49:39 -07003976
Eric Laurent59f48272012-04-05 19:42:21 -07003977 boolean connected = (state == BluetoothProfile.STATE_CONNECTED);
3978 if (handleDeviceConnection(connected, device, address)) {
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003979 synchronized (mScoClients) {
Eric Laurent59f48272012-04-05 19:42:21 -07003980 if (connected) {
3981 mBluetoothHeadsetDevice = btDevice;
3982 } else {
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003983 mBluetoothHeadsetDevice = null;
3984 resetBluetoothSco();
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08003985 }
Eric Laurent62ef7672010-11-24 10:58:32 -08003986 }
Eric Laurenta553c252009-07-17 12:17:14 -07003987 }
Eric Laurent59f48272012-04-05 19:42:21 -07003988 } else if (action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ||
3989 action.equals(Intent.ACTION_USB_AUDIO_DEVICE_PLUG)) {
3990 state = intent.getIntExtra("state", 0);
3991 int alsaCard = intent.getIntExtra("card", -1);
3992 int alsaDevice = intent.getIntExtra("device", -1);
Mike Lockwood98418182012-05-10 17:13:20 -07003993 String params = (alsaCard == -1 && alsaDevice == -1 ? ""
3994 : "card=" + alsaCard + ";device=" + alsaDevice);
Eric Laurent59f48272012-04-05 19:42:21 -07003995 device = action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ?
3996 AudioSystem.DEVICE_OUT_USB_ACCESSORY : AudioSystem.DEVICE_OUT_USB_DEVICE;
3997 Log.v(TAG, "Broadcast Receiver: Got "
3998 + (action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ?
3999 "ACTION_USB_AUDIO_ACCESSORY_PLUG" : "ACTION_USB_AUDIO_DEVICE_PLUG")
4000 + ", state = " + state + ", card: " + alsaCard + ", device: " + alsaDevice);
Mike Lockwooddb454842012-09-18 11:16:57 -07004001 setWiredDeviceConnectionState(device, state, params);
Eric Laurent3def1ee2010-03-17 23:26:26 -07004002 } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
Eric Laurent62ef7672010-11-24 10:58:32 -08004003 boolean broadcast = false;
Eric Laurent59f48272012-04-05 19:42:21 -07004004 int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
Eric Laurent3def1ee2010-03-17 23:26:26 -07004005 synchronized (mScoClients) {
Eric Laurent62ef7672010-11-24 10:58:32 -08004006 int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
Eric Laurentdc03c612011-04-01 10:59:41 -07004007 // broadcast intent if the connection was initated by AudioService
4008 if (!mScoClients.isEmpty() &&
4009 (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
4010 mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
4011 mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
Eric Laurent62ef7672010-11-24 10:58:32 -08004012 broadcast = true;
4013 }
4014 switch (btState) {
4015 case BluetoothHeadset.STATE_AUDIO_CONNECTED:
Eric Laurent59f48272012-04-05 19:42:21 -07004016 scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
Eric Laurentdc03c612011-04-01 10:59:41 -07004017 if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
4018 mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
4019 mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
Eric Laurent62ef7672010-11-24 10:58:32 -08004020 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
Eric Laurent3def1ee2010-03-17 23:26:26 -07004021 }
Eric Laurent62ef7672010-11-24 10:58:32 -08004022 break;
4023 case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
Eric Laurent59f48272012-04-05 19:42:21 -07004024 scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
Eric Laurent62ef7672010-11-24 10:58:32 -08004025 mScoAudioState = SCO_STATE_INACTIVE;
Eric Laurentd7454be2011-09-14 08:45:58 -07004026 clearAllScoClients(0, false);
Eric Laurent62ef7672010-11-24 10:58:32 -08004027 break;
4028 case BluetoothHeadset.STATE_AUDIO_CONNECTING:
Eric Laurentdc03c612011-04-01 10:59:41 -07004029 if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
4030 mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
4031 mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
Eric Laurent62ef7672010-11-24 10:58:32 -08004032 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
Eric Laurent3def1ee2010-03-17 23:26:26 -07004033 }
Eric Laurent62ef7672010-11-24 10:58:32 -08004034 default:
4035 // do not broadcast CONNECTING or invalid state
4036 broadcast = false;
4037 break;
Eric Laurent3def1ee2010-03-17 23:26:26 -07004038 }
4039 }
Eric Laurent62ef7672010-11-24 10:58:32 -08004040 if (broadcast) {
Eric Laurent59f48272012-04-05 19:42:21 -07004041 broadcastScoConnectionState(scoAudioState);
Eric Laurentdc03c612011-04-01 10:59:41 -07004042 //FIXME: this is to maintain compatibility with deprecated intent
4043 // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
Eric Laurent62ef7672010-11-24 10:58:32 -08004044 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
Eric Laurent59f48272012-04-05 19:42:21 -07004045 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, scoAudioState);
Dianne Hackborn5ac72a22012-08-29 18:32:08 -07004046 sendStickyBroadcastToAll(newIntent);
Eric Laurent62ef7672010-11-24 10:58:32 -08004047 }
4048 } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
Eric Laurenta60e2122010-12-28 16:49:07 -08004049 mBootCompleted = true;
Eric Laurent5d3eb44a2013-03-21 15:35:10 -07004050 sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_QUEUE,
Eric Laurent117b7bb2011-01-16 17:07:27 -08004051 0, 0, null, 0);
Eric Laurentdc03c612011-04-01 10:59:41 -07004052
Amith Yamasani6243edd2011-12-05 19:58:48 -08004053 mKeyguardManager =
4054 (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
Eric Laurentdc03c612011-04-01 10:59:41 -07004055 mScoConnectionState = AudioManager.SCO_AUDIO_STATE_ERROR;
4056 resetBluetoothSco();
4057 getBluetoothHeadset();
4058 //FIXME: this is to maintain compatibility with deprecated intent
4059 // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
Eric Laurent62ef7672010-11-24 10:58:32 -08004060 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
4061 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
4062 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
Dianne Hackborn5ac72a22012-08-29 18:32:08 -07004063 sendStickyBroadcastToAll(newIntent);
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08004064
4065 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
4066 if (adapter != null) {
4067 adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
4068 BluetoothProfile.A2DP);
4069 }
Eric Laurentd640bd32012-09-28 18:01:48 -07004070
4071 sendMsg(mAudioHandler,
4072 MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED,
4073 SENDMSG_REPLACE,
4074 0,
4075 0,
4076 null,
4077 SAFE_VOLUME_CONFIGURE_TIMEOUT_MS);
Dianne Hackborn79f7ec72013-04-04 18:50:23 -07004078 } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
4079 || action.equals(Intent.ACTION_PACKAGE_DATA_CLEARED)) {
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07004080 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
4081 // a package is being removed, not replaced
4082 String packageName = intent.getData().getSchemeSpecificPart();
4083 if (packageName != null) {
Dianne Hackborn79f7ec72013-04-04 18:50:23 -07004084 cleanupMediaButtonReceiverForPackage(packageName, true);
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07004085 }
4086 }
Dianne Hackborn79f7ec72013-04-04 18:50:23 -07004087 } else if (action.equals(Intent.ACTION_PACKAGE_ADDED)
4088 || action.equals(Intent.ACTION_PACKAGE_CHANGED)) {
4089 String packageName = intent.getData().getSchemeSpecificPart();
4090 if (packageName != null) {
4091 cleanupMediaButtonReceiverForPackage(packageName, false);
4092 }
Eric Laurent950e8cb2011-10-13 08:57:54 -07004093 } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
4094 AudioSystem.setParameters("screen_state=on");
4095 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
4096 AudioSystem.setParameters("screen_state=off");
Dianne Hackborn961cae92013-03-20 14:59:43 -07004097 } else if (action.equals(Intent.ACTION_CONFIGURATION_CHANGED)) {
Jean-Michel Trivif26f0172012-04-25 16:23:20 -07004098 handleConfigurationChanged(context);
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07004099 } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
Jean-Michel Trivif4a8eb22012-10-29 12:42:55 -07004100 // attempt to stop music playback for background user
Eric Laurent5bfaeae2012-09-21 18:44:48 -07004101 sendMsg(mAudioHandler,
4102 MSG_BROADCAST_AUDIO_BECOMING_NOISY,
4103 SENDMSG_REPLACE,
4104 0,
4105 0,
4106 null,
4107 0);
Jean-Michel Trivif4a8eb22012-10-29 12:42:55 -07004108 // the current audio focus owner is no longer valid
4109 discardAudioFocusOwner();
4110
Eric Laurent5bfaeae2012-09-21 18:44:48 -07004111 // load volume settings for new user
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07004112 readAudioSettings(true /*userSwitch*/);
4113 // preserve STREAM_MUSIC volume from one user to the next.
4114 sendMsg(mAudioHandler,
4115 MSG_SET_ALL_VOLUMES,
4116 SENDMSG_QUEUE,
4117 0,
4118 0,
4119 mStreamStates[AudioSystem.STREAM_MUSIC], 0);
Eric Laurenta553c252009-07-17 12:17:14 -07004120 }
4121 }
4122 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004123
4124 //==========================================================================================
4125 // AudioFocus
4126 //==========================================================================================
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07004127
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -07004128 /* constant to identify focus stack entry that is used to hold the focus while the phone
Jean-Michel Trivi4dd36742012-01-24 09:52:39 -08004129 * is ringing or during a call. Used by com.android.internal.telephony.CallManager when
4130 * entering and exiting calls.
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -07004131 */
Jean-Michel Trivi4dd36742012-01-24 09:52:39 -08004132 public final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls";
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -07004133
Jean-Michel Trivi392a2bb2010-05-10 20:02:46 -07004134 private final static Object mAudioFocusLock = new Object();
4135
Jean-Michel Trivie73131a2010-06-14 09:53:30 -07004136 private final static Object mRingingLock = new Object();
4137
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -07004138 private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
4139 @Override
4140 public void onCallStateChanged(int state, String incomingNumber) {
4141 if (state == TelephonyManager.CALL_STATE_RINGING) {
4142 //Log.v(TAG, " CALL_STATE_RINGING");
Jean-Michel Trivie73131a2010-06-14 09:53:30 -07004143 synchronized(mRingingLock) {
4144 mIsRinging = true;
4145 }
Jean-Michel Trivi2ade5762010-12-11 13:18:30 -08004146 } else if ((state == TelephonyManager.CALL_STATE_OFFHOOK)
4147 || (state == TelephonyManager.CALL_STATE_IDLE)) {
Jean-Michel Trivie73131a2010-06-14 09:53:30 -07004148 synchronized(mRingingLock) {
4149 mIsRinging = false;
4150 }
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07004151 }
4152 }
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -07004153 };
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07004154
Jean-Michel Trivif4a8eb22012-10-29 12:42:55 -07004155 /**
4156 * Discard the current audio focus owner.
4157 * Notify top of audio focus stack that it lost focus (regardless of possibility to reassign
4158 * focus), remove it from the stack, and clear the remote control display.
4159 */
4160 private void discardAudioFocusOwner() {
4161 synchronized(mAudioFocusLock) {
4162 if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
4163 // notify the current focus owner it lost focus after removing it from stack
4164 FocusStackEntry focusOwner = mFocusStack.pop();
4165 try {
4166 focusOwner.mFocusDispatcher.dispatchAudioFocusChange(
4167 AudioManager.AUDIOFOCUS_LOSS, focusOwner.mClientId);
4168 } catch (RemoteException e) {
4169 Log.e(TAG, "Failure to signal loss of audio focus due to "+ e);
4170 e.printStackTrace();
4171 }
4172 focusOwner.unlinkToDeath();
4173 // clear RCD
4174 synchronized(mRCStack) {
4175 clearRemoteControlDisplay_syncAfRcs();
4176 }
4177 }
4178 }
4179 }
4180
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07004181 private void notifyTopOfAudioFocusStack() {
4182 // notify the top of the stack it gained focus
4183 if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
4184 if (canReassignAudioFocus()) {
4185 try {
4186 mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
4187 AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId);
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07004188 } catch (RemoteException e) {
4189 Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e);
4190 e.printStackTrace();
4191 }
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07004192 }
4193 }
4194 }
4195
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004196 private static class FocusStackEntry {
4197 public int mStreamType = -1;// no stream type
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004198 public IAudioFocusDispatcher mFocusDispatcher = null;
4199 public IBinder mSourceRef = null;
4200 public String mClientId;
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07004201 public int mFocusChangeType;
Jean-Michel Trivi061214b2011-08-14 11:37:24 -07004202 public AudioFocusDeathHandler mHandler;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004203 public String mPackageName;
4204 public int mCallingUid;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004205
4206 public FocusStackEntry() {
4207 }
4208
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004209 public FocusStackEntry(int streamType, int duration,
Jean-Michel Trivi061214b2011-08-14 11:37:24 -07004210 IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr,
4211 String pn, int uid) {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004212 mStreamType = streamType;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004213 mFocusDispatcher = afl;
4214 mSourceRef = source;
4215 mClientId = id;
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07004216 mFocusChangeType = duration;
Jean-Michel Trivi061214b2011-08-14 11:37:24 -07004217 mHandler = hdlr;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004218 mPackageName = pn;
4219 mCallingUid = uid;
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004220 }
Jean-Michel Trivi061214b2011-08-14 11:37:24 -07004221
Jean-Michel Trivie7edc862011-11-15 16:27:05 -08004222 public void unlinkToDeath() {
4223 try {
4224 if (mSourceRef != null && mHandler != null) {
4225 mSourceRef.unlinkToDeath(mHandler, 0);
4226 mHandler = null;
4227 }
4228 } catch (java.util.NoSuchElementException e) {
4229 Log.e(TAG, "Encountered " + e + " in FocusStackEntry.unlinkToDeath()");
Jean-Michel Trivi061214b2011-08-14 11:37:24 -07004230 }
4231 }
Jean-Michel Trivide1f0652011-11-11 16:13:48 -08004232
4233 @Override
4234 protected void finalize() throws Throwable {
Jean-Michel Trivie7edc862011-11-15 16:27:05 -08004235 unlinkToDeath(); // unlink exception handled inside method
4236 super.finalize();
Jean-Michel Trivide1f0652011-11-11 16:13:48 -08004237 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004238 }
4239
Glenn Kasten30c918c2011-11-10 17:56:41 -08004240 private final Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>();
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004241
4242 /**
4243 * Helper function:
4244 * Display in the log the current entries in the audio focus stack
4245 */
4246 private void dumpFocusStack(PrintWriter pw) {
Jean-Michel Trivic30f45b2012-12-26 10:46:29 -08004247 pw.println("\nAudio Focus stack entries (last is top of stack):");
Jean-Michel Trivi392a2bb2010-05-10 20:02:46 -07004248 synchronized(mAudioFocusLock) {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004249 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
4250 while(stackIterator.hasNext()) {
4251 FocusStackEntry fse = stackIterator.next();
Jean-Michel Trivif4a8eb22012-10-29 12:42:55 -07004252 pw.println(" source:" + fse.mSourceRef
4253 + " -- pack: " + fse.mPackageName
4254 + " -- client: " + fse.mClientId
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004255 + " -- duration: " + fse.mFocusChangeType
Jean-Michel Trivi1f9196a2012-07-26 15:11:07 -07004256 + " -- uid: " + fse.mCallingUid
4257 + " -- stream: " + fse.mStreamType);
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004258 }
4259 }
4260 }
4261
4262 /**
4263 * Helper function:
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004264 * Called synchronized on mAudioFocusLock
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004265 * Remove a focus listener from the focus stack.
Dianne Hackborn961cae92013-03-20 14:59:43 -07004266 * @param clientToRemove the focus listener
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004267 * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
4268 * focus, notify the next item in the stack it gained focus.
4269 */
4270 private void removeFocusStackEntry(String clientToRemove, boolean signal) {
Jean-Michel Trivie7edc862011-11-15 16:27:05 -08004271 // is the current top of the focus stack abandoning focus? (because of request, not death)
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004272 if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove))
4273 {
4274 //Log.i(TAG, " removeFocusStackEntry() removing top of stack");
Jean-Michel Trivie7edc862011-11-15 16:27:05 -08004275 FocusStackEntry fse = mFocusStack.pop();
4276 fse.unlinkToDeath();
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004277 if (signal) {
4278 // notify the new top of the stack it gained focus
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07004279 notifyTopOfAudioFocusStack();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004280 // there's a new top of the stack, let the remote control know
4281 synchronized(mRCStack) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07004282 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004283 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004284 }
4285 } else {
4286 // focus is abandoned by a client that's not at the top of the stack,
4287 // no need to update focus.
Jean-Michel Trivic30f45b2012-12-26 10:46:29 -08004288 // (using an iterator on the stack so we can safely remove an entry after having
4289 // evaluated it, traversal order doesn't matter here)
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004290 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
4291 while(stackIterator.hasNext()) {
4292 FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
4293 if(fse.mClientId.equals(clientToRemove)) {
4294 Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for "
4295 + fse.mClientId);
Jean-Michel Trivi72843372011-04-18 10:18:37 -07004296 stackIterator.remove();
Jean-Michel Trivie7edc862011-11-15 16:27:05 -08004297 fse.unlinkToDeath();
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004298 }
4299 }
4300 }
4301 }
4302
4303 /**
4304 * Helper function:
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004305 * Called synchronized on mAudioFocusLock
Jean-Michel Trivie7edc862011-11-15 16:27:05 -08004306 * Remove focus listeners from the focus stack for a particular client when it has died.
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004307 */
4308 private void removeFocusStackEntryForClient(IBinder cb) {
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07004309 // is the owner of the audio focus part of the client to remove?
4310 boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
4311 mFocusStack.peek().mSourceRef.equals(cb);
Jean-Michel Trivic30f45b2012-12-26 10:46:29 -08004312 // (using an iterator on the stack so we can safely remove an entry after having
4313 // evaluated it, traversal order doesn't matter here)
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004314 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
4315 while(stackIterator.hasNext()) {
4316 FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
4317 if(fse.mSourceRef.equals(cb)) {
4318 Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for "
4319 + fse.mClientId);
Jean-Michel Trivi72843372011-04-18 10:18:37 -07004320 stackIterator.remove();
Jean-Michel Trivie7edc862011-11-15 16:27:05 -08004321 // the client just died, no need to unlink to its death
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004322 }
4323 }
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07004324 if (isTopOfStackForClientToRemove) {
4325 // we removed an entry at the top of the stack:
4326 // notify the new top of the stack it gained focus.
4327 notifyTopOfAudioFocusStack();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004328 // there's a new top of the stack, let the remote control know
4329 synchronized(mRCStack) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07004330 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004331 }
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07004332 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004333 }
4334
4335 /**
4336 * Helper function:
4337 * Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
4338 */
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004339 private boolean canReassignAudioFocus() {
Jean-Michel Trivib4bccb62010-04-20 14:56:34 -07004340 // focus requests are rejected during a phone call or when the phone is ringing
4341 // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus
4342 if (!mFocusStack.isEmpty() && IN_VOICE_COMM_FOCUS_ID.equals(mFocusStack.peek().mClientId)) {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004343 return false;
4344 }
4345 return true;
4346 }
4347
4348 /**
4349 * Inner class to monitor audio focus client deaths, and remove them from the audio focus
4350 * stack if necessary.
4351 */
4352 private class AudioFocusDeathHandler implements IBinder.DeathRecipient {
4353 private IBinder mCb; // To be notified of client's death
4354
4355 AudioFocusDeathHandler(IBinder cb) {
4356 mCb = cb;
4357 }
4358
4359 public void binderDied() {
Jean-Michel Trivi392a2bb2010-05-10 20:02:46 -07004360 synchronized(mAudioFocusLock) {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004361 Log.w(TAG, " AudioFocus audio focus client died");
4362 removeFocusStackEntryForClient(mCb);
4363 }
4364 }
4365
4366 public IBinder getBinder() {
4367 return mCb;
4368 }
4369 }
4370
4371
Dianne Hackborn961cae92013-03-20 14:59:43 -07004372 /** @see AudioManager#requestAudioFocus(AudioManager.OnAudioFocusChangeListener, int, int) */
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07004373 public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb,
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004374 IAudioFocusDispatcher fd, String clientId, String callingPackageName) {
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004375 Log.i(TAG, " AudioFocus requestAudioFocus() from " + clientId);
4376 // the main stream type for the audio focus request is currently not used. It may
4377 // potentially be used to handle multiple stream type-dependent audio focuses.
4378
Jean-Michel Trivi382f4e6e2011-03-08 14:38:59 -08004379 // we need a valid binder callback for clients
4380 if (!cb.pingBinder()) {
4381 Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004382 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
4383 }
4384
Jean-Michel Trivi392a2bb2010-05-10 20:02:46 -07004385 synchronized(mAudioFocusLock) {
4386 if (!canReassignAudioFocus()) {
4387 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
4388 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004389
Jean-Michel Trivibaf674e2011-08-14 15:16:21 -07004390 // handle the potential premature death of the new holder of the focus
4391 // (premature death == death before abandoning focus)
4392 // Register for client death notification
4393 AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
4394 try {
4395 cb.linkToDeath(afdh, 0);
4396 } catch (RemoteException e) {
4397 // client has already died!
4398 Log.w(TAG, "AudioFocus requestAudioFocus() could not link to "+cb+" binder death");
4399 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
4400 }
4401
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004402 if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) {
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07004403 // if focus is already owned by this client and the reason for acquiring the focus
4404 // hasn't changed, don't do anything
4405 if (mFocusStack.peek().mFocusChangeType == focusChangeHint) {
Jean-Michel Trivide1f0652011-11-11 16:13:48 -08004406 // unlink death handler so it can be gc'ed.
4407 // linkToDeath() creates a JNI global reference preventing collection.
4408 cb.unlinkToDeath(afdh, 0);
Jean-Michel Trivie5e1e872010-03-19 15:31:20 -07004409 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
4410 }
4411 // the reason for the audio focus request has changed: remove the current top of
4412 // stack and respond as if we had a new focus owner
Jean-Michel Trivie7edc862011-11-15 16:27:05 -08004413 FocusStackEntry fse = mFocusStack.pop();
4414 fse.unlinkToDeath();
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004415 }
4416
4417 // notify current top of stack it is losing focus
4418 if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
4419 try {
4420 mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
Jean-Michel Trivi078fd472010-03-18 16:51:04 -07004421 -1 * focusChangeHint, // loss and gain codes are inverse of each other
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004422 mFocusStack.peek().mClientId);
4423 } catch (RemoteException e) {
4424 Log.e(TAG, " Failure to signal loss of focus due to "+ e);
4425 e.printStackTrace();
4426 }
4427 }
4428
Jean-Michel Trivi55d1bb32010-04-01 17:40:58 -07004429 // focus requester might already be somewhere below in the stack, remove it
Jean-Michel Trivibaf674e2011-08-14 15:16:21 -07004430 removeFocusStackEntry(clientId, false /* signal */);
Jean-Michel Trivi061214b2011-08-14 11:37:24 -07004431
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004432 // push focus requester at the top of the audio focus stack
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004433 mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, fd, cb,
Jean-Michel Trivi061214b2011-08-14 11:37:24 -07004434 clientId, afdh, callingPackageName, Binder.getCallingUid()));
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004435
4436 // there's a new top of the stack, let the remote control know
4437 synchronized(mRCStack) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07004438 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004439 }
Jean-Michel Trivi392a2bb2010-05-10 20:02:46 -07004440 }//synchronized(mAudioFocusLock)
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004441
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004442 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
4443 }
4444
Dianne Hackborn961cae92013-03-20 14:59:43 -07004445 /** @see AudioManager#abandonAudioFocus(AudioManager.OnAudioFocusChangeListener) */
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004446 public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) {
4447 Log.i(TAG, " AudioFocus abandonAudioFocus() from " + clientId);
Jean-Michel Trivi392a2bb2010-05-10 20:02:46 -07004448 try {
4449 // this will take care of notifying the new focus owner if needed
4450 synchronized(mAudioFocusLock) {
4451 removeFocusStackEntry(clientId, true);
4452 }
4453 } catch (java.util.ConcurrentModificationException cme) {
4454 // Catching this exception here is temporary. It is here just to prevent
4455 // a crash seen when the "Silent" notification is played. This is believed to be fixed
4456 // but this try catch block is left just to be safe.
4457 Log.e(TAG, "FATAL EXCEPTION AudioFocus abandonAudioFocus() caused " + cme);
4458 cme.printStackTrace();
Jean-Michel Trivi2930bb22010-04-09 19:27:58 -07004459 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004460
4461 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
4462 }
4463
4464
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004465 public void unregisterAudioFocusClient(String clientId) {
Jean-Michel Trivi392a2bb2010-05-10 20:02:46 -07004466 synchronized(mAudioFocusLock) {
Jean-Michel Trivi2930bb22010-04-09 19:27:58 -07004467 removeFocusStackEntry(clientId, false);
4468 }
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08004469 }
4470
4471
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004472 //==========================================================================================
4473 // RemoteControl
4474 //==========================================================================================
Jean-Michel Trivic6802222012-04-30 11:15:03 -07004475 public void dispatchMediaKeyEvent(KeyEvent keyEvent) {
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004476 filterMediaKeyEvent(keyEvent, false /*needWakeLock*/);
Jean-Michel Trivic6802222012-04-30 11:15:03 -07004477 }
4478
4479 public void dispatchMediaKeyEventUnderWakelock(KeyEvent keyEvent) {
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004480 filterMediaKeyEvent(keyEvent, true /*needWakeLock*/);
Jean-Michel Trivic6802222012-04-30 11:15:03 -07004481 }
4482
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004483 private void filterMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
Jean-Michel Trivic6802222012-04-30 11:15:03 -07004484 // sanity check on the incoming key event
4485 if (!isValidMediaKeyEvent(keyEvent)) {
4486 Log.e(TAG, "not dispatching invalid media key event " + keyEvent);
4487 return;
4488 }
Jean-Michel Trivi722b8082012-05-15 15:18:33 -07004489 // event filtering for telephony
Jean-Michel Trivic6802222012-04-30 11:15:03 -07004490 synchronized(mRingingLock) {
Jean-Michel Trivi722b8082012-05-15 15:18:33 -07004491 synchronized(mRCStack) {
4492 if ((mMediaReceiverForCalls != null) &&
4493 (mIsRinging || (getMode() == AudioSystem.MODE_IN_CALL))) {
4494 dispatchMediaKeyEventForCalls(keyEvent, needWakeLock);
4495 return;
4496 }
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004497 }
Jean-Michel Trivic6802222012-04-30 11:15:03 -07004498 }
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004499 // event filtering based on voice-based interactions
4500 if (isValidVoiceInputKeyCode(keyEvent.getKeyCode())) {
4501 filterVoiceInputKeyEvent(keyEvent, needWakeLock);
4502 } else {
4503 dispatchMediaKeyEvent(keyEvent, needWakeLock);
4504 }
4505 }
4506
4507 /**
Jean-Michel Trivi722b8082012-05-15 15:18:33 -07004508 * Handles the dispatching of the media button events to the telephony package.
4509 * Precondition: mMediaReceiverForCalls != null
4510 * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons
4511 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
4512 * is dispatched.
4513 */
4514 private void dispatchMediaKeyEventForCalls(KeyEvent keyEvent, boolean needWakeLock) {
4515 Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
4516 keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
4517 keyIntent.setPackage(mMediaReceiverForCalls.getPackageName());
4518 if (needWakeLock) {
4519 mMediaEventWakeLock.acquire();
4520 keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
4521 }
Dianne Hackborn5ac72a22012-08-29 18:32:08 -07004522 final long ident = Binder.clearCallingIdentity();
4523 try {
4524 mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
4525 null, mKeyEventDone, mAudioHandler, Activity.RESULT_OK, null, null);
4526 } finally {
4527 Binder.restoreCallingIdentity(ident);
4528 }
Jean-Michel Trivi722b8082012-05-15 15:18:33 -07004529 }
4530
4531 /**
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004532 * Handles the dispatching of the media button events to one of the registered listeners,
4533 * or if there was none, broadcast an ACTION_MEDIA_BUTTON intent to the rest of the system.
4534 * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons
4535 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
4536 * is dispatched.
4537 */
4538 private void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
Jean-Michel Trivic6802222012-04-30 11:15:03 -07004539 if (needWakeLock) {
4540 mMediaEventWakeLock.acquire();
4541 }
4542 Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
4543 keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
4544 synchronized(mRCStack) {
4545 if (!mRCStack.empty()) {
4546 // send the intent that was registered by the client
4547 try {
4548 mRCStack.peek().mMediaIntent.send(mContext,
4549 needWakeLock ? WAKELOCK_RELEASE_ON_FINISHED : 0 /*code*/,
4550 keyIntent, AudioService.this, mAudioHandler);
4551 } catch (CanceledException e) {
4552 Log.e(TAG, "Error sending pending intent " + mRCStack.peek());
4553 e.printStackTrace();
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004554 }
Jean-Michel Trivic6802222012-04-30 11:15:03 -07004555 } else {
4556 // legacy behavior when nobody registered their media button event receiver
4557 // through AudioManager
4558 if (needWakeLock) {
4559 keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004560 }
Dianne Hackborn5ac72a22012-08-29 18:32:08 -07004561 final long ident = Binder.clearCallingIdentity();
4562 try {
4563 mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
4564 null, mKeyEventDone,
4565 mAudioHandler, Activity.RESULT_OK, null, null);
4566 } finally {
4567 Binder.restoreCallingIdentity(ident);
4568 }
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004569 }
4570 }
4571 }
4572
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004573 /**
Jeff Brownab624c22012-05-17 11:08:31 -07004574 * The different actions performed in response to a voice button key event.
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004575 */
4576 private final static int VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS = 1;
4577 private final static int VOICEBUTTON_ACTION_START_VOICE_INPUT = 2;
4578 private final static int VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS = 3;
4579
4580 private final Object mVoiceEventLock = new Object();
Jeff Brownab624c22012-05-17 11:08:31 -07004581 private boolean mVoiceButtonDown;
4582 private boolean mVoiceButtonHandled;
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004583
4584 /**
4585 * Filter key events that may be used for voice-based interactions
4586 * @param keyEvent a non-null KeyEvent whose key code is that of one of the supported
4587 * media buttons that can be used to trigger voice-based interactions.
4588 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
4589 * is dispatched.
4590 */
4591 private void filterVoiceInputKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
Jeff Brownab624c22012-05-17 11:08:31 -07004592 if (DEBUG_RC) {
4593 Log.v(TAG, "voice input key event: " + keyEvent + ", needWakeLock=" + needWakeLock);
4594 }
4595
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004596 int voiceButtonAction = VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS;
4597 int keyAction = keyEvent.getAction();
4598 synchronized (mVoiceEventLock) {
Jeff Brownab624c22012-05-17 11:08:31 -07004599 if (keyAction == KeyEvent.ACTION_DOWN) {
4600 if (keyEvent.getRepeatCount() == 0) {
4601 // initial down
4602 mVoiceButtonDown = true;
4603 mVoiceButtonHandled = false;
4604 } else if (mVoiceButtonDown && !mVoiceButtonHandled
4605 && (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
4606 // long-press, start voice-based interactions
4607 mVoiceButtonHandled = true;
4608 voiceButtonAction = VOICEBUTTON_ACTION_START_VOICE_INPUT;
4609 }
4610 } else if (keyAction == KeyEvent.ACTION_UP) {
4611 if (mVoiceButtonDown) {
4612 // voice button up
4613 mVoiceButtonDown = false;
4614 if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
4615 voiceButtonAction = VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS;
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004616 }
Jeff Brownab624c22012-05-17 11:08:31 -07004617 }
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004618 }
4619 }//synchronized (mVoiceEventLock)
4620
4621 // take action after media button event filtering for voice-based interactions
4622 switch (voiceButtonAction) {
4623 case VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS:
4624 if (DEBUG_RC) Log.v(TAG, " ignore key event");
4625 break;
4626 case VOICEBUTTON_ACTION_START_VOICE_INPUT:
4627 if (DEBUG_RC) Log.v(TAG, " start voice-based interactions");
4628 // then start the voice-based interactions
4629 startVoiceBasedInteractions(needWakeLock);
4630 break;
4631 case VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS:
Jean-Michel Trivi1f9196a2012-07-26 15:11:07 -07004632 if (DEBUG_RC) Log.v(TAG, " send simulated key event, wakelock=" + needWakeLock);
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004633 sendSimulatedMediaButtonEvent(keyEvent, needWakeLock);
4634 break;
4635 }
4636 }
4637
4638 private void sendSimulatedMediaButtonEvent(KeyEvent originalKeyEvent, boolean needWakeLock) {
4639 // send DOWN event
4640 KeyEvent keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_DOWN);
4641 dispatchMediaKeyEvent(keyEvent, needWakeLock);
4642 // send UP event
4643 keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_UP);
4644 dispatchMediaKeyEvent(keyEvent, needWakeLock);
4645
4646 }
4647
4648
Jean-Michel Trivic6802222012-04-30 11:15:03 -07004649 private static boolean isValidMediaKeyEvent(KeyEvent keyEvent) {
4650 if (keyEvent == null) {
4651 return false;
4652 }
4653 final int keyCode = keyEvent.getKeyCode();
4654 switch (keyCode) {
4655 case KeyEvent.KEYCODE_MUTE:
4656 case KeyEvent.KEYCODE_HEADSETHOOK:
4657 case KeyEvent.KEYCODE_MEDIA_PLAY:
4658 case KeyEvent.KEYCODE_MEDIA_PAUSE:
4659 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
4660 case KeyEvent.KEYCODE_MEDIA_STOP:
4661 case KeyEvent.KEYCODE_MEDIA_NEXT:
4662 case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
4663 case KeyEvent.KEYCODE_MEDIA_REWIND:
4664 case KeyEvent.KEYCODE_MEDIA_RECORD:
4665 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
4666 case KeyEvent.KEYCODE_MEDIA_CLOSE:
4667 case KeyEvent.KEYCODE_MEDIA_EJECT:
4668 break;
4669 default:
4670 return false;
4671 }
4672 return true;
4673 }
4674
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004675 /**
4676 * Checks whether the given key code is one that can trigger the launch of voice-based
4677 * interactions.
4678 * @param keyCode the key code associated with the key event
4679 * @return true if the key is one of the supported voice-based interaction triggers
4680 */
4681 private static boolean isValidVoiceInputKeyCode(int keyCode) {
4682 if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK) {
4683 return true;
4684 } else {
4685 return false;
4686 }
4687 }
4688
4689 /**
4690 * Tell the system to start voice-based interactions / voice commands
4691 */
4692 private void startVoiceBasedInteractions(boolean needWakeLock) {
Jean-Michel Trivi3c2711f2012-05-20 12:07:03 -07004693 Intent voiceIntent = null;
4694 // select which type of search to launch:
4695 // - screen on and device unlocked: action is ACTION_WEB_SEARCH
4696 // - device locked or screen off: action is ACTION_VOICE_SEARCH_HANDS_FREE
4697 // with EXTRA_SECURE set to true if the device is securely locked
4698 PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
4699 boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
4700 if (!isLocked && pm.isScreenOn()) {
4701 voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
4702 } else {
4703 voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
4704 voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
4705 isLocked && mKeyguardManager.isKeyguardSecure());
4706 }
4707 // start the search activity
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004708 if (needWakeLock) {
4709 mMediaEventWakeLock.acquire();
4710 }
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004711 try {
Jean-Michel Trivi3c2711f2012-05-20 12:07:03 -07004712 if (voiceIntent != null) {
4713 voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
4714 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
4715 mContext.startActivity(voiceIntent);
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004716 }
4717 } catch (ActivityNotFoundException e) {
Jean-Michel Trivi3c2711f2012-05-20 12:07:03 -07004718 Log.w(TAG, "No activity for search: " + e);
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004719 } finally {
4720 if (needWakeLock) {
4721 mMediaEventWakeLock.release();
4722 }
4723 }
4724 }
4725
Jean-Michel Trivic6802222012-04-30 11:15:03 -07004726 private PowerManager.WakeLock mMediaEventWakeLock;
4727
4728 private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; //magic number
4729
4730 // only set when wakelock was acquired, no need to check value when received
4731 private static final String EXTRA_WAKELOCK_ACQUIRED =
4732 "android.media.AudioService.WAKELOCK_ACQUIRED";
4733
4734 public void onSendFinished(PendingIntent pendingIntent, Intent intent,
4735 int resultCode, String resultData, Bundle resultExtras) {
4736 if (resultCode == WAKELOCK_RELEASE_ON_FINISHED) {
4737 mMediaEventWakeLock.release();
4738 }
4739 }
4740
4741 BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
4742 public void onReceive(Context context, Intent intent) {
Jean-Michel Trivi4294b832012-04-30 17:39:29 -07004743 if (intent == null) {
4744 return;
4745 }
4746 Bundle extras = intent.getExtras();
4747 if (extras == null) {
4748 return;
4749 }
4750 if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)) {
Jean-Michel Trivic6802222012-04-30 11:15:03 -07004751 mMediaEventWakeLock.release();
4752 }
4753 }
4754 };
4755
Jean-Michel Trivi3261b532013-04-01 14:59:39 -07004756 /**
4757 * Synchronization on mCurrentRcLock always inside a block synchronized on mRCStack
4758 */
Neel Parekhced110e2011-08-05 14:32:49 -07004759 private final Object mCurrentRcLock = new Object();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004760 /**
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004761 * The one remote control client which will receive a request for display information.
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07004762 * This object may be null.
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004763 * Access protected by mCurrentRcLock.
4764 */
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004765 private IRemoteControlClient mCurrentRcClient = null;
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004766
Jean-Michel Trivi7b6198c2011-08-07 11:27:50 -07004767 private final static int RC_INFO_NONE = 0;
4768 private final static int RC_INFO_ALL =
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004769 RemoteControlClient.FLAG_INFORMATION_REQUEST_ALBUM_ART |
4770 RemoteControlClient.FLAG_INFORMATION_REQUEST_KEY_MEDIA |
4771 RemoteControlClient.FLAG_INFORMATION_REQUEST_METADATA |
4772 RemoteControlClient.FLAG_INFORMATION_REQUEST_PLAYSTATE;
Jean-Michel Trivi7b6198c2011-08-07 11:27:50 -07004773
4774 /**
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07004775 * A monotonically increasing generation counter for mCurrentRcClient.
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004776 * Only accessed with a lock on mCurrentRcLock.
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07004777 * No value wrap-around issues as we only act on equal values.
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004778 */
Neel Parekhced110e2011-08-05 14:32:49 -07004779 private int mCurrentRcClientGen = 0;
Jean-Michel Trivid327f212010-03-16 21:44:33 -07004780
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004781 /**
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004782 * Inner class to monitor remote control client deaths, and remove the client for the
4783 * remote control stack if necessary.
4784 */
4785 private class RcClientDeathHandler implements IBinder.DeathRecipient {
Dianne Hackborn961cae92013-03-20 14:59:43 -07004786 final private IBinder mCb; // To be notified of client's death
4787 final private PendingIntent mMediaIntent;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004788
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004789 RcClientDeathHandler(IBinder cb, PendingIntent pi) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004790 mCb = cb;
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004791 mMediaIntent = pi;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004792 }
4793
4794 public void binderDied() {
4795 Log.w(TAG, " RemoteControlClient died");
4796 // remote control client died, make sure the displays don't use it anymore
4797 // by setting its remote control client to null
Jean-Michel Trivi26b3d1f2011-09-28 11:05:15 -07004798 registerRemoteControlClient(mMediaIntent, null/*rcClient*/, null/*ignored*/);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07004799 // the dead client was maybe handling remote playback, reevaluate
4800 postReevaluateRemote();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004801 }
4802
4803 public IBinder getBinder() {
4804 return mCb;
4805 }
4806 }
4807
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07004808 /**
4809 * A global counter for RemoteControlClient identifiers
4810 */
4811 private static int sLastRccId = 0;
4812
4813 private class RemotePlaybackState {
4814 int mRccId;
4815 int mVolume;
4816 int mVolumeMax;
4817 int mVolumeHandling;
4818
4819 private RemotePlaybackState(int id, int vol, int volMax) {
4820 mRccId = id;
4821 mVolume = vol;
4822 mVolumeMax = volMax;
4823 mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
4824 }
4825 }
4826
4827 /**
4828 * Internal cache for the playback information of the RemoteControlClient whose volume gets to
4829 * be controlled by the volume keys ("main"), so we don't have to iterate over the RC stack
4830 * every time we need this info.
4831 */
4832 private RemotePlaybackState mMainRemote;
4833 /**
4834 * Indicates whether the "main" RemoteControlClient is considered active.
4835 * Use synchronized on mMainRemote.
4836 */
4837 private boolean mMainRemoteIsActive;
4838 /**
4839 * Indicates whether there is remote playback going on. True even if there is no "active"
4840 * remote playback (mMainRemoteIsActive is false), but a RemoteControlClient has declared it
4841 * handles remote playback.
4842 * Use synchronized on mMainRemote.
4843 */
4844 private boolean mHasRemotePlayback;
4845
Jean-Michel Trivibc43b4c2013-03-22 09:30:50 -07004846 private static class RccPlaybackState {
4847 public int mState;
4848 public long mPositionMs;
4849 public float mSpeed;
4850
4851 public RccPlaybackState(int state, long positionMs, float speed) {
4852 mState = state;
4853 mPositionMs = positionMs;
4854 mSpeed = speed;
4855 }
4856
4857 public void reset() {
4858 mState = RemoteControlClient.PLAYSTATE_STOPPED;
4859 mPositionMs = RemoteControlClient.PLAYBACK_POSITION_INVALID;
4860 mSpeed = RemoteControlClient.PLAYBACK_SPEED_1X;
4861 }
4862
4863 @Override
4864 public String toString() {
4865 return stateToString() + ", "
4866 + ((mPositionMs == RemoteControlClient.PLAYBACK_POSITION_INVALID) ?
4867 "PLAYBACK_POSITION_INVALID ," : String.valueOf(mPositionMs)) + "ms ,"
4868 + mSpeed + "X";
4869 }
4870
4871 private String stateToString() {
4872 switch (mState) {
4873 case RemoteControlClient.PLAYSTATE_NONE:
4874 return "PLAYSTATE_NONE";
4875 case RemoteControlClient.PLAYSTATE_STOPPED:
4876 return "PLAYSTATE_STOPPED";
4877 case RemoteControlClient.PLAYSTATE_PAUSED:
4878 return "PLAYSTATE_PAUSED";
4879 case RemoteControlClient.PLAYSTATE_PLAYING:
4880 return "PLAYSTATE_PLAYING";
4881 case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
4882 return "PLAYSTATE_FAST_FORWARDING";
4883 case RemoteControlClient.PLAYSTATE_REWINDING:
4884 return "PLAYSTATE_REWINDING";
4885 case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
4886 return "PLAYSTATE_SKIPPING_FORWARDS";
4887 case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
4888 return "PLAYSTATE_SKIPPING_BACKWARDS";
4889 case RemoteControlClient.PLAYSTATE_BUFFERING:
4890 return "PLAYSTATE_BUFFERING";
4891 case RemoteControlClient.PLAYSTATE_ERROR:
4892 return "PLAYSTATE_ERROR";
4893 default:
4894 return "[invalid playstate]";
4895 }
4896 }
4897 }
4898
Dianne Hackborn79f7ec72013-04-04 18:50:23 -07004899 private static class RemoteControlStackEntry implements DeathRecipient {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07004900 public int mRccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
Dianne Hackborn79f7ec72013-04-04 18:50:23 -07004901 final public AudioService mService;
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004902 /**
4903 * The target for the ACTION_MEDIA_BUTTON events.
4904 * Always non null.
4905 */
Dianne Hackborn961cae92013-03-20 14:59:43 -07004906 final public PendingIntent mMediaIntent;
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004907 /**
4908 * The registered media button event receiver.
4909 * Always non null.
4910 */
Dianne Hackborn961cae92013-03-20 14:59:43 -07004911 final public ComponentName mReceiverComponent;
Dianne Hackborn79f7ec72013-04-04 18:50:23 -07004912 public IBinder mToken;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004913 public String mCallingPackageName;
4914 public int mCallingUid;
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004915 /**
4916 * Provides access to the information to display on the remote control.
4917 * May be null (when a media button event receiver is registered,
4918 * but no remote control client has been registered) */
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07004919 public IRemoteControlClient mRcClient;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004920 public RcClientDeathHandler mRcClientDeathHandler;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07004921 /**
4922 * Information only used for non-local playback
4923 */
4924 public int mPlaybackType;
4925 public int mPlaybackVolume;
4926 public int mPlaybackVolumeMax;
4927 public int mPlaybackVolumeHandling;
4928 public int mPlaybackStream;
Jean-Michel Trivibc43b4c2013-03-22 09:30:50 -07004929 public RccPlaybackState mPlaybackState;
Jean-Michel Trivi13570122012-06-19 14:03:09 -07004930 public IRemoteVolumeObserver mRemoteVolumeObs;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07004931
4932 public void resetPlaybackInfo() {
4933 mPlaybackType = RemoteControlClient.PLAYBACK_TYPE_LOCAL;
4934 mPlaybackVolume = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
4935 mPlaybackVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
4936 mPlaybackVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
4937 mPlaybackStream = AudioManager.STREAM_MUSIC;
Jean-Michel Trivibc43b4c2013-03-22 09:30:50 -07004938 mPlaybackState.reset();
Jean-Michel Trivi13570122012-06-19 14:03:09 -07004939 mRemoteVolumeObs = null;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07004940 }
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004941
Jean-Michel Trivib839b832013-03-30 14:17:45 -07004942 /** precondition: mediaIntent != null */
Dianne Hackborn79f7ec72013-04-04 18:50:23 -07004943 public RemoteControlStackEntry(AudioService service, PendingIntent mediaIntent,
4944 ComponentName eventReceiver, IBinder token) {
4945 mService = service;
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07004946 mMediaIntent = mediaIntent;
4947 mReceiverComponent = eventReceiver;
Dianne Hackborn79f7ec72013-04-04 18:50:23 -07004948 mToken = token;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004949 mCallingUid = -1;
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07004950 mRcClient = null;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07004951 mRccId = ++sLastRccId;
Jean-Michel Trivi6f268362013-03-30 12:35:51 -07004952 mPlaybackState = new RccPlaybackState(
4953 RemoteControlClient.PLAYSTATE_STOPPED,
4954 RemoteControlClient.PLAYBACK_POSITION_INVALID,
4955 RemoteControlClient.PLAYBACK_SPEED_1X);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07004956
4957 resetPlaybackInfo();
Dianne Hackborn79f7ec72013-04-04 18:50:23 -07004958 if (mToken != null) {
4959 try {
4960 mToken.linkToDeath(this, 0);
4961 } catch (RemoteException e) {
4962 mService.mAudioHandler.post(new Runnable() {
4963 @Override public void run() {
4964 mService.unregisterMediaButtonIntent(mMediaIntent);
4965 }
4966 });
4967 }
4968 }
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004969 }
4970
4971 public void unlinkToRcClientDeath() {
4972 if ((mRcClientDeathHandler != null) && (mRcClientDeathHandler.mCb != null)) {
4973 try {
4974 mRcClientDeathHandler.mCb.unlinkToDeath(mRcClientDeathHandler, 0);
Jean-Michel Trivia9509732011-11-14 15:23:04 -08004975 mRcClientDeathHandler = null;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07004976 } catch (java.util.NoSuchElementException e) {
4977 // not much we can do here
4978 Log.e(TAG, "Encountered " + e + " in unlinkToRcClientDeath()");
4979 e.printStackTrace();
4980 }
4981 }
4982 }
Jean-Michel Trivia9509732011-11-14 15:23:04 -08004983
Dianne Hackborn79f7ec72013-04-04 18:50:23 -07004984 public void destroy() {
4985 unlinkToRcClientDeath();
4986 if (mToken != null) {
4987 mToken.unlinkToDeath(this, 0);
4988 mToken = null;
4989 }
4990 }
4991
4992 @Override
4993 public void binderDied() {
4994 mService.unregisterMediaButtonIntent(mMediaIntent);
4995 }
4996
Jean-Michel Trivia9509732011-11-14 15:23:04 -08004997 @Override
4998 protected void finalize() throws Throwable {
Dianne Hackborn79f7ec72013-04-04 18:50:23 -07004999 destroy(); // unlink exception handled inside method
Jean-Michel Trivia9509732011-11-14 15:23:04 -08005000 super.finalize();
5001 }
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005002 }
5003
5004 /**
5005 * The stack of remote control event receivers.
5006 * Code sections and methods that modify the remote control event receiver stack are
5007 * synchronized on mRCStack, but also BEFORE on mFocusLock as any change in either
5008 * stack, audio focus or RC, can lead to a change in the remote control display
5009 */
Glenn Kasten30c918c2011-11-10 17:56:41 -08005010 private final Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>();
Jean-Michel Trivid327f212010-03-16 21:44:33 -07005011
5012 /**
Jean-Michel Trivi722b8082012-05-15 15:18:33 -07005013 * The component the telephony package can register so telephony calls have priority to
5014 * handle media button events
5015 */
5016 private ComponentName mMediaReceiverForCalls = null;
5017
5018 /**
Jean-Michel Trivid327f212010-03-16 21:44:33 -07005019 * Helper function:
5020 * Display in the log the current entries in the remote control focus stack
5021 */
5022 private void dumpRCStack(PrintWriter pw) {
Jean-Michel Trivic30f45b2012-12-26 10:46:29 -08005023 pw.println("\nRemote Control stack entries (last is top of stack):");
Jean-Michel Trivid327f212010-03-16 21:44:33 -07005024 synchronized(mRCStack) {
5025 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
5026 while(stackIterator.hasNext()) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005027 RemoteControlStackEntry rcse = stackIterator.next();
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005028 pw.println(" pi: " + rcse.mMediaIntent +
Jean-Michel Trivif4a8eb22012-10-29 12:42:55 -07005029 " -- pack: " + rcse.mCallingPackageName +
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005030 " -- ercvr: " + rcse.mReceiverComponent +
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07005031 " -- client: " + rcse.mRcClient +
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005032 " -- uid: " + rcse.mCallingUid +
5033 " -- type: " + rcse.mPlaybackType +
5034 " state: " + rcse.mPlaybackState);
Jean-Michel Trivid327f212010-03-16 21:44:33 -07005035 }
5036 }
5037 }
5038
5039 /**
5040 * Helper function:
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005041 * Display in the log the current entries in the remote control stack, focusing
5042 * on RemoteControlClient data
5043 */
5044 private void dumpRCCStack(PrintWriter pw) {
Jean-Michel Trivic30f45b2012-12-26 10:46:29 -08005045 pw.println("\nRemote Control Client stack entries (last is top of stack):");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005046 synchronized(mRCStack) {
5047 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
5048 while(stackIterator.hasNext()) {
5049 RemoteControlStackEntry rcse = stackIterator.next();
5050 pw.println(" uid: " + rcse.mCallingUid +
5051 " -- id: " + rcse.mRccId +
5052 " -- type: " + rcse.mPlaybackType +
5053 " -- state: " + rcse.mPlaybackState +
5054 " -- vol handling: " + rcse.mPlaybackVolumeHandling +
5055 " -- vol: " + rcse.mPlaybackVolume +
Jean-Michel Trivi13570122012-06-19 14:03:09 -07005056 " -- volMax: " + rcse.mPlaybackVolumeMax +
5057 " -- volObs: " + rcse.mRemoteVolumeObs);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005058 }
Jean-Michel Trivi3261b532013-04-01 14:59:39 -07005059 synchronized(mCurrentRcLock) {
5060 pw.println("\nCurrent remote control generation ID = " + mCurrentRcClientGen);
5061 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005062 }
5063 synchronized (mMainRemote) {
5064 pw.println("\nRemote Volume State:");
5065 pw.println(" has remote: " + mHasRemotePlayback);
5066 pw.println(" is remote active: " + mMainRemoteIsActive);
5067 pw.println(" rccId: " + mMainRemote.mRccId);
5068 pw.println(" volume handling: "
5069 + ((mMainRemote.mVolumeHandling == RemoteControlClient.PLAYBACK_VOLUME_FIXED) ?
5070 "PLAYBACK_VOLUME_FIXED(0)" : "PLAYBACK_VOLUME_VARIABLE(1)"));
5071 pw.println(" volume: " + mMainRemote.mVolume);
5072 pw.println(" volume steps: " + mMainRemote.mVolumeMax);
5073 }
5074 }
5075
5076 /**
5077 * Helper function:
Jean-Michel Trivi4a570052013-03-04 12:03:17 -08005078 * Display in the log the current entries in the list of remote control displays
5079 */
5080 private void dumpRCDList(PrintWriter pw) {
5081 pw.println("\nRemote Control Display list entries:");
5082 synchronized(mRCStack) {
5083 final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
5084 while (displayIterator.hasNext()) {
5085 final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
5086 pw.println(" IRCD: " + di.mRcDisplay +
5087 " -- w:" + di.mArtworkExpectedWidth +
5088 " -- h:" + di.mArtworkExpectedHeight);
5089 }
5090 }
5091 }
5092
5093 /**
5094 * Helper function:
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07005095 * Remove any entry in the remote control stack that has the same package name as packageName
5096 * Pre-condition: packageName != null
5097 */
Dianne Hackborn79f7ec72013-04-04 18:50:23 -07005098 private void cleanupMediaButtonReceiverForPackage(String packageName, boolean removeAll) {
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07005099 synchronized(mRCStack) {
5100 if (mRCStack.empty()) {
5101 return;
5102 } else {
Dianne Hackborn79f7ec72013-04-04 18:50:23 -07005103 final PackageManager pm = mContext.getPackageManager();
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07005104 RemoteControlStackEntry oldTop = mRCStack.peek();
5105 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
5106 // iterate over the stack entries
Jean-Michel Trivic30f45b2012-12-26 10:46:29 -08005107 // (using an iterator on the stack so we can safely remove an entry after having
5108 // evaluated it, traversal order doesn't matter here)
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07005109 while(stackIterator.hasNext()) {
5110 RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
Dianne Hackborn79f7ec72013-04-04 18:50:23 -07005111 if (removeAll && packageName.equals(rcse.mMediaIntent.getCreatorPackage())) {
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07005112 // a stack entry is from the package being removed, remove it from the stack
5113 stackIterator.remove();
Dianne Hackborn79f7ec72013-04-04 18:50:23 -07005114 rcse.destroy();
5115 } else if (rcse.mReceiverComponent != null) {
5116 try {
5117 // Check to see if this receiver still exists.
5118 pm.getReceiverInfo(rcse.mReceiverComponent, 0);
5119 } catch (PackageManager.NameNotFoundException e) {
5120 // Not found -- remove it!
5121 stackIterator.remove();
5122 rcse.destroy();
5123 }
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07005124 }
5125 }
5126 if (mRCStack.empty()) {
5127 // no saved media button receiver
5128 mAudioHandler.sendMessage(
5129 mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
5130 null));
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07005131 } else if (oldTop != mRCStack.peek()) {
5132 // the top of the stack has changed, save it in the system settings
Dianne Hackborn961cae92013-03-20 14:59:43 -07005133 // by posting a message to persist it; only do this however if it has
5134 // a concrete component name (is not a transient registration)
5135 RemoteControlStackEntry rcse = mRCStack.peek();
5136 if (rcse.mReceiverComponent != null) {
5137 mAudioHandler.sendMessage(
5138 mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
5139 rcse.mReceiverComponent));
5140 }
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07005141 }
5142 }
5143 }
5144 }
5145
5146 /**
5147 * Helper function:
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005148 * Restore remote control receiver from the system settings.
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07005149 */
5150 private void restoreMediaButtonReceiver() {
Eric Laurentbc0fab1f2012-09-19 11:24:41 -07005151 String receiverName = Settings.System.getStringForUser(mContentResolver,
5152 Settings.System.MEDIA_BUTTON_RECEIVER, UserHandle.USER_CURRENT);
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07005153 if ((null != receiverName) && !receiverName.isEmpty()) {
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005154 ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
Jean-Michel Trivib839b832013-03-30 14:17:45 -07005155 if (eventReceiver == null) {
5156 // an invalid name was persisted
5157 return;
5158 }
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005159 // construct a PendingIntent targeted to the restored component name
5160 // for the media button and register it
5161 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
5162 // the associated intent will be handled by the component being registered
5163 mediaButtonIntent.setComponent(eventReceiver);
5164 PendingIntent pi = PendingIntent.getBroadcast(mContext,
5165 0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
Dianne Hackborn79f7ec72013-04-04 18:50:23 -07005166 registerMediaButtonIntent(pi, eventReceiver, null);
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07005167 }
5168 }
5169
5170 /**
5171 * Helper function:
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005172 * Set the new remote control receiver at the top of the RC focus stack.
Jean-Michel Trivibc834252012-12-26 15:09:03 -08005173 * Called synchronized on mAudioFocusLock, then mRCStack
Jean-Michel Trivib839b832013-03-30 14:17:45 -07005174 * precondition: mediaIntent != null
Jean-Michel Trivid327f212010-03-16 21:44:33 -07005175 */
Dianne Hackborn79f7ec72013-04-04 18:50:23 -07005176 private void pushMediaButtonReceiver_syncAfRcs(PendingIntent mediaIntent, ComponentName target,
5177 IBinder token) {
Jean-Michel Trivid327f212010-03-16 21:44:33 -07005178 // already at top of stack?
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005179 if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(mediaIntent)) {
Jean-Michel Trivid327f212010-03-16 21:44:33 -07005180 return;
5181 }
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005182 RemoteControlStackEntry rcse = null;
5183 boolean wasInsideStack = false;
Jean-Michel Trivibc834252012-12-26 15:09:03 -08005184 try {
5185 for (int index = mRCStack.size()-1; index >= 0; index--) {
5186 rcse = mRCStack.elementAt(index);
5187 if(rcse.mMediaIntent.equals(mediaIntent)) {
5188 // ok to remove element while traversing the stack since we're leaving the loop
5189 mRCStack.removeElementAt(index);
5190 wasInsideStack = true;
5191 break;
5192 }
Jean-Michel Trivid327f212010-03-16 21:44:33 -07005193 }
Jean-Michel Trivibc834252012-12-26 15:09:03 -08005194 } catch (ArrayIndexOutOfBoundsException e) {
5195 // not expected to happen, indicates improper concurrent modification
5196 Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
Jean-Michel Trivid327f212010-03-16 21:44:33 -07005197 }
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005198 if (!wasInsideStack) {
Dianne Hackborn79f7ec72013-04-04 18:50:23 -07005199 rcse = new RemoteControlStackEntry(this, mediaIntent, target, token);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005200 }
Jean-Michel Trivibc834252012-12-26 15:09:03 -08005201 mRCStack.push(rcse); // rcse is never null
Jean-Michel Trivid589fea2011-04-15 11:28:10 -07005202
5203 // post message to persist the default media button receiver
Jean-Michel Trivib839b832013-03-30 14:17:45 -07005204 if (target != null) {
5205 mAudioHandler.sendMessage( mAudioHandler.obtainMessage(
5206 MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) );
5207 }
Jean-Michel Trivid327f212010-03-16 21:44:33 -07005208 }
5209
5210 /**
5211 * Helper function:
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005212 * Remove the remote control receiver from the RC focus stack.
Jean-Michel Trivibc834252012-12-26 15:09:03 -08005213 * Called synchronized on mAudioFocusLock, then mRCStack
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005214 * precondition: pi != null
Jean-Michel Trivid327f212010-03-16 21:44:33 -07005215 */
Jean-Michel Trivibc834252012-12-26 15:09:03 -08005216 private void removeMediaButtonReceiver_syncAfRcs(PendingIntent pi) {
5217 try {
5218 for (int index = mRCStack.size()-1; index >= 0; index--) {
5219 final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
5220 if (rcse.mMediaIntent.equals(pi)) {
Dianne Hackborn79f7ec72013-04-04 18:50:23 -07005221 rcse.destroy();
Jean-Michel Trivibc834252012-12-26 15:09:03 -08005222 // ok to remove element while traversing the stack since we're leaving the loop
5223 mRCStack.removeElementAt(index);
5224 break;
5225 }
Jean-Michel Trivid327f212010-03-16 21:44:33 -07005226 }
Jean-Michel Trivibc834252012-12-26 15:09:03 -08005227 } catch (ArrayIndexOutOfBoundsException e) {
5228 // not expected to happen, indicates improper concurrent modification
5229 Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
Jean-Michel Trivid327f212010-03-16 21:44:33 -07005230 }
5231 }
5232
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005233 /**
5234 * Helper function:
5235 * Called synchronized on mRCStack
5236 */
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005237 private boolean isCurrentRcController(PendingIntent pi) {
5238 if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(pi)) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005239 return true;
5240 }
5241 return false;
5242 }
5243
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005244 //==========================================================================================
5245 // Remote control display / client
5246 //==========================================================================================
5247 /**
5248 * Update the remote control displays with the new "focused" client generation
5249 */
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005250 private void setNewRcClientOnDisplays_syncRcsCurrc(int newClientGeneration,
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005251 PendingIntent newMediaIntent, boolean clearing) {
Jean-Michel Trivi4a570052013-03-04 12:03:17 -08005252 synchronized(mRCStack) {
5253 if (mRcDisplays.size() > 0) {
5254 final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
5255 while (displayIterator.hasNext()) {
5256 final DisplayInfoForServer di = displayIterator.next();
5257 try {
5258 di.mRcDisplay.setCurrentClientId(
5259 newClientGeneration, newMediaIntent, clearing);
5260 } catch (RemoteException e) {
5261 Log.e(TAG, "Dead display in setNewRcClientOnDisplays_syncRcsCurrc()",e);
5262 di.release();
5263 displayIterator.remove();
5264 }
5265 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005266 }
5267 }
5268 }
5269
5270 /**
5271 * Update the remote control clients with the new "focused" client generation
5272 */
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005273 private void setNewRcClientGenerationOnClients_syncRcsCurrc(int newClientGeneration) {
Jean-Michel Trivic30f45b2012-12-26 10:46:29 -08005274 // (using an iterator on the stack so we can safely remove an entry if needed,
5275 // traversal order doesn't matter here as we update all entries)
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005276 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
5277 while(stackIterator.hasNext()) {
5278 RemoteControlStackEntry se = stackIterator.next();
5279 if ((se != null) && (se.mRcClient != null)) {
5280 try {
5281 se.mRcClient.setCurrentClientGenerationId(newClientGeneration);
5282 } catch (RemoteException e) {
Jean-Michel Trivi4a570052013-03-04 12:03:17 -08005283 Log.w(TAG, "Dead client in setNewRcClientGenerationOnClients_syncRcsCurrc()",e);
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005284 stackIterator.remove();
5285 se.unlinkToRcClientDeath();
5286 }
5287 }
5288 }
5289 }
5290
5291 /**
Jean-Michel Trivi4b6df6a32011-08-24 10:31:13 -07005292 * Update the displays and clients with the new "focused" client generation and name
5293 * @param newClientGeneration the new generation value matching a client update
Dianne Hackborn961cae92013-03-20 14:59:43 -07005294 * @param newMediaIntent the media button event receiver associated with the client.
Jean-Michel Trivi4b6df6a32011-08-24 10:31:13 -07005295 * May be null, which implies there is no registered media button event receiver.
5296 * @param clearing true if the new client generation value maps to a remote control update
5297 * where the display should be cleared.
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005298 */
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005299 private void setNewRcClient_syncRcsCurrc(int newClientGeneration,
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005300 PendingIntent newMediaIntent, boolean clearing) {
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07005301 // send the new valid client generation ID to all displays
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005302 setNewRcClientOnDisplays_syncRcsCurrc(newClientGeneration, newMediaIntent, clearing);
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07005303 // send the new valid client generation ID to all clients
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005304 setNewRcClientGenerationOnClients_syncRcsCurrc(newClientGeneration);
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005305 }
5306
5307 /**
5308 * Called when processing MSG_RCDISPLAY_CLEAR event
5309 */
5310 private void onRcDisplayClear() {
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005311 if (DEBUG_RC) Log.i(TAG, "Clear remote control display");
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005312
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07005313 synchronized(mRCStack) {
5314 synchronized(mCurrentRcLock) {
5315 mCurrentRcClientGen++;
5316 // synchronously update the displays and clients with the new client generation
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005317 setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005318 null /*newMediaIntent*/, true /*clearing*/);
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07005319 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005320 }
5321 }
5322
5323 /**
5324 * Called when processing MSG_RCDISPLAY_UPDATE event
5325 */
5326 private void onRcDisplayUpdate(RemoteControlStackEntry rcse, int flags /* USED ?*/) {
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07005327 synchronized(mRCStack) {
5328 synchronized(mCurrentRcLock) {
5329 if ((mCurrentRcClient != null) && (mCurrentRcClient.equals(rcse.mRcClient))) {
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005330 if (DEBUG_RC) Log.i(TAG, "Display/update remote control ");
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005331
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07005332 mCurrentRcClientGen++;
5333 // synchronously update the displays and clients with
5334 // the new client generation
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005335 setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005336 rcse.mMediaIntent /*newMediaIntent*/,
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07005337 false /*clearing*/);
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005338
Jean-Michel Trividb8a3612011-08-26 19:10:51 -07005339 // tell the current client that it needs to send info
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07005340 try {
Jean-Michel Trivi4a570052013-03-04 12:03:17 -08005341 mCurrentRcClient.onInformationRequested(mCurrentRcClientGen, flags);
Jean-Michel Trivi3094d952011-08-24 18:41:55 -07005342 } catch (RemoteException e) {
5343 Log.e(TAG, "Current valid remote client is dead: "+e);
5344 mCurrentRcClient = null;
5345 }
5346 } else {
5347 // the remote control display owner has changed between the
5348 // the message to update the display was sent, and the time it
5349 // gets to be processed (now)
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005350 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005351 }
5352 }
5353 }
5354
5355
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005356 /**
5357 * Helper function:
5358 * Called synchronized on mRCStack
5359 */
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07005360 private void clearRemoteControlDisplay_syncAfRcs() {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005361 synchronized(mCurrentRcLock) {
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07005362 mCurrentRcClient = null;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005363 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005364 // will cause onRcDisplayClear() to be called in AudioService's handler thread
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005365 mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_CLEAR) );
5366 }
5367
5368 /**
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07005369 * Helper function for code readability: only to be called from
5370 * checkUpdateRemoteControlDisplay_syncAfRcs() which checks the preconditions for
5371 * this method.
5372 * Preconditions:
5373 * - called synchronized mAudioFocusLock then on mRCStack
5374 * - mRCStack.isEmpty() is false
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005375 */
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07005376 private void updateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005377 RemoteControlStackEntry rcse = mRCStack.peek();
Jean-Michel Trivib5f04d62011-08-09 19:12:36 -07005378 int infoFlagsAboutToBeUsed = infoChangedFlags;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005379 // this is where we enforce opt-in for information display on the remote controls
5380 // with the new AudioManager.registerRemoteControlClient() API
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07005381 if (rcse.mRcClient == null) {
5382 //Log.w(TAG, "Can't update remote control display with null remote control client");
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07005383 clearRemoteControlDisplay_syncAfRcs();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005384 return;
5385 }
5386 synchronized(mCurrentRcLock) {
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07005387 if (!rcse.mRcClient.equals(mCurrentRcClient)) {
Jean-Michel Trivi7b6198c2011-08-07 11:27:50 -07005388 // new RC client, assume every type of information shall be queried
Jean-Michel Trivib5f04d62011-08-09 19:12:36 -07005389 infoFlagsAboutToBeUsed = RC_INFO_ALL;
Jean-Michel Trivi7b6198c2011-08-07 11:27:50 -07005390 }
Jean-Michel Trivi17da7692011-08-09 18:44:00 -07005391 mCurrentRcClient = rcse.mRcClient;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005392 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005393 // will cause onRcDisplayUpdate() to be called in AudioService's handler thread
Jean-Michel Trivib5f04d62011-08-09 19:12:36 -07005394 mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_UPDATE,
Jean-Michel Trivi0a9faa82011-08-14 17:36:42 -07005395 infoFlagsAboutToBeUsed /* arg1 */, 0, rcse /* obj, != null */) );
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005396 }
5397
5398 /**
5399 * Helper function:
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07005400 * Called synchronized on mAudioFocusLock, then mRCStack
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005401 * Check whether the remote control display should be updated, triggers the update if required
Jean-Michel Trivib5f04d62011-08-09 19:12:36 -07005402 * @param infoChangedFlags the flags corresponding to the remote control client information
5403 * that has changed, if applicable (checking for the update conditions might trigger a
5404 * clear, rather than an update event).
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005405 */
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07005406 private void checkUpdateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005407 // determine whether the remote control display should be refreshed
5408 // if either stack is empty, there is a mismatch, so clear the RC display
5409 if (mRCStack.isEmpty() || mFocusStack.isEmpty()) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07005410 clearRemoteControlDisplay_syncAfRcs();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005411 return;
5412 }
Jean-Michel Trivi1f9196a2012-07-26 15:11:07 -07005413
5414 // determine which entry in the AudioFocus stack to consider, and compare against the
5415 // top of the stack for the media button event receivers : simply using the top of the
5416 // stack would make the entry disappear from the RemoteControlDisplay in conditions such as
5417 // notifications playing during music playback.
Jean-Michel Trivi4dd3fb32012-11-20 17:06:51 -08005418 // Crawl the AudioFocus stack from the top until an entry is found with the following
5419 // characteristics:
Jean-Michel Trivi1f9196a2012-07-26 15:11:07 -07005420 // - focus gain on STREAM_MUSIC stream
5421 // - non-transient focus gain on a stream other than music
5422 FocusStackEntry af = null;
Jean-Michel Trivi4dd3fb32012-11-20 17:06:51 -08005423 try {
5424 for (int index = mFocusStack.size()-1; index >= 0; index--) {
5425 FocusStackEntry fse = mFocusStack.elementAt(index);
5426 if ((fse.mStreamType == AudioManager.STREAM_MUSIC)
5427 || (fse.mFocusChangeType == AudioManager.AUDIOFOCUS_GAIN)) {
5428 af = fse;
5429 break;
5430 }
Jean-Michel Trivi1f9196a2012-07-26 15:11:07 -07005431 }
Jean-Michel Trivi4dd3fb32012-11-20 17:06:51 -08005432 } catch (ArrayIndexOutOfBoundsException e) {
5433 Log.e(TAG, "Wrong index accessing audio focus stack when updating RCD: " + e);
5434 af = null;
Jean-Michel Trivi1f9196a2012-07-26 15:11:07 -07005435 }
5436 if (af == null) {
5437 clearRemoteControlDisplay_syncAfRcs();
5438 return;
5439 }
5440
5441 // if the audio focus and RC owners belong to different packages, there is a mismatch, clear
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005442 if ((mRCStack.peek().mCallingPackageName != null)
Jean-Michel Trivi1f9196a2012-07-26 15:11:07 -07005443 && (af.mPackageName != null)
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005444 && !(mRCStack.peek().mCallingPackageName.compareTo(
Jean-Michel Trivi1f9196a2012-07-26 15:11:07 -07005445 af.mPackageName) == 0)) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07005446 clearRemoteControlDisplay_syncAfRcs();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005447 return;
5448 }
5449 // if the audio focus didn't originate from the same Uid as the one in which the remote
5450 // control information will be retrieved, clear
Jean-Michel Trivi1f9196a2012-07-26 15:11:07 -07005451 if (mRCStack.peek().mCallingUid != af.mCallingUid) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07005452 clearRemoteControlDisplay_syncAfRcs();
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005453 return;
5454 }
Jean-Michel Trivi4dd3fb32012-11-20 17:06:51 -08005455
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005456 // refresh conditions were verified: update the remote controls
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07005457 // ok to call: synchronized mAudioFocusLock then on mRCStack, mRCStack is not empty
5458 updateRemoteControlDisplay_syncAfRcs(infoChangedFlags);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005459 }
Jean-Michel Trivid327f212010-03-16 21:44:33 -07005460
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08005461 /**
Jean-Michel Trivia578c482012-12-28 11:19:49 -08005462 * Helper function:
5463 * Post a message to asynchronously move the media button event receiver associated with the
5464 * given remote control client ID to the top of the remote control stack
5465 * @param rccId
5466 */
5467 private void postPromoteRcc(int rccId) {
5468 sendMsg(mAudioHandler, MSG_PROMOTE_RCC, SENDMSG_REPLACE,
5469 rccId /*arg1*/, 0, null, 0/*delay*/);
5470 }
5471
5472 private void onPromoteRcc(int rccId) {
5473 if (DEBUG_RC) { Log.d(TAG, "Promoting RCC " + rccId); }
5474 synchronized(mAudioFocusLock) {
5475 synchronized(mRCStack) {
5476 // ignore if given RCC ID is already at top of remote control stack
5477 if (!mRCStack.isEmpty() && (mRCStack.peek().mRccId == rccId)) {
5478 return;
5479 }
5480 int indexToPromote = -1;
5481 try {
5482 for (int index = mRCStack.size()-1; index >= 0; index--) {
5483 final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
5484 if (rcse.mRccId == rccId) {
5485 indexToPromote = index;
5486 break;
5487 }
5488 }
5489 if (indexToPromote >= 0) {
5490 if (DEBUG_RC) { Log.d(TAG, " moving RCC from index " + indexToPromote
5491 + " to " + (mRCStack.size()-1)); }
5492 final RemoteControlStackEntry rcse = mRCStack.remove(indexToPromote);
5493 mRCStack.push(rcse);
5494 // the RC stack changed, reevaluate the display
5495 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
5496 }
5497 } catch (ArrayIndexOutOfBoundsException e) {
5498 // not expected to happen, indicates improper concurrent modification
5499 Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
5500 }
5501 }//synchronized(mRCStack)
5502 }//synchronized(mAudioFocusLock)
5503 }
5504
5505 /**
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005506 * see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
Jean-Michel Trivib839b832013-03-30 14:17:45 -07005507 * precondition: mediaIntent != null
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005508 */
Dianne Hackborn79f7ec72013-04-04 18:50:23 -07005509 public void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver,
5510 IBinder token) {
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005511 Log.i(TAG, " Remote Control registerMediaButtonIntent() for " + mediaIntent);
Jean-Michel Trivid327f212010-03-16 21:44:33 -07005512
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005513 synchronized(mAudioFocusLock) {
5514 synchronized(mRCStack) {
Dianne Hackborn79f7ec72013-04-04 18:50:23 -07005515 pushMediaButtonReceiver_syncAfRcs(mediaIntent, eventReceiver, token);
Jean-Michel Trivib5f04d62011-08-09 19:12:36 -07005516 // new RC client, assume every type of information shall be queried
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07005517 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005518 }
Jean-Michel Trivid327f212010-03-16 21:44:33 -07005519 }
5520 }
5521
Eric Laurent6bc7f2c2011-11-29 18:49:40 -08005522 /**
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005523 * see AudioManager.unregisterMediaButtonIntent(PendingIntent mediaIntent)
5524 * precondition: mediaIntent != null, eventReceiver != null
5525 */
Jean-Michel Trivib839b832013-03-30 14:17:45 -07005526 public void unregisterMediaButtonIntent(PendingIntent mediaIntent)
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005527 {
5528 Log.i(TAG, " Remote Control unregisterMediaButtonIntent() for " + mediaIntent);
Jean-Michel Trivid327f212010-03-16 21:44:33 -07005529
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005530 synchronized(mAudioFocusLock) {
5531 synchronized(mRCStack) {
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005532 boolean topOfStackWillChange = isCurrentRcController(mediaIntent);
Jean-Michel Trivibc834252012-12-26 15:09:03 -08005533 removeMediaButtonReceiver_syncAfRcs(mediaIntent);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005534 if (topOfStackWillChange) {
Jean-Michel Trivib5f04d62011-08-09 19:12:36 -07005535 // current RC client will change, assume every type of info needs to be queried
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07005536 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005537 }
5538 }
Jean-Michel Trivid327f212010-03-16 21:44:33 -07005539 }
5540 }
5541
Jean-Michel Trivi26b3d1f2011-09-28 11:05:15 -07005542 /**
Jean-Michel Trivi722b8082012-05-15 15:18:33 -07005543 * see AudioManager.registerMediaButtonEventReceiverForCalls(ComponentName c)
5544 * precondition: c != null
5545 */
5546 public void registerMediaButtonEventReceiverForCalls(ComponentName c) {
5547 if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE")
5548 != PackageManager.PERMISSION_GRANTED) {
5549 Log.e(TAG, "Invalid permissions to register media button receiver for calls");
5550 return;
5551 }
5552 synchronized(mRCStack) {
5553 mMediaReceiverForCalls = c;
5554 }
5555 }
5556
5557 /**
5558 * see AudioManager.unregisterMediaButtonEventReceiverForCalls()
5559 */
5560 public void unregisterMediaButtonEventReceiverForCalls() {
5561 if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE")
5562 != PackageManager.PERMISSION_GRANTED) {
5563 Log.e(TAG, "Invalid permissions to unregister media button receiver for calls");
5564 return;
5565 }
5566 synchronized(mRCStack) {
5567 mMediaReceiverForCalls = null;
5568 }
5569 }
5570
5571 /**
Jean-Michel Trivi26b3d1f2011-09-28 11:05:15 -07005572 * see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...)
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005573 * @return the unique ID of the RemoteControlStackEntry associated with the RemoteControlClient
Jean-Michel Trivi26b3d1f2011-09-28 11:05:15 -07005574 * Note: using this method with rcClient == null is a way to "disable" the IRemoteControlClient
5575 * without modifying the RC stack, but while still causing the display to refresh (will
5576 * become blank as a result of this)
5577 */
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005578 public int registerRemoteControlClient(PendingIntent mediaIntent,
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005579 IRemoteControlClient rcClient, String callingPackageName) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07005580 if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005581 int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005582 synchronized(mAudioFocusLock) {
5583 synchronized(mRCStack) {
5584 // store the new display information
Jean-Michel Trivied2d10d2012-12-27 09:21:41 -08005585 try {
5586 for (int index = mRCStack.size()-1; index >= 0; index--) {
5587 final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
5588 if(rcse.mMediaIntent.equals(mediaIntent)) {
5589 // already had a remote control client?
5590 if (rcse.mRcClientDeathHandler != null) {
5591 // stop monitoring the old client's death
5592 rcse.unlinkToRcClientDeath();
5593 }
5594 // save the new remote control client
5595 rcse.mRcClient = rcClient;
5596 rcse.mCallingPackageName = callingPackageName;
5597 rcse.mCallingUid = Binder.getCallingUid();
5598 if (rcClient == null) {
5599 // here rcse.mRcClientDeathHandler is null;
5600 rcse.resetPlaybackInfo();
5601 break;
5602 }
5603 rccId = rcse.mRccId;
5604
5605 // there is a new (non-null) client:
Jean-Michel Trivi4a570052013-03-04 12:03:17 -08005606 // 1/ give the new client the displays (if any)
5607 if (mRcDisplays.size() > 0) {
5608 plugRemoteControlDisplaysIntoClient_syncRcStack(rcse.mRcClient);
Jean-Michel Trivied2d10d2012-12-27 09:21:41 -08005609 }
5610 // 2/ monitor the new client's death
5611 IBinder b = rcse.mRcClient.asBinder();
5612 RcClientDeathHandler rcdh =
5613 new RcClientDeathHandler(b, rcse.mMediaIntent);
5614 try {
5615 b.linkToDeath(rcdh, 0);
5616 } catch (RemoteException e) {
5617 // remote control client is DOA, disqualify it
5618 Log.w(TAG, "registerRemoteControlClient() has a dead client " + b);
5619 rcse.mRcClient = null;
5620 }
5621 rcse.mRcClientDeathHandler = rcdh;
Jean-Michel Trivi26b3d1f2011-09-28 11:05:15 -07005622 break;
5623 }
Jean-Michel Trivied2d10d2012-12-27 09:21:41 -08005624 }//for
5625 } catch (ArrayIndexOutOfBoundsException e) {
5626 // not expected to happen, indicates improper concurrent modification
5627 Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005628 }
Jean-Michel Trivied2d10d2012-12-27 09:21:41 -08005629
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005630 // if the eventReceiver is at the top of the stack
5631 // then check for potential refresh of the remote controls
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005632 if (isCurrentRcController(mediaIntent)) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07005633 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005634 }
Jean-Michel Trivied2d10d2012-12-27 09:21:41 -08005635 }//synchronized(mRCStack)
5636 }//synchronized(mAudioFocusLock)
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005637 return rccId;
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005638 }
5639
Jean-Michel Trivi4b6df6a32011-08-24 10:31:13 -07005640 /**
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005641 * see AudioManager.unregisterRemoteControlClient(PendingIntent pi, ...)
Jean-Michel Trivi4b6df6a32011-08-24 10:31:13 -07005642 * rcClient is guaranteed non-null
5643 */
Jean-Michel Trivif0cff042011-09-14 18:11:09 -07005644 public void unregisterRemoteControlClient(PendingIntent mediaIntent,
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005645 IRemoteControlClient rcClient) {
Jean-Michel Trivia578c482012-12-28 11:19:49 -08005646 if (DEBUG_RC) Log.i(TAG, "Unregister remote control client rcClient="+rcClient);
Jean-Michel Trivi4b6df6a32011-08-24 10:31:13 -07005647 synchronized(mAudioFocusLock) {
5648 synchronized(mRCStack) {
Jean-Michel Trivied2d10d2012-12-27 09:21:41 -08005649 boolean topRccChange = false;
5650 try {
5651 for (int index = mRCStack.size()-1; index >= 0; index--) {
5652 final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
5653 if ((rcse.mMediaIntent.equals(mediaIntent))
5654 && rcClient.equals(rcse.mRcClient)) {
5655 // we found the IRemoteControlClient to unregister
5656 // stop monitoring its death
5657 rcse.unlinkToRcClientDeath();
5658 // reset the client-related fields
5659 rcse.mRcClient = null;
5660 rcse.mCallingPackageName = null;
5661 topRccChange = (index == mRCStack.size()-1);
5662 // there can only be one matching RCC in the RC stack, we're done
5663 break;
5664 }
Jean-Michel Trivi4b6df6a32011-08-24 10:31:13 -07005665 }
Jean-Michel Trivied2d10d2012-12-27 09:21:41 -08005666 } catch (ArrayIndexOutOfBoundsException e) {
5667 // not expected to happen, indicates improper concurrent modification
5668 Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
5669 }
5670 if (topRccChange) {
5671 // no more RCC for the RCD, check for potential refresh of the remote controls
5672 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
Jean-Michel Trivi4b6df6a32011-08-24 10:31:13 -07005673 }
5674 }
5675 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005676 }
5677
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005678
Jean-Michel Trivi4a570052013-03-04 12:03:17 -08005679 /**
5680 * A class to encapsulate all the information about a remote control display.
5681 * After instanciation, init() must always be called before the object is added in the list
5682 * of displays.
5683 * Before being removed from the list of displays, release() must always be called (otherwise
5684 * it will leak death handlers).
5685 */
5686 private class DisplayInfoForServer implements IBinder.DeathRecipient {
5687 /** may never be null */
5688 private IRemoteControlDisplay mRcDisplay;
5689 private IBinder mRcDisplayBinder;
5690 private int mArtworkExpectedWidth = -1;
5691 private int mArtworkExpectedHeight = -1;
5692
5693 public DisplayInfoForServer(IRemoteControlDisplay rcd, int w, int h) {
5694 if (DEBUG_RC) Log.i(TAG, "new DisplayInfoForServer for " + rcd + " w=" + w + " h=" + h);
5695 mRcDisplay = rcd;
5696 mRcDisplayBinder = rcd.asBinder();
5697 mArtworkExpectedWidth = w;
5698 mArtworkExpectedHeight = h;
5699 }
5700
5701 public boolean init() {
5702 try {
5703 mRcDisplayBinder.linkToDeath(this, 0);
5704 } catch (RemoteException e) {
5705 // remote control display is DOA, disqualify it
5706 Log.w(TAG, "registerRemoteControlDisplay() has a dead client " + mRcDisplayBinder);
5707 return false;
5708 }
5709 return true;
5710 }
5711
5712 public void release() {
5713 try {
5714 mRcDisplayBinder.unlinkToDeath(this, 0);
5715 } catch (java.util.NoSuchElementException e) {
5716 // not much we can do here, the display should have been unregistered anyway
5717 Log.e(TAG, "Error in DisplaInfoForServer.relase()", e);
5718 }
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005719 }
5720
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005721 public void binderDied() {
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005722 synchronized(mRCStack) {
Jean-Michel Trivi4a570052013-03-04 12:03:17 -08005723 Log.w(TAG, "RemoteControl: display " + mRcDisplay + " died");
5724 // remove the display from the list
5725 final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
5726 while (displayIterator.hasNext()) {
5727 final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
5728 if (di.mRcDisplay == mRcDisplay) {
5729 if (DEBUG_RC) Log.w(TAG, " RCD removed from list");
5730 displayIterator.remove();
5731 return;
5732 }
5733 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005734 }
5735 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005736 }
5737
Jean-Michel Trivi4a570052013-03-04 12:03:17 -08005738 /**
5739 * The remote control displays.
5740 * Access synchronized on mRCStack
5741 */
5742 private ArrayList<DisplayInfoForServer> mRcDisplays = new ArrayList<DisplayInfoForServer>(1);
5743
5744 /**
5745 * Plug each registered display into the specified client
5746 * @param rcc, guaranteed non null
5747 */
5748 private void plugRemoteControlDisplaysIntoClient_syncRcStack(IRemoteControlClient rcc) {
5749 final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
5750 while (displayIterator.hasNext()) {
5751 final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005752 try {
Jean-Michel Trivi4a570052013-03-04 12:03:17 -08005753 rcc.plugRemoteControlDisplay(di.mRcDisplay, di.mArtworkExpectedWidth,
5754 di.mArtworkExpectedHeight);
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005755 } catch (RemoteException e) {
Jean-Michel Trivi4a570052013-03-04 12:03:17 -08005756 Log.e(TAG, "Error connecting RCD to RCC in RCC registration",e);
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005757 }
5758 }
5759 }
5760
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005761 /**
Jean-Michel Trivi4a570052013-03-04 12:03:17 -08005762 * Is the remote control display interface already registered
5763 * @param rcd
5764 * @return true if the IRemoteControlDisplay is already in the list of displays
5765 */
5766 private boolean rcDisplayIsPluggedIn_syncRcStack(IRemoteControlDisplay rcd) {
5767 final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
5768 while (displayIterator.hasNext()) {
5769 final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
5770 if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
5771 return true;
5772 }
5773 }
5774 return false;
5775 }
5776
5777 /**
Jean-Michel Trivi958bba62011-08-30 10:52:59 -07005778 * Register an IRemoteControlDisplay.
5779 * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient
5780 * at the top of the stack to update the new display with its information.
Jean-Michel Trivi4a570052013-03-04 12:03:17 -08005781 * @see android.media.IAudioService#registerRemoteControlDisplay(android.media.IRemoteControlDisplay, int, int)
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005782 * @param rcd the IRemoteControlDisplay to register. No effect if null.
Jean-Michel Trivi4a570052013-03-04 12:03:17 -08005783 * @param w the maximum width of the expected bitmap. Negative or zero values indicate this
5784 * display doesn't need to receive artwork.
5785 * @param h the maximum height of the expected bitmap. Negative or zero values indicate this
5786 * display doesn't need to receive artwork.
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005787 */
Jean-Michel Trivi4a570052013-03-04 12:03:17 -08005788 public void registerRemoteControlDisplay(IRemoteControlDisplay rcd, int w, int h) {
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005789 if (DEBUG_RC) Log.d(TAG, ">>> registerRemoteControlDisplay("+rcd+")");
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07005790 synchronized(mAudioFocusLock) {
5791 synchronized(mRCStack) {
Jean-Michel Trivi4a570052013-03-04 12:03:17 -08005792 if ((rcd == null) || rcDisplayIsPluggedIn_syncRcStack(rcd)) {
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07005793 return;
5794 }
Jean-Michel Trivi4a570052013-03-04 12:03:17 -08005795 DisplayInfoForServer di = new DisplayInfoForServer(rcd, w, h);
5796 if (!di.init()) {
5797 if (DEBUG_RC) Log.e(TAG, " error registering RCD");
5798 return;
5799 }
5800 // add RCD to list of displays
5801 mRcDisplays.add(di);
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005802
Jean-Michel Trivi4a570052013-03-04 12:03:17 -08005803 // let all the remote control clients know there is a new display (so the remote
5804 // control stack traversal order doesn't matter).
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07005805 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
5806 while(stackIterator.hasNext()) {
5807 RemoteControlStackEntry rcse = stackIterator.next();
5808 if(rcse.mRcClient != null) {
5809 try {
Jean-Michel Trivi4a570052013-03-04 12:03:17 -08005810 rcse.mRcClient.plugRemoteControlDisplay(rcd, w, h);
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07005811 } catch (RemoteException e) {
Jean-Michel Trivi4a570052013-03-04 12:03:17 -08005812 Log.e(TAG, "Error connecting RCD to client: ", e);
Jean-Michel Trivib716f0b2011-08-31 18:24:04 -07005813 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005814 }
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005815 }
Jean-Michel Trividb8a3612011-08-26 19:10:51 -07005816
Jean-Michel Trivi33879452011-08-31 11:42:17 -07005817 // 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 -07005818 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
Jean-Michel Trivi33879452011-08-31 11:42:17 -07005819 }
Jean-Michel Trivi8f619182011-07-21 15:10:10 -07005820 }
5821 }
Jean-Michel Trivid327f212010-03-16 21:44:33 -07005822
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005823 /**
5824 * Unregister an IRemoteControlDisplay.
Jean-Michel Trivi4a570052013-03-04 12:03:17 -08005825 * No effect if the IRemoteControlDisplay hasn't been successfully registered.
5826 * @see android.media.IAudioService#unregisterRemoteControlDisplay(android.media.IRemoteControlDisplay)
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005827 * @param rcd the IRemoteControlDisplay to unregister. No effect if null.
5828 */
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005829 public void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) {
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005830 if (DEBUG_RC) Log.d(TAG, "<<< unregisterRemoteControlDisplay("+rcd+")");
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005831 synchronized(mRCStack) {
Jean-Michel Trivi4a570052013-03-04 12:03:17 -08005832 if (rcd == null) {
Jean-Michel Trivi18e7bce2011-08-26 12:11:36 -07005833 return;
5834 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005835
Jean-Michel Trivi4a570052013-03-04 12:03:17 -08005836 boolean displayWasPluggedIn = false;
5837 final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
5838 while (displayIterator.hasNext() && !displayWasPluggedIn) {
5839 final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
5840 if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
5841 displayWasPluggedIn = true;
5842 di.release();
5843 displayIterator.remove();
5844 }
5845 }
5846
5847 if (displayWasPluggedIn) {
5848 // disconnect this remote control display from all the clients, so the remote
5849 // control stack traversal order doesn't matter
5850 final Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
5851 while(stackIterator.hasNext()) {
5852 final RemoteControlStackEntry rcse = stackIterator.next();
5853 if(rcse.mRcClient != null) {
5854 try {
5855 rcse.mRcClient.unplugRemoteControlDisplay(rcd);
5856 } catch (RemoteException e) {
5857 Log.e(TAG, "Error disconnecting remote control display to client: ", e);
5858 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005859 }
5860 }
Jean-Michel Trivi4a570052013-03-04 12:03:17 -08005861 } else {
5862 if (DEBUG_RC) Log.w(TAG, " trying to unregister unregistered RCD");
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005863 }
5864 }
5865 }
5866
Jean-Michel Trivi4a570052013-03-04 12:03:17 -08005867 /**
5868 * Update the size of the artwork used by an IRemoteControlDisplay.
5869 * @see android.media.IAudioService#remoteControlDisplayUsesBitmapSize(android.media.IRemoteControlDisplay, int, int)
5870 * @param rcd the IRemoteControlDisplay with the new artwork size requirement
5871 * @param w the maximum width of the expected bitmap. Negative or zero values indicate this
5872 * display doesn't need to receive artwork.
5873 * @param h the maximum height of the expected bitmap. Negative or zero values indicate this
5874 * display doesn't need to receive artwork.
5875 */
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005876 public void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) {
5877 synchronized(mRCStack) {
Jean-Michel Trivi4a570052013-03-04 12:03:17 -08005878 final Iterator<DisplayInfoForServer> displayIterator = mRcDisplays.iterator();
5879 boolean artworkSizeUpdate = false;
5880 while (displayIterator.hasNext() && !artworkSizeUpdate) {
5881 final DisplayInfoForServer di = (DisplayInfoForServer) displayIterator.next();
5882 if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
5883 if ((di.mArtworkExpectedWidth != w) || (di.mArtworkExpectedHeight != h)) {
5884 di.mArtworkExpectedWidth = w;
5885 di.mArtworkExpectedHeight = h;
5886 artworkSizeUpdate = true;
5887 }
5888 }
5889 }
5890 if (artworkSizeUpdate) {
5891 // RCD is currently plugged in and its artwork size has changed, notify all RCCs,
5892 // stack traversal order doesn't matter
5893 final Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
5894 while(stackIterator.hasNext()) {
5895 final RemoteControlStackEntry rcse = stackIterator.next();
5896 if(rcse.mRcClient != null) {
5897 try {
5898 rcse.mRcClient.setBitmapSizeForDisplay(rcd, w, h);
5899 } catch (RemoteException e) {
5900 Log.e(TAG, "Error setting bitmap size for RCD on RCC: ", e);
5901 }
5902 }
5903 }
5904 }
Jean-Michel Trivi4426e422011-08-18 19:16:47 -07005905 }
5906 }
5907
Jean-Michel Trivi3261b532013-04-01 14:59:39 -07005908 public void setRemoteControlClientPlaybackPosition(int generationId, long timeMs) {
Jean-Michel Trivi3fbf67e2013-04-09 11:10:18 -07005909 // ignore position change requests if invalid generation ID
5910 synchronized(mRCStack) {
5911 synchronized(mCurrentRcLock) {
5912 if (mCurrentRcClientGen != generationId) {
5913 return;
5914 }
5915 }
5916 }
5917 // discard any unprocessed seek request in the message queue, and replace with latest
5918 sendMsg(mAudioHandler, MSG_RCC_SEEK_REQUEST, SENDMSG_REPLACE, generationId /* arg1 */,
Jean-Michel Trivi3261b532013-04-01 14:59:39 -07005919 0 /* arg2 ignored*/, new Long(timeMs) /* obj */, 0 /* delay */);
5920 }
5921
5922 public void onSetRemoteControlClientPlaybackPosition(int generationId, long timeMs) {
5923 if(DEBUG_RC) Log.d(TAG, "onSetRemoteControlClientPlaybackPosition(genId=" + generationId +
5924 ", timeMs=" + timeMs + ")");
5925 synchronized(mRCStack) {
5926 synchronized(mCurrentRcLock) {
5927 if ((mCurrentRcClient != null) && (mCurrentRcClientGen == generationId)) {
5928 // tell the current client to seek to the requested location
5929 try {
5930 mCurrentRcClient.seekTo(generationId, timeMs);
5931 } catch (RemoteException e) {
5932 Log.e(TAG, "Current valid remote client is dead: "+e);
5933 mCurrentRcClient = null;
5934 }
5935 }
5936 }
5937 }
5938 }
5939
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005940 public void setPlaybackInfoForRcc(int rccId, int what, int value) {
Jean-Michel Trivif98de1e2012-06-20 13:40:22 -07005941 sendMsg(mAudioHandler, MSG_RCC_NEW_PLAYBACK_INFO, SENDMSG_QUEUE,
5942 rccId /* arg1 */, what /* arg2 */, Integer.valueOf(value) /* obj */, 0 /* delay */);
5943 }
5944
5945 // handler for MSG_RCC_NEW_PLAYBACK_INFO
5946 private void onNewPlaybackInfoForRcc(int rccId, int key, int value) {
5947 if(DEBUG_RC) Log.d(TAG, "onNewPlaybackInfoForRcc(id=" + rccId +
5948 ", what=" + key + ",val=" + value + ")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005949 synchronized(mRCStack) {
Jean-Michel Trivi84aa61e2012-12-27 12:33:57 -08005950 // iterating from top of stack as playback information changes are more likely
5951 // on entries at the top of the remote control stack
5952 try {
5953 for (int index = mRCStack.size()-1; index >= 0; index--) {
5954 final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
5955 if (rcse.mRccId == rccId) {
5956 switch (key) {
5957 case RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE:
5958 rcse.mPlaybackType = value;
5959 postReevaluateRemote();
5960 break;
5961 case RemoteControlClient.PLAYBACKINFO_VOLUME:
5962 rcse.mPlaybackVolume = value;
5963 synchronized (mMainRemote) {
5964 if (rccId == mMainRemote.mRccId) {
5965 mMainRemote.mVolume = value;
5966 mVolumePanel.postHasNewRemotePlaybackInfo();
5967 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005968 }
Jean-Michel Trivi84aa61e2012-12-27 12:33:57 -08005969 break;
5970 case RemoteControlClient.PLAYBACKINFO_VOLUME_MAX:
5971 rcse.mPlaybackVolumeMax = value;
5972 synchronized (mMainRemote) {
5973 if (rccId == mMainRemote.mRccId) {
5974 mMainRemote.mVolumeMax = value;
5975 mVolumePanel.postHasNewRemotePlaybackInfo();
5976 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005977 }
Jean-Michel Trivi84aa61e2012-12-27 12:33:57 -08005978 break;
5979 case RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING:
5980 rcse.mPlaybackVolumeHandling = value;
5981 synchronized (mMainRemote) {
5982 if (rccId == mMainRemote.mRccId) {
5983 mMainRemote.mVolumeHandling = value;
5984 mVolumePanel.postHasNewRemotePlaybackInfo();
5985 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005986 }
Jean-Michel Trivi84aa61e2012-12-27 12:33:57 -08005987 break;
5988 case RemoteControlClient.PLAYBACKINFO_USES_STREAM:
5989 rcse.mPlaybackStream = value;
5990 break;
Jean-Michel Trivi84aa61e2012-12-27 12:33:57 -08005991 default:
5992 Log.e(TAG, "unhandled key " + key + " for RCC " + rccId);
5993 break;
5994 }
5995 return;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07005996 }
Jean-Michel Trivi84aa61e2012-12-27 12:33:57 -08005997 }//for
5998 } catch (ArrayIndexOutOfBoundsException e) {
5999 // not expected to happen, indicates improper concurrent modification
Jean-Michel Trivibc43b4c2013-03-22 09:30:50 -07006000 Log.e(TAG, "Wrong index mRCStack on onNewPlaybackInfoForRcc, lock error? ", e);
6001 }
6002 }
6003 }
6004
6005 public void setPlaybackStateForRcc(int rccId, int state, long timeMs, float speed) {
6006 sendMsg(mAudioHandler, MSG_RCC_NEW_PLAYBACK_STATE, SENDMSG_QUEUE,
6007 rccId /* arg1 */, state /* arg2 */,
6008 new RccPlaybackState(state, timeMs, speed) /* obj */, 0 /* delay */);
6009 }
6010
6011 public void onNewPlaybackStateForRcc(int rccId, int state, RccPlaybackState newState) {
6012 if(DEBUG_RC) Log.d(TAG, "onNewPlaybackStateForRcc(id=" + rccId + ", state=" + state
6013 + ", time=" + newState.mPositionMs + ", speed=" + newState.mSpeed + ")");
6014 synchronized(mRCStack) {
6015 // iterating from top of stack as playback information changes are more likely
6016 // on entries at the top of the remote control stack
6017 try {
6018 for (int index = mRCStack.size()-1; index >= 0; index--) {
6019 final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
6020 if (rcse.mRccId == rccId) {
6021 rcse.mPlaybackState = newState;
6022 synchronized (mMainRemote) {
6023 if (rccId == mMainRemote.mRccId) {
6024 mMainRemoteIsActive = isPlaystateActive(state);
6025 postReevaluateRemote();
6026 }
6027 }
6028 // an RCC moving to a "playing" state should become the media button
6029 // event receiver so it can be controlled, without requiring the
6030 // app to re-register its receiver
6031 if (isPlaystateActive(state)) {
6032 postPromoteRcc(rccId);
6033 }
6034 }
6035 }//for
6036 } catch (ArrayIndexOutOfBoundsException e) {
6037 // not expected to happen, indicates improper concurrent modification
6038 Log.e(TAG, "Wrong index on mRCStack in onNewPlaybackStateForRcc, lock error? ", e);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07006039 }
6040 }
6041 }
6042
Jean-Michel Trivi13570122012-06-19 14:03:09 -07006043 public void registerRemoteVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
Jean-Michel Trivif98de1e2012-06-20 13:40:22 -07006044 sendMsg(mAudioHandler, MSG_RCC_NEW_VOLUME_OBS, SENDMSG_QUEUE,
6045 rccId /* arg1 */, 0, rvo /* obj */, 0 /* delay */);
6046 }
6047
6048 // handler for MSG_RCC_NEW_VOLUME_OBS
6049 private void onRegisterVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
Jean-Michel Trivi13570122012-06-19 14:03:09 -07006050 synchronized(mRCStack) {
Jean-Michel Trivi66cbd192012-12-27 10:24:58 -08006051 // The stack traversal order doesn't matter because there is only one stack entry
Jean-Michel Trivi5df0e6a2013-01-07 11:22:40 -08006052 // with this RCC ID, but the matching ID is more likely at the top of the stack, so
6053 // start iterating from the top.
6054 try {
6055 for (int index = mRCStack.size()-1; index >= 0; index--) {
6056 final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
6057 if (rcse.mRccId == rccId) {
6058 rcse.mRemoteVolumeObs = rvo;
6059 break;
6060 }
Jean-Michel Trivi13570122012-06-19 14:03:09 -07006061 }
Jean-Michel Trivi5df0e6a2013-01-07 11:22:40 -08006062 } catch (ArrayIndexOutOfBoundsException e) {
6063 // not expected to happen, indicates improper concurrent modification
6064 Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
Jean-Michel Trivi13570122012-06-19 14:03:09 -07006065 }
6066 }
6067 }
6068
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07006069 /**
6070 * Checks if a remote client is active on the supplied stream type. Update the remote stream
6071 * volume state if found and playing
6072 * @param streamType
6073 * @return false if no remote playing is currently playing
6074 */
6075 private boolean checkUpdateRemoteStateIfActive(int streamType) {
6076 synchronized(mRCStack) {
Jean-Michel Trivi84aa61e2012-12-27 12:33:57 -08006077 // iterating from top of stack as active playback is more likely on entries at the top
6078 try {
6079 for (int index = mRCStack.size()-1; index >= 0; index--) {
6080 final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
6081 if ((rcse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE)
Jean-Michel Trivibc43b4c2013-03-22 09:30:50 -07006082 && isPlaystateActive(rcse.mPlaybackState.mState)
Jean-Michel Trivi84aa61e2012-12-27 12:33:57 -08006083 && (rcse.mPlaybackStream == streamType)) {
6084 if (DEBUG_RC) Log.d(TAG, "remote playback active on stream " + streamType
6085 + ", vol =" + rcse.mPlaybackVolume);
6086 synchronized (mMainRemote) {
6087 mMainRemote.mRccId = rcse.mRccId;
6088 mMainRemote.mVolume = rcse.mPlaybackVolume;
6089 mMainRemote.mVolumeMax = rcse.mPlaybackVolumeMax;
6090 mMainRemote.mVolumeHandling = rcse.mPlaybackVolumeHandling;
6091 mMainRemoteIsActive = true;
6092 }
6093 return true;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07006094 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07006095 }
Jean-Michel Trivi84aa61e2012-12-27 12:33:57 -08006096 } catch (ArrayIndexOutOfBoundsException e) {
6097 // not expected to happen, indicates improper concurrent modification
6098 Log.e(TAG, "Wrong index accessing RC stack, lock error? ", e);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07006099 }
6100 }
6101 synchronized (mMainRemote) {
6102 mMainRemoteIsActive = false;
6103 }
6104 return false;
6105 }
6106
6107 /**
6108 * Returns true if the given playback state is considered "active", i.e. it describes a state
6109 * where playback is happening, or about to
6110 * @param playState the playback state to evaluate
6111 * @return true if active, false otherwise (inactive or unknown)
6112 */
6113 private static boolean isPlaystateActive(int playState) {
6114 switch (playState) {
6115 case RemoteControlClient.PLAYSTATE_PLAYING:
6116 case RemoteControlClient.PLAYSTATE_BUFFERING:
6117 case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
6118 case RemoteControlClient.PLAYSTATE_REWINDING:
6119 case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
6120 case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
6121 return true;
6122 default:
6123 return false;
6124 }
6125 }
6126
6127 private void adjustRemoteVolume(int streamType, int direction, int flags) {
6128 int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
6129 boolean volFixed = false;
6130 synchronized (mMainRemote) {
6131 if (!mMainRemoteIsActive) {
6132 if (DEBUG_VOL) Log.w(TAG, "adjustRemoteVolume didn't find an active client");
6133 return;
6134 }
6135 rccId = mMainRemote.mRccId;
6136 volFixed = (mMainRemote.mVolumeHandling ==
6137 RemoteControlClient.PLAYBACK_VOLUME_FIXED);
6138 }
6139 // unlike "local" stream volumes, we can't compute the new volume based on the direction,
6140 // we can only notify the remote that volume needs to be updated, and we'll get an async'
6141 // update through setPlaybackInfoForRcc()
6142 if (!volFixed) {
6143 sendVolumeUpdateToRemote(rccId, direction);
6144 }
6145
6146 // fire up the UI
6147 mVolumePanel.postRemoteVolumeChanged(streamType, flags);
6148 }
6149
6150 private void sendVolumeUpdateToRemote(int rccId, int direction) {
6151 if (DEBUG_VOL) { Log.d(TAG, "sendVolumeUpdateToRemote(rccId="+rccId+" , dir="+direction); }
6152 if (direction == 0) {
6153 // only handling discrete events
6154 return;
6155 }
Jean-Michel Trivi13570122012-06-19 14:03:09 -07006156 IRemoteVolumeObserver rvo = null;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07006157 synchronized (mRCStack) {
Jean-Michel Trivi66cbd192012-12-27 10:24:58 -08006158 // The stack traversal order doesn't matter because there is only one stack entry
Jean-Michel Trivi5df0e6a2013-01-07 11:22:40 -08006159 // with this RCC ID, but the matching ID is more likely at the top of the stack, so
6160 // start iterating from the top.
6161 try {
6162 for (int index = mRCStack.size()-1; index >= 0; index--) {
6163 final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
6164 //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
6165 if (rcse.mRccId == rccId) {
6166 rvo = rcse.mRemoteVolumeObs;
6167 break;
6168 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07006169 }
Jean-Michel Trivi5df0e6a2013-01-07 11:22:40 -08006170 } catch (ArrayIndexOutOfBoundsException e) {
6171 // not expected to happen, indicates improper concurrent modification
6172 Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07006173 }
6174 }
Jean-Michel Trivi13570122012-06-19 14:03:09 -07006175 if (rvo != null) {
6176 try {
6177 rvo.dispatchRemoteVolumeUpdate(direction, -1);
6178 } catch (RemoteException e) {
6179 Log.e(TAG, "Error dispatching relative volume update", e);
6180 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07006181 }
6182 }
6183
6184 public int getRemoteStreamMaxVolume() {
6185 synchronized (mMainRemote) {
6186 if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
6187 return 0;
6188 }
6189 return mMainRemote.mVolumeMax;
6190 }
6191 }
6192
6193 public int getRemoteStreamVolume() {
6194 synchronized (mMainRemote) {
6195 if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
6196 return 0;
6197 }
6198 return mMainRemote.mVolume;
6199 }
6200 }
6201
6202 public void setRemoteStreamVolume(int vol) {
6203 if (DEBUG_VOL) { Log.d(TAG, "setRemoteStreamVolume(vol="+vol+")"); }
6204 int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
6205 synchronized (mMainRemote) {
6206 if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
6207 return;
6208 }
6209 rccId = mMainRemote.mRccId;
6210 }
Jean-Michel Trivi13570122012-06-19 14:03:09 -07006211 IRemoteVolumeObserver rvo = null;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07006212 synchronized (mRCStack) {
Jean-Michel Trivi66cbd192012-12-27 10:24:58 -08006213 // The stack traversal order doesn't matter because there is only one stack entry
Jean-Michel Trivi5df0e6a2013-01-07 11:22:40 -08006214 // with this RCC ID, but the matching ID is more likely at the top of the stack, so
6215 // start iterating from the top.
6216 try {
6217 for (int index = mRCStack.size()-1; index >= 0; index--) {
6218 final RemoteControlStackEntry rcse = mRCStack.elementAt(index);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07006219 //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
Jean-Michel Trivi5df0e6a2013-01-07 11:22:40 -08006220 if (rcse.mRccId == rccId) {
6221 rvo = rcse.mRemoteVolumeObs;
6222 break;
6223 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07006224 }
Jean-Michel Trivi5df0e6a2013-01-07 11:22:40 -08006225 } catch (ArrayIndexOutOfBoundsException e) {
6226 // not expected to happen, indicates improper concurrent modification
6227 Log.e(TAG, "Wrong index accessing media button stack, lock error? ", e);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07006228 }
6229 }
Jean-Michel Trivi13570122012-06-19 14:03:09 -07006230 if (rvo != null) {
6231 try {
6232 rvo.dispatchRemoteVolumeUpdate(0, vol);
6233 } catch (RemoteException e) {
6234 Log.e(TAG, "Error dispatching absolute volume update", e);
6235 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07006236 }
6237 }
6238
6239 /**
6240 * Call to make AudioService reevaluate whether it's in a mode where remote players should
6241 * have their volume controlled. In this implementation this is only to reset whether
6242 * VolumePanel should display remote volumes
6243 */
6244 private void postReevaluateRemote() {
6245 sendMsg(mAudioHandler, MSG_REEVALUATE_REMOTE, SENDMSG_QUEUE, 0, 0, null, 0);
6246 }
6247
6248 private void onReevaluateRemote() {
6249 if (DEBUG_VOL) { Log.w(TAG, "onReevaluateRemote()"); }
6250 // is there a registered RemoteControlClient that is handling remote playback
6251 boolean hasRemotePlayback = false;
6252 synchronized (mRCStack) {
Jean-Michel Trivi66cbd192012-12-27 10:24:58 -08006253 // iteration stops when PLAYBACK_TYPE_REMOTE is found, so remote control stack
6254 // traversal order doesn't matter
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07006255 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
6256 while(stackIterator.hasNext()) {
6257 RemoteControlStackEntry rcse = stackIterator.next();
6258 if (rcse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) {
6259 hasRemotePlayback = true;
6260 break;
6261 }
6262 }
6263 }
6264 synchronized (mMainRemote) {
6265 if (mHasRemotePlayback != hasRemotePlayback) {
6266 mHasRemotePlayback = hasRemotePlayback;
6267 mVolumePanel.postRemoteSliderVisibility(hasRemotePlayback);
6268 }
6269 }
6270 }
6271
Jean-Michel Trivif26f0172012-04-25 16:23:20 -07006272 //==========================================================================================
6273 // Device orientation
6274 //==========================================================================================
6275 /**
6276 * Handles device configuration changes that may map to a change in the orientation.
6277 * This feature is optional, and is defined by the definition and value of the
6278 * "ro.audio.monitorOrientation" system property.
6279 */
6280 private void handleConfigurationChanged(Context context) {
6281 try {
6282 // reading new orientation "safely" (i.e. under try catch) in case anything
6283 // goes wrong when obtaining resources and configuration
Eric Laurentd640bd32012-09-28 18:01:48 -07006284 Configuration config = context.getResources().getConfiguration();
6285 if (mMonitorOrientation) {
6286 int newOrientation = config.orientation;
6287 if (newOrientation != mDeviceOrientation) {
6288 mDeviceOrientation = newOrientation;
6289 setOrientationForAudioSystem();
6290 }
Jean-Michel Trivif26f0172012-04-25 16:23:20 -07006291 }
Eric Laurentd640bd32012-09-28 18:01:48 -07006292 sendMsg(mAudioHandler,
6293 MSG_CONFIGURE_SAFE_MEDIA_VOLUME,
6294 SENDMSG_REPLACE,
6295 0,
6296 0,
6297 null,
6298 0);
Eric Laurentdd45d012012-10-08 09:04:34 -07006299
6300 boolean cameraSoundForced = mContext.getResources().getBoolean(
6301 com.android.internal.R.bool.config_camera_sound_forced);
6302 synchronized (mSettingsLock) {
6303 synchronized (mCameraSoundForced) {
6304 if (cameraSoundForced != mCameraSoundForced) {
6305 mCameraSoundForced = cameraSoundForced;
6306
6307 VolumeStreamState s = mStreamStates[AudioSystem.STREAM_SYSTEM_ENFORCED];
6308 if (cameraSoundForced) {
6309 s.setAllIndexesToMax();
6310 mRingerModeAffectedStreams &=
6311 ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
6312 } else {
Eric Laurent42b041e2013-03-29 11:36:03 -07006313 s.setAllIndexes(mStreamStates[AudioSystem.STREAM_SYSTEM]);
Eric Laurentdd45d012012-10-08 09:04:34 -07006314 mRingerModeAffectedStreams |=
6315 (1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
6316 }
6317 // take new state into account for streams muted by ringer mode
6318 setRingerModeInt(getRingerMode(), false);
6319
6320 sendMsg(mAudioHandler,
6321 MSG_SET_FORCE_USE,
6322 SENDMSG_QUEUE,
6323 AudioSystem.FOR_SYSTEM,
6324 cameraSoundForced ?
6325 AudioSystem.FORCE_SYSTEM_ENFORCED : AudioSystem.FORCE_NONE,
6326 null,
6327 0);
6328
6329 sendMsg(mAudioHandler,
6330 MSG_SET_ALL_VOLUMES,
6331 SENDMSG_QUEUE,
6332 0,
6333 0,
6334 mStreamStates[AudioSystem.STREAM_SYSTEM_ENFORCED], 0);
6335 }
6336 }
6337 }
Fabrice Di Meglio8c028842013-01-09 18:20:38 -08006338 mVolumePanel.setLayoutDirection(config.getLayoutDirection());
Jean-Michel Trivif26f0172012-04-25 16:23:20 -07006339 } catch (Exception e) {
6340 Log.e(TAG, "Error retrieving device orientation: " + e);
6341 }
6342 }
6343
6344 private void setOrientationForAudioSystem() {
6345 switch (mDeviceOrientation) {
6346 case Configuration.ORIENTATION_LANDSCAPE:
6347 //Log.i(TAG, "orientation is landscape");
6348 AudioSystem.setParameters("orientation=landscape");
6349 break;
6350 case Configuration.ORIENTATION_PORTRAIT:
6351 //Log.i(TAG, "orientation is portrait");
6352 AudioSystem.setParameters("orientation=portrait");
6353 break;
6354 case Configuration.ORIENTATION_SQUARE:
6355 //Log.i(TAG, "orientation is square");
6356 AudioSystem.setParameters("orientation=square");
6357 break;
6358 case Configuration.ORIENTATION_UNDEFINED:
6359 //Log.i(TAG, "orientation is undefined");
6360 AudioSystem.setParameters("orientation=undefined");
6361 break;
6362 default:
6363 Log.e(TAG, "Unknown orientation");
6364 }
6365 }
6366
6367
Eric Laurent78472112012-05-21 08:57:21 -07006368 // Handles request to override default use of A2DP for media.
6369 public void setBluetoothA2dpOnInt(boolean on) {
6370 synchronized (mBluetoothA2dpEnabledLock) {
6371 mBluetoothA2dpEnabled = on;
Eric Laurentc390bed2012-07-03 12:24:05 -07006372 mAudioHandler.removeMessages(MSG_SET_FORCE_BT_A2DP_USE);
6373 AudioSystem.setForceUse(AudioSystem.FOR_MEDIA,
6374 mBluetoothA2dpEnabled ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP);
Eric Laurent78472112012-05-21 08:57:21 -07006375 }
6376 }
6377
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08006378 @Override
Jeff Sharkey098d5802012-04-26 17:30:34 -07006379 public void setRingtonePlayer(IRingtonePlayer player) {
6380 mContext.enforceCallingOrSelfPermission(REMOTE_AUDIO_PLAYBACK, null);
6381 mRingtonePlayer = player;
6382 }
6383
6384 @Override
6385 public IRingtonePlayer getRingtonePlayer() {
6386 return mRingtonePlayer;
6387 }
6388
6389 @Override
Dianne Hackborn632ca412012-06-14 19:34:10 -07006390 public AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
6391 synchronized (mCurAudioRoutes) {
6392 AudioRoutesInfo routes = new AudioRoutesInfo(mCurAudioRoutes);
6393 mRoutesObservers.register(observer);
6394 return routes;
6395 }
6396 }
6397
Eric Laurentc34dcc12012-09-10 13:51:52 -07006398
6399 //==========================================================================================
6400 // Safe media volume management.
6401 // MUSIC stream volume level is limited when headphones are connected according to safety
6402 // regulation. When the user attempts to raise the volume above the limit, a warning is
6403 // displayed and the user has to acknowlegde before the volume is actually changed.
6404 // The volume index corresponding to the limit is stored in config_safe_media_volume_index
6405 // property. Platforms with a different limit must set this property accordingly in their
6406 // overlay.
6407 //==========================================================================================
6408
Eric Laurentd640bd32012-09-28 18:01:48 -07006409 // mSafeMediaVolumeState indicates whether the media volume is limited over headphones.
6410 // It is SAFE_MEDIA_VOLUME_NOT_CONFIGURED at boot time until a network service is connected
6411 // or the configure time is elapsed. It is then set to SAFE_MEDIA_VOLUME_ACTIVE or
6412 // SAFE_MEDIA_VOLUME_DISABLED according to country option. If not SAFE_MEDIA_VOLUME_DISABLED, it
6413 // can be set to SAFE_MEDIA_VOLUME_INACTIVE by calling AudioService.disableSafeMediaVolume()
6414 // (when user opts out).
6415 private final int SAFE_MEDIA_VOLUME_NOT_CONFIGURED = 0;
6416 private final int SAFE_MEDIA_VOLUME_DISABLED = 1;
6417 private final int SAFE_MEDIA_VOLUME_INACTIVE = 2;
6418 private final int SAFE_MEDIA_VOLUME_ACTIVE = 3;
6419 private Integer mSafeMediaVolumeState;
6420
6421 private int mMcc = 0;
Eric Laurentc34dcc12012-09-10 13:51:52 -07006422 // mSafeMediaVolumeIndex is the cached value of config_safe_media_volume_index property
Eric Laurentd640bd32012-09-28 18:01:48 -07006423 private int mSafeMediaVolumeIndex;
Eric Laurentc34dcc12012-09-10 13:51:52 -07006424 // mSafeMediaVolumeDevices lists the devices for which safe media volume is enforced,
6425 private final int mSafeMediaVolumeDevices = AudioSystem.DEVICE_OUT_WIRED_HEADSET |
6426 AudioSystem.DEVICE_OUT_WIRED_HEADPHONE;
6427 // mMusicActiveMs is the cumulative time of music activity since safe volume was disabled.
6428 // When this time reaches UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX, the safe media volume is re-enabled
6429 // automatically. mMusicActiveMs is rounded to a multiple of MUSIC_ACTIVE_POLL_PERIOD_MS.
6430 private int mMusicActiveMs;
6431 private static final int UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX = (20 * 3600 * 1000); // 20 hours
6432 private static final int MUSIC_ACTIVE_POLL_PERIOD_MS = 60000; // 1 minute polling interval
Eric Laurentd640bd32012-09-28 18:01:48 -07006433 private static final int SAFE_VOLUME_CONFIGURE_TIMEOUT_MS = 30000; // 30s after boot completed
Eric Laurentc34dcc12012-09-10 13:51:52 -07006434
6435 private void setSafeMediaVolumeEnabled(boolean on) {
Eric Laurentd640bd32012-09-28 18:01:48 -07006436 synchronized (mSafeMediaVolumeState) {
6437 if ((mSafeMediaVolumeState != SAFE_MEDIA_VOLUME_NOT_CONFIGURED) &&
6438 (mSafeMediaVolumeState != SAFE_MEDIA_VOLUME_DISABLED)) {
6439 if (on && (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_INACTIVE)) {
6440 mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_ACTIVE;
6441 enforceSafeMediaVolume();
6442 } else if (!on && (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE)) {
6443 mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_INACTIVE;
6444 mMusicActiveMs = 0;
6445 sendMsg(mAudioHandler,
6446 MSG_CHECK_MUSIC_ACTIVE,
6447 SENDMSG_REPLACE,
6448 0,
6449 0,
6450 null,
6451 MUSIC_ACTIVE_POLL_PERIOD_MS);
6452 }
Eric Laurentc34dcc12012-09-10 13:51:52 -07006453 }
Eric Laurentc34dcc12012-09-10 13:51:52 -07006454 }
6455 }
6456
6457 private void enforceSafeMediaVolume() {
6458 VolumeStreamState streamState = mStreamStates[AudioSystem.STREAM_MUSIC];
Eric Laurentc34dcc12012-09-10 13:51:52 -07006459 int devices = mSafeMediaVolumeDevices;
6460 int i = 0;
6461
6462 while (devices != 0) {
6463 int device = 1 << i++;
6464 if ((device & devices) == 0) {
6465 continue;
6466 }
Eric Laurent42b041e2013-03-29 11:36:03 -07006467 int index = streamState.getIndex(device);
Eric Laurentc34dcc12012-09-10 13:51:52 -07006468 if (index > mSafeMediaVolumeIndex) {
Eric Laurent42b041e2013-03-29 11:36:03 -07006469 streamState.setIndex(mSafeMediaVolumeIndex, device);
6470 sendMsg(mAudioHandler,
6471 MSG_SET_DEVICE_VOLUME,
6472 SENDMSG_QUEUE,
6473 device,
6474 0,
6475 streamState,
6476 0);
Eric Laurentc34dcc12012-09-10 13:51:52 -07006477 }
6478 devices &= ~device;
6479 }
6480 }
6481
6482 private boolean checkSafeMediaVolume(int streamType, int index, int device) {
Eric Laurentd640bd32012-09-28 18:01:48 -07006483 synchronized (mSafeMediaVolumeState) {
6484 if ((mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE) &&
Eric Laurentc34dcc12012-09-10 13:51:52 -07006485 (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) &&
6486 ((device & mSafeMediaVolumeDevices) != 0) &&
6487 (index > mSafeMediaVolumeIndex)) {
Eric Laurentc34dcc12012-09-10 13:51:52 -07006488 return false;
6489 }
6490 return true;
6491 }
6492 }
6493
6494 public void disableSafeMediaVolume() {
Eric Laurentd640bd32012-09-28 18:01:48 -07006495 synchronized (mSafeMediaVolumeState) {
Eric Laurentc34dcc12012-09-10 13:51:52 -07006496 setSafeMediaVolumeEnabled(false);
Eric Laurentfde16d52012-12-03 14:42:39 -08006497 if (mPendingVolumeCommand != null) {
6498 onSetStreamVolume(mPendingVolumeCommand.mStreamType,
6499 mPendingVolumeCommand.mIndex,
6500 mPendingVolumeCommand.mFlags,
6501 mPendingVolumeCommand.mDevice);
6502 mPendingVolumeCommand = null;
6503 }
Eric Laurentc34dcc12012-09-10 13:51:52 -07006504 }
6505 }
6506
6507
Eric Laurentdd45d012012-10-08 09:04:34 -07006508 //==========================================================================================
6509 // Camera shutter sound policy.
6510 // config_camera_sound_forced configuration option in config.xml defines if the camera shutter
6511 // sound is forced (sound even if the device is in silent mode) or not. This option is false by
6512 // default and can be overridden by country specific overlay in values-mccXXX/config.xml.
6513 //==========================================================================================
6514
6515 // cached value of com.android.internal.R.bool.config_camera_sound_forced
6516 private Boolean mCameraSoundForced;
6517
6518 // called by android.hardware.Camera to populate CameraInfo.canDisableShutterSound
6519 public boolean isCameraSoundForced() {
6520 synchronized (mCameraSoundForced) {
6521 return mCameraSoundForced;
6522 }
6523 }
6524
6525 private static final String[] RINGER_MODE_NAMES = new String[] {
6526 "SILENT",
6527 "VIBRATE",
6528 "NORMAL"
6529 };
6530
6531 private void dumpRingerMode(PrintWriter pw) {
6532 pw.println("\nRinger mode: ");
6533 pw.println("- mode: "+RINGER_MODE_NAMES[mRingerMode]);
6534 pw.print("- ringer mode affected streams = 0x");
6535 pw.println(Integer.toHexString(mRingerModeAffectedStreams));
6536 pw.print("- ringer mode muted streams = 0x");
6537 pw.println(Integer.toHexString(mRingerModeMutedStreams));
6538 }
6539
Dianne Hackborn632ca412012-06-14 19:34:10 -07006540 @Override
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08006541 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
Jeff Sharkeyeb4cc4922012-04-26 18:17:29 -07006542 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
6543
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08006544 dumpFocusStack(pw);
Jean-Michel Trivid327f212010-03-16 21:44:33 -07006545 dumpRCStack(pw);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07006546 dumpRCCStack(pw);
Jean-Michel Trivi4a570052013-03-04 12:03:17 -08006547 dumpRCDList(pw);
Eric Laurentbffc3d12012-05-07 17:43:49 -07006548 dumpStreamStates(pw);
Eric Laurentdd45d012012-10-08 09:04:34 -07006549 dumpRingerMode(pw);
Dianne Hackborn632ca412012-06-14 19:34:10 -07006550 pw.println("\nAudio routes:");
6551 pw.print(" mMainType=0x"); pw.println(Integer.toHexString(mCurAudioRoutes.mMainType));
6552 pw.print(" mBluetoothName="); pw.println(mCurAudioRoutes.mBluetoothName);
Jean-Michel Trivid5176cf2010-01-28 11:56:42 -08006553 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006554}