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

Commit 1f09fa5d authored by Haijie Hong's avatar Haijie Hong
Browse files

Move bluetooth operations inAdvancedBluetoothDetailHeader to background thread

Bug: 305636727
Test: atest AdvancedBluetoothDetailsHeaderControllerTest

Change-Id: I2827deb7ab989169eb2c64c8d075e18cd1a307c7
parent 57290578
Loading
Loading
Loading
Loading
+169 −73
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.settings.bluetooth;

import static com.android.settings.bluetooth.Utils.preloadAndRun;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.ContentResolver;
@@ -55,9 +57,13 @@ import com.android.settingslib.utils.StringUtil;
import com.android.settingslib.utils.ThreadUtils;
import com.android.settingslib.widget.LayoutPreference;

import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;

import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
@@ -236,24 +242,42 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont
    @VisibleForTesting
    void refresh() {
        if (mLayoutPreference != null && mCachedDevice != null) {
            final TextView title = mLayoutPreference.findViewById(R.id.entity_header_title);
            title.setText(mCachedDevice.getName());
            final TextView summary = mLayoutPreference.findViewById(R.id.entity_header_summary);

            if (!mCachedDevice.isConnected() || mCachedDevice.isBusy()) {
                summary.setText(mCachedDevice.getConnectionSummary(true /* shortSummary */));
            Supplier<String> deviceName = Suppliers.memoize(() -> mCachedDevice.getName());
            Supplier<Boolean> disconnected =
                    Suppliers.memoize(() -> !mCachedDevice.isConnected() || mCachedDevice.isBusy());
            Supplier<Boolean> isUntetheredHeadset =
                    Suppliers.memoize(() -> isUntetheredHeadset(mCachedDevice.getDevice()));
            Supplier<String> summaryText =
                    Suppliers.memoize(
                            () -> {
                                if (disconnected.get() || isUntetheredHeadset.get()) {
                                    return mCachedDevice.getConnectionSummary(
                                            /* shortSummary= */ true);
                                }
                                return mCachedDevice.getConnectionSummary(
                                        BluetoothUtils.getIntMetaData(
                                                        mCachedDevice.getDevice(),
                                                        BluetoothDevice.METADATA_MAIN_BATTERY)
                                                != BluetoothUtils.META_INT_ERROR);
                            });
            preloadAndRun(
                    List.of(deviceName, disconnected, isUntetheredHeadset, summaryText),
                    () -> {
                        final TextView title =
                                mLayoutPreference.findViewById(R.id.entity_header_title);
                        title.setText(deviceName.get());
                        final TextView summary =
                                mLayoutPreference.findViewById(R.id.entity_header_summary);

                        if (disconnected.get()) {
                            summary.setText(summaryText.get());
                            updateDisconnectLayout();
                            return;
                        }
            final BluetoothDevice device = mCachedDevice.getDevice();
            final String deviceType = BluetoothUtils.getStringMetaData(device,
                    BluetoothDevice.METADATA_DEVICE_TYPE);
            if (TextUtils.equals(deviceType,
                    BluetoothDevice.DEVICE_TYPE_UNTETHERED_HEADSET)
                    || BluetoothUtils.getBooleanMetaData(device,
                    BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)) {
                summary.setText(mCachedDevice.getConnectionSummary(true /* shortSummary */));
                updateSubLayout(mLayoutPreference.findViewById(R.id.layout_left),
                        if (isUntetheredHeadset.get()) {
                            summary.setText(summaryText.get());
                            updateSubLayout(
                                    mLayoutPreference.findViewById(R.id.layout_left),
                                    BluetoothDevice.METADATA_UNTETHERED_LEFT_ICON,
                                    BluetoothDevice.METADATA_UNTETHERED_LEFT_BATTERY,
                                    BluetoothDevice.METADATA_UNTETHERED_LEFT_LOW_BATTERY_THRESHOLD,
@@ -261,7 +285,8 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont
                                    R.string.bluetooth_left_name,
                                    LEFT_DEVICE_ID);

                updateSubLayout(mLayoutPreference.findViewById(R.id.layout_middle),
                            updateSubLayout(
                                    mLayoutPreference.findViewById(R.id.layout_middle),
                                    BluetoothDevice.METADATA_UNTETHERED_CASE_ICON,
                                    BluetoothDevice.METADATA_UNTETHERED_CASE_BATTERY,
                                    BluetoothDevice.METADATA_UNTETHERED_CASE_LOW_BATTERY_THRESHOLD,
@@ -269,7 +294,8 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont
                                    R.string.bluetooth_middle_name,
                                    CASE_DEVICE_ID);

                updateSubLayout(mLayoutPreference.findViewById(R.id.layout_right),
                            updateSubLayout(
                                    mLayoutPreference.findViewById(R.id.layout_right),
                                    BluetoothDevice.METADATA_UNTETHERED_RIGHT_ICON,
                                    BluetoothDevice.METADATA_UNTETHERED_RIGHT_BATTERY,
                                    BluetoothDevice.METADATA_UNTETHERED_RIGHT_LOW_BATTERY_THRESHOLD,
@@ -279,20 +305,24 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont

                            showBothDevicesBatteryPredictionIfNecessary();
                        } else {
                mLayoutPreference.findViewById(R.id.layout_left).setVisibility(View.GONE);
                mLayoutPreference.findViewById(R.id.layout_right).setVisibility(View.GONE);

                summary.setText(mCachedDevice.getConnectionSummary(
                        BluetoothUtils.getIntMetaData(device, BluetoothDevice.METADATA_MAIN_BATTERY)
                                != BluetoothUtils.META_INT_ERROR));
                updateSubLayout(mLayoutPreference.findViewById(R.id.layout_middle),
                            mLayoutPreference
                                    .findViewById(R.id.layout_left)
                                    .setVisibility(View.GONE);
                            mLayoutPreference
                                    .findViewById(R.id.layout_right)
                                    .setVisibility(View.GONE);

                            summary.setText(summaryText.get());
                            updateSubLayout(
                                    mLayoutPreference.findViewById(R.id.layout_middle),
                                    BluetoothDevice.METADATA_MAIN_ICON,
                                    BluetoothDevice.METADATA_MAIN_BATTERY,
                                    BluetoothDevice.METADATA_MAIN_LOW_BATTERY_THRESHOLD,
                                    BluetoothDevice.METADATA_MAIN_CHARGING,
                        /* titleResId */ 0,
                                    /* titleResId= */ 0,
                                    MAIN_DEVICE_ID);
                        }
                    });
        }
    }

@@ -315,13 +345,87 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont
        return drawable;
    }

    private void updateSubLayout(LinearLayout linearLayout, int iconMetaKey, int batteryMetaKey,
            int lowBatteryMetaKey, int chargeMetaKey, int titleResId, int deviceId) {
    private void updateSubLayout(
            LinearLayout linearLayout,
            int iconMetaKey,
            int batteryMetaKey,
            int lowBatteryMetaKey,
            int chargeMetaKey,
            int titleResId,
            int deviceId) {
        if (linearLayout == null) {
            return;
        }
        BluetoothDevice bluetoothDevice = mCachedDevice.getDevice();
        Supplier<String> iconUri =
                Suppliers.memoize(
                        () -> BluetoothUtils.getStringMetaData(bluetoothDevice, iconMetaKey));
        Supplier<Integer> batteryLevel =
                Suppliers.memoize(
                        () -> BluetoothUtils.getIntMetaData(bluetoothDevice, batteryMetaKey));
        Supplier<Boolean> charging =
                Suppliers.memoize(
                        () -> BluetoothUtils.getBooleanMetaData(bluetoothDevice, chargeMetaKey));
        Supplier<Integer> lowBatteryLevel =
                Suppliers.memoize(
                        () -> {
                            int level =
                                    BluetoothUtils.getIntMetaData(
                                            bluetoothDevice, lowBatteryMetaKey);
                            if (level == BluetoothUtils.META_INT_ERROR) {
                                if (batteryMetaKey
                                        == BluetoothDevice.METADATA_UNTETHERED_CASE_BATTERY) {
                                    level = CASE_LOW_BATTERY_LEVEL;
                                } else {
                                    level = LOW_BATTERY_LEVEL;
                                }
                            }
                            return level;
                        });
        Supplier<Boolean> isUntethered =
                Suppliers.memoize(() -> isUntetheredHeadset(bluetoothDevice));
        Supplier<Integer> nativeBatteryLevel = Suppliers.memoize(bluetoothDevice::getBatteryLevel);
        preloadAndRun(
                List.of(
                        iconUri,
                        batteryLevel,
                        charging,
                        lowBatteryLevel,
                        isUntethered,
                        nativeBatteryLevel),
                () ->
                        updateSubLayoutUi(
                                linearLayout,
                                iconMetaKey,
                                batteryMetaKey,
                                lowBatteryMetaKey,
                                chargeMetaKey,
                                titleResId,
                                deviceId,
                                iconUri,
                                batteryLevel,
                                charging,
                                lowBatteryLevel,
                                isUntethered,
                                nativeBatteryLevel));
    }

    private void updateSubLayoutUi(
            LinearLayout linearLayout,
            int iconMetaKey,
            int batteryMetaKey,
            int lowBatteryMetaKey,
            int chargeMetaKey,
            int titleResId,
            int deviceId,
            Supplier<String> preloadedIconUri,
            Supplier<Integer> preloadedBatteryLevel,
            Supplier<Boolean> preloadedCharging,
            Supplier<Integer> preloadedLowBatteryLevel,
            Supplier<Boolean> preloadedIsUntethered,
            Supplier<Integer> preloadedNativeBatteryLevel) {
        final BluetoothDevice bluetoothDevice = mCachedDevice.getDevice();
        final String iconUri = BluetoothUtils.getStringMetaData(bluetoothDevice, iconMetaKey);
        final String iconUri = preloadedIconUri.get();
        final ImageView imageView = linearLayout.findViewById(R.id.header_icon);
        if (iconUri != null) {
            updateIcon(imageView, iconUri);
@@ -331,17 +435,9 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont
            imageView.setImageDrawable(pair.first);
            imageView.setContentDescription(pair.second);
        }
        final int batteryLevel = BluetoothUtils.getIntMetaData(bluetoothDevice, batteryMetaKey);
        final boolean charging = BluetoothUtils.getBooleanMetaData(bluetoothDevice, chargeMetaKey);
        int lowBatteryLevel = BluetoothUtils.getIntMetaData(bluetoothDevice,
                lowBatteryMetaKey);
        if (lowBatteryLevel == BluetoothUtils.META_INT_ERROR) {
            if (batteryMetaKey == BluetoothDevice.METADATA_UNTETHERED_CASE_BATTERY) {
                lowBatteryLevel = CASE_LOW_BATTERY_LEVEL;
            } else {
                lowBatteryLevel = LOW_BATTERY_LEVEL;
            }
        }
        final int batteryLevel = preloadedBatteryLevel.get();
        final boolean charging = preloadedCharging.get();
        int lowBatteryLevel = preloadedLowBatteryLevel.get();

        Log.d(TAG, "buletoothDevice: " + bluetoothDevice.getAnonymizedAddress()
                + ", updateSubLayout() icon : " + iconMetaKey + ", battery : " + batteryMetaKey
@@ -353,7 +449,7 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont
            showBatteryPredictionIfNecessary(linearLayout, deviceId, batteryLevel);
        }
        final TextView batterySummaryView = linearLayout.findViewById(R.id.bt_battery_summary);
        if (isUntetheredHeadset(bluetoothDevice)) {
        if (preloadedIsUntethered.get()) {
            if (batteryLevel != BluetoothUtils.META_INT_ERROR) {
                linearLayout.setVisibility(View.VISIBLE);
                batterySummaryView.setText(
@@ -364,7 +460,7 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont
                if (deviceId == MAIN_DEVICE_ID) {
                    linearLayout.setVisibility(View.VISIBLE);
                    linearLayout.findViewById(R.id.bt_battery_icon).setVisibility(View.GONE);
                    int level = bluetoothDevice.getBatteryLevel();
                    int level = preloadedNativeBatteryLevel.get();
                    if (level != BluetoothDevice.BATTERY_LEVEL_UNKNOWN
                            && level != BluetoothDevice.BATTERY_LEVEL_BLUETOOTH_OFF) {
                        batterySummaryView.setText(
+22 −0
Original line number Diff line number Diff line
@@ -37,12 +37,16 @@ import androidx.annotation.VisibleForTesting;
import androidx.appcompat.app.AlertDialog;

import com.android.settings.R;
import com.android.settings.flags.Flags;
import com.android.settings.overlay.FeatureFactory;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.BluetoothUtils.ErrorListener;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothManager.BluetoothManagerCallback;
import com.android.settingslib.utils.ThreadUtils;

import com.google.common.base.Supplier;

import java.util.ArrayList;
import java.util.List;
@@ -272,4 +276,22 @@ public final class Utils {
                + " , deviceList = " + cachedBluetoothDevices);
        return cachedBluetoothDevices;
    }

    /**
     * Preloads the values and run the Runnable afterwards.
     * @param suppliers the value supplier, should be a memoized supplier
     * @param runnable the runnable to be run after value is preloaded
     */
    public static void preloadAndRun(List<Supplier<?>> suppliers, Runnable runnable) {
        if (!Flags.enableOffloadBluetoothOperationsToBackgroundThread()) {
            runnable.run();
            return;
        }
        ThreadUtils.postOnBackgroundThread(() -> {
            for (Supplier<?> supplier : suppliers) {
                supplier.get();
            }
            ThreadUtils.postOnMainThread(runnable);
        });
    }
}