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

Commit 0cbc7f6c authored by Jeffrey Huang's avatar Jeffrey Huang Committed by Android (Google) Code Review
Browse files

Merge "Introduce BluetoothAudioSampleRatePreferenceCtrl"

parents 82102a28 d229c251
Loading
Loading
Loading
Loading
+108 −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.settings.development;

import android.bluetooth.BluetoothCodecConfig;

/**
 * Utility class for storing current Bluetooth A2DP profile values
 */
public class BluetoothA2dpSharedStore {

    // init default values
    private static int sCodecType = BluetoothCodecConfig.SOURCE_CODEC_TYPE_INVALID;
    private static int sCodecPriority = BluetoothCodecConfig.CODEC_PRIORITY_DEFAULT;
    private static int sSampleRate = BluetoothCodecConfig.SAMPLE_RATE_NONE;
    private static int sBitsPerSample = BluetoothCodecConfig.BITS_PER_SAMPLE_NONE;
    private static int sChannelMode = BluetoothCodecConfig.CHANNEL_MODE_NONE;
    private static long sCodecSpecific1Value = 0;
    private static long sCodecSpecific2Value = 0;
    private static long sCodecSpecific3Value = 0;
    private static long sCodecSpecific4Value = 0;

    public static int getCodecType() {
        return sCodecType;
    }

    public static int getCodecPriority() {
        return sCodecPriority;
    }

    public static int getSampleRate() {
        return sSampleRate;
    }

    public static int getBitsPerSample() {
        return sBitsPerSample;
    }

    public static int getChannelMode() {
        return sChannelMode;
    }

    public static long getCodecSpecific1Value() {
        return sCodecSpecific1Value;
    }

    public static long getCodecSpecific2Value() {
        return sCodecSpecific2Value;
    }

    public static long getCodecSpecific3Value() {
        return sCodecSpecific3Value;
    }

    public static long getCodecSpecific4Value() {
        return sCodecSpecific4Value;
    }

    public static void setCodecType(int codecType) {
        sCodecType = codecType;
    }

    public static void setCodecPriority(int codecPriority) {
        sCodecPriority = codecPriority;
    }

    public static void setSampleRate(int sampleRate) {
        sSampleRate = sampleRate;
    }

    public static void setBitsPerSample(int bitsPerSample) {
        sBitsPerSample = bitsPerSample;
    }

    public static void setChannelMode(int channelMode) {
        sChannelMode = channelMode;
    }

    public static void setCodecSpecific1Value(int codecSpecific1Value) {
        sCodecSpecific1Value = codecSpecific1Value;
    }

    public static void setCodecSpecific2Value(int codecSpecific2Value) {
        sCodecSpecific2Value = codecSpecific2Value;
    }

    public static void setCodecSpecific3Value(int codecSpecific3Value) {
        sCodecSpecific3Value = codecSpecific3Value;
    }

    public static void setCodecSpecific4Value(int codecSpecific4Value) {
        sCodecSpecific4Value = codecSpecific4Value;
    }
}
+243 −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.settings.development;

import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothCodecConfig;
import android.content.Context;
import android.support.annotation.VisibleForTesting;
import android.support.v7.preference.ListPreference;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceScreen;

import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnDestroy;
import com.android.settingslib.development.DeveloperOptionsPreferenceController;

public class BluetoothAudioSampleRatePreferenceController extends
        DeveloperOptionsPreferenceController implements Preference.OnPreferenceChangeListener,
        PreferenceControllerMixin, BluetoothServiceConnectionListener, LifecycleObserver,
        OnDestroy {

    private static final String BLUETOOTH_SELECT_A2DP_SAMPLE_RATE_KEY =
            "bluetooth_select_a2dp_sample_rate";

    @VisibleForTesting
    static final int STREAMING_LABEL_ID = R.string.bluetooth_select_a2dp_codec_streaming_label;

    private final String[] mListValues;
    private final String[] mListSummaries;
    private final Object mBluetoothA2dpLock;
    private ListPreference mPreference;
    private BluetoothA2dp mBluetoothA2dp;

    public BluetoothAudioSampleRatePreferenceController(Context context, Lifecycle lifecycle,
            Object bluetoothA2dpLock) {
        super(context);

        mBluetoothA2dpLock = bluetoothA2dpLock;
        mListValues = context.getResources().getStringArray(
                R.array.bluetooth_a2dp_codec_sample_rate_values);
        mListSummaries = context.getResources().getStringArray(
                R.array.bluetooth_a2dp_codec_sample_rate_summaries);

        if (lifecycle != null) {
            lifecycle.addObserver(this);
        }
    }


    @Override
    public String getPreferenceKey() {
        return BLUETOOTH_SELECT_A2DP_SAMPLE_RATE_KEY;
    }

    @Override
    public void displayPreference(PreferenceScreen screen) {
        super.displayPreference(screen);

        mPreference = (ListPreference) screen.findPreference(getPreferenceKey());
    }

    @Override
    public boolean onPreferenceChange(Preference preference, Object newValue) {
        if (mBluetoothA2dp == null) {
            return false;
        }

        final int sampleRate = mapPreferenceValueToSampleRate(newValue.toString());
        BluetoothA2dpSharedStore.setSampleRate(sampleRate);

        final int codecTypeValue = BluetoothA2dpSharedStore.getCodecType();
        final int codecPriorityValue = BluetoothA2dpSharedStore.getCodecPriority();
        final int sampleRateValue = BluetoothA2dpSharedStore.getSampleRate();
        final int bitsPerSampleValue = BluetoothA2dpSharedStore.getBitsPerSample();
        final int channelModeValue = BluetoothA2dpSharedStore.getChannelMode();
        final long codecSpecific1Value = BluetoothA2dpSharedStore.getCodecSpecific1Value();
        final long codecSpecific2Value = BluetoothA2dpSharedStore.getCodecSpecific2Value();
        final long codecSpecific3Value = BluetoothA2dpSharedStore.getCodecSpecific3Value();
        final long codecSpecific4Value = BluetoothA2dpSharedStore.getCodecSpecific4Value();

        // get values from shared store
        BluetoothCodecConfig codecConfig = createCodecConfig(codecTypeValue, codecPriorityValue,
                sampleRateValue, bitsPerSampleValue,
                channelModeValue, codecSpecific1Value,
                codecSpecific2Value, codecSpecific3Value,
                codecSpecific4Value);

        synchronized (mBluetoothA2dpLock) {
            if (mBluetoothA2dp != null) {
                setCodecConfigPreference(codecConfig);
            }
        }
        updateState(mPreference);
        return true;
    }

    @Override
    public void updateState(Preference preference) {
        if (getCodecConfig() == null || mPreference == null) {
            return;
        }

        BluetoothCodecConfig codecConfig;
        synchronized (mBluetoothA2dpLock) {
            codecConfig = getCodecConfig();
        }
        final int sampleRate = codecConfig.getSampleRate();
        final int index = mapSampleRateToIndex(sampleRate);

        mPreference.setValue(mListValues[index]);
        mPreference.setSummary(
                mContext.getResources().getString(STREAMING_LABEL_ID, mListSummaries[index]));

        // write value to shared store
        BluetoothA2dpSharedStore.setSampleRate(sampleRate);
    }

    @Override
    public void onBluetoothServiceConnected(BluetoothA2dp bluetoothA2dp) {
        mBluetoothA2dp = bluetoothA2dp;
        updateState(mPreference);
    }

    @Override
    public void onBluetoothCodecUpdated() {
        updateState(mPreference);
    }

    @Override
    public void onBluetoothServiceDisconnected() {
        mBluetoothA2dp = null;
    }

    @Override
    public void onDestroy() {
        mBluetoothA2dp = null;
    }

    @Override
    protected void onDeveloperOptionsSwitchEnabled() {
        mPreference.setEnabled(true);
    }

    @Override
    protected void onDeveloperOptionsSwitchDisabled() {
        mPreference.setEnabled(false);
    }

    private int mapSampleRateToIndex(int sampleRate) {
        int index = 0;
        switch (sampleRate) {
            case BluetoothCodecConfig.SAMPLE_RATE_44100:
                index = 1;
                break;
            case BluetoothCodecConfig.SAMPLE_RATE_48000:
                index = 2;
                break;
            case BluetoothCodecConfig.SAMPLE_RATE_88200:
                index = 3;
                break;
            case BluetoothCodecConfig.SAMPLE_RATE_96000:
                index = 4;
                break;
            case BluetoothCodecConfig.SAMPLE_RATE_176400:
            case BluetoothCodecConfig.SAMPLE_RATE_192000:
            case BluetoothCodecConfig.SAMPLE_RATE_NONE:
            default:
                break;
        }
        return index;
    }

    private int mapPreferenceValueToSampleRate(String value) {
        final int index = mPreference.findIndexOfValue(value);
        int sampleRateValue = 0;
        switch (index) {
            case 0:
                // Reset to default
                sampleRateValue = BluetoothCodecConfig.SAMPLE_RATE_NONE;
                break;
            case 1:
                sampleRateValue = BluetoothCodecConfig.SAMPLE_RATE_44100;
                break;
            case 2:
                sampleRateValue = BluetoothCodecConfig.SAMPLE_RATE_48000;
                break;
            case 3:
                sampleRateValue = BluetoothCodecConfig.SAMPLE_RATE_88200;
                break;
            case 4:
                sampleRateValue = BluetoothCodecConfig.SAMPLE_RATE_96000;
                break;
            default:
                break;
        }
        return sampleRateValue;
    }

    @VisibleForTesting
    void setCodecConfigPreference(BluetoothCodecConfig config) {
        mBluetoothA2dp.setCodecConfigPreference(config);
    }

    @VisibleForTesting
    BluetoothCodecConfig getCodecConfig() {
        if (mBluetoothA2dp == null || mBluetoothA2dp.getCodecStatus() == null) {
            return null;
        }

        return mBluetoothA2dp.getCodecStatus().getCodecConfig();
    }

    @VisibleForTesting
    BluetoothCodecConfig createCodecConfig(int codecTypeValue, int codecPriorityValue,
            int sampleRateValue, int bitsPerSampleValue,
            int channelModeValue, long codecSpecific1Value,
            long codecSpecific2Value, long codecSpecific3Value,
            long codecSpecific4Value) {
        return new BluetoothCodecConfig(codecTypeValue, codecPriorityValue,
                sampleRateValue, bitsPerSampleValue,
                channelModeValue, codecSpecific1Value,
                codecSpecific2Value, codecSpecific3Value,
                codecSpecific4Value);
    }

}
+41 −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.settings.development;

import android.bluetooth.BluetoothA2dp;

/**
 * Interface for callbacks about bluetooth connectivity.
 */
public interface BluetoothServiceConnectionListener {

    /**
     * Called when the bluetooth service is connected.
     * @param bluetoothA2dp controller for Bluetooth A2DP profile.
     */
    void onBluetoothServiceConnected(BluetoothA2dp bluetoothA2dp);

    /**
     * Called when the bluetooth codec configuration is changed.
     */
    void onBluetoothCodecUpdated();

    /**
     * Called with the bluetooth service is disconnected.
     */
    void onBluetoothServiceDisconnected();
}
+81 −4
Original line number Diff line number Diff line
@@ -17,6 +17,10 @@
package com.android.settings.development;

import android.app.Activity;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothCodecStatus;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -56,10 +60,13 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra

    private static final String TAG = "DevSettingsDashboard";

    private final Object mBluetoothA2dpLock = new Object();

    private boolean mIsAvailable = true;
    private SwitchBar mSwitchBar;
    private DevelopmentSwitchBarController mSwitchBarController;
    private List<AbstractPreferenceController> mPreferenceControllers = new ArrayList<>();
    private BluetoothA2dp mBluetoothA2dp;

    private final BroadcastReceiver mEnableAdbReceiver = new BroadcastReceiver() {
        @Override
@@ -72,6 +79,56 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
        }
    };

    private final BroadcastReceiver mBluetoothA2dpReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.d(TAG, "mBluetoothA2dpReceiver.onReceive intent=" + intent);
            String action = intent.getAction();

            if (BluetoothA2dp.ACTION_CODEC_CONFIG_CHANGED.equals(action)) {
                BluetoothCodecStatus codecStatus = intent.getParcelableExtra(
                        BluetoothCodecStatus.EXTRA_CODEC_STATUS);
                Log.d(TAG, "Received BluetoothCodecStatus=" + codecStatus);
                for (AbstractPreferenceController controller : mPreferenceControllers) {
                    if (controller instanceof BluetoothServiceConnectionListener) {
                        ((BluetoothServiceConnectionListener) controller).onBluetoothCodecUpdated();
                    }
                }
            }
        }
    };


    private final BluetoothProfile.ServiceListener mBluetoothA2dpServiceListener =
            new BluetoothProfile.ServiceListener() {
                @Override
                public void onServiceConnected(int profile,
                        BluetoothProfile proxy) {
                    synchronized (mBluetoothA2dpLock) {
                        mBluetoothA2dp = (BluetoothA2dp) proxy;
                    }
                    for (AbstractPreferenceController controller : mPreferenceControllers) {
                        if (controller instanceof BluetoothServiceConnectionListener) {
                            ((BluetoothServiceConnectionListener) controller)
                                    .onBluetoothServiceConnected(mBluetoothA2dp);
                        }
                    }
                }

                @Override
                public void onServiceDisconnected(int profile) {
                    synchronized (mBluetoothA2dpLock) {
                        mBluetoothA2dp = null;
                    }
                    for (AbstractPreferenceController controller : mPreferenceControllers) {
                        if (controller instanceof BluetoothServiceConnectionListener) {
                            ((BluetoothServiceConnectionListener) controller)
                                    .onBluetoothServiceDisconnected();
                        }
                    }
                }
            };

    public DevelopmentSettingsDashboardFragment() {
        super(UserManager.DISALLOW_DEBUGGING_FEATURES);
    }
@@ -103,6 +160,12 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        registerReceivers();

        final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
        if (adapter != null) {
            adapter.getProfileProxy(getActivity(), mBluetoothA2dpServiceListener,
                    BluetoothProfile.A2DP);
        }
        return super.onCreateView(inflater, container, savedInstanceState);
    }

@@ -110,6 +173,12 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
    public void onDestroyView() {
        super.onDestroyView();
        unregisterReceivers();

        final BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
        if (adapter != null) {
            adapter.closeProfileProxy(BluetoothProfile.A2DP, mBluetoothA2dp);
            mBluetoothA2dp = null;
        }
    }

    @Override
@@ -229,7 +298,7 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
    @Override
    protected List<AbstractPreferenceController> getPreferenceControllers(Context context) {
        mPreferenceControllers = buildPreferenceControllers(context, getActivity(), getLifecycle(),
                this /* devOptionsDashboardFragment */);
                this /* devOptionsDashboardFragment */, mBluetoothA2dpLock);
        return mPreferenceControllers;
    }

@@ -237,10 +306,15 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
        LocalBroadcastManager.getInstance(getContext())
                .registerReceiver(mEnableAdbReceiver, new IntentFilter(
                        AdbPreferenceController.ACTION_ENABLE_ADB_STATE_CHANGED));

        final IntentFilter filter = new IntentFilter();
        filter.addAction(BluetoothA2dp.ACTION_CODEC_CONFIG_CHANGED);
        getActivity().registerReceiver(mBluetoothA2dpReceiver, filter);
    }

    private void unregisterReceivers() {
        LocalBroadcastManager.getInstance(getContext()).unregisterReceiver(mEnableAdbReceiver);
        getActivity().unregisterReceiver(mBluetoothA2dpReceiver);
    }

    void onEnableDevelopmentOptionsConfirmed() {
@@ -258,7 +332,8 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
    }

    private static List<AbstractPreferenceController> buildPreferenceControllers(Context context,
            Activity activity, Lifecycle lifecycle, DevelopmentSettingsDashboardFragment fragment) {
            Activity activity, Lifecycle lifecycle, DevelopmentSettingsDashboardFragment fragment,
            Object bluetoothA2dpLock) {
        final List<AbstractPreferenceController> controllers = new ArrayList<>();
        controllers.add(new BugReportPreferenceControllerV2(context));
        controllers.add(new LocalBackupPasswordPreferenceController(context));
@@ -301,7 +376,8 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
        controllers.add(new BluetoothInbandRingingPreferenceController(context));
        controllers.add(new BluetoothAvrcpVersionPreferenceController(context));
        // bluetooth audio codec
        // bluetooth audio sample rate
        controllers.add(new BluetoothAudioSampleRatePreferenceController(context, lifecycle,
                bluetoothA2dpLock));
        // bluetooth audio bits per sample
        // bluetooth audio channel mode
        // bluetooth audio ldac codec: playback quality
@@ -368,7 +444,8 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
                public List<AbstractPreferenceController> getPreferenceControllers(Context
                        context) {
                    return buildPreferenceControllers(context, null /* activity */,
                            null /* lifecycle */, null /* devOptionsDashboardFragment */);
                            null /* lifecycle */, null /* devOptionsDashboardFragment */,
                            null /* bluetoothA2dpLock */);
                }
            };
}
+11 −0
Original line number Diff line number Diff line
@@ -20,4 +20,15 @@ package android.bluetooth;
 * A placeholder class to prevent ClassNotFound exceptions caused by lack of visibility.
 */
public class BluetoothCodecConfig {

    public static final int SAMPLE_RATE_NONE = 0;
    public static final int SAMPLE_RATE_48000 = 0x1 << 1;
    public static final int SOURCE_CODEC_TYPE_INVALID = 1000 * 1000;
    public static final int CODEC_PRIORITY_DEFAULT = 0;
    public static final int BITS_PER_SAMPLE_NONE = 0;
    public static final int CHANNEL_MODE_NONE = 0;

    public int getSampleRate() {
        return 0;
    }
}
Loading