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

Commit b79278da authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Fix race condition in QSTileHost" into tm-qpr-dev am: dc67d818 am: 3480f1db

parents 1e2cfcb8 3480f1db
Loading
Loading
Loading
Loading
+89 −43
Original line number Diff line number Diff line
@@ -19,8 +19,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings.Secure;
@@ -28,6 +26,7 @@ import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;

import androidx.annotation.MainThread;
import androidx.annotation.Nullable;

import com.android.internal.logging.InstanceId;
@@ -35,9 +34,7 @@ import com.android.internal.logging.InstanceIdSequence;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.Dumpable;
import com.android.systemui.R;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.plugins.PluginListener;
@@ -68,12 +65,20 @@ import java.util.LinkedHashMap;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Predicate;

import javax.inject.Inject;
import javax.inject.Provider;

/** Platform implementation of the quick settings tile host **/
/** Platform implementation of the quick settings tile host
 *
 * This class keeps track of the set of current tiles and is the in memory source of truth
 * (ground truth is kept in {@link Secure#QS_TILES}). When the ground truth changes,
 * {@link #onTuningChanged} will be called and the tiles will be re-created as needed.
 *
 * This class also provides the interface for adding/removing/changing tiles.
 */
@SysUISingleton
public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, Dumpable {
    private static final String TAG = "QSTileHost";
@@ -89,11 +94,11 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
    private final TunerService mTunerService;
    private final PluginManager mPluginManager;
    private final DumpManager mDumpManager;
    private final BroadcastDispatcher mBroadcastDispatcher;
    private final QSLogger mQSLogger;
    private final UiEventLogger mUiEventLogger;
    private final InstanceIdSequence mInstanceIdSequence;
    private final CustomTileStatePersister mCustomTileStatePersister;
    private final Executor mMainExecutor;

    private final List<Callback> mCallbacks = new ArrayList<>();
    @Nullable
@@ -113,13 +118,11 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
    public QSTileHost(Context context,
            StatusBarIconController iconController,
            QSFactory defaultFactory,
            @Main Handler mainHandler,
            @Background Looper bgLooper,
            @Main Executor mainExecutor,
            PluginManager pluginManager,
            TunerService tunerService,
            Provider<AutoTileManager> autoTiles,
            DumpManager dumpManager,
            BroadcastDispatcher broadcastDispatcher,
            Optional<CentralSurfaces> centralSurfacesOptional,
            QSLogger qsLogger,
            UiEventLogger uiEventLogger,
@@ -137,7 +140,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
        mDumpManager = dumpManager;
        mQSLogger = qsLogger;
        mUiEventLogger = uiEventLogger;
        mBroadcastDispatcher = broadcastDispatcher;
        mMainExecutor = mainExecutor;
        mTileServiceRequestController = tileServiceRequestControllerBuilder.create(this);
        mTileLifeCycleManagerFactory = tileLifecycleManagerFactory;

@@ -151,7 +154,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
        mSecureSettings = secureSettings;
        mCustomTileStatePersister = customTileStatePersister;

        mainHandler.post(() -> {
        mainExecutor.execute(() -> {
            // This is technically a hack to avoid circular dependency of
            // QSTileHost -> XXXTile -> QSTileHost. Posting ensures creation
            // finishes before creating any tiles.
@@ -258,6 +261,33 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
        return mTileSpecs.indexOf(spec);
    }

    /**
     * Whenever the Secure Setting keeping track of the current tiles changes (or upon start) this
     * will be called with the new value of the setting.
     *
     * This method will do the following:
     * <ol>
     *     <li>Destroy any existing tile that's not one of the current tiles (in the setting)</li>
     *     <li>Create new tiles for those that don't already exist. If this tiles end up being
     *         not available, they'll also be destroyed.</li>
     *     <li>Save the resolved list of tiles (current tiles that are available) into the setting.
     *         This means that after this call ends, the tiles in the Setting, {@link #mTileSpecs},
     *         and visible tiles ({@link #mTiles}) must match.
     *         </li>
     * </ol>
     *
     * Additionally, if the user has changed, it'll do the following:
     * <ul>
     *     <li>Change the user for SystemUI tiles: {@link QSTile#userSwitch}.</li>
     *     <li>Destroy any {@link CustomTile} and recreate it for the new user.</li>
     * </ul>
     *
     * This happens in main thread as {@link com.android.systemui.tuner.TunerServiceImpl} dispatches
     * in main thread.
     *
     * @see QSTile#isAvailable
     */
    @MainThread
    @Override
    public void onTuningChanged(String key, String newValue) {
        if (!TILES_SETTING.equals(key)) {
@@ -330,34 +360,44 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
        mCurrentUser = currentUser;
        List<String> currentSpecs = new ArrayList<>(mTileSpecs);
        mTileSpecs.clear();
        mTileSpecs.addAll(tileSpecs);
        mTileSpecs.addAll(newTiles.keySet()); // Only add the valid (available) tiles.
        mTiles.clear();
        mTiles.putAll(newTiles);
        if (newTiles.isEmpty() && !tileSpecs.isEmpty()) {
            // If we didn't manage to create any tiles, set it to empty (default)
            Log.d(TAG, "No valid tiles on tuning changed. Setting to default.");
            changeTiles(currentSpecs, loadTileSpecs(mContext, ""));
            changeTilesByUser(currentSpecs, loadTileSpecs(mContext, ""));
        } else {
            String resolvedTiles = TextUtils.join(",", mTileSpecs);
            if (!resolvedTiles.equals(newValue)) {
                // If the resolved tiles (those we actually ended up with) are different than
                // the ones that are in the setting, update the Setting.
                saveTilesToSettings(mTileSpecs);
            }
            for (int i = 0; i < mCallbacks.size(); i++) {
                mCallbacks.get(i).onTilesChanged();
            }
        }
    }

    /**
     * Only use with [CustomTile] if the tile doesn't exist anymore (and therefore doesn't need
     * its lifecycle terminated).
     */
    @Override
    public void removeTile(String spec) {
        changeTileSpecs(tileSpecs-> tileSpecs.remove(spec));
        mMainExecutor.execute(() -> changeTileSpecs(tileSpecs-> tileSpecs.remove(spec)));
    }

    /**
     * Remove many tiles at once.
     *
     * It will only save to settings once (as opposed to {@link QSTileHost#removeTile} called
     * It will only save to settings once (as opposed to {@link QSTileHost#removeTileByUser} called
     * multiple times).
     */
    @Override
    public void removeTiles(Collection<String> specs) {
        changeTileSpecs(tileSpecs -> tileSpecs.removeAll(specs));
        mMainExecutor.execute(() -> changeTileSpecs(tileSpecs -> tileSpecs.removeAll(specs)));
    }

    @Override
@@ -381,7 +421,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
     * @param requestPosition -1 for end, 0 for beginning, or X for insertion at position X
     */
    public void addTile(String spec, int requestPosition) {
        if (spec.equals("work")) Log.wtfStack(TAG, "Adding work tile");
        mMainExecutor.execute(() ->
                changeTileSpecs(tileSpecs -> {
                    if (tileSpecs.contains(spec)) return false;

@@ -392,15 +432,18 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
                        tileSpecs.add(requestPosition, spec);
                    }
                    return true;
        });
                })
        );
    }

    void saveTilesToSettings(List<String> tileSpecs) {
    @MainThread
    private void saveTilesToSettings(List<String> tileSpecs) {
        mSecureSettings.putStringForUser(TILES_SETTING, TextUtils.join(",", tileSpecs),
                null /* tag */, false /* default */, mCurrentUser,
                true /* overrideable by restore */);
    }

    @MainThread
    private void changeTileSpecs(Predicate<List<String>> changeFunction) {
        final String setting = mSecureSettings.getStringForUser(TILES_SETTING, mCurrentUser);
        final List<String> tileSpecs = loadTileSpecs(mContext, setting);
@@ -420,29 +463,32 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
     */
    public void addTile(ComponentName tile, boolean end) {
        String spec = CustomTile.toSpec(tile);
        if (!mTileSpecs.contains(spec)) {
            List<String> newSpecs = new ArrayList<>(mTileSpecs);
            if (end) {
                newSpecs.add(spec);
            } else {
                newSpecs.add(0, spec);
            }
            changeTiles(mTileSpecs, newSpecs);
        }
        addTile(spec, end ? POSITION_AT_END : 0);
    }

    public void removeTile(ComponentName tile) {
    /**
     * This will call through {@link #changeTilesByUser}. It should only be used when a tile is
     * removed by a <b>user action</b> like {@code adb}.
     */
    public void removeTileByUser(ComponentName tile) {
        mMainExecutor.execute(() -> {
            List<String> newSpecs = new ArrayList<>(mTileSpecs);
        newSpecs.remove(CustomTile.toSpec(tile));
        changeTiles(mTileSpecs, newSpecs);
            if (newSpecs.remove(CustomTile.toSpec(tile))) {
                changeTilesByUser(mTileSpecs, newSpecs);
            }
        });
    }

    /**
     * Change the tiles triggered by the user editing.
     * <p>
     * This is not called on device start, or on user change.
     *
     * {@link android.service.quicksettings.TileService#onTileRemoved} will be called for tiles
     * that are removed.
     */
    public void changeTiles(List<String> previousTiles, List<String> newTiles) {
    @MainThread
    public void changeTilesByUser(List<String> previousTiles, List<String> newTiles) {
        final List<String> copy = new ArrayList<>(previousTiles);
        final int NP = copy.size();
        for (int i = 0; i < NP; i++) {
+2 −2
Original line number Diff line number Diff line
@@ -182,7 +182,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
        for (int i = 1; i < mTiles.size() && mTiles.get(i) != null; i++) {
            newSpecs.add(mTiles.get(i).spec);
        }
        host.changeTiles(mCurrentSpecs, newSpecs);
        host.changeTilesByUser(mCurrentSpecs, newSpecs);
        mCurrentSpecs = newSpecs;
    }

@@ -200,7 +200,7 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
    /** */
    public void resetTileSpecs(List<String> specs) {
        // Notify the host so the tiles get removed callbacks.
        mHost.changeTiles(mCurrentSpecs, specs);
        mHost.changeTilesByUser(mCurrentSpecs, specs);
        setTileSpecs(specs);
    }

+1 −1
Original line number Diff line number Diff line
@@ -289,7 +289,7 @@ public class TileServiceManager {
                }
            }

            mServices.getHost().removeTile(component);
            mServices.getHost().removeTile(CustomTile.toSpec(component));
        }
    };
}
+1 −1
Original line number Diff line number Diff line
@@ -428,7 +428,7 @@ public class AutoTileManager implements UserAwareController {
            if (isSafetyCenterEnabled && !mAutoTracker.isAdded(mSafetySpec)) {
                initSafetyTile();
            } else if (!isSafetyCenterEnabled && mAutoTracker.isAdded(mSafetySpec)) {
                mHost.removeTile(CustomTile.getComponentFromSpec(mSafetySpec));
                mHost.removeTile(mSafetySpec);
                mHost.unmarkTileAsAutoAdded(mSafetySpec);
            }
        }
+1 −1
Original line number Diff line number Diff line
@@ -191,7 +191,7 @@ public class CentralSurfacesCommandQueueCallbacks implements CommandQueue.Callba
    public void remQsTile(ComponentName tile) {
        QSPanelController qsPanelController = mCentralSurfaces.getQSPanelController();
        if (qsPanelController != null && qsPanelController.getHost() != null) {
            qsPanelController.getHost().removeTile(tile);
            qsPanelController.getHost().removeTileByUser(tile);
        }
    }

Loading