Merge change 25931 into eclair

* changes:
  Add better APIs for the battery status, and other cleanup.
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index f81ba73..b52a822 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -28,6 +28,7 @@
 
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
+import java.util.UUID;
 
 /**
  * Represents a remote Bluetooth device.
@@ -226,6 +227,20 @@
     public static final String EXTRA_PASSKEY = "android.bluetooth.device.extra.PASSKEY";
 
     /**
+     * Broadcast Action: This intent is used to broadcast the {@link UUID}
+     * wrapped as a {@link ParcelUuid} of the remote device after it has been
+     * fetched. This intent is sent only when the UUIDs of the remote device
+     * are requested to be fetched using Service Discovery Protocol
+     * <p> Always contains the extra field {@link #EXTRA_DEVICE}
+     * <p> Always contains the extra filed {@link #EXTRA_UUID}
+     * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
+     * @hide
+     */
+    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+    public static final String ACTION_UUID =
+            "android.bleutooth.device.action.UUID";
+
+    /**
      * Broadcast Action: Indicates a failure to retrieve the name of a remote
      * device.
      * <p>Always contains the extra field {@link #EXTRA_DEVICE}.
@@ -292,6 +307,15 @@
      * @hide */
     public static final int PAIRING_VARIANT_DISPLAY_PASSKEY = 4;
 
+    /**
+     * Used as an extra field in {@link #ACTION_UUID} intents,
+     * Contains the {@link ParcelUuid}s of the remote device which is a parcelable
+     * version of {@link UUID}.
+     * @hide
+     */
+    public static final String EXTRA_UUID = "android.bluetooth.device.extra.UUID";
+
+
     private static IBluetooth sService;  /* Guarenteed constant after first object constructed */
 
     private final String mAddress;
@@ -507,6 +531,27 @@
         return null;
     }
 
+     /**
+      *  Perform a SDP query on the remote device to get the UUIDs
+      *  supported. This API is asynchronous and an Intent is sent,
+      *  with the UUIDs supported by the remote end. If there is an error
+      *  in getting the SDP records or if the process takes a long time,
+      *  an Intent is sent with the UUIDs that is currently present in the
+      *  cache. Clients should use the {@link getUuids} to get UUIDs
+      *  is SDP is not to be performed.
+      *
+      *  @return False if the sanity check fails, True if the process
+      *               of initiating an ACL connection to the remote device
+      *               was started.
+      *  @hide
+      */
+     public boolean fetchUuidsWithSdp() {
+        try {
+            return sService.fetchRemoteUuidsWithSdp(mAddress);
+        } catch (RemoteException e) {Log.e(TAG, "", e);}
+        return false;
+    }
+
     /** @hide */
     public int getServiceChannel(ParcelUuid uuid) {
          try {
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index 04c8ec9..203a61d 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -53,6 +53,7 @@
     String getRemoteName(in String address);
     int getRemoteClass(in String address);
     ParcelUuid[] getRemoteUuids(in String address);
+    boolean fetchRemoteUuidsWithSdp(in String address);
     int getRemoteServiceChannel(in String address,in ParcelUuid uuid);
 
     boolean setPin(in String address, in byte[] pin);
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index ba53307..ba0a0d4 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -330,6 +330,9 @@
             Log.e(TAG, "onDevicePropertyChanged: Address of the remote device in null");
             return;
         }
+        if (DBG) {
+            log("Device property changed:" + address + "property:" + name);
+        }
         BluetoothDevice device = mAdapter.getRemoteDevice(address);
         if (name.equals("Name")) {
             Intent intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED);
@@ -366,6 +369,7 @@
                 uuid = str.toString();
             }
             mBluetoothService.setRemoteDeviceProperty(address, name, uuid);
+            mBluetoothService.sendUuidIntent(address);
         } else if (name.equals("Paired")) {
             if (propValues[1].equals("true")) {
                 mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDED);
@@ -528,6 +532,25 @@
         return;
     }
 
+    private void onDiscoverServicesResult(String deviceObjectPath, boolean result) {
+        String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
+        // We don't parse the xml here, instead just query Bluez for the properties.
+        if (result) {
+            String[] properties = mBluetoothService.getRemoteDeviceProperties(address);
+            mBluetoothService.addRemoteDeviceProperties(address, properties);
+        }
+        mBluetoothService.sendUuidIntent(address);
+    }
+
+    private void onCreateDeviceResult(String address, boolean result) {
+        if (DBG) {
+            log("Result of onCreateDeviceResult:" + result);
+        }
+        if (!result) {
+            mBluetoothService.sendUuidIntent(address);
+        }
+    }
+
     private void onRestartRequired() {
         if (mBluetoothService.isEnabled()) {
             Log.e(TAG, "*** A serious error occured (did bluetoothd crash?) - " +
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index c0e4f34..ce62f07 100644
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -76,10 +76,17 @@
 
     private static final int MESSAGE_REGISTER_SDP_RECORDS = 1;
     private static final int MESSAGE_FINISH_DISABLE = 2;
+    private static final int MESSAGE_UUID_INTENT = 3;
+
+    // The timeout used to sent the UUIDs Intent
+    // This timeout should be greater than the page timeout
+    private static final int UUID_INTENT_DELAY = 6000;
 
     private final Map<String, String> mAdapterProperties;
     private final HashMap <String, Map<String, String>> mDeviceProperties;
 
+    private final ArrayList <String> mUuidIntentTracker;
+
     static {
         classInitNative();
     }
@@ -104,6 +111,7 @@
         mIsDiscovering = false;
         mAdapterProperties = new HashMap<String, String>();
         mDeviceProperties = new HashMap<String, Map<String,String>>();
+        mUuidIntentTracker = new ArrayList<String>();
         registerForAirplaneMode();
     }
 
@@ -291,6 +299,11 @@
             case MESSAGE_FINISH_DISABLE:
                 finishDisable(msg.arg1 != 0);
                 break;
+            case MESSAGE_UUID_INTENT:
+                String address = (String)msg.obj;
+                if (address != null)
+                    sendUuidIntent(address);
+                break;
             }
         }
     };
@@ -976,6 +989,10 @@
         if (!BluetoothAdapter.checkBluetoothAddress(address)) {
             return null;
         }
+        return getUuidFromCache(address);
+    }
+
+    private ParcelUuid[] getUuidFromCache(String address) {
         String value = getRemoteDeviceProperty(address, "UUIDs");
         if (value == null) return null;
 
@@ -990,6 +1007,36 @@
         return uuids;
     }
 
+    public synchronized boolean fetchRemoteUuidsWithSdp(String address) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        if (!BluetoothAdapter.checkBluetoothAddress(address)) {
+            return false;
+        }
+
+        if (mUuidIntentTracker.contains(address)) {
+            // An SDP query for this address is already in progress
+            return true;
+        }
+
+        boolean ret;
+        if (getBondState(address) == BluetoothDevice.BOND_BONDED) {
+            String path = getObjectPathFromAddress(address);
+            if (path == null) return false;
+
+            // Use an empty string for the UUID pattern
+            ret = discoverServicesNative(path, "");
+        } else {
+            ret = createDeviceNative(address);
+        }
+
+        mUuidIntentTracker.add(address);
+
+        Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
+        message.obj = address;
+        mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY);
+        return ret;
+    }
+
     /**
      * Gets the rfcomm channel associated with the UUID.
      *
@@ -1121,6 +1168,18 @@
                 Settings.System.AIRPLANE_MODE_ON, 0) == 1;
     }
 
+    /* Broadcast the Uuid intent */
+    /*package*/ synchronized void sendUuidIntent(String address) {
+        if (mUuidIntentTracker.contains(address)) {
+            ParcelUuid[] uuid = getUuidFromCache(address);
+            Intent intent = new Intent(BluetoothDevice.ACTION_UUID);
+            intent.putExtra(BluetoothDevice.EXTRA_UUID, uuid);
+            mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
+
+            mUuidIntentTracker.remove(address);
+        }
+    }
+
     @Override
     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
         pw.println("\nmIsAirplaneSensitive = " + mIsAirplaneSensitive + "\n");
@@ -1284,4 +1343,6 @@
     private native boolean setPairingConfirmationNative(String address, boolean confirm,
             int nativeData);
     private native boolean setDevicePropertyBooleanNative(String objectPath, String key, int value);
+    private native boolean createDeviceNative(String address);
+    private native boolean discoverServicesNative(String objectPath, String pattern);
 }
diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp
index e703ed8..e37e832 100644
--- a/core/jni/android_server_BluetoothEventLoop.cpp
+++ b/core/jni/android_server_BluetoothEventLoop.cpp
@@ -48,6 +48,8 @@
 static jmethodID method_onDeviceDisconnectRequested;
 
 static jmethodID method_onCreatePairedDeviceResult;
+static jmethodID method_onCreateDeviceResult;
+static jmethodID method_onDiscoverServicesResult;
 static jmethodID method_onGetDeviceServiceChannelResult;
 
 static jmethodID method_onRequestPinCode;
@@ -92,6 +94,10 @@
 
     method_onCreatePairedDeviceResult = env->GetMethodID(clazz, "onCreatePairedDeviceResult",
                                                          "(Ljava/lang/String;I)V");
+    method_onCreateDeviceResult = env->GetMethodID(clazz, "onCreateDeviceResult",
+                                                         "(Ljava/lang/String;Z)V");
+    method_onDiscoverServicesResult = env->GetMethodID(clazz, "onDiscoverServicesResult",
+                                                         "(Ljava/lang/String;Z)V");
 
     method_onAgentAuthorize = env->GetMethodID(clazz, "onAgentAuthorize",
                                                "(Ljava/lang/String;Ljava/lang/String;)Z");
@@ -1097,6 +1103,54 @@
     free(user);
 }
 
+void onCreateDeviceResult(DBusMessage *msg, void *user, void *n) {
+    LOGV(__FUNCTION__);
+
+    native_data_t *nat = (native_data_t *)n;
+    const char *address= (const char *)user;
+    DBusError err;
+    dbus_error_init(&err);
+    JNIEnv *env;
+    nat->vm->GetEnv((void**)&env, nat->envVer);
+
+    LOGV("... Address = %s", address);
+
+    bool result = JNI_TRUE;
+    if (dbus_set_error_from_message(&err, msg)) {
+        LOG_AND_FREE_DBUS_ERROR(&err);
+        result = JNI_FALSE;
+    }
+    env->CallVoidMethod(nat->me,
+                        method_onCreateDeviceResult,
+                        env->NewStringUTF(address),
+                        result);
+    free(user);
+}
+
+void onDiscoverServicesResult(DBusMessage *msg, void *user, void *n) {
+    LOGV(__FUNCTION__);
+
+    native_data_t *nat = (native_data_t *)n;
+    const char *path = (const char *)user;
+    DBusError err;
+    dbus_error_init(&err);
+    JNIEnv *env;
+    nat->vm->GetEnv((void**)&env, nat->envVer);
+
+    LOGV("... Device Path = %s", path);
+
+    bool result = JNI_TRUE;
+    if (dbus_set_error_from_message(&err, msg)) {
+        LOG_AND_FREE_DBUS_ERROR(&err);
+        result = JNI_FALSE;
+    }
+    env->CallVoidMethod(nat->me,
+                        method_onDiscoverServicesResult,
+                        env->NewStringUTF(path),
+                        result);
+    free(user);
+}
+
 void onGetDeviceServiceChannelResult(DBusMessage *msg, void *user, void *n) {
     LOGV(__FUNCTION__);
 
diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp
index 0b71acb..c432ed9a 100644
--- a/core/jni/android_server_BluetoothService.cpp
+++ b/core/jni/android_server_BluetoothService.cpp
@@ -66,6 +66,8 @@
                                             DBusMessage *msg,
                                             void *data);
 void onCreatePairedDeviceResult(DBusMessage *msg, void *user, void *nat);
+void onDiscoverServicesResult(DBusMessage *msg, void *user, void *nat);
+void onCreateDeviceResult(DBusMessage *msg, void *user, void *nat);
 
 
 /** Get native data stored in the opaque (Java code maintained) pointer mNativeData
@@ -757,6 +759,75 @@
 #endif
 }
 
+
+static jboolean createDeviceNative(JNIEnv *env, jobject object,
+                                                jstring address) {
+    LOGV(__FUNCTION__);
+#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 && eventLoopNat) {
+        const char *c_address = env->GetStringUTFChars(address, NULL);
+        LOGV("... address = %s", c_address);
+        char *context_address = (char *)calloc(BTADDR_SIZE, sizeof(char));
+        strlcpy(context_address, c_address, BTADDR_SIZE);  // for callback
+
+        bool ret = dbus_func_args_async(env, nat->conn, -1,
+                                        onCreateDeviceResult,
+                                        context_address,
+                                        eventLoopNat,
+                                        get_adapter_path(env, object),
+                                        DBUS_ADAPTER_IFACE,
+                                        "CreateDevice",
+                                        DBUS_TYPE_STRING, &c_address,
+                                        DBUS_TYPE_INVALID);
+        env->ReleaseStringUTFChars(address, c_address);
+        return ret ? JNI_TRUE : JNI_FALSE;
+    }
+#endif
+    return JNI_FALSE;
+}
+
+static jboolean discoverServicesNative(JNIEnv *env, jobject object,
+                                               jstring path, jstring pattern) {
+    LOGV(__FUNCTION__);
+#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 && eventLoopNat) {
+        const char *c_path = env->GetStringUTFChars(path, NULL);
+        const char *c_pattern = env->GetStringUTFChars(pattern, NULL);
+        int len = env->GetStringLength(path) + 1;
+        char *context_path = (char *)calloc(len, sizeof(char));
+        strlcpy(context_path, c_path, len);  // for callback
+
+        LOGV("... Object Path = %s", c_path);
+        LOGV("... Pattern = %s, strlen = %d", c_pattern, strlen(c_pattern));
+
+        bool ret = dbus_func_args_async(env, nat->conn, -1,
+                                        onDiscoverServicesResult,
+                                        context_path,
+                                        eventLoopNat,
+                                        c_path,
+                                        DBUS_DEVICE_IFACE,
+                                        "DiscoverServices",
+                                        DBUS_TYPE_STRING, &c_pattern,
+                                        DBUS_TYPE_INVALID);
+        env->ReleaseStringUTFChars(path, c_path);
+        env->ReleaseStringUTFChars(pattern, c_pattern);
+        return ret ? JNI_TRUE : JNI_FALSE;
+    }
+#endif
+    return JNI_FALSE;
+}
+
+
 static JNINativeMethod sMethods[] = {
      /* name, signature, funcPtr */
     {"classInitNative", "()V", (void*)classInitNative},
@@ -797,6 +868,8 @@
             (void *)cancelPairingUserInputNative},
     {"setDevicePropertyBooleanNative", "(Ljava/lang/String;Ljava/lang/String;I)Z",
             (void *)setDevicePropertyBooleanNative},
+    {"createDeviceNative", "(Ljava/lang/String;)Z", (void *)createDeviceNative},
+    {"discoverServicesNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)discoverServicesNative},
 };
 
 int register_android_server_BluetoothService(JNIEnv *env) {