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

Commit 3c3f3621 authored by Jason Chiu's avatar Jason Chiu
Browse files

Improve BT slice card loading performance

The bottleneck is getting LocalBluetoothManager the first time.
1. Initialize LocalBluetoothManager earlier and asynchronously.
2. Don't block in slice's constructor and getSlice().
  - Initialize the bt updaters until the manager is ready.
  - Just show a header if the manager is not ready yet.

Fixes: 157702021
Test: robotest
Change-Id: I427df55f259b45ba4c37557b22e09dcc24079e93
parent 03b20f86
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -37,6 +37,7 @@ import androidx.recyclerview.widget.ItemTouchHelper;

import com.android.settings.R;
import com.android.settings.core.InstrumentedFragment;
import com.android.settings.homepage.contextualcards.slices.BluetoothUpdateWorker;
import com.android.settings.homepage.contextualcards.slices.SwipeDismissalDelegate;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.wifi.slice.ContextualWifiScanWorker;
@@ -67,6 +68,7 @@ public class ContextualCardsFragment extends InstrumentedFragment implements
        final Context context = getContext();
        if (savedInstanceState == null) {
            FeatureFactory.getFactory(context).getSlicesFeatureProvider().newUiSession();
            BluetoothUpdateWorker.initLocalBtManager(getContext());
        }
        mContextualCardManager = new ContextualCardManager(context, getSettingsLifecycle(),
                savedInstanceState);
+35 −22
Original line number Diff line number Diff line
@@ -81,15 +81,12 @@ public class BluetoothDevicesSlice implements CustomSliceable {
    private static boolean sBluetoothEnabling;

    private final Context mContext;
    private final AvailableMediaBluetoothDeviceUpdater mAvailableMediaBtDeviceUpdater;
    private final SavedBluetoothDeviceUpdater mSavedBtDeviceUpdater;
    private AvailableMediaBluetoothDeviceUpdater mAvailableMediaBtDeviceUpdater;
    private SavedBluetoothDeviceUpdater mSavedBtDeviceUpdater;

    public BluetoothDevicesSlice(Context context) {
        mContext = context;
        mAvailableMediaBtDeviceUpdater = new AvailableMediaBluetoothDeviceUpdater(mContext,
                null /* fragment */, null /* devicePreferenceCallback */);
        mSavedBtDeviceUpdater = new SavedBluetoothDeviceUpdater(mContext,
                null /* fragment */, null /* devicePreferenceCallback */);
        BluetoothUpdateWorker.initLocalBtManager(context);
    }

    @Override
@@ -119,16 +116,8 @@ public class BluetoothDevicesSlice implements CustomSliceable {
        // Add the header of Bluetooth on
        listBuilder.addRow(getBluetoothOnHeader());

        // Get row builders of Bluetooth devices.
        final List<ListBuilder.RowBuilder> rows = getBluetoothRowBuilders();

        // Determine the displayable row count.
        final int displayableCount = Math.min(rows.size(), DEFAULT_EXPANDED_ROW_COUNT);

        // Add device rows up to the count.
        for (int i = 0; i < displayableCount; i++) {
            listBuilder.addRow(rows.get(i));
        }
        // Add row builders of Bluetooth devices.
        getBluetoothRowBuilders().forEach(row -> listBuilder.addRow(row));

        return listBuilder.build();
    }
@@ -181,19 +170,18 @@ public class BluetoothDevicesSlice implements CustomSliceable {
    List<CachedBluetoothDevice> getPairedBluetoothDevices() {
        final List<CachedBluetoothDevice> bluetoothDeviceList = new ArrayList<>();

        // If Bluetooth is disable, skip to get the Bluetooth devices.
        // If Bluetooth is disable, skip getting the Bluetooth devices.
        if (!BluetoothAdapter.getDefaultAdapter().isEnabled()) {
            Log.i(TAG, "Cannot get Bluetooth devices, Bluetooth is disabled.");
            return bluetoothDeviceList;
        }

        // Get the Bluetooth devices from LocalBluetoothManager.
        final LocalBluetoothManager localBtManager =
                com.android.settings.bluetooth.Utils.getLocalBtManager(mContext);
        final LocalBluetoothManager localBtManager = BluetoothUpdateWorker.getLocalBtManager();
        if (localBtManager == null) {
            Log.i(TAG, "Cannot get Bluetooth devices, Bluetooth is unsupported.");
            Log.i(TAG, "Cannot get Bluetooth devices, Bluetooth is not ready.");
            return bluetoothDeviceList;
        }

        final Collection<CachedBluetoothDevice> cachedDevices =
                localBtManager.getCachedDeviceManager().getCachedDevicesCopy();

@@ -292,8 +280,21 @@ public class BluetoothDevicesSlice implements CustomSliceable {

    private List<ListBuilder.RowBuilder> getBluetoothRowBuilders() {
        final List<ListBuilder.RowBuilder> bluetoothRows = new ArrayList<>();
        final List<CachedBluetoothDevice> pairedDevices = getPairedBluetoothDevices();
        if (pairedDevices.isEmpty()) {
            return bluetoothRows;
        }

        // Initialize updaters without being blocked after paired devices is available because
        // LocalBluetoothManager is ready.
        lazyInitUpdaters();

        // Create row builders based on paired devices.
        for (CachedBluetoothDevice device : getPairedBluetoothDevices()) {
        for (CachedBluetoothDevice device : pairedDevices) {
            if (bluetoothRows.size() >= DEFAULT_EXPANDED_ROW_COUNT) {
                break;
            }

            String summary = device.getConnectionSummary();
            if (summary == null) {
                summary = mContext.getString(
@@ -321,6 +322,18 @@ public class BluetoothDevicesSlice implements CustomSliceable {
        return bluetoothRows;
    }

    private void lazyInitUpdaters() {
        if (mAvailableMediaBtDeviceUpdater == null) {
            mAvailableMediaBtDeviceUpdater = new AvailableMediaBluetoothDeviceUpdater(mContext,
                    null /* fragment */, null /* devicePreferenceCallback */);
        }

        if (mSavedBtDeviceUpdater == null) {
            mSavedBtDeviceUpdater = new SavedBluetoothDeviceUpdater(mContext,
                    null /* fragment */, null /* devicePreferenceCallback */);
        }
    }

    @VisibleForTesting
    SliceAction buildPrimaryBluetoothAction(CachedBluetoothDevice bluetoothDevice) {
        final Intent intent = new Intent(getUri().toString())
+86 −9
Original line number Diff line number Diff line
@@ -18,9 +18,14 @@ package com.android.settings.homepage.contextualcards.slices;

import android.content.Context;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Process;
import android.util.Log;

import com.android.settings.bluetooth.Utils;
import androidx.annotation.Nullable;

import com.android.settings.slices.SliceBackgroundWorker;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -30,29 +35,46 @@ public class BluetoothUpdateWorker extends SliceBackgroundWorker implements Blue

    private static final String TAG = "BluetoothUpdateWorker";

    private final LocalBluetoothManager mLocalBluetoothManager;
    private static LocalBluetoothManager sLocalBluetoothManager;

    private LoadBtManagerHandler mLoadBtManagerHandler;

    public BluetoothUpdateWorker(Context context, Uri uri) {
        super(context, uri);
        mLocalBluetoothManager = Utils.getLocalBtManager(context);
        mLoadBtManagerHandler = LoadBtManagerHandler.getInstance(context);
        if (sLocalBluetoothManager == null) {
            mLoadBtManagerHandler.startLoadingBtManager(this);
        }
    }

    /** Initialize {@link LocalBluetoothManager} in the background */
    public static void initLocalBtManager(Context context) {
        if (sLocalBluetoothManager == null) {
            LoadBtManagerHandler.getInstance(context).startLoadingBtManager();
        }
    }

    @Nullable
    static LocalBluetoothManager getLocalBtManager() {
        return sLocalBluetoothManager;
    }

    @Override
    protected void onSlicePinned() {
        if (mLocalBluetoothManager == null) {
            Log.i(TAG, "onSlicePinned() Bluetooth is unsupported.");
        final LocalBluetoothManager localBtManager = mLoadBtManagerHandler.getLocalBtManager();
        if (localBtManager == null) {
            return;
        }
        mLocalBluetoothManager.getEventManager().registerCallback(this);
        localBtManager.getEventManager().registerCallback(this);
    }

    @Override
    protected void onSliceUnpinned() {
        if (mLocalBluetoothManager == null) {
            Log.i(TAG, "onSliceUnpinned() Bluetooth is unsupported.");
        final LocalBluetoothManager localBtManager = mLoadBtManagerHandler.getLocalBtManager();
        if (localBtManager == null) {
            return;
        }
        mLocalBluetoothManager.getEventManager().unregisterCallback(this);
        localBtManager.getEventManager().unregisterCallback(this);
    }

    @Override
@@ -84,4 +106,59 @@ public class BluetoothUpdateWorker extends SliceBackgroundWorker implements Blue
            int bluetoothProfile) {
        notifySliceChange();
    }

    private static class LoadBtManagerHandler extends Handler {

        private static LoadBtManagerHandler sHandler;

        private final Runnable mLoadBtManagerTask;
        private final Context mContext;
        private BluetoothUpdateWorker mWorker;

        private static LoadBtManagerHandler getInstance(Context context) {
            if (sHandler == null) {
                final HandlerThread workerThread = new HandlerThread(TAG,
                        Process.THREAD_PRIORITY_BACKGROUND);
                workerThread.start();
                sHandler = new LoadBtManagerHandler(context, workerThread.getLooper());
            }
            return sHandler;
        }

        private LoadBtManagerHandler(Context context, Looper looper) {
            super(looper);
            mContext = context;
            mLoadBtManagerTask = () -> {
                Log.d(TAG, "LoadBtManagerHandler: start loading...");
                final long startTime = System.currentTimeMillis();
                sLocalBluetoothManager = getLocalBtManager();
                Log.d(TAG, "LoadBtManagerHandler took " + (System.currentTimeMillis() - startTime)
                        + " ms");
            };
        }

        private LocalBluetoothManager getLocalBtManager() {
            if (sLocalBluetoothManager != null) {
                return sLocalBluetoothManager;
            }
            return LocalBluetoothManager.getInstance(mContext,
                    (context, btManager) -> {
                        if (mWorker != null) {
                            // notify change if the worker is ready
                            mWorker.notifySliceChange();
                        }
                    });
        }

        private void startLoadingBtManager() {
            if (!hasCallbacks(mLoadBtManagerTask)) {
                post(mLoadBtManagerTask);
            }
        }

        private void startLoadingBtManager(BluetoothUpdateWorker worker) {
            mWorker = worker;
            startLoadingBtManager();
        }
    }
}
 No newline at end of file