Merge changes from topic "208671346"
* changes: Auto-deny requests after some user denials Add tests for StatusBarManagerService
This commit is contained in:
committed by
Android (Google) Code Review
commit
8f3255ae3b
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,659 @@
|
||||
/*
|
||||
* 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 static android.app.ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE;
|
||||
import static android.app.ActivityManager.PROCESS_STATE_TOP;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.mockito.ArgumentMatchers.nullable;
|
||||
import static org.mockito.Mockito.any;
|
||||
import static org.mockito.Mockito.anyInt;
|
||||
import static org.mockito.Mockito.anyString;
|
||||
import static org.mockito.Mockito.argThat;
|
||||
import static org.mockito.Mockito.eq;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.ActivityManagerInternal;
|
||||
import android.app.StatusBarManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.graphics.drawable.Icon;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.os.Binder;
|
||||
import android.os.Looper;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.service.quicksettings.TileService;
|
||||
import android.testing.TestableContext;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
|
||||
import com.android.internal.statusbar.IAddTileResultCallback;
|
||||
import com.android.internal.statusbar.IStatusBar;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.policy.GlobalActionsProvider;
|
||||
import com.android.server.wm.ActivityTaskManagerInternal;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.ArgumentMatcher;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
public class StatusBarManagerServiceTest {
|
||||
|
||||
private static final String TEST_PACKAGE = "test_pkg";
|
||||
private static final String TEST_SERVICE = "test_svc";
|
||||
private static final ComponentName TEST_COMPONENT = new ComponentName(TEST_PACKAGE,
|
||||
TEST_SERVICE);
|
||||
private static final CharSequence APP_NAME = "AppName";
|
||||
private static final CharSequence TILE_LABEL = "Tile label";
|
||||
|
||||
@Rule
|
||||
public final TestableContext mContext =
|
||||
new NoBroadcastContextWrapper(InstrumentationRegistry.getContext());
|
||||
|
||||
@Mock
|
||||
private ActivityTaskManagerInternal mActivityTaskManagerInternal;
|
||||
@Mock
|
||||
private PackageManagerInternal mPackageManagerInternal;
|
||||
@Mock
|
||||
private ActivityManagerInternal mActivityManagerInternal;
|
||||
@Mock
|
||||
private ApplicationInfo mApplicationInfo;
|
||||
@Mock
|
||||
private IStatusBar.Stub mMockStatusBar;
|
||||
@Captor
|
||||
private ArgumentCaptor<IAddTileResultCallback> mAddTileResultCallbackCaptor;
|
||||
|
||||
private Icon mIcon;
|
||||
private StatusBarManagerService mStatusBarManagerService;
|
||||
|
||||
@BeforeClass
|
||||
public static void oneTimeInitialization() {
|
||||
if (Looper.myLooper() == null) {
|
||||
Looper.prepare();
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
LocalServices.removeServiceForTest(ActivityTaskManagerInternal.class);
|
||||
LocalServices.addService(ActivityTaskManagerInternal.class, mActivityTaskManagerInternal);
|
||||
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
|
||||
LocalServices.addService(ActivityManagerInternal.class, mActivityManagerInternal);
|
||||
LocalServices.removeServiceForTest(PackageManagerInternal.class);
|
||||
LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal);
|
||||
|
||||
when(mMockStatusBar.asBinder()).thenReturn(mMockStatusBar);
|
||||
when(mApplicationInfo.loadLabel(any())).thenReturn(APP_NAME);
|
||||
|
||||
mStatusBarManagerService = new StatusBarManagerService(mContext);
|
||||
LocalServices.removeServiceForTest(StatusBarManagerInternal.class);
|
||||
LocalServices.removeServiceForTest(GlobalActionsProvider.class);
|
||||
|
||||
mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(
|
||||
mStatusBarManagerService);
|
||||
|
||||
mStatusBarManagerService.registerStatusBar(mMockStatusBar);
|
||||
|
||||
mIcon = Icon.createWithResource(mContext, android.R.drawable.btn_plus);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHandleIncomingUserCalled() {
|
||||
int fakeUser = 17;
|
||||
try {
|
||||
mStatusBarManagerService.requestAddTile(
|
||||
TEST_COMPONENT,
|
||||
TILE_LABEL,
|
||||
mIcon,
|
||||
fakeUser,
|
||||
new Callback()
|
||||
);
|
||||
fail("Should have SecurityException from uid check");
|
||||
} catch (SecurityException e) {
|
||||
verify(mActivityManagerInternal).handleIncomingUser(
|
||||
eq(Binder.getCallingPid()),
|
||||
eq(Binder.getCallingUid()),
|
||||
eq(fakeUser),
|
||||
eq(false),
|
||||
eq(ActivityManagerInternal.ALLOW_NON_FULL),
|
||||
anyString(),
|
||||
eq(TEST_PACKAGE)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckUid_pass() {
|
||||
when(mPackageManagerInternal.getPackageUid(TEST_PACKAGE, 0, mContext.getUserId()))
|
||||
.thenReturn(Binder.getCallingUid());
|
||||
try {
|
||||
mStatusBarManagerService.requestAddTile(
|
||||
TEST_COMPONENT,
|
||||
TILE_LABEL,
|
||||
mIcon,
|
||||
mContext.getUserId(),
|
||||
new Callback()
|
||||
);
|
||||
} catch (SecurityException e) {
|
||||
fail("No SecurityException should be thrown");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckUid_pass_differentUser() {
|
||||
int otherUserUid = UserHandle.getUid(17, UserHandle.getAppId(Binder.getCallingUid()));
|
||||
when(mPackageManagerInternal.getPackageUid(TEST_PACKAGE, 0, mContext.getUserId()))
|
||||
.thenReturn(otherUserUid);
|
||||
try {
|
||||
mStatusBarManagerService.requestAddTile(
|
||||
TEST_COMPONENT,
|
||||
TILE_LABEL,
|
||||
mIcon,
|
||||
mContext.getUserId(),
|
||||
new Callback()
|
||||
);
|
||||
} catch (SecurityException e) {
|
||||
fail("No SecurityException should be thrown");
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCheckUid_fail() {
|
||||
when(mPackageManagerInternal.getPackageUid(TEST_PACKAGE, 0, mContext.getUserId()))
|
||||
.thenReturn(Binder.getCallingUid() + 1);
|
||||
try {
|
||||
mStatusBarManagerService.requestAddTile(
|
||||
TEST_COMPONENT,
|
||||
TILE_LABEL,
|
||||
mIcon,
|
||||
mContext.getUserId(),
|
||||
new Callback()
|
||||
);
|
||||
fail("Should throw SecurityException");
|
||||
} catch (SecurityException e) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCurrentUser_fail() {
|
||||
mockUidCheck();
|
||||
int user = 0;
|
||||
when(mActivityManagerInternal.getCurrentUserId()).thenReturn(user + 1);
|
||||
|
||||
Callback callback = new Callback();
|
||||
mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback);
|
||||
|
||||
assertEquals(StatusBarManager.TILE_ADD_REQUEST_ERROR_NOT_CURRENT_USER,
|
||||
callback.mUserResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCurrentUser_pass() {
|
||||
mockUidCheck();
|
||||
int user = 0;
|
||||
when(mActivityManagerInternal.getCurrentUserId()).thenReturn(user);
|
||||
|
||||
Callback callback = new Callback();
|
||||
mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback);
|
||||
|
||||
assertNotEquals(StatusBarManager.TILE_ADD_REQUEST_ERROR_NOT_CURRENT_USER,
|
||||
callback.mUserResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidComponent_fail_noComponentFound() {
|
||||
int user = 10;
|
||||
mockUidCheck();
|
||||
mockCurrentUserCheck(user);
|
||||
IntentMatcher im = new IntentMatcher(
|
||||
new Intent(TileService.ACTION_QS_TILE).setComponent(TEST_COMPONENT));
|
||||
when(mPackageManagerInternal.resolveService(argThat(im), nullable(String.class), eq(0),
|
||||
eq(user), anyInt())).thenReturn(null);
|
||||
|
||||
Callback callback = new Callback();
|
||||
mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback);
|
||||
|
||||
assertEquals(StatusBarManager.TILE_ADD_REQUEST_ERROR_BAD_COMPONENT, callback.mUserResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidComponent_fail_notEnabled() {
|
||||
int user = 10;
|
||||
mockUidCheck();
|
||||
mockCurrentUserCheck(user);
|
||||
|
||||
ResolveInfo r = makeResolveInfo();
|
||||
r.serviceInfo.permission = Manifest.permission.BIND_QUICK_SETTINGS_TILE;
|
||||
|
||||
IntentMatcher im = new IntentMatcher(
|
||||
new Intent(TileService.ACTION_QS_TILE).setComponent(TEST_COMPONENT));
|
||||
when(mPackageManagerInternal.resolveService(argThat(im), nullable(String.class), eq(0),
|
||||
eq(user), anyInt())).thenReturn(r);
|
||||
when(mPackageManagerInternal.getComponentEnabledSetting(TEST_COMPONENT,
|
||||
Binder.getCallingUid(), user)).thenReturn(
|
||||
PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
|
||||
|
||||
Callback callback = new Callback();
|
||||
mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback);
|
||||
|
||||
assertEquals(StatusBarManager.TILE_ADD_REQUEST_ERROR_BAD_COMPONENT, callback.mUserResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidComponent_fail_noPermission() {
|
||||
int user = 10;
|
||||
mockUidCheck();
|
||||
mockCurrentUserCheck(user);
|
||||
|
||||
ResolveInfo r = makeResolveInfo();
|
||||
|
||||
IntentMatcher im = new IntentMatcher(
|
||||
new Intent(TileService.ACTION_QS_TILE).setComponent(TEST_COMPONENT));
|
||||
when(mPackageManagerInternal.resolveService(argThat(im), nullable(String.class), eq(0),
|
||||
eq(user), anyInt())).thenReturn(r);
|
||||
when(mPackageManagerInternal.getComponentEnabledSetting(TEST_COMPONENT,
|
||||
Binder.getCallingUid(), user)).thenReturn(
|
||||
PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
|
||||
|
||||
Callback callback = new Callback();
|
||||
mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback);
|
||||
|
||||
assertEquals(StatusBarManager.TILE_ADD_REQUEST_ERROR_BAD_COMPONENT, callback.mUserResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidComponent_fail_notExported() {
|
||||
int user = 10;
|
||||
mockUidCheck();
|
||||
mockCurrentUserCheck(user);
|
||||
|
||||
ResolveInfo r = makeResolveInfo();
|
||||
r.serviceInfo.permission = Manifest.permission.BIND_QUICK_SETTINGS_TILE;
|
||||
r.serviceInfo.exported = false;
|
||||
|
||||
IntentMatcher im = new IntentMatcher(
|
||||
new Intent(TileService.ACTION_QS_TILE).setComponent(TEST_COMPONENT));
|
||||
when(mPackageManagerInternal.resolveService(argThat(im), nullable(String.class), eq(0),
|
||||
eq(user), anyInt())).thenReturn(r);
|
||||
when(mPackageManagerInternal.getComponentEnabledSetting(TEST_COMPONENT,
|
||||
Binder.getCallingUid(), user)).thenReturn(
|
||||
PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
|
||||
|
||||
Callback callback = new Callback();
|
||||
mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback);
|
||||
|
||||
assertEquals(StatusBarManager.TILE_ADD_REQUEST_ERROR_BAD_COMPONENT, callback.mUserResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidComponent_pass() {
|
||||
int user = 10;
|
||||
mockUidCheck();
|
||||
mockCurrentUserCheck(user);
|
||||
|
||||
ResolveInfo r = makeResolveInfo();
|
||||
r.serviceInfo.permission = Manifest.permission.BIND_QUICK_SETTINGS_TILE;
|
||||
r.serviceInfo.exported = true;
|
||||
|
||||
IntentMatcher im = new IntentMatcher(
|
||||
new Intent(TileService.ACTION_QS_TILE).setComponent(TEST_COMPONENT));
|
||||
when(mPackageManagerInternal.resolveService(argThat(im), nullable(String.class), eq(0),
|
||||
eq(user), anyInt())).thenReturn(r);
|
||||
when(mPackageManagerInternal.getComponentEnabledSetting(TEST_COMPONENT,
|
||||
Binder.getCallingUid(), user)).thenReturn(
|
||||
PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
|
||||
|
||||
Callback callback = new Callback();
|
||||
mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback);
|
||||
|
||||
assertNotEquals(StatusBarManager.TILE_ADD_REQUEST_ERROR_BAD_COMPONENT,
|
||||
callback.mUserResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAppInForeground_fail() {
|
||||
int user = 10;
|
||||
mockUidCheck();
|
||||
mockCurrentUserCheck(user);
|
||||
mockComponentInfo(user);
|
||||
|
||||
when(mActivityManagerInternal.getUidProcessState(Binder.getCallingUid())).thenReturn(
|
||||
PROCESS_STATE_FOREGROUND_SERVICE);
|
||||
|
||||
Callback callback = new Callback();
|
||||
mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback);
|
||||
|
||||
assertEquals(StatusBarManager.TILE_ADD_REQUEST_ERROR_APP_NOT_IN_FOREGROUND,
|
||||
callback.mUserResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAppInForeground_pass() {
|
||||
int user = 10;
|
||||
mockUidCheck();
|
||||
mockCurrentUserCheck(user);
|
||||
mockComponentInfo(user);
|
||||
|
||||
when(mActivityManagerInternal.getUidProcessState(Binder.getCallingUid())).thenReturn(
|
||||
PROCESS_STATE_TOP);
|
||||
|
||||
Callback callback = new Callback();
|
||||
mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback);
|
||||
|
||||
assertNotEquals(StatusBarManager.TILE_ADD_REQUEST_ERROR_APP_NOT_IN_FOREGROUND,
|
||||
callback.mUserResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestToStatusBar() throws RemoteException {
|
||||
int user = 10;
|
||||
mockEverything(user);
|
||||
|
||||
mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user,
|
||||
new Callback());
|
||||
|
||||
verify(mMockStatusBar).requestAddTile(
|
||||
eq(TEST_COMPONENT),
|
||||
eq(APP_NAME),
|
||||
eq(TILE_LABEL),
|
||||
eq(mIcon),
|
||||
any()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestInProgress_samePackage() throws RemoteException {
|
||||
int user = 10;
|
||||
mockEverything(user);
|
||||
|
||||
mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user,
|
||||
new Callback());
|
||||
|
||||
Callback callback = new Callback();
|
||||
mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback);
|
||||
|
||||
assertEquals(StatusBarManager.TILE_ADD_REQUEST_ERROR_REQUEST_IN_PROGRESS,
|
||||
callback.mUserResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestInProgress_differentPackage() throws RemoteException {
|
||||
int user = 10;
|
||||
mockEverything(user);
|
||||
ComponentName otherComponent = new ComponentName("a", "b");
|
||||
mockUidCheck(otherComponent.getPackageName());
|
||||
mockComponentInfo(user, otherComponent);
|
||||
|
||||
mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user,
|
||||
new Callback());
|
||||
|
||||
Callback callback = new Callback();
|
||||
mStatusBarManagerService.requestAddTile(otherComponent, TILE_LABEL, mIcon, user, callback);
|
||||
|
||||
assertNotEquals(StatusBarManager.TILE_ADD_REQUEST_ERROR_REQUEST_IN_PROGRESS,
|
||||
callback.mUserResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponseForwardedToCallback_tileAdded() throws RemoteException {
|
||||
int user = 10;
|
||||
mockEverything(user);
|
||||
|
||||
Callback callback = new Callback();
|
||||
mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback);
|
||||
|
||||
verify(mMockStatusBar).requestAddTile(
|
||||
eq(TEST_COMPONENT),
|
||||
eq(APP_NAME),
|
||||
eq(TILE_LABEL),
|
||||
eq(mIcon),
|
||||
mAddTileResultCallbackCaptor.capture()
|
||||
);
|
||||
|
||||
mAddTileResultCallbackCaptor.getValue().onTileRequest(
|
||||
StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_ADDED);
|
||||
assertEquals(StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_ADDED, callback.mUserResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponseForwardedToCallback_tileNotAdded() throws RemoteException {
|
||||
int user = 10;
|
||||
mockEverything(user);
|
||||
|
||||
Callback callback = new Callback();
|
||||
mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback);
|
||||
|
||||
verify(mMockStatusBar).requestAddTile(
|
||||
eq(TEST_COMPONENT),
|
||||
eq(APP_NAME),
|
||||
eq(TILE_LABEL),
|
||||
eq(mIcon),
|
||||
mAddTileResultCallbackCaptor.capture()
|
||||
);
|
||||
|
||||
mAddTileResultCallbackCaptor.getValue().onTileRequest(
|
||||
StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_NOT_ADDED);
|
||||
assertEquals(StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_NOT_ADDED,
|
||||
callback.mUserResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponseForwardedToCallback_tileAlreadyAdded() throws RemoteException {
|
||||
int user = 10;
|
||||
mockEverything(user);
|
||||
|
||||
Callback callback = new Callback();
|
||||
mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback);
|
||||
|
||||
verify(mMockStatusBar).requestAddTile(
|
||||
eq(TEST_COMPONENT),
|
||||
eq(APP_NAME),
|
||||
eq(TILE_LABEL),
|
||||
eq(mIcon),
|
||||
mAddTileResultCallbackCaptor.capture()
|
||||
);
|
||||
|
||||
mAddTileResultCallbackCaptor.getValue().onTileRequest(
|
||||
StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_ALREADY_ADDED);
|
||||
assertEquals(StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_ALREADY_ADDED,
|
||||
callback.mUserResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResponseForwardedToCallback_dialogDismissed() throws RemoteException {
|
||||
int user = 10;
|
||||
mockEverything(user);
|
||||
|
||||
Callback callback = new Callback();
|
||||
mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback);
|
||||
|
||||
verify(mMockStatusBar).requestAddTile(
|
||||
eq(TEST_COMPONENT),
|
||||
eq(APP_NAME),
|
||||
eq(TILE_LABEL),
|
||||
eq(mIcon),
|
||||
mAddTileResultCallbackCaptor.capture()
|
||||
);
|
||||
|
||||
mAddTileResultCallbackCaptor.getValue().onTileRequest(
|
||||
StatusBarManager.TILE_ADD_REQUEST_RESULT_DIALOG_DISMISSED);
|
||||
// This gets translated to TILE_NOT_ADDED
|
||||
assertEquals(StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_NOT_ADDED,
|
||||
callback.mUserResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInstaDenialAfterManyDenials() throws RemoteException {
|
||||
int user = 10;
|
||||
mockEverything(user);
|
||||
|
||||
for (int i = 0; i < TileRequestTracker.MAX_NUM_DENIALS; i++) {
|
||||
mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user,
|
||||
new Callback());
|
||||
|
||||
verify(mMockStatusBar, times(i + 1)).requestAddTile(
|
||||
eq(TEST_COMPONENT),
|
||||
eq(APP_NAME),
|
||||
eq(TILE_LABEL),
|
||||
eq(mIcon),
|
||||
mAddTileResultCallbackCaptor.capture()
|
||||
);
|
||||
mAddTileResultCallbackCaptor.getValue().onTileRequest(
|
||||
StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_NOT_ADDED);
|
||||
}
|
||||
|
||||
Callback callback = new Callback();
|
||||
mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user, callback);
|
||||
|
||||
// Only called MAX_NUM_DENIALS times
|
||||
verify(mMockStatusBar, times(TileRequestTracker.MAX_NUM_DENIALS)).requestAddTile(
|
||||
any(),
|
||||
any(),
|
||||
any(),
|
||||
any(),
|
||||
mAddTileResultCallbackCaptor.capture()
|
||||
);
|
||||
assertEquals(StatusBarManager.TILE_ADD_REQUEST_RESULT_TILE_NOT_ADDED,
|
||||
callback.mUserResponse);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDialogDismissalNotCountingAgainstDenials() throws RemoteException {
|
||||
int user = 10;
|
||||
mockEverything(user);
|
||||
|
||||
for (int i = 0; i < TileRequestTracker.MAX_NUM_DENIALS * 2; i++) {
|
||||
mStatusBarManagerService.requestAddTile(TEST_COMPONENT, TILE_LABEL, mIcon, user,
|
||||
new Callback());
|
||||
|
||||
verify(mMockStatusBar, times(i + 1)).requestAddTile(
|
||||
eq(TEST_COMPONENT),
|
||||
eq(APP_NAME),
|
||||
eq(TILE_LABEL),
|
||||
eq(mIcon),
|
||||
mAddTileResultCallbackCaptor.capture()
|
||||
);
|
||||
mAddTileResultCallbackCaptor.getValue().onTileRequest(
|
||||
StatusBarManager.TILE_ADD_REQUEST_RESULT_DIALOG_DISMISSED);
|
||||
}
|
||||
}
|
||||
|
||||
private void mockUidCheck() {
|
||||
mockUidCheck(TEST_PACKAGE);
|
||||
}
|
||||
|
||||
private void mockUidCheck(String packageName) {
|
||||
when(mPackageManagerInternal.getPackageUid(eq(packageName), anyInt(), anyInt()))
|
||||
.thenReturn(Binder.getCallingUid());
|
||||
}
|
||||
|
||||
private void mockCurrentUserCheck(int user) {
|
||||
when(mActivityManagerInternal.getCurrentUserId()).thenReturn(user);
|
||||
}
|
||||
|
||||
private void mockComponentInfo(int user) {
|
||||
mockComponentInfo(user, TEST_COMPONENT);
|
||||
}
|
||||
|
||||
private ResolveInfo makeResolveInfo() {
|
||||
ResolveInfo r = new ResolveInfo();
|
||||
r.serviceInfo = new ServiceInfo();
|
||||
r.serviceInfo.applicationInfo = mApplicationInfo;
|
||||
return r;
|
||||
}
|
||||
|
||||
private void mockComponentInfo(int user, ComponentName componentName) {
|
||||
ResolveInfo r = makeResolveInfo();
|
||||
r.serviceInfo.exported = true;
|
||||
r.serviceInfo.permission = Manifest.permission.BIND_QUICK_SETTINGS_TILE;
|
||||
|
||||
IntentMatcher im = new IntentMatcher(
|
||||
new Intent(TileService.ACTION_QS_TILE).setComponent(componentName));
|
||||
when(mPackageManagerInternal.resolveService(argThat(im), nullable(String.class), eq(0),
|
||||
eq(user), anyInt())).thenReturn(r);
|
||||
when(mPackageManagerInternal.getComponentEnabledSetting(componentName,
|
||||
Binder.getCallingUid(), user)).thenReturn(
|
||||
PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
|
||||
}
|
||||
|
||||
private void mockProcessState() {
|
||||
when(mActivityManagerInternal.getUidProcessState(Binder.getCallingUid())).thenReturn(
|
||||
PROCESS_STATE_TOP);
|
||||
}
|
||||
|
||||
private void mockEverything(int user) {
|
||||
mockUidCheck();
|
||||
mockCurrentUserCheck(user);
|
||||
mockComponentInfo(user);
|
||||
mockProcessState();
|
||||
}
|
||||
|
||||
private static class Callback extends IAddTileResultCallback.Stub {
|
||||
int mUserResponse = -1;
|
||||
|
||||
@Override
|
||||
public void onTileRequest(int userResponse) throws RemoteException {
|
||||
if (mUserResponse != -1) {
|
||||
throw new IllegalStateException(
|
||||
"Setting response to " + userResponse + " but it already has "
|
||||
+ mUserResponse);
|
||||
}
|
||||
mUserResponse = userResponse;
|
||||
}
|
||||
}
|
||||
|
||||
private static class IntentMatcher implements ArgumentMatcher<Intent> {
|
||||
private final Intent mIntent;
|
||||
|
||||
IntentMatcher(Intent intent) {
|
||||
mIntent = intent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Intent argument) {
|
||||
return argument != null && argument.filterEquals(mIntent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Expected: " + mIntent;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* 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 static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.net.Uri;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import androidx.test.InstrumentationRegistry;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
@RunWith(JUnit4.class)
|
||||
public class TileRequestTrackerTest {
|
||||
|
||||
private static final String TEST_PACKAGE = "test_pkg";
|
||||
private static final String TEST_SERVICE = "test_svc";
|
||||
private static final String TEST_SERVICE_OTHER = "test_svc_other";
|
||||
private static final ComponentName TEST_COMPONENT = new ComponentName(TEST_PACKAGE,
|
||||
TEST_SERVICE);
|
||||
private static final ComponentName TEST_COMPONENT_OTHER = new ComponentName(TEST_PACKAGE,
|
||||
TEST_SERVICE_OTHER);
|
||||
private static final ComponentName TEST_COMPONENT_OTHER_PACKAGE = new ComponentName("other",
|
||||
TEST_SERVICE);
|
||||
private static final int USER_ID = 0;
|
||||
private static final int USER_ID_OTHER = 10;
|
||||
private static final int APP_UID = 12345;
|
||||
private static final int USER_UID = UserHandle.getUid(USER_ID, APP_UID);
|
||||
private static final int USER_OTHER_UID = UserHandle.getUid(USER_ID_OTHER, APP_UID);
|
||||
|
||||
@Rule
|
||||
public final NoBroadcastContextWrapper mContext =
|
||||
new NoBroadcastContextWrapper(InstrumentationRegistry.getContext());
|
||||
|
||||
private TileRequestTracker mTileRequestTracker;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
|
||||
mTileRequestTracker = new TileRequestTracker(mContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBroadcastReceiverRegistered() {
|
||||
NoBroadcastContextWrapper.BroadcastReceiverRegistration reg = getReceiverRegistration();
|
||||
|
||||
assertEquals(UserHandle.ALL, reg.mUser);
|
||||
assertNull(reg.mBroadcastPermission);
|
||||
assertNotNull(reg.mReceiver);
|
||||
|
||||
IntentFilter filter = reg.mIntentFilter;
|
||||
assertEquals(2, filter.countActions());
|
||||
assertTrue(filter.hasAction(Intent.ACTION_PACKAGE_REMOVED));
|
||||
assertTrue(filter.hasAction(Intent.ACTION_PACKAGE_DATA_CLEARED));
|
||||
assertTrue(filter.hasDataScheme("package"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoDenialsFromStart() {
|
||||
// Certainly not an exhaustive test
|
||||
assertFalse(mTileRequestTracker.shouldBeDenied(USER_ID, TEST_COMPONENT));
|
||||
assertFalse(mTileRequestTracker.shouldBeDenied(USER_ID_OTHER, TEST_COMPONENT));
|
||||
assertFalse(mTileRequestTracker.shouldBeDenied(USER_ID, TEST_COMPONENT_OTHER));
|
||||
assertFalse(mTileRequestTracker.shouldBeDenied(USER_ID_OTHER, TEST_COMPONENT_OTHER));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNoDenialBeforeMax() {
|
||||
for (int i = 1; i < TileRequestTracker.MAX_NUM_DENIALS; i++) {
|
||||
mTileRequestTracker.addDenial(USER_ID, TEST_COMPONENT);
|
||||
}
|
||||
|
||||
assertFalse(mTileRequestTracker.shouldBeDenied(USER_ID, TEST_COMPONENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDenialOnMax() {
|
||||
for (int i = 1; i <= TileRequestTracker.MAX_NUM_DENIALS; i++) {
|
||||
mTileRequestTracker.addDenial(USER_ID, TEST_COMPONENT);
|
||||
}
|
||||
assertTrue(mTileRequestTracker.shouldBeDenied(USER_ID, TEST_COMPONENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDenialPerUser() {
|
||||
for (int i = 1; i <= TileRequestTracker.MAX_NUM_DENIALS; i++) {
|
||||
mTileRequestTracker.addDenial(USER_ID, TEST_COMPONENT);
|
||||
}
|
||||
|
||||
assertFalse(mTileRequestTracker.shouldBeDenied(USER_ID_OTHER, TEST_COMPONENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDenialPerComponent() {
|
||||
for (int i = 1; i <= TileRequestTracker.MAX_NUM_DENIALS; i++) {
|
||||
mTileRequestTracker.addDenial(USER_ID, TEST_COMPONENT);
|
||||
}
|
||||
|
||||
assertFalse(mTileRequestTracker.shouldBeDenied(USER_ID, TEST_COMPONENT_OTHER));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPackageUninstallRemovesDenials_allComponents() {
|
||||
for (int i = 1; i <= TileRequestTracker.MAX_NUM_DENIALS; i++) {
|
||||
mTileRequestTracker.addDenial(USER_ID, TEST_COMPONENT);
|
||||
mTileRequestTracker.addDenial(USER_ID, TEST_COMPONENT_OTHER);
|
||||
}
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED);
|
||||
intent.putExtra(Intent.EXTRA_UID, USER_UID);
|
||||
intent.setData(Uri.parse("package:" + TEST_PACKAGE));
|
||||
getReceiverRegistration().mReceiver.onReceive(mContext, intent);
|
||||
|
||||
assertFalse(mTileRequestTracker.shouldBeDenied(USER_ID, TEST_COMPONENT));
|
||||
assertFalse(mTileRequestTracker.shouldBeDenied(USER_ID, TEST_COMPONENT_OTHER));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPackageUninstallRemoveDenials_differentUsers() {
|
||||
for (int i = 1; i <= TileRequestTracker.MAX_NUM_DENIALS; i++) {
|
||||
mTileRequestTracker.addDenial(USER_ID, TEST_COMPONENT);
|
||||
mTileRequestTracker.addDenial(USER_ID_OTHER, TEST_COMPONENT);
|
||||
}
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED);
|
||||
intent.putExtra(Intent.EXTRA_UID, USER_OTHER_UID);
|
||||
intent.setData(Uri.parse("package:" + TEST_PACKAGE));
|
||||
getReceiverRegistration().mReceiver.onReceive(mContext, intent);
|
||||
|
||||
// User 0 package was not removed
|
||||
assertTrue(mTileRequestTracker.shouldBeDenied(USER_ID, TEST_COMPONENT));
|
||||
// User 10 package was removed
|
||||
assertFalse(mTileRequestTracker.shouldBeDenied(USER_ID_OTHER, TEST_COMPONENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPackageUninstallRemoveDenials_differentPackages() {
|
||||
for (int i = 1; i <= TileRequestTracker.MAX_NUM_DENIALS; i++) {
|
||||
mTileRequestTracker.addDenial(USER_ID, TEST_COMPONENT);
|
||||
mTileRequestTracker.addDenial(USER_ID, TEST_COMPONENT_OTHER_PACKAGE);
|
||||
}
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED);
|
||||
intent.putExtra(Intent.EXTRA_UID, USER_UID);
|
||||
intent.setData(Uri.parse("package:" + TEST_PACKAGE));
|
||||
getReceiverRegistration().mReceiver.onReceive(mContext, intent);
|
||||
|
||||
// Package TEST_PACKAGE removed
|
||||
assertFalse(mTileRequestTracker.shouldBeDenied(USER_ID, TEST_COMPONENT));
|
||||
// Package "other" not removed
|
||||
assertTrue(mTileRequestTracker.shouldBeDenied(USER_ID, TEST_COMPONENT_OTHER_PACKAGE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPackageUpdateDoesntRemoveDenials() {
|
||||
for (int i = 1; i <= TileRequestTracker.MAX_NUM_DENIALS; i++) {
|
||||
mTileRequestTracker.addDenial(USER_ID, TEST_COMPONENT);
|
||||
}
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_PACKAGE_REMOVED);
|
||||
intent.putExtra(Intent.EXTRA_REPLACING, true);
|
||||
intent.putExtra(Intent.EXTRA_UID, USER_UID);
|
||||
intent.setData(Uri.parse("package:" + TEST_PACKAGE));
|
||||
getReceiverRegistration().mReceiver.onReceive(mContext, intent);
|
||||
|
||||
assertTrue(mTileRequestTracker.shouldBeDenied(USER_ID, TEST_COMPONENT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClearPackageDataRemovesDenials() {
|
||||
for (int i = 1; i <= TileRequestTracker.MAX_NUM_DENIALS; i++) {
|
||||
mTileRequestTracker.addDenial(USER_ID, TEST_COMPONENT);
|
||||
}
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED);
|
||||
intent.putExtra(Intent.EXTRA_UID, USER_UID);
|
||||
intent.setData(Uri.parse("package:" + TEST_PACKAGE));
|
||||
getReceiverRegistration().mReceiver.onReceive(mContext, intent);
|
||||
|
||||
assertFalse(mTileRequestTracker.shouldBeDenied(USER_ID, TEST_COMPONENT));
|
||||
}
|
||||
|
||||
private NoBroadcastContextWrapper.BroadcastReceiverRegistration getReceiverRegistration() {
|
||||
assertEquals(1, mContext.mRegistrationList.size());
|
||||
return mContext.mRegistrationList.get(0);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user