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

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

Merge changes from topic "208671346"

* changes:
  Auto-deny requests after some user denials
  Add tests for StatusBarManagerService
parents 7b4bf2ee 3a738b18
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -626,6 +626,9 @@ public class StatusBarManager {
     * foreground ({@link ActivityManager.RunningAppProcessInfo#IMPORTANCE_FOREGROUND}
     * and the {@link android.service.quicksettings.TileService} must be exported.
     *
     * Note: the system can choose to auto-deny a request if the user has denied that specific
     * request (user, ComponentName) enough times before.
     *
     * @param tileServiceComponentName {@link ComponentName} of the
     *        {@link android.service.quicksettings.TileService} for the request.
     * @param tileLabel label of the tile to show to the user.
+22 −0
Original line number Diff line number Diff line
@@ -64,6 +64,7 @@ import android.service.quicksettings.TileService;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
@@ -139,6 +140,8 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
    private int mCurrentUserId;
    private boolean mTracingEnabled;

    private final TileRequestTracker mTileRequestTracker;

    private final SparseArray<UiState> mDisplayUiState = new SparseArray<>();
    @GuardedBy("mLock")
    private IUdfpsHbmListener mUdfpsHbmListener;
@@ -245,6 +248,8 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
        mActivityTaskManager = LocalServices.getService(ActivityTaskManagerInternal.class);
        mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
        mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);

        mTileRequestTracker = new TileRequestTracker(mContext);
    }

    @Override
@@ -1765,11 +1770,26 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
            mCurrentRequestAddTilePackages.put(packageName, currentTime);
        }

        if (mTileRequestTracker.shouldBeDenied(userId, componentName)) {
            if (clearTileAddRequest(packageName)) {
                try {
                    callback.onTileRequest(StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_NOT_ADDED);
                } catch (RemoteException e) {
                    Slog.e(TAG, "requestAddTile - callback", e);
                }
            }
            return;
        }

        IAddTileResultCallback proxyCallback = new IAddTileResultCallback.Stub() {
            @Override
            public void onTileRequest(int i) {
                if (i == StatusBarManager.TILE_ADD_REQUEST_RESULT_DIALOG_DISMISSED) {
                    i = StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_NOT_ADDED;
                } else if (i == StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_NOT_ADDED) {
                    mTileRequestTracker.addDenial(userId, componentName);
                } else if (i == StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_ADDED) {
                    mTileRequestTracker.resetRequests(userId, componentName);
                }
                if (clearTileAddRequest(packageName)) {
                    try {
@@ -1961,6 +1981,8 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D
                pw.println("    " + requests.get(i) + ",");
            }
            pw.println("  ]");
            IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
            mTileRequestTracker.dump(fd, ipw.increaseIndent(), args);
        }
    }

+138 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.statusbar;

import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.UserHandle;
import android.util.ArraySet;
import android.util.IndentingPrintWriter;
import android.util.SparseArrayMap;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;

import java.io.FileDescriptor;

/**
 * Tracks user denials of requests from {@link StatusBarManagerService#requestAddTile}.
 *
 * After a certain number of denials for a particular pair (user,ComponentName), requests will be
 * auto-denied without showing a dialog to the user.
 */
public class TileRequestTracker {

    @VisibleForTesting
    static final int MAX_NUM_DENIALS = 3;

    private final Context mContext;
    private final Object mLock = new Object();

    @GuardedBy("mLock")
    private final SparseArrayMap<ComponentName, Integer> mTrackingMap = new SparseArrayMap<>();
    @GuardedBy("mLock")
    private final ArraySet<ComponentName> mComponentsToRemove = new ArraySet<>();

    private final BroadcastReceiver mUninstallReceiver = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
                return;
            }

            Uri data = intent.getData();
            String packageName = data.getEncodedSchemeSpecificPart();

            if (!intent.hasExtra(Intent.EXTRA_UID)) {
                return;
            }
            int userId = UserHandle.getUserId(intent.getIntExtra(Intent.EXTRA_UID, -1));
            synchronized (mLock) {
                mComponentsToRemove.clear();
                final int elementsForUser = mTrackingMap.numElementsForKey(userId);
                final int userKeyIndex = mTrackingMap.indexOfKey(userId);
                for (int compKeyIndex = 0; compKeyIndex < elementsForUser; compKeyIndex++) {
                    ComponentName c = mTrackingMap.keyAt(userKeyIndex, compKeyIndex);
                    if (c.getPackageName().equals(packageName)) {
                        mComponentsToRemove.add(c);
                    }
                }
                final int compsToRemoveNum = mComponentsToRemove.size();
                for (int i = 0; i < compsToRemoveNum; i++) {
                    ComponentName c = mComponentsToRemove.valueAt(i);
                    mTrackingMap.delete(userId, c);
                }
            }
        }
    };

    TileRequestTracker(Context context) {
        mContext = context;

        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        intentFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED);
        intentFilter.addDataScheme("package");
        mContext.registerReceiverAsUser(mUninstallReceiver, UserHandle.ALL, intentFilter, null,
                null);
    }

    /**
     * Return whether this combination of {@code userId} and {@link ComponentName} should be
     * auto-denied.
     */
    boolean shouldBeDenied(int userId, ComponentName componentName) {
        synchronized (mLock) {
            return mTrackingMap.getOrDefault(userId, componentName, 0) >= MAX_NUM_DENIALS;
        }
    }

    /**
     * Add a new denial instance for a given {@code userId} and {@link ComponentName}.
     */
    void addDenial(int userId, ComponentName componentName) {
        synchronized (mLock) {
            int current = mTrackingMap.getOrDefault(userId, componentName, 0);
            mTrackingMap.add(userId, componentName, current + 1);
        }
    }

    /**
     * Reset the number of denied request for a given {@code userId} and {@link ComponentName}.
     */
    void resetRequests(int userId, ComponentName componentName) {
        synchronized (mLock) {
            mTrackingMap.delete(userId, componentName);
        }
    }

    void dump(FileDescriptor fd, IndentingPrintWriter pw, String[] args) {
        pw.println("TileRequestTracker:");
        pw.increaseIndent();
        synchronized (mLock) {
            mTrackingMap.forEach((user, componentName, value) -> {
                pw.println("user=" + user + ", " + componentName.toShortString() + ": " + value);
            });
        }
        pw.decreaseIndent();
    }
}
+130 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.statusbar;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Handler;
import android.os.UserHandle;
import android.testing.TestableContext;

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

import java.util.ArrayList;

/**
 * {@link ContextWrapper} that doesn't register {@link BroadcastReceiver}.
 *
 * Instead, it keeps a list of the registrations for querying.
 */
class NoBroadcastContextWrapper extends TestableContext {

    ArrayList<BroadcastReceiverRegistration> mRegistrationList =
            new ArrayList<>();

    NoBroadcastContextWrapper(Context context) {
        super(context);
    }

    @Override
    public Intent registerReceiver(@Nullable BroadcastReceiver receiver, IntentFilter filter) {
        return registerReceiver(receiver, filter, 0);
    }

    @Override
    public Intent registerReceiver(@Nullable BroadcastReceiver receiver, IntentFilter filter,
            int flags) {
        return registerReceiver(receiver, filter, null, null, flags);
    }

    @Override
    public Intent registerReceiver(@Nullable BroadcastReceiver receiver, IntentFilter filter,
            @Nullable String broadcastPermission, @Nullable Handler scheduler) {
        return registerReceiver(receiver, filter, broadcastPermission, scheduler, 0);
    }

    @Override
    public Intent registerReceiver(@Nullable BroadcastReceiver receiver, IntentFilter filter,
            @Nullable String broadcastPermission, @Nullable Handler scheduler, int flags) {
        return registerReceiverAsUser(receiver, getUser(), filter, broadcastPermission, scheduler,
                flags);
    }

    @Nullable
    @Override
    public Intent registerReceiverForAllUsers(@Nullable BroadcastReceiver receiver,
            @NonNull IntentFilter filter, @Nullable String broadcastPermission,
            @Nullable Handler scheduler) {
        return registerReceiverForAllUsers(receiver, filter, broadcastPermission, scheduler, 0);
    }

    @Nullable
    @Override
    public Intent registerReceiverForAllUsers(@Nullable BroadcastReceiver receiver,
            @NonNull IntentFilter filter, @Nullable String broadcastPermission,
            @Nullable Handler scheduler, int flags) {
        return registerReceiverAsUser(receiver, UserHandle.ALL, filter, broadcastPermission,
                scheduler, flags);
    }

    @Override
    public Intent registerReceiverAsUser(@Nullable BroadcastReceiver receiver, UserHandle user,
            IntentFilter filter, @Nullable String broadcastPermission,
            @Nullable Handler scheduler) {
        return registerReceiverAsUser(receiver, user, filter, broadcastPermission,
                scheduler, 0);
    }

    @Override
    public Intent registerReceiverAsUser(@Nullable BroadcastReceiver receiver, UserHandle user,
            IntentFilter filter, @Nullable String broadcastPermission,
            @Nullable Handler scheduler, int flags) {
        BroadcastReceiverRegistration reg = new BroadcastReceiverRegistration(
                receiver, user, filter, broadcastPermission, scheduler, flags
        );
        mRegistrationList.add(reg);
        return null;
    }

    @Override
    public void unregisterReceiver(BroadcastReceiver receiver) {
        mRegistrationList.removeIf((reg) -> reg.mReceiver == receiver);
    }

    static class BroadcastReceiverRegistration {
        final BroadcastReceiver mReceiver;
        final UserHandle mUser;
        final IntentFilter mIntentFilter;
        final String mBroadcastPermission;
        final Handler mHandler;
        final int mFlags;

        BroadcastReceiverRegistration(BroadcastReceiver receiver, UserHandle user,
                IntentFilter intentFilter, String broadcastPermission, Handler handler, int flags) {
            mReceiver = receiver;
            mUser = user;
            mIntentFilter = intentFilter;
            mBroadcastPermission = broadcastPermission;
            mHandler = handler;
            mFlags = flags;
        }
    }
}
+659 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading