diff options
author | Dianne Hackborn <hackbod@google.com> | 2012-06-15 14:10:42 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2012-06-15 14:10:42 -0700 |
commit | 87959cdd8560e743956a71bb687799e94053e086 (patch) | |
tree | e32c058fe1721e29341068b27ad2686e38b60361 /media/java/android | |
parent | c4f4b0f0a4905180314d1a681ddf5767ed1070ed (diff) | |
parent | 632ca417f0a33e3fa9ccece531afa2db3f0d4a30 (diff) | |
download | frameworks_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.aidl | 18 | ||||
-rw-r--r-- | media/java/android/media/AudioRoutesInfo.java | 71 | ||||
-rw-r--r-- | media/java/android/media/AudioService.java | 82 | ||||
-rw-r--r-- | media/java/android/media/IAudioRoutesObserver.aidl | 27 | ||||
-rw-r--r-- | media/java/android/media/IAudioService.aidl | 4 | ||||
-rw-r--r-- | media/java/android/media/MediaRouter.java | 148 |
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); - } - } - } } |