Deprecate GATT callbacks and methods that were not memory safe and
replace with memory safe versions.

Tag: #feature
Bug: 195157393
Test: Manual
CTS-Coverage-Bug: 205190062
Merged-In: I5ae604ec20febcf646bfe6a8f866b218448349c1
Change-Id: I5ae604ec20febcf646bfe6a8f866b218448349c1
diff --git a/core/api/current.txt b/core/api/current.txt
index 1c014454..9d1a171 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -9125,8 +9125,10 @@
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean requestMtu(int);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setCharacteristicNotification(android.bluetooth.BluetoothGattCharacteristic, boolean);
     method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public void setPreferredPhy(int, int, int);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean writeCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
-    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean writeDescriptor(android.bluetooth.BluetoothGattDescriptor);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean writeCharacteristic(android.bluetooth.BluetoothGattCharacteristic);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int writeCharacteristic(@NonNull android.bluetooth.BluetoothGattCharacteristic, @NonNull byte[], int);
+    method @Deprecated @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean writeDescriptor(android.bluetooth.BluetoothGattDescriptor);
+    method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int writeDescriptor(@NonNull android.bluetooth.BluetoothGattDescriptor, @NonNull byte[]);
     field public static final int CONNECTION_PRIORITY_BALANCED = 0; // 0x0
     field public static final int CONNECTION_PRIORITY_HIGH = 1; // 0x1
     field public static final int CONNECTION_PRIORITY_LOW_POWER = 2; // 0x2
@@ -9145,11 +9147,14 @@
 
   public abstract class BluetoothGattCallback {
     ctor public BluetoothGattCallback();
-    method public void onCharacteristicChanged(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic);
-    method public void onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int);
+    method @Deprecated public void onCharacteristicChanged(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic);
+    method public void onCharacteristicChanged(@NonNull android.bluetooth.BluetoothGatt, @NonNull android.bluetooth.BluetoothGattCharacteristic, @NonNull byte[]);
+    method @Deprecated public void onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int);
+    method public void onCharacteristicRead(@NonNull android.bluetooth.BluetoothGatt, @NonNull android.bluetooth.BluetoothGattCharacteristic, @NonNull byte[], int);
     method public void onCharacteristicWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int);
     method public void onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int);
-    method public void onDescriptorRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int);
+    method @Deprecated public void onDescriptorRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int);
+    method public void onDescriptorRead(@NonNull android.bluetooth.BluetoothGatt, @NonNull android.bluetooth.BluetoothGattDescriptor, int, @NonNull byte[]);
     method public void onDescriptorWrite(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattDescriptor, int);
     method public void onMtuChanged(android.bluetooth.BluetoothGatt, int, int);
     method public void onPhyRead(android.bluetooth.BluetoothGatt, int, int, int);
@@ -9166,20 +9171,20 @@
     method public int describeContents();
     method public android.bluetooth.BluetoothGattDescriptor getDescriptor(java.util.UUID);
     method public java.util.List<android.bluetooth.BluetoothGattDescriptor> getDescriptors();
-    method public Float getFloatValue(int, int);
+    method @Deprecated public Float getFloatValue(int, int);
     method public int getInstanceId();
-    method public Integer getIntValue(int, int);
+    method @Deprecated public Integer getIntValue(int, int);
     method public int getPermissions();
     method public int getProperties();
     method public android.bluetooth.BluetoothGattService getService();
-    method public String getStringValue(int);
+    method @Deprecated public String getStringValue(int);
     method public java.util.UUID getUuid();
-    method public byte[] getValue();
+    method @Deprecated public byte[] getValue();
     method public int getWriteType();
-    method public boolean setValue(byte[]);
-    method public boolean setValue(int, int, int);
-    method public boolean setValue(int, int, int, int);
-    method public boolean setValue(String);
+    method @Deprecated public boolean setValue(byte[]);
+    method @Deprecated public boolean setValue(int, int, int);
+    method @Deprecated public boolean setValue(int, int, int, int);
+    method @Deprecated public boolean setValue(String);
     method public void setWriteType(int);
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothGattCharacteristic> CREATOR;
@@ -9219,8 +9224,8 @@
     method public android.bluetooth.BluetoothGattCharacteristic getCharacteristic();
     method public int getPermissions();
     method public java.util.UUID getUuid();
-    method public byte[] getValue();
-    method public boolean setValue(byte[]);
+    method @Deprecated public byte[] getValue();
+    method @Deprecated public boolean setValue(byte[]);
     method public void writeToParcel(android.os.Parcel, int);
     field @NonNull public static final android.os.Parcelable.Creator<android.bluetooth.BluetoothGattDescriptor> CREATOR;
     field public static final byte[] DISABLE_NOTIFICATION_VALUE;
@@ -9508,8 +9513,12 @@
     field public static final int ERROR_BLUETOOTH_NOT_ALLOWED = 2; // 0x2
     field public static final int ERROR_BLUETOOTH_NOT_ENABLED = 1; // 0x1
     field public static final int ERROR_DEVICE_NOT_BONDED = 3; // 0x3
-    field public static final int ERROR_FEATURE_NOT_SUPPORTED = 8; // 0x8
+    field public static final int ERROR_FEATURE_NOT_SUPPORTED = 10; // 0xa
+    field public static final int ERROR_GATT_WRITE_NOT_ALLOWED = 101; // 0x65
+    field public static final int ERROR_GATT_WRITE_REQUEST_BUSY = 102; // 0x66
     field public static final int ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION = 6; // 0x6
+    field public static final int ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION = 8; // 0x8
+    field public static final int ERROR_PROFILE_SERVICE_NOT_BOUND = 9; // 0x9
     field public static final int ERROR_UNKNOWN = 2147483647; // 0x7fffffff
     field public static final int SUCCESS = 0; // 0x0
   }
diff --git a/core/java/android/bluetooth/BluetoothGatt.java b/core/java/android/bluetooth/BluetoothGatt.java
index ca898bd..4e7c01a 100644
--- a/core/java/android/bluetooth/BluetoothGatt.java
+++ b/core/java/android/bluetooth/BluetoothGatt.java
@@ -16,6 +16,8 @@
 
 package android.bluetooth;
 
+import android.annotation.IntDef;
+import android.annotation.NonNull;
 import android.annotation.RequiresNoPermission;
 import android.annotation.RequiresPermission;
 import android.annotation.SuppressLint;
@@ -29,6 +31,8 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.UUID;
@@ -140,27 +144,6 @@
     public static final int CONNECTION_PRIORITY_LOW_POWER = 2;
 
     /**
-     * A GATT writeCharacteristic request is started successfully.
-     *
-     * @hide
-     */
-    public static final int GATT_WRITE_REQUEST_SUCCESS = 0;
-
-    /**
-     * A GATT writeCharacteristic request failed to start.
-     *
-     * @hide
-     */
-    public static final int GATT_WRITE_REQUEST_FAIL = 1;
-
-    /**
-     * A GATT writeCharacteristic request is issued to a busy remote device.
-     *
-     * @hide
-     */
-    public static final int GATT_WRITE_REQUEST_BUSY = 2;
-
-    /**
      * No authentication required.
      *
      * @hide
@@ -430,6 +413,9 @@
                             if (callback != null) {
                                 if (status == 0) characteristic.setValue(value);
                                 callback.onCharacteristicRead(BluetoothGatt.this, characteristic,
+                                        value, status);
+                                // Keep calling deprecated callback to maintain app compatibility
+                                callback.onCharacteristicRead(BluetoothGatt.this, characteristic,
                                         status);
                             }
                         }
@@ -443,7 +429,8 @@
                  */
                 @Override
                 @SuppressLint("AndroidFrameworkRequiresPermission")
-                public void onCharacteristicWrite(String address, int status, int handle) {
+                public void onCharacteristicWrite(String address, int status, int handle,
+                        byte[] value) {
                     if (VDBG) {
                         Log.d(TAG, "onCharacteristicWrite() - Device=" + address
                                 + " handle=" + handle + " Status=" + status);
@@ -467,12 +454,13 @@
                         try {
                             final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
                                     ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
-                            int requestStatus = GATT_WRITE_REQUEST_FAIL;
+                            int requestStatus = BluetoothStatusCodes.ERROR_UNKNOWN;
                             for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) {
                                 requestStatus =  mService.writeCharacteristic(mClientIf, address,
                                                   handle, characteristic.getWriteType(), authReq,
-                                                  characteristic.getValue(), mAttributionSource);
-                                if (requestStatus != GATT_WRITE_REQUEST_BUSY) {
+                                                  value, mAttributionSource);
+                                if (requestStatus
+                                        != BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY) {
                                     break;
                                 }
                                 try {
@@ -488,7 +476,6 @@
                     }
 
                     mAuthRetryState = AUTH_RETRY_STATE_IDLE;
-
                     runOrQueueCallback(new Runnable() {
                         @Override
                         public void run() {
@@ -525,6 +512,9 @@
                             if (callback != null) {
                                 characteristic.setValue(value);
                                 callback.onCharacteristicChanged(BluetoothGatt.this,
+                                        characteristic, value);
+                                // Keep calling deprecated callback to maintain app compatibility
+                                callback.onCharacteristicChanged(BluetoothGatt.this,
                                         characteristic);
                             }
                         }
@@ -578,6 +568,9 @@
                             final BluetoothGattCallback callback = mCallback;
                             if (callback != null) {
                                 if (status == 0) descriptor.setValue(value);
+                                callback.onDescriptorRead(BluetoothGatt.this, descriptor, status,
+                                        value);
+                                // Keep calling deprecated callback to maintain app compatibility
                                 callback.onDescriptorRead(BluetoothGatt.this, descriptor, status);
                             }
                         }
@@ -590,7 +583,8 @@
                  */
                 @Override
                 @SuppressLint("AndroidFrameworkRequiresPermission")
-                public void onDescriptorWrite(String address, int status, int handle) {
+                public void onDescriptorWrite(String address, int status, int handle,
+                        byte[] value) {
                     if (VDBG) {
                         Log.d(TAG,
                                 "onDescriptorWrite() - Device=" + address + " handle=" + handle);
@@ -614,7 +608,7 @@
                             final int authReq = (mAuthRetryState == AUTH_RETRY_STATE_IDLE)
                                     ? AUTHENTICATION_NO_MITM : AUTHENTICATION_MITM;
                             mService.writeDescriptor(mClientIf, address, handle,
-                                    authReq, descriptor.getValue(), mAttributionSource);
+                                    authReq, value, mAttributionSource);
                             mAuthRetryState++;
                             return;
                         } catch (RemoteException e) {
@@ -1194,8 +1188,8 @@
      * Reads the requested characteristic from the associated remote device.
      *
      * <p>This is an asynchronous operation. The result of the read operation
-     * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
-     * callback.
+     * is reported by the {@link BluetoothGattCallback#onCharacteristicRead(BluetoothGatt,
+     * BluetoothGattCharacteristic, byte[], int)} callback.
      *
      * @param characteristic Characteristic to read from the remote device
      * @return true, if the read operation was initiated successfully
@@ -1240,8 +1234,8 @@
      * Reads the characteristic using its UUID from the associated remote device.
      *
      * <p>This is an asynchronous operation. The result of the read operation
-     * is reported by the {@link BluetoothGattCallback#onCharacteristicRead}
-     * callback.
+     * is reported by the {@link BluetoothGattCallback#onCharacteristicRead(BluetoothGatt,
+     * BluetoothGattCharacteristic, byte[], int)} callback.
      *
      * @param uuid UUID of characteristic to read from the remote device
      * @return true, if the read operation was initiated successfully
@@ -1284,40 +1278,94 @@
      *
      * @param characteristic Characteristic to write on the remote device
      * @return true, if the write operation was initiated successfully
+     * @throws IllegalArgumentException if characteristic or its value are null
+     *
+     * @deprecated Use {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[],
+     * int)} as this is not memory safe.
      */
+    @Deprecated
     @RequiresLegacyBluetoothPermission
     @RequiresBluetoothConnectPermission
     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
     public boolean writeCharacteristic(BluetoothGattCharacteristic characteristic) {
+        try {
+            return writeCharacteristic(characteristic, characteristic.getValue(),
+                    characteristic.getWriteType()) == BluetoothStatusCodes.SUCCESS;
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+    /** @hide */
+    @Retention(RetentionPolicy.SOURCE)
+    @IntDef(value = {
+            BluetoothStatusCodes.SUCCESS,
+            BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION,
+            BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION,
+            BluetoothStatusCodes.ERROR_DEVICE_NOT_CONNECTED,
+            BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND,
+            BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED,
+            BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY,
+            BluetoothStatusCodes.ERROR_UNKNOWN
+    })
+    public @interface WriteOperationReturnValues{}
+
+    /**
+     * Writes a given characteristic and its values to the associated remote device.
+     *
+     * <p>Once the write operation has been completed, the
+     * {@link BluetoothGattCallback#onCharacteristicWrite} callback is invoked,
+     * reporting the result of the operation.
+     *
+     * @param characteristic Characteristic to write on the remote device
+     * @return whether the characteristic was successfully written to
+     * @throws IllegalArgumentException if characteristic or value are null
+     */
+    @RequiresBluetoothConnectPermission
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+    @WriteOperationReturnValues
+    public int writeCharacteristic(@NonNull BluetoothGattCharacteristic characteristic,
+            @NonNull byte[] value, int writeType) {
+        if (characteristic == null) {
+            throw new IllegalArgumentException("characteristic must not be null");
+        }
+        if (value == null) {
+            throw new IllegalArgumentException("value must not be null");
+        }
+        if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
         if ((characteristic.getProperties() & BluetoothGattCharacteristic.PROPERTY_WRITE) == 0
                 && (characteristic.getProperties()
                 & BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) == 0) {
-            return false;
+            return BluetoothStatusCodes.ERROR_GATT_WRITE_NOT_ALLOWED;
+        }
+        if (mService == null || mClientIf == 0) {
+            return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND;
         }
 
-        if (VDBG) Log.d(TAG, "writeCharacteristic() - uuid: " + characteristic.getUuid());
-        if (mService == null || mClientIf == 0 || characteristic.getValue() == null) return false;
-
         BluetoothGattService service = characteristic.getService();
-        if (service == null) return false;
+        if (service == null) {
+            throw new IllegalArgumentException("Characteristic must have a non-null service");
+        }
 
         BluetoothDevice device = service.getDevice();
-        if (device == null) return false;
+        if (device == null) {
+            throw new IllegalArgumentException("Service must have a non-null device");
+        }
 
         synchronized (mDeviceBusyLock) {
             if (mDeviceBusy) {
-                return false;
+                return BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY;
             }
             mDeviceBusy = true;
         }
 
-        int requestStatus = GATT_WRITE_REQUEST_FAIL;
+        int requestStatus = BluetoothStatusCodes.ERROR_UNKNOWN;
         try {
             for (int i = 0; i < WRITE_CHARACTERISTIC_MAX_RETRIES; i++) {
                 requestStatus = mService.writeCharacteristic(mClientIf, device.getAddress(),
-                    characteristic.getInstanceId(), characteristic.getWriteType(),
-                    AUTHENTICATION_NONE, characteristic.getValue(), mAttributionSource);
-                if (requestStatus != GATT_WRITE_REQUEST_BUSY) {
+                        characteristic.getInstanceId(), writeType, AUTHENTICATION_NONE, value,
+                        mAttributionSource);
+                if (requestStatus != BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY) {
                     break;
                 }
                 try {
@@ -1330,10 +1378,10 @@
             synchronized (mDeviceBusyLock) {
                 mDeviceBusy = false;
             }
-            return false;
+            throw e.rethrowFromSystemServer();
         }
 
-        return requestStatus == GATT_WRITE_REQUEST_SUCCESS;
+        return requestStatus;
     }
 
     /**
@@ -1384,45 +1432,86 @@
     /**
      * Write the value of a given descriptor to the associated remote device.
      *
-     * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is
-     * triggered to report the result of the write operation.
+     * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is triggered to report the
+     * result of the write operation.
      *
      * @param descriptor Descriptor to write to the associated remote device
      * @return true, if the write operation was initiated successfully
+     * @throws IllegalArgumentException if descriptor or its value are null
+     *
+     * @deprecated Use {@link BluetoothGatt#writeDescriptor(BluetoothGattDescriptor, byte[])} as
+     * this is not memory safe.
      */
+    @Deprecated
     @RequiresLegacyBluetoothPermission
     @RequiresBluetoothConnectPermission
     @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
     public boolean writeDescriptor(BluetoothGattDescriptor descriptor) {
+        try {
+            return writeDescriptor(descriptor, descriptor.getValue())
+                    == BluetoothStatusCodes.SUCCESS;
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+    /**
+     * Write the value of a given descriptor to the associated remote device.
+     *
+     * <p>A {@link BluetoothGattCallback#onDescriptorWrite} callback is triggered to report the
+     * result of the write operation.
+     *
+     * @param descriptor Descriptor to write to the associated remote device
+     * @return true, if the write operation was initiated successfully
+     * @throws IllegalArgumentException if descriptor or value are null
+     */
+    @RequiresBluetoothConnectPermission
+    @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
+    @WriteOperationReturnValues
+    public int writeDescriptor(@NonNull BluetoothGattDescriptor descriptor,
+            @NonNull byte[] value) {
+        if (descriptor == null) {
+            throw new IllegalArgumentException("descriptor must not be null");
+        }
+        if (value == null) {
+            throw new IllegalArgumentException("value must not be null");
+        }
         if (VDBG) Log.d(TAG, "writeDescriptor() - uuid: " + descriptor.getUuid());
-        if (mService == null || mClientIf == 0 || descriptor.getValue() == null) return false;
+        if (mService == null || mClientIf == 0) {
+            return BluetoothStatusCodes.ERROR_PROFILE_SERVICE_NOT_BOUND;
+        }
 
         BluetoothGattCharacteristic characteristic = descriptor.getCharacteristic();
-        if (characteristic == null) return false;
+        if (characteristic == null) {
+            throw new IllegalArgumentException("Descriptor must have a non-null characteristic");
+        }
 
         BluetoothGattService service = characteristic.getService();
-        if (service == null) return false;
+        if (service == null) {
+            throw new IllegalArgumentException("Characteristic must have a non-null service");
+        }
 
         BluetoothDevice device = service.getDevice();
-        if (device == null) return false;
+        if (device == null) {
+            throw new IllegalArgumentException("Service must have a non-null device");
+        }
 
         synchronized (mDeviceBusyLock) {
-            if (mDeviceBusy) return false;
+            if (mDeviceBusy) return BluetoothStatusCodes.ERROR_GATT_WRITE_REQUEST_BUSY;
             mDeviceBusy = true;
         }
 
         try {
-            mService.writeDescriptor(mClientIf, device.getAddress(), descriptor.getInstanceId(),
-                    AUTHENTICATION_NONE, descriptor.getValue(), mAttributionSource);
+            return mService.writeDescriptor(mClientIf, device.getAddress(),
+                    descriptor.getInstanceId(), AUTHENTICATION_NONE, value, mAttributionSource);
         } catch (RemoteException e) {
             Log.e(TAG, "", e);
             synchronized (mDeviceBusyLock) {
                 mDeviceBusy = false;
             }
-            return false;
+            e.rethrowFromSystemServer();
         }
-
-        return true;
+        return BluetoothStatusCodes.ERROR_UNKNOWN;
     }
 
     /**
@@ -1431,9 +1520,9 @@
      * <p>Once a reliable write transaction has been initiated, all calls
      * to {@link #writeCharacteristic} are sent to the remote device for
      * verification and queued up for atomic execution. The application will
-     * receive an {@link BluetoothGattCallback#onCharacteristicWrite} callback
-     * in response to every {@link #writeCharacteristic} call and is responsible
-     * for verifying if the value has been transmitted accurately.
+     * receive a {@link BluetoothGattCallback#onCharacteristicWrite} callback in response to every
+     * {@link #writeCharacteristic(BluetoothGattCharacteristic, byte[], int)} call and is
+     * responsible for verifying if the value has been transmitted accurately.
      *
      * <p>After all characteristics have been queued up and verified,
      * {@link #executeReliableWrite} will execute all writes. If a characteristic
@@ -1530,9 +1619,9 @@
      * Enable or disable notifications/indications for a given characteristic.
      *
      * <p>Once notifications are enabled for a characteristic, a
-     * {@link BluetoothGattCallback#onCharacteristicChanged} callback will be
-     * triggered if the remote device indicates that the given characteristic
-     * has changed.
+     * {@link BluetoothGattCallback#onCharacteristicChanged(BluetoothGatt,
+     * BluetoothGattCharacteristic, byte[])} callback will be triggered if the remote device
+     * indicates that the given characteristic has changed.
      *
      * @param characteristic The characteristic for which to enable notifications
      * @param enable Set to true to enable notifications/indications
diff --git a/core/java/android/bluetooth/BluetoothGattCallback.java b/core/java/android/bluetooth/BluetoothGattCallback.java
index 1c40cff..d0a5a1e 100644
--- a/core/java/android/bluetooth/BluetoothGattCallback.java
+++ b/core/java/android/bluetooth/BluetoothGattCallback.java
@@ -80,16 +80,34 @@
     /**
      * Callback reporting the result of a characteristic read operation.
      *
-     * @param gatt GATT client invoked {@link BluetoothGatt#readCharacteristic}
+     * @param gatt           GATT client invoked
+     *                       {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)}
      * @param characteristic Characteristic that was read from the associated remote device.
-     * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed
-     * successfully.
+     * @param status         {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed
+     *                       successfully.
+     * @deprecated Use {@link BluetoothGattCallback#onCharacteristicRead(BluetoothGatt,
+     * BluetoothGattCharacteristic, byte[], int)} as it is memory safe
      */
+    @Deprecated
     public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic,
             int status) {
     }
 
     /**
+     * Callback reporting the result of a characteristic read operation.
+     *
+     * @param gatt           GATT client invoked
+     *                       {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)}
+     * @param characteristic Characteristic that was read from the associated remote device.
+     * @param value          the value of the characteristic
+     * @param status         {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed
+     *                       successfully.
+     */
+    public void onCharacteristicRead(@NonNull BluetoothGatt gatt, @NonNull
+            BluetoothGattCharacteristic characteristic, @NonNull byte[] value, int status) {
+    }
+
+    /**
      * Callback indicating the result of a characteristic write operation.
      *
      * <p>If this callback is invoked while a reliable write transaction is
@@ -98,10 +116,13 @@
      * value to the desired value to be written. If the values don't match,
      * the application must abort the reliable write transaction.
      *
-     * @param gatt GATT client invoked {@link BluetoothGatt#writeCharacteristic}
+     * @param gatt           GATT client that invoked
+     *                       {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic,
+     *                       byte[], int)}
      * @param characteristic Characteristic that was written to the associated remote device.
-     * @param status The result of the write operation {@link BluetoothGatt#GATT_SUCCESS} if the
-     * operation succeeds.
+     * @param status         The result of the write operation {@link BluetoothGatt#GATT_SUCCESS} if
+     *                       the
+     *                       operation succeeds.
      */
     public void onCharacteristicWrite(BluetoothGatt gatt,
             BluetoothGattCharacteristic characteristic, int status) {
@@ -110,33 +131,68 @@
     /**
      * Callback triggered as a result of a remote characteristic notification.
      *
-     * @param gatt GATT client the characteristic is associated with
+     * @param gatt           GATT client the characteristic is associated with
      * @param characteristic Characteristic that has been updated as a result of a remote
-     * notification event.
+     *                       notification event.
+     * @deprecated Use {@link BluetoothGattCallback#onCharacteristicChanged(BluetoothGatt,
+     * BluetoothGattCharacteristic, byte[])} as it is memory safe by providing the characteristic
+     * value at the time of notification.
      */
+    @Deprecated
     public void onCharacteristicChanged(BluetoothGatt gatt,
             BluetoothGattCharacteristic characteristic) {
     }
 
     /**
+     * Callback triggered as a result of a remote characteristic notification. Note that the value
+     * within the characteristic object may have changed since receiving the remote characteristic
+     * notification, so check the parameter value for the value at the time of notification.
+     *
+     * @param gatt           GATT client the characteristic is associated with
+     * @param characteristic Characteristic that has been updated as a result of a remote
+     *                       notification event.
+     * @param value          notified characteristic value
+     */
+    public void onCharacteristicChanged(@NonNull BluetoothGatt gatt,
+            @NonNull BluetoothGattCharacteristic characteristic, @NonNull byte[] value) {
+    }
+
+    /**
      * Callback reporting the result of a descriptor read operation.
      *
-     * @param gatt GATT client invoked {@link BluetoothGatt#readDescriptor}
+     * @param gatt       GATT client invoked {@link BluetoothGatt#readDescriptor}
      * @param descriptor Descriptor that was read from the associated remote device.
-     * @param status {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed
-     * successfully
+     * @param status     {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed
+     *                   successfully
+     * @deprecated Use {@link BluetoothGattCallback#onDescriptorRead(BluetoothGatt,
+     * BluetoothGattDescriptor, int, byte[])} as it is memory safe by providing the descriptor
+     * value at the time it was read.
      */
+    @Deprecated
     public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
             int status) {
     }
 
     /**
+     * Callback reporting the result of a descriptor read operation.
+     *
+     * @param gatt       GATT client invoked {@link BluetoothGatt#readDescriptor}
+     * @param descriptor Descriptor that was read from the associated remote device.
+     * @param status     {@link BluetoothGatt#GATT_SUCCESS} if the read operation was completed
+     *                   successfully
+     * @param value      the descriptor value at the time of the read operation
+     */
+    public void onDescriptorRead(@NonNull BluetoothGatt gatt,
+            @NonNull BluetoothGattDescriptor descriptor, int status, @NonNull byte[] value) {
+    }
+
+    /**
      * Callback indicating the result of a descriptor write operation.
      *
-     * @param gatt GATT client invoked {@link BluetoothGatt#writeDescriptor}
+     * @param gatt       GATT client invoked {@link BluetoothGatt#writeDescriptor}
      * @param descriptor Descriptor that was writte to the associated remote device.
-     * @param status The result of the write operation {@link BluetoothGatt#GATT_SUCCESS} if the
-     * operation succeeds.
+     * @param status     The result of the write operation {@link BluetoothGatt#GATT_SUCCESS} if the
+     *                   operation succeeds.
      */
     public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor,
             int status) {
diff --git a/core/java/android/bluetooth/BluetoothGattCharacteristic.java b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
index 8a7d4ba..c5e986e 100644
--- a/core/java/android/bluetooth/BluetoothGattCharacteristic.java
+++ b/core/java/android/bluetooth/BluetoothGattCharacteristic.java
@@ -451,8 +451,8 @@
      * Set the write type for this characteristic
      *
      * <p>Setting the write type of a characteristic determines how the
-     * {@link BluetoothGatt#writeCharacteristic} function write this
-     * characteristic.
+     * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)} function
+     * write this characteristic.
      *
      * @param writeType The write type to for this characteristic. Can be one of: {@link
      * #WRITE_TYPE_DEFAULT}, {@link #WRITE_TYPE_NO_RESPONSE} or {@link #WRITE_TYPE_SIGNED}.
@@ -504,7 +504,10 @@
      * operation or if a characteristic update notification has been received.
      *
      * @return Cached value of the characteristic
+     *
+     * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} instead
      */
+    @Deprecated
     public byte[] getValue() {
         return mValue;
     }
@@ -521,7 +524,11 @@
      * @param formatType The format type used to interpret the characteristic value.
      * @param offset Offset at which the integer value can be found.
      * @return Cached value of the characteristic or null of offset exceeds value size.
+     *
+     * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} to get
+     * the characteristic value
      */
+    @Deprecated
     public Integer getIntValue(int formatType, int offset) {
         if ((offset + getTypeLen(formatType)) > mValue.length) return null;
 
@@ -558,7 +565,11 @@
      * @param offset Offset at which the float value can be found.
      * @return Cached value of the characteristic at a given offset or null if the requested offset
      * exceeds the value size.
+     *
+     * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} to get
+     * the characteristic value
      */
+    @Deprecated
     public Float getFloatValue(int formatType, int offset) {
         if ((offset + getTypeLen(formatType)) > mValue.length) return null;
 
@@ -580,7 +591,11 @@
      *
      * @param offset Offset at which the string value can be found.
      * @return Cached value of the characteristic
+     *
+     * @deprecated Use {@link BluetoothGatt#readCharacteristic(BluetoothGattCharacteristic)} to get
+     * the characteristic value
      */
+    @Deprecated
     public String getStringValue(int offset) {
         if (mValue == null || offset > mValue.length) return null;
         byte[] strBytes = new byte[mValue.length - offset];
@@ -599,7 +614,11 @@
      * @param value New value for this characteristic
      * @return true if the locally stored value has been set, false if the requested value could not
      * be stored locally.
+     *
+     * @deprecated Pass the characteristic value directly into
+     * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)}
      */
+    @Deprecated
     public boolean setValue(byte[] value) {
         mValue = value;
         return true;
@@ -613,7 +632,11 @@
      * @param formatType Integer format type used to transform the value parameter
      * @param offset Offset at which the value should be placed
      * @return true if the locally stored value has been set
+     *
+     * @deprecated Pass the characteristic value directly into
+     * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)}
      */
+    @Deprecated
     public boolean setValue(int value, int formatType, int offset) {
         int len = offset + getTypeLen(formatType);
         if (mValue == null) mValue = new byte[len];
@@ -660,7 +683,11 @@
      * @param formatType Float format type used to transform the value parameter
      * @param offset Offset at which the value should be placed
      * @return true if the locally stored value has been set
+     *
+     * @deprecated Pass the characteristic value directly into
+     * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)}
      */
+    @Deprecated
     public boolean setValue(int mantissa, int exponent, int formatType, int offset) {
         int len = offset + getTypeLen(formatType);
         if (mValue == null) mValue = new byte[len];
@@ -697,7 +724,11 @@
      *
      * @param value New value for this characteristic
      * @return true if the locally stored value has been set
+     *
+     * @deprecated Pass the characteristic value directly into
+     * {@link BluetoothGatt#writeCharacteristic(BluetoothGattCharacteristic, byte[], int)}
      */
+    @Deprecated
     public boolean setValue(String value) {
         mValue = value.getBytes();
         return true;
diff --git a/core/java/android/bluetooth/BluetoothGattDescriptor.java b/core/java/android/bluetooth/BluetoothGattDescriptor.java
index ed5ea08..a35d5b9 100644
--- a/core/java/android/bluetooth/BluetoothGattDescriptor.java
+++ b/core/java/android/bluetooth/BluetoothGattDescriptor.java
@@ -260,7 +260,10 @@
      * operation.
      *
      * @return Cached value of the descriptor
+     *
+     * @deprecated  Use {@link BluetoothGatt#readDescriptor(BluetoothGattDescriptor)} instead
      */
+    @Deprecated
     public byte[] getValue() {
         return mValue;
     }
@@ -276,7 +279,11 @@
      * @param value New value for this descriptor
      * @return true if the locally stored value has been set, false if the requested value could not
      * be stored locally.
+     *
+     * @deprecated Pass the descriptor value directly into
+     * {@link BluetoothGatt#writeDescriptor(BluetoothGattDescriptor, byte[])}
      */
+    @Deprecated
     public boolean setValue(byte[] value) {
         mValue = value;
         return true;
diff --git a/core/java/android/bluetooth/BluetoothStatusCodes.java b/core/java/android/bluetooth/BluetoothStatusCodes.java
index 63e84ed..ca01784 100644
--- a/core/java/android/bluetooth/BluetoothStatusCodes.java
+++ b/core/java/android/bluetooth/BluetoothStatusCodes.java
@@ -79,9 +79,31 @@
     public static final int ERROR_MISSING_BLUETOOTH_SCAN_PERMISSION = 7;
 
     /**
+     * Error code indicating that the caller does not have the
+     * {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission
+     */
+    public static final int ERROR_MISSING_BLUETOOTH_PRIVILEGED_PERMISSION = 8;
+
+    /**
+     * Error code indicating that the profile service is not bound. You can bind a profile service
+     * by calling {@link BluetoothAdapter#getProfileProxy}
+     */
+    public static final int ERROR_PROFILE_SERVICE_NOT_BOUND = 9;
+
+    /**
      * Error code indicating that the feature is not supported.
      */
-    public static final int ERROR_FEATURE_NOT_SUPPORTED = 8;
+    public static final int ERROR_FEATURE_NOT_SUPPORTED = 10;
+
+    /**
+     * A GATT writeCharacteristic request is not permitted on the remote device.
+     */
+    public static final int ERROR_GATT_WRITE_NOT_ALLOWED = 101;
+
+    /**
+     * A GATT writeCharacteristic request is issued to a busy remote device.
+     */
+    public static final int ERROR_GATT_WRITE_REQUEST_BUSY = 102;
 
     /**
      * If another application has already requested {@link OobData} then another fetch will be