Merge "Add BluetoothDevice#connect and BluetoothDevice#disconnect as System APIs" into stage-aosp-master am: 4a39b54ec1 am: 0db21e73d3

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15936310

Change-Id: Ic42df1b1158199c0f0efaeb166e632d16aa55e0f
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 6a57c24..ba7903b 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1981,8 +1981,10 @@
   public final class BluetoothDevice implements android.os.Parcelable {
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean canBondWithoutDialog();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean cancelBondProcess();
+    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.MODIFY_PHONE_STATE}) public int connect();
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean createBond(int);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean createBondOutOfBand(int, @Nullable android.bluetooth.OobData, @Nullable android.bluetooth.OobData);
+    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int disconnect();
     method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean fetchUuidsWithSdp(int);
     method @Nullable @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public byte[] getMetadata(int);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getSimAccessPermission();
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
index 77abbe9..e9be0ec 100644
--- a/core/java/android/bluetooth/BluetoothAdapter.java
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -2005,71 +2005,6 @@
     }
 
     /**
-     * Connects all enabled and supported bluetooth profiles between the local and remote device.
-     * Connection is asynchronous and you should listen to each profile's broadcast intent
-     * ACTION_CONNECTION_STATE_CHANGED to verify whether connection was successful. For example,
-     * to verify a2dp is connected, you would listen for
-     * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED}
-     *
-     * @param device is the remote device with which to connect these profiles
-     * @return true if message sent to try to connect all profiles, false if an error occurred
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-            android.Manifest.permission.MODIFY_PHONE_STATE,
-    })
-    public boolean connectAllEnabledProfiles(@NonNull BluetoothDevice device) {
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.connectAllEnabledProfiles(device, mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-
-        return false;
-    }
-
-    /**
-     * Disconnects all enabled and supported bluetooth profiles between the local and remote device.
-     * Disconnection is asynchronous and you should listen to each profile's broadcast intent
-     * ACTION_CONNECTION_STATE_CHANGED to verify whether disconnection was successful. For example,
-     * to verify a2dp is disconnected, you would listen for
-     * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED}
-     *
-     * @param device is the remote device with which to disconnect these profiles
-     * @return true if message sent to try to disconnect all profiles, false if an error occurred
-     *
-     * @hide
-     */
-    @RequiresBluetoothConnectPermission
-    @RequiresPermission(allOf = {
-            android.Manifest.permission.BLUETOOTH_CONNECT,
-            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
-    })
-    public boolean disconnectAllEnabledProfiles(@NonNull BluetoothDevice device) {
-        try {
-            mServiceLock.readLock().lock();
-            if (mService != null) {
-                return mService.disconnectAllEnabledProfiles(device, mAttributionSource);
-            }
-        } catch (RemoteException e) {
-            Log.e(TAG, "", e);
-        } finally {
-            mServiceLock.readLock().unlock();
-        }
-
-        return false;
-    }
-
-    /**
      * Return true if the multi advertisement is supported by the chipset
      *
      * @return true if Multiple Advertisement feature is supported
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 71f05f52..6e918bd 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1683,6 +1683,90 @@
         return false;
     }
 
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            BluetoothStatusCodes.SUCCESS,
+            BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
+            BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED,
+            BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION,
+            BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED
+    })
+    public @interface ConnectionReturnValues{}
+
+    /**
+     * Connects all user enabled and supported bluetooth profiles between the local and remote
+     * device. If no profiles are user enabled (e.g. first connection), we connect all supported
+     * profiles. If the device is not already connected, this will page the device before initiating
+     * profile connections. Connection is asynchronous and you should listen to each profile's
+     * broadcast intent ACTION_CONNECTION_STATE_CHANGED to verify whether connection was successful.
+     * For example, to verify a2dp is connected, you would listen for
+     * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED}
+     *
+     * @return whether the messages were successfully sent to try to connect all profiles
+     * @throws IllegalArgumentException if the device address is invalid
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresBluetoothConnectPermission
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.BLUETOOTH_CONNECT,
+            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+            android.Manifest.permission.MODIFY_PHONE_STATE,
+    })
+    public @ConnectionReturnValues int connect() {
+        if (!BluetoothAdapter.checkBluetoothAddress(getAddress())) {
+            throw new IllegalArgumentException("device cannot have an invalid address");
+        }
+
+        try {
+            if (sService == null) {
+                Log.e(TAG, "BT not enabled. Cannot connect to remote device.");
+                return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
+            }
+            return sService.connectAllEnabledProfiles(this, mAttributionSource);
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Disconnects all connected bluetooth profiles between the local and remote device.
+     * Disconnection is asynchronous and you should listen to each profile's broadcast intent
+     * ACTION_CONNECTION_STATE_CHANGED to verify whether disconnection was successful. For example,
+     * to verify a2dp is disconnected, you would listen for
+     * {@link BluetoothA2dp#ACTION_CONNECTION_STATE_CHANGED}
+     *
+     * @return whether the messages were successfully sent to try to disconnect all profiles
+     * @throws IllegalArgumentException if the device address is invalid
+     *
+     * @hide
+     */
+    @SystemApi
+    @RequiresBluetoothConnectPermission
+    @RequiresPermission(allOf = {
+            android.Manifest.permission.BLUETOOTH_CONNECT,
+            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
+    })
+    public @ConnectionReturnValues int disconnect() {
+        if (!BluetoothAdapter.checkBluetoothAddress(getAddress())) {
+            throw new IllegalArgumentException("device cannot have an invalid address");
+        }
+
+        try {
+            if (sService == null) {
+                Log.e(TAG, "BT not enabled. Cannot disconnect from remote device.");
+                return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
+            }
+            return sService.disconnectAllEnabledProfiles(this, mAttributionSource);
+        } catch (RemoteException e) {
+            Log.e(TAG, "", e);
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
     /**
      * Returns whether there is an open connection to this device.
      *
diff --git a/core/java/android/bluetooth/BluetoothStatusCodes.java b/core/java/android/bluetooth/BluetoothStatusCodes.java
index 31bb0f6..3e46c49 100644
--- a/core/java/android/bluetooth/BluetoothStatusCodes.java
+++ b/core/java/android/bluetooth/BluetoothStatusCodes.java
@@ -21,7 +21,7 @@
 /**
  * A class with constants representing possible return values for Bluetooth APIs. General return
  * values occupy the range 0 to 99. Profile-specific return values occupy the range 100-999.
- * API-specific return values start at 1000. The exception to this is the "other" error code which
+ * API-specific return values start at 1000. The exception to this is the "UNKNOWN" error code which
  * occupies the max integer value.
  */
 public final class BluetoothStatusCodes {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 5ecff44..7ce9b51 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -273,7 +273,7 @@
 
     public void disconnect() {
         synchronized (mProfileLock) {
-            mLocalAdapter.disconnectAllEnabledProfiles(mDevice);
+            mDevice.disconnect();
         }
         // Disconnect  PBAP server in case its connected
         // This is to ensure all the profiles are disconnected as some CK/Hs do not
@@ -314,7 +314,7 @@
         }
 
         mConnectAttempted = SystemClock.elapsedRealtime();
-        connectAllEnabledProfiles();
+        connectDevice();
     }
 
     public long getHiSyncId() {
@@ -371,7 +371,7 @@
         connect();
     }
 
-    private void connectAllEnabledProfiles() {
+    private void connectDevice() {
         synchronized (mProfileLock) {
             // Try to initialize the profiles if they were not.
             if (mProfiles.isEmpty()) {
@@ -386,7 +386,7 @@
                 return;
             }
 
-            mLocalAdapter.connectAllEnabledProfiles(mDevice);
+            mDevice.connect();
         }
     }
 
@@ -769,8 +769,8 @@
          * Otherwise, allow the connect on UUID change.
          */
         if ((mConnectAttempted + timeout) > SystemClock.elapsedRealtime()) {
-            Log.d(TAG, "onUuidChanged: triggering connectAllEnabledProfiles");
-            connectAllEnabledProfiles();
+            Log.d(TAG, "onUuidChanged: triggering connectDevice");
+            connectDevice();
         }
 
         dispatchAttributesChanged();