* any locks in this method.
*/
public abstract void onSystemLocaleChangedNoLock();
+
+ /**
+ * Called by PM before sending package broadcasts to other components. PM doesn't hold the PM
+ * lock, but do not take any locks in here anyway, and don't do any heavy tasks, as doing so
+ * would slow down all the package broadcasts.
+ */
+ public abstract void onPackageBroadcast(Intent intent);
}
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.ShortcutServiceInternal;
import android.content.pm.Signature;
import android.content.pm.UserInfo;
import android.content.pm.VerifierDeviceIdentity;
} else {
resolvedUserIds = userIds;
}
+ final ShortcutServiceInternal shortcutService =
+ LocalServices.getService(ShortcutServiceInternal.class);
+
for (int id : resolvedUserIds) {
final Intent intent = new Intent(action,
pkg != null ? Uri.fromParts("package", pkg, null) : null);
+ intent.toShortString(false, true, false, false)
+ " " + intent.getExtras(), here);
}
+ // TODO b/29385425 Consider making lifecycle callbacks for this.
+ if (shortcutService != null) {
+ shortcutService.onPackageBroadcast(intent);
+ }
am.broadcastIntent(null, intent, null, finishedReceiver,
0, null, null, null, android.app.AppOpsManager.OP_NONE,
null, finishedReceiver != null, false, id);
--- /dev/null
+/*
+ * Copyright (C) 2016 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.pm;
+
+import android.annotation.NonNull;
+import android.util.Slog;
+
+import java.io.PrintWriter;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.function.BooleanSupplier;
+import java.util.function.Consumer;
+import java.util.logging.Handler;
+
+/**
+ * Used by {@link ShortcutService} to register tasks to be executed on Handler and also wait for
+ * all pending tasks.
+ *
+ * Tasks can be registered with {@link #addTask(Runnable)}. Call {@link #waitOnAllTasks()} to wait
+ * on all tasks that have been registered.
+ *
+ * In order to avoid deadlocks, {@link #waitOnAllTasks} MUST NOT be called with any lock held, nor
+ * on the handler thread. These conditions are checked by {@link #mWaitThreadChecker} and wtf'ed.
+ *
+ * During unit tests, we can't run tasks asynchronously, so we just run Runnables synchronously,
+ * which also means the "is lock held" check doesn't work properly during unit tests (e.g. normally
+ * when a Runnable is executed on a Handler, the thread doesn't hold any lock, but during the tests
+ * we just run a Runnable on the thread that registers it, so the thread may or may not hold locks.)
+ * So unfortunately we have to disable {@link #mWaitThreadChecker} during unit tests.
+ *
+ * Because of the complications like those, this class should be used only for specific purposes:
+ * - {@link #addTask(Runnable)} should only be used to register tasks on callbacks from lower level
+ * services like the package manager or the activity manager.
+ *
+ * - {@link #waitOnAllTasks} should only be called at the entry point of RPC calls (or the test only
+ * accessors}.
+ */
+public class ShortcutPendingTasks {
+ private static final String TAG = "ShortcutPendingTasks";
+
+ private static final boolean DEBUG = false || ShortcutService.DEBUG; // DO NOT SUBMIT WITH TRUE.
+
+ private final Consumer<Runnable> mRunner;
+
+ private final BooleanSupplier mWaitThreadChecker;
+
+ private final Consumer<Throwable> mExceptionHandler;
+
+ /** # of tasks in the queue, including the running one. */
+ private final AtomicInteger mRunningTaskCount = new AtomicInteger();
+
+ /** For dumpsys */
+ private final AtomicLong mLastTaskStartTime = new AtomicLong();
+
+ /**
+ * Constructor. In order to allow injection during unit tests, it doesn't take a
+ * {@link Handler} directly, and instead takes {@code runner} which will post an argument
+ * to a handler.
+ */
+ public ShortcutPendingTasks(Consumer<Runnable> runner, BooleanSupplier waitThreadChecker,
+ Consumer<Throwable> exceptionHandler) {
+ mRunner = runner;
+ mWaitThreadChecker = waitThreadChecker;
+ mExceptionHandler = exceptionHandler;
+ }
+
+ private static void dlog(String message) {
+ if (DEBUG) {
+ Slog.d(TAG, message);
+ }
+ }
+
+ /**
+ * Block until all tasks that are already queued finish. DO NOT call it while holding any lock
+ * or on the handler thread.
+ */
+ public boolean waitOnAllTasks() {
+ dlog("waitOnAllTasks: enter");
+ try {
+ // Make sure it's not holding the lock.
+ if (!mWaitThreadChecker.getAsBoolean()) {
+ return false;
+ }
+
+ // Optimize for the no-task case.
+ if (mRunningTaskCount.get() == 0) {
+ return true;
+ }
+
+ final CountDownLatch latch = new CountDownLatch(1);
+
+ addTask(latch::countDown);
+
+ for (; ; ) {
+ try {
+ if (latch.await(1, TimeUnit.SECONDS)) {
+ return true;
+ }
+ dlog("waitOnAllTasks: Task(s) still running...");
+ } catch (InterruptedException ignore) {
+ }
+ }
+ } finally {
+ dlog("waitOnAllTasks: exit");
+ }
+ }
+
+ /**
+ * Add a new task. This operation is lock-free.
+ */
+ public void addTask(Runnable task) {
+ mRunningTaskCount.incrementAndGet();
+ mLastTaskStartTime.set(System.currentTimeMillis());
+
+ dlog("Task registered");
+
+ mRunner.accept(() -> {
+ try {
+ dlog("Task started");
+
+ task.run();
+ } catch (Throwable th) {
+ mExceptionHandler.accept(th);
+ } finally {
+ dlog("Task finished");
+ mRunningTaskCount.decrementAndGet();
+ }
+ });
+ }
+
+ public void dump(@NonNull PrintWriter pw, @NonNull String prefix) {
+ pw.print(prefix);
+ pw.print("Pending tasks: # running tasks: ");
+ pw.println(mRunningTaskCount.get());
+
+ pw.print(prefix);
+ pw.print(" Last task started time: ");
+ final long lastStarted = mLastTaskStartTime.get();
+ pw.print(" [");
+ pw.print(lastStarted);
+ pw.print("] ");
+ pw.println(ShortcutService.formatTime(lastStarted));
+ }
+}
import android.graphics.Canvas;
import android.graphics.RectF;
import android.graphics.drawable.Icon;
+import android.net.Uri;
import android.os.Binder;
import android.os.Environment;
import android.os.FileUtils;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
/**
* TODO:
- * - Deal with the async nature of PACKAGE_ADD. Basically when a publisher does anything after
- * it's upgraded, the manager should make sure the upgrade process has been executed.
- *
* - getIconMaxWidth()/getIconMaxHeight() should use xdpi and ydpi.
* -> But TypedValue.applyDimension() doesn't differentiate x and y..?
*
private final AtomicBoolean mBootCompleted = new AtomicBoolean();
+ private final ShortcutPendingTasks mPendingTasks;
+
private static final int PACKAGE_MATCH_FLAGS =
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE
@interface ShortcutOperation {
}
+ @GuardedBy("mLock")
+ private int mWtfCount = 0;
+
+ @GuardedBy("mLock")
+ private Exception mLastWtfStacktrace;
+
public ShortcutService(Context context) {
this(context, BackgroundThread.get().getLooper(), /*onyForPackgeManagerApis*/ false);
}
mUsageStatsManagerInternal = Preconditions.checkNotNull(
LocalServices.getService(UsageStatsManagerInternal.class));
+ mPendingTasks = new ShortcutPendingTasks(
+ this::injectPostToHandler,
+ this::injectCheckPendingTaskWaitThread,
+ throwable -> wtf(throwable.getMessage(), throwable));
+
if (onlyForPackageManagerApis) {
return; // Don't do anything further. For unit tests only.
}
- mPackageMonitor.register(context, looper, UserHandle.ALL, /* externalStorage= */ false);
-
injectRegisterUidObserver(mUidObserver, ActivityManager.UID_OBSERVER_PROCSTATE
| ActivityManager.UID_OBSERVER_GONE);
}
+ /**
+ * Check whether {@link ShortcutPendingTasks#waitOnAllTasks()} can be called on the current
+ * thread.
+ *
+ * During unit tests, all tasks are executed synchronously which makes the lock held check would
+ * misfire, so we override this method to always return true.
+ */
+ @VisibleForTesting
+ boolean injectCheckPendingTaskWaitThread() {
+ // We shouldn't wait while holding mLock. We should never do this so wtf().
+ if (Thread.holdsLock(mLock)) {
+ wtf("waitOnAllTasks() called while holding the lock");
+ return false;
+ }
+ // This shouldn't be called on the handler thread either.
+ if (Thread.currentThread() == mHandler.getLooper().getThread()) {
+ wtf("waitOnAllTasks() called on handler thread");
+ return false;
+ }
+ return true;
+ }
+
void logDurationStat(int statId, long start) {
synchronized (mStatLock) {
mCountStats[statId]++;
@UserIdInt int userId) {
verifyCaller(packageName, userId);
+ mPendingTasks.waitOnAllTasks();
+
final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
final int size = newShortcuts.size();
@UserIdInt int userId) {
verifyCaller(packageName, userId);
+ mPendingTasks.waitOnAllTasks();
+
final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
final int size = newShortcuts.size();
@UserIdInt int userId) {
verifyCaller(packageName, userId);
+ mPendingTasks.waitOnAllTasks();
+
final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList();
final int size = newShortcuts.size();
verifyCaller(packageName, userId);
Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
+ mPendingTasks.waitOnAllTasks();
+
synchronized (mLock) {
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
verifyCaller(packageName, userId);
Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
+ mPendingTasks.waitOnAllTasks();
+
synchronized (mLock) {
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
verifyCaller(packageName, userId);
Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided");
+ mPendingTasks.waitOnAllTasks();
+
synchronized (mLock) {
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
public void removeAllDynamicShortcuts(String packageName, @UserIdInt int userId) {
verifyCaller(packageName, userId);
+ mPendingTasks.waitOnAllTasks();
+
synchronized (mLock) {
getPackageShortcutsLocked(packageName, userId).deleteAllDynamicShortcuts();
}
public ParceledListSlice<ShortcutInfo> getDynamicShortcuts(String packageName,
@UserIdInt int userId) {
verifyCaller(packageName, userId);
+
+ mPendingTasks.waitOnAllTasks();
+
synchronized (mLock) {
return getShortcutsWithQueryLocked(
packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
public ParceledListSlice<ShortcutInfo> getManifestShortcuts(String packageName,
@UserIdInt int userId) {
verifyCaller(packageName, userId);
+
+ mPendingTasks.waitOnAllTasks();
+
synchronized (mLock) {
return getShortcutsWithQueryLocked(
packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
public ParceledListSlice<ShortcutInfo> getPinnedShortcuts(String packageName,
@UserIdInt int userId) {
verifyCaller(packageName, userId);
+
+ mPendingTasks.waitOnAllTasks();
+
synchronized (mLock) {
return getShortcutsWithQueryLocked(
packageName, userId, ShortcutInfo.CLONE_REMOVE_FOR_CREATOR,
public int getRemainingCallCount(String packageName, @UserIdInt int userId) {
verifyCaller(packageName, userId);
+ mPendingTasks.waitOnAllTasks();
+
synchronized (mLock) {
return mMaxUpdatesPerInterval
- getPackageShortcutsLocked(packageName, userId).getApiCallCount();
public long getRateLimitResetTime(String packageName, @UserIdInt int userId) {
verifyCaller(packageName, userId);
+ mPendingTasks.waitOnAllTasks();
+
synchronized (mLock) {
return getNextResetTimeLocked();
}
public void reportShortcutUsed(String packageName, String shortcutId, int userId) {
verifyCaller(packageName, userId);
+ mPendingTasks.waitOnAllTasks();
+
Preconditions.checkNotNull(shortcutId);
if (DEBUG) {
public void resetThrottling() {
enforceSystemOrShell();
+ mPendingTasks.waitOnAllTasks();
+
resetThrottlingInner(getCallingUserId());
}
if (DEBUG) {
Slog.d(TAG, "onApplicationActive: package=" + packageName + " userid=" + userId);
}
+
+ mPendingTasks.waitOnAllTasks();
+
enforceResetThrottlingPermission();
resetPackageThrottling(packageName, userId);
}
@Nullable String packageName, @Nullable List<String> shortcutIds,
@Nullable ComponentName componentName,
int queryFlags, int userId) {
+
+ // When this method is called from onShortcutChangedInner() in LauncherApps,
+ // we're on the handler thread. Do not try to wait on tasks. Not waiting for pending
+ // tasks on this specific case should be fine.
+ if (Thread.currentThread() != mHandler.getLooper().getThread()) {
+ mPendingTasks.waitOnAllTasks();
+ }
+
final ArrayList<ShortcutInfo> ret = new ArrayList<>();
final boolean cloneKeyFieldOnly =
((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) != 0);
Preconditions.checkStringNotEmpty(packageName, "packageName");
Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
+ mPendingTasks.waitOnAllTasks();
+
synchronized (mLock) {
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
.attemptToRestoreIfNeededAndSave();
Preconditions.checkStringNotEmpty(packageName, "packageName");
Preconditions.checkNotNull(shortcutIds, "shortcutIds");
+ mPendingTasks.waitOnAllTasks();
+
synchronized (mLock) {
final ShortcutLauncher launcher =
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId);
Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty");
Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty");
+ mPendingTasks.waitOnAllTasks();
+
synchronized (mLock) {
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
.attemptToRestoreIfNeededAndSave();
Preconditions.checkNotNull(packageName, "packageName");
Preconditions.checkNotNull(shortcutId, "shortcutId");
+ mPendingTasks.waitOnAllTasks();
+
synchronized (mLock) {
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
.attemptToRestoreIfNeededAndSave();
Preconditions.checkNotNull(packageName, "packageName");
Preconditions.checkNotNull(shortcutId, "shortcutId");
+ mPendingTasks.waitOnAllTasks();
+
synchronized (mLock) {
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
.attemptToRestoreIfNeededAndSave();
if (DEBUG) {
Slog.d(TAG, "onSystemLocaleChangedNoLock: " + mLocaleChangeSequenceNumber.get());
}
- injectPostToHandler(() -> handleLocaleChanged());
+ mPendingTasks.addTask(() -> handleLocaleChanged());
}
}
+
+ @Override
+ public void onPackageBroadcast(Intent intent) {
+ if (DEBUG) {
+ Slog.d(TAG, "onPackageBroadcast");
+ }
+ mPendingTasks.addTask(() -> ShortcutService.this.onPackageBroadcast(
+ new Intent(intent)));
+ }
}
void handleLocaleChanged() {
}
}
- /**
- * Package event callbacks.
- */
- @VisibleForTesting
- final PackageMonitor mPackageMonitor = new PackageMonitor() {
- @Override
- public void onPackageAdded(String packageName, int uid) {
- handlePackageAdded(packageName, getChangingUserId());
+ private void onPackageBroadcast(Intent intent) {
+ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+ if (userId == UserHandle.USER_NULL) {
+ Slog.w(TAG, "Intent broadcast does not contain user handle: " + intent);
+ return;
}
- @Override
- public void onPackageUpdateFinished(String packageName, int uid) {
- handlePackageUpdateFinished(packageName, getChangingUserId());
- }
+ final String action = intent.getAction();
- @Override
- public void onPackageRemoved(String packageName, int uid) {
- handlePackageRemoved(packageName, getChangingUserId());
+ if (!mUserManager.isUserUnlocked(userId)) {
+ if (DEBUG) {
+ Slog.d(TAG, "Ignoring package broadcast " + action + " for locked/stopped user "
+ + userId);
+ }
+ return;
}
- @Override
- public void onPackageDataCleared(String packageName, int uid) {
- handlePackageDataCleared(packageName, getChangingUserId());
+ final Uri intentUri = intent.getData();
+ final String packageName = (intentUri != null) ? intentUri.getSchemeSpecificPart() : null;
+ if (packageName == null) {
+ Slog.w(TAG, "Intent broadcast does not contain package name: " + intent);
+ return;
}
- @Override
- public boolean onPackageChanged(String packageName, int uid, String[] components) {
- handlePackageChanged(packageName, getChangingUserId());
- return false; // We don't need to receive onSomePackagesChanged(), so just false.
+ final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
+
+ if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+ if (replacing) {
+ handlePackageUpdateFinished(packageName, userId);
+ } else {
+ handlePackageAdded(packageName, userId);
+ }
+ } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
+ if (!replacing) {
+ handlePackageRemoved(packageName, userId);
+ }
+ } else if (Intent.ACTION_PACKAGE_CHANGED.equals(action)) {
+ handlePackageChanged(packageName, userId);
+
+ } else if (Intent.ACTION_PACKAGE_DATA_CLEARED.equals(action)) {
+ handlePackageDataCleared(packageName, userId);
}
- };
+ }
/**
* Called when a user is unlocked.
dumpStatLS(pw, p, Stats.IS_ACTIVITY_ENABLED, "isActivityEnabled");
}
+ pw.println();
+ pw.print(" #Failures: ");
+ pw.println(mWtfCount);
+
+ if (mLastWtfStacktrace != null) {
+ pw.print(" Last failure stack trace: ");
+ pw.println(Log.getStackTraceString(mLastWtfStacktrace));
+ }
+
+ pw.println();
+ mPendingTasks.dump(pw, " ");
+
for (int i = 0; i < mUsers.size(); i++) {
pw.println();
mUsers.valueAt(i).dump(pw, " ");
enforceShell();
+ mPendingTasks.waitOnAllTasks();
+
final int status = (new MyShellCommand()).exec(this, in, out, err, args, resultReceiver);
resultReceiver.send(status, null);
}
// Injection point.
- void wtf(String message, Exception e) {
+ void wtf(String message, Throwable e) {
+ if (e == null) {
+ e = new RuntimeException("Stacktrace");
+ }
+ synchronized (mLock) {
+ mWtfCount++;
+ mLastWtfStacktrace = new Exception("Last failure was logged here:");
+ }
Slog.wtf(TAG, message, e);
}
@VisibleForTesting
ShortcutPackage getPackageShortcutForTest(String packageName, int userId) {
+ mPendingTasks.waitOnAllTasks();
synchronized (mLock) {
final ShortcutUser user = mUsers.get(userId);
if (user == null) return null;
@VisibleForTesting
ShortcutInfo getPackageShortcutForTest(String packageName, String shortcutId, int userId) {
+ mPendingTasks.waitOnAllTasks();
synchronized (mLock) {
- final ShortcutPackage pkg = getPackageShortcutForTest(packageName, userId);
+ final ShortcutUser user = mUsers.get(userId);
+ if (user == null) return null;
+
+ final ShortcutPackage pkg = user.getAllPackagesForTest().get(packageName);
if (pkg == null) return null;
return pkg.findShortcutById(shortcutId);
forEachLoadedUserLocked(u -> u.forAllPackageItems(ShortcutPackageItem::verifyStates));
}
}
+
+ ShortcutPendingTasks getPendingTasksForTest() {
+ return mPendingTasks;
+ }
+
+ Object getLockForTest() {
+ return mLock;
+ }
}
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
+import java.util.function.Function;
public abstract class BaseShortcutManagerTest extends InstrumentationTestCase {
protected static final String TAG = "ShortcutManagerTest";
@Override
void injectPostToHandler(Runnable r) {
- final long token = mContext.injectClearCallingIdentity();
- r.run();
- mContext.injectRestoreCallingIdentity(token);
+ runOnHandler(r);
}
@Override
}
@Override
- void wtf(String message, Exception e) {
+ void wtf(String message, Throwable th) {
// During tests, WTF is fatal.
- fail(message + " exception: " + e);
+ fail(message + " exception: " + th + "\n" + Log.getStackTraceString(th));
+ }
+
+ @Override
+ boolean injectCheckPendingTaskWaitThread() {
+ return true;
}
}
@Override
void postToPackageMonitorHandler(Runnable r) {
- final long token = mContext.injectClearCallingIdentity();
- r.run();
- mContext.injectRestoreCallingIdentity(token);
+ runOnHandler(r);
}
@Override
public static class ShortcutActivity3 extends Activity {
}
+ protected Looper mLooper;
+ protected Handler mHandler;
+
protected ServiceContext mServiceContext;
protected ClientContext mClientContext;
protected final HashMap<String, LinkedHashMap<ComponentName, Integer>> mActivityMetadataResId
= new HashMap<>();
+ protected final Map<Integer, UserInfo> mUserInfos = new HashMap<>();
+ protected final Map<Integer, Boolean> mRunningUsers = new HashMap<>();
+ protected final Map<Integer, Boolean> mUnlockedUsers = new HashMap<>();
+
static {
QUERY_ALL.setQueryFlags(
ShortcutQuery.FLAG_GET_ALL_KINDS);
protected void setUp() throws Exception {
super.setUp();
+ mLooper = Looper.getMainLooper();
+ mHandler = new Handler(mLooper);
+
mServiceContext = spy(new ServiceContext());
mClientContext = new ClientContext();
deleteAllSavedFiles();
// Set up users.
- doAnswer(inv -> {
- assertSystem();
- return USER_INFO_0;
- }).when(mMockUserManager).getUserInfo(eq(USER_0));
-
- doAnswer(inv -> {
- assertSystem();
- return USER_INFO_10;
- }).when(mMockUserManager).getUserInfo(eq(USER_10));
-
- doAnswer(inv -> {
- assertSystem();
- return USER_INFO_11;
- }).when(mMockUserManager).getUserInfo(eq(USER_11));
-
- doAnswer(inv -> {
- assertSystem();
- return USER_INFO_P0;
- }).when(mMockUserManager).getUserInfo(eq(USER_P0));
-
- // User 0 is always running.
- when(mMockUserManager.isUserRunning(eq(USER_0))).thenAnswer(new AnswerIsUserRunning(true));
-
+ when(mMockUserManager.getUserInfo(anyInt())).thenAnswer(new AnswerWithSystemCheck<>(
+ inv -> mUserInfos.get((Integer) inv.getArguments()[0])));
+
+ mUserInfos.put(USER_0, USER_INFO_0);
+ mUserInfos.put(USER_10, USER_INFO_10);
+ mUserInfos.put(USER_11, USER_INFO_11);
+ mUserInfos.put(USER_P0, USER_INFO_P0);
+
+ // Set up isUserRunning and isUserUnlocked.
+ when(mMockUserManager.isUserRunning(anyInt())).thenAnswer(new AnswerWithSystemCheck<>(
+ inv -> mRunningUsers.get((Integer) inv.getArguments()[0])));
+
+ when(mMockUserManager.isUserUnlocked(anyInt())).thenAnswer(new AnswerWithSystemCheck<>(
+ inv -> {
+ final int userId = (Integer) inv.getArguments()[0];
+ return mRunningUsers.get(userId) && mUnlockedUsers.get(userId);
+ }));
+
+ // User 0 is always running
+ mRunningUsers.put(USER_0, true);
+ mRunningUsers.put(USER_10, false);
+ mRunningUsers.put(USER_11, false);
+ mRunningUsers.put(USER_P0, false);
+
+ // Unlock all users by default.
+ mUnlockedUsers.put(USER_0, true);
+ mUnlockedUsers.put(USER_10, true);
+ mUnlockedUsers.put(USER_11, true);
+ mUnlockedUsers.put(USER_P0, true);
+
+ // Set up resources
setUpAppResources();
// Start the service.
/**
* Returns a boolean but also checks if the current UID is SYSTEM_UID.
*/
- protected class AnswerIsUserRunning implements Answer<Boolean> {
- protected final boolean mAnswer;
+ protected class AnswerWithSystemCheck<T> implements Answer<T> {
+ private final Function<InvocationOnMock, T> mChecker;
- protected AnswerIsUserRunning(boolean answer) {
- mAnswer = answer;
+ public AnswerWithSystemCheck(Function<InvocationOnMock, T> checker) {
+ mChecker = checker;
}
@Override
- public Boolean answer(InvocationOnMock invocation) throws Throwable {
- assertEquals("isUserRunning() must be called on SYSTEM UID.",
+ public T answer(InvocationOnMock invocation) throws Throwable {
+ assertEquals("Must be called on SYSTEM UID.",
Process.SYSTEM_UID, mInjectedCallingUid);
- return mAnswer;
+ return mChecker.apply(invocation);
}
}
LocalServices.removeServiceForTest(ShortcutServiceInternal.class);
// Instantiate targets.
- mService = new ShortcutServiceTestable(mServiceContext, Looper.getMainLooper());
+ mService = new ShortcutServiceTestable(mServiceContext, mLooper);
mManager = new ShortcutManagerTestable(mClientContext, mService);
mInternal = LocalServices.getService(ShortcutServiceInternal.class);
protected void shutdownServices() {
if (mService != null) {
+ mService.getPendingTasksForTest().waitOnAllTasks();
+
// Flush all the unsaved data from the previous instance.
mService.saveDirtyInfo();
mLauncherAppsMap.clear();
}
+ protected void runOnHandler(Runnable r) {
+ final long token = mServiceContext.injectClearCallingIdentity();
+ try {
+ r.run();
+ } finally {
+ mServiceContext.injectRestoreCallingIdentity(token);
+ }
+ }
+
protected void addPackage(String packageName, int uid, int version) {
addPackage(packageName, uid, version, packageName);
}
import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils.waitOnMainThread;
import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_3);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
- genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+ mInternal.onPackageBroadcast(genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_2);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
- genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
+ mInternal.onPackageBroadcast(genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_2);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
}).assertCallbackCalledForPackageAndUser(CALLING_PACKAGE_1, HANDLE_USER_0)
.areAllManifest()
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_0);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
assertForLauncherCallback(mLauncherApps, () -> {
assertCallbackNotReceived(c11_1);
// Work profile, now running.
- doAnswer(new AnswerIsUserRunning(false)).when(mMockUserManager).isUserRunning(anyInt());
- doAnswer(new AnswerIsUserRunning(true)).when(mMockUserManager).isUserRunning(eq(USER_P0));
+ mRunningUsers.clear();
+ mRunningUsers.put(USER_P0, true);
resetAll(all);
runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
assertCallbackReceived(cP0_1, HANDLE_USER_P0, CALLING_PACKAGE_1, "s1", "s2", "s3", "s4");
// Normal secondary user.
-
- doAnswer(new AnswerIsUserRunning(false)).when(mMockUserManager).isUserRunning(anyInt());
- doAnswer(new AnswerIsUserRunning(true)).when(mMockUserManager).isUserRunning(eq(USER_10));
+ mRunningUsers.clear();
+ mRunningUsers.put(USER_10, true);
resetAll(all);
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_2);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_1);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_1);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertWith(getCallerShortcuts())
assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
uninstallPackage(USER_0, CALLING_PACKAGE_1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageDeleteIntent(CALLING_PACKAGE_1, USER_0));
assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_10));
assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
+ mRunningUsers.put(USER_10, true);
+
uninstallPackage(USER_10, CALLING_PACKAGE_2);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageDeleteIntent(CALLING_PACKAGE_2, USER_10));
assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_10));
assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageDataClear(CALLING_PACKAGE_1, USER_0));
assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_10));
assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mRunningUsers.put(USER_10, true);
+
+ mInternal.onPackageBroadcast(
genPackageDataClear(CALLING_PACKAGE_2, USER_10));
assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
public void testHandlePackageClearData_manifestRepublished() {
+ mRunningUsers.put(USER_10, true);
+
// Add two manifests and two dynamics.
addManifestShortcutResource(
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_2);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_10));
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
});
// Clear data
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageDataClear(CALLING_PACKAGE_1, USER_10));
// Only manifest shortcuts will remain, and are no longer pinned.
reset(c0);
reset(c10);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageUpdateIntent(CALLING_PACKAGE_1, USER_0));
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageUpdateIntent(CALLING_PACKAGE_1, USER_10));
waitOnMainThread();
updatePackageVersion(CALLING_PACKAGE_1, 1);
// Then send the broadcast, to only user-0.
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageUpdateIntent(CALLING_PACKAGE_1, USER_0));
waitOnMainThread();
// notification to the launcher.
mInjectedCurrentTimeMillis = START_TIME + 200;
- doAnswer(new AnswerIsUserRunning(true)).when(mMockUserManager).isUserRunning(eq(USER_10));
+ mRunningUsers.put(USER_10, true);
reset(c0);
reset(c10);
mService.handleUnlockUser(USER_10);
+ waitOnMainThread();
+
shortcuts = ArgumentCaptor.forClass(List.class);
verify(c0, times(0)).onShortcutsChanged(
eq(CALLING_PACKAGE_1),
updatePackageVersion(CALLING_PACKAGE_2, 10);
// Then send the broadcast, to only user-0.
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageUpdateIntent(CALLING_PACKAGE_2, USER_0));
mService.handleUnlockUser(USER_10);
updatePackageVersion(CALLING_PACKAGE_3, 100);
// Then send the broadcast, to only user-0.
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageUpdateIntent(CALLING_PACKAGE_3, USER_0));
mService.handleUnlockUser(USER_10);
// Update the package.
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageUpdateIntent(CALLING_PACKAGE_1, USER_0));
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
addManifestShortcutResource(ACTIVITY1, R.xml.shortcut_1);
addManifestShortcutResource(ACTIVITY2, R.xml.shortcut_1_alt);
+ mRunningUsers.put(USER_10, true);
+
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_10));
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
});
// First, no changes.
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageChangedIntent(CALLING_PACKAGE_1, USER_10));
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
// Disable activity 1
mEnabledActivityChecker = (activity, userId) -> !ACTIVITY1.equals(activity);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageChangedIntent(CALLING_PACKAGE_1, USER_10));
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
// Re-enable activity 1.
// Manifest shortcuts will be re-published, but dynamic ones are not.
mEnabledActivityChecker = (activity, userId) -> true;
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageChangedIntent(CALLING_PACKAGE_1, USER_10));
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
// Disable activity 2
// Because "ms1-alt" and "s2" are both pinned, they will remain, but disabled.
mEnabledActivityChecker = (activity, userId) -> !ACTIVITY2.equals(activity);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageChangedIntent(CALLING_PACKAGE_1, USER_10));
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
setCaller(LAUNCHER_1, USER_0);
assertForLauncherCallback(mLauncherApps, () -> {
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageUpdateIntent(CALLING_PACKAGE_1, USER_0));
}).assertCallbackCalledForPackageAndUser(CALLING_PACKAGE_1, HANDLE_USER_0)
// Make sure the launcher gets callbacks.
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_1);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
new ComponentName(CALLING_PACKAGE_2, ShortcutActivity.class.getName()),
R.xml.shortcut_5);
updatePackageVersion(CALLING_PACKAGE_2, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_2, USER_0));
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
new ComponentName(CALLING_PACKAGE_2, ShortcutActivity.class.getName()),
R.xml.shortcut_2);
updatePackageLastUpdateTime(CALLING_PACKAGE_2, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_2, USER_0));
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertEmpty(mManager.getManifestShortcuts());
assertEmpty(mManager.getPinnedShortcuts());
});
+ // Send add broadcast, but the user is not running, so should be ignored.
+ mRunningUsers.put(USER_10, false);
+ mUnlockedUsers.put(USER_10, false);
+
+ mInternal.onPackageBroadcast(
+ genPackageAddIntent(CALLING_PACKAGE_2, USER_10));
+ runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
+ assertEmpty(mManager.getManifestShortcuts());
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ // Try again, but the user is locked, so still ignored.
+ mRunningUsers.put(USER_10, true);
+
+ mInternal.onPackageBroadcast(
+ genPackageAddIntent(CALLING_PACKAGE_2, USER_10));
+ runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
+ assertEmpty(mManager.getManifestShortcuts());
+ assertEmpty(mManager.getPinnedShortcuts());
+ });
+
+ // Unlock the user, now it should work.
+ mUnlockedUsers.put(USER_10, true);
+
// Send PACKAGE_ADD broadcast to have Package 2 on user-10 publish manifest shortcuts.
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_2, USER_10));
runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
R.xml.shortcut_5_reverse);
updatePackageLastUpdateTime(CALLING_PACKAGE_2, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_2, USER_0));
runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
new ComponentName(CALLING_PACKAGE_2, ShortcutActivity2.class.getName()),
R.xml.shortcut_0);
updatePackageLastUpdateTime(CALLING_PACKAGE_2, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_2, USER_0));
// No manifest shortcuts, and pinned ones are disabled.
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_error_1);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
// Only the valid one is published.
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_error_2);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
// Only the valid one is published.
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_error_3);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
// Only the valid one is published.
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_error_4);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_5);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_error_4);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
// Make sure 3, 4 and 5 still exist but disabled.
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_5);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
// Only the valid one is published.
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_2);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_1);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
// Only the valid one is published.
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_1_disable);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
// Because shortcut 1 wasn't pinned, it'll just go away.
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_1);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
// Only the valid one is published.
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_1_disable);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
// Because shortcut 1 was pinned, it'll still exist as pinned, but disabled.
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_2_duplicate);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity2.class.getName()),
R.xml.shortcut_5);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_5);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_2);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
runWithCaller(LAUNCHER_1, USER_0, () -> {
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_1);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_5);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_2);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
assertEquals(2, mManager.getManifestShortcuts().size());
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_2);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
assertEquals(2, mManager.getManifestShortcuts().size());
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity.class.getName()),
R.xml.shortcut_1);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
assertEquals(1, mManager.getManifestShortcuts().size());
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity2.class.getName()),
R.xml.shortcut_1_alt);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
assertEquals(3, mManager.getManifestShortcuts().size());
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity2.class.getName()),
R.xml.shortcut_5_alt); // manifest has 5, but max is 3, so a2 will have 3.
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
assertEquals(5, mManager.getManifestShortcuts().size());
new ComponentName(CALLING_PACKAGE_1, ShortcutActivity2.class.getName()),
R.xml.shortcut_0);
updatePackageVersion(CALLING_PACKAGE_1, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE_1, USER_0));
assertEquals(0, mManager.getManifestShortcuts().size());
private void publishManifestShortcuts(ComponentName activity, int resId) {
addManifestShortcutResource(activity, resId);
updatePackageVersion(CALLING_PACKAGE, 1);
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mInternal.onPackageBroadcast(
genPackageAddIntent(CALLING_PACKAGE, USER_0));
}
--- /dev/null
+/*
+ * Copyright (C) 2016 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.pm;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.test.MoreAsserts;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Run with:
+ adb install \
+ -r -g ${ANDROID_PRODUCT_OUT}/data/app/FrameworksServicesTests/FrameworksServicesTests.apk &&
+ adb shell am instrument -e class com.android.server.pm.ShortcutPendingTasksTest \
+ -w com.android.frameworks.servicestests
+ */
+@LargeTest
+public class ShortcutPendingTasksTest extends BaseShortcutManagerTest {
+ public void testAll() {
+ final AtomicReference<Throwable> thrown = new AtomicReference<>();
+
+ final AtomicBoolean threadCheckerResult = new AtomicBoolean(true);
+
+ final Handler handler = new Handler(Looper.getMainLooper());
+
+ final ShortcutPendingTasks tasks = new ShortcutPendingTasks(
+ handler::post,
+ threadCheckerResult::get,
+ thrown::set);
+
+ // No pending tasks, shouldn't block.
+ assertTrue(tasks.waitOnAllTasks());
+
+ final AtomicInteger counter = new AtomicInteger();
+
+ // Run one task.
+ tasks.addTask(() -> {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException ignore) {
+ }
+ counter.incrementAndGet();
+ });
+
+ assertTrue(tasks.waitOnAllTasks());
+ assertNull(thrown.get());
+
+ assertEquals(1, counter.get());
+
+ // Run 3 tasks.
+
+ // We use this ID to make sure only one task can run at the same time.
+ final AtomicInteger currentTaskId = new AtomicInteger();
+
+ tasks.addTask(() -> {
+ currentTaskId.set(1);
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException ignore) {
+ }
+ counter.incrementAndGet();
+ assertEquals(1, currentTaskId.get());
+ });
+ tasks.addTask(() -> {
+ currentTaskId.set(2);
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException ignore) {
+ }
+ counter.incrementAndGet();
+ assertEquals(2, currentTaskId.get());
+ });
+ tasks.addTask(() -> {
+ currentTaskId.set(3);
+ try {
+ Thread.sleep(500);
+ } catch (InterruptedException ignore) {
+ }
+ counter.incrementAndGet();
+ assertEquals(3, currentTaskId.get());
+ });
+
+ assertTrue(tasks.waitOnAllTasks());
+ assertNull(thrown.get());
+ assertEquals(4, counter.get());
+
+ // No tasks running, shouldn't block.
+ assertTrue(tasks.waitOnAllTasks());
+ assertNull(thrown.get());
+ assertEquals(4, counter.get());
+
+ // Now the thread checker returns false, so waitOnAllTasks() returns false.
+ threadCheckerResult.set(false);
+ assertFalse(tasks.waitOnAllTasks());
+
+ threadCheckerResult.set(true);
+
+ // Make sure the exception handler is called.
+ tasks.addTask(() -> {
+ throw new RuntimeException("XXX");
+ });
+ assertTrue(tasks.waitOnAllTasks());
+ assertNotNull(thrown.get());
+ MoreAsserts.assertContainsRegex("XXX", thrown.get().getMessage());
+ }
+}