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

Commit ae13eee9 authored by Makoto Onuki's avatar Makoto Onuki Committed by Android (Google) Code Review
Browse files

Merge "Handle package broadcasts before apps do" into nyc-mr1-dev

parents 9dbc259b a2241834
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -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);
}
+8 −0
Original line number Diff line number Diff line
@@ -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);
+159 −0
Original line number Diff line number Diff line
/*
 * 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));
    }
}
+169 −32
Original line number Diff line number Diff line
@@ -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,8 +2379,17 @@ 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)));
        }
    }

@@ -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();

        if (!mUserManager.isUserUnlocked(userId)) {
            if (DEBUG) {
                Slog.d(TAG, "Ignoring package broadcast " + action + " for locked/stopped user "
                        + userId);
            }
            return;
        }

        @Override
        public void onPackageRemoved(String packageName, int uid) {
            handlePackageRemoved(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 void onPackageDataCleared(String packageName, int uid) {
            handlePackageDataCleared(packageName, getChangingUserId());
        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);

        @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.
        } 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;
    }
}
+70 −39

File changed.

Preview size limit exceeded, changes collapsed.

Loading