Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 60da0582 authored by Rahul Sabnis's avatar Rahul Sabnis Committed by Automerger Merge Worker
Browse files

Merge "Adds API for audio framework to notify BT that a preferred audio...

Merge "Adds API for audio framework to notify BT that a preferred audio profile change has taken effect" am: 3f957404 am: 0ce8789b

Original change: https://android-review.googlesource.com/c/platform/packages/modules/Bluetooth/+/2390970



Change-Id: I42b9f035668861ab1be56e17f115ed218bac8baf
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 497252f9 0ce8789b
Loading
Loading
Loading
Loading
+23 −0
Original line number Original line Diff line number Diff line
@@ -639,6 +639,11 @@ public final class Utils {
        return false;
        return false;
    }
    }


    private static boolean checkCallerIsSystem() {
        int callingUid = Binder.getCallingUid();
        return UserHandle.getAppId(Process.SYSTEM_UID) == UserHandle.getAppId(callingUid);
    }

    private static boolean checkCallerIsSystemOrActiveUser() {
    private static boolean checkCallerIsSystemOrActiveUser() {
        int callingUid = Binder.getCallingUid();
        int callingUid = Binder.getCallingUid();
        UserHandle callingUser = UserHandle.getUserHandleForUid(callingUid);
        UserHandle callingUser = UserHandle.getUserHandleForUid(callingUid);
@@ -660,6 +665,24 @@ public final class Utils {
        return checkCallerIsSystemOrActiveUser(tag + "." + method + "()");
        return checkCallerIsSystemOrActiveUser(tag + "." + method + "()");
    }
    }


    /**
     * Checks if the caller to the method is system server.
     *
     * @param tag the log tag to use in case the caller is not system server
     * @param method the API method name
     * @return {@code true} if the caller is system server, {@code false} otherwise
     */
    public static boolean callerIsSystem(String tag, String method) {
        if (isInstrumentationTestMode()) {
            return true;
        }
        final boolean res = checkCallerIsSystem();
        if (!res) {
            Log.w(TAG, tag + "." + method + "()" + " - Not allowed outside system server");
        }
        return res;
    }

    private static boolean checkCallerIsSystemOrActiveOrManagedUser(Context context) {
    private static boolean checkCallerIsSystemOrActiveOrManagedUser(Context context) {
        if (context == null) {
        if (context == null) {
            return checkCallerIsSystemOrActiveUser();
            return checkCallerIsSystemOrActiveUser();
+100 −12
Original line number Original line Diff line number Diff line
@@ -20,6 +20,7 @@ import static android.bluetooth.BluetoothDevice.TRANSPORT_AUTO;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.DateUtils.SECOND_IN_MILLIS;
import static android.text.format.DateUtils.SECOND_IN_MILLIS;


import static com.android.bluetooth.Utils.callerIsSystem;
import static com.android.bluetooth.Utils.callerIsSystemOrActiveOrManagedUser;
import static com.android.bluetooth.Utils.callerIsSystemOrActiveOrManagedUser;
import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission;
import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission;
import static com.android.bluetooth.Utils.enforceCdmAssociation;
import static com.android.bluetooth.Utils.enforceCdmAssociation;
@@ -283,6 +284,7 @@ public class AdapterService extends Service {
    private Set<IBluetoothConnectionCallback> mBluetoothConnectionCallbacks = new HashSet<>();
    private Set<IBluetoothConnectionCallback> mBluetoothConnectionCallbacks = new HashSet<>();
    private RemoteCallbackList<IBluetoothPreferredAudioProfilesCallback>
    private RemoteCallbackList<IBluetoothPreferredAudioProfilesCallback>
            mPreferredAudioProfilesCallbacks;
            mPreferredAudioProfilesCallbacks;
    private Set<BluetoothDevice> mDevicesPendingAudioProfileChanges = new HashSet<>();
    //Only BluetoothManagerService should be registered
    //Only BluetoothManagerService should be registered
    private RemoteCallbackList<IBluetoothCallback> mCallbacks;
    private RemoteCallbackList<IBluetoothCallback> mCallbacks;
    private int mCurrentRequestId;
    private int mCurrentRequestId;
@@ -4241,6 +4243,40 @@ public class AdapterService extends Service {
            return service.getPreferredAudioProfiles(device);
            return service.getPreferredAudioProfiles(device);
        }
        }


        @Override
        public void notifyPreferredAudioProfileChangeApplied(BluetoothDevice device,
                AttributionSource source, SynchronousResultReceiver receiver) {
            try {
                receiver.send(notifyPreferredAudioProfileChangeApplied(device, source));
            } catch (RuntimeException e) {
                receiver.propagateException(e);
            }
        }

        private int notifyPreferredAudioProfileChangeApplied(BluetoothDevice device,
                AttributionSource source) {
            AdapterService service = getService();
            if (service == null) {
                return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
            }
            if (!callerIsSystem(TAG, "setPreferredAudioProfiles")) {
                return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
            }
            Objects.requireNonNull(device);
            if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
                throw new IllegalArgumentException("device cannot have an invalid address");
            }
            if (service.getBondState(device) != BluetoothDevice.BOND_BONDED) {
                return BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED;
            }
            if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
                return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
            }
            enforceBluetoothPrivilegedPermission(service);

            return service.notifyPreferredAudioProfileChangeApplied(device);
        }

        @Override
        @Override
        public void registerPreferredAudioProfilesChangedCallback(
        public void registerPreferredAudioProfilesChangedCallback(
                IBluetoothPreferredAudioProfilesCallback callback,
                IBluetoothPreferredAudioProfilesCallback callback,
@@ -4380,6 +4416,11 @@ public class AdapterService extends Service {
            return BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED;
            return BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED;
        }
        }


        synchronized (mDevicesPendingAudioProfileChanges) {
            if (mDevicesPendingAudioProfileChanges.contains(groupLead)) {
                return BluetoothStatusCodes.ERROR_ANOTHER_ACTIVE_REQUEST;
            }

            // Copies relevant keys & values from modeToProfile bundle
            // Copies relevant keys & values from modeToProfile bundle
            Bundle strippedPreferences = new Bundle();
            Bundle strippedPreferences = new Bundle();
            if (modeToProfileBundle.containsKey(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY)
            if (modeToProfileBundle.containsKey(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY)
@@ -4393,9 +4434,56 @@ public class AdapterService extends Service {
                        modeToProfileBundle.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX));
                        modeToProfileBundle.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX));
            }
            }


            mDevicesPendingAudioProfileChanges.add(groupLead);
            return mDatabaseManager.setPreferredAudioProfiles(groupLead,
            return mDatabaseManager.setPreferredAudioProfiles(groupLead,
                    strippedPreferences);
                    strippedPreferences);
        }
        }
    }

    /**
     * Notification from the audio framework that a preferred audio profile change has taken effect.
     * See {@link BluetoothAdapter#notifyPreferredAudioProfileChangeApplied(BluetoothDevice)} for
     * more details.
     *
     * @param device the remote device whose preferred audio profiles have been changed
     * @return whether the Bluetooth stack acknowledged the change successfully
     */
    private int notifyPreferredAudioProfileChangeApplied(BluetoothDevice device) {
        // Gets the lead device in the CSIP group to set the preference
        BluetoothDevice groupLead = mLeAudioService.getLeadDevice(device);
        if (groupLead == null) {
            return BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED;
        }

        synchronized (mDevicesPendingAudioProfileChanges) {
            if (!mDevicesPendingAudioProfileChanges.contains(groupLead)) {
                Log.e(TAG, "notifyPreferredAudioProfileChangeApplied, but no pending request for "
                        + "device: " + groupLead);
                return BluetoothStatusCodes.ERROR_UNKNOWN;
            }

            if (mPreferredAudioProfilesCallbacks != null) {
                int n = mPreferredAudioProfilesCallbacks.beginBroadcast();
                debugLog("notifyPreferredAudioProfileChangeApplied() - Broadcasting audio profile "
                        + "change applied to device: " + groupLead + " to " + n + " receivers.");
                for (int i = 0; i < n; i++) {
                    try {
                        mPreferredAudioProfilesCallbacks.getBroadcastItem(i)
                                .onPreferredAudioProfilesChanged(device,
                                        getPreferredAudioProfiles(device),
                                        BluetoothStatusCodes.SUCCESS);
                    } catch (RemoteException e) {
                        debugLog("notifyPreferredAudioProfileChangeApplied() - Callback #" + i
                                + " failed (" + e + ")");
                    }
                }
                mPreferredAudioProfilesCallbacks.finishBroadcast();
            }
            mDevicesPendingAudioProfileChanges.remove(groupLead);
        }

        return BluetoothStatusCodes.SUCCESS;
    }


    // ----API Methods--------
    // ----API Methods--------


+2 −0
Original line number Original line Diff line number Diff line
@@ -68,6 +68,7 @@ package android.bluetooth {
    method public boolean isBleScanAlwaysAvailable();
    method public boolean isBleScanAlwaysAvailable();
    method public boolean isLeEnabled();
    method public boolean isLeEnabled();
    method @NonNull public static String nameForState(int);
    method @NonNull public static String nameForState(int);
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int notifyPreferredAudioProfileChangeApplied(@NonNull android.bluetooth.BluetoothDevice);
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean registerBluetoothConnectionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothAdapter.BluetoothConnectionCallback);
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public boolean registerBluetoothConnectionCallback(@NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothAdapter.BluetoothConnectionCallback);
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int registerPreferredAudioProfilesChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothAdapter.PreferredAudioProfilesChangedCallback);
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED}) public int registerPreferredAudioProfilesChangedCallback(@NonNull java.util.concurrent.Executor, @NonNull android.bluetooth.BluetoothAdapter.PreferredAudioProfilesChangedCallback);
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean removeActiveDevice(int);
    method @RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT, android.Manifest.permission.BLUETOOTH_PRIVILEGED, android.Manifest.permission.MODIFY_PHONE_STATE}) public boolean removeActiveDevice(int);
@@ -711,6 +712,7 @@ package android.bluetooth {
    field public static final int ALLOWED = 400; // 0x190
    field public static final int ALLOWED = 400; // 0x190
    field public static final int ERROR_ALREADY_IN_TARGET_STATE = 26; // 0x1a
    field public static final int ERROR_ALREADY_IN_TARGET_STATE = 26; // 0x1a
    field public static final int ERROR_ANOTHER_ACTIVE_OOB_REQUEST = 1000; // 0x3e8
    field public static final int ERROR_ANOTHER_ACTIVE_OOB_REQUEST = 1000; // 0x3e8
    field public static final int ERROR_ANOTHER_ACTIVE_REQUEST = 29; // 0x1d
    field public static final int ERROR_AUDIO_DEVICE_ALREADY_CONNECTED = 1116; // 0x45c
    field public static final int ERROR_AUDIO_DEVICE_ALREADY_CONNECTED = 1116; // 0x45c
    field public static final int ERROR_AUDIO_DEVICE_ALREADY_DISCONNECTED = 1117; // 0x45d
    field public static final int ERROR_AUDIO_DEVICE_ALREADY_DISCONNECTED = 1117; // 0x45d
    field public static final int ERROR_AUDIO_ROUTE_BLOCKED = 1118; // 0x45e
    field public static final int ERROR_AUDIO_ROUTE_BLOCKED = 1118; // 0x45e
+51 −0
Original line number Original line Diff line number Diff line
@@ -4979,6 +4979,7 @@ public final class BluetoothAdapter {
    @Retention(RetentionPolicy.SOURCE)
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(value = {
    @IntDef(value = {
            BluetoothStatusCodes.SUCCESS,
            BluetoothStatusCodes.SUCCESS,
            BluetoothStatusCodes.ERROR_ANOTHER_ACTIVE_REQUEST,
            BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
            BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
            BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED,
            BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED,
            BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED,
            BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED,
@@ -5138,6 +5139,56 @@ public final class BluetoothAdapter {
        return defaultValue;
        return defaultValue;
    }
    }


    /**
     * Called by audio framework to inform the Bluetooth stack that a request from
     * {@link #setPreferredAudioProfiles(BluetoothDevice, Bundle)} has taken effect in the audio
     * framework. After this is called, the Bluetooth stack will invoke
     * {@link PreferredAudioProfilesChangedCallback#onPreferredAudioProfilesChanged(
     * BluetoothDevice, Bundle, int)}.
     * <p>
     * This method will return
     * {@link BluetoothStatusCodes#ERROR_BLUETOOTH_NOT_ALLOWED} if called outside system server.
     *
     * @param device is the BluetoothDevice that had its preferred audio profile changed
     * @return whether the Bluetooth stack acknowledged the change successfully
     * @throws NullPointerException if device is null
     * @throws IllegalArgumentException if the device's address is invalid
     *
     * @hide
     */
    @SystemApi
    @RequiresPermission(allOf = {
            android.Manifest.permission.BLUETOOTH_CONNECT,
            android.Manifest.permission.BLUETOOTH_PRIVILEGED,
    })
    public int notifyPreferredAudioProfileChangeApplied(@NonNull BluetoothDevice device) {
        if (DBG) Log.d(TAG, "notifyPreferredProfileChangeApplied(" + device + ")");
        Objects.requireNonNull(device, "device cannot be null");
        if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
            throw new IllegalArgumentException("device cannot have an invalid address");
        }

        final int defaultValue = BluetoothStatusCodes.ERROR_UNKNOWN;
        mServiceLock.readLock().lock();
        try {
            if (mService != null) {
                final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get();
                mService.notifyPreferredAudioProfileChangeApplied(device,
                        mAttributionSource, recv);
                return recv.awaitResultNoInterrupt(getSyncTimeout()).getValue(defaultValue);
            }
        } catch (RemoteException e) {
            Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
            throw e.rethrowFromSystemServer();
        } catch (TimeoutException e) {
            Log.e(TAG, e.toString() + "\n" + Log.getStackTraceString(new Throwable()));
        } finally {
            mServiceLock.readLock().unlock();
        }

        return defaultValue;
    }

    @SuppressLint("AndroidFrameworkBluetoothPermission")
    @SuppressLint("AndroidFrameworkBluetoothPermission")
    private final IBluetoothPreferredAudioProfilesCallback mPreferredAudioProfilesChangedCallback =
    private final IBluetoothPreferredAudioProfilesCallback mPreferredAudioProfilesChangedCallback =
            new IBluetoothPreferredAudioProfilesCallback.Stub() {
            new IBluetoothPreferredAudioProfilesCallback.Stub() {
+8 −1
Original line number Original line Diff line number Diff line
@@ -25,7 +25,6 @@ import android.annotation.SystemApi;
 * occupies the max integer value.
 * occupies the max integer value.
 */
 */
public final class BluetoothStatusCodes {
public final class BluetoothStatusCodes {

    private BluetoothStatusCodes() {}
    private BluetoothStatusCodes() {}


    /**
    /**
@@ -223,6 +222,14 @@ public final class BluetoothStatusCodes {
    @SystemApi
    @SystemApi
    public static final int ERROR_CALLBACK_NOT_REGISTERED = 28;
    public static final int ERROR_CALLBACK_NOT_REGISTERED = 28;


    /**
     * Indicates that there is another active request and therefore, this operation is not allowed.
     *
     * @hide
     */
    @SystemApi
    public static final int ERROR_ANOTHER_ACTIVE_REQUEST = 29;

    /**
    /**
     * A GATT writeCharacteristic request is not permitted on the remote device.
     * A GATT writeCharacteristic request is not permitted on the remote device.
     */
     */
Loading