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

Commit c695e448 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add a "Use high quality audio" option to Bluetooth A2DP device settings" into oc-dev

parents a61f09f6 374d2598
Loading
Loading
Loading
Loading
+83 −1
Original line number Diff line number Diff line
@@ -19,6 +19,8 @@ package com.android.settingslib.bluetooth;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothCodecConfig;
import android.bluetooth.BluetoothCodecStatus;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
@@ -26,16 +28,22 @@ import android.content.Context;
import android.os.ParcelUuid;
import android.util.Log;

import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.R;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public final class A2dpProfile implements LocalBluetoothProfile {
public class A2dpProfile implements LocalBluetoothProfile {
    private static final String TAG = "A2dpProfile";
    private static boolean V = false;

    private Context mContext;

    private BluetoothA2dp mService;
    BluetoothA2dpWrapper.Factory mWrapperFactory;
    private BluetoothA2dpWrapper mServiceWrapper;
    private boolean mIsProfileReady;

    private final LocalBluetoothAdapter mLocalAdapter;
@@ -59,6 +67,7 @@ public final class A2dpProfile implements LocalBluetoothProfile {
        public void onServiceConnected(int profile, BluetoothProfile proxy) {
            if (V) Log.d(TAG,"Bluetooth service connected");
            mService = (BluetoothA2dp) proxy;
            mServiceWrapper = mWrapperFactory.getInstance(mService);
            // We just bound to the service, so refresh the UI for any connected A2DP devices.
            List<BluetoothDevice> deviceList = mService.getConnectedDevices();
            while (!deviceList.isEmpty()) {
@@ -88,11 +97,18 @@ public final class A2dpProfile implements LocalBluetoothProfile {
    A2dpProfile(Context context, LocalBluetoothAdapter adapter,
            CachedBluetoothDeviceManager deviceManager,
            LocalBluetoothProfileManager profileManager) {
        mContext = context;
        mLocalAdapter = adapter;
        mDeviceManager = deviceManager;
        mProfileManager = profileManager;
        mLocalAdapter.getProfileProxy(context, new A2dpServiceListener(),
                BluetoothProfile.A2DP);
        mWrapperFactory = new BluetoothA2dpWrapperImpl.Factory();
    }

    @VisibleForTesting
    void setWrapperFactory(BluetoothA2dpWrapper.Factory factory) {
        mWrapperFactory = factory;
    }

    public boolean isConnectable() {
@@ -173,6 +189,72 @@ public final class A2dpProfile implements LocalBluetoothProfile {
        return false;
    }

    public boolean supportsHighQualityAudio(BluetoothDevice device) {
        int support = mServiceWrapper.supportsOptionalCodecs(device);
        return support == BluetoothA2dp.OPTIONAL_CODECS_SUPPORTED;
    }

    public boolean isHighQualityAudioEnabled(BluetoothDevice device) {
        int enabled = mServiceWrapper.getOptionalCodecsEnabled(device);
        if (enabled != BluetoothA2dp.OPTIONAL_CODECS_PREF_UNKNOWN) {
            return enabled == BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED;
        } else if (getConnectionStatus(device) != BluetoothProfile.STATE_CONNECTED &&
                supportsHighQualityAudio(device)) {
            // Since we don't have a stored preference and the device isn't connected, just return
            // true since the default behavior when the device gets connected in the future would be
            // to have optional codecs enabled.
            return true;
        }
        BluetoothCodecConfig codecConfig = null;
        if (mServiceWrapper.getCodecStatus() != null) {
            codecConfig = mServiceWrapper.getCodecStatus().getCodecConfig();
        }
        if (codecConfig != null)  {
            return !codecConfig.isMandatoryCodec();
        } else {
            return false;
        }
    }

    public void setHighQualityAudioEnabled(BluetoothDevice device, boolean enabled) {
        int prefValue = enabled
                ? BluetoothA2dp.OPTIONAL_CODECS_PREF_ENABLED
                : BluetoothA2dp.OPTIONAL_CODECS_PREF_DISABLED;
        mServiceWrapper.setOptionalCodecsEnabled(device, prefValue);
        if (getConnectionStatus(device) != BluetoothProfile.STATE_CONNECTED) {
            return;
        }
        if (enabled) {
            mService.enableOptionalCodecs();
        } else {
            mService.disableOptionalCodecs();
        }
    }

    public String getHighQualityAudioOptionLabel(BluetoothDevice device) {
        int unknownCodecId = R.string.bluetooth_profile_a2dp_high_quality_unknown_codec;
        if (!supportsHighQualityAudio(device) ||
                getConnectionStatus(device) != BluetoothProfile.STATE_CONNECTED) {
            return mContext.getString(unknownCodecId);
        }
        // We want to get the highest priority codec, since that's the one that will be used with
        // this device, and see if it is high-quality (ie non-mandatory).
        BluetoothCodecConfig[] selectable = null;
        if (mServiceWrapper.getCodecStatus() != null) {
            selectable = mServiceWrapper.getCodecStatus().getCodecsSelectableCapabilities();
            // To get the highest priority, we sort in reverse.
            Arrays.sort(selectable,
                    (a, b) -> {
                        return b.getCodecPriority() - a.getCodecPriority();
                    });
        }
        if (selectable == null || selectable.length < 1 || selectable[0].isMandatoryCodec()) {
            return mContext.getString(unknownCodecId);
        }
        return mContext.getString(R.string.bluetooth_profile_a2dp_high_quality,
                selectable[0].getCodecName());
    }

    public String toString() {
        return NAME;
    }
+58 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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 com.android.settingslib.bluetooth;

import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothCodecStatus;
import android.bluetooth.BluetoothDevice;

/**
 * This interface replicates some methods of android.bluetooth.BluetoothA2dp that are new and not
 * yet available in our current version of  Robolectric. It provides a thin wrapper to call the real
 * methods in production and a mock in tests.
 */
public interface BluetoothA2dpWrapper {

    static interface Factory {
        BluetoothA2dpWrapper getInstance(BluetoothA2dp service);
    }

    /**
     * @return the real {@code BluetoothA2dp} object
     */
    BluetoothA2dp getService();

    /**
     * Wraps {@code BluetoothA2dp.getCodecStatus}
     */
    public BluetoothCodecStatus getCodecStatus();

    /**
     * Wraps {@code BluetoothA2dp.supportsOptionalCodecs}
     */
    int supportsOptionalCodecs(BluetoothDevice device);

    /**
     * Wraps {@code BluetoothA2dp.getOptionalCodecsEnabled}
     */
    int getOptionalCodecsEnabled(BluetoothDevice device);

    /**
     * Wraps {@code BluetoothA2dp.setOptionalCodecsEnabled}
     */
    void setOptionalCodecsEnabled(BluetoothDevice device, int value);
}
+62 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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 com.android.settingslib.bluetooth;

import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothCodecStatus;
import android.bluetooth.BluetoothDevice;

public class BluetoothA2dpWrapperImpl implements BluetoothA2dpWrapper {

    public static class Factory implements BluetoothA2dpWrapper.Factory {
        @Override
        public BluetoothA2dpWrapper getInstance(BluetoothA2dp service) {
            return new BluetoothA2dpWrapperImpl(service);
        }
    }

    private BluetoothA2dp mService;

    public BluetoothA2dpWrapperImpl(BluetoothA2dp service) {
        mService = service;
    }

    @Override
    public BluetoothA2dp getService() {
        return mService;
    }

    @Override
    public BluetoothCodecStatus getCodecStatus() {
        return mService.getCodecStatus();
    }

    @Override
    public int supportsOptionalCodecs(BluetoothDevice device) {
        return mService.supportsOptionalCodecs(device);
    }

    @Override
    public int getOptionalCodecsEnabled(BluetoothDevice device) {
        return mService.getOptionalCodecsEnabled(device);
    }

    @Override
    public void setOptionalCodecsEnabled(BluetoothDevice device, int value) {
        mService.setOptionalCodecsEnabled(device, value);
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -42,7 +42,7 @@ import java.util.Map;
 * LocalBluetoothProfileManager provides access to the LocalBluetoothProfile
 * objects for the available Bluetooth profiles.
 */
public final class LocalBluetoothProfileManager {
public class LocalBluetoothProfileManager {
    private static final String TAG = "LocalBluetoothProfileManager";
    private static final boolean DEBUG = Utils.D;
    /** Singleton instance. */
+25 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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;

/**
 * A placeholder class to prevent ClassNotFound exceptions caused by lack of visibility.
 */
public class BluetoothCodecConfig {
    public boolean isMandatoryCodec() { return true; }
    public String getCodecName() { return null;}
}
Loading