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

Commit 86d56ed9 authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge "Add APIs to set and get the preferred audio mode"

parents 920db440 1097fafc
Loading
Loading
Loading
Loading
+67 −0
Original line number Diff line number Diff line
@@ -4062,6 +4062,73 @@ public class AdapterService extends Service {
            enforceBluetoothPrivilegedPermission(service);
            Utils.setForegroundUserId(userId);
        }

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

        private int setPreferredAudioProfiles(BluetoothDevice device, Bundle modeToProfileBundle,
                AttributionSource source) {
            AdapterService service = getService();
            if (service == null) {
                return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ENABLED;
            }
            if (!callerIsSystemOrActiveOrManagedUser(service, TAG, "setPreferredAudioProfiles")) {
                return BluetoothStatusCodes.ERROR_BLUETOOTH_NOT_ALLOWED;
            }
            if (device == null) {
                throw new IllegalArgumentException("device cannot be null");
            }
            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.setPreferredAudioProfiles(device, modeToProfileBundle);
        }

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

        private Bundle getPreferredAudioProfiles(BluetoothDevice device,
                AttributionSource source) {
            AdapterService service = getService();
            if (service == null) {
                return Bundle.EMPTY;
            }
            if (!callerIsSystemOrActiveOrManagedUser(service, TAG, "getPreferredAudioProfiles")) {
                throw new IllegalStateException("Caller is not the system or part of the "
                        + "active/managed user");
            }
            if (device == null) {
                throw new IllegalArgumentException("device cannot be null");
            }
            if (!BluetoothAdapter.checkBluetoothAddress(device.getAddress())) {
                throw new IllegalArgumentException("device cannot have an invalid address");
            }
            if (!Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) {
                return Bundle.EMPTY;
            }

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

    // ----API Methods--------
+87 −0
Original line number Diff line number Diff line
@@ -24,12 +24,14 @@ import android.bluetooth.BluetoothAudioPolicy;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothProtoEnums;
import android.bluetooth.BluetoothStatusCodes;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -771,6 +773,91 @@ public class DatabaseManager {
        }
    }

    /**
     * Sets the preferred profile for the supplied audio modes. See
     * {@link BluetoothDevice#setPreferredAudioProfiles(Bundle)} for more details.
     *
     * @param device is the remote device for which we are setting the preferred audio profiles
     * @param modeToProfileBundle contains the preferred profile
     * @return
     */
    public int setPreferredAudioProfiles(BluetoothDevice device, Bundle modeToProfileBundle) {
        synchronized (mMetadataCache) {
            if (device == null) {
                Log.e(TAG, "setPreferredAudioProfiles: device is null");
                throw new IllegalArgumentException("setPreferredAudioProfiles: device is null");
            }

            String address = device.getAddress();

            if (!mMetadataCache.containsKey(address)) {
                return BluetoothStatusCodes.ERROR_DEVICE_NOT_BONDED;
            }

            // Updates preferred audio profiles for the device
            Metadata metadata = mMetadataCache.get(address);
            int outputProfile = modeToProfileBundle.getInt(BluetoothDevice.AUDIO_MODE_OUTPUT_ONLY);
            int duplexProfile = modeToProfileBundle.getInt(BluetoothDevice.AUDIO_MODE_DUPLEX);
            if (outputProfile != 0) {
                Log.i(TAG, "setPreferredAudioProfiles: Updating output only audio profile for "
                        + "device: " + device + " to "
                        + BluetoothProfile.getProfileName(outputProfile));
                metadata.preferred_output_only_profile = outputProfile;
            }
            if (duplexProfile != 0) {
                Log.i(TAG, "setPreferredAudioProfiles: Updating duplex audio profile for device: "
                        + device + " to " + BluetoothProfile.getProfileName(duplexProfile));
                metadata.preferred_duplex_profile = duplexProfile;
            }

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

    /**
     * Sets the preferred profile for the supplied audio modes. See
     * {@link BluetoothDevice#getPreferredAudioProfiles()} for more details.
     *
     * @param device is the device for which we want to get the preferred audio profiles
     * @return a Bundle containing the preferred audio profiles
     */
    public Bundle getPreferredAudioProfiles(BluetoothDevice device) {
        synchronized (mMetadataCache) {
            if (device == null) {
                Log.e(TAG, "getPreferredAudioProfiles: device is null");
                throw new IllegalArgumentException("getPreferredAudioProfiles: device is null");
            }

            String address = device.getAddress();

            if (!mMetadataCache.containsKey(address)) {
                return Bundle.EMPTY;
            }

            // Gets the preferred audio profiles for each audio mode
            Metadata metadata = mMetadataCache.get(address);
            int outputOnlyProfile = metadata.preferred_output_only_profile;
            int duplexProfile = metadata.preferred_duplex_profile;

            // Checks if the default values are present (aka no explicit preference)
            if (outputOnlyProfile == 0 && duplexProfile == 0) {
                return Bundle.EMPTY;
            }

            Bundle modeToProfileBundle = new Bundle();
            if (outputOnlyProfile != 0) {
                modeToProfileBundle.putInt(BluetoothDevice.AUDIO_MODE_OUTPUT_ONLY,
                        outputOnlyProfile);
            }
            if (duplexProfile != 0) {
                modeToProfileBundle.putInt(BluetoothDevice.AUDIO_MODE_DUPLEX, duplexProfile);
            }

            return modeToProfileBundle;
        }
    }

    /**
     * Get the {@link Looper} for the handler thread. This is used in testing and helper
     * objects
+18 −0
Original line number Diff line number Diff line
@@ -54,6 +54,22 @@ class Metadata {
    @Embedded
    public AudioPolicyEntity audioPolicyMetadata;

    /**
     * The preferred profile to be used for {@link BluetoothDevice#AUDIO_MODE_OUTPUT_ONLY}. This can
     * be either {@link BluetoothProfile#A2DP} or {@link BluetoothProfile#LE_AUDIO}. This value is
     * only used if the remote device supports both A2DP and LE Audio and both transports are
     * connected and active.
     */
    public int preferred_output_only_profile;

    /**
     * The preferred profile to be used for {@link BluetoothDevice#AUDIO_MODE_DUPLEX}. This can
     * be either {@link BluetoothProfile#HEADSET} or {@link BluetoothProfile#LE_AUDIO}. This value
     * is only used if the remote device supports both HFP and LE Audio and both transports are
     * connected and active.
     */
    public int preferred_duplex_profile;

    Metadata(String address) {
        this.address = address;
        migrated = false;
@@ -64,6 +80,8 @@ class Metadata {
        last_active_time = MetadataDatabase.sCurrentConnectionNumber++;
        is_active_a2dp_device = true;
        audioPolicyMetadata = new AudioPolicyEntity();
        preferred_output_only_profile = 0;
        preferred_duplex_profile = 0;
    }

    String getAddress() {
+22 −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 = 115)
@Database(entities = {Metadata.class}, version = 116)
public abstract class MetadataDatabase extends RoomDatabase {
    /**
     * The metadata database file name
@@ -526,4 +526,25 @@ public abstract class MetadataDatabase extends RoomDatabase {
            }
        }
    };

    @VisibleForTesting
    static final Migration MIGRATION_115_116 = new Migration(115, 116) {
        @Override
        public void migrate(SupportSQLiteDatabase database) {
            try {
                database.execSQL("ALTER TABLE metadata ADD COLUMN `preferred_output_only_profile` "
                        + "INTEGER NOT NULL DEFAULT 0");
                database.execSQL("ALTER TABLE metadata ADD COLUMN `preferred_duplex_profile` "
                        + "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("preferred_output_only_profile") == -1
                        || cursor.getColumnIndex("preferred_duplex_profile") == -1) {
                    throw ex;
                }
            }
        }
    };
}
+31 −4
Original line number Diff line number Diff line
@@ -1158,7 +1158,7 @@ public final class DatabaseManagerTest {
    @Test
    public void testDatabaseMigration_111_112() throws IOException {
        String testString = "TEST STRING";
        // Create a database with version 109
        // Create a database with version 111
        SupportSQLiteDatabase db = testHelper.createDatabase(DB_NAME, 111);
        // insert a device to the database
        ContentValues device = new ContentValues();
@@ -1204,7 +1204,7 @@ public final class DatabaseManagerTest {

    @Test
    public void testDatabaseMigration_113_114() throws IOException {
        // Create a database with version 112
        // Create a database with version 113
        SupportSQLiteDatabase db = testHelper.createDatabase(DB_NAME, 113);
        // insert a device to the database
        ContentValues device = new ContentValues();
@@ -1226,7 +1226,7 @@ public final class DatabaseManagerTest {

    @Test
    public void testDatabaseMigration_114_115() throws IOException {
        // Create a database with version 112
        // Create a database with version 114
        SupportSQLiteDatabase db = testHelper.createDatabase(DB_NAME, 114);
        // insert a device to the database
        ContentValues device = new ContentValues();
@@ -1234,11 +1234,13 @@ public final class DatabaseManagerTest {
        device.put("migrated", false);
        assertThat(db.insert("metadata", SQLiteDatabase.CONFLICT_IGNORE, device),
                CoreMatchers.not(-1));
        // Migrate database from 112 to 113

        // Migrate database from 114 to 115
        db.close();
        db = testHelper.runMigrationsAndValidate(DB_NAME, 115, true,
                MetadataDatabase.MIGRATION_114_115);
        Cursor cursor = db.query("SELECT * FROM metadata");

        assertHasColumn(cursor, "call_establish_audio_policy", true);
        assertHasColumn(cursor, "connecting_time_audio_policy", true);
        assertHasColumn(cursor, "in_band_ringtone_audio_policy", true);
@@ -1250,6 +1252,31 @@ public final class DatabaseManagerTest {
        }
    }

    @Test
    public void testDatabaseMigration_115_116() throws IOException {
        // Create a database with version 115
        SupportSQLiteDatabase db = testHelper.createDatabase(DB_NAME, 115);
        // insert a device to the database
        ContentValues device = new ContentValues();
        device.put("address", TEST_BT_ADDR);
        device.put("migrated", false);
        assertThat(db.insert("metadata", SQLiteDatabase.CONFLICT_IGNORE, device),
                CoreMatchers.not(-1));

        // Migrate database from 115 to 116
        db.close();
        db = testHelper.runMigrationsAndValidate(DB_NAME, 116, true,
                MetadataDatabase.MIGRATION_115_116);
        Cursor cursor = db.query("SELECT * FROM metadata");
        assertHasColumn(cursor, "preferred_output_only_profile", true);
        assertHasColumn(cursor, "preferred_duplex_profile", true);
        while (cursor.moveToNext()) {
            // Check the new columns was added with default value
            assertColumnIntData(cursor, "preferred_output_only_profile", 0);
            assertColumnIntData(cursor, "preferred_duplex_profile", 0);
        }
    }

    /**
     * Helper function to check whether the database has the expected column
     */
Loading