summaryrefslogtreecommitdiffstats
path: root/media/java/android
diff options
context:
space:
mode:
authorDianne Hackborn <hackbod@google.com>2012-06-15 14:10:42 -0700
committerAndroid (Google) Code Review <android-gerrit@google.com>2012-06-15 14:10:42 -0700
commit87959cdd8560e743956a71bb687799e94053e086 (patch)
treee32c058fe1721e29341068b27ad2686e38b60361 /media/java/android
parentc4f4b0f0a4905180314d1a681ddf5767ed1070ed (diff)
parent632ca417f0a33e3fa9ccece531afa2db3f0d4a30 (diff)
downloadframeworks_base-87959cdd8560e743956a71bb687799e94053e086.zip
frameworks_base-87959cdd8560e743956a71bb687799e94053e086.tar.gz
frameworks_base-87959cdd8560e743956a71bb687799e94053e086.tar.bz2
Merge "Add new callback for MediaRouter to found out about device changes." into jb-dev
Diffstat (limited to 'media/java/android')
-rw-r--r--media/java/android/media/AudioRoutesInfo.aidl18
-rw-r--r--media/java/android/media/AudioRoutesInfo.java71
-rw-r--r--media/java/android/media/AudioService.java82
-rw-r--r--media/java/android/media/IAudioRoutesObserver.aidl27
-rw-r--r--media/java/android/media/IAudioService.aidl4
-rw-r--r--media/java/android/media/MediaRouter.java148
6 files changed, 267 insertions, 83 deletions
diff --git a/media/java/android/media/AudioRoutesInfo.aidl b/media/java/android/media/AudioRoutesInfo.aidl
new file mode 100644
index 0000000..d665851
--- /dev/null
+++ b/media/java/android/media/AudioRoutesInfo.aidl
@@ -0,0 +1,18 @@
+/* Copyright 2012, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.media;
+
+parcelable AudioRoutesInfo;
diff --git a/media/java/android/media/AudioRoutesInfo.java b/media/java/android/media/AudioRoutesInfo.java
new file mode 100644
index 0000000..df9fc06
--- /dev/null
+++ b/media/java/android/media/AudioRoutesInfo.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+/**
+ * Information available from AudioService about the current routes.
+ * @hide
+ */
+public class AudioRoutesInfo implements Parcelable {
+ static final int MAIN_SPEAKER = 0;
+ static final int MAIN_HEADSET = 1<<0;
+ static final int MAIN_HEADPHONES = 1<<1;
+ static final int MAIN_DOCK_SPEAKERS = 1<<2;
+ static final int MAIN_HDMI = 1<<3;
+
+ CharSequence mBluetoothName;
+ int mMainType = MAIN_SPEAKER;
+
+ public AudioRoutesInfo() {
+ }
+
+ public AudioRoutesInfo(AudioRoutesInfo o) {
+ mBluetoothName = o.mBluetoothName;
+ mMainType = o.mMainType;
+ }
+
+ AudioRoutesInfo(Parcel src) {
+ mBluetoothName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(src);
+ mMainType = src.readInt();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ TextUtils.writeToParcel(mBluetoothName, dest, flags);
+ dest.writeInt(mMainType);
+ }
+
+ public static final Parcelable.Creator<AudioRoutesInfo> CREATOR
+ = new Parcelable.Creator<AudioRoutesInfo>() {
+ public AudioRoutesInfo createFromParcel(Parcel in) {
+ return new AudioRoutesInfo(in);
+ }
+
+ public AudioRoutesInfo[] newArray(int size) {
+ return new AudioRoutesInfo[size];
+ }
+ };
+}
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index a69912d..ddb7e6b 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -53,6 +53,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
+import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
@@ -62,6 +63,7 @@ import android.provider.Settings.System;
import android.speech.RecognizerIntent;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import android.util.Log;
import android.view.KeyEvent;
import android.view.VolumePanel;
@@ -135,10 +137,11 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
private static final int MSG_RCDISPLAY_UPDATE = 13;
private static final int MSG_SET_ALL_VOLUMES = 14;
private static final int MSG_PERSIST_MASTER_VOLUME_MUTE = 15;
+ private static final int MSG_REPORT_NEW_ROUTES = 16;
// messages handled under wakelock, can only be queued, i.e. sent with queueMsgUnderWakeLock(),
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
- private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 16;
- private static final int MSG_SET_A2DP_CONNECTION_STATE = 17;
+ private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 17;
+ private static final int MSG_SET_A2DP_CONNECTION_STATE = 18;
// flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be
@@ -397,6 +400,11 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
private boolean mBluetoothA2dpEnabled;
private final Object mBluetoothA2dpEnabledLock = new Object();
+ // Monitoring of audio routes. Protected by mCurAudioRoutes.
+ final AudioRoutesInfo mCurAudioRoutes = new AudioRoutesInfo();
+ final RemoteCallbackList<IAudioRoutesObserver> mRoutesObservers
+ = new RemoteCallbackList<IAudioRoutesObserver>();
+
///////////////////////////////////////////////////////////////////////////
// Construction
///////////////////////////////////////////////////////////////////////////
@@ -3011,6 +3019,26 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
onSetA2dpConnectionState((BluetoothDevice)msg.obj, msg.arg1);
mMediaEventWakeLock.release();
break;
+
+ case MSG_REPORT_NEW_ROUTES: {
+ int N = mRoutesObservers.beginBroadcast();
+ if (N > 0) {
+ AudioRoutesInfo routes;
+ synchronized (mCurAudioRoutes) {
+ routes = new AudioRoutesInfo(mCurAudioRoutes);
+ }
+ while (N > 0) {
+ N--;
+ IAudioRoutesObserver obs = mRoutesObservers.getBroadcastItem(N);
+ try {
+ obs.dispatchAudioRoutesChanged(routes);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ mRoutesObservers.finishBroadcast();
+ break;
+ }
}
}
}
@@ -3127,6 +3155,13 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
} else {
makeA2dpDeviceUnavailableNow(address);
}
+ synchronized (mCurAudioRoutes) {
+ if (mCurAudioRoutes.mBluetoothName != null) {
+ mCurAudioRoutes.mBluetoothName = null;
+ sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
+ SENDMSG_NOOP, 0, 0, null, 0);
+ }
+ }
} else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
if (btDevice.isBluetoothDock()) {
// this could be a reconnection after a transient disconnection
@@ -3141,6 +3176,14 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
}
makeA2dpDeviceAvailable(address);
+ synchronized (mCurAudioRoutes) {
+ String name = btDevice.getAliasName();
+ if (!TextUtils.equals(mCurAudioRoutes.mBluetoothName, name)) {
+ mCurAudioRoutes.mBluetoothName = name;
+ sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
+ SENDMSG_NOOP, 0, 0, null, 0);
+ }
+ }
}
}
}
@@ -3204,20 +3247,43 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
intent.putExtra("name", name);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ int connType = 0;
+
if (device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) {
+ connType = AudioRoutesInfo.MAIN_HEADSET;
intent.setAction(Intent.ACTION_HEADSET_PLUG);
intent.putExtra("microphone", 1);
} else if (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) {
+ connType = AudioRoutesInfo.MAIN_HEADPHONES;
intent.setAction(Intent.ACTION_HEADSET_PLUG);
intent.putExtra("microphone", 0);
} else if (device == AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET) {
+ connType = AudioRoutesInfo.MAIN_DOCK_SPEAKERS;
intent.setAction(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG);
} else if (device == AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET) {
+ connType = AudioRoutesInfo.MAIN_DOCK_SPEAKERS;
intent.setAction(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG);
} else if (device == AudioSystem.DEVICE_OUT_AUX_DIGITAL) {
+ connType = AudioRoutesInfo.MAIN_HDMI;
intent.setAction(Intent.ACTION_HDMI_AUDIO_PLUG);
}
+ synchronized (mCurAudioRoutes) {
+ if (connType != 0) {
+ int newConn = mCurAudioRoutes.mMainType;
+ if (state != 0) {
+ newConn |= connType;
+ } else {
+ newConn &= ~connType;
+ }
+ if (newConn != mCurAudioRoutes.mMainType) {
+ mCurAudioRoutes.mMainType = newConn;
+ sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
+ SENDMSG_NOOP, 0, 0, null, 0);
+ }
+ }
+ }
+
ActivityManagerNative.broadcastStickyIntent(intent, null);
}
@@ -4796,6 +4862,15 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
@Override
+ public AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
+ synchronized (mCurAudioRoutes) {
+ AudioRoutesInfo routes = new AudioRoutesInfo(mCurAudioRoutes);
+ mRoutesObservers.register(observer);
+ return routes;
+ }
+ }
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
@@ -4803,5 +4878,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
dumpFocusStack(pw);
dumpRCStack(pw);
dumpStreamStates(pw);
+ pw.println("\nAudio routes:");
+ pw.print(" mMainType=0x"); pw.println(Integer.toHexString(mCurAudioRoutes.mMainType));
+ pw.print(" mBluetoothName="); pw.println(mCurAudioRoutes.mBluetoothName);
}
}
diff --git a/media/java/android/media/IAudioRoutesObserver.aidl b/media/java/android/media/IAudioRoutesObserver.aidl
new file mode 100644
index 0000000..c269b83
--- /dev/null
+++ b/media/java/android/media/IAudioRoutesObserver.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.media.AudioRoutesInfo;
+
+/**
+ * AIDL for the AudioService to report changes in available audio routes.
+ * @hide
+ */
+oneway interface IAudioRoutesObserver {
+ void dispatchAudioRoutesChanged(in AudioRoutesInfo newRoutes);
+}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 70fc623..fc5b8f1 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -19,7 +19,9 @@ package android.media;
import android.app.PendingIntent;
import android.bluetooth.BluetoothDevice;
import android.content.ComponentName;
+import android.media.AudioRoutesInfo;
import android.media.IAudioFocusDispatcher;
+import android.media.IAudioRoutesObserver;
import android.media.IRemoteControlClient;
import android.media.IRemoteControlDisplay;
import android.media.IRingtonePlayer;
@@ -137,4 +139,6 @@ interface IAudioService {
void setWiredDeviceConnectionState(int device, int state, String name);
int setBluetoothA2dpDeviceConnectionState(in BluetoothDevice device, int state);
+
+ AudioRoutesInfo startWatchingRoutes(in IAudioRoutesObserver observer);
}
diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java
index 9e70b7f..1086503 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -16,14 +16,14 @@
package android.media;
-import android.bluetooth.BluetoothA2dp;
-import android.content.BroadcastReceiver;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.text.TextUtils;
import android.util.Log;
import java.util.ArrayList;
@@ -46,7 +46,7 @@ public class MediaRouter {
static class Static {
final Resources mResources;
- final AudioManager mAudioManager;
+ final IAudioService mAudioService;
final Handler mHandler;
final ArrayList<CallbackInfo> mCallbacks = new ArrayList<CallbackInfo>();
@@ -54,38 +54,89 @@ public class MediaRouter {
final ArrayList<RouteCategory> mCategories = new ArrayList<RouteCategory>();
final RouteCategory mSystemCategory;
- final HeadphoneChangedBroadcastReceiver mHeadphoneChangedReceiver;
+
+ final AudioRoutesInfo mCurRoutesInfo = new AudioRoutesInfo();
RouteInfo mDefaultAudio;
RouteInfo mBluetoothA2dpRoute;
RouteInfo mSelectedRoute;
+ final IAudioRoutesObserver.Stub mRoutesObserver = new IAudioRoutesObserver.Stub() {
+ public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) {
+ mHandler.post(new Runnable() {
+ @Override public void run() {
+ updateRoutes(newRoutes);
+ }
+ });
+ }
+ };
+
Static(Context appContext) {
mResources = Resources.getSystem();
mHandler = new Handler(appContext.getMainLooper());
- mAudioManager = (AudioManager)appContext.getSystemService(Context.AUDIO_SERVICE);
+ IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
+ mAudioService = IAudioService.Stub.asInterface(b);
// XXX this doesn't deal with locale changes!
mSystemCategory = new RouteCategory(mResources.getText(
com.android.internal.R.string.default_audio_route_category_name),
ROUTE_TYPE_LIVE_AUDIO, false);
-
- final IntentFilter speakerFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
- speakerFilter.addAction(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG);
- speakerFilter.addAction(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG);
- speakerFilter.addAction(Intent.ACTION_HDMI_AUDIO_PLUG);
- mHeadphoneChangedReceiver = new HeadphoneChangedBroadcastReceiver();
- appContext.registerReceiver(mHeadphoneChangedReceiver, speakerFilter);
}
// Called after sStatic is initialized
- void initDefaultRoutes() {
+ void startMonitoringRoutes() {
mDefaultAudio = new RouteInfo(mSystemCategory);
mDefaultAudio.mNameResId = com.android.internal.R.string.default_audio_route_name;
mDefaultAudio.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO;
addRoute(mDefaultAudio);
+
+ AudioRoutesInfo newRoutes = null;
+ try {
+ newRoutes = mAudioService.startWatchingRoutes(mRoutesObserver);
+ } catch (RemoteException e) {
+ }
+ if (newRoutes != null) {
+ updateRoutes(newRoutes);
+ }
+ }
+
+ void updateRoutes(AudioRoutesInfo newRoutes) {
+ if (newRoutes.mMainType != mCurRoutesInfo.mMainType) {
+ mCurRoutesInfo.mMainType = newRoutes.mMainType;
+ int name;
+ if ((newRoutes.mMainType&AudioRoutesInfo.MAIN_HEADPHONES) != 0
+ || (newRoutes.mMainType&AudioRoutesInfo.MAIN_HEADSET) != 0) {
+ name = com.android.internal.R.string.default_audio_route_name_headphones;
+ } else if ((newRoutes.mMainType&AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) {
+ name = com.android.internal.R.string.default_audio_route_name_dock_speakers;
+ } else if ((newRoutes.mMainType&AudioRoutesInfo.MAIN_HDMI) != 0) {
+ name = com.android.internal.R.string.default_audio_route_name_hdmi;
+ } else {
+ name = com.android.internal.R.string.default_audio_route_name;
+ }
+ sStatic.mDefaultAudio.mNameResId = name;
+ dispatchRouteChanged(sStatic.mDefaultAudio);
+ }
+ if (!TextUtils.equals(newRoutes.mBluetoothName, mCurRoutesInfo.mBluetoothName)) {
+ mCurRoutesInfo.mBluetoothName = newRoutes.mBluetoothName;
+ if (mCurRoutesInfo.mBluetoothName != null) {
+ if (sStatic.mBluetoothA2dpRoute == null) {
+ final RouteInfo info = new RouteInfo(sStatic.mSystemCategory);
+ info.mName = mCurRoutesInfo.mBluetoothName;
+ info.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO;
+ sStatic.mBluetoothA2dpRoute = info;
+ addRoute(sStatic.mBluetoothA2dpRoute);
+ } else {
+ sStatic.mBluetoothA2dpRoute.mName = mCurRoutesInfo.mBluetoothName;
+ dispatchRouteChanged(sStatic.mBluetoothA2dpRoute);
+ }
+ } else if (sStatic.mBluetoothA2dpRoute != null) {
+ removeRoute(sStatic.mBluetoothA2dpRoute);
+ sStatic.mBluetoothA2dpRoute = null;
+ }
+ }
}
}
@@ -132,7 +183,7 @@ public class MediaRouter {
synchronized (Static.class) {
if (sStatic == null) {
sStatic = new Static(context.getApplicationContext());
- sStatic.initDefaultRoutes();
+ sStatic.startMonitoringRoutes();
}
}
}
@@ -154,19 +205,6 @@ public class MediaRouter {
return sStatic.mSelectedRoute;
}
- static void onHeadphonesPlugged(boolean headphonesPresent, String headphonesName) {
- if (headphonesPresent) {
- sStatic.mDefaultAudio.mName = headphonesName;
- sStatic.mDefaultAudio.mNameResId = 0;
- } else {
- sStatic.mDefaultAudio.mName = null;
- sStatic.mDefaultAudio.mNameResId =
- com.android.internal.R.string.default_audio_route_name;
- }
-
- dispatchRouteChanged(sStatic.mDefaultAudio);
- }
-
/**
* Add a callback to listen to events about specific kinds of media routes.
* If the specified callback is already registered, its registration will be updated for any
@@ -528,18 +566,6 @@ public class MediaRouter {
}
}
- static void onA2dpDeviceConnected() {
- final RouteInfo info = new RouteInfo(sStatic.mSystemCategory);
- info.mNameResId = com.android.internal.R.string.bluetooth_a2dp_audio_route_name;
- sStatic.mBluetoothA2dpRoute = info;
- addRoute(sStatic.mBluetoothA2dpRoute);
- }
-
- static void onA2dpDeviceDisconnected() {
- removeRoute(sStatic.mBluetoothA2dpRoute);
- sStatic.mBluetoothA2dpRoute = null;
- }
-
/**
* Information about a media route.
*/
@@ -1158,44 +1184,4 @@ public class MediaRouter {
}
}
-
- class BtChangedBroadcastReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
- final int state = intent.getIntExtra(BluetoothA2dp.EXTRA_STATE, -1);
- if (state == BluetoothA2dp.STATE_CONNECTED) {
- onA2dpDeviceConnected();
- } else if (state == BluetoothA2dp.STATE_DISCONNECTING ||
- state == BluetoothA2dp.STATE_DISCONNECTED) {
- onA2dpDeviceDisconnected();
- }
- }
- }
- }
-
- static class HeadphoneChangedBroadcastReceiver extends BroadcastReceiver {
- @Override
- public void onReceive(Context context, Intent intent) {
- final String action = intent.getAction();
- if (Intent.ACTION_HEADSET_PLUG.equals(action)) {
- final boolean plugged = intent.getIntExtra("state", 0) != 0;
- final String name = sStatic.mResources.getString(
- com.android.internal.R.string.default_audio_route_name_headphones);
- onHeadphonesPlugged(plugged, name);
- } else if (Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG.equals(action) ||
- Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG.equals(action)) {
- final boolean plugged = intent.getIntExtra("state", 0) != 0;
- final String name = sStatic.mResources.getString(
- com.android.internal.R.string.default_audio_route_name_dock_speakers);
- onHeadphonesPlugged(plugged, name);
- } else if (Intent.ACTION_HDMI_AUDIO_PLUG.equals(action)) {
- final boolean plugged = intent.getIntExtra("state", 0) != 0;
- final String name = sStatic.mResources.getString(
- com.android.internal.R.string.default_audio_route_name_hdmi);
- onHeadphonesPlugged(plugged, name);
- }
- }
- }
}