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

Commit 3f957404 authored by Rahul Sabnis's avatar Rahul Sabnis Committed by Gerrit Code Review
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"
parents c3c3c2c3 6aac755a
Loading
Loading
Loading
Loading
+23 −0
Original line number Diff line number Diff line
@@ -639,6 +639,11 @@ public final class Utils {
        return false;
    }

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

    private static boolean checkCallerIsSystemOrActiveUser() {
        int callingUid = Binder.getCallingUid();
        UserHandle callingUser = UserHandle.getUserHandleForUid(callingUid);
@@ -660,6 +665,24 @@ public final class Utils {
        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) {
        if (context == null) {
            return checkCallerIsSystemOrActiveUser();
+100 −12
Original line number 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.SECOND_IN_MILLIS;

import static com.android.bluetooth.Utils.callerIsSystem;
import static com.android.bluetooth.Utils.callerIsSystemOrActiveOrManagedUser;
import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission;
import static com.android.bluetooth.Utils.enforceCdmAssociation;
@@ -283,6 +284,7 @@ public class AdapterService extends Service {
    private Set<IBluetoothConnectionCallback> mBluetoothConnectionCallbacks = new HashSet<>();
    private RemoteCallbackList<IBluetoothPreferredAudioProfilesCallback>
            mPreferredAudioProfilesCallbacks;
    private Set<BluetoothDevice> mDevicesPendingAudioProfileChanges = new HashSet<>();
    //Only BluetoothManagerService should be registered
    private RemoteCallbackList<IBluetoothCallback> mCallbacks;
    private int mCurrentRequestId;
@@ -4241,6 +4243,40 @@ public class AdapterService extends Service {
            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
        public void registerPreferredAudioProfilesChangedCallback(
                IBluetoothPreferredAudioProfilesCallback callback,
@@ -4380,6 +4416,11 @@ public class AdapterService extends Service {
            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
            Bundle strippedPreferences = new Bundle();
            if (modeToProfileBundle.containsKey(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY)
@@ -4393,9 +4434,56 @@ public class AdapterService extends Service {
                        modeToProfileBundle.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX));
            }

            mDevicesPendingAudioProfileChanges.add(groupLead);
            return mDatabaseManager.setPreferredAudioProfiles(groupLead,
                    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--------

+2 −0
Original line number Diff line number Diff line
@@ -68,6 +68,7 @@ package android.bluetooth {
    method public boolean isBleScanAlwaysAvailable();
    method public boolean isLeEnabled();
    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 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);
@@ -711,6 +712,7 @@ package android.bluetooth {
    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_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_DISCONNECTED = 1117; // 0x45d
    field public static final int ERROR_AUDIO_ROUTE_BLOCKED = 1118; // 0x45e
+51 −0
Original line number Diff line number Diff line
@@ -4979,6 +4979,7 @@ public final class BluetoothAdapter {
    @Retention(RetentionPolicy.SOURCE)
    @IntDef(value = {
            BluetoothStatusCodes.SUCCESS,
            BluetoothStatusCodes.ERROR_ANOTHER_ACTIVE_REQUEST,
            BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED,
            BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED,
            BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED,
@@ -5138,6 +5139,56 @@ public final class BluetoothAdapter {
        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")
    private final IBluetoothPreferredAudioProfilesCallback mPreferredAudioProfilesChangedCallback =
            new IBluetoothPreferredAudioProfilesCallback.Stub() {
+8 −1
Original line number Diff line number Diff line
@@ -25,7 +25,6 @@ import android.annotation.SystemApi;
 * occupies the max integer value.
 */
public final class BluetoothStatusCodes {

    private BluetoothStatusCodes() {}

    /**
@@ -223,6 +222,14 @@ public final class BluetoothStatusCodes {
    @SystemApi
    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.
     */
Loading