diff options
8 files changed, 631 insertions, 137 deletions
diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java index 3f8bad15035b..de52f73fc213 100644 --- a/core/java/android/content/pm/ShortcutServiceInternal.java +++ b/core/java/android/content/pm/ShortcutServiceInternal.java @@ -73,4 +73,11 @@ public abstract class ShortcutServiceInternal { * 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); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 84ebdd15947d..0ce6ed0c8ae3 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -159,6 +159,7 @@ import android.content.pm.PermissionInfo; 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; @@ -11443,6 +11444,9 @@ public class PackageManagerService extends IPackageManager.Stub { } 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); @@ -11467,6 +11471,10 @@ public class PackageManagerService extends IPackageManager.Stub { + 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); diff --git a/services/core/java/com/android/server/pm/ShortcutPendingTasks.java b/services/core/java/com/android/server/pm/ShortcutPendingTasks.java new file mode 100644 index 000000000000..a5ace56fee1f --- /dev/null +++ b/services/core/java/com/android/server/pm/ShortcutPendingTasks.java @@ -0,0 +1,159 @@ +/* + * 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)); + } +} diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index c6949e499021..dda9a5d74b3c 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -49,6 +49,7 @@ import android.graphics.Bitmap.CompressFormat; 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; @@ -81,7 +82,6 @@ import android.view.IWindowManager; 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; @@ -122,9 +122,6 @@ import java.util.function.Predicate; /** * 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..? * @@ -304,6 +301,8 @@ public class ShortcutService extends IShortcutService.Stub { 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 @@ -355,6 +354,12 @@ public class ShortcutService extends IShortcutService.Stub { @interface ShortcutOperation { } + @GuardedBy("mLock") + private int mWtfCount = 0; + + @GuardedBy("mLock") + private Exception mLastWtfStacktrace; + public ShortcutService(Context context) { this(context, BackgroundThread.get().getLooper(), /*onyForPackgeManagerApis*/ false); } @@ -371,16 +376,41 @@ public class ShortcutService extends IShortcutService.Stub { 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]++; @@ -1486,6 +1516,8 @@ public class ShortcutService extends IShortcutService.Stub { @UserIdInt int userId) { verifyCaller(packageName, userId); + mPendingTasks.waitOnAllTasks(); + final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList(); final int size = newShortcuts.size(); @@ -1535,6 +1567,8 @@ public class ShortcutService extends IShortcutService.Stub { @UserIdInt int userId) { verifyCaller(packageName, userId); + mPendingTasks.waitOnAllTasks(); + final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList(); final int size = newShortcuts.size(); @@ -1613,6 +1647,8 @@ public class ShortcutService extends IShortcutService.Stub { @UserIdInt int userId) { verifyCaller(packageName, userId); + mPendingTasks.waitOnAllTasks(); + final List<ShortcutInfo> newShortcuts = (List<ShortcutInfo>) shortcutInfoList.getList(); final int size = newShortcuts.size(); @@ -1663,6 +1699,8 @@ public class ShortcutService extends IShortcutService.Stub { verifyCaller(packageName, userId); Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided"); + mPendingTasks.waitOnAllTasks(); + synchronized (mLock) { final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); @@ -1690,6 +1728,8 @@ public class ShortcutService extends IShortcutService.Stub { verifyCaller(packageName, userId); Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided"); + mPendingTasks.waitOnAllTasks(); + synchronized (mLock) { final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); @@ -1710,6 +1750,8 @@ public class ShortcutService extends IShortcutService.Stub { verifyCaller(packageName, userId); Preconditions.checkNotNull(shortcutIds, "shortcutIds must be provided"); + mPendingTasks.waitOnAllTasks(); + synchronized (mLock) { final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); @@ -1732,6 +1774,8 @@ public class ShortcutService extends IShortcutService.Stub { public void removeAllDynamicShortcuts(String packageName, @UserIdInt int userId) { verifyCaller(packageName, userId); + mPendingTasks.waitOnAllTasks(); + synchronized (mLock) { getPackageShortcutsLocked(packageName, userId).deleteAllDynamicShortcuts(); } @@ -1744,6 +1788,9 @@ public class ShortcutService extends IShortcutService.Stub { 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, @@ -1755,6 +1802,9 @@ public class ShortcutService extends IShortcutService.Stub { 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, @@ -1766,6 +1816,9 @@ public class ShortcutService extends IShortcutService.Stub { 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, @@ -1795,6 +1848,8 @@ public class ShortcutService extends IShortcutService.Stub { public int getRemainingCallCount(String packageName, @UserIdInt int userId) { verifyCaller(packageName, userId); + mPendingTasks.waitOnAllTasks(); + synchronized (mLock) { return mMaxUpdatesPerInterval - getPackageShortcutsLocked(packageName, userId).getApiCallCount(); @@ -1805,6 +1860,8 @@ public class ShortcutService extends IShortcutService.Stub { public long getRateLimitResetTime(String packageName, @UserIdInt int userId) { verifyCaller(packageName, userId); + mPendingTasks.waitOnAllTasks(); + synchronized (mLock) { return getNextResetTimeLocked(); } @@ -1823,6 +1880,8 @@ public class ShortcutService extends IShortcutService.Stub { public void reportShortcutUsed(String packageName, String shortcutId, int userId) { verifyCaller(packageName, userId); + mPendingTasks.waitOnAllTasks(); + Preconditions.checkNotNull(shortcutId); if (DEBUG) { @@ -1855,6 +1914,8 @@ public class ShortcutService extends IShortcutService.Stub { public void resetThrottling() { enforceSystemOrShell(); + mPendingTasks.waitOnAllTasks(); + resetThrottlingInner(getCallingUserId()); } @@ -1887,6 +1948,9 @@ public class ShortcutService extends IShortcutService.Stub { if (DEBUG) { Slog.d(TAG, "onApplicationActive: package=" + packageName + " userid=" + userId); } + + mPendingTasks.waitOnAllTasks(); + enforceResetThrottlingPermission(); resetPackageThrottling(packageName, userId); } @@ -2049,6 +2113,14 @@ public class ShortcutService extends IShortcutService.Stub { @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); @@ -2127,6 +2199,8 @@ public class ShortcutService extends IShortcutService.Stub { Preconditions.checkStringNotEmpty(packageName, "packageName"); Preconditions.checkStringNotEmpty(shortcutId, "shortcutId"); + mPendingTasks.waitOnAllTasks(); + synchronized (mLock) { getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) .attemptToRestoreIfNeededAndSave(); @@ -2164,6 +2238,8 @@ public class ShortcutService extends IShortcutService.Stub { Preconditions.checkStringNotEmpty(packageName, "packageName"); Preconditions.checkNotNull(shortcutIds, "shortcutIds"); + mPendingTasks.waitOnAllTasks(); + synchronized (mLock) { final ShortcutLauncher launcher = getLauncherShortcutsLocked(callingPackage, userId, launcherUserId); @@ -2184,6 +2260,8 @@ public class ShortcutService extends IShortcutService.Stub { 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(); @@ -2213,6 +2291,8 @@ public class ShortcutService extends IShortcutService.Stub { Preconditions.checkNotNull(packageName, "packageName"); Preconditions.checkNotNull(shortcutId, "shortcutId"); + mPendingTasks.waitOnAllTasks(); + synchronized (mLock) { getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) .attemptToRestoreIfNeededAndSave(); @@ -2237,6 +2317,8 @@ public class ShortcutService extends IShortcutService.Stub { Preconditions.checkNotNull(packageName, "packageName"); Preconditions.checkNotNull(shortcutId, "shortcutId"); + mPendingTasks.waitOnAllTasks(); + synchronized (mLock) { getLauncherShortcutsLocked(callingPackage, userId, launcherUserId) .attemptToRestoreIfNeededAndSave(); @@ -2297,9 +2379,18 @@ public class ShortcutService extends IShortcutService.Stub { 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() { @@ -2316,37 +2407,49 @@ public class ShortcutService extends IShortcutService.Stub { } } - /** - * 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. @@ -2984,6 +3087,18 @@ public class ShortcutService extends IShortcutService.Stub { 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, " "); @@ -3032,6 +3147,8 @@ public class ShortcutService extends IShortcutService.Stub { enforceShell(); + mPendingTasks.waitOnAllTasks(); + final int status = (new MyShellCommand()).exec(this, in, out, err, args, resultReceiver); resultReceiver.send(status, null); @@ -3303,7 +3420,14 @@ public class ShortcutService extends IShortcutService.Stub { } // 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); } @@ -3376,6 +3500,7 @@ public class ShortcutService extends IShortcutService.Stub { @VisibleForTesting ShortcutPackage getPackageShortcutForTest(String packageName, int userId) { + mPendingTasks.waitOnAllTasks(); synchronized (mLock) { final ShortcutUser user = mUsers.get(userId); if (user == null) return null; @@ -3386,8 +3511,12 @@ public class ShortcutService extends IShortcutService.Stub { @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); @@ -3422,4 +3551,12 @@ public class ShortcutService extends IShortcutService.Stub { forEachLoadedUserLocked(u -> u.forAllPackageItems(ShortcutPackageItem::verifyStates)); } } + + ShortcutPendingTasks getPendingTasksForTest() { + return mPendingTasks; + } + + Object getLockForTest() { + return mLock; + } } diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java index 3f6ab369f6bb..d853f915fc49 100644 --- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java @@ -104,6 +104,7 @@ import java.util.Set; 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"; @@ -382,9 +383,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { @Override void injectPostToHandler(Runnable r) { - final long token = mContext.injectClearCallingIdentity(); - r.run(); - mContext.injectRestoreCallingIdentity(token); + runOnHandler(r); } @Override @@ -400,9 +399,14 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { } @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; } } @@ -452,9 +456,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { @Override void postToPackageMonitorHandler(Runnable r) { - final long token = mContext.injectClearCallingIdentity(); - r.run(); - mContext.injectRestoreCallingIdentity(token); + runOnHandler(r); } @Override @@ -497,6 +499,9 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { public static class ShortcutActivity3 extends Activity { } + protected Looper mLooper; + protected Handler mHandler; + protected ServiceContext mServiceContext; protected ClientContext mClientContext; @@ -615,6 +620,10 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { 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); @@ -624,6 +633,9 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { protected void setUp() throws Exception { super.setUp(); + mLooper = Looper.getMainLooper(); + mHandler = new Handler(mLooper); + mServiceContext = spy(new ServiceContext()); mClientContext = new ClientContext(); @@ -667,29 +679,37 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { 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. @@ -705,18 +725,18 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { /** * 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); } } @@ -805,7 +825,7 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { 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); @@ -828,6 +848,8 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { protected void shutdownServices() { if (mService != null) { + mService.getPendingTasksForTest().waitOnAllTasks(); + // Flush all the unsaved data from the previous instance. mService.saveDirtyInfo(); @@ -844,6 +866,15 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { 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); } diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java index c7be3d93942e..f5ae706b9b02 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest1.java @@ -54,9 +54,7 @@ import static com.android.server.pm.shortcutmanagertest.ShortcutManagerTestUtils 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; @@ -1293,8 +1291,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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( @@ -1312,8 +1309,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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( @@ -2669,7 +2665,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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() @@ -2706,7 +2702,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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, () -> { @@ -2843,8 +2839,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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, () -> { @@ -2862,9 +2858,8 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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, () -> { @@ -3327,7 +3322,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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, () -> { @@ -3344,7 +3339,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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, () -> { @@ -3706,7 +3701,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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()) @@ -3746,7 +3741,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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)); @@ -3763,8 +3758,10 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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)); @@ -3855,7 +3852,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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)); @@ -3872,7 +3869,9 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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)); @@ -3892,12 +3891,14 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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, () -> { @@ -3918,7 +3919,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); // Clear data - mService.mPackageMonitor.onReceive(getTestContext(), + mInternal.onPackageBroadcast( genPackageDataClear(CALLING_PACKAGE_1, USER_10)); // Only manifest shortcuts will remain, and are no longer pinned. @@ -3983,9 +3984,9 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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(); @@ -4006,7 +4007,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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(); @@ -4037,12 +4038,14 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { // 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), @@ -4069,7 +4072,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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); @@ -4093,7 +4096,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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); @@ -4175,7 +4178,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { // 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, () -> { @@ -4201,8 +4204,10 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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, () -> { @@ -4234,7 +4239,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { }); // First, no changes. - mService.mPackageMonitor.onReceive(getTestContext(), + mInternal.onPackageBroadcast( genPackageChangedIntent(CALLING_PACKAGE_1, USER_10)); runWithCaller(CALLING_PACKAGE_1, USER_10, () -> { @@ -4257,7 +4262,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { // 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, () -> { @@ -4277,7 +4282,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { // 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, () -> { @@ -4301,7 +4306,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { // 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, () -> { @@ -4364,7 +4369,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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. @@ -5364,7 +5369,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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, () -> { @@ -5385,7 +5390,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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, () -> { @@ -5422,7 +5427,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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, () -> { @@ -5455,8 +5460,32 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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, () -> { @@ -5497,7 +5526,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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, () -> { @@ -5525,7 +5554,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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. @@ -5556,7 +5585,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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. @@ -5571,7 +5600,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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. @@ -5586,7 +5615,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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. @@ -5602,7 +5631,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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, () -> { @@ -5630,7 +5659,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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, () -> { @@ -5668,7 +5697,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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. @@ -5716,7 +5745,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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. @@ -5821,7 +5850,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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, () -> { @@ -5918,7 +5947,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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. @@ -5937,7 +5966,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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. @@ -5958,7 +5987,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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. @@ -5981,7 +6010,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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. @@ -6014,7 +6043,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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, () -> { @@ -6044,7 +6073,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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, () -> { @@ -6116,7 +6145,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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, () -> { @@ -6166,7 +6195,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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, () -> { @@ -6177,7 +6206,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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, () -> { @@ -6259,7 +6288,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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, () -> { @@ -6329,7 +6358,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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()); @@ -6455,7 +6484,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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()); @@ -6604,7 +6633,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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()); @@ -6624,7 +6653,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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()); @@ -6644,7 +6673,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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()); @@ -6663,7 +6692,7 @@ public class ShortcutManagerTest1 extends BaseShortcutManagerTest { 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()); diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest3.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest3.java index eb4db7a0f4af..fcf7ea2dcfbb 100644 --- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest3.java +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest3.java @@ -66,7 +66,7 @@ public class ShortcutManagerTest3 extends BaseShortcutManagerTest { 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)); } diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutPendingTasksTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutPendingTasksTest.java new file mode 100644 index 000000000000..bf1ed98983df --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutPendingTasksTest.java @@ -0,0 +1,123 @@ +/* + * 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()); + } +} |