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

Commit bea47613 authored by Rongxuan Liu's avatar Rongxuan Liu
Browse files

Add new metadata API for active device preferrence

This API allows application to set preference if the device should
remain as inactive audio device upon connection.

Tag: #feature
Bug: 311243225
Bug: 322387487
Test: atest DatabaseManagerTest BluetoothDeviceTest
Change-Id: I20aeb3b9a232a5d6544795871a64c88e782e46c5
parent 4988757b
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -313,4 +313,10 @@ interface IBluetooth

    @JavaPassthrough(annotation="@android.annotation.RequiresNoPermission")
    oneway void getProfile(int profile, in SynchronousResultReceiver receiver);

    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
    oneway void setActiveAudioDevicePolicy(in BluetoothDevice device, int activeAudioDevicePolicy, in AttributionSource source, in SynchronousResultReceiver receiver);

    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(allOf={android.Manifest.permission.BLUETOOTH_CONNECT,android.Manifest.permission.BLUETOOTH_PRIVILEGED})")
    oneway void getActiveAudioDevicePolicy(in BluetoothDevice device, in AttributionSource source, in SynchronousResultReceiver receiver);
}
+77 −1
Original line number Diff line number Diff line
@@ -128,7 +128,6 @@ import com.android.bluetooth.csip.CsipSetCoordinatorService;
import com.android.bluetooth.flags.FeatureFlagsImpl;
import com.android.bluetooth.flags.Flags;
import com.android.bluetooth.gatt.GattService;
import com.android.bluetooth.le_scan.ScanManager;
import com.android.bluetooth.hap.HapClientService;
import com.android.bluetooth.hearingaid.HearingAidService;
import com.android.bluetooth.hfp.HeadsetService;
@@ -136,6 +135,7 @@ import com.android.bluetooth.hfpclient.HeadsetClientService;
import com.android.bluetooth.hid.HidDeviceService;
import com.android.bluetooth.hid.HidHostService;
import com.android.bluetooth.le_audio.LeAudioService;
import com.android.bluetooth.le_scan.ScanManager;
import com.android.bluetooth.map.BluetoothMapService;
import com.android.bluetooth.mapclient.MapClientService;
import com.android.bluetooth.mcp.McpService;
@@ -5389,6 +5389,82 @@ public class AdapterService extends Service {

            return service.getProfile(profileId);
        }

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

        @RequiresPermission(
                allOf = {
                    android.Manifest.permission.BLUETOOTH_CONNECT,
                    android.Manifest.permission.BLUETOOTH_PRIVILEGED,
                })
        private int setActiveAudioDevicePolicy(
                BluetoothDevice device, int activeAudioDevicePolicy, AttributionSource source) {
            AdapterService service = getService();
            if (service == null) {
                return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
            }
            if (!callerIsSystemOrActiveOrManagedUser(service, TAG, "setActiveAudioDevicePolicy")) {
                return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
            }
            if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
                throw new IllegalArgumentException("device cannot have an invalid address");
            }
            if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
                return BluetoothStatusCodes.ERROR_MISSING_BLUETOOTH_CONNECT_PERMISSION;
            }

            enforceBluetoothPrivilegedPermission(service);
            return service.mDatabaseManager.setActiveAudioDevicePolicy(
                    device, activeAudioDevicePolicy);
        }

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

        @RequiresPermission(
                allOf = {
                    android.Manifest.permission.BLUETOOTH_CONNECT,
                    android.Manifest.permission.BLUETOOTH_PRIVILEGED,
                })
        private int getActiveAudioDevicePolicy(BluetoothDevice device, AttributionSource source) {
            AdapterService service = getService();
            if (service == null) {
                return BluetoothDevice.ACTIVE_AUDIO_DEVICE_POLICY_DEFAULT;
            }
            if (!callerIsSystemOrActiveOrManagedUser(service, TAG, "getActiveAudioDevicePolicy")) {
                throw new IllegalStateException(
                        "Caller is not the system or part of the active/managed user");
            }
            if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
                throw new IllegalArgumentException("device cannot have an invalid address");
            }
            if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
                return BluetoothDevice.ACTIVE_AUDIO_DEVICE_POLICY_DEFAULT;
            }

            enforceBluetoothPrivilegedPermission(service);
            return service.mDatabaseManager.getActiveAudioDevicePolicy(device);
        }
    }

    /**
+54 −0
Original line number Diff line number Diff line
@@ -1014,6 +1014,60 @@ public class DatabaseManager {
        return modeToProfileBundle;
    }

    /**
     * Set the device active audio policy. See {@link
     * BluetoothDevice#setActiveAudioDevicePolicy(activeAudioDevicePolicy)} for more details.
     *
     * @param device is the remote device for which we are setting the active audio device policy.
     * @param activeAudioDevicePolicy active audio device policy.
     * @return whether the policy was set properly
     */
    public int setActiveAudioDevicePolicy(BluetoothDevice device, int activeAudioDevicePolicy) {
        synchronized (mMetadataCache) {
            String address = device.getAddress();

            if (!mMetadataCache.containsKey(address)) {
                Log.e(TAG, "device is not bonded");
                return BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED;
            }

            Metadata metadata = mMetadataCache.get(address);
            Log.i(
                    TAG,
                    "Updating active_audio_device_policy setting for "
                            + "device "
                            + device
                            + " to: "
                            + activeAudioDevicePolicy);
            metadata.active_audio_device_policy = activeAudioDevicePolicy;

            updateDatabase(metadata);
        }
        return BluetoothStatusCodes.SUCCESS;
    }

    /**
     * Get the active audio device policy for this device. See {@link
     * BluetoothDevice#getActiveAudioDevicePolicy()} for more details.
     *
     * @param device is the device for which we want to get the policy
     * @return active audio device policy for this device
     */
    public int getActiveAudioDevicePolicy(BluetoothDevice device) {
        synchronized (mMetadataCache) {
            String address = device.getAddress();

            if (!mMetadataCache.containsKey(address)) {
                Log.e(TAG, "device is not bonded");
                return BluetoothDevice.ACTIVE_AUDIO_DEVICE_POLICY_DEFAULT;
            }

            Metadata metadata = mMetadataCache.get(address);

            return metadata.active_audio_device_policy;
        }
    }

    /**
     * Get the {@link Looper} for the handler thread. This is used in testing and helper
     * objects
+4 −0
Original line number Diff line number Diff line
@@ -79,6 +79,9 @@ public class Metadata {
     */
    public int preferred_duplex_profile;

    /** This is used to indicate whether device's active audio policy */
    public int active_audio_device_policy;

    Metadata(String address) {
        this(address, false, false);
    }
@@ -96,6 +99,7 @@ public class Metadata {
        audioPolicyMetadata = new AudioPolicyEntity();
        preferred_output_only_profile = 0;
        preferred_duplex_profile = 0;
        active_audio_device_policy = BluetoothDevice.ACTIVE_AUDIO_DEVICE_POLICY_DEFAULT;
    }

    static final class Builder {
+23 −1
Original line number Diff line number Diff line
@@ -33,7 +33,7 @@ import java.util.List;
/** MetadataDatabase is a Room database stores Bluetooth persistence data */
@Database(
        entities = {Metadata.class},
        version = 119)
        version = 120)
public abstract class MetadataDatabase extends RoomDatabase {
    /** The metadata database file name */
    public static final String DATABASE_NAME = "bluetooth_db";
@@ -69,6 +69,7 @@ public abstract class MetadataDatabase extends RoomDatabase {
                .addMigrations(MIGRATION_116_117)
                .addMigrations(MIGRATION_117_118)
                .addMigrations(MIGRATION_118_119)
                .addMigrations(MIGRATION_119_120)
                .allowMainThreadQueries()
                .build();
    }
@@ -649,4 +650,25 @@ public abstract class MetadataDatabase extends RoomDatabase {
                    }
                }
            };

    @VisibleForTesting
    static final Migration MIGRATION_119_120 =
            new Migration(119, 120) {
                @Override
                public void migrate(SupportSQLiteDatabase database) {
                    try {
                        database.execSQL(
                                "ALTER TABLE metadata ADD COLUMN"
                                        + " `active_audio_device_policy` INTEGER NOT NULL"
                                        + " DEFAULT 0");
                    } catch (SQLException ex) {
                        // Check if user has new schema, but is just missing the version update
                        Cursor cursor = database.query("SELECT * FROM metadata");
                        if (cursor == null
                                || cursor.getColumnIndex("active_audio_device_policy") == -1) {
                            throw ex;
                        }
                    }
                }
            };
}
Loading