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

Commit f482c100 authored by Fabian Kozynski's avatar Fabian Kozynski Committed by Android (Google) Code Review
Browse files

Merge "Use the correct user context in CustomTile" into rvc-dev

parents a0eed34e 4ba3f9aa
Loading
Loading
Loading
Loading
+37 −7
Original line number Original line Diff line number Diff line
@@ -28,6 +28,7 @@ import android.provider.Settings;
import android.provider.Settings.Secure;
import android.provider.Settings.Secure;
import android.service.quicksettings.Tile;
import android.service.quicksettings.Tile;
import android.text.TextUtils;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
import android.util.Log;


import com.android.systemui.Dumpable;
import com.android.systemui.Dumpable;
@@ -61,6 +62,7 @@ import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.List;
import java.util.Optional;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.function.Predicate;


import javax.inject.Inject;
import javax.inject.Inject;
@@ -91,6 +93,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
    private final ArrayList<QSFactory> mQsFactories = new ArrayList<>();
    private final ArrayList<QSFactory> mQsFactories = new ArrayList<>();
    private int mCurrentUser;
    private int mCurrentUser;
    private final Optional<StatusBar> mStatusBarOptional;
    private final Optional<StatusBar> mStatusBarOptional;
    private Context mUserContext;


    @Inject
    @Inject
    public QSTileHost(Context context,
    public QSTileHost(Context context,
@@ -107,6 +110,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
            QSLogger qsLogger) {
            QSLogger qsLogger) {
        mIconController = iconController;
        mIconController = iconController;
        mContext = context;
        mContext = context;
        mUserContext = context;
        mTunerService = tunerService;
        mTunerService = tunerService;
        mPluginManager = pluginManager;
        mPluginManager = pluginManager;
        mDumpManager = dumpManager;
        mDumpManager = dumpManager;
@@ -207,6 +211,9 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
        return mContext;
        return mContext;
    }
    }


    public Context getUserContext() {
        return mUserContext;
    }


    public TileServices getTileServices() {
    public TileServices getTileServices() {
        return mServices;
        return mServices;
@@ -227,6 +234,9 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
        }
        }
        final List<String> tileSpecs = loadTileSpecs(mContext, newValue);
        final List<String> tileSpecs = loadTileSpecs(mContext, newValue);
        int currentUser = ActivityManager.getCurrentUser();
        int currentUser = ActivityManager.getCurrentUser();
        if (currentUser != mCurrentUser) {
            mUserContext = mContext.createContextAsUser(UserHandle.of(currentUser), 0);
        }
        if (tileSpecs.equals(mTileSpecs) && currentUser == mCurrentUser) return;
        if (tileSpecs.equals(mTileSpecs) && currentUser == mCurrentUser) return;
        mTiles.entrySet().stream().filter(tile -> !tileSpecs.contains(tile.getKey())).forEach(
        mTiles.entrySet().stream().filter(tile -> !tileSpecs.contains(tile.getKey())).forEach(
                tile -> {
                tile -> {
@@ -253,6 +263,13 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
                    mQSLogger.logTileDestroyed(tileSpec, "Tile not available");
                    mQSLogger.logTileDestroyed(tileSpec, "Tile not available");
                }
                }
            } else {
            } else {
                // This means that the tile is a CustomTile AND the user is different, so let's
                // destroy it
                if (tile != null) {
                    tile.destroy();
                    Log.d(TAG, "Destroying tile for wrong user: " + tileSpec);
                    mQSLogger.logTileDestroyed(tileSpec, "Tile for wrong user");
                }
                Log.d(TAG, "Creating tile: " + tileSpec);
                Log.d(TAG, "Creating tile: " + tileSpec);
                try {
                try {
                    tile = createTile(tileSpec);
                    tile = createTile(tileSpec);
@@ -273,7 +290,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
            }
            }
        }
        }
        mCurrentUser = currentUser;
        mCurrentUser = currentUser;
        List<String> currentSpecs = new ArrayList(mTileSpecs);
        List<String> currentSpecs = new ArrayList<>(mTileSpecs);
        mTileSpecs.clear();
        mTileSpecs.clear();
        mTileSpecs.addAll(tileSpecs);
        mTileSpecs.addAll(tileSpecs);
        mTiles.clear();
        mTiles.clear();
@@ -300,7 +317,7 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
    }
    }


    public void addTile(String spec) {
    public void addTile(String spec) {
        changeTileSpecs(tileSpecs-> tileSpecs.add(spec));
        changeTileSpecs(tileSpecs-> !tileSpecs.contains(spec) && tileSpecs.add(spec));
    }
    }


    private void changeTileSpecs(Predicate<List<String>> changeFunction) {
    private void changeTileSpecs(Predicate<List<String>> changeFunction) {
@@ -314,10 +331,13 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
    }
    }


    public void addTile(ComponentName tile) {
    public void addTile(ComponentName tile) {
        String spec = CustomTile.toSpec(tile);
        if (!mTileSpecs.contains(spec)) {
            List<String> newSpecs = new ArrayList<>(mTileSpecs);
            List<String> newSpecs = new ArrayList<>(mTileSpecs);
        newSpecs.add(0, CustomTile.toSpec(tile));
            newSpecs.add(0, spec);
            changeTiles(mTileSpecs, newSpecs);
            changeTiles(mTileSpecs, newSpecs);
        }
        }
    }


    public void removeTile(ComponentName tile) {
    public void removeTile(ComponentName tile) {
        List<String> newSpecs = new ArrayList<>(mTileSpecs);
        List<String> newSpecs = new ArrayList<>(mTileSpecs);
@@ -380,16 +400,26 @@ public class QSTileHost implements QSHost, Tunable, PluginListener<QSFactory>, D
        }
        }
        final ArrayList<String> tiles = new ArrayList<String>();
        final ArrayList<String> tiles = new ArrayList<String>();
        boolean addedDefault = false;
        boolean addedDefault = false;
        Set<String> addedSpecs = new ArraySet<>();
        for (String tile : tileList.split(",")) {
        for (String tile : tileList.split(",")) {
            tile = tile.trim();
            tile = tile.trim();
            if (tile.isEmpty()) continue;
            if (tile.isEmpty()) continue;
            if (tile.equals("default")) {
            if (tile.equals("default")) {
                if (!addedDefault) {
                if (!addedDefault) {
                    tiles.addAll(getDefaultSpecs(context));
                    List<String> defaultSpecs = getDefaultSpecs(context);
                    for (String spec : defaultSpecs) {
                        if (!addedSpecs.contains(spec)) {
                            tiles.add(spec);
                            addedSpecs.add(spec);
                        }
                    }
                    addedDefault = true;
                    addedDefault = true;
                }
                }
            } else {
            } else {
                if (!addedSpecs.contains(tile)) {
                    tiles.add(tile);
                    tiles.add(tile);
                    addedSpecs.add(tile);
                }
            }
            }
        }
        }
        return tiles;
        return tiles;
+11 −7
Original line number Original line Diff line number Diff line
@@ -20,6 +20,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;


import android.app.ActivityManager;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ResolveInfo;
@@ -72,15 +73,19 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
    private android.graphics.drawable.Icon mDefaultIcon;
    private android.graphics.drawable.Icon mDefaultIcon;
    private CharSequence mDefaultLabel;
    private CharSequence mDefaultLabel;


    private final Context mUserContext;

    private boolean mListening;
    private boolean mListening;
    private boolean mIsTokenGranted;
    private boolean mIsTokenGranted;
    private boolean mIsShowingDialog;
    private boolean mIsShowingDialog;


    private CustomTile(QSTileHost host, String action) {
    private CustomTile(QSTileHost host, String action, Context userContext) {
        super(host);
        super(host);
        mWindowManager = WindowManagerGlobal.getWindowManagerService();
        mWindowManager = WindowManagerGlobal.getWindowManagerService();
        mComponent = ComponentName.unflattenFromString(action);
        mComponent = ComponentName.unflattenFromString(action);
        mTile = new Tile();
        mTile = new Tile();
        mUserContext = userContext;
        mUser = mUserContext.getUserId();
        updateDefaultTileAndIcon();
        updateDefaultTileAndIcon();
        mServiceManager = host.getTileServices().getTileWrapper(this);
        mServiceManager = host.getTileServices().getTileWrapper(this);
        if (mServiceManager.isToggleableTile()) {
        if (mServiceManager.isToggleableTile()) {
@@ -90,7 +95,6 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener


        mService = mServiceManager.getTileService();
        mService = mServiceManager.getTileService();
        mServiceManager.setTileChangeListener(this);
        mServiceManager.setTileChangeListener(this);
        mUser = ActivityManager.getCurrentUser();
    }
    }


    @Override
    @Override
@@ -100,7 +104,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener


    private void updateDefaultTileAndIcon() {
    private void updateDefaultTileAndIcon() {
        try {
        try {
            PackageManager pm = mContext.getPackageManager();
            PackageManager pm = mUserContext.getPackageManager();
            int flags = PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_DIRECT_BOOT_AWARE;
            int flags = PackageManager.MATCH_DIRECT_BOOT_UNAWARE | PackageManager.MATCH_DIRECT_BOOT_AWARE;
            if (isSystemApp(pm)) {
            if (isSystemApp(pm)) {
                flags |= PackageManager.MATCH_DISABLED_COMPONENTS;
                flags |= PackageManager.MATCH_DISABLED_COMPONENTS;
@@ -318,11 +322,11 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
        state.state = tileState;
        state.state = tileState;
        Drawable drawable;
        Drawable drawable;
        try {
        try {
            drawable = mTile.getIcon().loadDrawable(mContext);
            drawable = mTile.getIcon().loadDrawable(mUserContext);
        } catch (Exception e) {
        } catch (Exception e) {
            Log.w(TAG, "Invalid icon, forcing into unavailable state");
            Log.w(TAG, "Invalid icon, forcing into unavailable state");
            state.state = Tile.STATE_UNAVAILABLE;
            state.state = Tile.STATE_UNAVAILABLE;
            drawable = mDefaultIcon.loadDrawable(mContext);
            drawable = mDefaultIcon.loadDrawable(mUserContext);
        }
        }


        final Drawable drawableF = drawable;
        final Drawable drawableF = drawable;
@@ -388,7 +392,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
        return ComponentName.unflattenFromString(action);
        return ComponentName.unflattenFromString(action);
    }
    }


    public static CustomTile create(QSTileHost host, String spec) {
    public static CustomTile create(QSTileHost host, String spec, Context userContext) {
        if (spec == null || !spec.startsWith(PREFIX) || !spec.endsWith(")")) {
        if (spec == null || !spec.startsWith(PREFIX) || !spec.endsWith(")")) {
            throw new IllegalArgumentException("Bad custom tile spec: " + spec);
            throw new IllegalArgumentException("Bad custom tile spec: " + spec);
        }
        }
@@ -396,6 +400,6 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
        if (action.isEmpty()) {
        if (action.isEmpty()) {
            throw new IllegalArgumentException("Empty custom tile spec action");
            throw new IllegalArgumentException("Empty custom tile spec action");
        }
        }
        return new CustomTile(host, action);
        return new CustomTile(host, action, userContext);
    }
    }
}
}
+1 −0
Original line number Original line Diff line number Diff line
@@ -279,6 +279,7 @@ public class TileLifecycleManager extends BroadcastReceiver implements
        if (mPackageReceiverRegistered.get() || mUserReceiverRegistered.get()) {
        if (mPackageReceiverRegistered.get() || mUserReceiverRegistered.get()) {
            stopPackageListening();
            stopPackageListening();
        }
        }
        mChangeListener = null;
    }
    }


    private void handleDeath() {
    private void handleDeath() {
+3 −1
Original line number Original line Diff line number Diff line
@@ -178,7 +178,9 @@ public class QSFactoryImpl implements QSFactory {
        }
        }


        // Custom tiles
        // Custom tiles
        if (tileSpec.startsWith(CustomTile.PREFIX)) return CustomTile.create(mHost, tileSpec);
        if (tileSpec.startsWith(CustomTile.PREFIX)) {
            return CustomTile.create(mHost, tileSpec, mHost.getUserContext());
        }


        // Debug tiles.
        // Debug tiles.
        if (Build.IS_DEBUGGABLE) {
        if (Build.IS_DEBUGGABLE) {
+71 −10
Original line number Original line Diff line number Diff line
@@ -27,6 +27,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.Mockito.when;


import android.app.ActivityManager;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Context;
import android.content.Intent;
import android.content.Intent;
import android.os.Handler;
import android.os.Handler;
@@ -76,7 +77,9 @@ import javax.inject.Provider;
public class QSTileHostTest extends SysuiTestCase {
public class QSTileHostTest extends SysuiTestCase {


    private static String MOCK_STATE_STRING = "MockState";
    private static String MOCK_STATE_STRING = "MockState";
    private static final String CUSTOM_TILE_SPEC = "custom(TEST_PKG/.TEST_CLS)";
    private static ComponentName CUSTOM_TILE =
            ComponentName.unflattenFromString("TEST_PKG/.TEST_CLS");
    private static final String CUSTOM_TILE_SPEC = CustomTile.toSpec(CUSTOM_TILE);


    @Mock
    @Mock
    private StatusBarIconController mIconController;
    private StatusBarIconController mIconController;
@@ -114,23 +117,29 @@ public class QSTileHostTest extends SysuiTestCase {
                mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager,
                mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager,
                mBroadcastDispatcher, mStatusBar, mQSLogger);
                mBroadcastDispatcher, mStatusBar, mQSLogger);
        setUpTileFactory();
        setUpTileFactory();

        // Override this config so there are no unexpected tiles
        mContext.getOrCreateTestableResources().addOverride(
                com.android.internal.R.string.config_defaultExtraQuickSettingsTiles,
                "");

        Settings.Secure.putStringForUser(mContext.getContentResolver(), QSTileHost.TILES_SETTING,
        Settings.Secure.putStringForUser(mContext.getContentResolver(), QSTileHost.TILES_SETTING,
                "", ActivityManager.getCurrentUser());
                "", ActivityManager.getCurrentUser());
    }
    }


    private void setUpTileFactory() {
    private void setUpTileFactory() {
        when(mMockState.toString()).thenReturn(MOCK_STATE_STRING);
        when(mMockState.toString()).thenReturn(MOCK_STATE_STRING);
        // Only create this kind of tiles
        when(mDefaultFactory.createTile(anyString())).thenAnswer(
        when(mDefaultFactory.createTile(anyString())).thenAnswer(
                invocation -> {
                invocation -> {
                    String spec = invocation.getArgument(0);
                    String spec = invocation.getArgument(0);
                    switch (spec) {
                    if ("spec1".equals(spec)) {
                        case "spec1":
                        return new TestTile1(mQSTileHost);
                        return new TestTile1(mQSTileHost);
                        case "spec2":
                    } else if ("spec2".equals(spec)) {
                        return new TestTile2(mQSTileHost);
                        return new TestTile2(mQSTileHost);
                        case CUSTOM_TILE_SPEC:
                    } else if (CUSTOM_TILE_SPEC.equals(spec)) {
                        return mCustomTile;
                        return mCustomTile;
                        default:
                    } else {
                        return null;
                        return null;
                    }
                    }
                });
                });
@@ -222,6 +231,58 @@ public class QSTileHostTest extends SysuiTestCase {
        verify(mQSLogger).logTileAdded(CUSTOM_TILE_SPEC);
        verify(mQSLogger).logTileAdded(CUSTOM_TILE_SPEC);
    }
    }


    @Test
    public void testNoRepeatedSpecs_addTile() {
        mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, "spec1,spec2");

        mQSTileHost.addTile("spec1");

        assertEquals(2, mQSTileHost.mTileSpecs.size());
        assertEquals("spec1", mQSTileHost.mTileSpecs.get(0));
        assertEquals("spec2", mQSTileHost.mTileSpecs.get(1));
    }

    @Test
    public void testNoRepeatedSpecs_customTile() {
        mQSTileHost.onTuningChanged(QSTileHost.TILES_SETTING, CUSTOM_TILE_SPEC);

        mQSTileHost.addTile(CUSTOM_TILE);

        assertEquals(1, mQSTileHost.mTileSpecs.size());
        assertEquals(CUSTOM_TILE_SPEC, mQSTileHost.mTileSpecs.get(0));
    }

    @Test
    public void testLoadTileSpec_repeated() {
        List<String> specs = QSTileHost.loadTileSpecs(mContext, "spec1,spec1,spec2");

        assertEquals(2, specs.size());
        assertEquals("spec1", specs.get(0));
        assertEquals("spec2", specs.get(1));
    }

    @Test
    public void testLoadTileSpec_repeatedInDefault() {
        mContext.getOrCreateTestableResources()
                .addOverride(R.string.quick_settings_tiles_default, "spec1,spec1");
        List<String> specs = QSTileHost.loadTileSpecs(mContext, "default");

        // Remove spurious tiles, like dbg:mem
        specs.removeIf(spec -> !"spec1".equals(spec));
        assertEquals(1, specs.size());
    }

    @Test
    public void testLoadTileSpec_repeatedDefaultAndSetting() {
        mContext.getOrCreateTestableResources()
                .addOverride(R.string.quick_settings_tiles_default, "spec1");
        List<String> specs = QSTileHost.loadTileSpecs(mContext, "default,spec1");

        // Remove spurious tiles, like dbg:mem
        specs.removeIf(spec -> !"spec1".equals(spec));
        assertEquals(1, specs.size());
    }

    private static class TestQSTileHost extends QSTileHost {
    private static class TestQSTileHost extends QSTileHost {
        TestQSTileHost(Context context, StatusBarIconController iconController,
        TestQSTileHost(Context context, StatusBarIconController iconController,
                QSFactoryImpl defaultFactory, Handler mainHandler, Looper bgLooper,
                QSFactoryImpl defaultFactory, Handler mainHandler, Looper bgLooper,
Loading