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

Commit 857ae16f authored by Fabián Kozynski's avatar Fabián Kozynski
Browse files

Use new Icon API to restrict showing some drawables

Using the new API, we check if the Icon passed to a Tile can be loaded
by the current user and the app. If not, a default icon is shown.

Fixes: 276896619
Test: atest com.android.systemui.qs
Test: manual, using test app
Test: manual, CustomTile and dialog show correct icon
Change-Id: I25e3d390091c5fd290c83287ead3718aa7084edd
parent d1937863
Loading
Loading
Loading
Loading
+8 −1
Original line number Diff line number Diff line
@@ -316,7 +316,14 @@ oneway interface IStatusBar
     */
    void requestTileServiceListeningState(in ComponentName componentName);

    void requestAddTile(in ComponentName componentName, in CharSequence appName, in CharSequence label, in Icon icon, in IAddTileResultCallback callback);
    void requestAddTile(
        int callingUid,
        in ComponentName componentName,
        in CharSequence appName,
        in CharSequence label,
        in Icon icon,
        in IAddTileResultCallback callback
    );
    void cancelRequestAddTile(in String packageName);

    /** Notifies System UI about an update to the media tap-to-transfer sender state. */
+9 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.app.AppOpsManager;
import android.app.IActivityManager;
import android.app.IActivityTaskManager;
import android.app.INotificationManager;
import android.app.IUriGrantsManager;
import android.app.IWallpaperManager;
import android.app.KeyguardManager;
import android.app.NotificationManager;
@@ -688,4 +689,12 @@ public class FrameworkServicesModule {
    static StatusBarManager provideStatusBarManager(Context context) {
        return context.getSystemService(StatusBarManager.class);
    }

    @Provides
    @Singleton
    static IUriGrantsManager provideIUriGrantsManager() {
        return IUriGrantsManager.Stub.asInterface(
                ServiceManager.getService(Context.URI_GRANTS_SERVICE)
        );
    }
}
+45 −104
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@ package com.android.systemui.qs.external;

import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;

import android.app.IUriGrantsManager;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
@@ -31,6 +32,7 @@ import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Process;
import android.os.RemoteException;
import android.provider.Settings;
import android.service.quicksettings.IQSTileService;
@@ -43,11 +45,9 @@ import android.view.View;
import android.view.WindowManagerGlobal;
import android.widget.Switch;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.animation.ActivityLaunchAnimator;
@@ -67,9 +67,10 @@ import com.android.systemui.settings.DisplayTracker;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.inject.Inject;

import dagger.Lazy;
import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
import dagger.assisted.AssistedInject;

public class CustomTile extends QSTileImpl<State> implements TileChangeListener {
    public static final String PREFIX = "custom(";
@@ -109,24 +110,30 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
    private final AtomicBoolean mInitialDefaultIconFetched = new AtomicBoolean(false);
    private final TileServices mTileServices;

    private CustomTile(
            QSHost host,
    private int mServiceUid = Process.INVALID_UID;

    private final IUriGrantsManager mIUriGrantsManager;

    @AssistedInject
    CustomTile(
            Lazy<QSHost> host,
            QsEventLogger uiEventLogger,
            Looper backgroundLooper,
            Handler mainHandler,
            @Background Looper backgroundLooper,
            @Main Handler mainHandler,
            FalsingManager falsingManager,
            MetricsLogger metricsLogger,
            StatusBarStateController statusBarStateController,
            ActivityStarter activityStarter,
            QSLogger qsLogger,
            String action,
            Context userContext,
            @Assisted String action,
            @Assisted Context userContext,
            CustomTileStatePersister customTileStatePersister,
            TileServices tileServices,
            DisplayTracker displayTracker
            DisplayTracker displayTracker,
            IUriGrantsManager uriGrantsManager
    ) {
        super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger,
                statusBarStateController, activityStarter, qsLogger);
        super(host.get(), uiEventLogger, backgroundLooper, mainHandler, falsingManager,
                metricsLogger, statusBarStateController, activityStarter, qsLogger);
        mTileServices = tileServices;
        mWindowManager = WindowManagerGlobal.getWindowManagerService();
        mComponent = ComponentName.unflattenFromString(action);
@@ -139,6 +146,7 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
        mService = mServiceManager.getTileService();
        mCustomTileStatePersister = customTileStatePersister;
        mDisplayTracker = displayTracker;
        mIUriGrantsManager = uriGrantsManager;
    }

    @Override
@@ -268,7 +276,8 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
     *
     * @param tile tile populated with state to apply
     */
    public void updateTileState(Tile tile) {
    public void updateTileState(Tile tile, int appUid) {
        mServiceUid = appUid;
        // This comes from a binder call IQSService.updateQsTile
        mHandler.post(() -> handleUpdateTileState(tile));
    }
@@ -433,14 +442,25 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
        state.state = tileState;
        Drawable drawable = null;
        try {
            drawable = mTile.getIcon().loadDrawable(mUserContext);
            drawable = mTile.getIcon().loadDrawableCheckingUriGrant(
                    mUserContext,
                    mIUriGrantsManager,
                    mServiceUid,
                    mComponent.getPackageName()
            );
        } catch (Exception e) {
            Log.w(TAG, "Invalid icon, forcing into unavailable state");
            state.state = Tile.STATE_UNAVAILABLE;
            drawable = mDefaultIcon.loadDrawable(mUserContext);
        }

        final Drawable drawableF = drawable;
        final Drawable drawableF;
        if (drawable != null) {
            drawableF = drawable;
        } else if (mDefaultIcon != null) {
            drawableF = mDefaultIcon.loadDrawable(mUserContext);
        } else {
            drawableF = null;
        }
        state.iconSupplier = () -> {
            if (drawableF == null) return null;
            Drawable.ConstantState cs = drawableF.getConstantState();
@@ -543,96 +563,17 @@ public class CustomTile extends QSTileImpl<State> implements TileChangeListener
    /**
     * Create a {@link CustomTile} for a given spec and user.
     *
     * @param builder     including injected common dependencies.
     * @param factory     including injected common dependencies.
     * @param spec        as provided by {@link CustomTile#toSpec}
     * @param userContext context for the user that is creating this tile.
     * @return a new {@link CustomTile}
     */
    public static CustomTile create(Builder builder, String spec, Context userContext) {
        return builder
                .setSpec(spec)
                .setUserContext(userContext)
                .build();
    }

    public static class Builder {
        final Lazy<QSHost> mQSHostLazy;
        final QsEventLogger mUiEventLogger;
        final Looper mBackgroundLooper;
        final Handler mMainHandler;
        private final FalsingManager mFalsingManager;
        final MetricsLogger mMetricsLogger;
        final StatusBarStateController mStatusBarStateController;
        final ActivityStarter mActivityStarter;
        final QSLogger mQSLogger;
        final CustomTileStatePersister mCustomTileStatePersister;
        private TileServices mTileServices;
        final DisplayTracker mDisplayTracker;

        Context mUserContext;
        String mSpec = "";

        @Inject
        public Builder(
                Lazy<QSHost> hostLazy,
                QsEventLogger uiEventLogger,
                @Background Looper backgroundLooper,
                @Main Handler mainHandler,
                FalsingManager falsingManager,
                MetricsLogger metricsLogger,
                StatusBarStateController statusBarStateController,
                ActivityStarter activityStarter,
                QSLogger qsLogger,
                CustomTileStatePersister customTileStatePersister,
                TileServices tileServices,
                DisplayTracker displayTracker
        ) {
            mQSHostLazy = hostLazy;
            mUiEventLogger = uiEventLogger;
            mBackgroundLooper = backgroundLooper;
            mMainHandler = mainHandler;
            mFalsingManager = falsingManager;
            mMetricsLogger = metricsLogger;
            mStatusBarStateController = statusBarStateController;
            mActivityStarter = activityStarter;
            mQSLogger = qsLogger;
            mCustomTileStatePersister = customTileStatePersister;
            mTileServices = tileServices;
            mDisplayTracker = displayTracker;
        }

        Builder setSpec(@NonNull String spec) {
            mSpec = spec;
            return this;
    public static CustomTile create(Factory factory, String spec, Context userContext) {
        return factory.create(getAction(spec), userContext);
    }

        Builder setUserContext(@NonNull Context userContext) {
            mUserContext = userContext;
            return this;
        }

        @VisibleForTesting
        public CustomTile build() {
            if (mUserContext == null) {
                throw new NullPointerException("UserContext cannot be null");
            }
            String action = getAction(mSpec);
            return new CustomTile(
                    mQSHostLazy.get(),
                    mUiEventLogger,
                    mBackgroundLooper,
                    mMainHandler,
                    mFalsingManager,
                    mMetricsLogger,
                    mStatusBarStateController,
                    mActivityStarter,
                    mQSLogger,
                    action,
                    mUserContext,
                    mCustomTileStatePersister,
                    mTileServices,
                    mDisplayTracker
            );
        }
    @AssistedFactory
    public interface Factory {
        CustomTile create(String action, Context userContext);
    }
}
+17 −6
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.systemui.qs.external

import android.app.IUriGrantsManager
import android.content.Context
import android.graphics.drawable.Icon
import android.view.ContextThemeWrapper
@@ -34,7 +35,7 @@ import com.android.systemui.statusbar.phone.SystemUIDialog
 * Dialog to present to the user to ask for authorization to add a [TileService].
 */
class TileRequestDialog(
    context: Context
    context: Context,
) : SystemUIDialog(context) {

    companion object {
@@ -44,7 +45,7 @@ class TileRequestDialog(
    /**
     * Set the data of the tile to add, to show the user.
     */
    fun setTileData(tileData: TileData) {
    fun setTileData(tileData: TileData, iUriGrantsManager: IUriGrantsManager) {
        val ll = (LayoutInflater
                        .from(context)
                        .inflate(R.layout.tile_service_request_dialog, null)
@@ -54,7 +55,7 @@ class TileRequestDialog(
                                .getString(R.string.qs_tile_request_dialog_text, tileData.appName)
                    }
                    addView(
                            createTileView(tileData),
                            createTileView(tileData, iUriGrantsManager),
                            context.resources.getDimensionPixelSize(
                                    R.dimen.qs_tile_service_request_tile_width),
                            context.resources.getDimensionPixelSize(R.dimen.qs_quick_tile_size)
@@ -65,13 +66,21 @@ class TileRequestDialog(
        setView(ll, spacing, spacing, spacing, spacing / 2)
    }

    private fun createTileView(tileData: TileData): QSTileView {
    private fun createTileView(
            tileData: TileData,
            iUriGrantsManager: IUriGrantsManager,
    ): QSTileView {
        val themedContext = ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings)
        val tile = QSTileViewImpl(themedContext, true)
        val state = QSTile.BooleanState().apply {
            label = tileData.label
            handlesLongClick = false
            icon = tileData.icon?.loadDrawable(context)?.let {
            icon = tileData.icon?.loadDrawableCheckingUriGrant(
                    context,
                    iUriGrantsManager,
                    tileData.callingUid,
                    tileData.packageName,
            )?.let {
                QSTileImpl.DrawableIcon(it)
            } ?: ResourceIcon.get(R.drawable.android)
            contentDescription = label
@@ -93,8 +102,10 @@ class TileRequestDialog(
     * @property icon Icon for the tile.
     */
    data class TileData(
        val callingUid: Int,
        val appName: CharSequence,
        val label: CharSequence,
        val icon: Icon?
        val icon: Icon?,
        val packageName: String,
    )
}
+24 −12
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.qs.external

import android.app.Dialog
import android.app.IUriGrantsManager
import android.app.StatusBarManager
import android.content.ComponentName
import android.content.DialogInterface
@@ -42,11 +43,12 @@ private const val TAG = "TileServiceRequestController"
/**
 * Controller to interface between [TileRequestDialog] and [QSHost].
 */
class TileServiceRequestController constructor(
class TileServiceRequestController(
        private val qsHost: QSHost,
        private val commandQueue: CommandQueue,
        private val commandRegistry: CommandRegistry,
        private val eventLogger: TileRequestDialogEventLogger,
        private val iUriGrantsManager: IUriGrantsManager,
        private val dialogCreator: () -> TileRequestDialog = { TileRequestDialog(qsHost.context) }
) {

@@ -62,13 +64,14 @@ class TileServiceRequestController constructor(

    private val commandQueueCallback = object : CommandQueue.Callbacks {
        override fun requestAddTile(
            callingUid: Int,
            componentName: ComponentName,
            appName: CharSequence,
            label: CharSequence,
            icon: Icon,
            callback: IAddTileResultCallback
        ) {
            requestTileAdd(componentName, appName, label, icon) {
            requestTileAdd(callingUid, componentName, appName, label, icon) {
                try {
                    callback.onTileRequest(it)
                } catch (e: RemoteException) {
@@ -98,6 +101,7 @@ class TileServiceRequestController constructor(

    @VisibleForTesting
    internal fun requestTileAdd(
        callingUid: Int,
        componentName: ComponentName,
        appName: CharSequence,
        label: CharSequence,
@@ -119,7 +123,13 @@ class TileServiceRequestController constructor(
            eventLogger.logUserResponse(response, packageName, instanceId)
            callback.accept(response)
        }
        val tileData = TileRequestDialog.TileData(appName, label, icon)
        val tileData = TileRequestDialog.TileData(
                callingUid,
                appName,
                label,
                icon,
                componentName.packageName,
        )
        createDialog(tileData, dialogResponse).also { dialog ->
            dialogCanceller = {
                if (packageName == it) {
@@ -143,7 +153,7 @@ class TileServiceRequestController constructor(
            }
        }
        return dialogCreator().apply {
            setTileData(tileData)
            setTileData(tileData, iUriGrantsManager)
            setShowForAllUsers(true)
            setCanceledOnTouchOutside(true)
            setOnCancelListener { responseHandler.accept(DISMISSED) }
@@ -168,7 +178,7 @@ class TileServiceRequestController constructor(
                        Log.w(TAG, "Malformed componentName ${args[0]}")
                        return
                    }
            requestTileAdd(componentName, args[1], args[2], null) {
            requestTileAdd(0, componentName, args[1], args[2], null) {
                Log.d(TAG, "Response: $it")
            }
        }
@@ -192,14 +202,16 @@ class TileServiceRequestController constructor(
    @SysUISingleton
    class Builder @Inject constructor(
        private val commandQueue: CommandQueue,
        private val commandRegistry: CommandRegistry
        private val commandRegistry: CommandRegistry,
        private val iUriGrantsManager: IUriGrantsManager,
    ) {
        fun create(qsHost: QSHost): TileServiceRequestController {
            return TileServiceRequestController(
                    qsHost,
                    commandQueue,
                    commandRegistry,
                    TileRequestDialogEventLogger()
                    TileRequestDialogEventLogger(),
                    iUriGrantsManager,
            )
        }
    }
Loading