OSDN Git Service

AVRCP volume controls for the docks.
authorJaikumar Ganesh <jaikumar@google.com>
Tue, 16 Feb 2010 19:57:06 +0000 (11:57 -0800)
committerJaikumar Ganesh <jaikumar@google.com>
Tue, 16 Feb 2010 21:21:05 +0000 (13:21 -0800)
Send volume updates to the dock when the user presses volume buttons
on the device.

Bug: 2311007

core/java/android/server/BluetoothA2dpService.java
core/jni/android_server_BluetoothA2dpService.cpp

index 22bb43c..096ad39 100644 (file)
@@ -121,10 +121,44 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
                         handleSinkStateChange(device, state, BluetoothA2dp.STATE_DISCONNECTED);
                     }
                 }
+            } else if (action.equals(AudioManager.VOLUME_CHANGED_ACTION)) {
+                int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1);
+                if (streamType == AudioManager.STREAM_MUSIC) {
+                    BluetoothDevice sinks[] = getConnectedSinks();
+                    if (sinks.length != 0 && isPhoneDocked(sinks[0])) {
+                        String address = sinks[0].getAddress();
+                        int newVolLevel =
+                          intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
+                        int oldVolLevel =
+                          intent.getIntExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 0);
+                        String path = mBluetoothService.getObjectPathFromAddress(address);
+                        if (newVolLevel > oldVolLevel) {
+                            avrcpVolumeUpNative(path);
+                        } else if (newVolLevel < oldVolLevel) {
+                            avrcpVolumeDownNative(path);
+                        }
+                    }
+                }
             }
         }
     };
 
+
+    private boolean isPhoneDocked(BluetoothDevice device) {
+        // This works only because these broadcast intents are "sticky"
+        Intent i = mContext.registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT));
+        if (i != null) {
+            int state = i.getIntExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_UNDOCKED);
+            if (state != Intent.EXTRA_DOCK_STATE_UNDOCKED) {
+                BluetoothDevice dockDevice = i.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
+                if (dockDevice != null && device.equals(dockDevice)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     public BluetoothA2dpService(Context context, BluetoothService bluetoothService) {
         mContext = context;
 
@@ -145,6 +179,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
         mIntentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
         mIntentFilter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
         mIntentFilter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
+        mIntentFilter.addAction(AudioManager.VOLUME_CHANGED_ACTION);
         mContext.registerReceiver(mReceiver, mIntentFilter);
 
         mAudioDevices = new HashMap<BluetoothDevice, Integer>();
@@ -551,4 +586,6 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub {
     private synchronized native boolean suspendSinkNative(String path);
     private synchronized native boolean resumeSinkNative(String path);
     private synchronized native Object []getSinkPropertiesNative(String path);
+    private synchronized native boolean avrcpVolumeUpNative(String path);
+    private synchronized native boolean avrcpVolumeDownNative(String path);
 }
index 4eab4b3..cf53a06 100644 (file)
@@ -200,6 +200,38 @@ static jboolean resumeSinkNative(JNIEnv *env, jobject object,
     return JNI_FALSE;
 }
 
+static jboolean avrcpVolumeUpNative(JNIEnv *env, jobject object,
+                                     jstring path) {
+#ifdef HAVE_BLUETOOTH
+    LOGV(__FUNCTION__);
+    if (nat) {
+        const char *c_path = env->GetStringUTFChars(path, NULL);
+        bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat,
+                           c_path, "org.bluez.Control", "VolumeUp",
+                           DBUS_TYPE_INVALID);
+        env->ReleaseStringUTFChars(path, c_path);
+        return ret ? JNI_TRUE : JNI_FALSE;
+    }
+#endif
+    return JNI_FALSE;
+}
+
+static jboolean avrcpVolumeDownNative(JNIEnv *env, jobject object,
+                                     jstring path) {
+#ifdef HAVE_BLUETOOTH
+    LOGV(__FUNCTION__);
+    if (nat) {
+        const char *c_path = env->GetStringUTFChars(path, NULL);
+        bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat,
+                           c_path, "org.bluez.Control", "VolumeDown",
+                           DBUS_TYPE_INVALID);
+        env->ReleaseStringUTFChars(path, c_path);
+        return ret ? JNI_TRUE : JNI_FALSE;
+    }
+#endif
+    return JNI_FALSE;
+}
+
 #ifdef HAVE_BLUETOOTH
 DBusHandlerResult a2dp_event_filter(DBusMessage *msg, JNIEnv *env) {
     DBusError err;
@@ -267,6 +299,7 @@ void onConnectSinkResult(DBusMessage *msg, void *user, void *n) {
     free(user);
 }
 
+
 #endif
 
 
@@ -281,6 +314,8 @@ static JNINativeMethod sMethods[] = {
     {"resumeSinkNative", "(Ljava/lang/String;)Z", (void*)resumeSinkNative},
     {"getSinkPropertiesNative", "(Ljava/lang/String;)[Ljava/lang/Object;",
                                     (void *)getSinkPropertiesNative},
+    {"avrcpVolumeUpNative", "(Ljava/lang/String;)Z", (void*)avrcpVolumeUpNative},
+    {"avrcpVolumeDownNative", "(Ljava/lang/String;)Z", (void*)avrcpVolumeDownNative},
 };
 
 int register_android_server_BluetoothA2dpService(JNIEnv *env) {