Merge "Add error codes for channel disconnection / connection."
diff --git a/core/java/android/bluetooth/BluetoothHealth.java b/core/java/android/bluetooth/BluetoothHealth.java
index c165d92..9b2b8ca 100644
--- a/core/java/android/bluetooth/BluetoothHealth.java
+++ b/core/java/android/bluetooth/BluetoothHealth.java
@@ -81,6 +81,20 @@
      */
     public static final int CHANNEL_TYPE_ANY = 12;
 
+    /** @hide */
+    public static final int HEALTH_OPERATION_SUCCESS = 6000;
+    /** @hide */
+    public static final int HEALTH_OPERATION_ERROR = 6001;
+    /** @hide */
+    public static final int HEALTH_OPERATION_INVALID_ARGS = 6002;
+    /** @hide */
+    public static final int HEALTH_OPERATION_GENERIC_FAILURE = 6003;
+    /** @hide */
+    public static final int HEALTH_OPERATION_NOT_FOUND = 6004;
+    /** @hide */
+    public static final int HEALTH_OPERATION_NOT_ALLOWED = 6005;
+
+
     /**
      * Register an application configuration that acts as a Health SINK.
      * This is the configuration that will be used to communicate with health devices
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index e4f2d32..56da69d 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -20,6 +20,7 @@
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothClass;
 import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothHealth;
 import android.bluetooth.BluetoothInputDevice;
 import android.bluetooth.BluetoothPan;
 import android.bluetooth.BluetoothProfile;
@@ -974,6 +975,22 @@
     }
 
     /**
+     * Called by native code for the async response to a Connect
+     * method call to org.bluez.Health
+     *
+     * @param chanCode The internal id of the channel
+     * @param result Result code of the operation.
+     */
+    private void onHealthDeviceConnectionResult(int chanCode, int result) {
+        log ("onHealthDeviceConnectionResult " + chanCode + " " + result);
+        // Success case gets handled by Property Change signal
+        if (result != BluetoothHealth.HEALTH_OPERATION_SUCCESS) {
+            mBluetoothService.onHealthDeviceChannelConnectionError(chanCode,
+                                                 BluetoothHealth.STATE_CHANNEL_DISCONNECTED);
+        }
+    }
+
+    /**
      * Called by native code on a DeviceDisconnected signal from
      * org.bluez.NetworkServer.
      *
diff --git a/core/java/android/server/BluetoothHealthProfileHandler.java b/core/java/android/server/BluetoothHealthProfileHandler.java
index a6ada2b..2d80de4 100644
--- a/core/java/android/server/BluetoothHealthProfileHandler.java
+++ b/core/java/android/server/BluetoothHealthProfileHandler.java
@@ -84,7 +84,6 @@
             result = 31 * result + (mChannelPath == null ? 0 : mChannelPath.hashCode());
             result = 31 * result + mDevice.hashCode();
             result = 31 * result + mConfig.hashCode();
-            result = 31 * result + mState;
             result = 31 * result + mChannelType;
             return result;
         }
@@ -152,7 +151,7 @@
                 String channelType = getStringChannelType(chan.mChannelType);
 
                 if (!mBluetoothService.createChannelNative(deviceObjectPath, configPath,
-                          channelType)) {
+                          channelType, chan.hashCode())) {
                     int prevState = chan.mState;
                     int state = BluetoothHealth.STATE_CHANNEL_DISCONNECTED;
                     callHealthChannelCallback(chan.mConfig, chan.mDevice, prevState, state, null,
@@ -258,7 +257,7 @@
 
     boolean disconnectChannel(BluetoothDevice device,
             BluetoothHealthAppConfiguration config, int id) {
-        HealthChannel chan = findChannelById(device, config, id);
+        HealthChannel chan = findChannelById(id);
         if (chan == null) {
           return false;
         }
@@ -273,7 +272,8 @@
         callHealthChannelCallback(config, device, prevState, chan.mState,
                 null, chan.hashCode());
 
-        if (!mBluetoothService.destroyChannelNative(deviceObjectPath, chan.mChannelPath)) {
+        if (!mBluetoothService.destroyChannelNative(deviceObjectPath, chan.mChannelPath,
+                                                    chan.hashCode())) {
             prevState = chan.mState;
             chan.mState = BluetoothHealth.STATE_CHANNEL_CONNECTED;
             callHealthChannelCallback(config, device, prevState, chan.mState,
@@ -284,8 +284,7 @@
         }
     }
 
-    private HealthChannel findChannelById(BluetoothDevice device,
-            BluetoothHealthAppConfiguration config, int id) {
+    private HealthChannel findChannelById(int id) {
         for (HealthChannel chan : mHealthChannels) {
             if (chan.hashCode() == id) return chan;
         }
@@ -384,6 +383,15 @@
         }
     }
 
+    /*package*/ void onHealthDeviceChannelConnectionError(int chanCode,
+                                                          int state) {
+        HealthChannel channel = findChannelById(chanCode);
+        if (channel == null) errorLog("No record of this channel:" + chanCode);
+
+        callHealthChannelCallback(channel.mConfig, channel.mDevice, channel.mState, state, null,
+                chanCode);
+    }
+
     private BluetoothHealthAppConfiguration findHealthApplication(
             BluetoothDevice device, String channelPath) {
         BluetoothHealthAppConfiguration config = null;
@@ -424,9 +432,19 @@
         config = findHealthApplication(device, channelPath);
 
         if (exists) {
+            channel = findConnectingChannel(device, config);
+            if (channel == null) {
+               channel = new HealthChannel(device, config, null, false,
+                       channelPath);
+               channel.mState = BluetoothHealth.STATE_CHANNEL_DISCONNECTED;
+               mHealthChannels.add(channel);
+            }
+            channel.mChannelPath = channelPath;
+
             fd = mBluetoothService.getChannelFdNative(channelPath);
             if (fd == null) {
                 errorLog("Error obtaining fd for channel:" + channelPath);
+                disconnectChannel(device, config, channel.hashCode());
                 return;
             }
             boolean mainChannel =
@@ -440,18 +458,10 @@
                 }
                 if (mainChannelPath.equals(channelPath)) mainChannel = true;
             }
-            channel = findConnectingChannel(device, config);
-            if (channel != null) {
-               channel.mChannelFd = fd;
-               channel.mMainChannel = mainChannel;
-               channel.mChannelPath = channelPath;
-               prevState = channel.mState;
-            } else {
-               channel = new HealthChannel(device, config, fd, mainChannel,
-                       channelPath);
-               mHealthChannels.add(channel);
-               prevState = BluetoothHealth.STATE_CHANNEL_DISCONNECTED;
-            }
+
+            channel.mChannelFd = fd;
+            channel.mMainChannel = mainChannel;
+            prevState = channel.mState;
             state = BluetoothHealth.STATE_CHANNEL_CONNECTED;
         } else {
             channel = findChannelByPath(device, channelPath);
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index 03180ca..00d3331 100755
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -2255,6 +2255,14 @@
         }
     }
 
+    /*package*/ void onHealthDeviceChannelConnectionError(int channelCode,
+            int newState) {
+        synchronized(mBluetoothHealthProfileHandler) {
+            mBluetoothHealthProfileHandler.onHealthDeviceChannelConnectionError(channelCode,
+                                                                                newState);
+        }
+    }
+
     public int getHealthDeviceConnectionState(BluetoothDevice device) {
         mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
                 "Need BLUETOOTH permission");
@@ -2785,8 +2793,9 @@
             String channelType);
     native String registerHealthApplicationNative(int dataType, String role, String name);
     native boolean unregisterHealthApplicationNative(String path);
-    native boolean createChannelNative(String devicePath, String appPath, String channelType);
-    native boolean destroyChannelNative(String devicePath, String channelpath);
+    native boolean createChannelNative(String devicePath, String appPath, String channelType,
+                                       int code);
+    native boolean destroyChannelNative(String devicePath, String channelpath, int code);
     native String getMainChannelNative(String path);
     native String getChannelApplicationNative(String channelPath);
     native ParcelFileDescriptor getChannelFdNative(String channelPath);
diff --git a/core/jni/android_bluetooth_common.h b/core/jni/android_bluetooth_common.h
index 2f5fd5a..1f4da3a 100644
--- a/core/jni/android_bluetooth_common.h
+++ b/core/jni/android_bluetooth_common.h
@@ -202,6 +202,13 @@
 #define INPUT_OPERATION_GENERIC_FAILURE        5003
 #define INPUT_OPERATION_SUCCESS                5004
 
+#define HEALTH_OPERATION_SUCCESS               6000
+#define HEALTH_OPERATION_ERROR                 6001
+#define HEALTH_OPERATION_INVALID_ARGS          6002
+#define HEALTH_OPERATION_GENERIC_FAILURE       6003
+#define HEALTH_OPERATION_NOT_FOUND             6004
+#define HEALTH_OPERATION_NOT_ALLOWED           6005
+
 #endif
 } /* namespace android */
 
diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp
index eee256a..0335ce7 100644
--- a/core/jni/android_server_BluetoothEventLoop.cpp
+++ b/core/jni/android_server_BluetoothEventLoop.cpp
@@ -74,6 +74,7 @@
 static jmethodID method_onPanDeviceConnectionResult;
 static jmethodID method_onHealthDevicePropertyChanged;
 static jmethodID method_onHealthDeviceChannelChanged;
+static jmethodID method_onHealthDeviceConnectionResult;
 
 typedef event_loop_native_data_t native_data_t;
 
@@ -141,6 +142,9 @@
                                                "(Ljava/lang/String;[Ljava/lang/String;)V");
     method_onPanDeviceConnectionResult = env->GetMethodID(clazz, "onPanDeviceConnectionResult",
                                                "(Ljava/lang/String;I)V");
+    method_onHealthDeviceConnectionResult = env->GetMethodID(clazz,
+                                                             "onHealthDeviceConnectionResult",
+                                                             "(II)V");
     method_onHealthDevicePropertyChanged = env->GetMethodID(clazz, "onHealthDevicePropertyChanged",
                                                "(Ljava/lang/String;[Ljava/lang/String;)V");
     method_onHealthDeviceChannelChanged = env->GetMethodID(clazz, "onHealthDeviceChannelChanged",
@@ -1533,6 +1537,39 @@
     free(user);
 }
 
+void onHealthDeviceConnectionResult(DBusMessage *msg, void *user, void *n) {
+    LOGV("%s", __FUNCTION__);
+
+    native_data_t *nat = (native_data_t *)n;
+    DBusError err;
+    dbus_error_init(&err);
+    JNIEnv *env;
+    nat->vm->GetEnv((void**)&env, nat->envVer);
+
+    jint result = HEALTH_OPERATION_SUCCESS;
+    if (dbus_set_error_from_message(&err, msg)) {
+        if (!strcmp(err.name, BLUEZ_ERROR_IFC ".InvalidArgs")) {
+            result = HEALTH_OPERATION_INVALID_ARGS;
+        } else if (!strcmp(err.name, BLUEZ_ERROR_IFC ".HealthError")) {
+            result = HEALTH_OPERATION_ERROR;
+        } else if (!strcmp(err.name, BLUEZ_ERROR_IFC ".NotFound")) {
+            result = HEALTH_OPERATION_NOT_FOUND;
+        } else if (!strcmp(err.name, BLUEZ_ERROR_IFC ".NotAllowed")) {
+            result = HEALTH_OPERATION_NOT_ALLOWED;
+        } else {
+            result = HEALTH_OPERATION_GENERIC_FAILURE;
+        }
+        LOG_AND_FREE_DBUS_ERROR(&err);
+    }
+
+    LOGV("... Health Device Code = %d, result = %d", code, result);
+    jint code = *(int *) user;
+    env->CallVoidMethod(nat->me,
+                        method_onHealthDeviceConnectionResult,
+                        code,
+                        result);
+    free(user);
+}
 #endif
 
 static JNINativeMethod sMethods[] = {
diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp
index 292047b..a49c918 100644
--- a/core/jni/android_server_BluetoothService.cpp
+++ b/core/jni/android_server_BluetoothService.cpp
@@ -78,8 +78,8 @@
 void onDiscoverServicesResult(DBusMessage *msg, void *user, void *nat);
 void onCreateDeviceResult(DBusMessage *msg, void *user, void *nat);
 void onInputDeviceConnectionResult(DBusMessage *msg, void *user, void *nat);
-void onConnectPanResult(DBusMessage *msg, void *user, void *n);
 void onPanDeviceConnectionResult(DBusMessage *msg, void *user, void *nat);
+void onHealthDeviceConnectionResult(DBusMessage *msg, void *user, void *nat);
 
 
 /** Get native data stored in the opaque (Java code maintained) pointer mNativeData
@@ -1450,79 +1450,70 @@
 }
 
 static jboolean createChannelNative(JNIEnv *env, jobject object,
-                                       jstring devicePath, jstring appPath, jstring config) {
+                                       jstring devicePath, jstring appPath, jstring config,
+                                       jint code) {
     LOGV("%s", __FUNCTION__);
-    jboolean result = JNI_FALSE;
 #ifdef HAVE_BLUETOOTH
     native_data_t *nat = get_native_data(env, object);
+    jobject eventLoop = env->GetObjectField(object, field_mEventLoop);
+    struct event_loop_native_data_t *eventLoopNat =
+            get_EventLoop_native_data(env, eventLoop);
 
-    if (nat) {
-        DBusError err;
-        dbus_error_init(&err);
-
+    if (nat && eventLoopNat) {
         const char *c_device_path = env->GetStringUTFChars(devicePath, NULL);
         const char *c_app_path = env->GetStringUTFChars(appPath, NULL);
         const char *c_config = env->GetStringUTFChars(config, NULL);
+        int *data = (int *) malloc(sizeof(int));
+        if (data == NULL) return JNI_FALSE;
 
-        DBusMessage *reply  = dbus_func_args(env, nat->conn,
-                                             c_device_path,
-                                             DBUS_HEALTH_DEVICE_IFACE,
-                                             "CreateChannel",
-                                             DBUS_TYPE_OBJECT_PATH, &c_app_path,
-                                             DBUS_TYPE_STRING, &c_config,
-                                             DBUS_TYPE_INVALID);
+        *data = code;
+        bool ret = dbus_func_args_async(env, nat->conn, -1, onHealthDeviceConnectionResult,
+                                        data, eventLoopNat, c_device_path,
+                                        DBUS_HEALTH_DEVICE_IFACE, "CreateChannel",
+                                        DBUS_TYPE_OBJECT_PATH, &c_app_path,
+                                        DBUS_TYPE_STRING, &c_config,
+                                        DBUS_TYPE_INVALID);
 
 
         env->ReleaseStringUTFChars(devicePath, c_device_path);
         env->ReleaseStringUTFChars(appPath, c_app_path);
         env->ReleaseStringUTFChars(config, c_config);
 
-        if (!reply) {
-            if (dbus_error_is_set(&err)) {
-                LOG_AND_FREE_DBUS_ERROR(&err);
-            }
-        } else {
-            result = JNI_TRUE;
-        }
+        return ret ? JNI_TRUE : JNI_FALSE;
     }
 #endif
-    return result;
+    return JNI_FALSE;
 }
 
 static jboolean destroyChannelNative(JNIEnv *env, jobject object, jstring devicePath,
-                                     jstring channelPath) {
+                                     jstring channelPath, jint code) {
     LOGE("%s", __FUNCTION__);
-    jboolean result = JNI_FALSE;
 #ifdef HAVE_BLUETOOTH
     native_data_t *nat = get_native_data(env, object);
+    jobject eventLoop = env->GetObjectField(object, field_mEventLoop);
+    struct event_loop_native_data_t *eventLoopNat =
+            get_EventLoop_native_data(env, eventLoop);
 
-    if (nat) {
-        DBusError err;
-        dbus_error_init(&err);
-
+    if (nat && eventLoopNat) {
         const char *c_device_path = env->GetStringUTFChars(devicePath, NULL);
         const char *c_channel_path = env->GetStringUTFChars(channelPath, NULL);
+        int *data = (int *) malloc(sizeof(int));
+        if (data == NULL) return JNI_FALSE;
 
-        DBusMessage *reply = dbus_func_args(env, nat->conn,
-                                            c_device_path,
-                                            DBUS_HEALTH_DEVICE_IFACE,
-                                            "DestroyChannel",
-                                            DBUS_TYPE_OBJECT_PATH, &c_channel_path,
-                                            DBUS_TYPE_INVALID);
+        *data = code;
+        bool ret = dbus_func_args_async(env, nat->conn, -1, onHealthDeviceConnectionResult,
+                                        data, eventLoopNat, c_device_path,
+                                        DBUS_HEALTH_DEVICE_IFACE, "DestroyChannel",
+                                        DBUS_TYPE_OBJECT_PATH, &c_channel_path,
+                                        DBUS_TYPE_INVALID);
 
         env->ReleaseStringUTFChars(devicePath, c_device_path);
         env->ReleaseStringUTFChars(channelPath, c_channel_path);
 
-        if (!reply) {
-            if (dbus_error_is_set(&err)) {
-                LOG_AND_FREE_DBUS_ERROR(&err);
-            }
-        } else {
-            result = JNI_TRUE;
-        }
+        return ret ? JNI_TRUE : JNI_FALSE;
     }
 #endif
-    return result;
+    return JNI_FALSE;
 }
 
 static jstring getMainChannelNative(JNIEnv *env, jobject object, jstring devicePath) {
@@ -1755,9 +1746,10 @@
 
     {"unregisterHealthApplicationNative", "(Ljava/lang/String;)Z",
               (void *)unregisterHealthApplicationNative},
-    {"createChannelNative", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z",
+    {"createChannelNative", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)Z",
               (void *)createChannelNative},
-    {"destroyChannelNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)destroyChannelNative},
+    {"destroyChannelNative", "(Ljava/lang/String;Ljava/lang/String;I)Z",
+              (void *)destroyChannelNative},
     {"getMainChannelNative", "(Ljava/lang/String;)Ljava/lang/String;", (void *)getMainChannelNative},
     {"getChannelApplicationNative", "(Ljava/lang/String;)Ljava/lang/String;",
               (void *)getChannelApplicationNative},