Merge "Update BluetoothDevice#setAlias based on API council feedback: now accepts null input and returns an int (with error codes). Update CompanionDeviceManager#canPairWithoutPrompt to take a UserHandle instead of an int. Adds BluetoothStatusCodes class for all new Bluetooth error / success codes. Moved OOB and hci disconnect constants to the new BluetoothStatusCodes class." into sc-dev

This commit is contained in:
Rahul Sabnis
2021-05-19 16:00:04 +00:00
committed by Android (Google) Code Review
6 changed files with 279 additions and 170 deletions

View File

@@ -9039,7 +9039,7 @@ package android.bluetooth {
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public String getName();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int getType();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public android.os.ParcelUuid[] getUuids();
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setAlias(@NonNull String);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public int setAlias(@Nullable String);
method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean setPairingConfirmation(boolean);
method @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) public boolean setPin(byte[]);
method public void writeToParcel(android.os.Parcel, int);
@@ -9487,6 +9487,15 @@ package android.bluetooth {
field public static final int TYPE_SCO = 2; // 0x2
}
public final class BluetoothStatusCodes {
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_MISSING_BLUETOOTH_CONNECT_PERMISSION = 6; // 0x6
field public static final int ERROR_UNKNOWN = 2147483647; // 0x7fffffff
field public static final int SUCCESS = 0; // 0x0
}
}
package android.bluetooth.le {

View File

@@ -1950,9 +1950,6 @@ package android.bluetooth {
field public static final int ACTIVE_DEVICE_ALL = 2; // 0x2
field public static final int ACTIVE_DEVICE_AUDIO = 0; // 0x0
field public static final int ACTIVE_DEVICE_PHONE_CALL = 1; // 0x1
field public static final int OOB_ERROR_ADAPTER_DISABLED = 2; // 0x2
field public static final int OOB_ERROR_ANOTHER_ACTIVE_REQUEST = 1; // 0x1
field public static final int OOB_ERROR_UNKNOWN = 0; // 0x0
}
public static interface BluetoothAdapter.OnMetadataChangedListener {
@@ -2090,6 +2087,10 @@ package android.bluetooth {
field @Deprecated public static final int PRIORITY_ON = 100; // 0x64
}
public final class BluetoothStatusCodes {
field public static final int ERROR_ANOTHER_ACTIVE_OOB_REQUEST = 1000; // 0x3e8
}
public final class BluetoothUuid {
method public static boolean containsAnyUuid(@Nullable android.os.ParcelUuid[], @Nullable android.os.ParcelUuid[]);
method @NonNull public static android.os.ParcelUuid parseUuidFrom(@Nullable byte[]);
@@ -2245,7 +2246,7 @@ package android.companion {
public final class CompanionDeviceManager {
method @RequiresPermission(android.Manifest.permission.ASSOCIATE_COMPANION_DEVICES) public void associate(@NonNull String, @NonNull android.net.MacAddress);
method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean canPairWithoutPrompt(@NonNull String, @NonNull String, int);
method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean canPairWithoutPrompt(@NonNull String, @NonNull String, @NonNull android.os.UserHandle);
method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public boolean isDeviceAssociatedForWifiConnection(@NonNull String, @NonNull android.net.MacAddress, @NonNull android.os.UserHandle);
}

View File

@@ -3258,38 +3258,13 @@ public final class BluetoothAdapter {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = { "OOB_ERROR_" }, value = {
OOB_ERROR_UNKNOWN,
OOB_ERROR_ANOTHER_ACTIVE_REQUEST,
OOB_ERROR_ADAPTER_DISABLED
@IntDef(value = {
BluetoothStatusCodes.ERROR_UNKNOWN,
BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
BluetoothStatusCodes.ERROR_ANOTHER_ACTIVE_OOB_REQUEST,
})
public @interface OobError {}
/**
* An unknown error has occurred in the controller, stack, or callback pipeline.
*
* @hide
*/
@SystemApi
public static final int OOB_ERROR_UNKNOWN = 0;
/**
* If another application has already requested {@link OobData} then another fetch will be
* disallowed until the callback is removed.
*
* @hide
*/
@SystemApi
public static final int OOB_ERROR_ANOTHER_ACTIVE_REQUEST = 1;
/**
* The adapter is currently disabled, please enable it.
*
* @hide
*/
@SystemApi
public static final int OOB_ERROR_ADAPTER_DISABLED = 2;
/**
* Provides callback methods for receiving {@link OobData} from the host stack, as well as an
* error interface in order to allow the caller to determine next steps based on the {@code
@@ -3310,7 +3285,7 @@ public final class BluetoothAdapter {
/**
* Provides feedback when things don't go as expected.
*
* @param errorCode - the code descibing the type of error that occurred.
* @param errorCode - the code describing the type of error that occurred.
*/
void onError(@OobError int errorCode);
}
@@ -3407,7 +3382,7 @@ public final class BluetoothAdapter {
Preconditions.checkNotNull(callback);
if (!isEnabled()) {
Log.w(TAG, "generateLocalOobData(): Adapter isn't enabled!");
callback.onError(OOB_ERROR_ADAPTER_DISABLED);
callback.onError(BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED);
} else {
try {
mService.generateLocalOobData(transport, new WrappedOobDataCallback(callback,
@@ -4210,141 +4185,45 @@ public final class BluetoothAdapter {
*/
@Retention(RetentionPolicy.SOURCE)
@IntDef(prefix = { "REASON_" }, value = {
REASON_UNKNOWN,
REASON_LOCAL_REQUEST,
REASON_REMOTE_REQUEST,
REASON_LOCAL_ERROR,
REASON_REMOTE_ERROR,
REASON_TIMEOUT,
REASON_SECURITY,
REASON_SYSTEM_POLICY,
REASON_RESOURCE_LIMIT_REACHED,
REASON_CONNECTION_EXISTS,
REASON_BAD_PARAMETERS})
BluetoothStatusCodes.ERROR_UNKNOWN,
BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL_REQUEST,
BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE_REQUEST,
BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL,
BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE,
BluetoothStatusCodes.ERROR_DISCONNECT_REASON_TIMEOUT,
BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SECURITY,
BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SYSTEM_POLICY,
BluetoothStatusCodes.ERROR_DISCONNECT_REASON_RESOURCE_LIMIT_REACHED,
BluetoothStatusCodes.ERROR_DISCONNECT_REASON_CONNECTION_ALREADY_EXISTS,
BluetoothStatusCodes.ERROR_DISCONNECT_REASON_BAD_PARAMETERS})
public @interface DisconnectReason {}
/**
* Indicates that the ACL disconnected due to an unknown reason.
*/
public static final int REASON_UNKNOWN = 0;
/**
* Indicates that the ACL disconnected due to an explicit request from the local device.
* <p>
* Example cause: This is a normal disconnect reason, e.g., user/app initiates
* disconnection.
*/
public static final int REASON_LOCAL_REQUEST = 1;
/**
* Indicates that the ACL disconnected due to an explicit request from the remote device.
* <p>
* Example cause: This is a normal disconnect reason, e.g., user/app initiates
* disconnection.
* <p>
* Example solution: The app can also prompt the user to check their remote device.
*/
public static final int REASON_REMOTE_REQUEST = 2;
/**
* Generic disconnect reason indicating the ACL disconnected due to an error on the local
* device.
* <p>
* Example solution: Prompt the user to check their local device (e.g., phone, car
* headunit).
*/
public static final int REASON_LOCAL_ERROR = 3;
/**
* Generic disconnect reason indicating the ACL disconnected due to an error on the remote
* device.
* <p>
* Example solution: Prompt the user to check their remote device (e.g., headset, car
* headunit, watch).
*/
public static final int REASON_REMOTE_ERROR = 4;
/**
* Indicates that the ACL disconnected due to a timeout.
* <p>
* Example cause: remote device might be out of range.
* <p>
* Example solution: Prompt user to verify their remote device is on or in
* connection/pairing mode.
*/
public static final int REASON_TIMEOUT = 5;
/**
* Indicates that the ACL disconnected due to link key issues.
* <p>
* Example cause: Devices are either unpaired or remote device is refusing our pairing
* request.
* <p>
* Example solution: Prompt user to unpair and pair again.
*/
public static final int REASON_SECURITY = 6;
/**
* Indicates that the ACL disconnected due to the local device's system policy.
* <p>
* Example cause: privacy policy, power management policy, permissions, etc.
* <p>
* Example solution: Prompt the user to check settings, or check with their system
* administrator (e.g. some corp-managed devices do not allow OPP connection).
*/
public static final int REASON_SYSTEM_POLICY = 7;
/**
* Indicates that the ACL disconnected due to resource constraints, either on the local
* device or the remote device.
* <p>
* Example cause: controller is busy, memory limit reached, maximum number of connections
* reached.
* <p>
* Example solution: The app should wait and try again. If still failing, prompt the user
* to disconnect some devices, or toggle Bluetooth on the local and/or the remote device.
*/
public static final int REASON_RESOURCE_LIMIT_REACHED = 8;
/**
* Indicates that the ACL disconnected because another ACL connection already exists.
*/
public static final int REASON_CONNECTION_EXISTS = 9;
/**
* Indicates that the ACL disconnected due to incorrect parameters passed in from the app.
* <p>
* Example solution: Change parameters and try again. If error persists, the app can report
* telemetry and/or log the error in a bugreport.
*/
public static final int REASON_BAD_PARAMETERS = 10;
/**
* Returns human-readable strings corresponding to {@link DisconnectReason}.
*/
public static String disconnectReasonText(@DisconnectReason int reason) {
switch (reason) {
case REASON_UNKNOWN:
case BluetoothStatusCodes.ERROR_UNKNOWN:
return "Reason unknown";
case REASON_LOCAL_REQUEST:
case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL_REQUEST:
return "Local request";
case REASON_REMOTE_REQUEST:
case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE_REQUEST:
return "Remote request";
case REASON_LOCAL_ERROR:
case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_LOCAL:
return "Local error";
case REASON_REMOTE_ERROR:
case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_REMOTE:
return "Remote error";
case REASON_TIMEOUT:
case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_TIMEOUT:
return "Timeout";
case REASON_SECURITY:
case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SECURITY:
return "Security";
case REASON_SYSTEM_POLICY:
case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_SYSTEM_POLICY:
return "System policy";
case REASON_RESOURCE_LIMIT_REACHED:
case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_RESOURCE_LIMIT_REACHED:
return "Resource constrained";
case REASON_CONNECTION_EXISTS:
case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_CONNECTION_ALREADY_EXISTS:
return "Connection already exists";
case REASON_BAD_PARAMETERS:
case BluetoothStatusCodes.ERROR_DISCONNECT_REASON_BAD_PARAMETERS:
return "Bad parameters";
default:
return "Unrecognized disconnect reason: " + reason;

View File

@@ -1351,6 +1351,17 @@ public final class BluetoothDevice implements Parcelable {
return null;
}
/** @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 SetAliasReturnValues{}
/**
* Sets the locally modifiable name (alias) of the remote Bluetooth device. This method
* overwrites the previously stored alias. The new alias is saved in local
@@ -1358,34 +1369,35 @@ public final class BluetoothDevice implements Parcelable {
*
* <p>This method requires the calling app to be associated with Companion Device Manager (see
* {@link android.companion.CompanionDeviceManager#associate(AssociationRequest,
* android.companion.CompanionDeviceManager.Callback, Handler)}) and have the {@link
* android.Manifest.permission#BLUETOOTH} permission. Alternatively, if the caller has the
* {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission, they can bypass the
* Companion Device Manager association requirement.
* android.companion.CompanionDeviceManager.Callback, Handler)}) and have the
* {@link android.Manifest.permission#BLUETOOTH_CONNECT} permission. Alternatively, if the
* caller has the {@link android.Manifest.permission#BLUETOOTH_PRIVILEGED} permission, they can
* bypass the Companion Device Manager association requirement as well as other permission
* requirements.
*
* @param alias is the new locally modifiable name for the remote Bluetooth device which must be
* non-null and not the empty string.
* @return {@code true} if the alias is successfully set, {@code false} on error
* @throws IllegalArgumentException if the alias is {@code null} or the empty string
* @param alias is the new locally modifiable name for the remote Bluetooth device which must
* be the empty string. If null, we clear the alias.
* @return whether the alias was successfully changed
* @throws IllegalArgumentException if the alias is the empty string
*/
@RequiresLegacyBluetoothPermission
@RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public boolean setAlias(@NonNull String alias) {
if (alias == null || alias.isEmpty()) {
throw new IllegalArgumentException("Cannot set the alias to null or the empty string");
public @SetAliasReturnValues int setAlias(@Nullable String alias) {
if (alias != null && alias.isEmpty()) {
throw new IllegalArgumentException("alias cannot be the empty string");
}
final IBluetooth service = sService;
if (service == null) {
Log.e(TAG, "BT not enabled. Cannot set Remote Device name");
return false;
return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
}
try {
return service.setRemoteAlias(this, alias, mAttributionSource);
} catch (RemoteException e) {
Log.e(TAG, "", e);
throw e.rethrowFromSystemServer();
}
return false;
}
/**

View File

@@ -0,0 +1,205 @@
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.bluetooth;
import android.annotation.SystemApi;
/**
* 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
* occupies the max integer value.
*/
public final class BluetoothStatusCodes {
private BluetoothStatusCodes() {}
/**
* Indicates that the API call was successful
*/
public static final int SUCCESS = 0;
/**
* Error code indicating that Bluetooth is not enabled
*/
public static final int ERROR_BLUETOOTH_NOT_ENABLED = 1;
/**
* Error code indicating that the API call was initiated by neither the system nor the active
* Zuser
*/
public static final int ERROR_BLUETOOTH_NOT_ALLOWED = 2;
/**
* Error code indicating that the Bluetooth Device specified is not bonded
*/
public static final int ERROR_DEVICE_NOT_BONDED = 3;
/**
* Error code indicating that the Bluetooth Device specified is not connected, but is bonded
*
* @hide
*/
public static final int ERROR_DEVICE_NOT_CONNECTED = 4;
/**
* Error code indicating that the caller does not have the
* {@link android.Manifest.permission#BLUETOOTH_ADVERTISE} permission
*
* @hide
*/
public static final int ERROR_MISSING_BLUETOOTH_ADVERTISE_PERMISSION = 5;
/**
* Error code indicating that the caller does not have the
* {@link android.Manifest.permission#BLUETOOTH_CONNECT} permission
*/
public static final int ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION = 6;
/**
* Error code indicating that the caller does not have the
* {@link android.Manifest.permission#BLUETOOTH_SCAN} permission
*
* @hide
*/
public static final int ERROR_MISSING_BLUETOOTH_SCAN_PERMISSION = 7;
/**
* If another application has already requested {@link OobData} then another fetch will be
* disallowed until the callback is removed.
*
* @hide
*/
@SystemApi
public static final int ERROR_ANOTHER_ACTIVE_OOB_REQUEST = 1000;
/**
* Indicates that the ACL disconnected due to an explicit request from the local device.
* <p>
* Example cause: This is a normal disconnect reason, e.g., user/app initiates
* disconnection.
*
* @hide
*/
public static final int ERROR_DISCONNECT_REASON_LOCAL_REQUEST = 1100;
/**
* Indicates that the ACL disconnected due to an explicit request from the remote device.
* <p>
* Example cause: This is a normal disconnect reason, e.g., user/app initiates
* disconnection.
* <p>
* Example solution: The app can also prompt the user to check their remote device.
*
* @hide
*/
public static final int ERROR_DISCONNECT_REASON_REMOTE_REQUEST = 1101;
/**
* Generic disconnect reason indicating the ACL disconnected due to an error on the local
* device.
* <p>
* Example solution: Prompt the user to check their local device (e.g., phone, car
* headunit).
*
* @hide
*/
public static final int ERROR_DISCONNECT_REASON_LOCAL = 1102;
/**
* Generic disconnect reason indicating the ACL disconnected due to an error on the remote
* device.
* <p>
* Example solution: Prompt the user to check their remote device (e.g., headset, car
* headunit, watch).
*
* @hide
*/
public static final int ERROR_DISCONNECT_REASON_REMOTE = 1103;
/**
* Indicates that the ACL disconnected due to a timeout.
* <p>
* Example cause: remote device might be out of range.
* <p>
* Example solution: Prompt user to verify their remote device is on or in
* connection/pairing mode.
*
* @hide
*/
public static final int ERROR_DISCONNECT_REASON_TIMEOUT = 1104;
/**
* Indicates that the ACL disconnected due to link key issues.
* <p>
* Example cause: Devices are either unpaired or remote device is refusing our pairing
* request.
* <p>
* Example solution: Prompt user to unpair and pair again.
*
* @hide
*/
public static final int ERROR_DISCONNECT_REASON_SECURITY = 1105;
/**
* Indicates that the ACL disconnected due to the local device's system policy.
* <p>
* Example cause: privacy policy, power management policy, permissions, etc.
* <p>
* Example solution: Prompt the user to check settings, or check with their system
* administrator (e.g. some corp-managed devices do not allow OPP connection).
*
* @hide
*/
public static final int ERROR_DISCONNECT_REASON_SYSTEM_POLICY = 1106;
/**
* Indicates that the ACL disconnected due to resource constraints, either on the local
* device or the remote device.
* <p>
* Example cause: controller is busy, memory limit reached, maximum number of connections
* reached.
* <p>
* Example solution: The app should wait and try again. If still failing, prompt the user
* to disconnect some devices, or toggle Bluetooth on the local and/or the remote device.
*
* @hide
*/
public static final int ERROR_DISCONNECT_REASON_RESOURCE_LIMIT_REACHED = 1107;
/**
* Indicates that the ACL disconnected because another ACL connection already exists.
*
* @hide
*/
public static final int ERROR_DISCONNECT_REASON_CONNECTION_ALREADY_EXISTS = 1108;
/**
* Indicates that the ACL disconnected due to incorrect parameters passed in from the app.
* <p>
* Example solution: Change parameters and try again. If error persists, the app can report
* telemetry and/or log the error in a bugreport.
*
* @hide
*/
public static final int ERROR_DISCONNECT_REASON_BAD_PARAMETERS = 1109;
/**
* Indicates that an unknown error has occurred has occurred.
*/
public static final int ERROR_UNKNOWN = Integer.MAX_VALUE;
}

View File

@@ -338,21 +338,24 @@ public final class CompanionDeviceManager {
*
* @param packageName the package name of the calling app
* @param deviceMacAddress the bluetooth device's mac address
* @param userId the calling user's identifier
* @param user the user handle that currently hosts the package being queried for a companion
* device association
* @return true if it was recently associated and we can bypass the dialog, false otherwise
* @hide
*/
@SystemApi
@RequiresPermission(android.Manifest.permission.MANAGE_COMPANION_DEVICES)
public boolean canPairWithoutPrompt(@NonNull String packageName,
@NonNull String deviceMacAddress, int userId) {
@NonNull String deviceMacAddress, @NonNull UserHandle user) {
if (!checkFeaturePresent()) {
return false;
}
Objects.requireNonNull(packageName, "package name cannot be null");
Objects.requireNonNull(deviceMacAddress, "device mac address cannot be null");
Objects.requireNonNull(user, "user handle cannot be null");
try {
return mService.canPairWithoutPrompt(packageName, deviceMacAddress, userId);
return mService.canPairWithoutPrompt(packageName, deviceMacAddress,
user.getIdentifier());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}