Provide an API to set the friendly name of a remote device.

BluetoothDevice setName overwrite the locally cached remote name.
The changed name is saved in the local storage so that the change
is preserved over power cycle.
bug 5081605
Change-Id: I486966033828d153bfb1076a99e274c8a7f41636
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index d9525a3..4cb8220 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -566,6 +566,54 @@
     }
 
     /**
+     * Get the Bluetooth alias of the remote device.
+     * <p>Alias is the locally modified name of a remote device.
+     *
+     * @return the Bluetooth alias, or null if no alias or there was a problem
+     * @hide
+     */
+    public String getAlias() {
+        try {
+            return sService.getRemoteAlias(mAddress);
+        } catch (RemoteException e) {Log.e(TAG, "", e);}
+        return null;
+    }
+
+    /**
+     * Set the Bluetooth alias of the remote device.
+     * <p>Alias is the locally modified name of a remote device.
+     * <p>This methoid overwrites the alias. The changed
+     * alias is saved in the local storage so that the change
+     * is preserved over power cycle.
+     *
+     * @return true on success, false on error
+     * @hide
+     */
+    public boolean setAlias(String alias) {
+        try {
+            return sService.setRemoteAlias(mAddress, alias);
+        } catch (RemoteException e) {Log.e(TAG, "", e);}
+        return false;
+    }
+
+    /**
+     * Get the Bluetooth alias of the remote device.
+     * If Alias is null, get the Bluetooth name instead.
+     * @see #getAlias()
+     * @see #getName()
+     *
+     * @return the Bluetooth alias, or null if no alias or there was a problem
+     * @hide
+     */
+    public String getAliasName() {
+        String name = getAlias();
+        if (name == null) {
+            name = getName();
+        }
+        return name;
+    }
+
+    /**
      * Start the bonding (pairing) process with the remote device.
      * <p>This is an asynchronous call, it will return immediately. Register
      * for {@link #ACTION_BOND_STATE_CHANGED} intents to be notified when
diff --git a/core/java/android/bluetooth/IBluetooth.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index 183772d..da66b1a 100644
--- a/core/java/android/bluetooth/IBluetooth.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -62,6 +62,8 @@
     boolean setDeviceOutOfBandData(in String address, in byte[] hash, in byte[] randomizer);
 
     String getRemoteName(in String address);
+    String getRemoteAlias(in String address);
+    boolean setRemoteAlias(in String address, in String name);
     int getRemoteClass(in String address);
     ParcelUuid[] getRemoteUuids(in String address);
     boolean fetchRemoteUuids(in String address, in ParcelUuid uuid, in IBluetoothCallback callback);
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index 107a2a9..5273910 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -418,6 +418,8 @@
             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
             intent.putExtra(BluetoothDevice.EXTRA_NAME, propValues[1]);
             mContext.sendBroadcast(intent, BLUETOOTH_PERM);
+        } else if (name.equals("Alias")) {
+            mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
         } else if (name.equals("Class")) {
             mBluetoothService.setRemoteDeviceProperty(address, name, propValues[1]);
             Intent intent = new Intent(BluetoothDevice.ACTION_CLASS_CHANGED);
diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java
index 34f1971..3a563e6 100755
--- a/core/java/android/server/BluetoothService.java
+++ b/core/java/android/server/BluetoothService.java
@@ -843,7 +843,6 @@
         return uuids;
     }
 
-
     /**
      * Returns the user-friendly name of a remote device.  This value is
      * returned from our local cache, which is updated when onPropertyChange
@@ -864,6 +863,40 @@
     }
 
     /**
+     * Returns alias of a remote device.  This value is returned from our
+     * local cache, which is updated when onPropertyChange event is received.
+     *
+     * @param address Bluetooth address of remote device.
+     *
+     * @return The alias of the specified remote device.
+     */
+    public synchronized String getRemoteAlias(String address) {
+
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        if (!BluetoothAdapter.checkBluetoothAddress(address)) {
+            return null;
+        }
+        return mDeviceProperties.getProperty(address, "Alias");
+    }
+
+    /**
+     * Set the alias of a remote device.
+     *
+     * @param address Bluetooth address of remote device.
+     * @param alias new alias to change to
+     * @return true on success, false on error
+     */
+    public synchronized boolean setRemoteAlias(String address, String alias) {
+        mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
+        if (!BluetoothAdapter.checkBluetoothAddress(address)) {
+            return false;
+        }
+
+        return setDevicePropertyStringNative(getObjectPathFromAddress(address),
+                                             "Alias", alias);
+    }
+
+    /**
      * Get the discoverability window for the device.  A timeout of zero
      * means that the device is permanently discoverable (if the device is
      * in the discoverable mode).
@@ -2553,6 +2586,8 @@
 
     private native boolean setDevicePropertyBooleanNative(String objectPath, String key,
             int value);
+    private native boolean setDevicePropertyStringNative(String objectPath, String key,
+            String value);
     private native boolean createDeviceNative(String address);
     /*package*/ native boolean discoverServicesNative(String objectPath, String pattern);
 
diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp
index 86e7cc0..41056fd 100644
--- a/core/jni/android_server_BluetoothService.cpp
+++ b/core/jni/android_server_BluetoothService.cpp
@@ -895,6 +895,18 @@
 #endif
 }
 
+static jboolean setDevicePropertyStringNative(JNIEnv *env, jobject object,
+                                              jstring path, jstring key, jstring value) {
+#ifdef HAVE_BLUETOOTH
+    const char *c_value = env->GetStringUTFChars(value, NULL);
+    jboolean ret = setDevicePropertyNative(env, object, path, key,
+                                           (void *)&c_value, DBUS_TYPE_STRING);
+    env->ReleaseStringUTFChars(value, (char *)c_value);
+    return ret;
+#else
+    return JNI_FALSE;
+#endif
+}
 
 static jboolean createDeviceNative(JNIEnv *env, jobject object,
                                                 jstring address) {
@@ -1718,6 +1730,8 @@
             (void *)cancelPairingUserInputNative},
     {"setDevicePropertyBooleanNative", "(Ljava/lang/String;Ljava/lang/String;I)Z",
             (void *)setDevicePropertyBooleanNative},
+    {"setDevicePropertyStringNative", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z",
+            (void *)setDevicePropertyStringNative},
     {"createDeviceNative", "(Ljava/lang/String;)Z", (void *)createDeviceNative},
     {"discoverServicesNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)discoverServicesNative},
     {"addRfcommServiceRecordNative", "(Ljava/lang/String;JJS)I", (void *)addRfcommServiceRecordNative},