/*
 * Copyright (C) 2006 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 static android.Manifest.permission.MANAGE_DEVICE_ADMINS;
import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS;
import static android.app.AppOpsManager.MODE_IGNORED;
import static android.app.admin.flags.Flags.crossUserSuspensionEnabledRo;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
import static android.content.pm.PackageManager.MATCH_FACTORY_ONLY;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
import static android.crashrecovery.flags.Flags.refactorCrashrecovery;
import static android.os.Process.INVALID_UID;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.os.UserHandle.USER_ALL;
import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
import static android.os.storage.StorageManager.FLAG_STORAGE_DE;
import static android.os.storage.StorageManager.FLAG_STORAGE_EXTERNAL;
import static android.provider.DeviceConfig.NAMESPACE_PACKAGE_MANAGER_SERVICE;

import static com.android.internal.annotations.VisibleForTesting.Visibility;
import static com.android.internal.util.FrameworkStatsLog.BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_INIT_TIME;
import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
import static com.android.server.pm.PackageManagerServiceUtils.isInstalledByAdb;
import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo;

import android.Manifest;
import android.annotation.AppIdInt;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SpecialUsers.CanBeALL;
import android.annotation.StringRes;
import android.annotation.UserIdInt;
import android.annotation.WorkerThread;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
import android.app.AppOpsManager;
import android.app.AppOpsManagerInternal;
import android.app.ApplicationExitInfo;
import android.app.ApplicationPackageManager;
import android.app.BroadcastOptions;
import android.app.IActivityManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.IDevicePolicyManager;
import android.app.admin.SecurityLog;
import android.app.backup.IBackupManager;
import android.app.role.RoleManager;
import android.companion.virtual.VirtualDeviceManager;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledAfter;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.IntentSender.SendIntentException;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ArchivedPackageParcel;
import android.content.pm.AuxiliaryResolveInfo;
import android.content.pm.ChangedPackages;
import android.content.pm.Checksum;
import android.content.pm.ComponentInfo;
import android.content.pm.DataLoaderType;
import android.content.pm.FallbackCategoryProvider;
import android.content.pm.FeatureInfo;
import android.content.pm.Flags;
import android.content.pm.GosPackageState;
import android.content.pm.IDexModuleRegisterCallback;
import android.content.pm.IOnChecksumsReadyListener;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver2;
import android.content.pm.IPackageLoadingProgressCallback;
import android.content.pm.IPackageMoveObserver;
import android.content.pm.IncrementalStatesInfo;
import android.content.pm.InstallSourceInfo;
import android.content.pm.InstantAppInfo;
import android.content.pm.InstantAppRequest;
import android.content.pm.ModuleInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.ComponentEnabledSetting;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackagePartitions;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.Signature;
import android.content.pm.SigningDetails;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.TestUtilityService;
import android.content.pm.UserInfo;
import android.content.pm.UserPackage;
import android.content.pm.VerifierDeviceIdentity;
import android.content.pm.VerifierInfo;
import android.content.pm.VersionedPackage;
import android.content.pm.overlay.OverlayPaths;
import android.content.pm.parsing.PackageLite;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Bitmap;
import android.hardware.display.DisplayManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.FileUtils;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.Message;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.ParcelableException;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.ReconcileSdkDataArgs;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.incremental.IncrementalManager;
import android.os.incremental.PerUidReadTimeouts;
import android.os.storage.StorageManager;
import android.os.storage.VolumeRecord;
import android.permission.PermissionManager;
import android.provider.DeviceConfig;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DisplayMetrics;
import android.util.EventLog;
import android.util.ExceptionUtils;
import android.util.IntArray;
import android.util.Log;
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.Xml;
import android.view.Display;

import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.F2fsUtils;
import com.android.internal.content.InstallLocationUtils;
import com.android.internal.content.om.OverlayConfig;
import com.android.internal.pm.parsing.PackageParser2;
import com.android.internal.pm.parsing.pkg.AndroidPackageInternal;
import com.android.internal.pm.parsing.pkg.ParsedPackage;
import com.android.internal.pm.pkg.component.ParsedInstrumentation;
import com.android.internal.pm.pkg.component.ParsedMainComponent;
import com.android.internal.pm.pkg.parsing.ParsingPackageUtils;
import com.android.internal.telephony.CarrierAppUtils;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.util.FunctionalUtils;
import com.android.internal.util.Preconditions;
import com.android.modules.utils.TypedXmlPullParser;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.permission.persistence.RuntimePermissionsPersistence;
import com.android.permission.persistence.RuntimePermissionsState;
import com.android.server.EventLogTags;
import com.android.server.FgThread;
import com.android.server.LocalManagerRegistry;
import com.android.server.LocalServices;
import com.android.server.LockGuard;
import com.android.server.ServiceThread;
import com.android.server.StorageManagerInternal;
import com.android.server.SystemConfig;
import com.android.server.ThreadPriorityBooster;
import com.android.server.Watchdog;
import com.android.server.apphibernation.AppHibernationManagerInternal;
import com.android.server.appwindowlayout.AppWindowLayoutSettingsService;
import com.android.server.art.DexUseManagerLocal;
import com.android.server.art.model.DeleteResult;
import com.android.server.compat.CompatChange;
import com.android.server.compat.PlatformCompat;
import com.android.server.crashrecovery.CrashRecoveryAdaptor;
import com.android.server.ext.PackageManagerHooks;
import com.android.server.ext.SeInfoOverride;
import com.android.server.pm.Installer.InstallerException;
import com.android.server.pm.Settings.VersionInfo;
import com.android.server.pm.dex.ArtManagerService;
import com.android.server.pm.dex.DynamicCodeLogger;
import com.android.server.pm.local.PackageManagerLocalImpl;
import com.android.server.pm.parsing.PackageCacher;
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.permission.LegacyPermissionManagerInternal;
import com.android.server.pm.permission.LegacyPermissionManagerService;
import com.android.server.pm.permission.LegacyPermissionSettings;
import com.android.server.pm.permission.PermissionManagerService;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.permission.SpecialRuntimePermUtils;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.ArchiveState;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.pkg.PackageUserStateInternal;
import com.android.server.pm.pkg.SharedUserApi;
import com.android.server.pm.pkg.mutate.PackageStateMutator;
import com.android.server.pm.pkg.mutate.PackageStateWrite;
import com.android.server.pm.pkg.mutate.PackageUserStateWrite;
import com.android.server.pm.resolution.ComponentResolver;
import com.android.server.pm.resolution.ComponentResolverApi;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.pm.verify.domain.DomainVerificationService;
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
import com.android.server.sdksandbox.SdkSandboxManagerLocal;
import com.android.server.storage.DeviceStorageMonitorInternal;
import com.android.server.utils.SnapshotCache;
import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.utils.Watchable;
import com.android.server.utils.Watched;
import com.android.server.utils.WatchedArrayMap;
import com.android.server.utils.WatchedSparseBooleanArray;
import com.android.server.utils.WatchedSparseIntArray;
import com.android.server.utils.Watcher;

import libcore.util.EmptyArray;
import libcore.util.HexEncoding;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Predicate;

/**
 * Keep track of all those APKs everywhere.
 * <p>
 * Internally there are three important locks:
 * <ul>
 * <li>{@link #mLock} is used to guard all in-memory parsed package details
 * and other related state. It is a fine-grained lock that should only be held
 * momentarily, as it's one of the most contended locks in the system.
 * <li>{@link #mInstallLock} is used to guard all {@code installd} access, whose
 * operations typically involve heavy lifting of application data on disk. Since
 * {@code installd} is single-threaded, and it's operations can often be slow,
 * this lock should never be acquired while already holding {@link #mLock}.
 * Conversely, it's safe to acquire {@link #mLock} momentarily while already
 * holding {@link #mInstallLock}.
 * <li>{@link #mSnapshotLock} is used to guard access to two snapshot fields: the snapshot
 * itself and the snapshot invalidation flag.  This lock should never be acquired while
 * already holding {@link #mLock}. Conversely, it's safe to acquire {@link #mLock}
 * momentarily while already holding {@link #mSnapshotLock}.
 * </ul>
 * Many internal methods rely on the caller to hold the appropriate locks, and
 * this contract is expressed through method name suffixes:
 * <ul>
 * <li>fooLI(): the caller must hold {@link #mInstallLock}
 * <li>fooLIF(): the caller must hold {@link #mInstallLock} and the package
 * being modified must be frozen
 * <li>fooLPr(): the caller must hold {@link #mLock} for reading
 * <li>fooLPw(): the caller must hold {@link #mLock} for writing
 * </ul>
 * {@link #mSnapshotLock} is taken in exactly one place - {@code snapshotComputer()}.  It
 * should not be taken anywhere else or used for any other purpose.
 * <p>
 * Because this class is very central to the platform's security; please run all
 * CTS and unit tests whenever making modifications:
 *
 * <pre>
 * $ runtest -c android.content.pm.PackageManagerTests frameworks-core
 * $ cts-tradefed run commandAndExit cts -m CtsAppSecurityHostTestCases
 * </pre>
 */
public class PackageManagerService implements PackageSender, TestUtilityService {

    static final String TAG = "PackageManager";
    public static final boolean DEBUG_SETTINGS = false;
    static final boolean DEBUG_PREFERRED = false;
    static final boolean DEBUG_UPGRADE = false;
    static final boolean DEBUG_DOMAIN_VERIFICATION = false;
    static final boolean DEBUG_BACKUP = false;
    public static final boolean DEBUG_INSTALL = false;
    public static final boolean DEBUG_REMOVE = false;
    static final boolean DEBUG_PACKAGE_INFO = false;
    static final boolean DEBUG_INTENT_MATCHING = false;
    public static final boolean DEBUG_PACKAGE_SCANNING = false;
    static final boolean DEBUG_VERIFY = false;
    public static final boolean DEBUG_PERMISSIONS = false;
    public static final boolean DEBUG_COMPRESSION = Build.IS_DEBUGGABLE;
    public static final boolean TRACE_SNAPSHOTS = false;
    private static final boolean DEBUG_PER_UID_READ_TIMEOUTS = false;

    static final boolean DEBUG_ABI_SELECTION = false;
    public static final boolean DEBUG_INSTANT = Build.IS_DEBUGGABLE;

    static final String SHELL_PACKAGE_NAME = "com.android.shell";

    static final boolean HIDE_EPHEMERAL_APIS = true;

    static final String PRECOMPILE_LAYOUTS = "pm.precompile_layouts";

    private static final int RADIO_UID = Process.PHONE_UID;
    private static final int LOG_UID = Process.LOG_UID;
    private static final int NFC_UID = Process.NFC_UID;
    private static final int BLUETOOTH_UID = Process.BLUETOOTH_UID;
    private static final int SHELL_UID = Process.SHELL_UID;
    private static final int SE_UID = Process.SE_UID;
    private static final int NETWORKSTACK_UID = Process.NETWORK_STACK_UID;
    private static final int UWB_UID = Process.UWB_UID;

    static final int SCAN_NO_DEX = 1 << 0;
    static final int SCAN_UPDATE_SIGNATURE = 1 << 1;
    static final int SCAN_NEW_INSTALL = 1 << 2;
    static final int SCAN_UPDATE_TIME = 1 << 3;
    static final int SCAN_BOOTING = 1 << 4;
    static final int SCAN_REQUIRE_KNOWN = 1 << 7;
    static final int SCAN_MOVE = 1 << 8;
    static final int SCAN_INITIAL = 1 << 9;
    static final int SCAN_DONT_KILL_APP = 1 << 10;
    static final int SCAN_IGNORE_FROZEN = 1 << 11;
    static final int SCAN_FIRST_BOOT_OR_UPGRADE = 1 << 12;
    static final int SCAN_AS_INSTANT_APP = 1 << 13;
    static final int SCAN_AS_FULL_APP = 1 << 14;
    static final int SCAN_AS_VIRTUAL_PRELOAD = 1 << 15;
    static final int SCAN_AS_SYSTEM = 1 << 16;
    static final int SCAN_AS_PRIVILEGED = 1 << 17;
    static final int SCAN_AS_OEM = 1 << 18;
    static final int SCAN_AS_VENDOR = 1 << 19;
    static final int SCAN_AS_PRODUCT = 1 << 20;
    static final int SCAN_AS_SYSTEM_EXT = 1 << 21;
    static final int SCAN_AS_ODM = 1 << 22;
    static final int SCAN_AS_APK_IN_APEX = 1 << 23;
    static final int SCAN_DROP_CACHE = 1 << 24;
    static final int SCAN_AS_FACTORY = 1 << 25;
    static final int SCAN_AS_APEX = 1 << 26;
    static final int SCAN_AS_STOPPED_SYSTEM_APP = 1 << 27;

    @IntDef(flag = true, prefix = { "SCAN_" }, value = {
            SCAN_NO_DEX,
            SCAN_UPDATE_SIGNATURE,
            SCAN_NEW_INSTALL,
            SCAN_UPDATE_TIME,
            SCAN_BOOTING,
            SCAN_REQUIRE_KNOWN,
            SCAN_MOVE,
            SCAN_INITIAL,
            SCAN_DONT_KILL_APP,
            SCAN_IGNORE_FROZEN,
            SCAN_FIRST_BOOT_OR_UPGRADE,
            SCAN_AS_INSTANT_APP,
            SCAN_AS_FULL_APP,
            SCAN_AS_VIRTUAL_PRELOAD,
            SCAN_AS_STOPPED_SYSTEM_APP,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface ScanFlags {}

    /**
     * Used as the result code of the {@link Computer#getPackageStartability(boolean, String, int,
     * int)}.
     */
    @IntDef(value = {
        PACKAGE_STARTABILITY_OK,
        PACKAGE_STARTABILITY_NOT_FOUND,
        PACKAGE_STARTABILITY_NOT_SYSTEM,
        PACKAGE_STARTABILITY_FROZEN,
        PACKAGE_STARTABILITY_DIRECT_BOOT_UNSUPPORTED,
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface PackageStartability {}

    /**
     * Used as the result code of the {@link Computer#getPackageStartability(boolean, String, int,
     * int)} to indicate the given package is allowed to start.
     */
    public static final int PACKAGE_STARTABILITY_OK = 0;

    /**
     * Used as the result code of the {@link Computer#getPackageStartability(boolean, String, int,
     * int)} to indicate the given package is <b>not</b> allowed to start because it's not found
     * (could be due to that package is invisible to the given user).
     */
    public static final int PACKAGE_STARTABILITY_NOT_FOUND = 1;

    /**
     * Used as the result code of the {@link Computer#getPackageStartability(boolean, String, int,
     * int)} to indicate the given package is <b>not</b> allowed to start because it's not a system
     * app and the system is running in safe mode.
     */
    public static final int PACKAGE_STARTABILITY_NOT_SYSTEM = 2;

    /**
     * Used as the result code of the {@link Computer#getPackageStartability(boolean, String, int,
     * int)} to indicate the given package is <b>not</b> allowed to start because it's currently
     * frozen.
     */
    public static final int PACKAGE_STARTABILITY_FROZEN = 3;

    /**
     * Used as the result code of the {@link Computer#getPackageStartability(boolean, String, int,
     * int)} to indicate the given package is <b>not</b> allowed to start because it doesn't support
     * direct boot.
     */
    public static final int PACKAGE_STARTABILITY_DIRECT_BOOT_UNSUPPORTED = 4;

    private static final String STATIC_SHARED_LIB_DELIMITER = "_";
    /**
     * Extension of the compressed packages
     */
    public final static String COMPRESSED_EXTENSION = ".gz";
    /** Suffix of stub packages on the system partition */
    public final static String STUB_SUFFIX = "-Stub";

    static final int[] EMPTY_INT_ARRAY = new int[0];

    /**
     * Timeout (in milliseconds) after which the watchdog should declare that
     * our handler thread is wedged.  The usual default for such things is one
     * minute but we sometimes do very lengthy I/O operations on this thread,
     * such as installing multi-gigabyte applications, so ours needs to be longer.
     */
    static final long WATCHDOG_TIMEOUT = 1000*60*10;     // ten minutes

    /**
     * Default IncFs timeouts. Maximum values in IncFs is 1hr.
     *
     * <p>If flag value is empty, the default value will be assigned.
     *
     * Flag type: {@code String}
     * Namespace: NAMESPACE_PACKAGE_MANAGER_SERVICE
     */
    private static final String PROPERTY_INCFS_DEFAULT_TIMEOUTS = "incfs_default_timeouts";

    /**
     * Known digesters with optional timeouts.
     *
     * Flag type: {@code String}
     * Namespace: NAMESPACE_PACKAGE_MANAGER_SERVICE
     */
    private static final String PROPERTY_KNOWN_DIGESTERS_LIST = "known_digesters_list";

    /**
     * Whether or not requesting the approval before committing sessions is available.
     *
     * Flag type: {@code boolean}
     * Namespace: NAMESPACE_PACKAGE_MANAGER_SERVICE
     */
    private static final String PROPERTY_IS_PRE_APPROVAL_REQUEST_AVAILABLE =
            "is_preapproval_available";

    /**
     * Whether or not the update ownership enforcement is available.
     *
     * Flag type: {@code boolean}
     * Namespace: NAMESPACE_PACKAGE_MANAGER_SERVICE
     */
    private static final String PROPERTY_IS_UPDATE_OWNERSHIP_ENFORCEMENT_AVAILABLE =
            "is_update_ownership_enforcement_available";

    private static final String PROPERTY_DEFERRED_NO_KILL_POST_DELETE_DELAY_MS_EXTENDED =
            "deferred_no_kill_post_delete_delay_ms_extended";

    /**
     * The default response for package verification timeout.
     *
     * This can be either PackageManager.VERIFICATION_ALLOW or
     * PackageManager.VERIFICATION_REJECT.
     */
    static final int DEFAULT_VERIFICATION_RESPONSE = PackageManager.VERIFICATION_ALLOW;

    /**
     * Adding an installer package name to a package that does not have one set requires the
     * INSTALL_PACKAGES permission.
     *
     * If the caller targets R, this will throw a SecurityException. Otherwise the request will
     * fail silently. In both cases, and regardless of whether this change is enabled, the
     * installer package will remain unchanged.
     */
    @ChangeId
    @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
    private static final long THROW_EXCEPTION_ON_REQUIRE_INSTALL_PACKAGES_TO_ADD_INSTALLER_PACKAGE =
            150857253;

    public static final String PLATFORM_PACKAGE_NAME = "android";

    static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";

    static final String PACKAGE_SCHEME = "package";

    private static final String COMPANION_PACKAGE_NAME = "com.android.companiondevicemanager";

    // How many required verifiers can be on the system.
    private static final int REQUIRED_VERIFIERS_MAX_COUNT = 2;

    /**
     * Specifies the minimum target SDK version an apk must specify in order to be installed
     * on the system. This improves security and privacy by blocking low
     * target sdk apps as malware can target older sdk versions to avoid
     * the enforcement of new API behavior.
     */
    public static final int MIN_INSTALLABLE_TARGET_SDK =
            Flags.minTargetSdk24() ? Build.VERSION_CODES.N : Build.VERSION_CODES.M;

    static final String RANDOM_DIR_PREFIX = "~~";
    static final char RANDOM_CODEPATH_PREFIX = '-';

    public static final String APP_METADATA_FILE_NAME = "app.metadata";

    static final int DEFAULT_FILE_ACCESS_MODE = 0644;

    static final int DEFAULT_NATIVE_LIBRARY_FILE_ACCESS_MODE = 0755;

    final Handler mHandler;
    final Handler mBackgroundHandler;

    final ProcessLoggingHandler mProcessLoggingHandler;

    private final int mSdkVersion;
    private final int mSdkVersionFull;
    final Context mContext;
    final boolean mFactoryTest;
    final DisplayMetrics mMetrics;
    private final int mDefParseFlags;
    private final String[] mSeparateProcesses;
    private final boolean mIsUpgrade;
    private final boolean mIsPreNMR1Upgrade;
    private final boolean mIsPreQUpgrade;
    // If mIsUpgrade == true, contains the prior SDK version, else -1.
    private final int mPriorSdkVersion;
    // If mIsUpgrade == true, contains the prior full SDK version, else -1.
    private final int mPriorSdkVersionFull;

    // Used for privilege escalation. MUST NOT BE CALLED WITH mPackages
    // LOCK HELD.  Can be called with mInstallLock held.
    @GuardedBy("mInstallLock")
    final Installer mInstaller;

    /** Directory where installed applications are stored */
    private final File mAppInstallDir;

    // ----------------------------------------------------------------

    // Lock for state used when installing and doing other long running
    // operations.  Methods that must be called with this lock held have
    // the suffix "LI".
    final PackageManagerTracedLock mInstallLock;

    // ----------------------------------------------------------------

    // Lock for global state used when modifying package state or settings.
    // Methods that must be called with this lock held have
    // the suffix "Locked". Some methods may use the legacy suffix "LP"
    final PackageManagerTracedLock mLock;

    // Ensures order of overlay updates until data storage can be moved to overlay code
    private final PackageManagerTracedLock mOverlayPathsLock = new PackageManagerTracedLock();

    // Lock alias for doing package state mutation
    private final PackageManagerTracedLock mPackageStateWriteLock;

    private final PackageStateMutator mPackageStateMutator = new PackageStateMutator(
            this::getPackageSettingForMutation,
            this::getDisabledPackageSettingForMutation);

    // Keys are String (package name), values are Package.
    @Watched
    @GuardedBy("mLock")
    final WatchedArrayMap<String, AndroidPackage> mPackages = new WatchedArrayMap<>();
    private final SnapshotCache<WatchedArrayMap<String, AndroidPackage>> mPackagesSnapshot =
            new SnapshotCache.Auto(mPackages, mPackages, "PackageManagerService.mPackages");

    // Keys are isolated uids and values are the uid of the application
    // that created the isolated process.
    @Watched
    @GuardedBy("mLock")
    final WatchedSparseIntArray mIsolatedOwners = new WatchedSparseIntArray();
    private final SnapshotCache<WatchedSparseIntArray> mIsolatedOwnersSnapshot =
            new SnapshotCache.Auto(mIsolatedOwners, mIsolatedOwners,
                                   "PackageManagerService.mIsolatedOwners");

    /**
     * Tracks existing packages prior to receiving an OTA. Keys are package name.
     * Only non-null during an OTA, and even then it is nulled again once systemReady().
     */
    private @Nullable ArraySet<String> mExistingPackages = null;

    /**
     * List of code paths that need to be released when the system becomes ready.
     * <p>
     * NOTE: We have to delay releasing cblocks for no other reason than we cannot
     * retrieve the setting {@link Secure#RELEASE_COMPRESS_BLOCKS_ON_INSTALL}. When
     * we no longer need to read that setting, cblock release can occur in the
     * constructor.
     *
     * @see Secure#RELEASE_COMPRESS_BLOCKS_ON_INSTALL
     * @see #systemReady()
     */
    @Nullable List<File> mReleaseOnSystemReady;

    /**
     * Whether or not system app permissions should be promoted from install to runtime.
     */
    boolean mPromoteSystemApps;

    private final TestUtilityService mTestUtilityService;

    @Watched
    @GuardedBy("mLock")
    final Settings mSettings;

    /**
     * Map of package names to frozen counts that are currently "frozen",
     * which means active surgery is being done on the code/data for that
     * package. The platform will refuse to launch frozen packages to avoid
     * race conditions.
     *
     * @see PackageFreezer
     */
    @GuardedBy("mLock")
    final WatchedArrayMap<String, Integer> mFrozenPackages = new WatchedArrayMap<>();
    private final SnapshotCache<WatchedArrayMap<String, Integer>> mFrozenPackagesSnapshot =
            new SnapshotCache.Auto(mFrozenPackages, mFrozenPackages,
                    "PackageManagerService.mFrozenPackages");

    final ProtectedPackages mProtectedPackages;

    private boolean mFirstBoot;

    final boolean mIsEngBuild;
    private final boolean mIsUserDebugBuild;
    private final String mIncrementalVersion;

    PackageManagerInternal.ExternalSourcesPolicy mExternalSourcesPolicy;

    private final ArrayMap<String, FeatureInfo> mAvailableFeatures;

    @Watched
    final InstantAppRegistry mInstantAppRegistry;

    @NonNull
    final ChangedPackagesTracker mChangedPackagesTracker;

    @NonNull
    private final PackageObserverHelper mPackageObserverHelper = new PackageObserverHelper();

    @NonNull
    private final PackageMonitorCallbackHelper mPackageMonitorCallbackHelper;

    private final ModuleInfoProvider mModuleInfoProvider;

    final ApexManager mApexManager;

    final PackageManagerServiceInjector mInjector;

    /**
     * The list of all system partitions that may contain packages in ascending order of
     * specificity (the more generic, the earlier in the list a partition appears).
     */
    @VisibleForTesting(visibility = Visibility.PACKAGE)
    public static final List<ScanPartition> SYSTEM_PARTITIONS = Collections.unmodifiableList(
            PackagePartitions.getOrderedPartitions(ScanPartition::new));

    private @NonNull final OverlayConfig mOverlayConfig;

    // Cached parsed flag value. Invalidated on each flag change.
    PerUidReadTimeouts[] mPerUidReadTimeoutsCache;

    private static final PerUidReadTimeouts[] EMPTY_PER_UID_READ_TIMEOUTS_ARRAY = {};

    private static class DefaultSystemWrapper implements
            PackageManagerServiceInjector.SystemWrapper {

        @Override
        public void disablePackageCaches() {
            // disable all package caches that shouldn't apply within system server
            PackageManager.disableApplicationInfoCache();
            PackageManager.disablePackageInfoCache();
            invalidateGetPackagesForUidCache(
                    PackageMetrics.INVALIDATION_REASON_DISABLE_PACKAGE_CACHES);
            ApplicationPackageManager.disableGetPackagesForUidCache();
            ApplicationPackageManager.invalidateHasSystemFeatureCache();
            PackageManager.corkPackageInfoCache();
            ApplicationPackageManager.invalidateQueryIntentActivitiesCache();
            ApplicationPackageManager.disableQueryIntentActivitiesCacheForCurrentProcess();
            AppOpsManager.disableCheckPackageCache();
        }

        @Override
        public void enablePackageCaches() {
            PackageManager.uncorkPackageInfoCache();
        }
    }

    @Watched
    final AppsFilterImpl mAppsFilter;

    final PackageParser2.Callback mPackageParserCallback;

    // Currently known shared libraries.
    @Watched
    private final SharedLibrariesImpl mSharedLibraries;

    // Mapping from instrumentation class names to info about them.
    @Watched
    private final WatchedArrayMap<ComponentName, ParsedInstrumentation> mInstrumentation =
            new WatchedArrayMap<>();
    private final SnapshotCache<WatchedArrayMap<ComponentName, ParsedInstrumentation>>
            mInstrumentationSnapshot =
            new SnapshotCache.Auto<>(mInstrumentation, mInstrumentation,
                                     "PackageManagerService.mInstrumentation");

    // Packages whose data we have transfered into another package, thus
    // should no longer exist.
    final ArraySet<String> mTransferredPackages = new ArraySet<>();

    // Broadcast actions that are only available to the system.
    @GuardedBy("mProtectedBroadcasts")
    final ArraySet<String> mProtectedBroadcasts = new ArraySet<>();

    /**
     * List of packages waiting for verification.
     * Handler thread only!
     */
    final SparseArray<PackageVerificationState> mPendingVerification = new SparseArray<>();

    /**
     * List of packages waiting for rollback to be enabled.
     * Handler thread only!
     */
    final SparseArray<VerifyingSession> mPendingEnableRollback = new SparseArray<>();

    final PackageInstallerService mInstallerService;
    final ArtManagerService mArtManagerService;

    private final DynamicCodeLogger mDynamicCodeLogger;

    private final AtomicInteger mNextMoveId = new AtomicInteger();
    final MovePackageHelper.MoveCallbacks mMoveCallbacks;

    /**
     * Token for keys in mPendingVerification.
     * Handler thread only!
     */
    int mPendingVerificationToken = 0;

    /**
     * Token for keys in mPendingEnableRollback.
     * Handler thread only!
     */
    int mPendingEnableRollbackToken = 0;

    @Watched(manual = true)
    private volatile boolean mSystemReady;
    @Watched(manual = true)
    private volatile boolean mSafeMode;
    @Watched
    private final WatchedSparseBooleanArray mWebInstantAppsDisabled =
            new WatchedSparseBooleanArray();

    @Watched(manual = true)
    private ApplicationInfo mAndroidApplication;
    @Watched(manual = true)
    private final ActivityInfo mResolveActivity = new ActivityInfo();
    private final ResolveInfo mResolveInfo = new ResolveInfo();
    @Watched(manual = true)
    ComponentName mResolveComponentName;
    private AndroidPackage mPlatformPackage;
    ComponentName mCustomResolverComponentName;

    // Recorded overlay paths configuration for the Android app info.
    private String[] mPlatformPackageOverlayPaths = null;
    private String[] mPlatformPackageOverlayResourceDirs = null;
    // And the same paths for the replaced resolver activity package
    private String[] mReplacedResolverPackageOverlayPaths = null;
    private String[] mReplacedResolverPackageOverlayResourceDirs = null;

    private boolean mResolverReplaced = false;

    @NonNull
    final DomainVerificationManagerInternal mDomainVerificationManager;

    /** The service connection to the ephemeral resolver */
    final InstantAppResolverConnection mInstantAppResolverConnection;
    /** Component used to show resolver settings for Instant Apps */
    final ComponentName mInstantAppResolverSettingsComponent;

    /** Activity used to install instant applications */
    @Watched(manual = true)
    ActivityInfo mInstantAppInstallerActivity;
    @Watched(manual = true)
    private final ResolveInfo mInstantAppInstallerInfo = new ResolveInfo();

    private final Map<String, InstallRequest>
            mNoKillInstallObservers = Collections.synchronizedMap(new HashMap<>());

    private final Map<String, InstallRequest>
            mPendingKillInstallObservers = Collections.synchronizedMap(new HashMap<>());

    // Internal interface for permission manager
    final PermissionManagerServiceInternal mPermissionManager;

    @Watched
    final ComponentResolver mComponentResolver;

    // Set of packages names to keep cached, even if they are uninstalled for all users
    @GuardedBy("mKeepUninstalledPackages")
    @NonNull
    private final ArraySet<String> mKeepUninstalledPackages = new ArraySet<>();

    // Cached reference to IDevicePolicyManager.
    private IDevicePolicyManager mDevicePolicyManager = null;

    private File mCacheDir;

    private Future<?> mPrepareAppDataFuture;

    final IncrementalManager mIncrementalManager;

    private final DefaultAppProvider mDefaultAppProvider;

    private final LegacyPermissionManagerInternal mLegacyPermissionManager;

    private final PackageProperty mPackageProperty = new PackageProperty();

    final PendingPackageBroadcasts mPendingBroadcasts;

    static final int SEND_PENDING_BROADCAST = 1;
    // public static final int UNUSED = 5;
    static final int POST_INSTALL = 9;
    static final int WRITE_SETTINGS = 13;
    static final int WRITE_DIRTY_PACKAGE_RESTRICTIONS = 14;
    static final int PACKAGE_VERIFIED = 15;
    static final int CHECK_PENDING_VERIFICATION = 16;
    // public static final int UNUSED = 17;
    // public static final int UNUSED = 18;
    static final int WRITE_PACKAGE_LIST = 19;
    static final int INSTANT_APP_RESOLUTION_PHASE_TWO = 20;
    static final int ENABLE_ROLLBACK_STATUS = 21;
    static final int ENABLE_ROLLBACK_TIMEOUT = 22;
    static final int DEFERRED_NO_KILL_POST_DELETE = 23;
    static final int DEFERRED_NO_KILL_INSTALL_OBSERVER = 24;
    // static final int UNUSED = 25;
    // static final int UNUSED = 26;
    static final int DOMAIN_VERIFICATION = 27;
    static final int PRUNE_UNUSED_STATIC_SHARED_LIBRARIES = 28;
    static final int DEFERRED_PENDING_KILL_INSTALL_OBSERVER = 29;

    static final int WRITE_USER_PACKAGE_RESTRICTIONS = 30;

    private static final int DEFERRED_NO_KILL_POST_DELETE_DELAY_MS = 3 * 1000;

    private static final long DEFERRED_NO_KILL_POST_DELETE_DELAY_MS_EXTENDED =
            TimeUnit.DAYS.toMillis(1);

    private static final int DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS = 500;
    private static final int DEFERRED_PENDING_KILL_INSTALL_OBSERVER_DELAY_MS = 1000;

    static final int WRITE_SETTINGS_DELAY = 10*1000;  // 10 seconds

    private static final long BROADCAST_DELAY_DURING_STARTUP = 10 * 1000L; // 10 seconds (in millis)
    private static final long BROADCAST_DELAY = 1 * 1000L; // 1 second (in millis)

    private static final long PRUNE_UNUSED_SHARED_LIBRARIES_DELAY =
            TimeUnit.MINUTES.toMillis(3); // 3 minutes

    // When the service constructor finished plus a delay (used for broadcast delay computation)
    private long mServiceStartWithDelay;

    static final long DEFAULT_UNUSED_STATIC_SHARED_LIB_MIN_CACHE_PERIOD =
            TimeUnit.DAYS.toMillis(7); /* 7 days */

    final UserManagerService mUserManager;

    final UserNeedsBadgingCache mUserNeedsBadging;

    // Stores a list of users whose package restrictions file needs to be updated
    final ArraySet<Integer> mDirtyUsers = new ArraySet<>();

    @GuardedBy("mRunningInstalls")
    final SparseArray<InstallRequest> mRunningInstalls = new SparseArray<>();
    int mNextInstallToken = 1;  // nonzero; will be wrapped back to 1 when ++ overflows

    final @NonNull String[] mRequiredVerifierPackages;
    final @NonNull String mRequiredInstallerPackage;
    final @NonNull String mRequiredUninstallerPackage;
    final @NonNull String mRequiredPermissionControllerPackage;
    final @Nullable String mSetupWizardPackage;
    final @Nullable String mStorageManagerPackage;
    final @Nullable String mDefaultTextClassifierPackage;
    final @Nullable String mSystemTextClassifierPackageName;
    final @Nullable String mConfiguratorPackage;
    final @Nullable String mAppPredictionServicePackage;
    final @Nullable String mIncidentReportApproverPackage;
    final @Nullable String mServicesExtensionPackageName;
    final @Nullable String mSharedSystemSharedLibraryPackageName;
    final @Nullable String mRetailDemoPackage;
    final @Nullable String mOverlayConfigSignaturePackage;
    final @Nullable String mRecentsPackage;
    final @Nullable String mAmbientContextDetectionPackage;
    final @Nullable String mWearableSensingPackage;
    final @NonNull Set<String> mInitialNonStoppedSystemPackages;
    final boolean mShouldStopSystemPackagesByDefault;
    private final @NonNull String mRequiredSdkSandboxPackage;
    private final @Nullable ComponentName mDeveloperVerificationServiceProvider;
    private final @Nullable String mDeveloperVerificationPolicyDelegatePackage;
    @GuardedBy("mLock")
    private final PackageUsage mPackageUsage = new PackageUsage();

    private final DomainVerificationConnection mDomainVerificationConnection;

    private final BroadcastHelper mBroadcastHelper;
    private final RemovePackageHelper mRemovePackageHelper;
    private final DeletePackageHelper mDeletePackageHelper;
    private final InitAppsHelper mInitAppsHelper;
    private final AppDataHelper mAppDataHelper;
    @NonNull private final InstallPackageHelper mInstallPackageHelper;
    private final PreferredActivityHelper mPreferredActivityHelper;
    private final ResolveIntentHelper mResolveIntentHelper;
    private final DexOptHelper mDexOptHelper;
    private final SuspendPackageHelper mSuspendPackageHelper;
    private final DistractingPackageHelper mDistractingPackageHelper;
    private final StorageEventHelper mStorageEventHelper;
    private final FreeStorageHelper mFreeStorageHelper;

    private static Set<String> sRestrictedPermissions;

    private static final boolean ENABLE_BOOST = false;

    private static ThreadPriorityBooster sThreadPriorityBooster = new ThreadPriorityBooster(
            Process.THREAD_PRIORITY_FOREGROUND, LockGuard.INDEX_PACKAGES);

    /**
     * Boost the priority of the thread before holding PM traced lock.
     * @hide
     */
    public static void boostPriorityForPackageManagerTracedLockedSection() {
        if (ENABLE_BOOST) {
            sThreadPriorityBooster.boost();
        }
    }


    /**
     * Restore the priority of the thread after release the PM traced lock.
     * @hide
     */
    public static void resetPriorityAfterPackageManagerTracedLockedSection() {
        if (ENABLE_BOOST) {
            sThreadPriorityBooster.reset();
        }
    }

    /**
     * Invalidate the package info cache, which includes updating the cached computer.
     * @hide
     */
    public static void invalidatePackageInfoCache(int invalidationReason) {
        PackageManager.invalidatePackageInfoCache();
        onChanged();
        PackageMetrics.reportCacheInvalidationEvent(
                PackageMetrics.CACHE_TYPE_APPLICATION_AND_PACKAGE_INFO, invalidationReason);
    }

    /**
     * Invalidate the get packages for UID cache, which includes updating the cached computer.
     * @hide
     */
    public static void invalidateGetPackagesForUidCache(int invalidationReason) {
        ApplicationPackageManager.invalidateGetPackagesForUidCache();
        PackageMetrics.reportCacheInvalidationEvent(
                PackageMetrics.CACHE_TYPE_GET_PACKAGES_FOR_UID, invalidationReason);
    }

    private final Watcher mWatcher = new Watcher() {
            @Override
                       public void onChange(@Nullable Watchable what) {
                PackageManagerService.onChange(what);
            }
        };

    /**
     * A Snapshot is a subset of PackageManagerService state.  A snapshot is either live
     * or snapped.  Live snapshots directly reference PackageManagerService attributes.
     * Snapped snapshots contain deep copies of the attributes.
     */
    class Snapshot {
        public static final int LIVE = 1;
        public static final int SNAPPED = 2;

        public final Settings settings;
        public final WatchedSparseIntArray isolatedOwners;
        public final WatchedArrayMap<String, AndroidPackage> packages;
        public final WatchedArrayMap<ComponentName, ParsedInstrumentation> instrumentation;
        public final WatchedSparseBooleanArray webInstantAppsDisabled;
        public final ComponentName resolveComponentName;
        public final ActivityInfo resolveActivity;
        public final ActivityInfo instantAppInstallerActivity;
        public final ResolveInfo instantAppInstallerInfo;
        public final InstantAppRegistry instantAppRegistry;
        public final ApplicationInfo androidApplication;
        public final String appPredictionServicePackage;
        public final AppsFilterSnapshot appsFilter;
        public final ComponentResolverApi componentResolver;
        public final PackageManagerService service;
        public final WatchedArrayMap<String, Integer> frozenPackages;
        public final SharedLibrariesRead sharedLibraries;

        Snapshot(int type) {
            if (type == Snapshot.SNAPPED) {
                settings = mSettings.snapshot();
                isolatedOwners = mIsolatedOwnersSnapshot.snapshot();
                packages = mPackagesSnapshot.snapshot();
                instrumentation = mInstrumentationSnapshot.snapshot();
                resolveComponentName = mResolveComponentName == null
                        ? null : mResolveComponentName.clone();
                resolveActivity = new ActivityInfo(mResolveActivity);
                instantAppInstallerActivity =
                        (mInstantAppInstallerActivity == null)
                        ? null
                        : new ActivityInfo(mInstantAppInstallerActivity);
                instantAppInstallerInfo = new ResolveInfo(mInstantAppInstallerInfo);
                webInstantAppsDisabled = mWebInstantAppsDisabled.snapshot();
                instantAppRegistry = mInstantAppRegistry.snapshot();
                androidApplication =
                        (mAndroidApplication == null)
                        ? null
                        : new ApplicationInfo(mAndroidApplication);
                appPredictionServicePackage = mAppPredictionServicePackage;
                appsFilter = mAppsFilter.snapshot();
                componentResolver = mComponentResolver.snapshot();
                frozenPackages = mFrozenPackagesSnapshot.snapshot();
                sharedLibraries = mSharedLibraries.snapshot();
            } else if (type == Snapshot.LIVE) {
                settings = mSettings;
                isolatedOwners = mIsolatedOwners;
                packages = mPackages;
                instrumentation = mInstrumentation;
                resolveComponentName = mResolveComponentName;
                resolveActivity = mResolveActivity;
                instantAppInstallerActivity = mInstantAppInstallerActivity;
                instantAppInstallerInfo = mInstantAppInstallerInfo;
                webInstantAppsDisabled = mWebInstantAppsDisabled;
                instantAppRegistry = mInstantAppRegistry;
                androidApplication = mAndroidApplication;
                appPredictionServicePackage = mAppPredictionServicePackage;
                appsFilter = mAppsFilter;
                componentResolver = mComponentResolver;
                frozenPackages = mFrozenPackages;
                sharedLibraries = mSharedLibraries;
            } else {
                throw new IllegalArgumentException();
            }
            service = PackageManagerService.this;
        }
    }

    // Compute read-only functions, based on live data.  This attribute may be modified multiple
    // times during the PackageManagerService constructor but it should not be modified thereafter.
    private ComputerLocked mLiveComputer;

    private static final AtomicReference<Computer> sSnapshot = new AtomicReference<>();

    // If this differs from Computer#getVersion, the snapshot is invalid (stale).
    private static final AtomicInteger sSnapshotPendingVersion = new AtomicInteger(1);

    /**
     * This lock is used to make reads from {@link #sSnapshotPendingVersion} and
     * {@link #sSnapshot} atomic inside {@code snapshotComputer()} when the versions mismatch.
     * This lock is not meant to be used outside that method. This lock must be taken before
     * {@link #mLock} is taken.
     */
    private final Object mSnapshotLock = new Object();

    /**
     * The snapshot statistics.  These are collected to track performance and to identify
     * situations in which the snapshots are misbehaving.
     */
    @Nullable
    private final SnapshotStatistics mSnapshotStatistics;

    /**
     * Return the cached computer.  The method will rebuild the cached computer if necessary.
     * The live computer will be returned if snapshots are disabled.
     */
    @VisibleForTesting(visibility = Visibility.PACKAGE)
    @NonNull
    public Computer snapshotComputer() {
        return snapshotComputer(true /*allowLiveComputer*/);
    }

    /**
     * This method should only ever be called from {@link PackageManagerLocal#snapshot()}.
     *
     * @param allowLiveComputer Whether to allow a live computer instance based on caller {@link
     *                          #mLock} hold state. In certain cases, like for {@link
     *                          PackageManagerLocal} API, it must be enforced that the caller gets
     *                          a snapshot at time, and never the live variant.
     * @deprecated Use {@link #snapshotComputer()}
     */
    @Deprecated
    @NonNull
    public Computer snapshotComputer(boolean allowLiveComputer) {
        var isHoldingPackageLock = Thread.holdsLock(mLock);
        if (allowLiveComputer) {
            if (isHoldingPackageLock) {
                // If the current thread holds mLock then it may have modified state but not
                // yet invalidated the snapshot.  Always give the thread the live computer.
                return mLiveComputer;
            }
        }

        var oldSnapshot = sSnapshot.get();
        var pendingVersion = sSnapshotPendingVersion.get();

        if (oldSnapshot != null && oldSnapshot.getVersion() == pendingVersion) {
            return oldSnapshot.use();
        }

        if (isHoldingPackageLock) {
            // If the current thread holds mLock then it already has exclusive write access to the
            // two snapshot fields, and we can just go ahead and rebuild the snapshot.
            @SuppressWarnings("GuardedBy")
            var newSnapshot = rebuildSnapshot(oldSnapshot, pendingVersion);
            sSnapshot.set(newSnapshot);
            return newSnapshot.use();
        }

        synchronized (mSnapshotLock) {
            // Re-capture pending version in case a new invalidation occurred since last check
            var rebuildSnapshot = sSnapshot.get();
            var rebuildVersion = sSnapshotPendingVersion.get();

            // Check the versions again while the lock is held, in case the rebuild time caused
            // multiple threads to wait on the snapshot lock. When the first thread finishes
            // a rebuild, the snapshot is now valid and the other waiting threads can use it
            // without kicking off their own rebuilds.
            if (rebuildSnapshot != null && rebuildSnapshot.getVersion() == rebuildVersion) {
                return rebuildSnapshot.use();
            }

            synchronized (mLock) {
                // Fetch version one last time to ensure that the rebuilt snapshot matches
                // the latest invalidation, which could have come in between entering the
                // SnapshotLock and mLock sync blocks.
                rebuildSnapshot = sSnapshot.get();
                rebuildVersion = sSnapshotPendingVersion.get();
                if (rebuildSnapshot != null && rebuildSnapshot.getVersion() == rebuildVersion) {
                    return rebuildSnapshot.use();
                }

                // Build the snapshot for this version
                var newSnapshot = rebuildSnapshot(rebuildSnapshot, rebuildVersion);
                sSnapshot.set(newSnapshot);
                return newSnapshot.use();
            }
        }
    }

    @GuardedBy("mLock")
    private Computer rebuildSnapshot(@Nullable Computer oldSnapshot, int newVersion) {
        var now = SystemClock.currentTimeMicro();
        var hits = oldSnapshot == null ? -1 : oldSnapshot.getUsed();
        var args = new Snapshot(Snapshot.SNAPPED);
        var newSnapshot = new ComputerEngine(args, newVersion);
        var done = SystemClock.currentTimeMicro();

        if (mSnapshotStatistics != null) {
            mSnapshotStatistics.rebuild(now, done, hits, newSnapshot.getPackageStates().size());
        }
        return newSnapshot;
    }

    /**
     * Create a live computer
     */
    private ComputerLocked createLiveComputer() {
        return new ComputerLocked(new Snapshot(Snapshot.LIVE));
    }

    /**
     * This method is called when the state of PackageManagerService changes so as to
     * invalidate the current snapshot.
     * @param what The {@link Watchable} that reported the change
     * @hide
     */
    public static void onChange(@Nullable Watchable what) {
        if (TRACE_SNAPSHOTS) {
            Log.i(TAG, "snapshot: onChange(" + what + ")");
        }
        sSnapshotPendingVersion.incrementAndGet();
    }

    /**
     * Report a locally-detected change to observers.  The <what> parameter is left null,
     * but it signifies that the change was detected by PackageManagerService itself.
     */
    static void onChanged() {
        onChange(null);
    }

    void notifyInstallObserver(String packageName, boolean killApp) {
        final InstallRequest installRequest =
                killApp ? mPendingKillInstallObservers.remove(packageName)
                        : mNoKillInstallObservers.remove(packageName);

        if (installRequest != null) {
            notifyInstallObserver(installRequest);
        }
    }

    void notifyInstallObserver(InstallRequest request) {
        if (request.getObserver() != null) {
            try {
                Bundle extras = extrasForInstallResult(request);
                request.getObserver().onPackageInstalled(request.getName(),
                        request.getReturnCode(), request.getReturnMsg(), extras);
            } catch (RemoteException e) {
                Slog.i(TAG, "Observer no longer exists.");
            }
        }
    }

    void scheduleDeferredNoKillInstallObserver(InstallRequest request) {
        String packageName = request.getPkg().getPackageName();
        mNoKillInstallObservers.put(packageName, request);
        Message message = mHandler.obtainMessage(DEFERRED_NO_KILL_INSTALL_OBSERVER, packageName);
        mHandler.sendMessageDelayed(message, DEFERRED_NO_KILL_INSTALL_OBSERVER_DELAY_MS);
    }

    void scheduleDeferredNoKillPostDelete(CleanUpArgs args) {
        Message message = mHandler.obtainMessage(DEFERRED_NO_KILL_POST_DELETE, args);
        // If the feature flag is on, retain the old files for a day. Otherwise, delete the old
        // files after a few seconds.
        long deleteDelayMillis = DEFERRED_NO_KILL_POST_DELETE_DELAY_MS;
        deleteDelayMillis = Binder.withCleanCallingIdentity(() -> {
            return DeviceConfig.getLong(NAMESPACE_PACKAGE_MANAGER_SERVICE,
                    /* name= */ PROPERTY_DEFERRED_NO_KILL_POST_DELETE_DELAY_MS_EXTENDED,
                    /* defaultValue= */ DEFERRED_NO_KILL_POST_DELETE_DELAY_MS_EXTENDED);
        });
        Slog.w(TAG, "Delaying the deletion of <" + args.getCodePath() + "> by "
                + deleteDelayMillis + "ms or till the next reboot");
        mHandler.sendMessageDelayed(message, deleteDelayMillis);
    }

    void schedulePruneUnusedStaticSharedLibraries(boolean delay) {
        mHandler.removeMessages(PRUNE_UNUSED_STATIC_SHARED_LIBRARIES);
        mHandler.sendEmptyMessageDelayed(PRUNE_UNUSED_STATIC_SHARED_LIBRARIES,
                delay ? getPruneUnusedSharedLibrariesDelay() : 0);
    }

    void scheduleDeferredPendingKillInstallObserver(InstallRequest request) {
        final String packageName = request.getPkg().getPackageName();
        mPendingKillInstallObservers.put(packageName, request);
        final Message message = mHandler.obtainMessage(DEFERRED_PENDING_KILL_INSTALL_OBSERVER,
                packageName);
        mHandler.sendMessageDelayed(message, DEFERRED_PENDING_KILL_INSTALL_OBSERVER_DELAY_MS);
    }

    private static long getPruneUnusedSharedLibrariesDelay() {
        return SystemProperties.getLong("debug.pm.prune_unused_shared_libraries_delay",
                PRUNE_UNUSED_SHARED_LIBRARIES_DELAY);
    }

    /**
     * Requests checksums for the APK file.
     * See {@link PackageInstaller.Session#requestChecksums} for details.
     */
    public void requestFileChecksums(@NonNull File file,
            @NonNull String installerPackageName, @Checksum.TypeMask int optional,
            @Checksum.TypeMask int required, @Nullable List trustedInstallers,
            @NonNull IOnChecksumsReadyListener onChecksumsReadyListener)
            throws FileNotFoundException {
        if (!file.exists()) {
            throw new FileNotFoundException(file.getAbsolutePath());
        }

        final Executor executor = mInjector.getBackgroundExecutor();
        final Handler handler = mInjector.getBackgroundHandler();
        final Certificate[] trustedCerts = (trustedInstallers != null) ? decodeCertificates(
                trustedInstallers) : null;

        final List<Pair<String, File>> filesToChecksum = new ArrayList<>(1);
        filesToChecksum.add(Pair.create(null, file));

        executor.execute(() -> {
            ApkChecksums.Injector injector = new ApkChecksums.Injector(
                    () -> mContext,
                    () -> handler,
                    mInjector::getIncrementalManager,
                    () -> mInjector.getLocalService(PackageManagerInternal.class));
            ApkChecksums.getChecksums(filesToChecksum, optional, required, installerPackageName,
                    trustedCerts, onChecksumsReadyListener, injector);
        });
    }

    void requestChecksumsInternal(@NonNull Computer snapshot, @NonNull String packageName,
            boolean includeSplits, @Checksum.TypeMask int optional, @Checksum.TypeMask int required,
            @Nullable List trustedInstallers,
            @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId,
            @NonNull Executor executor, @NonNull Handler handler) {
        Objects.requireNonNull(packageName);
        Objects.requireNonNull(onChecksumsReadyListener);
        Objects.requireNonNull(executor);
        Objects.requireNonNull(handler);

        final ApplicationInfo applicationInfo = snapshot.getApplicationInfoInternal(packageName, 0,
                Binder.getCallingUid(), userId);
        if (applicationInfo == null) {
            throw new ParcelableException(new PackageManager.NameNotFoundException(packageName));
        }

        final InstallSourceInfo installSourceInfo = snapshot.getInstallSourceInfo(packageName,
                userId);
        final String installerPackageName;
        if (installSourceInfo != null) {
            final String initiatingPackageName = installSourceInfo.getInitiatingPackageName();
            if (!isInstalledByAdb(initiatingPackageName)) {
                installerPackageName = initiatingPackageName;
            } else {
                installerPackageName = installSourceInfo.getInstallingPackageName();
            }
        } else {
            installerPackageName = null;
        }

        List<Pair<String, File>> filesToChecksum = new ArrayList<>();

        // Adding base split.
        filesToChecksum.add(Pair.create(null, new File(applicationInfo.sourceDir)));

        // Adding other splits.
        if (includeSplits && applicationInfo.splitNames != null) {
            for (int i = 0, size = applicationInfo.splitNames.length; i < size; ++i) {
                filesToChecksum.add(Pair.create(applicationInfo.splitNames[i],
                        new File(applicationInfo.splitSourceDirs[i])));
            }
        }

        final Certificate[] trustedCerts = (trustedInstallers != null) ? decodeCertificates(
                trustedInstallers) : null;

        executor.execute(() -> {
            ApkChecksums.Injector injector = new ApkChecksums.Injector(
                    () -> mContext,
                    () -> handler,
                    mInjector::getIncrementalManager,
                    () -> mInjector.getLocalService(PackageManagerInternal.class));
            ApkChecksums.getChecksums(filesToChecksum, optional, required, installerPackageName,
                    trustedCerts, onChecksumsReadyListener, injector);
        });
    }

    private static @NonNull Certificate[] decodeCertificates(@NonNull List certs) {
        try {
            final CertificateFactory cf = CertificateFactory.getInstance("X.509");
            final Certificate[] result = new Certificate[certs.size()];
            for (int i = 0, size = certs.size(); i < size; ++i) {
                final InputStream is = new ByteArrayInputStream((byte[]) certs.get(i));
                final X509Certificate cert = (X509Certificate) cf.generateCertificate(is);
                result[i] = cert;
            }
            return result;
        } catch (CertificateException e) {
            throw ExceptionUtils.propagate(e);
        }
    }

    private static Bundle extrasForInstallResult(InstallRequest request) {
        Bundle extras = null;
        switch (request.getReturnCode()) {
            case PackageManager.INSTALL_FAILED_DUPLICATE_PERMISSION: {
                extras = new Bundle();
                extras.putString(PackageManager.EXTRA_FAILURE_EXISTING_PERMISSION,
                        request.getOrigPermission());
                extras.putString(PackageManager.EXTRA_FAILURE_EXISTING_PACKAGE,
                        request.getOrigPackage());
                break;
            }
            case PackageManager.INSTALL_SUCCEEDED: {
                extras = new Bundle();
                extras.putBoolean(Intent.EXTRA_REPLACING,
                        request.getRemovedInfo() != null
                                && request.getRemovedInfo().mRemovedPackage != null);
                break;
            }
        }
        if (!request.getWarnings().isEmpty() && extras != null) {
            extras.putStringArrayList(PackageInstaller.EXTRA_WARNINGS, request.getWarnings());
        }
        return extras;
    }

    ArchivedPackageParcel getArchivedPackageInternal(@NonNull String packageName, int userId) {
        Objects.requireNonNull(packageName);
        int binderUid = Binder.getCallingUid();

        Computer snapshot = snapshotComputer();
        snapshot.enforceCrossUserPermission(binderUid, userId, true, true,
                "getArchivedPackage");

        ArchivedPackageParcel archPkg = new ArchivedPackageParcel();
        archPkg.packageName = packageName;

        ArchiveState archiveState;
        synchronized (mLock) {
            PackageSetting ps = mSettings.getPackageLPr(packageName);
            if (ps == null || snapshot.shouldFilterApplication(ps, binderUid, userId)) {
                return null;
            }
            var psi = ps.getUserStateOrDefault(userId);
            archiveState = psi.getArchiveState();
            if (archiveState == null && !psi.isInstalled()) {
                return null;
            }

            archPkg.signingDetails = ps.getSigningDetails();

            long longVersionCode = ps.getVersionCode();
            archPkg.versionCodeMajor = (int) (longVersionCode >> 32);
            archPkg.versionCode = (int) longVersionCode;

            archPkg.targetSdkVersion = ps.getTargetSdkVersion();

            // These get translated in flags important for user data management.
            archPkg.defaultToDeviceProtectedStorage = String.valueOf(
                    ps.isDefaultToDeviceProtectedStorage());
            archPkg.requestLegacyExternalStorage = String.valueOf(
                    ps.isRequestLegacyExternalStorage());
            archPkg.userDataFragile = String.valueOf(ps.isUserDataFragile());
        }

        try {
            if (archiveState != null) {
                archPkg.archivedActivities = PackageArchiver.createArchivedActivities(
                        archiveState);
            } else {
                final int iconSize = mContext.getSystemService(
                        ActivityManager.class).getLauncherLargeIconSize();

                var mainActivities =
                        mInstallerService.mPackageArchiver.getLauncherActivityInfos(packageName,
                                userId);
                archPkg.archivedActivities = PackageArchiver.createArchivedActivities(
                        mainActivities, iconSize);
            }
        } catch (Exception e) {
            throw new IllegalArgumentException("Package does not have a main activity", e);
        }

        return archPkg;
    }

    void markPackageAsArchivedIfNeeded(PackageSetting pkgSetting,
            ArchivedPackageParcel archivePackage, SparseArray<String> responsibleInstallerTitles,
            int[] userIds) {
        if (pkgSetting == null || archivePackage == null
                || archivePackage.archivedActivities == null
                || responsibleInstallerTitles == null
                || userIds == null || userIds.length == 0) {
            return;
        }

        // Initialize all necessary settings for archival installation.
        pkgSetting
                // No package.
                .setPkg(null)
                // Mark for later restore.
                .setPendingRestore(true);
        for (int userId : userIds) {
            // Unmark "installed" for all users.
            pkgSetting
                    .modifyUserState(userId)
                    .setInstalled(false);
        }

        String responsibleInstallerPackage = PackageArchiver.getResponsibleInstallerPackage(
                pkgSetting);
        // TODO(b/278553670) Check if responsibleInstallerPackage supports unarchival.
        if (TextUtils.isEmpty(responsibleInstallerPackage)) {
            Slog.e(TAG, "Can't create archive state: responsible installer is empty");
            return;
        }
        for (int userId : userIds) {
            var archiveState = mInstallerService.mPackageArchiver.createArchiveState(
                    archivePackage, userId, responsibleInstallerPackage,
                    responsibleInstallerTitles.get(userId));
            if (archiveState != null) {
                pkgSetting
                    .modifyUserState(userId)
                    .setArchiveState(archiveState);
            }
        }
    }


    void scheduleWriteSettings() {
        // We normally invalidate when we write settings, but in cases where we delay and
        // coalesce settings writes, this strategy would have us invalidate the cache too late.
        // Invalidating on schedule addresses this problem.
        invalidatePackageInfoCache(
                PackageMetrics.INVALIDATION_REASON_SCHEDULE_WRITE_SETTINGS);
        ApplicationPackageManager.invalidateQueryIntentActivitiesCache();
        if (!mHandler.hasMessages(WRITE_SETTINGS)) {
            mHandler.sendEmptyMessageDelayed(WRITE_SETTINGS, WRITE_SETTINGS_DELAY);
        }
    }

    void scheduleWritePackageListLocked(int userId) {
        invalidatePackageInfoCache(
                PackageMetrics.INVALIDATION_REASON_SCHEDULE_WRITE_PACKAGE_LIST);
        ApplicationPackageManager.invalidateQueryIntentActivitiesCache();
        if (!mHandler.hasMessages(WRITE_PACKAGE_LIST)) {
            Message msg = mHandler.obtainMessage(WRITE_PACKAGE_LIST);
            msg.arg1 = userId;
            mHandler.sendMessageDelayed(msg, WRITE_SETTINGS_DELAY);
        }
    }

    void scheduleWritePackageRestrictions(UserHandle user) {
        final int userId = user == null ? USER_ALL : user.getIdentifier();
        scheduleWritePackageRestrictions(userId);
    }

    void scheduleWritePackageRestrictions(@CanBeALL @UserIdInt int userId) {
        invalidatePackageInfoCache(
                PackageMetrics.INVALIDATION_REASON_SCHEDULE_WRITE_PACKAGE_RESTRICTIONS);
        if (userId == USER_ALL) {
            synchronized (mDirtyUsers) {
                for (int aUserId : mUserManager.getUserIds()) {
                    mDirtyUsers.add(aUserId);
                }
            }
        } else {
            if (!mUserManager.exists(userId)) {
                return;
            }
            synchronized (mDirtyUsers) {
                mDirtyUsers.add(userId);
            }
        }
        if (!mBackgroundHandler.hasMessages(WRITE_DIRTY_PACKAGE_RESTRICTIONS)) {
            mBackgroundHandler.sendMessageDelayed(
                    mBackgroundHandler.obtainMessage(WRITE_DIRTY_PACKAGE_RESTRICTIONS, this),
                    WRITE_SETTINGS_DELAY);
        }
    }

    void writePendingRestrictions() {
        final Integer[] dirtyUsers;
        synchronized (mLock) {
            mBackgroundHandler.removeMessages(WRITE_DIRTY_PACKAGE_RESTRICTIONS);
            synchronized (mDirtyUsers) {
                if (mDirtyUsers.isEmpty()) {
                    return;
                }
                dirtyUsers = mDirtyUsers.toArray(Integer[]::new);
                mDirtyUsers.clear();
            }
        }
        mSettings.writePackageRestrictions(dirtyUsers);
    }

    void writeSettings(boolean sync) {
        final List<UserInfo> activeUsers = Settings.getActiveUsers(mUserManager);
        synchronized (mLock) {
            mHandler.removeMessages(WRITE_SETTINGS);
            mBackgroundHandler.removeMessages(WRITE_DIRTY_PACKAGE_RESTRICTIONS);
            writeSettingsLPrTEMP(activeUsers, sync);
            synchronized (mDirtyUsers) {
                mDirtyUsers.clear();
            }
        }
    }

    void writePackageList(int userId) {
        List<UserInfo> activeUsers = Settings.getActiveUsers(mUserManager);
        synchronized (mLock) {
            mHandler.removeMessages(WRITE_PACKAGE_LIST);
            mSettings.writePackageListLPr(activeUsers, userId);
        }
    }

    private static final Handler.Callback BACKGROUND_HANDLER_CALLBACK = new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message msg) {
            switch (msg.what) {
                case WRITE_DIRTY_PACKAGE_RESTRICTIONS: {
                    PackageManagerService pm = (PackageManagerService) msg.obj;
                    pm.writePendingRestrictions();
                    return true;
                }
                case WRITE_USER_PACKAGE_RESTRICTIONS: {
                    final Runnable r = (Runnable) msg.obj;
                    r.run();
                    return true;
                }
            }
            return false;
        }
    };

    /** Starts PackageManagerService. */
    public static PackageManagerService main(Context context,
            Installer installer, @NonNull DomainVerificationService domainVerificationService,
            boolean factoryTest) {
        final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
                Trace.TRACE_TAG_PACKAGE_MANAGER);
        t.traceBegin("create package manager");
        final PackageManagerTracedLock lock = new PackageManagerTracedLock("mLock");
        final PackageManagerTracedLock installLock = new PackageManagerTracedLock("mInstallLock");

        HandlerThread backgroundThread = new ServiceThread("PackageManagerBg",
                Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
        backgroundThread.start();
        Handler backgroundHandler = new Handler(backgroundThread.getLooper(),
                BACKGROUND_HANDLER_CALLBACK);

        PackageManagerServiceInjector injector = new PackageManagerServiceInjector(
                context, lock, installer, installLock, new PackageAbiHelperImpl(),
                backgroundHandler,
                SYSTEM_PARTITIONS,
                (i, pm) -> new ComponentResolver(i.getUserManagerService(), pm.mUserNeedsBadging),
                (i, pm) -> PermissionManagerService.create(context,
                        i.getSystemConfig().getAvailableFeatures()),
                (i, pm) -> LocalServices.getService(AppOpsManagerInternal.class),
                (i, pm) -> new UserManagerService(context, pm,
                        new UserDataPreparer(installer, installLock, context), lock),
                (i, pm) -> new Settings(Environment.getDataDirectory(),
                        RuntimePermissionsPersistence.createInstance(),
                        i.getPermissionManagerServiceInternal(),
                        domainVerificationService, backgroundHandler,
                        lock),
                (i, pm) -> AppsFilterImpl.create(i,
                        i.getLocalService(PackageManagerInternal.class)),
                (i, pm) -> (PlatformCompat) ServiceManager.getService("platform_compat"),
                (i, pm) -> SystemConfig.getInstance(),
                (i, pm) -> new DynamicCodeLogger(i.getInstaller()),
                (i, pm) -> new ArtManagerService(i.getContext()),
                (i, pm) -> ApexManager.getInstance(),
                (i, pm) -> (IncrementalManager)
                        i.getContext().getSystemService(Context.INCREMENTAL_SERVICE),
                (i, pm) -> new DefaultAppProvider(() -> context.getSystemService(RoleManager.class),
                        () -> LocalServices.getService(UserManagerInternal.class)),
                (i, pm) -> new DisplayMetrics(),
                (i, pm) -> new PackageParser2(pm.mSeparateProcesses, i.getDisplayMetrics(),
                        pm.hasCacheDir() ? new PackageCacher(pm.mCacheDir, pm.mPackageParserCallback) : null,
                        pm.mPackageParserCallback) /* scanningCachingPackageParserProducer */,
                (i, pm) -> new PackageParser2(pm.mSeparateProcesses, i.getDisplayMetrics(), null,
                        pm.mPackageParserCallback) /* scanningPackageParserProducer */,
                (i, pm) -> new PackageParser2(pm.mSeparateProcesses, i.getDisplayMetrics(), null,
                        pm.mPackageParserCallback) /* preparingPackageParserProducer */,
                // Prepare a supplier of package parser for the staging manager to parse apex file
                // during the staging installation.
                (i, pm, developerVerifierPackage) -> new PackageInstallerService(
                        i.getContext(), pm, i::getScanningPackageParser, developerVerifierPackage),
                (i, pm, cn) -> new InstantAppResolverConnection(
                        i.getContext(), cn, Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE),
                (i, pm) -> new ModuleInfoProvider(i.getContext()),
                (i, pm) -> LegacyPermissionManagerService.create(i.getContext()),
                (i, pm) -> domainVerificationService,
                (i, pm) -> {
                    HandlerThread thread = new ServiceThread(TAG,
                            Process.THREAD_PRIORITY_DEFAULT, true /*allowIo*/);
                    thread.start();
                    return new PackageHandler(thread.getLooper(), pm);
                },
                new DefaultSystemWrapper(),
                LocalServices::getService,
                context::getSystemService,
                (i, pm) -> IBackupManager.Stub.asInterface(ServiceManager.getService(
                        Context.BACKUP_SERVICE)),
                (i, pm) -> new SharedLibrariesImpl(pm, i),
                (i, pm) -> new CrossProfileIntentFilterHelper(i.getSettings(),
                        i.getUserManagerService(), i.getLock(), i.getUserManagerInternal(),
                        context),
                (i, pm) -> new UpdateOwnershipHelper(),
                (i, pm) -> new PackageMonitorCallbackHelper());

        if (Build.VERSION.SDK_INT <= 0) {
            Slog.w(TAG, "**** ro.build.version.sdk not set!");
        }

        PackageManagerService m = new PackageManagerService(injector, factoryTest,
                PackagePartitions.FINGERPRINT, Build.IS_ENG, Build.IS_USERDEBUG,
                Build.VERSION.SDK_INT, Build.VERSION.INCREMENTAL, Build.VERSION.SDK_INT_FULL);

        t.traceEnd(); // "create package manager"

        final CompatChange.ChangeListener selinuxChangeListener = packageName -> {
            synchronized (m.mInstallLock) {
                final Computer snapshot = m.snapshotComputer();
                final PackageStateInternal packageState =
                        snapshot.getPackageStateInternal(packageName);
                if (packageState == null) {
                    Slog.e(TAG, "Failed to find package setting " + packageName);
                    return;
                }
                AndroidPackage pkg = packageState.getPkg();
                SharedUserApi sharedUser = snapshot.getSharedUser(
                        packageState.getSharedUserAppId());
                String oldSeInfo = packageState.getSeInfo();

                if (pkg == null) {
                    Slog.e(TAG, "Failed to find package " + packageName);
                    return;
                }
                final String newSeInfo = SELinuxMMAC.getSeInfo(packageState, pkg, sharedUser,
                        m.mInjector.getCompatibility());

                if (!newSeInfo.equals(oldSeInfo)) {
                    Slog.i(TAG, "Updating seInfo for package " + packageName + " from: "
                            + oldSeInfo + " to: " + newSeInfo);
                    m.commitPackageStateMutation(null, packageName,
                            state -> state.setOverrideSeInfo(newSeInfo));
                    m.mAppDataHelper.prepareAppDataAfterInstallLIF(pkg);
                }
            }
        };

        injector.getCompatibility().registerListener(SELinuxMMAC.SELINUX_LATEST_CHANGES,
                selinuxChangeListener);
        injector.getCompatibility().registerListener(SELinuxMMAC.SELINUX_R_CHANGES,
                selinuxChangeListener);

        m.selinuxChangeListener = selinuxChangeListener;

        m.installAllowlistedSystemPackages();
        IPackageManagerImpl iPackageManager = m.new IPackageManagerImpl();
        ServiceManager.addService("package", iPackageManager);
        final PackageManagerNative pmn = new PackageManagerNative(m);
        ServiceManager.addService("package_native", pmn);
        return m;
    }

    /** Install/uninstall system packages for all users based on their user-type, as applicable. */
    private void installAllowlistedSystemPackages() {
        if (mUserManager.installAllowlistedSystemPackages(isFirstBoot(), isDeviceUpgrading(),
                mExistingPackages)) {
            scheduleWritePackageRestrictions(USER_ALL);
            scheduleWriteSettings();
        }
    }

    // Link watchables to the class
    @SuppressWarnings("GuardedBy")
    private void registerObservers(boolean verify) {
        // Null check to handle nullable test parameters
        if (mPackages != null) {
            mPackages.registerObserver(mWatcher);
        }
        if (mSharedLibraries != null) {
            mSharedLibraries.registerObserver(mWatcher);
        }
        if (mInstrumentation != null) {
            mInstrumentation.registerObserver(mWatcher);
        }
        if (mWebInstantAppsDisabled != null) {
            mWebInstantAppsDisabled.registerObserver(mWatcher);
        }
        if (mAppsFilter != null) {
            mAppsFilter.registerObserver(mWatcher);
        }
        if (mInstantAppRegistry != null) {
            mInstantAppRegistry.registerObserver(mWatcher);
        }
        if (mSettings != null) {
            mSettings.registerObserver(mWatcher);
        }
        if (mIsolatedOwners != null) {
            mIsolatedOwners.registerObserver(mWatcher);
        }
        if (mComponentResolver != null) {
            mComponentResolver.registerObserver(mWatcher);
        }
        if (mFrozenPackages != null) {
            mFrozenPackages.registerObserver(mWatcher);
        }
        if (verify) {
            // If neither "build" attribute is true then this may be a mockito test,
            // and verification can fail as a false positive.
            Watchable.verifyWatchedAttributes(this, mWatcher, !(mIsEngBuild || mIsUserDebugBuild));
        }
    }

    /**
     * An extremely minimal constructor designed to start up a PackageManagerService instance for
     * testing.
     *
     * It is assumed that all methods under test will mock the internal fields and thus
     * none of the initialization is needed.
     */
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
    public PackageManagerService(@NonNull PackageManagerServiceInjector injector,
            @NonNull PackageManagerServiceTestParams testParams) {
        mInjector = injector;
        mInjector.bootstrap(this);
        mAppsFilter = injector.getAppsFilter();
        mComponentResolver = injector.getComponentResolver();
        mContext = injector.getContext();
        mInstaller = injector.getInstaller();
        mInstallLock = injector.getInstallLock();
        mLock = injector.getLock();
        mPackageStateWriteLock = mLock;
        mPermissionManager = injector.getPermissionManagerServiceInternal();
        mSettings = injector.getSettings();
        mUserManager = injector.getUserManagerService();
        mUserNeedsBadging = new UserNeedsBadgingCache(mUserManager);
        mDomainVerificationManager = injector.getDomainVerificationManagerInternal();
        mHandler = injector.getHandler();
        mBackgroundHandler = injector.getBackgroundHandler();
        mSharedLibraries = injector.getSharedLibrariesImpl();

        mApexManager = testParams.apexManager;
        mArtManagerService = testParams.artManagerService;
        mAvailableFeatures = testParams.availableFeatures;
        mDefParseFlags = testParams.defParseFlags;
        mDefaultAppProvider = testParams.defaultAppProvider;
        mLegacyPermissionManager = testParams.legacyPermissionManagerInternal;
        mDynamicCodeLogger = testParams.dynamicCodeLogger;
        mFactoryTest = testParams.factoryTest;
        mIncrementalManager = testParams.incrementalManager;
        mInstallerService = testParams.installerService;
        mInstantAppRegistry = testParams.instantAppRegistry;
        mChangedPackagesTracker = testParams.changedPackagesTracker;
        mInstantAppResolverConnection = testParams.instantAppResolverConnection;
        mInstantAppResolverSettingsComponent = testParams.instantAppResolverSettingsComponent;
        mIsPreNMR1Upgrade = testParams.isPreNmr1Upgrade;
        mIsPreQUpgrade = testParams.isPreQupgrade;
        mPriorSdkVersion = testParams.priorSdkVersion;
        mPriorSdkVersionFull = testParams.priorSdkVersionFull;
        mIsUpgrade = testParams.isUpgrade;
        mMetrics = testParams.Metrics;
        mModuleInfoProvider = testParams.moduleInfoProvider;
        mMoveCallbacks = testParams.moveCallbacks;
        mOverlayConfig = testParams.overlayConfig;
        mPackageParserCallback = testParams.packageParserCallback;
        mPendingBroadcasts = testParams.pendingPackageBroadcasts;
        mTestUtilityService = testParams.testUtilityService;
        mProcessLoggingHandler = testParams.processLoggingHandler;
        mProtectedPackages = testParams.protectedPackages;
        mSeparateProcesses = testParams.separateProcesses;
        mRequiredVerifierPackages = testParams.requiredVerifierPackages;
        mRequiredInstallerPackage = testParams.requiredInstallerPackage;
        mRequiredUninstallerPackage = testParams.requiredUninstallerPackage;
        mRequiredPermissionControllerPackage = testParams.requiredPermissionControllerPackage;
        mSetupWizardPackage = testParams.setupWizardPackage;
        mStorageManagerPackage = testParams.storageManagerPackage;
        mDefaultTextClassifierPackage = testParams.defaultTextClassifierPackage;
        mSystemTextClassifierPackageName = testParams.systemTextClassifierPackage;
        mRetailDemoPackage = testParams.retailDemoPackage;
        mRecentsPackage = testParams.recentsPackage;
        mAmbientContextDetectionPackage = testParams.ambientContextDetectionPackage;
        mWearableSensingPackage = testParams.wearableSensingPackage;
        mConfiguratorPackage = testParams.configuratorPackage;
        mAppPredictionServicePackage = testParams.appPredictionServicePackage;
        mIncidentReportApproverPackage = testParams.incidentReportApproverPackage;
        mServicesExtensionPackageName = testParams.servicesExtensionPackageName;
        mSharedSystemSharedLibraryPackageName = testParams.sharedSystemSharedLibraryPackageName;
        mOverlayConfigSignaturePackage = testParams.overlayConfigSignaturePackage;
        mResolveComponentName = testParams.resolveComponentName;
        mRequiredSdkSandboxPackage = testParams.requiredSdkSandboxPackage;
        mInitialNonStoppedSystemPackages = testParams.initialNonStoppedSystemPackages;
        mShouldStopSystemPackagesByDefault = testParams.shouldStopSystemPackagesByDefault;
        mDeveloperVerificationServiceProvider =
                testParams.developerVerificationServiceProvider;
        mDeveloperVerificationPolicyDelegatePackage =
                testParams.developerVerificationPolicyDelegatePackage;

        mLiveComputer = createLiveComputer();
        mSnapshotStatistics = null;

        mPackages.putAll(testParams.packages);
        mFreeStorageHelper = testParams.freeStorageHelper;
        mSdkVersion = testParams.sdkVersion;
        mSdkVersionFull = testParams.sdkVersionFull;
        mAppInstallDir = testParams.appInstallDir;
        mIsEngBuild = testParams.isEngBuild;
        mIsUserDebugBuild = testParams.isUserDebugBuild;
        mIncrementalVersion = testParams.incrementalVersion;
        mDomainVerificationConnection = new DomainVerificationConnection(this);

        mBroadcastHelper = testParams.broadcastHelper;
        mAppDataHelper = testParams.appDataHelper;
        mInstallPackageHelper = testParams.installPackageHelper;
        mRemovePackageHelper = testParams.removePackageHelper;
        mInitAppsHelper = testParams.initAndSystemPackageHelper;
        mDeletePackageHelper = testParams.deletePackageHelper;
        mPreferredActivityHelper = testParams.preferredActivityHelper;
        mResolveIntentHelper = testParams.resolveIntentHelper;
        mDexOptHelper = testParams.dexOptHelper;
        mSuspendPackageHelper = testParams.suspendPackageHelper;
        mDistractingPackageHelper = testParams.distractingPackageHelper;

        mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);

        mStorageEventHelper = testParams.storageEventHelper;
        mPackageMonitorCallbackHelper = testParams.packageMonitorCallbackHelper;

        registerObservers(false);
        invalidatePackageInfoCache(
                PackageMetrics.INVALIDATION_REASON_PACKAGE_MANAGER_SERVICE_INIT);
    }

    public PackageManagerService(PackageManagerServiceInjector injector, boolean factoryTest,
            final String partitionsFingerprint, final boolean isEngBuild,
            final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion,
            final int sdkVersionFull) {
        mIsEngBuild = isEngBuild;
        mIsUserDebugBuild = isUserDebugBuild;
        mSdkVersion = sdkVersion;

        mSdkVersionFull = sdkVersionFull;
        // If the major version of sdkVersionFull and sdkVersion are not equal,
        // throw RuntimeException to crash the system.
        if (Build.getMajorSdkVersion(sdkVersionFull) != sdkVersion) {
            throw new RuntimeException("sdkVersionFull:" + sdkVersionFull + " and sdkVersion: "
                    + sdkVersion + " don't match. Please check your build configurations!");
        }

        mIncrementalVersion = incrementalVersion;
        mInjector = injector;
        mInjector.getSystemWrapper().disablePackageCaches();

        final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing",
                Trace.TRACE_TAG_PACKAGE_MANAGER);
        mPendingBroadcasts = new PendingPackageBroadcasts();

        mInjector.bootstrap(this);
        mLock = injector.getLock();
        mPackageStateWriteLock = mLock;
        mInstallLock = injector.getInstallLock();
        LockGuard.installLock(mLock, LockGuard.INDEX_PACKAGES);
        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
                SystemClock.uptimeMillis());

        mContext = injector.getContext();
        mFactoryTest = factoryTest;
        mMetrics = injector.getDisplayMetrics();
        mInstaller = injector.getInstaller();
        mFreeStorageHelper = new FreeStorageHelper(this);

        // Create sub-components that provide services / data. Order here is important.
        t.traceBegin("createSubComponents");

        // Expose private service for system components to use.
        LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());
        LocalManagerRegistry.addManager(PackageManagerLocal.class,
                new PackageManagerLocalImpl(this));
        LocalServices.addService(TestUtilityService.class, this);
        mTestUtilityService = LocalServices.getService(TestUtilityService.class);
        mUserManager = injector.getUserManagerService();
        mUserNeedsBadging = new UserNeedsBadgingCache(mUserManager);
        mComponentResolver = injector.getComponentResolver();
        mPermissionManager = injector.getPermissionManagerServiceInternal();
        mSettings = injector.getSettings();
        mIncrementalManager = mInjector.getIncrementalManager();
        mDefaultAppProvider = mInjector.getDefaultAppProvider();
        mLegacyPermissionManager = mInjector.getLegacyPermissionManagerInternal();
        PlatformCompat platformCompat = mInjector.getCompatibility();
        mPackageParserCallback = new PackageParser2.Callback() {
            @Override
            public boolean isChangeEnabled(long changeId, @NonNull ApplicationInfo appInfo) {
                return platformCompat.isChangeEnabled(changeId, appInfo);
            }

            @Override
            public boolean hasFeature(String feature) {
                return PackageManagerService.this.hasSystemFeature(feature, 0);
            }

            @Override
            public Set<String> getHiddenApiWhitelistedApps() {
                return SystemConfig.getInstance().getHiddenApiWhitelistedApps();
            }

            @Override
            public Set<String> getInstallConstraintsAllowlist() {
                return SystemConfig.getInstance().getInstallConstraintsAllowlist();
            }
        };

        // CHECKSTYLE:ON IndentationCheck
        t.traceEnd();

        t.traceBegin("get system config");
        SystemConfig systemConfig = injector.getSystemConfig();
        t.traceEnd();

        t.traceBegin("addSharedUsers");
        mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.se", SE_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.networkstack", NETWORKSTACK_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.uwb", UWB_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        final ArrayMap<String, Integer> oemDefinedUids = systemConfig.getOemDefinedUids();
        final int numOemDefinedUids = oemDefinedUids.size();
        for (int i = 0; i < numOemDefinedUids; i++) {
            mSettings.addOemSharedUserLPw(oemDefinedUids.keyAt(i), oemDefinedUids.valueAt(i),
                    ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        }

        t.traceEnd();

        String separateProcesses = SystemProperties.get("debug.separate_processes");

        if (separateProcesses != null && separateProcesses.length() > 0) {
            if ("*".equals(separateProcesses)) {
                mDefParseFlags = ParsingPackageUtils.PARSE_IGNORE_PROCESSES;
                mSeparateProcesses = null;
                Slog.w(TAG, "Running with debug.separate_processes: * (ALL)");
            } else {
                mDefParseFlags = 0;
                mSeparateProcesses = separateProcesses.split(",");
                Slog.w(TAG, "Running with debug.separate_processes: "
                        + separateProcesses);
            }
        } else {
            mDefParseFlags = 0;
            mSeparateProcesses = null;
        }

        mDynamicCodeLogger = injector.getDynamicCodeLogger();
        mArtManagerService = injector.getArtManagerService();
        mMoveCallbacks = new MovePackageHelper.MoveCallbacks(FgThread.get().getLooper());
        mSharedLibraries = mInjector.getSharedLibrariesImpl();
        mBackgroundHandler = injector.getBackgroundHandler();

        mContext.getSystemService(DisplayManager.class)
                .getDisplay(Display.DEFAULT_DISPLAY).getMetrics(mMetrics);

        mAvailableFeatures = systemConfig.getAvailableFeatures();

        mProtectedPackages = new ProtectedPackages(mContext);

        mApexManager = injector.getApexManager();
        mAppsFilter = mInjector.getAppsFilter();

        mChangedPackagesTracker = new ChangedPackagesTracker();

        mAppInstallDir = new File(Environment.getDataDirectory(), "app");

        mDomainVerificationConnection = new DomainVerificationConnection(this);
        mDomainVerificationManager = injector.getDomainVerificationManagerInternal();
        mDomainVerificationManager.setConnection(mDomainVerificationConnection);

        mBroadcastHelper = new BroadcastHelper(mInjector);
        mPackageMonitorCallbackHelper = injector.getPackageMonitorCallbackHelper();
        mAppDataHelper = new AppDataHelper(this);
        mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper, mBroadcastHelper);
        mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper,
                mBroadcastHelper);
        mInstallPackageHelper = new InstallPackageHelper(this, mAppDataHelper, mRemovePackageHelper,
                mDeletePackageHelper, mBroadcastHelper);

        mInstantAppRegistry = new InstantAppRegistry(mContext, mPermissionManager,
                mInjector.getUserManagerInternal(), mDeletePackageHelper);

        mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);
        mPreferredActivityHelper = new PreferredActivityHelper(this, mBroadcastHelper);
        mResolveIntentHelper = new ResolveIntentHelper(mContext, mPreferredActivityHelper,
                injector.getCompatibility(), mUserManager, mDomainVerificationManager,
                mUserNeedsBadging, () -> mResolveInfo, () -> mInstantAppInstallerActivity);
        mDexOptHelper = new DexOptHelper(this);
        mSuspendPackageHelper = new SuspendPackageHelper(this, mInjector, mBroadcastHelper,
                mProtectedPackages);
        mDistractingPackageHelper = new DistractingPackageHelper(this, mBroadcastHelper,
                mSuspendPackageHelper);
        mStorageEventHelper = new StorageEventHelper(this, mDeletePackageHelper,
                mRemovePackageHelper);

        synchronized (mLock) {
            // Create the computer as soon as the state objects have been installed.  The
            // cached computer is the same as the live computer until the end of the
            // constructor, at which time the invalidation method updates it.
            mSnapshotStatistics = new SnapshotStatistics();
            sSnapshotPendingVersion.incrementAndGet();
            mLiveComputer = createLiveComputer();
            registerObservers(true);
        }

        Computer computer = mLiveComputer;
        // CHECKSTYLE:OFF IndentationCheck
        try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) {
        // writer
        synchronized (mLock) {
            mHandler = injector.getHandler();
            mProcessLoggingHandler = new ProcessLoggingHandler();
            Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);

            ArrayMap<String, SystemConfig.SharedLibraryEntry> libConfig
                    = systemConfig.getSharedLibraries();
            final int builtInLibCount = libConfig.size();
            for (int i = 0; i < builtInLibCount; i++) {
                mSharedLibraries.addBuiltInSharedLibraryLPw(libConfig.valueAt(i));
            }

            // Now that we have added all the libraries, iterate again to add dependency
            // information IFF their dependencies are added.
            long undefinedVersion = SharedLibraryInfo.VERSION_UNDEFINED;
            for (int i = 0; i < builtInLibCount; i++) {
                String name = libConfig.keyAt(i);
                SystemConfig.SharedLibraryEntry entry = libConfig.valueAt(i);
                final int dependencyCount = entry.dependencies.length;
                for (int j = 0; j < dependencyCount; j++) {
                    final SharedLibraryInfo dependency =
                        computer.getSharedLibraryInfo(entry.dependencies[j], undefinedVersion);
                    if (dependency != null) {
                        computer.getSharedLibraryInfo(name, undefinedVersion)
                                .addDependency(dependency);
                    }
                }
            }

            SELinuxMMAC.readInstallPolicy();

            t.traceBegin("loadFallbacks");
            FallbackCategoryProvider.loadFallbacks();
            t.traceEnd();

            t.traceBegin("read user settings");
            mFirstBoot = !mSettings.readLPw(computer,
                    mInjector.getUserManagerInternal().getUsers(/* excludeDying= */ false));
            t.traceEnd();

            if (mFirstBoot) {
                t.traceBegin("setFirstBoot: ");
                try {
                    mInstaller.setFirstBoot();
                } catch (InstallerException e) {
                    Slog.w(TAG, "Could not set First Boot: ", e);
                }
                t.traceEnd();
            }

            mPermissionManager.readLegacyPermissionsTEMP(mSettings.mPermissions);
            mPermissionManager.readLegacyPermissionStateTEMP();

            if (mFirstBoot) {
                DexOptHelper.requestCopyPreoptedFiles();
            }

            String customResolverActivityName = Resources.getSystem().getString(
                    R.string.config_customResolverActivity);
            if (!TextUtils.isEmpty(customResolverActivityName)) {
                mCustomResolverComponentName = ComponentName.unflattenFromString(
                        customResolverActivityName);
            }

            long startTime = SystemClock.uptimeMillis();

            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
                    startTime);

            final String bootClassPath = System.getenv("BOOTCLASSPATH");
            final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");

            if (bootClassPath == null) {
                Slog.w(TAG, "No BOOTCLASSPATH found!");
            }

            if (systemServerClassPath == null) {
                Slog.w(TAG, "No SYSTEMSERVERCLASSPATH found!");
            }

            final VersionInfo ver = mSettings.getInternalVersion();
            mIsUpgrade =
                    !partitionsFingerprint.equals(ver.fingerprint);
            if (mIsUpgrade) {
                PackageManagerServiceUtils.logCriticalInfo(Log.INFO,
                        "Upgrading from " + ver.fingerprint + " (" + ver.buildFingerprint + ") to "
                                + PackagePartitions.FINGERPRINT + " (" + Build.FINGERPRINT + ")");
            }
            mPriorSdkVersion = mIsUpgrade ? ver.sdkVersion : -1;
            mPriorSdkVersionFull = mIsUpgrade ? ver.sdkVersionFull : -1;
            mInitAppsHelper = new InitAppsHelper(this, mApexManager, mInstallPackageHelper,
                    mInjector.getSystemPartitions());

            // when upgrading from pre-M, promote system app permissions from install to runtime
            mPromoteSystemApps =
                    mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1;

            mIsPreNMR1Upgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N_MR1;
            mIsPreQUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.Q;

            final WatchedArrayMap<String, PackageSetting> packageSettings =
                mSettings.getPackagesLocked();

            if (isDeviceUpgrading()) {
                // Save the names of pre-existing packages prior to scanning, so we can determine
                // which system packages are completely new due to an upgrade.
                mExistingPackages = new ArraySet<>(packageSettings.size());
                for (int i = 0; i < packageSettings.size(); i++) {
                    mExistingPackages.add(packageSettings.valueAt(i).getPackageName());
                }

                // Triggering {@link com.android.server.pm.crossprofile.
                // CrossProfileIntentFilterHelper.updateDefaultCrossProfileIntentFilter} to update
                // {@link  CrossProfileIntentFilter}s between eligible users and their parent
                t.traceBegin("cross profile intent filter update");
                mInjector.getCrossProfileIntentFilterHelper()
                        .updateDefaultCrossProfileIntentFilter();
                t.traceEnd();
            }

            File cacheDir = PackageManagerServiceUtils.preparePackageParserCache(
                    mIsEngBuild, mIsUserDebugBuild, mIncrementalVersion);
            if (cacheDir == null) {
                // several places don't expect mCacheDir to be null, despite it being nullable
                // upstream
                cacheDir = new File("/dev/null");
            }
            mCacheDir = cacheDir;

            mInitialNonStoppedSystemPackages = mInjector.getSystemConfig()
                    .getInitialNonStoppedSystemPackages();
            mShouldStopSystemPackagesByDefault = mContext.getResources()
                    .getBoolean(R.bool.config_stopSystemPackagesByDefault);

            final int[] userIds = mUserManager.getUserIds();
            PackageParser2 packageParser = mInjector.getScanningCachingPackageParser();
            mOverlayConfig = mInitAppsHelper.initSystemApps(packageParser, packageSettings, userIds,
                    startTime);
            mInitAppsHelper.initNonSystemApps(packageParser, userIds, startTime);
            packageParser.close();

            mRequiredVerifierPackages = getRequiredButNotReallyRequiredVerifiersLPr(computer);
            mRequiredInstallerPackage = getRequiredInstallerLPr(computer);
            mRequiredUninstallerPackage = getRequiredUninstallerLPr(computer);

            // PermissionController hosts default permission granting and role management, so it's a
            // critical part of the core system.
            mRequiredPermissionControllerPackage = getRequiredPermissionControllerLPr(computer);

            // Resolve the storage manager.
            mStorageManagerPackage = getStorageManagerPackageName(computer);

            // Resolve protected action filters. Only the setup wizard is allowed to
            // have a high priority filter for these actions.
            mSetupWizardPackage = getSetupWizardPackageNameImpl(computer);
            mComponentResolver.fixProtectedFilterPriorities(mSetupWizardPackage);

            mDefaultTextClassifierPackage = ensureSystemPackageName(computer,
                    mContext.getString(R.string.config_servicesExtensionPackage));
            mSystemTextClassifierPackageName = ensureSystemPackageName(computer,
                    mContext.getString(R.string.config_defaultTextClassifierPackage));
            mConfiguratorPackage = ensureSystemPackageName(computer,
                    mContext.getString(R.string.config_deviceConfiguratorPackageName));
            mAppPredictionServicePackage = ensureSystemPackageName(computer,
                    getPackageFromComponentString(R.string.config_defaultAppPredictionService));
            mIncidentReportApproverPackage = ensureSystemPackageName(computer,
                    mContext.getString(R.string.config_incidentReportApproverPackage));
            mRetailDemoPackage = getRetailDemoPackageName();
            mOverlayConfigSignaturePackage = ensureSystemPackageName(computer,
                    mInjector.getSystemConfig().getOverlayConfigSignaturePackage());
            mRecentsPackage = ensureSystemPackageName(computer,
                    getPackageFromComponentString(R.string.config_recentsComponentName));
            mAmbientContextDetectionPackage = ensureSystemPackageName(computer,
                    getPackageFromComponentString(
                            R.string.config_defaultAmbientContextDetectionService));
            mWearableSensingPackage = ensureSystemPackageName(computer,
                    getPackageFromComponentString(
                            R.string.config_defaultWearableSensingService));

            // Now that we know all of the shared libraries, update all clients to have
            // the correct library paths.
            mSharedLibraries.updateAllSharedLibrariesLPw(
                    null, null, Collections.unmodifiableMap(mPackages));

            for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) {
                // NOTE: We ignore potential failures here during a system scan (like
                // the rest of the commands above) because there's precious little we
                // can do about it. A settings error is reported, though.
                final List<String> changedAbiCodePath =
                        ScanPackageUtils.applyAdjustedAbiToSharedUser(setting,
                                null /*scannedPackage*/,
                                mInjector.getAbiHelper().getAdjustedAbiForSharedUser(
                                        setting.getPackageStates(), null /*scannedPackage*/));
                // Adjust seInfo to ensure apps which share a sharedUserId are placed in the same
                // SELinux domain.
                setting.fixSeInfoLocked();
                setting.updateProcesses();
            }

            // Now that we know all the packages we are keeping,
            // read and update their last usage times.
            mPackageUsage.read(packageSettings);

            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,
                    SystemClock.uptimeMillis());
            Slog.i(TAG, "Time to scan packages: "
                    + ((SystemClock.uptimeMillis() - startTime) / 1000f)
                    + " seconds");

            // If the partitions fingerprint has changed since the last time we booted,
            // we need to re-grant app permission to catch any new ones that
            // appear.  This is really a hack, and means that apps can in some
            // cases get permissions that the user didn't initially explicitly
            // allow...  it would be nice to have some better way to handle
            // this situation.
            if (mIsUpgrade) {
                Slog.i(TAG, "Partitions fingerprint changed from " + ver.fingerprint + " to "
                        + PackagePartitions.FINGERPRINT
                        + "; regranting permissions for internal storage");
            }
            mPermissionManager.onStorageVolumeMounted(
                    StorageManager.UUID_PRIVATE_INTERNAL, mIsUpgrade);
            ver.sdkVersion = mSdkVersion;
            ver.sdkVersionFull = mSdkVersionFull;

            // If this is the first boot or an update from pre-M, then we need to initialize the
            // default preferred apps across all defined users.
            if (mPromoteSystemApps || mFirstBoot) {
                final List<UserInfo> users = mInjector.getUserManagerInternal().getUsers(true);
                for (int i = 0; i < users.size(); i++) {
                    mSettings.applyDefaultPreferredAppsLPw(users.get(i).id);

                }
            }

            // If this is first boot after an OTA, then we need to clear code cache directories.
            // Note that we do *not* clear the application profiles. These remain valid
            // across OTAs and are used to drive profile verification (post OTA) and
            // profile compilation (without waiting to collect a fresh set of profiles).
            if (mIsUpgrade) {
                Slog.i(TAG, "Build fingerprint changed; clearing code caches");
                for (int i = 0; i < packageSettings.size(); i++) {
                    final PackageSetting ps = packageSettings.valueAt(i);
                    if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, ps.getVolumeUuid())) {
                        // No apps are running this early, so no need to freeze
                        mAppDataHelper.clearAppDataLIF(ps.getPkg(), USER_ALL,
                                FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL
                                        | Installer.FLAG_CLEAR_CODE_CACHE_ONLY
                                        | Installer.FLAG_CLEAR_APP_DATA_KEEP_ART_PROFILES);
                    }
                }
                ver.buildFingerprint = Build.FINGERPRINT;
                ver.fingerprint = PackagePartitions.FINGERPRINT;
            }

            // Defer the app data fixup until we are done with app data clearing above.
            mPrepareAppDataFuture = mAppDataHelper.fixAppsDataOnBoot();

            // Legacy existing (installed before Q) non-system apps to hide
            // their icons in launcher.
            if (mIsPreQUpgrade) {
                Slog.i(TAG, "Allowlisting all existing apps to hide their icons");
                int size = packageSettings.size();
                for (int i = 0; i < size; i++) {
                    final PackageSetting ps = packageSettings.valueAt(i);
                    if ((ps.getFlags() & ApplicationInfo.FLAG_SYSTEM) != 0) {
                        continue;
                    }
                    ps.disableComponentLPw(PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME,
                            UserHandle.USER_SYSTEM);
                }
            }

            // clear only after permissions and other defaults have been updated
            mPromoteSystemApps = false;

            // All the changes are done during package scanning.
            ver.databaseVersion = Settings.CURRENT_DATABASE_VERSION;

            // can downgrade to reader
            t.traceBegin("write settings");
            final List<UserInfo> activeUsers = Settings.getActiveUsers(mUserManager);
            writeSettingsLPrTEMP(activeUsers);
            t.traceEnd();
            EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,
                    SystemClock.uptimeMillis());

            ComponentName intentFilterVerifierComponent =
                    getIntentFilterVerifierComponentNameLPr(computer);
            ComponentName domainVerificationAgent =
                    getDomainVerificationAgentComponentNameLPr(computer, UserHandle.USER_SYSTEM);

            DomainVerificationProxy domainVerificationProxy = DomainVerificationProxy.makeProxy(
                    intentFilterVerifierComponent, domainVerificationAgent, mContext,
                    mDomainVerificationManager, mDomainVerificationManager.getCollector(),
                    mDomainVerificationConnection);

            mDomainVerificationManager.setProxy(domainVerificationProxy);

            mServicesExtensionPackageName = getRequiredServicesExtensionPackageLPr(computer);
            mSharedSystemSharedLibraryPackageName = getRequiredSharedLibrary(computer,
                    PackageManager.SYSTEM_SHARED_LIBRARY_SHARED,
                    SharedLibraryInfo.VERSION_UNDEFINED);

            mSettings.setPermissionControllerVersion(
                    computer.getPackageInfo(mRequiredPermissionControllerPackage, 0,
                            UserHandle.USER_SYSTEM).getLongVersionCode());

            // Resolve the sdk sandbox package
            mRequiredSdkSandboxPackage = getRequiredSdkSandboxPackageName(computer);
            // Check that the developer verification service provider package specified in the
            // config can indeed be a verifier.
            mDeveloperVerificationServiceProvider =
                    getVerificationServiceProvider(computer, mContext.getString(
                            R.string.config_developerVerificationServiceProviderPackageName));
            // Remember the developer verification policy delegate which is allowed to change the
            // developer verification policy on behalf of the developer verification service
            // provider defined above.
            mDeveloperVerificationPolicyDelegatePackage =
                    getVerificationPolicyDelegate(computer, mContext.getString(
                            R.string.config_developerVerificationPolicyDelegatePackageName));

            // Initialize InstantAppRegistry's Instant App list for all users.
            forEachPackageState(computer, packageState -> {
                var pkg = packageState.getAndroidPackage();
                if (pkg == null || packageState.isSystem()) {
                    return;
                }
                for (int userId : userIds) {
                    if (!packageState.getUserStateOrDefault(userId).isInstantApp()
                            || !packageState.getUserStateOrDefault(userId).isInstalled()) {
                        continue;
                    }
                    mInstantAppRegistry.addInstantApp(userId, packageState.getAppId());
                }
            });

            mInstallerService = mInjector.getPackageInstallerService(
                    mDeveloperVerificationServiceProvider);
            final ComponentName instantAppResolverComponent = getInstantAppResolver(computer);
            if (instantAppResolverComponent != null) {
                if (DEBUG_INSTANT) {
                    Slog.d(TAG, "Set ephemeral resolver: " + instantAppResolverComponent);
                }
                mInstantAppResolverConnection =
                        mInjector.getInstantAppResolverConnection(instantAppResolverComponent);
                mInstantAppResolverSettingsComponent =
                        getInstantAppResolverSettingsLPr(computer,
                                instantAppResolverComponent);
            } else {
                mInstantAppResolverConnection = null;
                mInstantAppResolverSettingsComponent = null;
            }
            updateInstantAppInstallerLocked(null);

            // Read and update the usage of dex files.
            // Do this at the end of PM init so that all the packages have their
            // data directory reconciled.
            // At this point we know the code paths of the packages, so we can validate
            // the disk file and build the internal cache.
            // The usage file is expected to be small so loading and verifying it
            // should take a fairly small time compare to the other activities (e.g. package
            // scanning).
            final Map<Integer, List<PackageInfo>> userPackages = new HashMap<>();
            for (int userId : userIds) {
                userPackages.put(userId, computer.getInstalledPackages(/*flags*/ 0, userId)
                        .getList());
            }
            mDynamicCodeLogger.load(userPackages);
            if (mIsUpgrade) {
                FrameworkStatsLog.write(
                        FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
                        BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_INIT_TIME,
                        SystemClock.uptimeMillis() - startTime);
            }

            // If this is first boot or first boot after OTA then set the file path to the app
            // metadata files for preloaded packages.
            if (mFirstBoot || isDeviceUpgrading()) {
                ArrayMap<String, String> paths = systemConfig.getAppMetadataFilePaths();
                for (Map.Entry<String, String> entry : paths.entrySet()) {
                    String pkgName = entry.getKey();
                    String path = entry.getValue();
                    File file = new File(path);
                    if (!file.exists()) {
                        path = null;
                    }
                    PackageSetting disabledPkgSetting = mSettings.getDisabledSystemPkgLPr(pkgName);
                    if (disabledPkgSetting == null) {
                        PackageSetting pkgSetting = mSettings.getPackageLPr(pkgName);
                        if (pkgSetting != null) {
                            pkgSetting.setAppMetadataFilePath(path);
                            if (Flags.aslInApkAppMetadataSource()) {
                                pkgSetting.setAppMetadataSource(
                                        PackageManager.APP_METADATA_SOURCE_SYSTEM_IMAGE);
                            }
                        } else {
                            Slog.w(TAG, "Cannot set app metadata file for nonexistent package "
                                    + pkgName);
                        }
                    } else {
                        disabledPkgSetting.setAppMetadataFilePath(path);
                        if (Flags.aslInApkAppMetadataSource()) {
                            disabledPkgSetting.setAppMetadataSource(
                                    PackageManager.APP_METADATA_SOURCE_SYSTEM_IMAGE);
                        }
                    }
                }
            }

            // Rebuild the live computer since some attributes have been rebuilt.
            mLiveComputer = createLiveComputer();

        } // synchronized (mLock)
        } // synchronized (mInstallLock)
        // CHECKSTYLE:ON IndentationCheck

        mModuleInfoProvider = mInjector.getModuleInfoProvider();

        mInjector.getSystemWrapper().enablePackageCaches();

        // The initial scanning above does many calls into installd while
        // holding the mPackages lock, but we're mostly interested in yelling
        // once we have a booted system.
        mInstaller.setWarnIfHeld(mLock);

        ParsingPackageUtils.readConfigUseRoundIcon(mContext.getResources());

        mServiceStartWithDelay = SystemClock.uptimeMillis() + (60 * 1000L);

        Slog.i(TAG, "Fix for b/169414761 is applied");
    }

    @GuardedBy("mLock")
    void updateInstantAppInstallerLocked(String modifiedPackage) {
        // we're only interested in updating the installer application when 1) it's not
        // already set or 2) the modified package is the installer
        if (mInstantAppInstallerActivity != null
                && !mInstantAppInstallerActivity.getComponentName().getPackageName()
                        .equals(modifiedPackage)) {
            return;
        }
        setUpInstantAppInstallerActivityLP(getInstantAppInstallerLPr());
    }

    public boolean isFirstBoot() {
        // allow instant applications
        return mFirstBoot;
    }

    public boolean isDeviceUpgrading() {
        // allow instant applications
        // The system property allows testing ota flow when upgraded to the same image.
        return mIsUpgrade || SystemProperties.getBoolean(
                "persist.pm.mock-upgrade", false /* default */);
    }

    @NonNull
    private String[] getRequiredButNotReallyRequiredVerifiersLPr(@NonNull Computer computer) {
        final Intent intent = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);

        final List<ResolveInfo> matches =
                mResolveIntentHelper.queryIntentReceiversInternal(computer, intent,
                        PACKAGE_MIME_TYPE,
                        MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
                        UserHandle.USER_SYSTEM, Binder.getCallingUid());
        final int size = matches.size();
        if (size == 0) {
            Log.w(TAG, "There should probably be a verifier, but, none were found");
            return EmptyArray.STRING;
        } else if (size <= REQUIRED_VERIFIERS_MAX_COUNT) {
            String[] verifiers = new String[size];
            for (int i = 0; i < size; ++i) {
                verifiers[i] = matches.get(i).getComponentInfo().packageName;
                if (TextUtils.isEmpty(verifiers[i])) {
                    throw new RuntimeException("Invalid verifier: " + matches);
                }
            }
            return verifiers;
        }
        throw new RuntimeException(
                "There must be no more than " + REQUIRED_VERIFIERS_MAX_COUNT + " verifiers; found "
                        + matches);
    }

    @NonNull
    private String getRequiredSharedLibrary(@NonNull Computer snapshot, @NonNull String name,
            int version) {
        SharedLibraryInfo libraryInfo = snapshot.getSharedLibraryInfo(name, version);
        if (libraryInfo == null) {
            throw new IllegalStateException("Missing required shared library:" + name);
        }
        String packageName = libraryInfo.getPackageName();
        if (packageName == null) {
            throw new IllegalStateException("Expected a package for shared library " + name);
        }
        return packageName;
    }

    @NonNull
    private String getRequiredServicesExtensionPackageLPr(@NonNull Computer computer) {
        String configServicesExtensionPackage = mContext.getString(
                R.string.config_servicesExtensionPackage);
        if (TextUtils.isEmpty(configServicesExtensionPackage)) {
            throw new RuntimeException(
                    "Required services extension package failed due to "
                            + "config_servicesExtensionPackage is empty.");
        }
        String servicesExtensionPackage = ensureSystemPackageName(computer,
                configServicesExtensionPackage);
        if (TextUtils.isEmpty(servicesExtensionPackage)) {
            throw new RuntimeException(
                    "Required services extension package is missing, "
                            + "config_servicesExtensionPackage had defined with "
                            + configServicesExtensionPackage
                            + ", but can not find the package info on the system image, check if "
                            + "the package has a problem.");
        }
        return servicesExtensionPackage;
    }

    private @NonNull String getRequiredInstallerLPr(@NonNull Computer computer) {
        final Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
        intent.addCategory(Intent.CATEGORY_DEFAULT);
        intent.setDataAndType(Uri.parse("content://com.example/foo.apk"), PACKAGE_MIME_TYPE);

        final List<ResolveInfo> matches = computer.queryIntentActivitiesInternal(intent,
                PACKAGE_MIME_TYPE,
                MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
                UserHandle.USER_SYSTEM);
        if (matches.size() == 1) {
            ResolveInfo resolveInfo = matches.get(0);
            if (!resolveInfo.activityInfo.applicationInfo.isPrivilegedApp()) {
                throw new RuntimeException("The installer must be a privileged app");
            }
            return matches.get(0).getComponentInfo().packageName;
        } else {
            throw new RuntimeException("There must be exactly one installer; found " + matches);
        }
    }

    private @NonNull String getRequiredUninstallerLPr(@NonNull Computer computer) {
        final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
        intent.addCategory(Intent.CATEGORY_DEFAULT);
        intent.setData(Uri.fromParts(PACKAGE_SCHEME, "foo.bar", null));

        final ResolveInfo resolveInfo = mResolveIntentHelper.resolveIntentInternal(computer, intent,
                null, MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
                0 /*privateResolveFlags*/, UserHandle.USER_SYSTEM, false,
                Binder.getCallingUid(), Binder.getCallingPid());
        if (resolveInfo == null ||
                mResolveActivity.name.equals(resolveInfo.getComponentInfo().name)) {
            throw new RuntimeException("There must be exactly one uninstaller; found "
                    + resolveInfo);
        }
        return resolveInfo.getComponentInfo().packageName;
    }

    private @NonNull String getRequiredPermissionControllerLPr(@NonNull Computer computer) {
        final Intent intent = new Intent(Intent.ACTION_MANAGE_PERMISSIONS);
        intent.addCategory(Intent.CATEGORY_DEFAULT);

        final List<ResolveInfo> matches = computer.queryIntentActivitiesInternal(intent, null,
                MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
                UserHandle.USER_SYSTEM);
        if (matches.size() == 1) {
            ResolveInfo resolveInfo = matches.get(0);
            if (!resolveInfo.activityInfo.applicationInfo.isPrivilegedApp()) {
                throw new RuntimeException("The permissions manager must be a privileged app");
            }
            return matches.get(0).getComponentInfo().packageName;
        } else {
            throw new RuntimeException("There must be exactly one permissions manager; found "
                    + matches);
        }
    }

    @NonNull
    private ComponentName getIntentFilterVerifierComponentNameLPr(@NonNull Computer computer) {
        final Intent intent = new Intent(Intent.ACTION_INTENT_FILTER_NEEDS_VERIFICATION);

        final List<ResolveInfo> matches =
                mResolveIntentHelper.queryIntentReceiversInternal(computer, intent,
                        PACKAGE_MIME_TYPE,
                        MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
                        UserHandle.USER_SYSTEM, Binder.getCallingUid());
        ResolveInfo best = null;
        final int N = matches.size();
        for (int i = 0; i < N; i++) {
            final ResolveInfo cur = matches.get(i);
            final String packageName = cur.getComponentInfo().packageName;
            if (checkPermission(
                    android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT, packageName,
                    UserHandle.USER_SYSTEM) != PackageManager.PERMISSION_GRANTED) {
                continue;
            }

            if (best == null || cur.priority > best.priority) {
                best = cur;
            }
        }

        if (best != null) {
            return best.getComponentInfo().getComponentName();
        }
        Slog.w(TAG, "Intent filter verifier not found");
        return null;
    }

    @Nullable
    private ComponentName getDomainVerificationAgentComponentNameLPr(@NonNull Computer computer,
            int userId) {
        Intent intent = new Intent(Intent.ACTION_DOMAINS_NEED_VERIFICATION);
        List<ResolveInfo> matches =
                mResolveIntentHelper.queryIntentReceiversInternal(computer, intent, null,
                        MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
                        userId, Binder.getCallingUid());
        ResolveInfo best = null;
        final int N = matches.size();
        for (int i = 0; i < N; i++) {
            final ResolveInfo cur = matches.get(i);
            final String packageName = cur.getComponentInfo().packageName;
            if (checkPermission(
                    android.Manifest.permission.DOMAIN_VERIFICATION_AGENT, packageName,
                    userId) != PackageManager.PERMISSION_GRANTED) {
                Slog.w(TAG, "Domain verification agent found but does not hold permission: "
                        + packageName);
                continue;
            }

            if (best == null || cur.priority > best.priority) {
                if (computer.isComponentEffectivelyEnabled(cur.getComponentInfo(),
                        UserHandle.of(userId))) {
                    best = cur;
                } else {
                    Slog.w(TAG, "Domain verification agent found but not enabled");
                }
            }
        }

        if (best != null) {
            return best.getComponentInfo().getComponentName();
        }
        Slog.w(TAG, "Domain verification agent not found");
        return null;
    }

    @Nullable ComponentName getInstantAppResolver(@NonNull Computer snapshot) {
        final String[] packageArray =
                mContext.getResources().getStringArray(R.array.config_ephemeralResolverPackage);
        if (packageArray.length == 0 && !Build.IS_DEBUGGABLE) {
            if (DEBUG_INSTANT) {
                Slog.d(TAG, "Ephemeral resolver NOT found; empty package list");
            }
            return null;
        }

        final int callingUid = Binder.getCallingUid();
        final int resolveFlags =
                MATCH_DIRECT_BOOT_AWARE
                | MATCH_DIRECT_BOOT_UNAWARE
                | (!Build.IS_DEBUGGABLE ? MATCH_SYSTEM_ONLY : 0);
        final Intent resolverIntent = new Intent(Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE);
        List<ResolveInfo> resolvers = snapshot.queryIntentServicesInternal(resolverIntent, null,
                resolveFlags, UserHandle.USER_SYSTEM, callingUid, Process.INVALID_PID,
                /*includeInstantApps*/ false, /*resolveForStart*/ false);
        final int N = resolvers.size();
        if (N == 0) {
            if (DEBUG_INSTANT) {
                Slog.d(TAG, "Ephemeral resolver NOT found; no matching intent filters");
            }
            return null;
        }

        final Set<String> possiblePackages = new ArraySet<>(Arrays.asList(packageArray));
        for (int i = 0; i < N; i++) {
            final ResolveInfo info = resolvers.get(i);

            if (info.serviceInfo == null) {
                continue;
            }

            final String packageName = info.serviceInfo.packageName;
            if (!possiblePackages.contains(packageName) && !Build.IS_DEBUGGABLE) {
                if (DEBUG_INSTANT) {
                    Slog.d(TAG, "Ephemeral resolver not in allowed package list;"
                            + " pkg: " + packageName + ", info:" + info);
                }
                continue;
            }

            if (DEBUG_INSTANT) {
                Slog.v(TAG, "Ephemeral resolver found;"
                        + " pkg: " + packageName + ", info:" + info);
            }
            return new ComponentName(packageName, info.serviceInfo.name);
        }
        if (DEBUG_INSTANT) {
            Slog.v(TAG, "Ephemeral resolver NOT found");
        }
        return null;
    }

    @GuardedBy("mLock")
    private @Nullable ActivityInfo getInstantAppInstallerLPr() {
        String[] orderedActions = mIsEngBuild
                ? new String[]{
                        Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE + "_TEST",
                        Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE}
                : new String[]{
                        Intent.ACTION_INSTALL_INSTANT_APP_PACKAGE};

        final int resolveFlags =
                MATCH_DIRECT_BOOT_AWARE
                        | MATCH_DIRECT_BOOT_UNAWARE
                        | Intent.FLAG_IGNORE_EPHEMERAL
                        | (mIsEngBuild ? 0 : MATCH_SYSTEM_ONLY);
        final Computer computer = snapshotComputer();
        final Intent intent = new Intent();
        intent.addCategory(Intent.CATEGORY_DEFAULT);
        intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
        List<ResolveInfo> matches = null;
        for (String action : orderedActions) {
            intent.setAction(action);
            matches = computer.queryIntentActivitiesInternal(intent, PACKAGE_MIME_TYPE,
                    resolveFlags, UserHandle.USER_SYSTEM);
            if (matches.isEmpty()) {
                if (DEBUG_INSTANT) {
                    Slog.d(TAG, "Instant App installer not found with " + action);
                }
            } else {
                break;
            }
        }
        Iterator<ResolveInfo> iter = matches.iterator();
        while (iter.hasNext()) {
            final ResolveInfo rInfo = iter.next();
            if (checkPermission(
                    Manifest.permission.INSTALL_PACKAGES,
                    rInfo.activityInfo.packageName, 0) == PERMISSION_GRANTED || mIsEngBuild) {
                continue;
            }
            iter.remove();
        }
        if (matches.size() == 0) {
            return null;
        } else if (matches.size() == 1) {
            return (ActivityInfo) matches.get(0).getComponentInfo();
        } else {
            throw new RuntimeException(
                    "There must be at most one ephemeral installer; found " + matches);
        }
    }

    private @Nullable ComponentName getInstantAppResolverSettingsLPr(@NonNull Computer computer,
            @NonNull ComponentName resolver) {
        final Intent intent =  new Intent(Intent.ACTION_INSTANT_APP_RESOLVER_SETTINGS)
                .addCategory(Intent.CATEGORY_DEFAULT)
                .setPackage(resolver.getPackageName());
        final int resolveFlags = MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
        List<ResolveInfo> matches = computer.queryIntentActivitiesInternal(intent, null,
                resolveFlags, UserHandle.USER_SYSTEM);
        if (matches.isEmpty()) {
            return null;
        }
        return matches.get(0).getComponentInfo().getComponentName();
    }

    public PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags) {
        // Because this is accessed via the package manager service AIDL,
        // go through the permission manager service AIDL
        return mContext.getSystemService(PermissionManager.class)
                .getPermissionGroupInfo(groupName, flags);
    }

    /**
     * Blocking call to clear all cached app data above quota.
     */
    public void freeAllAppCacheAboveQuota(String volumeUuid) throws IOException {
        try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) {
            // To avoid refactoring Installer.freeCache() and InstalldNativeService.freeCache(),
            // Long.MAX_VALUE is passed as an argument which is used in neither of two methods
            // when FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES is set
            mInstaller.freeCache(volumeUuid, Long.MAX_VALUE, Installer.FLAG_FREE_CACHE_V2
                    | Installer.FLAG_FREE_CACHE_DEFY_TARGET_FREE_BYTES);
        } catch (InstallerException ignored) {
        }
    }

    /**
     * Blocking call to clear various types of cached data across the system
     * until the requested bytes are available.
     */
    public void freeStorage(String volumeUuid, long bytes,
            @StorageManager.AllocateFlags int flags) throws IOException {
        mFreeStorageHelper.freeStorage(volumeUuid, bytes, flags);
    }

    int freeCacheForInstallation(int recommendedInstallLocation, PackageLite pkgLite,
            String resolvedPath, String mPackageAbiOverride, int installFlags) {
        return mFreeStorageHelper.freeCacheForInstallation(recommendedInstallLocation, pkgLite,
                resolvedPath, mPackageAbiOverride, installFlags);
    }

    public ModuleInfo getModuleInfo(String packageName, @PackageManager.ModuleInfoFlags int flags) {
        return mModuleInfoProvider.getModuleInfo(packageName, flags);
    }

    void updateSequenceNumberLP(PackageSetting pkgSetting, int[] userList) {
        mChangedPackagesTracker.updateSequenceNumber(pkgSetting.getPackageName(), userList);
    }

    public boolean hasSystemFeature(String name, int version) {
        // allow instant applications
        final FeatureInfo feat = mAvailableFeatures.get(name);
        if (feat == null) {
            return false;
        } else {
            return feat.version >= version;
        }
    }

    // NOTE: Can't remove due to unsupported app usage
    public int checkPermission(String permName, String pkgName, int userId) {
        return mPermissionManager.checkPermission(pkgName, permName,
                VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, userId);
    }

    public String getSdkSandboxPackageName() {
        return mRequiredSdkSandboxPackage;
    }

    String getPackageInstallerPackageName() {
        return mRequiredInstallerPackage;
    }

    void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj,
            Intent origIntent, String resolvedType, String callingPackage,
            @Nullable String callingFeatureId, boolean isRequesterInstantApp,
            Bundle verificationBundle, int userId) {
        final Message msg = mHandler.obtainMessage(INSTANT_APP_RESOLUTION_PHASE_TWO,
                new InstantAppRequest(responseObj, origIntent, resolvedType,
                        callingPackage, callingFeatureId, isRequesterInstantApp, userId,
                        verificationBundle, false /*resolveForStart*/,
                        responseObj.hostDigestPrefixSecure, responseObj.token));
        mHandler.sendMessage(msg);
    }

    // findPreferredActivityBody returns two items: a "things changed" flag and a
    // ResolveInfo, which is the preferred activity itself.
    static class FindPreferredActivityBodyResult {
        boolean mChanged;
        ResolveInfo mPreferredResolveInfo;
    }

    public @NonNull ParceledListSlice<ResolveInfo> queryIntentReceivers(@NonNull Computer snapshot,
            Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
            @UserIdInt int userId) {
        return new ParceledListSlice<>(mResolveIntentHelper.queryIntentReceiversInternal(
                snapshot, intent, resolvedType, flags, userId, Binder.getCallingUid()));
    }

    public static void reportSettingsProblem(int priority, String msg) {
        logCriticalInfo(priority, msg);
    }

    // TODO:(b/135203078): Move to parsing
    static void renameStaticSharedLibraryPackage(ParsedPackage parsedPackage) {
        // Derive the new package synthetic package name
        parsedPackage.setPackageName(toStaticSharedLibraryPackageName(
                parsedPackage.getPackageName(), parsedPackage.getStaticSharedLibraryVersion()));
    }

    private static String toStaticSharedLibraryPackageName(
            String packageName, long libraryVersion) {
        return packageName + STATIC_SHARED_LIB_DELIMITER + libraryVersion;
    }

    public void performFstrimIfNeeded() {
        mFreeStorageHelper.performFstrimIfNeeded();
    }

    public void updatePackagesIfNeeded() {
        mDexOptHelper.performPackageDexOptUpgradeIfNeeded();
    }

    public void updateMetricsIfNeeded() {
        final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
        if (displayManager != null) {
            final Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
            if (display != null) {
                display.getMetrics(mMetrics);
            }
        }
    }

    private void notifyPackageUseInternal(String packageName, int reason) {
        long time = System.currentTimeMillis();
        synchronized (mLock) {
            final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
            if (pkgSetting == null) {
                return;
            }
            pkgSetting.getPkgState().setLastPackageUsageTimeInMills(reason, time);
        }
    }

    /*package*/ DexOptHelper getDexOptHelper() {
        return mDexOptHelper;
    }

    /*package*/ DynamicCodeLogger getDynamicCodeLogger() {
        return mDynamicCodeLogger;
    }

    public void shutdown() {
        mDynamicCodeLogger.writeNow();
        if (!refactorCrashrecovery()) {
            CrashRecoveryAdaptor.packageWatchdogWriteNow(mContext);
        }

        synchronized (mLock) {
            mPackageUsage.writeNow(mSettings.getPackagesLocked());

            if (mHandler.hasMessages(WRITE_SETTINGS)
                    || mBackgroundHandler.hasMessages(WRITE_DIRTY_PACKAGE_RESTRICTIONS)
                    || mHandler.hasMessages(WRITE_PACKAGE_LIST)) {
                writeSettings(/*sync=*/true);
            }
        }
    }

    @NonNull
    int[] resolveUserIds(@CanBeALL @UserIdInt int userId) {
        return (userId == USER_ALL) ? mUserManager.getUserIds() : new int[]{userId};
    }

    private void setUpInstantAppInstallerActivityLP(ActivityInfo installerActivity) {
        if (installerActivity == null) {
            if (DEBUG_INSTANT) {
                Slog.d(TAG, "Clear ephemeral installer activity");
            }
            mInstantAppInstallerActivity = null;
            onChanged();
            return;
        }

        if (DEBUG_INSTANT) {
            Slog.d(TAG, "Set ephemeral installer activity: "
                    + installerActivity.getComponentName());
        }
        // Set up information for ephemeral installer activity
        mInstantAppInstallerActivity = installerActivity;
        mInstantAppInstallerActivity.flags |= ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS
                | ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS;
        mInstantAppInstallerActivity.exported = true;
        mInstantAppInstallerActivity.enabled = true;
        mInstantAppInstallerInfo.activityInfo = mInstantAppInstallerActivity;
        mInstantAppInstallerInfo.priority = 1;
        mInstantAppInstallerInfo.preferredOrder = 1;
        mInstantAppInstallerInfo.isDefault = true;
        mInstantAppInstallerInfo.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
                | IntentFilter.MATCH_ADJUSTMENT_NORMAL;
        onChanged();
    }

    void killApplication(String pkgName, @AppIdInt int appId, String reason, int exitInfoReason) {
        killApplication(pkgName, appId, USER_ALL, reason, exitInfoReason);
    }

    void killApplication(String pkgName, @AppIdInt int appId,
            @CanBeALL @UserIdInt int userId, String reason, int exitInfoReason) {
        // Request the ActivityManager to kill the process(only for existing packages)
        // so that we do not end up in a confused state while the user is still using the older
        // version of the application while the new one gets installed.
        final long token = Binder.clearCallingIdentity();
        try {
            IActivityManager am = ActivityManager.getService();
            if (am != null) {
                try {
                    am.killApplication(pkgName, appId, userId, reason, exitInfoReason);
                } catch (RemoteException e) {
                }
            }
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    void killApplicationSync(String pkgName, @AppIdInt int appId,
            @CanBeALL @UserIdInt int userId, String reason, int exitInfoReason) {
        ActivityManagerInternal mAmi = LocalServices.getService(ActivityManagerInternal.class);
        if (Thread.holdsLock(mLock) || mAmi == null) {
            // holds PM's lock, go back killApplication to avoid it run into watchdog reset.
            Slog.e(TAG, "Holds PM's lock, unable kill application synchronized");
            killApplication(pkgName, appId, userId, reason, exitInfoReason);
        } else {
            KillAppBlocker blocker = new KillAppBlocker();
            try {
                blocker.register();
                mAmi.killApplicationSync(pkgName, appId, userId, reason, exitInfoReason);
                blocker.waitAppProcessGone(mAmi, snapshotComputer(), mUserManager, pkgName);
            } finally {
                blocker.unregister();
            }
        }
    }

    @Override
    public void notifyPackageAdded(String packageName, int uid) {
        mPackageObserverHelper.notifyAdded(packageName, uid);
    }

    @Override
    public void notifyPackageChanged(String packageName, int uid) {
        mPackageObserverHelper.notifyChanged(packageName, uid);
    }

    @Override
    public void notifyPackageRemoved(String packageName, int uid) {
        mPackageObserverHelper.notifyRemoved(packageName, uid);
        UserPackage.removeFromCache(UserHandle.getUserId(uid), packageName);
    }

    boolean isUserRestricted(int userId, String restrictionKey) {
        Bundle restrictions = mUserManager.getUserRestrictions(userId);
        if (restrictions.getBoolean(restrictionKey, false)) {
            Log.w(TAG, "User is restricted: " + restrictionKey);
            return true;
        }
        return false;
    }

    private void enforceCanSetPackagesSuspendedAsUser(@NonNull Computer snapshot,
            boolean quarantined, UserPackage suspender, int callingUid, int targetUserId,
            String callingMethod) {
        if (callingUid == Process.ROOT_UID
                // Need to compare app-id to allow system dialogs access on secondary users
                || UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) {
            return;
        }

        final String ownerPackage =
                mProtectedPackages.getDeviceOwnerOrProfileOwnerPackage(targetUserId);
        if (ownerPackage != null) {
            final int ownerUid = snapshot.getPackageUid(ownerPackage, 0, targetUserId);
            if (ownerUid == callingUid) {
                return;
            }
        }

        if (quarantined) {
            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.QUARANTINE_APPS,
                    callingMethod);
        } else {
            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS,
                    callingMethod);
        }

        if (crossUserSuspensionEnabledRo()) {
            final int suspendingPackageUid =
                    snapshot.getPackageUid(suspender.packageName, 0, suspender.userId);
            if (suspendingPackageUid != callingUid) {
                throw new SecurityException("Suspender package %s doesn't match calling uid %d"
                                .formatted(suspender.packageName, callingUid));
            }
            if (targetUserId != suspender.userId) {
                mContext.enforceCallingOrSelfPermission(
                        Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingMethod);
            }
        } else {
            // Here only SHELL can suspend across users
            final int packageUid =
                    snapshot.getPackageUid(suspender.packageName, 0, targetUserId);
            final boolean allowedPackageUid = packageUid == callingUid;
            final boolean allowedShell = callingUid == SHELL_UID
                    && UserHandle.isSameApp(packageUid, callingUid);

            if (!allowedShell && !allowedPackageUid) {
                throw new SecurityException("Suspending package " + suspender.packageName
                        + " in user " + targetUserId + " does not belong to calling uid "
                        + callingUid);
            }
        }
    }

    /**
     * @param suspendingUserId The user that has suspended apps using the suspending package.
     * @param targetUserId The user whose apps should be unsuspended. Pass {@code USER_ALL} to
     *                     unsuspend for all users.
     */
    void unsuspendForSuspendingPackage(@NonNull Computer computer, String suspendingPackage,
            @UserIdInt int suspendingUserId, @CanBeALL @UserIdInt int targetUserId) {
        // TODO: This can be replaced by a special parameter to iterate all packages, rather than
        //  this weird pre-collect of all packages.
        final String[] allPackages = computer.getPackageStates().keySet().toArray(new String[0]);
        final Predicate<UserPackage> suspenderPredicate =
                UserPackage.of(suspendingUserId, suspendingPackage)::equals;
        if (!crossUserSuspensionEnabledRo() || targetUserId != USER_ALL) {
            mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(computer,
                    allPackages, suspenderPredicate, targetUserId);
        } else {
            for (int user : mUserManager.getUserIds()) {
                mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(
                        computer, allPackages, suspenderPredicate, user);
            }
        }
    }

    void removeAllDistractingPackageRestrictions(@NonNull Computer snapshot, int userId) {
        final String[] allPackages = snapshot.getAllAvailablePackageNames();
        mDistractingPackageHelper.removeDistractingPackageRestrictions(snapshot, allPackages,
                userId);
    }

    private void enforceCanSetDistractingPackageRestrictionsAsUser(int callingUid, int userId,
            String callingMethod) {
        mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS,
                callingMethod);

        if (!PackageManagerServiceUtils.isSystemOrRoot(callingUid)
                && UserHandle.getUserId(callingUid) != userId) {
            throw new SecurityException("Calling uid " + callingUid + " cannot call for user "
                    + userId);
        }
    }

    void setEnableRollbackCode(int token, int enableRollbackCode) {
        final Message msg = mHandler.obtainMessage(ENABLE_ROLLBACK_STATUS);
        msg.arg1 = token;
        msg.arg2 = enableRollbackCode;
        mHandler.sendMessage(msg);
    }

    /**
     * Callback from PackageSettings whenever an app is first transitioned out of the
     * 'stopped' state.  Normally we just issue the broadcast, but we can't do that if
     * the app was "launched" for a restoreAtInstall operation.  Therefore we check
     * here whether the app is the target of an ongoing install, and only send the
     * broadcast immediately if it is not in that state.  If it *is* undergoing a restore,
     * the first-launch broadcast will be sent implicitly on that basis in POST_INSTALL
     * handling.
     */
    void notifyFirstLaunch(final String packageName, final String installerPackage,
            final int userId) {
        // Serialize this with the rest of the install-process message chain.  In the
        // restore-at-install case, this Runnable will necessarily run before the
        // POST_INSTALL message is processed, so the contents of mRunningInstalls
        // are coherent.  In the non-restore case, the app has already completed install
        // and been launched through some other means, so it is not in a problematic
        // state for observers to see the FIRST_LAUNCH signal.
        mHandler.post(() -> {
            synchronized (mRunningInstalls) {
                for (int i = 0; i < mRunningInstalls.size(); i++) {
                    final InstallRequest installRequest = mRunningInstalls.valueAt(i);
                    if (installRequest.getReturnCode() != PackageManager.INSTALL_SUCCEEDED) {
                        continue;
                    }
                    final int[] newUsers = installRequest.getNewUsers();
                    if (packageName.equals(installRequest.getPkg().getPackageName())) {
                        // right package; but is it for the right user?
                        for (int uIndex = 0; uIndex < newUsers.length; uIndex++) {
                            if (userId == newUsers[uIndex]) {
                                if (DEBUG_BACKUP) {
                                    Slog.i(TAG, "Package " + packageName
                                            + " being restored so deferring FIRST_LAUNCH");
                                }
                                return;
                            }
                        }
                    }
                }
            }
            // didn't find it, so not being restored
            if (DEBUG_BACKUP) {
                Slog.i(TAG, "Package " + packageName + " sending normal FIRST_LAUNCH");
            }
            final boolean isInstantApp = snapshotComputer().isInstantAppInternal(
                    packageName, userId, Process.SYSTEM_UID);
            final int[] userIds = isInstantApp ? EMPTY_INT_ARRAY : new int[] { userId };
            final int[] instantUserIds = isInstantApp ? new int[] { userId } : EMPTY_INT_ARRAY;
            mBroadcastHelper.sendFirstLaunchBroadcast(
                    packageName, installerPackage, userIds, instantUserIds);
        });
    }

    @SuppressWarnings("GuardedBy")
    VersionInfo getSettingsVersionForPackage(AndroidPackage pkg) {
        if (pkg.isExternalStorage()) {
            if (TextUtils.isEmpty(pkg.getVolumeUuid())) {
                return mSettings.getExternalVersion();
            } else {
                return mSettings.findOrCreateVersion(pkg.getVolumeUuid());
            }
        } else {
            return mSettings.getInternalVersion();
        }
    }

    public void deleteExistingPackageAsUser(VersionedPackage versionedPackage,
            final IPackageDeleteObserver2 observer, final int userId) {
        mDeletePackageHelper.deleteExistingPackageAsUser(
                versionedPackage, observer, userId);
    }

    public void deletePackageVersioned(VersionedPackage versionedPackage,
            final IPackageDeleteObserver2 observer, final int userId, final int deleteFlags,
            final int callingUid) {
        mDeletePackageHelper.deletePackageVersionedInternal(
                versionedPackage, observer, userId, deleteFlags, callingUid,
                /* allowSilentUninstall= */ false);
    }

    public void deletePackageVersioned(VersionedPackage versionedPackage,
            final IPackageDeleteObserver2 observer, final int userId, final int deleteFlags) {
        mDeletePackageHelper.deletePackageVersionedInternal(
                versionedPackage, observer, userId, deleteFlags, /* allowSilentUninstall= */ false);
    }

    boolean isCallerVerifier(@NonNull Computer snapshot, int callingUid) {
        final int callingUserId = UserHandle.getUserId(callingUid);
        for (String requiredVerifierPackage : mRequiredVerifierPackages) {
            if (callingUid == snapshot.getPackageUid(requiredVerifierPackage, 0, callingUserId)) {
                return true;
            }
        }
        return false;
    }

    public boolean isPackageDeviceAdminOnAnyUser(@NonNull Computer snapshot, String packageName) {
        final int callingUid = Binder.getCallingUid();
        if (snapshot.checkUidPermission(android.Manifest.permission.MANAGE_USERS, callingUid)
                != PERMISSION_GRANTED) {
            EventLog.writeEvent(0x534e4554, "128599183", -1, "");
            throw new SecurityException(android.Manifest.permission.MANAGE_USERS
                    + " permission is required to call this API");
        }
        if (snapshot.getInstantAppPackageName(callingUid) != null
                && !snapshot.isCallerSameApp(packageName, callingUid)) {
            return false;
        }
        return isPackageDeviceAdmin(packageName, USER_ALL);
    }

    // TODO(b/261957226): centralise this logic in DPM
    boolean isPackageDeviceAdmin(String packageName, @CanBeALL @UserIdInt int userId) {
        final IDevicePolicyManager dpm = getDevicePolicyManager();
        final DevicePolicyManagerInternal dpmi =
                mInjector.getLocalService(DevicePolicyManagerInternal.class);
        try {
            if (dpm != null && dpmi != null) {
                final ComponentName deviceOwnerComponentName = dpm.getDeviceOwnerComponent(
                        /* callingUserOnly =*/ false);
                final String deviceOwnerPackageName = deviceOwnerComponentName == null ? null
                        : deviceOwnerComponentName.getPackageName();
                // Does the package contains the device owner?
                // TODO Do we have to do it even if userId != UserHandle.USER_ALL?  Otherwise,
                // this check is probably not needed, since DO should be registered as a device
                // admin on some user too. (Original bug for this: b/17657954)
                if (packageName.equals(deviceOwnerPackageName)) {
                    return true;
                }
                // Does it contain a device admin for any user?
                int[] allUsers = mUserManager.getUserIds();
                int[] targetUsers;
                if (userId == USER_ALL) {
                    targetUsers = allUsers;
                } else {
                    targetUsers = new int[]{userId};
                }

                for (int i = 0; i < targetUsers.length; ++i) {
                    if (dpm.packageHasActiveAdmins(packageName, targetUsers[i])) {
                        return true;
                    }
                }

                // If a package is DMRH on a managed user, it should also be treated as an admin on
                // that user. If that package is also a system package, it should also be protected
                // on other users otherwise "uninstall updates" on an unmanaged user may break
                // management on other users because apk version is shared between all users.
                var packageState = snapshotComputer().getPackageStateInternal(packageName);
                if (packageState == null) {
                    return false;
                }
                for (int user : packageState.isSystem() ? allUsers : targetUsers) {
                    if (isDeviceManagementRoleHolder(packageName, user)
                            && dpmi.isUserOrganizationManaged(user)) {
                        return true;
                    }
                }
            }
        } catch (RemoteException e) {
        }
        return false;
    }

    private boolean isDeviceManagementRoleHolder(String packageName, int userId) {
        return Objects.equals(packageName, getDevicePolicyManagementRoleHolderPackageName(userId));
    }

    @Nullable
    public String getDevicePolicyManagementRoleHolderPackageName(int userId) {
        return Binder.withCleanCallingIdentity(() -> {
            RoleManager roleManager = mContext.getSystemService(RoleManager.class);
            List<String> roleHolders =
                    roleManager.getRoleHoldersAsUser(
                            RoleManager.ROLE_DEVICE_POLICY_MANAGEMENT, UserHandle.of(userId));
            if (roleHolders.isEmpty()) {
                return null;
            }
            return roleHolders.get(0);
        });
    }

    /** Returns the device policy manager interface. */
    private IDevicePolicyManager getDevicePolicyManager() {
        if (mDevicePolicyManager == null) {
            // No need to synchronize; worst-case scenario it will be fetched twice.
            mDevicePolicyManager = IDevicePolicyManager.Stub.asInterface(
                            ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
        }
        return mDevicePolicyManager;
    }

    private boolean clearApplicationUserDataLIF(@NonNull Computer snapshot, String packageName,
            int userId) {
        if (packageName == null) {
            Slog.w(TAG, "Attempt to delete null packageName.");
            return false;
        }

        // Try finding details about the requested package
        AndroidPackage pkg = snapshot.getPackage(packageName);
        if (pkg == null) {
            Slog.w(TAG, "Package named '" + packageName + "' doesn't exist.");
            return false;
        }
        mPermissionManager.resetRuntimePermissions(pkg, userId);

        mAppDataHelper.clearAppDataLIF(pkg, userId,
                FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL);

        final int appId = UserHandle.getAppId(pkg.getUid());
        mAppDataHelper.clearKeystoreData(userId, appId);

        UserManagerInternal umInternal = mInjector.getUserManagerInternal();
        StorageManagerInternal smInternal = mInjector.getLocalService(StorageManagerInternal.class);
        final int flags;
        if (StorageManager.isCeStorageUnlocked(userId) && smInternal.isCeStoragePrepared(userId)) {
            flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE;
        } else if (umInternal.isUserRunning(userId)) {
            flags = StorageManager.FLAG_STORAGE_DE;
        } else {
            flags = 0;
        }
        mAppDataHelper.prepareAppDataContentsLIF(pkg, snapshot.getPackageStateInternal(packageName),
                userId, flags);

        return true;
    }

    /**
     * Update component enabled settings to {@link PackageManager#COMPONENT_ENABLED_STATE_DEFAULT}
     * if the resetEnabledSettingsOnAppDataCleared is {@code true}.
     */
    @GuardedBy("mLock")
    private void resetComponentEnabledSettingsIfNeededLPw(String packageName, int userId,
            int callingUid) {
        final AndroidPackage pkg = packageName != null ? mPackages.get(packageName) : null;
        if (pkg == null || !pkg.isResetEnabledSettingsOnAppDataCleared()) {
            return;
        }
        final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
        if (pkgSetting == null) {
            return;
        }
        final ArrayList<String> updatedComponents = new ArrayList<>();
        final Consumer<? super ParsedMainComponent> resetSettings = (component) -> {
            if (pkgSetting.restoreComponentLPw(component.getClassName(), userId)) {
                updatedComponents.add(component.getClassName());
            }
        };
        for (int i = 0; i < pkg.getActivities().size(); i++) {
            resetSettings.accept(pkg.getActivities().get(i));
        }
        for (int i = 0; i < pkg.getReceivers().size(); i++) {
            resetSettings.accept(pkg.getReceivers().get(i));
        }
        for (int i = 0; i < pkg.getServices().size(); i++) {
            resetSettings.accept(pkg.getServices().get(i));
        }
        for (int i = 0; i < pkg.getProviders().size(); i++) {
            resetSettings.accept(pkg.getProviders().get(i));
        }
        if (ArrayUtils.isEmpty(updatedComponents)) {
            // nothing changed
            return;
        }

        updateSequenceNumberLP(pkgSetting, new int[] { userId });
        updateInstantAppInstallerLocked(packageName);
        scheduleWritePackageRestrictions(userId);

        mPendingBroadcasts.addComponents(userId, packageName, updatedComponents);
        if (!mHandler.hasMessages(SEND_PENDING_BROADCAST)) {
            mHandler.sendMessageDelayed(
                    mHandler.obtainMessage(SEND_PENDING_BROADCAST, callingUid, 0 /* arg2 */,
                            PackageMetrics.STRING_RESET_COMPONENT_STATE_CHANGED), BROADCAST_DELAY);
        }
    }

    /** This method takes a specific user id as well as UserHandle.USER_ALL. */
    @GuardedBy("mLock")
    void clearPackagePreferredActivitiesLPw(String packageName,
            @NonNull SparseBooleanArray outUserChanged, @CanBeALL @UserIdInt int userId) {
        mSettings.clearPackagePreferredActivities(packageName, outUserChanged, userId);
    }

    void restorePermissionsAndUpdateRolesForNewUserInstall(String packageName,
            @UserIdInt int userId) {
        // We may also need to apply pending (restored) runtime permission grants
        // within these users.
        mPermissionManager.restoreDelayedRuntimePermissions(packageName, userId);

        // Restore default browser setting if it is now installed.
        String defaultBrowser;
        synchronized (mLock) {
            defaultBrowser = mSettings.getPendingDefaultBrowserLPr(userId);
        }
        if (Objects.equals(packageName, defaultBrowser)) {
            mDefaultAppProvider.setDefaultBrowser(packageName, userId);
            synchronized (mLock) {
                mSettings.removePendingDefaultBrowserLPw(userId);
            }
        }

        // Persistent preferred activity might have came into effect due to this
        // install.
        mPreferredActivityHelper.updateDefaultHomeNotLocked(snapshotComputer(), userId);
    }

    /**
     * Variant that takes a {@link WatchedIntentFilter}
     */
    public void addCrossProfileIntentFilter(@NonNull Computer snapshot,
            WatchedIntentFilter intentFilter, String ownerPackage, int sourceUserId,
            int targetUserId, int flags) {
        mContext.enforceCallingOrSelfPermission(
                        android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
        int callingUid = Binder.getCallingUid();
        enforceOwnerRights(snapshot, ownerPackage, callingUid);

        // Verifying that current calling uid should be able to add {@link CrossProfileIntentFilter}
        // for source and target user
        mUserManager.enforceCrossProfileIntentFilterAccess(sourceUserId, targetUserId, callingUid,
                /* addCrossProfileIntentFilter */ true);

        PackageManagerServiceUtils.enforceShellRestriction(mInjector.getUserManagerInternal(),
                UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId);
        if (!intentFilter.checkDataPathAndSchemeSpecificParts()) {
            EventLog.writeEvent(0x534e4554, "246749936", callingUid);
            throw new IllegalArgumentException("Invalid intent data paths or scheme specific parts"
                    + " in the filter.");
        }
        if (intentFilter.countActions() == 0) {
            Slog.w(TAG, "Cannot set a crossProfile intent filter with no filter actions");
            return;
        }
        synchronized (mLock) {
            CrossProfileIntentFilter newFilter = new CrossProfileIntentFilter(intentFilter,
                    ownerPackage, targetUserId, flags, mUserManager
                    .getCrossProfileIntentFilterAccessControl(sourceUserId, targetUserId));
            CrossProfileIntentResolver resolver =
                    mSettings.editCrossProfileIntentResolverLPw(sourceUserId);
            ArrayList<CrossProfileIntentFilter> existing = resolver.findFilters(intentFilter);
            // We have all those whose filter is equal. Now checking if the rest is equal as well.
            if (existing != null) {
                int size = existing.size();
                for (int i = 0; i < size; i++) {
                    if (newFilter.equalsIgnoreFilter(existing.get(i))) {
                        return;
                    }
                }
            }
            resolver.addFilter(snapshotComputer(), newFilter);
        }
        scheduleWritePackageRestrictions(sourceUserId);
    }


    // Enforcing that callingUid is owning pkg on userId
    private void enforceOwnerRights(@NonNull Computer snapshot, String pkg, int callingUid) {
        // The system owns everything.
        if (UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) {
            return;
        }
        final String[] callerPackageNames = snapshot.getPackagesForUid(callingUid);
        if (!ArrayUtils.contains(callerPackageNames, pkg)) {
            throw new SecurityException("Calling uid " + callingUid
                    + " does not own package " + pkg);
        }
        final int callingUserId = UserHandle.getUserId(callingUid);
        PackageInfo pi = snapshot.getPackageInfo(pkg, 0, callingUserId);
        if (pi == null) {
            throw new IllegalArgumentException("Unknown package " + pkg + " on user "
                    + callingUserId);
        }
    }

    public void sendSessionCommitBroadcast(PackageInstaller.SessionInfo sessionInfo, int userId) {
        mBroadcastHelper.sendSessionCommitBroadcast(snapshotComputer(), sessionInfo, userId,
                mAppPredictionServicePackage);
    }

    private @Nullable String getSetupWizardPackageNameImpl(@NonNull Computer computer) {
        final Intent intent = new Intent(Intent.ACTION_MAIN);
        intent.addCategory(Intent.CATEGORY_SETUP_WIZARD);

        final List<ResolveInfo> matches = computer.queryIntentActivitiesInternal(intent, null,
                MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE
                        | MATCH_DISABLED_COMPONENTS,
                UserHandle.myUserId());
        if (matches.size() == 1) {
            return matches.get(0).getComponentInfo().packageName;
        } else {
            Slog.e(TAG, "There should probably be exactly one setup wizard; found " + matches.size()
                    + ": matches=" + matches);
            return null;
        }
    }

    private @Nullable String getStorageManagerPackageName(@NonNull Computer computer) {
        final Intent intent = new Intent(StorageManager.ACTION_MANAGE_STORAGE);

        final List<ResolveInfo> matches = computer.queryIntentActivitiesInternal(intent, null,
                MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE
                        | MATCH_DISABLED_COMPONENTS,
                UserHandle.myUserId());
        if (matches.size() == 1) {
            return matches.get(0).getComponentInfo().packageName;
        } else {
            Slog.w(TAG, "There should probably be exactly one storage manager; found "
                    + matches.size() + ": matches=" + matches);
            return null;
        }
    }

    @NonNull
    private static String getRequiredSdkSandboxPackageName(@NonNull Computer computer) {
        final Intent intent = new Intent(SdkSandboxManagerLocal.SERVICE_INTERFACE);

        final List<ResolveInfo> matches = computer.queryIntentServicesInternal(
                intent,
                /* resolvedType= */ null,
                MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
                UserHandle.USER_SYSTEM,
                /* callingUid= */ Process.myUid(),
                Process.INVALID_PID,
                /* includeInstantApps= */ false,
                /* resolveForStart */ false);
        if (matches.size() == 1) {
            return matches.get(0).getComponentInfo().packageName;
        } else {
            throw new RuntimeException("There should exactly one sdk sandbox package; found "
                    + matches.size() + ": matches=" + matches);
        }
    }

    @Nullable
    private static ComponentName getVerificationServiceProvider(@NonNull Computer computer,
            @Nullable String packageName) {
        if (TextUtils.isEmpty(packageName)) {
            return null;
        }
        final Intent intent = new Intent(PackageManager.ACTION_VERIFY_DEVELOPER);
        intent.setPackage(packageName);

        final List<ResolveInfo> matches = computer.queryIntentServicesInternal(
                intent,
                /* resolvedType= */ null,
                MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE
                        | MATCH_DISABLED_COMPONENTS,
                UserHandle.USER_SYSTEM,
                /* callingUid= */ Process.myUid(),
                Process.INVALID_PID,
                /* includeInstantApps= */ false,
                /* resolveForStart */ false);
        if (matches.isEmpty()) {
            // The package name specified in sysconfig doesn't qualify as a verifier
            return null;
        }
        // There should only be one match since the intent specified the package name
        ResolveInfo ri = matches.getFirst();
        if (ri.getComponentInfo() == null || ri.getComponentInfo().applicationInfo == null) {
            return null;
        }
        ApplicationInfo applicationInfo = ri.getComponentInfo().applicationInfo;
        if (computer.checkUidPermission(
                android.Manifest.permission.DEVELOPER_VERIFICATION_AGENT, applicationInfo.uid)
                != PackageManager.PERMISSION_GRANTED) {
            return null;
        }
        return ri.getComponentInfo().getComponentName();
    }

    @Nullable
    private String getVerificationPolicyDelegate(@NonNull Computer computer,
            @Nullable String packageName) {
        if (TextUtils.isEmpty(packageName)) {
            return null;
        }
        // It should be a system and privileged app installed on the device
        final PackageStateInternal psi = computer.getPackageStateInternal(
                packageName, Process.SYSTEM_UID);
        if (psi != null && psi.isSystem() && psi.isPrivileged()) {
            return packageName;
        }
        return null;
    }

    @Nullable
    private String getRetailDemoPackageName() {
        final String predefinedPkgName = mContext.getString(R.string.config_retailDemoPackage);
        final String predefinedSignature = mContext.getString(
                R.string.config_retailDemoPackageSignature);

        if (TextUtils.isEmpty(predefinedPkgName) || TextUtils.isEmpty(predefinedSignature)) {
            return null;
        }

        final AndroidPackage androidPkg = mPackages.get(predefinedPkgName);
        if (androidPkg != null) {
            final SigningDetails signingDetail = androidPkg.getSigningDetails();
            if (signingDetail != null && signingDetail.getSignatures() != null) {
                try {
                    final MessageDigest msgDigest = MessageDigest.getInstance("SHA-256");
                    for (Signature signature : signingDetail.getSignatures()) {
                        if (TextUtils.equals(predefinedSignature,
                                HexEncoding.encodeToString(msgDigest.digest(
                                        signature.toByteArray()), false))) {
                            return predefinedPkgName;
                        }
                    }
                } catch (NoSuchAlgorithmException e) {
                    Slog.e(
                            TAG,
                            "Unable to verify signatures as getting the retail demo package name",
                            e);
                }
            }
        }

        return null;
    }

    @Nullable
    String getPackageFromComponentString(@StringRes int stringResId) {
        final String componentString = mContext.getString(stringResId);
        if (TextUtils.isEmpty(componentString)) {
            return null;
        }
        final ComponentName component = ComponentName.unflattenFromString(componentString);
        if (component == null) {
            return null;
        }
        return component.getPackageName();
    }

    @Nullable
    String ensureSystemPackageName(@NonNull Computer snapshot,
            @Nullable String packageName) {
        if (packageName == null) {
            return null;
        }
        final long token = Binder.clearCallingIdentity();
        try {
            if (snapshot.getPackageInfo(packageName, MATCH_FACTORY_ONLY,
                    UserHandle.USER_SYSTEM) == null) {
                PackageInfo packageInfo =
                        snapshot.getPackageInfo(packageName, 0, UserHandle.USER_SYSTEM);
                if (packageInfo != null) {
                    EventLog.writeEvent(0x534e4554, "145981139", packageInfo.applicationInfo.uid,
                            "");
                }
                Log.w(TAG, "Missing required system package: " + packageName + (packageInfo != null
                        ? ", but found with extended search." : "."));
                return null;
            }
        } finally {
            Binder.restoreCallingIdentity(token);
        }
        return packageName;
    }

    @VisibleForTesting(visibility = Visibility.PRIVATE)
    public void updateComponentLabelIcon(/*@NonNull*/ ComponentName componentName,
            @Nullable String nonLocalizedLabel, @Nullable Integer icon, int userId) {
        if (componentName == null) {
            throw new IllegalArgumentException("Must specify a component");
        }

        int callingUid = Binder.getCallingUid();
        String componentPkgName = componentName.getPackageName();

        Computer computer = snapshotComputer();

        int componentUid = computer.getPackageUid(componentPkgName, 0, userId);
        if (!UserHandle.isSameApp(callingUid, componentUid)) {
            throw new SecurityException("The calling UID (" + callingUid + ")"
                    + " does not match the target UID");
        }

        String allowedCallerPkg =
                mContext.getString(R.string.config_overrideComponentUiPackage);
        if (TextUtils.isEmpty(allowedCallerPkg)) {
            throw new SecurityException( "There is no package defined as allowed to change a "
                    + "component's label or icon");
        }

        int allowedCallerUid = computer.getPackageUid(allowedCallerPkg,
                PackageManager.MATCH_SYSTEM_ONLY, userId);
        if (allowedCallerUid == -1 || !UserHandle.isSameApp(callingUid, allowedCallerUid)) {
            throw new SecurityException("The calling UID (" + callingUid + ")"
                    + " is not allowed to change a component's label or icon");
        }
        PackageStateInternal packageState = computer.getPackageStateInternal(componentPkgName);
        if (packageState == null || packageState.getPkg() == null
                || (!packageState.isSystem()
                && !packageState.isUpdatedSystemApp())) {
            throw new SecurityException(
                    "Changing the label is not allowed for " + componentName);
        }

        if (!computer.getComponentResolver().componentExists(componentName)) {
            throw new IllegalArgumentException("Component " + componentName + " not found");
        }

        Pair<String, Integer> overrideLabelIcon = packageState.getUserStateOrDefault(userId)
                .getOverrideLabelIconForComponent(componentName);

        String existingLabel = overrideLabelIcon == null ? null : overrideLabelIcon.first;
        Integer existingIcon = overrideLabelIcon == null ? null : overrideLabelIcon.second;

        if (TextUtils.equals(existingLabel, nonLocalizedLabel)
                && Objects.equals(existingIcon, icon)) {
            // Nothing changed
            return;
        }

        commitPackageStateMutation(null, componentPkgName,
                state -> state.userState(userId)
                        .setComponentLabelIcon(componentName, nonLocalizedLabel, icon));

        mPendingBroadcasts.addComponent(userId, componentPkgName, componentName.getClassName());

        if (!mHandler.hasMessages(SEND_PENDING_BROADCAST)) {
            mHandler.sendMessageDelayed(
                    mHandler.obtainMessage(SEND_PENDING_BROADCAST, callingUid, 0 /* arg2 */,
                            PackageMetrics.STRING_COMPONENT_LABEL_ICON_CHANGED), BROADCAST_DELAY);
        }
    }

    private void setEnabledSettings(List<ComponentEnabledSetting> settings, int userId,
            @NonNull String callingPackage) {
        final int callingUid = Binder.getCallingUid();
        // TODO: This method is not properly snapshotified beyond this call
        final Computer preLockSnapshot = snapshotComputer();
        preLockSnapshot.enforceCrossUserPermission(callingUid, userId,
                false /* requireFullPermission */, true /* checkShell */, "set enabled");

        final int targetSize = settings.size();
        for (int i = 0; i < targetSize; i++) {
            final int newState = settings.get(i).getEnabledState();
            if (!(newState == COMPONENT_ENABLED_STATE_DEFAULT
                    || newState == COMPONENT_ENABLED_STATE_ENABLED
                    || newState == COMPONENT_ENABLED_STATE_DISABLED
                    || newState == COMPONENT_ENABLED_STATE_DISABLED_USER
                    || newState == COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED)) {
                throw new IllegalArgumentException("Invalid new component state: " + newState);
            }
        }
        if (targetSize > 1) {
            final ArraySet<String> checkDuplicatedPackage = new ArraySet<>();
            final ArraySet<ComponentName> checkDuplicatedComponent = new ArraySet<>();
            final ArrayMap<String, Integer> checkConflictFlag = new ArrayMap<>();
            for (int i = 0; i < targetSize; i++) {
                final ComponentEnabledSetting setting = settings.get(i);
                final String packageName = setting.getPackageName();
                if (setting.isComponent()) {
                    final ComponentName componentName = setting.getComponentName();
                    if (checkDuplicatedComponent.contains(componentName)) {
                        throw new IllegalArgumentException("The component " + componentName
                                + " is duplicated");
                    }
                    checkDuplicatedComponent.add(componentName);

                    // check if there is a conflict of the DONT_KILL_APP flag between components
                    // in the package
                    final Integer enabledFlags = checkConflictFlag.get(packageName);
                    if (enabledFlags == null) {
                        checkConflictFlag.put(packageName, setting.getEnabledFlags());
                    } else if ((enabledFlags & PackageManager.DONT_KILL_APP)
                            != (setting.getEnabledFlags() & PackageManager.DONT_KILL_APP)) {
                        throw new IllegalArgumentException("A conflict of the DONT_KILL_APP flag "
                                + "between components in the package " + packageName);
                    }
                } else {
                    if (checkDuplicatedPackage.contains(packageName)) {
                        throw new IllegalArgumentException("The package " + packageName
                                + " is duplicated");
                    }
                    checkDuplicatedPackage.add(packageName);
                }
            }
        }

        final boolean allowedByPermission = mContext.checkCallingOrSelfPermission(
                android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE) == PERMISSION_GRANTED;
        final boolean[] updateAllowed = new boolean[targetSize];
        Arrays.fill(updateAllowed, true);

        final Map<String, PackageSetting> pkgSettings = new ArrayMap<>(targetSize);
        // reader
        synchronized (mLock) {
            final Computer snapshot = snapshotComputer();
            // Checks for target packages
            for (int i = 0; i < targetSize; i++) {
                final ComponentEnabledSetting setting = settings.get(i);
                final String packageName = setting.getPackageName();
                if (pkgSettings.containsKey(packageName)) {
                    // this package has verified
                    continue;
                }
                final boolean isCallerTargetApp = ArrayUtils.contains(
                        snapshot.getPackagesForUid(callingUid), packageName);
                final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
                // Limit who can change which apps
                if (!isCallerTargetApp && !allowedByPermission) {
                    // Don't allow apps that don't have permission to modify other apps
                    throw new SecurityException("Attempt to change component state; "
                            + "pid=" + Binder.getCallingPid()
                            + ", uid=" + callingUid
                            + (!setting.isComponent() ? ", package=" + packageName
                            : ", component=" + setting.getComponentName()));
                }
                if (pkgSetting == null || snapshot.shouldFilterApplicationIncludingUninstalled(
                        pkgSetting, callingUid, userId)) {
                    throw new IllegalArgumentException(setting.isComponent()
                            ? "Unknown component: " + setting.getComponentName()
                            : "Unknown package: " + packageName);
                }
                // Don't allow changing protected packages.
                if (!isCallerTargetApp
                        && mProtectedPackages.isPackageStateProtected(userId, packageName)) {
                    throw new SecurityException(
                            "Cannot disable a protected package: " + packageName);
                }
                if (callingUid == Process.SHELL_UID
                        && (pkgSetting.getFlags() & ApplicationInfo.FLAG_TEST_ONLY) == 0) {
                    // Shell can only change whole packages between ENABLED and DISABLED_USER states
                    // unless it is a test package.
                    final int oldState = pkgSetting.getEnabled(userId);
                    final int newState = setting.getEnabledState();
                    if (!setting.isComponent()
                            &&
                            (oldState == COMPONENT_ENABLED_STATE_DISABLED_USER
                                    || oldState == COMPONENT_ENABLED_STATE_DEFAULT
                                    || oldState == COMPONENT_ENABLED_STATE_ENABLED)
                            &&
                            (newState == COMPONENT_ENABLED_STATE_DISABLED_USER
                                    || newState == COMPONENT_ENABLED_STATE_DEFAULT
                                    || newState == COMPONENT_ENABLED_STATE_ENABLED)) {
                        // ok
                    } else {
                        throw new SecurityException(
                                "Shell cannot change component state for "
                                        + setting.getComponentName() + " to " + newState);
                    }
                }
                pkgSettings.put(packageName, pkgSetting);
            }
            // Checks for target components
            for (int i = 0; i < targetSize; i++) {
                final ComponentEnabledSetting setting = settings.get(i);
                // skip if it's application
                if (!setting.isComponent()) continue;

                // Only allow apps with CHANGE_COMPONENT_ENABLED_STATE permission to change hidden
                // app details activity
                final String packageName = setting.getPackageName();
                final String className = setting.getClassName();
                if (!allowedByPermission
                        && PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME.equals(className)) {
                    throw new SecurityException("Cannot disable a system-generated component");
                }
                // Verify that this is a valid class name.
                final AndroidPackage pkg = pkgSettings.get(packageName).getPkg();
                if (pkg == null || !AndroidPackageUtils.hasComponentClassName(pkg, className)) {
                    if (pkg != null
                            && pkg.getTargetSdkVersion() >= Build.VERSION_CODES.JELLY_BEAN
                            && (setting.getEnabledFlags() & PackageManager.SKIP_IF_MISSING) == 0) {
                        throw new IllegalArgumentException("Component class " + className
                                + " does not exist in " + packageName);
                    } else {
                        Slog.w(TAG, "Failed setComponentEnabledSetting: component class "
                                + className + " does not exist in " + packageName);
                        updateAllowed[i] = false;
                    }
                }
            }
        }

        // More work for application enabled setting updates
        for (int i = 0; i < targetSize; i++) {
            final ComponentEnabledSetting setting = settings.get(i);
            // skip if it's component
            if (setting.isComponent()) continue;

            final PackageSetting pkgSetting = pkgSettings.get(setting.getPackageName());
            final int newState = setting.getEnabledState();
            synchronized (mLock) {
                if (pkgSetting.getEnabled(userId) == newState) {
                    // Nothing to do
                    updateAllowed[i] = false;
                    continue;
                }
            }
            // If we're enabling a system stub, there's a little more work to do.
            // Prior to enabling the package, we need to decompress the APK(s) to the
            // data partition and then replace the version on the system partition.
            final AndroidPackage deletedPkg = pkgSetting.getPkg();
            final boolean isSystemStub = (deletedPkg != null)
                    && deletedPkg.isStub()
                    && pkgSetting.isSystem();
            if (isSystemStub
                    && (newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT
                    || newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED)) {
                if (!enableCompressedPackage(deletedPkg, pkgSetting)) {
                    Slog.w(TAG, "Failed setApplicationEnabledSetting: failed to enable "
                            + "commpressed package " + setting.getPackageName());
                    updateAllowed[i] = false;
                }
            }
        }

        // packageName -> list of components to send broadcasts now
        final ArrayMap<String, ArrayList<String>> sendNowBroadcasts = new ArrayMap<>(targetSize);
        final IntArray changedComponentIndices = new IntArray();
        boolean scheduleBroadcastMessage = false;
        boolean isSynchronous = false;

        Computer computer = snapshotComputer();
        boolean anyChanged = false;
        synchronized (mLock) {
            for (int i = 0; i < targetSize; i++) {
                if (!updateAllowed[i]) {
                    continue;
                }
                // update enabled settings
                final ComponentEnabledSetting setting = settings.get(i);
                final String packageName = setting.getPackageName();
                final PackageSetting packageSetting = pkgSettings.get(packageName);
                if (!setEnabledSettingInternalLocked(computer, packageSetting, setting, userId,
                        callingPackage)) {
                    continue;
                }

                anyChanged = true;
                changedComponentIndices.add(i);

                if ((setting.getEnabledFlags() & PackageManager.SYNCHRONOUS) != 0) {
                    isSynchronous = true;
                }
                // collect broadcast list for the package
                final String componentName = setting.isComponent()
                        ? setting.getClassName() : packageName;
                if ((setting.getEnabledFlags() & PackageManager.DONT_KILL_APP) == 0) {
                    ArrayList<String> componentList = sendNowBroadcasts.get(packageName);
                    componentList = componentList == null ? new ArrayList<>() : componentList;
                    if (!componentList.contains(componentName)) {
                        componentList.add(componentName);
                    }
                    sendNowBroadcasts.put(packageName, componentList);
                    // Purge entry from pending broadcast list if another one exists already
                    // since we are sending one right away.
                    mPendingBroadcasts.remove(userId, packageName);
                } else {
                    mPendingBroadcasts.addComponent(userId, packageName, componentName);
                    Trace.instant(Trace.TRACE_TAG_PACKAGE_MANAGER, "setEnabledSetting broadcast: "
                            + componentName + ": " + setting.getEnabledState());
                    scheduleBroadcastMessage = true;
                }
            }
            if (!anyChanged) {
                // nothing changed, return immediately
                return;
            }
        }
        if (isSynchronous) {
            synchronized (mLock) {
                flushPackageRestrictionsAsUserInternalLocked(userId);
            }
        } else {
            scheduleWritePackageRestrictions(userId);
        }
        if (scheduleBroadcastMessage) {
            if (!mHandler.hasMessages(SEND_PENDING_BROADCAST)) {
                // Schedule a message - if it has been a "reasonably long time" since the
                // service started, send the broadcast with a delay of one second to avoid
                // delayed reactions from the receiver, else keep the default ten second delay
                // to avoid extreme thrashing on service startup.
                final long broadcastDelay = SystemClock.uptimeMillis() > mServiceStartWithDelay
                        ? BROADCAST_DELAY
                        : BROADCAST_DELAY_DURING_STARTUP;
                mHandler.sendMessageDelayed(
                        mHandler.obtainMessage(SEND_PENDING_BROADCAST, callingUid,
                                0 /* arg2 */, PackageMetrics.STRING_COMPONENT_STATE_CHANGED),
                        broadcastDelay);
            }
        }

        final List<PackageMetrics.ComponentStateMetrics> componentStateMetricsList =
                new ArrayList<PackageMetrics.ComponentStateMetrics>();
        final int listSize = changedComponentIndices.size();
        for (int i = 0; i < listSize; i++) {
            final int index = changedComponentIndices.get(i);
            final ComponentEnabledSetting setting = settings.get(index);
            final String packageName = setting.getPackageName();
            final PackageStateInternal psi = computer.getPackageStateInternal(packageName);
            // Gets component state from the old snapshot which was taken before the state change,
            // because the metrics want to use the old state
            final int componentOldState =
                    setting.isComponent() ? computer.getComponentEnabledSettingInternal(
                            setting.getComponentName(), callingUid, userId)
                            : psi.getUserStateOrDefault(userId).getEnabledState();
            final PackageMetrics.ComponentStateMetrics componentStateMetrics =
                    new PackageMetrics.ComponentStateMetrics(setting,
                            UserHandle.getUid(userId, psi.getAppId()),
                            componentOldState, callingUid);
            componentStateMetricsList.add(componentStateMetrics);
        }

        // Log the metrics when the component state is changed.
        PackageMetrics.reportComponentStateChanged(snapshotComputer(), componentStateMetricsList,
                userId);

        final long callingId = Binder.clearCallingIdentity();
        try {
            final Computer newSnapshot = snapshotComputer();
            for (int i = 0; i < sendNowBroadcasts.size(); i++) {
                final String packageName = sendNowBroadcasts.keyAt(i);
                final ArrayList<String> components = sendNowBroadcasts.valueAt(i);
                final int packageUid = UserHandle.getUid(
                        userId, pkgSettings.get(packageName).getAppId());
                mBroadcastHelper.sendPackageChangedBroadcast(newSnapshot, packageName,
                        false /* dontKillApp */, components, packageUid, null /* reason */,
                        PackageMetrics.STRING_COMPONENT_STATE_CHANGED, callingUid);
            }
        } finally {
            Binder.restoreCallingIdentity(callingId);
        }
    }

    @GuardedBy("mLock")
    private boolean setEnabledSettingInternalLocked(@NonNull Computer computer,
            PackageSetting pkgSetting, ComponentEnabledSetting setting, @UserIdInt int userId,
            String callingPackage) {
        final int newState = setting.getEnabledState();
        final String packageName = setting.getPackageName();
        boolean success = false;
        if (!setting.isComponent()) {
            // We're dealing with an application/package level state change
            pkgSetting.setEnabled(newState, userId, callingPackage);
            if ((newState == COMPONENT_ENABLED_STATE_DISABLED_USER
                    || newState == COMPONENT_ENABLED_STATE_DISABLED)
                    && checkPermission(Manifest.permission.SUSPEND_APPS, packageName, userId)
                    == PERMISSION_GRANTED) {
                // This app should not generally be allowed to get disabled by the UI, but
                // if it ever does, we don't want to end up with some of the user's apps
                // permanently suspended.
                unsuspendForSuspendingPackage(computer, packageName, userId, USER_ALL);
                removeAllDistractingPackageRestrictions(computer, userId);
            }
            success = true;
        } else {
            // We're dealing with a component level state change
            final String className = setting.getClassName();
            switch (newState) {
                case COMPONENT_ENABLED_STATE_ENABLED:
                    success = pkgSetting.enableComponentLPw(className, userId);
                    break;
                case COMPONENT_ENABLED_STATE_DISABLED:
                    success = pkgSetting.disableComponentLPw(className, userId);
                    break;
                case COMPONENT_ENABLED_STATE_DEFAULT:
                    success = pkgSetting.restoreComponentLPw(className, userId);
                    break;
                default:
                    Slog.e(TAG, "Failed setComponentEnabledSetting: component "
                            + packageName + "/" + className
                            + " requested an invalid new component state: " + newState);
                    break;
            }
        }
        if (!success) {
            return false;
        }

        updateSequenceNumberLP(pkgSetting, new int[] { userId });
        final long callingId = Binder.clearCallingIdentity();
        try {
            updateInstantAppInstallerLocked(packageName);
        } finally {
            Binder.restoreCallingIdentity(callingId);
        }

        return true;
    }

    @GuardedBy("mLock")
    private void flushPackageRestrictionsAsUserInternalLocked(int userId) {
        // NOTE: this invokes synchronous disk access, so callers using this
        // method should consider running on a background thread
        mSettings.writePackageRestrictionsLPr(userId);
        synchronized (mDirtyUsers) {
            mDirtyUsers.remove(userId);
            if (mDirtyUsers.isEmpty()) {
                mBackgroundHandler.removeMessages(WRITE_DIRTY_PACKAGE_RESTRICTIONS);
            }
        }
    }

    /**
     * Used by SystemServer
     */
    public void waitForAppDataPrepared() {
        if (mPrepareAppDataFuture == null) {
            return;
        }
        ConcurrentUtils.waitForFutureNoInterrupt(mPrepareAppDataFuture, "wait for prepareAppData");
        mPrepareAppDataFuture = null;
    }

    public void systemReady() {
        PackageManagerServiceUtils.enforceSystemOrRoot(
                "Only the system can claim the system is ready");

        final ContentResolver resolver = mContext.getContentResolver();
        if (mReleaseOnSystemReady != null) {
            for (int i = mReleaseOnSystemReady.size() - 1; i >= 0; --i) {
                final File dstCodePath = mReleaseOnSystemReady.get(i);
                F2fsUtils.releaseCompressedBlocks(resolver, dstCodePath);
            }
            mReleaseOnSystemReady = null;
        }
        mSystemReady = true;
        ContentObserver co = new ContentObserver(mHandler) {
            @Override
            public void onChange(boolean selfChange) {
                final boolean ephemeralFeatureDisabled =
                        Global.getInt(resolver, Global.ENABLE_EPHEMERAL_FEATURE, 1) == 0;
                for (int userId : UserManagerService.getInstance().getUserIds()) {
                    final boolean instantAppsDisabledForUser =
                            ephemeralFeatureDisabled || Secure.getIntForUser(resolver,
                                    Secure.INSTANT_APPS_ENABLED, 1, userId) == 0;
                    mWebInstantAppsDisabled.put(userId, instantAppsDisabledForUser);
                }
            }
        };
        mContext.getContentResolver().registerContentObserver(android.provider.Settings.Global
                        .getUriFor(Global.ENABLE_EPHEMERAL_FEATURE),
                false, co, USER_ALL);
        mContext.getContentResolver().registerContentObserver(android.provider.Settings.Secure
                .getUriFor(Secure.INSTANT_APPS_ENABLED), false, co, USER_ALL);
        co.onChange(true);

        mAppsFilter.onSystemReady(LocalServices.getService(PackageManagerInternal.class));

        // Disable any carrier apps. We do this very early in boot to prevent the apps from being
        // disabled after already being started.
        CarrierAppUtils.disableCarrierAppsUntilPrivileged(
                mContext.getOpPackageName(), UserHandle.USER_SYSTEM, mContext);

        // Read the compatibilty setting when the system is ready.
        boolean compatibilityModeEnabled = android.provider.Settings.Global.getInt(
                mContext.getContentResolver(),
                android.provider.Settings.Global.COMPATIBILITY_MODE, 1) == 1;
        ParsingPackageUtils.setCompatibilityModeEnabled(compatibilityModeEnabled);

        if (DEBUG_SETTINGS) {
            Log.d(TAG, "compatibility mode:" + compatibilityModeEnabled);
        }

        synchronized (mLock) {
            ArrayList<Integer> changed = mSettings.systemReady(mComponentResolver);
            for (int i = 0; i < changed.size(); i++) {
                mSettings.writePackageRestrictionsLPr(changed.get(i));
            }
        }

        mUserManager.systemReady();

        // Watch for external volumes that come and go over time
        final StorageManager storage = mInjector.getSystemService(StorageManager.class);
        storage.registerListener(mStorageEventHelper);

        mInstallerService.systemReady();

        // Now that we're mostly running, clean up stale users and apps
        mUserManager.reconcileUsers(StorageManager.UUID_PRIVATE_INTERNAL);
        mStorageEventHelper.reconcileApps(snapshotComputer(), StorageManager.UUID_PRIVATE_INTERNAL);

        mPermissionManager.onSystemReady();

        int[] grantPermissionsUserIds = EMPTY_INT_ARRAY;
        final List<UserInfo> livingUsers = mInjector.getUserManagerInternal().getUsers(
                /* excludeDying= */ true);
        final int livingUserCount = livingUsers.size();
        for (int i = 0; i < livingUserCount; i++) {
            final int userId = livingUsers.get(i).id;
            final boolean isPermissionUpgradeNeeded = !Objects.equals(
                    mPermissionManager.getDefaultPermissionGrantFingerprint(userId),
                    Build.FINGERPRINT);
            if (isPermissionUpgradeNeeded) {
                grantPermissionsUserIds = ArrayUtils.appendInt(
                        grantPermissionsUserIds, userId);
            }
        }
        // If we upgraded grant all default permissions before kicking off.
        for (int userId : grantPermissionsUserIds) {
            mLegacyPermissionManager.grantDefaultPermissions(userId);
            mPermissionManager.setDefaultPermissionGrantFingerprint(Build.FINGERPRINT, userId);
        }
        if (grantPermissionsUserIds == EMPTY_INT_ARRAY) {
            // If we did not grant default permissions, we preload from this the
            // default permission exceptions lazily to ensure we don't hit the
            // disk on a new user creation.
            mLegacyPermissionManager.scheduleReadDefaultPermissionExceptions();
        }

        if (mInstantAppResolverConnection != null) {
            mContext.registerReceiver(new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    mInstantAppResolverConnection.optimisticBind();
                    mContext.unregisterReceiver(this);
                }
            }, new IntentFilter(Intent.ACTION_BOOT_COMPLETED));
        }

        IntentFilter overlayFilter = new IntentFilter(Intent.ACTION_OVERLAY_CHANGED);
        overlayFilter.addDataScheme("package");
        mContext.registerReceiver(new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                if (intent == null) {
                    return;
                }
                final String action = intent.getAction();
                if (action == null) {
                    return;
                }
                Uri data = intent.getData();
                if (data == null) {
                    return;
                }
                String packageName = data.getSchemeSpecificPart();
                if (packageName == null) {
                    return;
                }
                final Computer snapshot = snapshotComputer();
                AndroidPackage pkg = snapshot.getPackage(packageName);
                if (pkg == null) {
                    return;
                }
                mBroadcastHelper.sendPackageChangedBroadcast(snapshot, pkg.getPackageName(),
                        true /* dontKillApp */,
                        new ArrayList<>(Collections.singletonList(pkg.getPackageName())),
                        pkg.getUid(),
                        Intent.ACTION_OVERLAY_CHANGED, PackageMetrics.STRING_OVERLAY_CHANGED,
                        Process.SYSTEM_UID);
            }
        }, overlayFilter);

        mModuleInfoProvider.systemReady();

        // Installer service might attempt to install some packages that have been staged for
        // installation on reboot. Make sure this is the last component to be call since the
        // installation might require other components to be ready.
        mInstallerService.restoreAndApplyStagedSessionIfNeeded();

        mExistingPackages = null;

        // Clear cache on flags changes.
        DeviceConfig.addOnPropertiesChangedListener(
                NAMESPACE_PACKAGE_MANAGER_SERVICE, mInjector.getBackgroundExecutor(),
                properties -> {
                    final Set<String> keyset = properties.getKeyset();
                    if (keyset.contains(PROPERTY_INCFS_DEFAULT_TIMEOUTS) || keyset.contains(
                            PROPERTY_KNOWN_DIGESTERS_LIST)) {
                        mPerUidReadTimeoutsCache = null;
                    }
                });

        // Prune unused static shared libraries which have been cached a period of time
        schedulePruneUnusedStaticSharedLibraries(false /* delay */);

        DexUseManagerLocal dexUseManager = DexOptHelper.getDexUseManagerLocal();
        if (dexUseManager != null) {
            dexUseManager.systemReady();
        }

        GosPackageStatePmHooks.init(this);

        PackageMetrics.logInvalidationMetrics();
    }

    public PackageFreezer freezePackage(String packageName, @CanBeALL @UserIdInt int userId,
            String killReason, int exitInfoReason, InstallRequest request) {
        return freezePackage(packageName, userId, killReason, exitInfoReason, request,
                /* waitAppKilled= */ false);
    }

    private PackageFreezer freezePackage(String packageName, @CanBeALL @UserIdInt int userId,
            String killReason, int exitInfoReason, InstallRequest request, boolean waitAppKilled) {
        return new PackageFreezer(packageName, userId, killReason, this, exitInfoReason, request,
                waitAppKilled);
    }

    public PackageFreezer freezePackageForDelete(String packageName, int userId, int deleteFlags,
            String killReason, int exitInfoReason) {
        if ((deleteFlags & PackageManager.DELETE_DONT_KILL_APP) != 0) {
            return new PackageFreezer(this, null /* request */);
        } else {
            return freezePackage(packageName, userId, killReason, exitInfoReason,
                    null /* request */);
        }
    }

    /** Called by UserManagerService */
    void cleanUpUser(UserManagerService userManager, @UserIdInt int userId) {
        final List<UserInfo> activeUsers = Settings.getActiveUsers(userManager);
        synchronized (mLock) {
            synchronized (mDirtyUsers) {
                mDirtyUsers.remove(userId);
            }
            mUserNeedsBadging.delete(userId);
            mDeletePackageHelper.removeUnusedPackagesLPw(userManager, userId);
            mSettings.removeUserLPw(activeUsers, userId);
            mPendingBroadcasts.remove(userId);
            mAppsFilter.onUserDeleted(snapshotComputer(), userId);
            mPermissionManager.onUserRemoved(userId);
            mInstallerService.onUserRemoved(userId);
        }
        mInstantAppRegistry.onUserRemoved(userId);
        mPackageMonitorCallbackHelper.onUserRemoved(userId);
        if (crossUserSuspensionEnabledRo()) {
            cleanUpCrossUserSuspension(userId);
        }
    }

    private void cleanUpCrossUserSuspension(int removedUser) {
        final Computer computer = snapshotComputer();
        var allPackages = computer.getAllAvailablePackageNames();
        for (int targetUserId : mUserManager.getUserIds()) {
            if (targetUserId == removedUser) continue;
            mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(computer, allPackages,
                    userPackage -> userPackage.userId == removedUser, targetUserId);
        }
    }

    /**
     * Called by UserManagerService.
     *
     * @param userTypeInstallablePackages system packages that should be initially installed for
     *                                    this type of user, or {@code null} if all system packages
     *                                    should be installed
     * @param disallowedPackages packages that should not be initially installed. Takes precedence
     *                           over installablePackages.
     */
    void createNewUser(int userId, @Nullable Set<String> userTypeInstallablePackages,
            String[] disallowedPackages) {
        try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) {
            mSettings.createNewUserLI(this, mInstaller, userId,
                    userTypeInstallablePackages, disallowedPackages);
        }
        synchronized (mLock) {
            scheduleWritePackageRestrictions(userId);
            scheduleWritePackageListLocked(userId);
            mAppsFilter.onUserCreated(snapshotComputer(), userId);
        }
    }

    void onNewUserCreated(@UserIdInt int userId, boolean convertedFromPreCreated) {
        if (DEBUG_PERMISSIONS) {
            Slog.d(TAG, "onNewUserCreated(id=" + userId
                    + ", convertedFromPreCreated=" + convertedFromPreCreated + ")");
        }
        if (!convertedFromPreCreated || !readPermissionStateForUser(userId)) {
            mPermissionManager.onUserCreated(userId);
            mLegacyPermissionManager.grantDefaultPermissions(userId);
            mPermissionManager.setDefaultPermissionGrantFingerprint(Build.FINGERPRINT, userId);
            mDomainVerificationManager.clearUser(userId);
            mInstallerService.onUserAdded(userId);
        }
    }

    private boolean readPermissionStateForUser(@UserIdInt int userId) {
        synchronized (mLock) {
            mPermissionManager.writeLegacyPermissionStateTEMP();
            mSettings.readPermissionStateForUserSyncLPr(userId);
            mPermissionManager.readLegacyPermissionStateTEMP();
            final boolean isPermissionUpgradeNeeded = !Objects.equals(
                    mPermissionManager.getDefaultPermissionGrantFingerprint(userId),
                    Build.FINGERPRINT);
            return isPermissionUpgradeNeeded;
        }
    }

    public boolean isStorageLow() {
        // allow instant applications
        final long token = Binder.clearCallingIdentity();
        try {
            final DeviceStorageMonitorInternal
                    dsm = mInjector.getLocalService(DeviceStorageMonitorInternal.class);
            if (dsm != null) {
                return dsm.isMemoryLow();
            } else {
                return false;
            }
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    private void deletePackageIfUnused(@NonNull Computer snapshot, final String packageName) {
        PackageStateInternal ps = snapshot.getPackageStateInternal(packageName);
        if (ps == null) {
            return;
        }
        final SparseArray<? extends PackageUserStateInternal> userStates = ps.getUserStates();
        for (int index = 0; index < userStates.size(); index++) {
            if (userStates.valueAt(index).isInstalled()) {
                return;
            }
        }
        // TODO Implement atomic delete if package is unused
        // It is currently possible that the package will be deleted even if it is installed
        // after this method returns.
        mHandler.post(() -> mDeletePackageHelper.deletePackageX(
                packageName, PackageManager.VERSION_CODE_HIGHEST,
                0, PackageManager.DELETE_ALL_USERS, true /*removedBySystem*/));
    }

    void deletePreloadsFileCache() {
        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CLEAR_APP_CACHE,
                "deletePreloadsFileCache");
        File dir = Environment.getDataPreloadsFileCacheDirectory();
        Slog.i(PackageManagerService.TAG, "Deleting preloaded file cache " + dir);
        FileUtils.deleteContents(dir);
    }

    void setSystemAppHiddenUntilInstalled(@NonNull Computer snapshot, String packageName,
            boolean hidden) {
        final int callingUid = Binder.getCallingUid();
        final boolean calledFromSystemOrPhone = isSystemOrPhone(callingUid);
        if (!calledFromSystemOrPhone) {
            mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS,
                    "setSystemAppHiddenUntilInstalled");
        }

        final PackageStateInternal stateRead = snapshot.getPackageStateInternal(packageName);
        if (stateRead == null || !stateRead.isSystem() || stateRead.getPkg() == null) {
            return;
        }
        if (stateRead.getPkg().isCoreApp() && !calledFromSystemOrPhone) {
            throw new SecurityException("Only system or phone callers can modify core apps");
        }

        commitPackageStateMutation(null, mutator -> {
            mutator.forPackage(packageName)
                    .setHiddenUntilInstalled(hidden);
            mutator.forDisabledSystemPackage(packageName)
                    .setHiddenUntilInstalled(hidden);
        });
    }

    boolean setSystemAppInstallState(@NonNull Computer snapshot, String packageName,
            boolean installed, int userId) {
        final int callingUid = Binder.getCallingUid();
        final boolean calledFromSystemOrPhone = isSystemOrPhone(callingUid);
        if (!calledFromSystemOrPhone) {
            mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS,
                    "setSystemAppHiddenUntilInstalled");
        }

        final PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName);
        // The target app should always be in system
        if (packageState == null || !packageState.isSystem() || packageState.getPkg() == null) {
            return false;
        }
        if (packageState.getPkg().isCoreApp() && !calledFromSystemOrPhone) {
            throw new SecurityException("Only system or phone callers can modify core apps");
        }
        // Check if the install state is the same
        if (packageState.getUserStateOrDefault(userId).isInstalled() == installed) {
            return false;
        }

        final long callingId = Binder.clearCallingIdentity();
        try {
            if (installed) {
                // install the app from uninstalled state
                mInstallPackageHelper.installExistingPackageAsUser(
                        packageName,
                        userId,
                        PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
                        PackageManager.INSTALL_REASON_DEVICE_SETUP,
                        null,
                        null);
                return true;
            }

            // uninstall the app from installed state
            deletePackageVersioned(
                    new VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST),
                    new PackageManager.LegacyPackageDeleteObserver(null).getBinder(),
                    userId,
                    PackageManager.DELETE_SYSTEM_APP);
            return true;
        } finally {
            Binder.restoreCallingIdentity(callingId);
        }
    }

    void finishPackageInstall(int token, boolean didLaunch) {
        PackageManagerServiceUtils.enforceSystemOrRoot(
                "Only the system is allowed to finish installs");

        if (PackageManagerService.DEBUG_INSTALL) {
            Slog.v(PackageManagerService.TAG, "BM finishing package install for " + token);
        }
        Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "restore", token);

        final Message msg = mHandler.obtainMessage(PackageManagerService.POST_INSTALL, token,
                didLaunch ? 1 : 0);
        mHandler.sendMessage(msg);
    }

    void checkPackageStartable(@NonNull Computer snapshot, @NonNull String packageName,
            @UserIdInt int userId) {
        final int callingUid = Binder.getCallingUid();
        if (snapshot.getInstantAppPackageName(callingUid) != null) {
            throw new SecurityException("Instant applications don't have access to this method");
        }
        if (!mUserManager.exists(userId)) {
            throw new SecurityException("User doesn't exist");
        }
        snapshot.enforceCrossUserPermission(callingUid, userId, false, false,
                "checkPackageStartable");
        switch (snapshot.getPackageStartability(mSafeMode, packageName, callingUid, userId)) {
            case PACKAGE_STARTABILITY_NOT_FOUND:
                throw new SecurityException("Package " + packageName + " was not found!");
            case PACKAGE_STARTABILITY_NOT_SYSTEM:
                throw new SecurityException("Package " + packageName + " not a system app!");
            case PACKAGE_STARTABILITY_FROZEN:
                throw new SecurityException("Package " + packageName + " is currently frozen!");
            case PACKAGE_STARTABILITY_DIRECT_BOOT_UNSUPPORTED:
                throw new SecurityException("Package " + packageName + " is not encryption aware!");
            case PACKAGE_STARTABILITY_OK:
            default:
        }
    }

    void setPackageStoppedState(@NonNull Computer snapshot, @NonNull String packageName,
            boolean stopped, @UserIdInt int userId) {
        if (!mUserManager.exists(userId)) return;
        final int callingUid = Binder.getCallingUid();
        boolean wasStopped = false;
        if (snapshot.getInstantAppPackageName(callingUid) == null) {
            final int permission = mContext.checkCallingOrSelfPermission(
                    Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE);
            final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
            if (!allowedByPermission
                    && !ArrayUtils.contains(snapshot.getPackagesForUid(callingUid), packageName)) {
                throw new SecurityException(
                        "Permission Denial: attempt to change stopped state from pid="
                                + Binder.getCallingPid()
                                + ", uid=" + callingUid + ", package=" + packageName);
            }
            snapshot.enforceCrossUserPermission(callingUid, userId,
                    true /* requireFullPermission */, true /* checkShell */, "stop package");

            final PackageStateInternal packageState =
                    snapshot.getPackageStateForInstalledAndFiltered(
                            packageName, callingUid, userId);
            final PackageUserState packageUserState = packageState == null
                    ? null : packageState.getUserStateOrDefault(userId);
            if (packageState != null && packageUserState.isStopped() != stopped) {
                boolean wasNotLaunched = packageUserState.isNotLaunched();
                wasStopped = packageUserState.isStopped();
                commitPackageStateMutation(null, packageName, state -> {
                    PackageUserStateWrite userState = state.userState(userId);
                    userState.setStopped(stopped);
                    if (wasNotLaunched) {
                        userState.setNotLaunched(false);
                    }
                });

                if (wasNotLaunched) {
                    final String installerPackageName =
                            packageState.getInstallSource().mInstallerPackageName;
                    if (installerPackageName != null) {
                        notifyFirstLaunch(packageName, installerPackageName, userId);
                    }
                }

                scheduleWritePackageRestrictions(userId);
            }
        }

        // If this would cause the app to leave force-stop, then also make sure to unhibernate the
        // app if needed.
        if (!stopped) {
            mHandler.post(() -> {
                AppHibernationManagerInternal ah =
                        mInjector.getLocalService(AppHibernationManagerInternal.class);
                if (ah != null && ah.isHibernatingForUser(packageName, userId)) {
                    ah.setHibernatingForUser(packageName, userId, false);
                    ah.setHibernatingGlobally(packageName, false);
                }
            });
            // Send UNSTOPPED broadcast if necessary
            if (wasStopped && Flags.stayStopped()) {
                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "unstoppedBroadcast");
                final PackageManagerInternal pmi =
                        mInjector.getLocalService(PackageManagerInternal.class);
                final int [] userIds = resolveUserIds(userId);
                final SparseArray<int[]> broadcastAllowList =
                        snapshotComputer().getVisibilityAllowLists(packageName, userIds);
                final Bundle extras = new Bundle();
                extras.putInt(Intent.EXTRA_UID, pmi.getPackageUid(packageName, 0, userId));
                extras.putInt(Intent.EXTRA_USER_HANDLE, userId);
                extras.putLong(Intent.EXTRA_TIME, SystemClock.elapsedRealtime());
                mHandler.post(() -> {
                    mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_UNSTOPPED,
                            packageName, extras, Intent.FLAG_RECEIVER_REGISTERED_ONLY,
                            null /* targetPkg */, null /* finishedReceiver */, userIds,
                            null /* instantUserIds */, broadcastAllowList,
                            null /* filterExtrasForReceiver */, null /* bOptions */,
                            null /* requiredPermissions */);
                });
                mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_UNSTOPPED,
                        packageName, extras, userIds, null /* instantUserIds */,
                        broadcastAllowList, mHandler, null /* filterExtras */);
                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            }
        }
    }

    void notifyComponentUsed(@NonNull Computer snapshot, @NonNull String packageName,
            @UserIdInt int userId, @Nullable String recentCallingPackage,
            @NonNull String debugInfo) {
        synchronized (mLock) {
            final PackageSetting pkgSetting = mSettings.getPackageLPr(packageName);
            // If the package doesn't exist, don't need to proceed to setPackageStoppedState.
            if (pkgSetting == null) {
                return;
            }
            if (pkgSetting.getUserStateOrDefault(userId).isQuarantined()) {
                Slog.i(TAG,
                        "Component is quarantined+suspended but being used: "
                                + packageName + " by " + recentCallingPackage + ", debugInfo: "
                                + debugInfo);
            }
        }
        PackageManagerService.this
                .setPackageStoppedState(snapshot, packageName, false /* stopped */,
                        userId);
    }

    public class IPackageManagerImpl extends IPackageManagerBase {

        public IPackageManagerImpl() {
            super(PackageManagerService.this, mContext, mDexOptHelper, mModuleInfoProvider,
                    mPreferredActivityHelper, mResolveIntentHelper, mDomainVerificationManager,
                    mDomainVerificationConnection, mInstallerService, mPackageProperty,
                    mResolveComponentName, mInstantAppResolverSettingsComponent,
                    mServicesExtensionPackageName, mSharedSystemSharedLibraryPackageName);
        }

        @Override
        public void checkPackageStartable(String packageName, int userId) {
            PackageManagerService.this
                    .checkPackageStartable(snapshotComputer(), packageName, userId);
        }

        @Override
        public void clearApplicationProfileData(String packageName) {
            PackageManagerServiceUtils.enforceSystemOrRootOrShell(
                    "Only the system or shell can clear all profile data");

            final Computer snapshot = snapshotComputer();
            final AndroidPackage pkg = snapshot.getPackage(packageName);
            try (PackageFreezer ignored =
                            freezePackage(packageName, USER_ALL,
                                    "clearApplicationProfileData",
                                    ApplicationExitInfo.REASON_OTHER, null /* request */)) {
                try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) {
                    mAppDataHelper.clearAppProfilesLIF(pkg);
                }
            }
        }

        @android.annotation.EnforcePermission(android.Manifest.permission.CLEAR_APP_USER_DATA)
        @Override
        public void clearApplicationUserData(final String packageName,
                final IPackageDataObserver observer, final int userId) {
            clearApplicationUserData_enforcePermission();

            final int callingUid = Binder.getCallingUid();
            final Computer snapshot = snapshotComputer();
            snapshot.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
                    false /* checkShell */, "clear application data");

            if (snapshot.getPackageStateForInstalledAndFiltered(
                    packageName, callingUid, userId) == null) {
                if (observer != null) {
                    mHandler.post(() -> {
                        try {
                            observer.onRemoveCompleted(packageName, false);
                        } catch (RemoteException e) {
                            Log.i(TAG, "Observer no longer exists.");
                        }
                    });
                }
                return;
            }
            if (mProtectedPackages.isPackageDataProtected(userId, packageName)) {
                throw new SecurityException("Cannot clear data for a protected package: "
                        + packageName);
            }
            final int callingPid = Binder.getCallingPid();
            EventLog.writeEvent(EventLogTags.PM_CLEAR_APP_DATA_CALLER, callingPid, callingUid,
                    packageName);

            // Queue up an async operation since the package deletion may take a little while.
            mHandler.post(new Runnable() {
                public void run() {
                    mHandler.removeCallbacks(this);

                    GosPackageStatePmHooks.onClearApplicationUserData(
                            PackageManagerService.this, packageName, userId);

                    final boolean succeeded;
                    try (PackageFreezer freezer = freezePackage(packageName, userId,
                            "clearApplicationUserData",
                            ApplicationExitInfo.REASON_USER_REQUESTED, null /* request */,
                            /* waitAppKilled= */ true)) {
                        try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) {
                            succeeded = clearApplicationUserDataLIF(snapshotComputer(), packageName,
                                    userId);
                        }
                        mInstantAppRegistry.deleteInstantApplicationMetadata(packageName, userId);
                        synchronized (mLock) {
                            if (succeeded) {
                                resetComponentEnabledSettingsIfNeededLPw(packageName, userId,
                                        callingUid);
                            }
                        }
                    }
                    if (succeeded) {
                        // invoke DeviceStorageMonitor's update method to clear any notifications
                        DeviceStorageMonitorInternal dsm = LocalServices
                                .getService(DeviceStorageMonitorInternal.class);
                        if (dsm != null) {
                            dsm.checkMemory();
                        }
                        if (checkPermission(Manifest.permission.SUSPEND_APPS, packageName, userId)
                                == PERMISSION_GRANTED) {
                            final Computer snapshot = snapshotComputer();
                            unsuspendForSuspendingPackage(
                                    snapshot, packageName, userId, USER_ALL);
                            removeAllDistractingPackageRestrictions(snapshot, userId);
                            synchronized (mLock) {
                                flushPackageRestrictionsAsUserInternalLocked(userId);
                            }
                        }
                    }
                    if (observer != null) {
                        try {
                            observer.onRemoveCompleted(packageName, succeeded);
                        } catch (RemoteException e) {
                            Log.i(TAG, "Observer no longer exists.");
                        }
                    } //end if observer
                } //end run
            });
        }

        @android.annotation.EnforcePermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
        @Override
        public void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage) {
            clearCrossProfileIntentFilters_enforcePermission();
            final int callingUid = Binder.getCallingUid();
            final Computer snapshot = snapshotComputer();
            enforceOwnerRights(snapshot, ownerPackage, callingUid);
            PackageManagerServiceUtils.enforceShellRestriction(mInjector.getUserManagerInternal(),
                    UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId);
            PackageManagerService.this.mInjector.getCrossProfileIntentFilterHelper()
                            .clearCrossProfileIntentFilters(sourceUserId, ownerPackage,
                                    null);
            scheduleWritePackageRestrictions(sourceUserId);
        }

        @android.annotation.EnforcePermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
        @Override
        public boolean removeCrossProfileIntentFilter(IntentFilter intentFilter,
                String ownerPackage,
                int sourceUserId,
                int targetUserId, int flags) {
            removeCrossProfileIntentFilter_enforcePermission();
            final int callingUid = Binder.getCallingUid();
            enforceOwnerRights(snapshotComputer(), ownerPackage, callingUid);
            mUserManager.enforceCrossProfileIntentFilterAccess(sourceUserId, targetUserId,
                    callingUid, /* addCrossProfileIntentFilter */ false);
            PackageManagerServiceUtils.enforceShellRestriction(mInjector.getUserManagerInternal(),
                    UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, sourceUserId);

            boolean removedMatchingFilter = false;
            synchronized (mLock) {
                CrossProfileIntentResolver resolver =
                        mSettings.editCrossProfileIntentResolverLPw(sourceUserId);

                ArraySet<CrossProfileIntentFilter> set =
                        new ArraySet<>(resolver.filterSet());
                for (int i = 0; i < set.size(); i++) {
                    final CrossProfileIntentFilter filter = set.valueAt(i);
                    if (IntentFilter.filterEquals(filter.mFilter, intentFilter)
                            && filter.getOwnerPackage().equals(ownerPackage)
                            && filter.getTargetUserId() == targetUserId
                            && filter.getFlags() == flags) {
                        resolver.removeFilter(filter);
                        removedMatchingFilter = true;
                        break;
                    }
                }
            }
            if (removedMatchingFilter) {
                scheduleWritePackageRestrictions(sourceUserId);
            }
            return removedMatchingFilter;
        }

        @Override
        public final void deleteApplicationCacheFiles(final String packageName,
                final IPackageDataObserver observer) {
            final int userId = UserHandle.getCallingUserId();
            deleteApplicationCacheFilesAsUser(packageName, userId, observer);
        }

        @Override
        public void deleteApplicationCacheFilesAsUser(final String packageName, final int userId,
                final IPackageDataObserver observer) {
            final int callingUid = Binder.getCallingUid();
            if (mContext.checkCallingOrSelfPermission(
                    android.Manifest.permission.INTERNAL_DELETE_CACHE_FILES)
                    != PackageManager.PERMISSION_GRANTED) {
                // If the caller has the old delete cache permission, silently ignore.  Else throw.
                if (mContext.checkCallingOrSelfPermission(
                        android.Manifest.permission.DELETE_CACHE_FILES)
                        == PackageManager.PERMISSION_GRANTED) {
                    Slog.w(TAG, "Calling uid " + callingUid + " does not have " +
                            android.Manifest.permission.INTERNAL_DELETE_CACHE_FILES +
                            ", silently ignoring");
                    return;
                }
                mContext.enforceCallingOrSelfPermission(
                        android.Manifest.permission.INTERNAL_DELETE_CACHE_FILES, null);
            }
            final Computer snapshot = snapshotComputer();
            snapshot.enforceCrossUserPermission(callingUid, userId, /* requireFullPermission= */ true,
                    /* checkShell= */ false, "delete application cache files");
            final int hasAccessInstantApps = mContext.checkCallingOrSelfPermission(
                    android.Manifest.permission.ACCESS_INSTANT_APPS);
            final int callingPid = Binder.getCallingPid();
            EventLog.writeEvent(EventLogTags.PM_CLEAR_APP_DATA_CALLER, callingPid, callingUid,
                    packageName);

            // Queue up an async operation since the package deletion may take a little while.
            mHandler.post(() -> {
                // Snapshot in the Handler Runnable since this may be deferred quite a bit
                // TODO: Is this and the later mInstallLock re-snapshot necessary?
                final Computer newSnapshot = snapshotComputer();
                final PackageStateInternal ps = newSnapshot.getPackageStateInternal(packageName);
                boolean doClearData = true;
                if (ps != null) {
                    final boolean targetIsInstantApp =
                            ps.getUserStateOrDefault(UserHandle.getUserId(callingUid)).isInstantApp();
                    doClearData = !targetIsInstantApp
                            || hasAccessInstantApps == PackageManager.PERMISSION_GRANTED;
                }
                if (doClearData) {
                    try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) {
                        final int flags = FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL;
                        // Snapshot again after mInstallLock?
                        final AndroidPackage pkg = snapshotComputer().getPackage(packageName);
                        // We're only clearing cache files, so we don't care if the
                        // app is unfrozen and still able to run
                        mAppDataHelper.clearAppDataLIF(pkg, userId,
                                flags | Installer.FLAG_CLEAR_CACHE_ONLY);
                        mAppDataHelper.clearAppDataLIF(pkg, userId,
                                flags | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
                    }
                }
                if (observer != null) {
                    try {
                        observer.onRemoveCompleted(packageName, true);
                    } catch (RemoteException e) {
                        Log.i(TAG, "Observer no longer exists.");
                    }
                }
            });
        }

        @Override
        public void enterSafeMode() {
            PackageManagerServiceUtils.enforceSystemOrRoot(
                    "Only the system can request entering safe mode");

            if (!mSystemReady) {
                mSafeMode = true;
            }
        }

        @Override
        public void extendVerificationTimeout(int verificationId, int verificationCodeAtTimeout,
                long millisecondsToDelay) {
            // Negative ids correspond to testing verifiers and will be silently enforced in
            // the handler thread.
            if (verificationId >= 0) {
                mContext.enforceCallingOrSelfPermission(
                        Manifest.permission.PACKAGE_VERIFICATION_AGENT,
                        "Only package verification agents can extend verification timeouts");
            }
            final int callingUid = Binder.getCallingUid();

            mHandler.post(() -> {
                final int id = verificationId >= 0 ? verificationId : -verificationId;
                final PackageVerificationState state = mPendingVerification.get(id);
                if (state == null || !state.extendTimeout(callingUid)) {
                    // Invalid uid or already extended.
                    return;
                }

                final PackageVerificationResponse response = new PackageVerificationResponse(
                        verificationCodeAtTimeout, callingUid);

                long delay = millisecondsToDelay;
                if (delay > PackageManager.MAXIMUM_VERIFICATION_TIMEOUT) {
                    delay = PackageManager.MAXIMUM_VERIFICATION_TIMEOUT;
                }
                if (delay < 0) {
                    delay = 0;
                }

                final Message msg = mHandler.obtainMessage(PackageManagerService.PACKAGE_VERIFIED);
                msg.arg1 = id;
                msg.obj = response;
                mHandler.sendMessageDelayed(msg, delay);
            });
        }

        @WorkerThread
        @Override
        public void flushPackageRestrictionsAsUser(int userId) {
            final Computer snapshot = snapshotComputer();
            final int callingUid = Binder.getCallingUid();
            if (snapshot.getInstantAppPackageName(callingUid) != null) {
                return;
            }
            if (!mUserManager.exists(userId)) {
                return;
            }
            snapshot.enforceCrossUserPermission(callingUid, userId,
                    false /* requireFullPermission*/, false /* checkShell */,
                    "flushPackageRestrictions");
            synchronized (mLock) {
                flushPackageRestrictionsAsUserInternalLocked(userId);
            }
        }


        @android.annotation.EnforcePermission(android.Manifest.permission.CLEAR_APP_CACHE)
        @Override
        public void freeStorage(final String volumeUuid, final long freeStorageSize,
                final @StorageManager.AllocateFlags int flags, final IntentSender pi) {
            freeStorage_enforcePermission();
            mHandler.post(() -> {
                boolean success = false;
                try {
                    PackageManagerService.this.freeStorage(volumeUuid, freeStorageSize, flags);
                    success = true;
                } catch (IOException e) {
                    Slog.w(TAG, e);
                }
                if (pi != null) {
                    try {
                        final BroadcastOptions options = BroadcastOptions.makeBasic();
                        options.setPendingIntentBackgroundActivityLaunchAllowed(false);
                        pi.sendIntent(null, success ? 1 : 0, null /* intent */,
                                null /* requiredPermission */, options.toBundle(),
                                null /* executor */, null /* onFinished*/);
                    } catch (SendIntentException e) {
                        Slog.w(TAG, e);
                    }
                }
            });
        }

        @android.annotation.EnforcePermission(android.Manifest.permission.CLEAR_APP_CACHE)
        @Override
        public void freeStorageAndNotify(final String volumeUuid, final long freeStorageSize,
                final @StorageManager.AllocateFlags int flags, final IPackageDataObserver observer) {
            freeStorageAndNotify_enforcePermission();
            mHandler.post(() -> {
                boolean success = false;
                try {
                    PackageManagerService.this.freeStorage(volumeUuid, freeStorageSize, flags);
                    success = true;
                } catch (IOException e) {
                    Slog.w(PackageManagerService.TAG, e);
                }
                if (observer != null) {
                    try {
                        observer.onRemoveCompleted(null, success);
                    } catch (RemoteException e) {
                        Slog.w(PackageManagerService.TAG, e);
                    }
                }
            });
        }

        @Override
        public ChangedPackages getChangedPackages(int sequenceNumber, int userId) {
            final int callingUid = Binder.getCallingUid();
            final Computer snapshot = snapshotComputer();
            if (snapshot.getInstantAppPackageName(callingUid) != null) {
                return null;
            }
            if (!mUserManager.exists(userId)) {
                return null;
            }
            snapshot.enforceCrossUserPermission(callingUid, userId, false, false,
                    "getChangedPackages");
            final ChangedPackages changedPackages = mChangedPackagesTracker.getChangedPackages(
                    sequenceNumber, userId);

            if (changedPackages != null) {
                final List<String> packageNames = changedPackages.getPackageNames();
                for (int index = packageNames.size() - 1; index >= 0; index--) {
                    // Filter out the changes if the calling package should not be able to see it.
                    final PackageStateInternal packageState =
                            snapshot.getPackageStateInternal(packageNames.get(index));
                    if (snapshot.shouldFilterApplication(packageState, callingUid, userId)) {
                        packageNames.remove(index);
                    }
                }
            }

            return changedPackages;
        }

        @Override
        public byte[] getDomainVerificationBackup(int userId) {
            if (Binder.getCallingUid() != Process.SYSTEM_UID) {
                throw new SecurityException("Only the system may call getDomainVerificationBackup()");
            }

            try {
                try (ByteArrayOutputStream output = new ByteArrayOutputStream()) {
                    TypedXmlSerializer serializer = Xml.resolveSerializer(output);
                    mDomainVerificationManager.writeSettings(snapshotComputer(), serializer, true,
                            userId);
                    return output.toByteArray();
                }
            } catch (Exception e) {
                if (PackageManagerService.DEBUG_BACKUP) {
                    Slog.e(PackageManagerService.TAG, "Unable to write domain verification for backup", e);
                }
                return null;
            }
        }

        @Override
        public IBinder getHoldLockToken() {
            if (!Build.IS_DEBUGGABLE) {
                throw new SecurityException("getHoldLockToken requires a debuggable build");
            }

            mContext.enforceCallingPermission(
                    Manifest.permission.INJECT_EVENTS,
                    "getHoldLockToken requires INJECT_EVENTS permission");

            final Binder token = new Binder();
            token.attachInterface(this, "holdLock:" + Binder.getCallingUid());
            return token;
        }

        @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_INSTANT_APPS)
        @Override
        public String getInstantAppAndroidId(String packageName, int userId) {
            getInstantAppAndroidId_enforcePermission();
            final Computer snapshot = snapshotComputer();
            snapshot.enforceCrossUserPermission(Binder.getCallingUid(), userId,
                    true /* requireFullPermission */, false /* checkShell */,
                    "getInstantAppAndroidId");
            // Make sure the target is an Instant App.
            if (!snapshot.isInstantApp(packageName, userId)) {
                return null;
            }
            return mInstantAppRegistry.getInstantAppAndroidId(packageName, userId);
        }

        @Override
        public byte[] getInstantAppCookie(String packageName, int userId) {
            if (HIDE_EPHEMERAL_APIS) {
                return null;
            }

            final Computer snapshot = snapshotComputer();
            snapshot.enforceCrossUserPermission(Binder.getCallingUid(), userId,
                    true /* requireFullPermission */, false /* checkShell */,
                    "getInstantAppCookie");
            if (!snapshot.isCallerSameApp(packageName, Binder.getCallingUid())) {
                return null;
            }
            PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName);
            if (packageState == null || packageState.getPkg() == null) {
                return null;
            }
            return mInstantAppRegistry.getInstantAppCookie(packageState.getPkg(), userId);
        }

        @Override
        public Bitmap getInstantAppIcon(String packageName, int userId) {
            if (HIDE_EPHEMERAL_APIS) {
                return null;
            }

            final Computer snapshot = snapshotComputer();
            if (!snapshot.canViewInstantApps(Binder.getCallingUid(), userId)) {
                mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS,
                        "getInstantAppIcon");
            }
            snapshot.enforceCrossUserPermission(Binder.getCallingUid(), userId,
                    true /* requireFullPermission */, false /* checkShell */,
                    "getInstantAppIcon");

            return mInstantAppRegistry.getInstantAppIcon(packageName, userId);
        }

        @Override
        public ParceledListSlice<InstantAppInfo> getInstantApps(int userId) {
            if (HIDE_EPHEMERAL_APIS) {
                return null;
            }

            final Computer snapshot = snapshotComputer();
            if (!snapshot.canViewInstantApps(Binder.getCallingUid(), userId)) {
                mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_INSTANT_APPS,
                        "getEphemeralApplications");
            }
            snapshot.enforceCrossUserPermission(Binder.getCallingUid(), userId,
                    true /* requireFullPermission */, false /* checkShell */,
                    "getEphemeralApplications");

            List<InstantAppInfo> instantApps = mInstantAppRegistry.getInstantApps(snapshot, userId);
            if (instantApps != null) {
                return new ParceledListSlice<>(instantApps);
            }
            return null;
        }

        @Override
        public ResolveInfo getLastChosenActivity(Intent intent, String resolvedType, int flags) {
            return mPreferredActivityHelper.getLastChosenActivity(snapshotComputer(), intent,
                    resolvedType, flags);
        }

        @Override
        public IntentSender getLaunchIntentSenderForPackage(String packageName, String callingPackage,
                String featureId, int userId) throws RemoteException {
            return mResolveIntentHelper.getLaunchIntentSenderForPackage(snapshotComputer(),
                    packageName, callingPackage, featureId, userId);
        }

        @Override
        public List<String> getMimeGroup(String packageName, String mimeGroup) {
            final Computer snapshot = snapshotComputer();
            enforceOwnerRights(snapshot, packageName, Binder.getCallingUid());
            return getMimeGroupInternal(snapshot, packageName, mimeGroup);
        }

        @android.annotation.EnforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)
        @Override
        public int getMoveStatus(int moveId) {
            getMoveStatus_enforcePermission();
            return mMoveCallbacks.mLastStatus.get(moveId);
        }

        @android.annotation.EnforcePermission(android.Manifest.permission.GET_APP_METADATA)
        @Override
        public ParcelFileDescriptor getAppMetadataFd(String packageName, int userId) {
            getAppMetadataFd_enforcePermission();
            final int callingUid = Binder.getCallingUid();
            final Computer snapshot = snapshotComputer();
            final PackageStateInternal ps = snapshot.getPackageStateForInstalledAndFiltered(
                    packageName, callingUid, userId);
            if (ps == null) {
                throw new ParcelableException(
                        new PackageManager.NameNotFoundException(packageName));
            }
            String filePath = ps.getAppMetadataFilePath();
            if (filePath == null) {
                return null;
            }
            File file = new File(filePath);
            try {
                return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
            } catch (FileNotFoundException e) {
                return null;
            }
        }

        @android.annotation.EnforcePermission(android.Manifest.permission.GET_APP_METADATA)
        @Override
        public int getAppMetadataSource(String packageName, int userId) {
            getAppMetadataSource_enforcePermission();
            final int callingUid = Binder.getCallingUid();
            final Computer snapshot = snapshotComputer();
            final PackageStateInternal ps = snapshot.getPackageStateForInstalledAndFiltered(
                    packageName, callingUid, userId);
            if (ps == null) {
                throw new ParcelableException(
                        new PackageManager.NameNotFoundException(packageName));
            }
            return ps.getAppMetadataSource();
        }

        @Override
        public String getPermissionControllerPackageName() {
            final int callingUid = Binder.getCallingUid();
            final int callingUserId = UserHandle.getUserId(callingUid);
            final Computer snapshot = snapshotComputer();
            if (snapshot.getPackageStateForInstalledAndFiltered(
                    mRequiredPermissionControllerPackage, callingUid, callingUserId) != null) {
                return mRequiredPermissionControllerPackage;
            }

            throw new IllegalStateException("PermissionController is not found");
        }

        @Override
        @SuppressWarnings("GuardedBy")
        public int getRuntimePermissionsVersion(@UserIdInt int userId) {
            Preconditions.checkArgumentNonnegative(userId);
            enforceAdjustRuntimePermissionsPolicyOrUpgradeRuntimePermissions(
                    "getRuntimePermissionVersion");
            return mSettings.getDefaultRuntimePermissionsVersion(userId);
        }

        @Override
        public String getSplashScreenTheme(@NonNull String packageName, int userId) {
            final Computer snapshot = snapshotComputer();
            final int callingUid = Binder.getCallingUid();
            snapshot.enforceCrossUserPermission(
                    callingUid, userId, false /* requireFullPermission */,
                    false /* checkShell */, "getSplashScreenTheme");
            PackageStateInternal packageState = snapshot.getPackageStateForInstalledAndFiltered(
                    packageName, callingUid, userId);
            return packageState == null ? null
                    : packageState.getUserStateOrDefault(userId).getSplashScreenTheme();
        }

        @Override
        @PackageManager.UserMinAspectRatio
        public int getUserMinAspectRatio(@NonNull String packageName, int userId) {
            final Computer snapshot = snapshotComputer();
            final int callingUid = Binder.getCallingUid();
            final PackageStateInternal packageState = snapshot
                    .getPackageStateForInstalledAndFiltered(packageName, callingUid, userId);
            return packageState == null ? USER_MIN_ASPECT_RATIO_UNSET
                    : packageState.getUserStateOrDefault(userId).getMinAspectRatio();
        }

        @Override
        public Bundle getSuspendedPackageAppExtras(String packageName, int userId) {
            final int callingUid = Binder.getCallingUid();
            final Computer snapshot = snapshot();
            if (snapshot.getPackageUid(packageName, 0, userId) != callingUid) {
                throw new SecurityException("Calling package " + packageName
                        + " does not belong to calling uid " + callingUid);
            }
            return SuspendPackageHelper
                    .getSuspendedPackageAppExtras(snapshot, packageName, userId, callingUid);
        }

        @Override
        public String getSuspendingPackage(String packageName, int userId) {
            try {
                final int callingUid = Binder.getCallingUid();
                final Computer snapshot = snapshot();
                // This will do visibility checks as well.
                if (!snapshot.isPackageSuspendedForUser(packageName, userId)) {
                    return null;
                }
                final UserPackage suspender = mSuspendPackageHelper.getSuspendingPackage(
                        snapshot, packageName, userId, callingUid);
                return suspender != null ? suspender.packageName : null;
            } catch (PackageManager.NameNotFoundException e) {
                return null;
            }
        }

        @Override
        public @NonNull ParceledListSlice<FeatureInfo> getSystemAvailableFeatures() {
            // allow instant applications
            ArrayList<FeatureInfo> res;
            res = new ArrayList<>(mAvailableFeatures.size() + 1);
            res.addAll(mAvailableFeatures.values());
            final FeatureInfo fi = new FeatureInfo();
            fi.reqGlEsVersion = SystemProperties.getInt("ro.opengles.version",
                    FeatureInfo.GL_ES_VERSION_UNDEFINED);
            res.add(fi);

            return new ParceledListSlice<>(res);
        }

        @Override
        public @NonNull List<String> getInitialNonStoppedSystemPackages() {
            return mInitialNonStoppedSystemPackages != null
                    ? new ArrayList<>(mInitialNonStoppedSystemPackages) : new ArrayList<>();
        }

        @Override
        public String[] getUnsuspendablePackagesForUser(String[] packageNames, int userId) {
            Objects.requireNonNull(packageNames, "packageNames cannot be null");
            mContext.enforceCallingOrSelfPermission(Manifest.permission.SUSPEND_APPS,
                    "getUnsuspendablePackagesForUser");
            final int callingUid = Binder.getCallingUid();
            if (UserHandle.getUserId(callingUid) != userId) {
                mContext.enforceCallingOrSelfPermission(
                        Manifest.permission.INTERACT_ACROSS_USERS_FULL,
                        "Calling uid " + callingUid
                                + " cannot query getUnsuspendablePackagesForUser for user "
                                + userId);
            }
            return mSuspendPackageHelper.getUnsuspendablePackagesForUser(snapshotComputer(),
                    packageNames, userId, callingUid);
        }

        @android.annotation.EnforcePermission(android.Manifest.permission.PACKAGE_VERIFICATION_AGENT)
        @Override
        public VerifierDeviceIdentity getVerifierDeviceIdentity() throws RemoteException {
            getVerifierDeviceIdentity_enforcePermission();

            final List<UserInfo> activeUsers = Settings.getActiveUsers(mUserManager);
            synchronized (mLock) {
                return mSettings.getVerifierDeviceIdentityLPw(mLiveComputer, activeUsers);
            }
        }

        @Override
        public void makeProviderVisible(int recipientUid, @NonNull String visibleAuthority) {
            final Computer snapshot = snapshotComputer();
            final int recipientUserId = UserHandle.getUserId(recipientUid);
            final ProviderInfo providerInfo =
                    snapshot.getGrantImplicitAccessProviderInfo(recipientUid, visibleAuthority);
            if (providerInfo == null) {
                return;
            }
            int visibleUid = providerInfo.applicationInfo.uid;
            PackageManagerService.this.grantImplicitAccess(snapshot, recipientUserId,
                    null /*Intent*/, UserHandle.getAppId(recipientUid), visibleUid,
                    false /*direct*/, false /* retainOnUpdate */);
        }

        @android.annotation.EnforcePermission(android.Manifest.permission.MAKE_UID_VISIBLE)
        @Override
        public void makeUidVisible(int recipientUid, int visibleUid) {
            makeUidVisible_enforcePermission();
            final int callingUid = Binder.getCallingUid();
            final int recipientUserId = UserHandle.getUserId(recipientUid);
            final int visibleUserId = UserHandle.getUserId(visibleUid);
            final Computer snapshot = snapshotComputer();
            snapshot.enforceCrossUserPermission(callingUid, recipientUserId,
                    false /* requireFullPermission */, false /* checkShell */, "makeUidVisible");
            snapshot.enforceCrossUserPermission(callingUid, visibleUserId,
                    false /* requireFullPermission */, false /* checkShell */, "makeUidVisible");
            snapshot.enforceCrossUserPermission(recipientUid, visibleUserId,
                    false /* requireFullPermission */, false /* checkShell */, "makeUidVisible");

            PackageManagerService.this.grantImplicitAccess(snapshot, recipientUserId,
                    null /*Intent*/, UserHandle.getAppId(recipientUid), visibleUid,
                    false /*direct*/, false /* retainOnUpdate */);
        }

        @Override
        public void holdLock(IBinder token, int durationMs) {
            mTestUtilityService.verifyHoldLockToken(token);

            synchronized (mLock) {
                SystemClock.sleep(durationMs);
            }
        }

        /**
         * @hide
         */
        @Override
        public int installExistingPackageAsUser(String packageName, int userId, int installFlags,
                int installReason, List<String> whiteListedPermissions) {
            return mInstallPackageHelper.installExistingPackageAsUser(packageName, userId, installFlags,
                    installReason, whiteListedPermissions, null).first;
        }

        @Override
        public boolean isAutoRevokeWhitelisted(String packageName) {
            int mode = mInjector.getSystemService(AppOpsManager.class).checkOpNoThrow(
                    AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED,
                    Binder.getCallingUid(), packageName);
            return mode == MODE_IGNORED;
        }

        @Override
        public boolean isPackageStateProtected(@NonNull String packageName, @UserIdInt int userId) {
            final int callingUid = Binder.getCallingUid();
            final int callingAppId = UserHandle.getAppId(callingUid);

            final Computer snapshot = snapshotComputer();
            snapshot.enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
                    true /*checkShell*/, "isPackageStateProtected");

            if (!PackageManagerServiceUtils.isSystemOrRoot(callingAppId)
                    && snapshot.checkUidPermission(MANAGE_DEVICE_ADMINS, callingUid)
                    != PERMISSION_GRANTED) {
                throw new SecurityException("Caller must have the "
                        + MANAGE_DEVICE_ADMINS + " permission.");
            }

            return mProtectedPackages.isPackageStateProtected(userId, packageName);
        }

        @Override
        public boolean isProtectedBroadcast(String actionName) {
            if (actionName != null) {
                // TODO: remove these terrible hacks
                if (actionName.startsWith("android.net.netmon.lingerExpired")
                        || actionName.startsWith("com.android.server.sip.SipWakeupTimer")
                        || actionName.startsWith("com.android.internal.telephony.data-reconnect")
                        || actionName.startsWith("android.net.netmon.launchCaptivePortalApp")) {
                    return true;
                }
            }
            // allow instant applications
            synchronized (mProtectedBroadcasts) {
                return mProtectedBroadcasts.contains(actionName);
            }
        }

        /**
         * Logs process start information (including base APK hash) to the security log.
         * @hide
         */
        @Override
        public void logAppProcessStartIfNeeded(String packageName, String processName, int uid,
                String seinfo, String apkFile, int pid) {
            final Computer snapshot = snapshotComputer();
            if (snapshot.getInstantAppPackageName(Binder.getCallingUid()) != null) {
                return;
            }
            if (!SecurityLog.isLoggingEnabled()) {
                return;
            }
            mProcessLoggingHandler.logAppProcessStart(mContext,
                    LocalServices.getService(PackageManagerInternal.class), apkFile, packageName,
                    processName, uid, seinfo, pid);
        }

        @android.annotation.EnforcePermission(android.Manifest.permission.MOVE_PACKAGE)
        @Override
        public int movePackage(final String packageName, final String volumeUuid) {
            movePackage_enforcePermission();

            final int callingUid = Binder.getCallingUid();
            final UserHandle user = new UserHandle(UserHandle.getUserId(callingUid));
            final int moveId = mNextMoveId.getAndIncrement();
            mHandler.post(() -> {
                try {
                    MovePackageHelper movePackageHelper =
                            new MovePackageHelper(PackageManagerService.this);
                    movePackageHelper.movePackageInternal(
                            packageName, volumeUuid, moveId, callingUid, user);
                } catch (PackageManagerException e) {
                    Slog.w(PackageManagerService.TAG, "Failed to move " + packageName, e);
                    mMoveCallbacks.notifyStatusChanged(moveId, e.error);
                }
            });
            return moveId;
        }

        @android.annotation.EnforcePermission(android.Manifest.permission.MOVE_PACKAGE)
        @Override
        public int movePrimaryStorage(String volumeUuid) throws RemoteException {
            movePrimaryStorage_enforcePermission();

            final int realMoveId = mNextMoveId.getAndIncrement();
            final Bundle extras = new Bundle();
            extras.putString(VolumeRecord.EXTRA_FS_UUID, volumeUuid);
            mMoveCallbacks.notifyCreated(realMoveId, extras);

            final IPackageMoveObserver callback = new IPackageMoveObserver.Stub() {
                @Override
                public void onCreated(int moveId, Bundle extras) {
                    // Ignored
                }

                @Override
                public void onStatusChanged(int moveId, int status, long estMillis) {
                    mMoveCallbacks.notifyStatusChanged(realMoveId, status, estMillis);
                }
            };

            final StorageManager storage = mInjector.getSystemService(StorageManager.class);
            storage.setPrimaryStorageUuid(volumeUuid, callback);
            return realMoveId;
        }

        @Override
        public void notifyDexLoad(String loadingPackageName,
                Map<String, String> classLoaderContextMap,
                String loaderIsa) {
            int callingUid = Binder.getCallingUid();
            Computer snapshot = snapshot();

            // System server should be able to report dex load on behalf of other apps. E.g., it
            // could potentially resend the notifications in order to migrate the existing dex load
            // info to ART Service.
            if (!PackageManagerServiceUtils.isSystemOrRoot()
                    && !snapshot.isCallerSameApp(
                            loadingPackageName, callingUid, true /* resolveIsolatedUid */)) {
                Slog.w(PackageManagerService.TAG,
                        TextUtils.formatSimple(
                                "Invalid dex load report. loadingPackageName=%s, uid=%d",
                                loadingPackageName, callingUid));
                return;
            }

            DexUseManagerLocal dexUseManager = DexOptHelper.getDexUseManagerLocal();
            try (PackageManagerLocal.FilteredSnapshot filteredSnapshot =
                            LocalManagerRegistry.getManager(PackageManagerLocal.class)
                                    .withUnownedFilteredSnapshot(snapshot)) {
                // This is called from binder, so exceptions thrown here are caught and handled
                // by it.
                dexUseManager.notifyDexContainersLoaded(
                        filteredSnapshot, loadingPackageName, classLoaderContextMap);
            }
        }

        @Override
        public void notifyPackageUse(String packageName, int reason) {
            final int callingUid = Binder.getCallingUid();
            final int callingUserId = UserHandle.getUserId(callingUid);
            Computer snapshot = snapshotComputer();
            final boolean notify;
            if (snapshot.getInstantAppPackageName(callingUid) != null) {
                notify = snapshot.isCallerSameApp(packageName, callingUid);
            } else {
                notify = !snapshot.isInstantAppInternal(packageName, callingUserId,
                        Process.SYSTEM_UID);
            }
            if (!notify) {
                return;
            }

            notifyPackageUseInternal(packageName, reason);
        }

        @Override
        public void overrideLabelAndIcon(@NonNull ComponentName componentName,
                @NonNull String nonLocalizedLabel, int icon, int userId) {
            if (TextUtils.isEmpty(nonLocalizedLabel)) {
                throw new IllegalArgumentException("Override label should be a valid String");
            }
            updateComponentLabelIcon(componentName, nonLocalizedLabel, icon, userId);
        }

        @Override
        public ParceledListSlice<PackageManager.Property> queryProperty(
                String propertyName, @PackageManager.PropertyLocation int componentType) {
            Objects.requireNonNull(propertyName);
            final int callingUid = Binder.getCallingUid();
            final int callingUserId = UserHandle.getCallingUserId();
            final Computer snapshot = snapshotComputer();
            final List<PackageManager.Property> result =
                    mPackageProperty.queryProperty(propertyName, componentType,
                            packageName -> snapshot.getPackageStateForInstalledAndFiltered(
                                    packageName, callingUid, callingUserId) == null
                    );
            if (result == null) {
                return ParceledListSlice.emptyList();
            }
            return new ParceledListSlice<>(result);
        }

        @Override
        public void registerDexModule(String packageName, String dexModulePath,
                boolean isSharedModule,
                IDexModuleRegisterCallback callback) {
            // ART Service doesn't support this explicit dexopting and instead relies on background
            // dexopt for secondary dex files. For compat parity between ART Service and the legacy
            // code it's disabled for both.
            //
            // Also, this API is problematic anyway since it doesn't provide the correct classloader
            // context, so it is hard to produce dexopt artifacts that the runtime can load
            // successfully.
            Slog.i(TAG,
                    "Ignored unsupported registerDexModule call for " + dexModulePath + " in "
                            + packageName);

            if (callback != null) {
                mHandler.post(() -> {
                    try {
                        callback.onDexModuleRegistered(dexModulePath, false,
                                "registerDexModule call not supported since Android U");
                    } catch (RemoteException e) {
                        Slog.w(PackageManagerService.TAG,
                                "Failed to callback after module registration " + dexModulePath, e);
                    }
                });
            }
        }

        @android.annotation.EnforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)
        @Override
        public void registerMoveCallback(IPackageMoveObserver callback) {
            registerMoveCallback_enforcePermission();
            mMoveCallbacks.register(callback);
        }

        @Override
        public void restoreDomainVerification(byte[] backup, int userId) {
            if (Binder.getCallingUid() != Process.SYSTEM_UID) {
                throw new SecurityException("Only the system may call restorePreferredActivities()");
            }

            try {
                ByteArrayInputStream input = new ByteArrayInputStream(backup);
                TypedXmlPullParser parser = Xml.resolvePullParser(input);

                // User ID input isn't necessary here as it assumes the user integers match and that
                // the only states inside the backup XML are for the target user.
                mDomainVerificationManager.restoreSettings(snapshotComputer(), parser);
                input.close();
            } catch (Exception e) {
                if (PackageManagerService.DEBUG_BACKUP) {
                    Slog.e(PackageManagerService.TAG, "Exception restoring domain verification: " + e.getMessage());
                }
            }
        }

        @Override
        public void restoreLabelAndIcon(@NonNull ComponentName componentName, int userId) {
            updateComponentLabelIcon(componentName, null, null, userId);
        }

        @Override
        public void sendDeviceCustomizationReadyBroadcast() {
            mContext.enforceCallingPermission(Manifest.permission.SEND_DEVICE_CUSTOMIZATION_READY,
                    "sendDeviceCustomizationReadyBroadcast");

            final long ident = Binder.clearCallingIdentity();
            try {
                BroadcastHelper.sendDeviceCustomizationReadyBroadcast();
            } finally {
                Binder.restoreCallingIdentity(ident);
            }
        }

        @Override
        public void setApplicationCategoryHint(String packageName, int categoryHint,
                String callerPackageName) {
            final int callingUid = Binder.getCallingUid();
            final int userId = UserHandle.getCallingUserId();
            final FunctionalUtils.ThrowingBiFunction<PackageStateMutator.InitialState, Computer,
                    PackageStateMutator.Result> implementation = (initialState, computer) -> {
                if (computer.getInstantAppPackageName(callingUid) != null) {
                    throw new SecurityException(
                            "Instant applications don't have access to this method");
                }
                final int callerPackageUid = computer.getPackageUid(callerPackageName, 0, userId);
                if (callerPackageUid != callingUid) {
                    throw new SecurityException(
                            "Package " + callerPackageName + " does not belong to " + callingUid);
                }

                PackageStateInternal packageState = computer.getPackageStateForInstalledAndFiltered(
                        packageName, callingUid, userId);
                if (packageState == null) {
                    throw new IllegalArgumentException("Unknown target package " + packageName);
                }

                if (!Objects.equals(callerPackageName,
                        packageState.getInstallSource().mInstallerPackageName)) {
                    throw new IllegalArgumentException("Calling package " + callerPackageName
                            + " is not installer for " + packageName);
                }

                if (packageState.getCategoryOverride() != categoryHint) {
                    return commitPackageStateMutation(initialState,
                            packageName, state -> state.setCategoryOverride(categoryHint));
                } else {
                    return null;
                }
            };

            PackageStateMutator.Result result =
                    implementation.apply(recordInitialState(), snapshotComputer());
            if (result != null && result.isStateChanged() && !result.isSpecificPackageNull()) {
                // TODO: Specific return value of what state changed?
                // The installer on record might have changed, retry with lock
                synchronized (mPackageStateWriteLock) {
                    result = implementation.apply(recordInitialState(), snapshotComputer());
                }
            }

            if (result != null && result.isCommitted()) {
                scheduleWriteSettings();
            }
        }

        @Override
        public void setApplicationEnabledSetting(String appPackageName,
                int newState, int flags, int userId, String callingPackage) {
            if (!mUserManager.exists(userId)) return;
            if (callingPackage == null) {
                callingPackage = Integer.toString(Binder.getCallingUid());
            }

            setEnabledSettings(List.of(new PackageManager.ComponentEnabledSetting(appPackageName, newState, flags)),
                    userId, callingPackage);
        }

        @Override
        public void setPageSizeAppCompatFlagsSettingsOverride(String packageName, boolean enabled) {
            final int callingUid = Binder.getCallingUid();
            final int callingAppId = UserHandle.getAppId(callingUid);

            if (!PackageManagerServiceUtils.isSystemOrRoot(callingAppId)) {
                throw new SecurityException("Caller must be the system or root.");
            }

            int settingsMode = enabled
                    ? ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_ENABLED
                    : ApplicationInfo.PAGE_SIZE_APP_COMPAT_FLAG_SETTINGS_OVERRIDE_DISABLED;
            PackageStateMutator.Result result =
                    commitPackageStateMutation(
                            null,
                            packageName,
                            packageState ->
                                    packageState
                                            .setPageSizeAppCompatFlags(settingsMode));
            if (result.isSpecificPackageNull()) {
                throw new IllegalArgumentException("Unknown package: " + packageName);
            }
            scheduleWriteSettings();
        }

        @Override
        public boolean isPageSizeCompatEnabled(String packageName) {
            final int callingUid = Binder.getCallingUid();
            final int callingAppId = UserHandle.getAppId(callingUid);
            final int userId = UserHandle.getCallingUserId();

            if (!PackageManagerServiceUtils.isSystemOrRoot(callingAppId)) {
                throw new SecurityException("Caller must be the system or root.");
            }

            PackageStateInternal packageState =
                    snapshotComputer().getPackageStateForInstalledAndFiltered(
                            packageName, callingUid, userId);

            return packageState == null ? false : packageState.isPageSizeAppCompatEnabled();
        }

        @Override
        public String getPageSizeCompatWarningMessage(String packageName) {
            final int callingUid = Binder.getCallingUid();
            final int callingAppId = UserHandle.getAppId(callingUid);
            final int userId = UserHandle.getCallingUserId();

            if (!PackageManagerServiceUtils.isSystemOrRoot(callingAppId)) {
                throw new SecurityException("Caller must be the system or root.");
            }

            PackageStateInternal packageState =
                    snapshotComputer().getPackageStateForInstalledAndFiltered(
                            packageName, callingUid, userId);

            return packageState == null
                    ? null
                    : packageState.getPageSizeCompatWarningMessage(mContext);
        }

        @android.annotation.EnforcePermission(android.Manifest.permission.MANAGE_USERS)
        @Override
        public boolean setApplicationHiddenSettingAsUser(String packageName, boolean hidden,
                int userId) {
            setApplicationHiddenSettingAsUser_enforcePermission();
            final int callingUid = Binder.getCallingUid();
            final Computer snapshot = snapshotComputer();
            snapshot.enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
                    true /* checkShell */, "setApplicationHiddenSetting for user " + userId);

            if (hidden && isPackageDeviceAdmin(packageName, userId)) {
                Slog.w(TAG, "Not hiding package " + packageName + ": has active device admin");
                return false;
            }

            // Don't allow hiding "android" or SysUI as it makes device unusable.
            if ("android".equals(packageName)
                    || LocalServices.getService(PackageManagerInternal.class)
                            .getSystemUiServiceComponent().getPackageName().equals(packageName)) {
                Slog.w(TAG, "Cannot hide package: " + packageName);
                return false;
            }

            final long callingId = Binder.clearCallingIdentity();
            try {
                final PackageStateInternal packageState = snapshot.getPackageStateInternal(
                        packageName);
                if (packageState == null) {
                    return false;
                }

                final PackageUserStateInternal userState = packageState.getUserStateOrDefault(
                        userId);
                if (userState.isHidden() == hidden || !userState.isInstalled()
                        || snapshot.shouldFilterApplication(packageState, callingUid, userId)) {
                    return false;
                }

                // Cannot hide static shared libs as they are considered
                // a part of the using app (emulating static linking). Also
                // static libs are installed always on internal storage.
                AndroidPackage pkg = packageState.getPkg();
                if (pkg != null) {
                    // Cannot hide SDK libs as they are controlled by SDK manager.
                    if (pkg.getSdkLibraryName() != null) {
                        Slog.w(TAG, "Cannot hide package: " + packageName
                                + " providing SDK library: "
                                + pkg.getSdkLibraryName());
                        return false;
                    }
                    // Cannot hide static shared libs as they are considered
                    // a part of the using app (emulating static linking). Also
                    // static libs are installed always on internal storage.
                    if (pkg.getStaticSharedLibraryName() != null) {
                        Slog.w(TAG, "Cannot hide package: " + packageName
                                + " providing static shared library: "
                                + pkg.getStaticSharedLibraryName());
                        return false;
                    }
                }
                // Only allow protected packages to hide themselves.
                if (hidden && !UserHandle.isSameApp(callingUid, packageState.getAppId())
                        && mProtectedPackages.isPackageStateProtected(userId, packageName)) {
                    Slog.w(TAG, "Not hiding protected package: " + packageName);
                    return false;
                }

                commitPackageStateMutation(null, packageName, packageState1 ->
                        packageState1.userState(userId).setHidden(hidden));

                final Computer newSnapshot = snapshotComputer();
                final PackageStateInternal newPackageState =
                        newSnapshot.getPackageStateInternal(packageName);

                if (hidden) {
                    killApplication(packageName, newPackageState.getAppId(), userId, "hiding pkg",
                            ApplicationExitInfo.REASON_OTHER);
                    mBroadcastHelper.sendApplicationHiddenForUser(
                            packageName, newPackageState, userId,
                            /* packageSender= */ PackageManagerService.this);
                } else {
                    mBroadcastHelper.sendPackageAddedForUser(
                            newSnapshot, packageName, newPackageState, userId,
                            false /* isArchived */, DataLoaderType.NONE,
                            mAppPredictionServicePackage);
                }

                scheduleWritePackageRestrictions(userId);
                return true;
            } finally {
                Binder.restoreCallingIdentity(callingId);
            }
        }

        @android.annotation.EnforcePermission(android.Manifest.permission.DELETE_PACKAGES)
        @Override
        public boolean setBlockUninstallForUser(String packageName, boolean blockUninstall,
                int userId) {
            setBlockUninstallForUser_enforcePermission();
            final Computer snapshot = snapshotComputer();
            PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName);
            if (packageState != null && packageState.getPkg() != null) {
                AndroidPackage pkg = packageState.getPkg();
                // Cannot block uninstall SDK libs as they are controlled by SDK manager.
                if (pkg.getSdkLibraryName() != null) {
                    Slog.w(PackageManagerService.TAG, "Cannot block uninstall of package: " + packageName
                            + " providing SDK library: " + pkg.getSdkLibraryName());
                    return false;
                }
                // Cannot block uninstall of static shared libs as they are
                // considered a part of the using app (emulating static linking).
                // Also static libs are installed always on internal storage.
                if (pkg.getStaticSharedLibraryName() != null) {
                    Slog.w(PackageManagerService.TAG, "Cannot block uninstall of package: " + packageName
                            + " providing static shared library: " + pkg.getStaticSharedLibraryName());
                    return false;
                }
            }
            synchronized (mLock) {
                mSettings.setBlockUninstallLPw(userId, packageName, blockUninstall);
            }

            scheduleWritePackageRestrictions(userId);
            return true;
        }

        @Override
        public void setComponentEnabledSetting(ComponentName componentName,
                int newState, int flags, int userId, String callingPackage) {
            if (!mUserManager.exists(userId)) return;
            if (callingPackage == null) {
                callingPackage = Integer.toString(Binder.getCallingUid());
            }

            setEnabledSettings(List.of(new PackageManager.ComponentEnabledSetting(componentName, newState, flags)),
                    userId, callingPackage);
        }

        @Override
        public void setComponentEnabledSettings(
                List<PackageManager.ComponentEnabledSetting> settings, int userId,
                String callingPackage) {
            if (!mUserManager.exists(userId)) return;
            if (settings == null || settings.isEmpty()) {
                throw new IllegalArgumentException("The list of enabled settings is empty");
            }
            if (callingPackage == null) {
                callingPackage = Integer.toString(Binder.getCallingUid());
            }
            setEnabledSettings(settings, userId, callingPackage);
        }

        @Override
        public String[] setDistractingPackageRestrictionsAsUser(String[] packageNames,
                int restrictionFlags, int userId) {
            final int callingUid = Binder.getCallingUid();
            final Computer snapshot = snapshotComputer();
            enforceCanSetDistractingPackageRestrictionsAsUser(callingUid, userId,
                    "setDistractingPackageRestrictionsAsUser");
            Objects.requireNonNull(packageNames, "packageNames cannot be null");
            return mDistractingPackageHelper.setDistractingPackageRestrictionsAsUser(snapshot,
                    packageNames, restrictionFlags, userId, callingUid);
        }

        @Override
        public void setHarmfulAppWarning(@NonNull String packageName, @Nullable CharSequence warning,
                int userId) {
            final int callingUid = Binder.getCallingUid();
            final int callingAppId = UserHandle.getAppId(callingUid);

            final Computer snapshot = snapshotComputer();
            snapshot.enforceCrossUserPermission(callingUid, userId, true /*requireFullPermission*/,
                    true /*checkShell*/, "setHarmfulAppInfo");

            if (!PackageManagerServiceUtils.isSystemOrRoot(callingAppId)
                    && snapshot.checkUidPermission(SET_HARMFUL_APP_WARNINGS, callingUid)
                            != PERMISSION_GRANTED) {
                throw new SecurityException("Caller must have the "
                        + SET_HARMFUL_APP_WARNINGS + " permission.");
            }

            PackageStateMutator.Result result = commitPackageStateMutation(null, packageName,
                    packageState -> packageState.userState(userId)
                            .setHarmfulAppWarning(warning == null ? null : warning.toString()));
            if (result.isSpecificPackageNull()) {
                throw new IllegalArgumentException("Unknown package: " + packageName);
            }
            scheduleWritePackageRestrictions(userId);
        }

        @android.annotation.EnforcePermission(android.Manifest.permission.WRITE_SECURE_SETTINGS)
        @Override
        public boolean setInstallLocation(int loc) {
            setInstallLocation_enforcePermission();
            if (getInstallLocation() == loc) {
                return true;
            }
            if (loc == InstallLocationUtils.APP_INSTALL_AUTO
                    || loc == InstallLocationUtils.APP_INSTALL_INTERNAL
                    || loc == InstallLocationUtils.APP_INSTALL_EXTERNAL) {
                android.provider.Settings.Global.putInt(mContext.getContentResolver(),
                        android.provider.Settings.Global.DEFAULT_INSTALL_LOCATION, loc);
                return true;
            }
            return false;
        }

        @Override
        public void setInstallerPackageName(String targetPackage,
                @Nullable String installerPackageName) {
            final int callingUid = Binder.getCallingUid();
            final int callingUserId = UserHandle.getUserId(callingUid);
            final FunctionalUtils.ThrowingCheckedFunction<Computer, Boolean, RuntimeException>
                    implementation = snapshot -> {
                if (snapshot.getInstantAppPackageName(callingUid) != null) {
                    return false;
                }

                PackageStateInternal targetPackageState =
                        snapshot.getPackageStateForInstalledAndFiltered(
                                targetPackage, callingUid, callingUserId);
                if (targetPackageState == null) {
                    throw new IllegalArgumentException("Unknown target package: " + targetPackage);
                }

                PackageStateInternal installerPackageState = null;
                if (installerPackageName != null) {
                    installerPackageState = snapshot.getPackageStateForInstalledAndFiltered(
                            installerPackageName, callingUid, callingUserId);
                    if (installerPackageState == null) {
                        throw new IllegalArgumentException("Unknown installer package: "
                                + installerPackageName);
                    }
                }

                SigningDetails callerSigningDetails;
                final int appId = UserHandle.getAppId(callingUid);
                Pair<PackageStateInternal, SharedUserApi> either =
                        snapshot.getPackageOrSharedUser(appId);
                if (either != null) {
                    if (either.first != null) {
                        callerSigningDetails = either.first.getSigningDetails();
                    } else {
                        callerSigningDetails = either.second.getSigningDetails();
                    }
                } else {
                    throw new SecurityException("Unknown calling UID: " + callingUid);
                }

                // Verify: can't set installerPackageName to a package that is
                // not signed with the same cert as the caller.
                if (installerPackageState != null) {
                    if (compareSignatures(callerSigningDetails,
                            installerPackageState.getSigningDetails())
                            != PackageManager.SIGNATURE_MATCH) {
                        throw new SecurityException(
                                "Caller does not have same cert as new installer package "
                                        + installerPackageName);
                    }
                }

                // Verify: if target already has an installer package, it must
                // be signed with the same cert as the caller.
                String targetInstallerPackageName =
                        targetPackageState.getInstallSource().mInstallerPackageName;
                PackageStateInternal targetInstallerPkgSetting = targetInstallerPackageName == null
                        ? null : snapshot.getPackageStateInternal(targetInstallerPackageName);

                if (targetInstallerPkgSetting != null) {
                    if (compareSignatures(callerSigningDetails,
                            targetInstallerPkgSetting.getSigningDetails())
                            != PackageManager.SIGNATURE_MATCH) {
                        throw new SecurityException(
                                "Caller does not have same cert as old installer package "
                                        + targetInstallerPackageName);
                    }
                } else if (mContext.checkCallingOrSelfPermission(
                        Manifest.permission.INSTALL_PACKAGES) != PERMISSION_GRANTED) {
                    // This is probably an attempt to exploit vulnerability b/150857253 of taking
                    // privileged installer permissions when the installer has been uninstalled or
                    // was never set.
                    EventLog.writeEvent(0x534e4554, "150857253", callingUid, "");

                    final long binderToken = Binder.clearCallingIdentity();
                    try {
                        if (mInjector.getCompatibility().isChangeEnabledByUid(
                                PackageManagerService.THROW_EXCEPTION_ON_REQUIRE_INSTALL_PACKAGES_TO_ADD_INSTALLER_PACKAGE,
                                callingUid)) {
                            throw new SecurityException("Neither user " + callingUid
                                    + " nor current process has "
                                    + Manifest.permission.INSTALL_PACKAGES);
                        } else {
                            // If change disabled, fail silently for backwards compatibility
                            return false;
                        }
                    } finally {
                        Binder.restoreCallingIdentity(binderToken);
                    }
                }

                return true;
            };
            PackageStateMutator.InitialState initialState = recordInitialState();
            boolean allowed = implementation.apply(snapshotComputer());
            if (allowed) {
                // TODO: Need to lock around here to handle mSettings.addInstallerPackageNames,
                //  should find an alternative which avoids any race conditions
                final int installerPackageUid = installerPackageName == null
                        ? INVALID_UID : snapshotComputer().getPackageUid(installerPackageName,
                        0 /* flags */, callingUserId);
                PackageStateInternal targetPackageState;
                synchronized (mLock) {
                    PackageStateMutator.Result result = commitPackageStateMutation(initialState,
                            targetPackage, state -> state.setInstaller(installerPackageName,
                                    installerPackageUid));
                    if (result.isPackagesChanged() || result.isStateChanged()) {
                        synchronized (mPackageStateWriteLock) {
                            allowed = implementation.apply(snapshotComputer());
                            if (allowed) {
                                commitPackageStateMutation(null, targetPackage,
                                        state -> state.setInstaller(installerPackageName,
                                                installerPackageUid));
                            } else {
                                return;
                            }
                        }
                    }
                    targetPackageState = snapshotComputer().getPackageStateInternal(targetPackage);
                    mSettings.addInstallerPackageNames(targetPackageState.getInstallSource());
                }
                mAppsFilter.addPackage(snapshotComputer(), targetPackageState);
                scheduleWriteSettings();
            }
        }

        @Override
        public void relinquishUpdateOwnership(String targetPackage) {
            final int callingUid = Binder.getCallingUid();
            final int callingUserId = UserHandle.getUserId(callingUid);
            final Computer snapshot = snapshotComputer();

            final PackageStateInternal targetPackageState =
                    snapshot.getPackageStateForInstalledAndFiltered(targetPackage, callingUid,
                            callingUserId);
            if (targetPackageState == null) {
                throw new IllegalArgumentException("Unknown target package: " + targetPackage);
            }

            final String targetUpdateOwnerPackageName =
                    targetPackageState.getInstallSource().mUpdateOwnerPackageName;
            final PackageStateInternal targetUpdateOwnerPkgSetting =
                    targetUpdateOwnerPackageName == null ? null
                            : snapshot.getPackageStateInternal(targetUpdateOwnerPackageName);

            if (targetUpdateOwnerPkgSetting == null) {
                return;
            }

            final int callingAppId = UserHandle.getAppId(callingUid);
            final int targetUpdateOwnerAppId = targetUpdateOwnerPkgSetting.getAppId();
            if (callingAppId != Process.SYSTEM_UID
                    && callingAppId != Process.SHELL_UID
                    && callingAppId != targetUpdateOwnerAppId) {
                throw new SecurityException("Caller is not the current update owner.");
            }

            commitPackageStateMutation(null /* initialState */, targetPackage,
                    state -> state.setUpdateOwner(null /* updateOwnerPackageName */));
            scheduleWriteSettings();
        }

        @Override
        public boolean setInstantAppCookie(String packageName, byte[] cookie, int userId) {
            if (HIDE_EPHEMERAL_APIS) {
                return true;
            }

            final Computer snapshot = snapshotComputer();
            snapshot.enforceCrossUserPermission(Binder.getCallingUid(), userId,
                    true /* requireFullPermission */, true /* checkShell */,
                    "setInstantAppCookie");
            if (!snapshot.isCallerSameApp(packageName, Binder.getCallingUid())) {
                return false;
            }

            PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName);
            if (packageState == null || packageState.getPkg() == null) {
                return false;
            }
            return mInstantAppRegistry.setInstantAppCookie(packageState.getPkg(), cookie,
                    mContext.getPackageManager().getInstantAppCookieMaxBytes(), userId);
        }

        @Override
        public void setKeepUninstalledPackages(List<String> packageList) {
            mContext.enforceCallingPermission(
                    Manifest.permission.KEEP_UNINSTALLED_PACKAGES,
                    "setKeepUninstalledPackages requires KEEP_UNINSTALLED_PACKAGES permission");
            Objects.requireNonNull(packageList);

            setKeepUninstalledPackagesInternal(snapshot(), packageList);
        }

        @Override
        public void setMimeGroup(String packageName, String mimeGroup, List<String> mimeTypes) {
            final Computer snapshot = snapshotComputer();
            final int callingUid = Binder.getCallingUid();
            enforceOwnerRights(snapshot, packageName, callingUid);
            mimeTypes = CollectionUtils.emptyIfNull(mimeTypes);
            for (int i = 0; i < mimeTypes.size(); i++) {
                if (mimeTypes.get(i).length() > 255) {
                    throw new IllegalArgumentException("MIME type length exceeds 255 characters");
                }
            }
            final PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName);
            Set<String> existingMimeTypes = packageState.getMimeGroups().get(mimeGroup);
            if (existingMimeTypes == null) {
                throw new IllegalArgumentException("Unknown MIME group " + mimeGroup
                        + " for package " + packageName);
            }
            if (existingMimeTypes.size() == mimeTypes.size()
                    && existingMimeTypes.containsAll(mimeTypes)) {
                return;
            }
            if (mimeTypes.size() > 500) {
                throw new IllegalStateException("Max limit on MIME types for MIME group "
                        + mimeGroup + " exceeded for package " + packageName);
            }

            ArraySet<String> mimeTypesSet = new ArraySet<>(mimeTypes);
            commitPackageStateMutation(null, packageName, packageStateWrite -> {
                packageStateWrite.setMimeGroup(mimeGroup, mimeTypesSet);
            });
            if (mComponentResolver.updateMimeGroup(snapshotComputer(), packageName, mimeGroup)) {
                Binder.withCleanCallingIdentity(() -> {
                    mPreferredActivityHelper.clearPackagePreferredActivities(packageName,
                            USER_ALL);
                    // Send the ACTION_PACKAGE_CHANGED when the mimeGroup has changes
                    final Computer snapShot = snapshotComputer();
                    final ArrayList<String> components = new ArrayList<>(
                            Collections.singletonList(packageName));
                    final int appId = packageState.getAppId();
                    final int[] userIds = resolveUserIds(USER_ALL);
                    final String reason = "The mimeGroup is changed";
                    for (int i = 0; i < userIds.length; i++) {
                        final PackageUserStateInternal pkgUserState =
                                packageState.getUserStates().get(userIds[i]);
                        if (pkgUserState != null && pkgUserState.isInstalled()) {
                            final int packageUid = UserHandle.getUid(userIds[i], appId);
                            mBroadcastHelper.sendPackageChangedBroadcast(snapShot, packageName,
                                    true /* dontKillApp */, components, packageUid, reason,
                                    PackageMetrics.STRING_MIME_GROUP_CHANGED, callingUid);
                        }
                    }
                });
            }

            scheduleWriteSettings();
        }

        @Override
        public void setPackageStoppedState(String packageName, boolean stopped, int userId) {
            PackageManagerService.this
                    .setPackageStoppedState(snapshotComputer(), packageName, stopped, userId);
        }

        @Override
        public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
                PersistableBundle appExtras, PersistableBundle launcherExtras,
                SuspendDialogInfo dialogInfo, int flags, String suspendingPackage,
                int suspendingUserId, int targetUserId) {
            final int callingUid = Binder.getCallingUid();
            final boolean quarantined = ((flags & PackageManager.FLAG_SUSPEND_QUARANTINED) != 0)
                    && Flags.quarantinedEnabled();
            final Computer snapshot = snapshotComputer();
            final UserPackage suspender = crossUserSuspensionEnabledRo()
                    ? UserPackage.of(suspendingUserId, suspendingPackage)
                    : UserPackage.of(targetUserId, suspendingPackage);
            enforceCanSetPackagesSuspendedAsUser(snapshot, quarantined, suspender, callingUid,
                    targetUserId, "setPackagesSuspendedAsUser");
            return mSuspendPackageHelper.setPackagesSuspended(snapshot, packageNames, suspended,
                    appExtras, launcherExtras, dialogInfo, suspender, targetUserId, callingUid,
                    quarantined);
        }

        @Override
        public boolean setRequiredForSystemUser(String packageName, boolean requiredForSystemUser) {
            PackageManagerServiceUtils.enforceSystemOrRoot(
                    "setRequiredForSystemUser can only be run by the system or root");

            PackageStateMutator.Result result = commitPackageStateMutation(null, packageName,
                    packageState -> packageState.setRequiredForSystemUser(requiredForSystemUser));
            if (!result.isCommitted()) {
                return false;
            }

            scheduleWriteSettings();
            return true;
        }

        @android.annotation.EnforcePermission(android.Manifest.permission.INSTALL_PACKAGES)
        @Override
        public void setUserMinAspectRatio(@NonNull String packageName, int userId,
                @PackageManager.UserMinAspectRatio int aspectRatio) {
            setUserMinAspectRatio_enforcePermission();
            final int callingUid = Binder.getCallingUid();
            final Computer snapshot = snapshotComputer();
            snapshot.enforceCrossUserPermission(callingUid, userId,
                    false /* requireFullPermission */, false /* checkShell */,
                    "setUserMinAspectRatio");
            enforceOwnerRights(snapshot, packageName, callingUid);

            final PackageStateInternal packageState = snapshot
                    .getPackageStateForInstalledAndFiltered(packageName, callingUid, userId);
            if (packageState == null) {
                if (com.android.window.flags.Flags.restoreUserAspectRatioSettingsUsingService()) {
                    Slog.d(TAG, "Package: " + packageName + " not yet installed. "
                            + "Scheduling aspect ratio update upon install, for aspect ratio: "
                            + aspectRatio);
                    // Pass along the request to `AppWindowLayoutSettingsService`, which will retry
                    // to set the user aspect ratio after the package has been installed.
                    final AppWindowLayoutSettingsService appWindowLayoutSettingsService =
                            LocalServices.getService(AppWindowLayoutSettingsService.class);
                    if (appWindowLayoutSettingsService == null) {
                        Slog.w(TAG, "Could not find AppWindowLayoutSettingsService.");
                        return;
                    }
                    // TODO(b/414381398): expose this API to the Settings app to call directly, so
                    //  that `setUserMinAspectRatio()` becomes a no-op when app is not installed.
                    appWindowLayoutSettingsService.awaitPackageInstallForAspectRatio(packageName,
                            userId, aspectRatio);
                }
                return;
            }

            if (packageState.getUserStateOrDefault(userId).getMinAspectRatio() == aspectRatio) {
                return;
            }

            commitPackageStateMutation(null, packageName, state ->
                    state.userState(userId).setMinAspectRatio(aspectRatio));
        }

        @Override
        @SuppressWarnings("GuardedBy")
        public void setRuntimePermissionsVersion(int version, @UserIdInt int userId) {
            Preconditions.checkArgumentNonnegative(version);
            Preconditions.checkArgumentNonnegative(userId);
            enforceAdjustRuntimePermissionsPolicyOrUpgradeRuntimePermissions(
                    "setRuntimePermissionVersion");
            mSettings.setDefaultRuntimePermissionsVersion(version, userId);
        }

        @Override
        public void setSplashScreenTheme(@NonNull String packageName, @Nullable String themeId,
                int userId) {
            final int callingUid = Binder.getCallingUid();
            final Computer snapshot = snapshotComputer();
            snapshot.enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
                    false /* checkShell */, "setSplashScreenTheme");
            enforceOwnerRights(snapshot, packageName, callingUid);

            PackageStateInternal packageState = snapshot.getPackageStateForInstalledAndFiltered(
                    packageName, callingUid, userId);
            if (packageState == null) {
                return;
            }

            commitPackageStateMutation(null, packageName, state ->
                    state.userState(userId).setSplashScreenTheme(themeId));
        }

        @android.annotation.EnforcePermission(android.Manifest.permission.INSTALL_PACKAGES)
        @Override
        public void setUpdateAvailable(String packageName, boolean updateAvailable) {
            setUpdateAvailable_enforcePermission();
            commitPackageStateMutation(null, packageName, state ->
                    state.setUpdateAvailable(updateAvailable));
        }

        @android.annotation.EnforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS)
        @Override
        public void unregisterMoveCallback(IPackageMoveObserver callback) {
            unregisterMoveCallback_enforcePermission();
            mMoveCallbacks.unregister(callback);
        }

        @Override
        public void verifyPendingInstall(int verificationId, int verificationCode)
                throws RemoteException {
            // Negative ids correspond to testing verifiers and will be silently enforced in
            // the handler thread.
            if (verificationId >= 0) {
                mContext.enforceCallingOrSelfPermission(
                        Manifest.permission.PACKAGE_VERIFICATION_AGENT,
                        "Only package verification agents can verify applications");
            }
            final int callingUid = Binder.getCallingUid();

            mHandler.post(() -> {
                final int id = verificationId >= 0 ? verificationId : -verificationId;
                final PackageVerificationState state = mPendingVerification.get(id);
                if (state == null) {
                    return;
                }
                if (!state.checkRequiredVerifierUid(callingUid)
                        && !state.checkSufficientVerifierUid(callingUid)) {
                    // Only allow calls from verifiers.
                    return;
                }

                final Message msg = mHandler.obtainMessage(PackageManagerService.PACKAGE_VERIFIED);
                final PackageVerificationResponse response = new PackageVerificationResponse(
                        verificationCode, callingUid);
                msg.arg1 = id;
                msg.obj = response;
                mHandler.sendMessage(msg);
            });
        }

        @Override
        public void registerPackageMonitorCallback(@NonNull IRemoteCallback callback, int userId) {
            int uid = Binder.getCallingUid();
            int targetUserId = ActivityManager.handleIncomingUser(Binder.getCallingPid(), uid,
                    userId, true, true, "registerPackageMonitorCallback",
                    mContext.getPackageName());
            mPackageMonitorCallbackHelper.registerPackageMonitorCallback(callback, targetUserId,
                    uid);
        }

        @Override
        public void unregisterPackageMonitorCallback(@NonNull IRemoteCallback callback) {
            mPackageMonitorCallbackHelper.unregisterPackageMonitorCallback(callback);
        }

        @Override
        public void requestPackageChecksums(@NonNull String packageName, boolean includeSplits,
                @Checksum.TypeMask int optional, @Checksum.TypeMask int required,
                @Nullable List trustedInstallers,
                @NonNull IOnChecksumsReadyListener onChecksumsReadyListener, int userId) {
            requestChecksumsInternal(snapshotComputer(), packageName, includeSplits, optional,
                    required, trustedInstallers, onChecksumsReadyListener, userId,
                    mInjector.getBackgroundExecutor(), mInjector.getBackgroundHandler());
        }

        @Override
        public void notifyPackagesReplacedReceived(String[] packages) {
            Computer computer = snapshotComputer();
            ArraySet<String> packagesToNotify = computer.getNotifyPackagesForReplacedReceived(packages);
            for (int index = 0; index < packagesToNotify.size(); index++) {
                notifyInstallObserver(packagesToNotify.valueAt(index), false /* killApp */);
            }
        }

        @Override
        public ArchivedPackageParcel getArchivedPackage(@NonNull String packageName, int userId) {
            return getArchivedPackageInternal(packageName, userId);
        }

        @Override
        public Bitmap getArchivedAppIcon(@NonNull String packageName, @NonNull UserHandle user,
                @NonNull String callingPackageName) {
            return mInstallerService.mPackageArchiver.getArchivedAppIcon(packageName, user,
                    callingPackageName);
        }

        @Override
        public boolean isAppArchivable(@NonNull String packageName, @NonNull UserHandle user) {
            return mInstallerService.mPackageArchiver.isAppArchivable(packageName, user);
        }

        /**
         * Wait for the handler to finish handling all pending messages.
         * @param timeoutMillis Maximum time in milliseconds to wait.
         * @param forBackgroundHandler Whether to wait for the background handler instead.
         * @return True if all the waiting messages in the handler has been handled.
         *         False if timeout.
         */
        @Override
        public boolean waitForHandler(long timeoutMillis, boolean forBackgroundHandler) {
            final CountDownLatch latch = new CountDownLatch(1);
            if (forBackgroundHandler) {
                mBackgroundHandler.post(latch::countDown);
            } else {
                mHandler.post(latch::countDown);
            }
            final long endTimeMillis = System.currentTimeMillis() + timeoutMillis;
            while (latch.getCount() > 0) {
                try {
                    final long remainingTimeMillis = endTimeMillis - System.currentTimeMillis();
                    if (remainingTimeMillis <= 0) {
                        return false;
                    }
                    return latch.await(remainingTimeMillis, TimeUnit.MILLISECONDS);
                } catch (InterruptedException e) {
                    // ignore and retry
                }
            }
            return true;
        }

        @Override
        @Nullable
        public ComponentName getDomainVerificationAgent(int userId) {
            final int callerUid = Binder.getCallingUid();
            if (!PackageManagerServiceUtils.isRootOrShell(callerUid)) {
                throw new SecurityException("Not allowed to query domain verification agent");
            }
            final Computer snapshot = snapshotComputer();
            final ComponentName agent = mDomainVerificationManager.getProxy().getComponentName();
            final PackageStateInternal ps = snapshot.getPackageStateForInstalledAndFiltered(
                    agent.getPackageName(), callerUid, userId);
            if (ps == null) {
                return null;
            }
            final var disabledComponents =
                    ps.getUserStateOrDefault(userId).getDisabledComponentsNoCopy();
            if (disabledComponents != null && disabledComponents.contains(agent.getClassName())) {
                return null;
            }
            // Only return the result if the agent is installed and enabled on the target user
            return agent;
        }

        @Override
        @NonNull
        public List<String> getAllApexDirectories() {
            PackageManagerServiceUtils.enforceSystemOrRoot(
                    "getAllApexDirectories can only be called by system or root");
            List<String> apexDirectories = new ArrayList<>();
            List<ApexManager.ActiveApexInfo> apexes = mApexManager.getActiveApexInfos();
            for (int i = 0; i < apexes.size(); i++) {
                ApexManager.ActiveApexInfo apex = apexes.get(i);
                apexDirectories.add(apex.apexDirectory.getAbsolutePath());
            }
            return apexDirectories;
        }

        @Override
        public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
                throws RemoteException {
            try {
                return super.onTransact(code, data, reply, flags);
            } catch (RuntimeException e) {
                if (!(e instanceof SecurityException) && !(e instanceof IllegalArgumentException)
                        && !(e instanceof ParcelableException)) {
                    Slog.wtf(TAG, "Package Manager Unexpected Exception", e);
                }
                throw e;
            }
        }

        @Override
        public void onShellCommand(FileDescriptor in, FileDescriptor out,
                FileDescriptor err, String[] args, ShellCallback callback,
                ResultReceiver resultReceiver) {
            (new PackageManagerShellCommand(this, mContext,
                    mDomainVerificationManager.getShell()))
                    .exec(this, in, out, err, args, callback, resultReceiver);
        }

        @SuppressWarnings("resource")
        @Override
        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
            if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
            final Computer snapshot = snapshotComputer();
            final KnownPackages knownPackages = new KnownPackages(
                    mDefaultAppProvider,
                    mRequiredInstallerPackage,
                    mRequiredUninstallerPackage,
                    mSetupWizardPackage,
                    mRequiredVerifierPackages,
                    mDefaultTextClassifierPackage,
                    mSystemTextClassifierPackageName,
                    mRequiredPermissionControllerPackage,
                    mConfiguratorPackage,
                    mIncidentReportApproverPackage,
                    mAmbientContextDetectionPackage,
                    mWearableSensingPackage,
                    mAppPredictionServicePackage,
                    COMPANION_PACKAGE_NAME,
                    mRetailDemoPackage,
                    mOverlayConfigSignaturePackage,
                    mRecentsPackage,
                    mDeveloperVerificationServiceProvider == null
                            ? null : mDeveloperVerificationServiceProvider.getPackageName());
            final ArrayMap<String, FeatureInfo> availableFeatures;
            availableFeatures = new ArrayMap<>(mAvailableFeatures);
            final ArraySet<String> protectedBroadcasts;
            synchronized (mProtectedBroadcasts) {
                protectedBroadcasts = new ArraySet<>(mProtectedBroadcasts);
            }
            new DumpHelper(mPermissionManager, mStorageEventHelper,
                    mDomainVerificationManager, mInstallerService, mRequiredVerifierPackages,
                    knownPackages, mChangedPackagesTracker, availableFeatures, protectedBroadcasts,
                    getPerUidReadTimeouts(snapshot), mSnapshotStatistics
            ).doDump(snapshot, fd, pw, args);
        }

        @Nullable
        @Override
        public Bundle getExtraAppBindArgs(String packageName) {
            return PackageManagerHooks.getExtraAppBindArgs(PackageManagerService.this, packageName);
        }

        @Override
        public GosPackageState getGosPackageState(@NonNull String packageName, int userId) {
            int callingUid = Binder.getCallingUid();
            int callingPid = Binder.getCallingPid();
            return GosPackageStatePmHooks.getFiltered(PackageManagerService.this, callingUid, callingPid, packageName, userId);
        }

        @Override
        public boolean setGosPackageState(@NonNull String packageName, int userId,
                                                  @NonNull GosPackageState updatedPs, int editorFlags) {
            int callingUid = Binder.getCallingUid();
            int callingPid = Binder.getCallingPid();
            return GosPackageStatePmHooks.set(PackageManagerService.this, callingUid, callingPid, packageName, userId,
                    updatedPs, editorFlags);
        }

        // Allow privileged installer to search for packages across all users to let it avoid
        // redownloading APKs for packages that are already installed in other user profiles,
        // and to avoid potential downgrade errors (when other user has a newer package version).

        // Note that even without this method, package installers (including unprivileged ones) can
        // detect the presence and version code of a package that is not installed for the current user
        // by looking at error codes that PackageManager returns after attempt to install a package
        // with the same package name. Such packages can be generated at runtime by the installer.
        @Override
        public PackageInfo findPackage(String packageName, long minVersion, Bundle validSignaturesSha256) {
            mContext.enforceCallingPermission(Manifest.permission.INSTALL_PACKAGES, null);

            PackageStateInternal psi = snapshot().getPackageStateInternal(packageName);
            if (psi == null) {
                return null;
            }

            AndroidPackage pkg = psi.getPkg();

            if (pkg == null) {
                return null;
            }

            long version = pkg.getLongVersionCode();

            if (version < minVersion) {
                return null;
            }

            // no simple way to pass byte[][] array directly due to AIDL limitation
            final int numSignatures = validSignaturesSha256.getInt("len");

            boolean signatureMatch = false;

            for (int i = 0; i < numSignatures; ++i) {
                byte[] signatureSha256 = validSignaturesSha256.getByteArray(Integer.toString(i));
                if (pkg.getSigningDetails().hasSha256Certificate(signatureSha256)) {
                    signatureMatch = true;
                    break;
                }
            }

            if (!signatureMatch) {
                return null;
            }

            var pi = new PackageInfo();
            pi.setLongVersionCode(version);
            pi.versionName = pkg.getVersionName();
            pi.applicationInfo = new ApplicationInfo();
            pi.applicationInfo.setBaseCodePath(pkg.getBaseApkPath());
            pi.applicationInfo.setSplitCodePaths(pkg.getSplitCodePaths());

            if (psi.isSystem()) {
                pi.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
            }
            return pi;
        }

        private final PrivilegedInstallerHelper privInstallerHelper =
                new PrivilegedInstallerHelper(PackageManagerService.this);

        @Override
        public boolean updateListOfBusyPackages(boolean add, List<String> packageNames, IBinder callerBinder) {
            return privInstallerHelper.updateListOfBusyPackages(add, packageNames, callerBinder);
        }

        @Override
        public void updateSeInfo(String packageName) {
            SeInfoOverride.updateSeInfo(PackageManagerService.this, packageName);
        }

        @Override
        public void sendBootCompletedBroadcastToPackage(String packageName, boolean includeStopped,
                                                    int userId) {
            mContext.enforceCallingPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS, null);

            if (userId != UserHandle.getUserId(Binder.getCallingUid())) {
                throw new SecurityException("userId mismatch");
            }

            final long token = Binder.clearCallingIdentity();
            try {
                mBroadcastHelper.sendBootCompletedBroadcastToSystemApp(packageName, includeStopped,
                        userId);
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }
    }

    private class PackageManagerInternalImpl extends PackageManagerInternalBase {

        public PackageManagerInternalImpl() {
            super(PackageManagerService.this);
        }

        @NonNull
        @Override
        protected Context getContext() {
            return mContext;
        }

        @NonNull
        @Override
        protected PermissionManagerServiceInternal getPermissionManager() {
            return mPermissionManager;
        }

        @NonNull
        @Override
        protected AppDataHelper getAppDataHelper() {
            return mAppDataHelper;
        }

        @NonNull
        @Override
        protected PackageObserverHelper getPackageObserverHelper() {
            return mPackageObserverHelper;
        }

        @NonNull
        @Override
        protected ResolveIntentHelper getResolveIntentHelper() {
            return mResolveIntentHelper;
        }

        @NonNull
        @Override
        protected SuspendPackageHelper getSuspendPackageHelper() {
            return mSuspendPackageHelper;
        }

        @NonNull
        @Override
        protected DistractingPackageHelper getDistractingPackageHelper() {
            return mDistractingPackageHelper;
        }

        @NonNull
        @Override
        protected ProtectedPackages getProtectedPackages() {
            return mProtectedPackages;
        }

        @NonNull
        @Override
        protected UserNeedsBadgingCache getUserNeedsBadging() {
            return mUserNeedsBadging;
        }

        @NonNull
        @Override
        protected InstantAppRegistry getInstantAppRegistry() {
            return mInstantAppRegistry;
        }

        @NonNull
        @Override
        protected ApexManager getApexManager() {
            return mApexManager;
        }

        @NonNull
        @Override
        public DynamicCodeLogger getDynamicCodeLogger() {
            return mDynamicCodeLogger;
        }

        @Override
        public boolean isPlatformSigned(String packageName) {
            PackageStateInternal packageState = snapshot().getPackageStateInternal(packageName);
            if (packageState == null) {
                return false;
            }
            SigningDetails signingDetails = packageState.getSigningDetails();
            return signingDetails.hasAncestorOrSelf(mPlatformPackage.getSigningDetails())
                    || mPlatformPackage.getSigningDetails().checkCapability(signingDetails,
                    SigningDetails.CertCapabilities.PERMISSION);
        }

        @Override
        public boolean isDataRestoreSafe(byte[] restoringFromSigHash, String packageName) {
            final Computer snapshot = snapshot();
            SigningDetails sd = snapshot.getSigningDetails(packageName);
            if (sd == null) {
                return false;
            }
            return sd.hasSha256Certificate(restoringFromSigHash,
                    SigningDetails.CertCapabilities.INSTALLED_DATA);
        }

        @Override
        public boolean isDataRestoreSafe(Signature restoringFromSig, String packageName) {
            final Computer snapshot = snapshot();
            SigningDetails sd = snapshot.getSigningDetails(packageName);
            if (sd == null) {
                return false;
            }
            return sd.hasCertificate(restoringFromSig,
                    SigningDetails.CertCapabilities.INSTALLED_DATA);
        }

        @Override
        public boolean hasSignatureCapability(int serverUid, int clientUid,
                @SigningDetails.CertCapabilities int capability) {
            final Computer snapshot = snapshot();
            SigningDetails serverSigningDetails = snapshot.getSigningDetails(serverUid);
            SigningDetails clientSigningDetails = snapshot.getSigningDetails(clientUid);
            return serverSigningDetails.checkCapability(clientSigningDetails, capability)
                    || clientSigningDetails.hasAncestorOrSelf(serverSigningDetails);
        }

        @Override
        public PackageList getPackageList(@Nullable PackageListObserver observer) {
            final ArrayList<String> list = new ArrayList<>();
            PackageManagerService.this.forEachPackageState(snapshot(), packageState -> {
                AndroidPackage pkg = packageState.getPkg();
                if (pkg != null) {
                    list.add(pkg.getPackageName());
                }
            });
            final PackageList packageList = new PackageList(list, observer);
            if (observer != null) {
                mPackageObserverHelper.addObserver(packageList);
            }
            return packageList;
        }

        @Override
        public @Nullable
        String getDisabledSystemPackageName(@NonNull String packageName) {
            PackageStateInternal disabledPkgSetting = snapshot().getDisabledSystemPackage(
                    packageName);
            AndroidPackage disabledPkg = disabledPkgSetting == null
                    ? null : disabledPkgSetting.getPkg();
            return disabledPkg == null ? null : disabledPkg.getPackageName();
        }

        @Override
        public boolean isResolveActivityComponent(ComponentInfo component) {
            return mResolveActivity.packageName.equals(component.packageName)
                    && mResolveActivity.name.equals(component.name);
        }

        @Override
        public long getCeDataInode(String packageName, int userId) {
            final PackageStateInternal packageState =
                    snapshot().getPackageStateInternal(packageName);
            if (packageState == null) {
                return 0;
            } else {
                return packageState.getUserStateOrDefault(userId).getCeDataInode();
            }
        }

        @Override
        public void removeAllNonSystemPackageSuspensions(int userId) {
            final Computer computer = snapshotComputer();
            final String[] allPackages = computer.getAllAvailablePackageNames();
            mSuspendPackageHelper.removeSuspensionsBySuspendingPackage(computer, allPackages,
                    (suspender) -> !PLATFORM_PACKAGE_NAME.equals(suspender.packageName),
                    userId);
        }

        @Override
        public void flushPackageRestrictions(int userId) {
            synchronized (mLock) {
                PackageManagerService.this.flushPackageRestrictionsAsUserInternalLocked(userId);
            }
        }

        @Override
        public String[] setPackagesSuspendedByAdmin(
                @UserIdInt int userId, @NonNull String[] packageNames, boolean suspended) {
            // Suspension by admin isn't attributed to admin package but to the platform,
            // Using USER_SYSTEM for consistency with other internal suspenders, like shell or root.
            final int suspendingUserId =
                    crossUserSuspensionEnabledRo() ? UserHandle.USER_SYSTEM : userId;
            final UserPackage suspender = UserPackage.of(
                    suspendingUserId, PackageManagerService.PLATFORM_PACKAGE_NAME);
            return mSuspendPackageHelper.setPackagesSuspended(snapshotComputer(), packageNames,
                    suspended, null /* appExtras */, null /* launcherExtras */,
                    null /* dialogInfo */, suspender, userId, Process.SYSTEM_UID,
                    false /* quarantined */);
        }

        @Override
        public void setDeviceAndProfileOwnerPackages(
                int deviceOwnerUserId, String deviceOwnerPackage,
                SparseArray<String> profileOwnerPackages) {
            mProtectedPackages.setDeviceAndProfileOwnerPackages(
                    deviceOwnerUserId, deviceOwnerPackage, profileOwnerPackages);
            final ArraySet<Integer> usersWithPoOrDo = new ArraySet<>();
            if (deviceOwnerPackage != null) {
                usersWithPoOrDo.add(deviceOwnerUserId);
            }
            final int sz = profileOwnerPackages.size();
            for (int i = 0; i < sz; i++) {
                if (profileOwnerPackages.valueAt(i) != null) {
                    removeAllNonSystemPackageSuspensions(profileOwnerPackages.keyAt(i));
                }
            }
        }

        @Override
        public void setExternalSourcesPolicy(ExternalSourcesPolicy policy) {
            if (policy != null) {
                mExternalSourcesPolicy = policy;
            }
        }

        @Override
        public boolean isPackagePersistent(String packageName) {
            final PackageStateInternal packageState =
                    snapshot().getPackageStateInternal(packageName);
            if (packageState == null) {
                return false;
            }

            AndroidPackage pkg = packageState.getPkg();
            return pkg != null && packageState.isSystem() && pkg.isPersistent();
        }

        @Override
        public List<PackageInfo> getOverlayPackages(int userId) {
            final Computer snapshot = snapshotComputer();
            final ArrayList<PackageInfo> overlayPackages = new ArrayList<>();
            final ArrayMap<String, ? extends PackageStateInternal> packageStates =
                    snapshot.getPackageStates();
            for (int index = 0; index < packageStates.size(); index++) {
                final PackageStateInternal packageState = packageStates.valueAt(index);
                final AndroidPackage pkg = packageState.getPkg();
                if (pkg != null && pkg.getOverlayTarget() != null) {
                    PackageInfo pkgInfo = snapshot.generatePackageInfo(packageState, 0, userId);
                    if (pkgInfo != null) {
                        overlayPackages.add(pkgInfo);
                    }
                }
            }

            return overlayPackages;
        }

        @Override
        public List<String> getTargetPackageNames(int userId) {
            List<String> targetPackages = new ArrayList<>();
            PackageManagerService.this.forEachPackageState(snapshot(), packageState -> {
                final AndroidPackage pkg = packageState.getPkg();
                if (pkg != null && !pkg.isResourceOverlay()) {
                    targetPackages.add(pkg.getPackageName());
                }
            });
            return targetPackages;
        }

        @Override
        public void setEnabledOverlayPackages(int userId,
                @NonNull ArrayMap<String, OverlayPaths> pendingChanges,
                @NonNull Set<String> outUpdatedPackageNames,
                @NonNull Set<String> outInvalidPackageNames) {
            PackageManagerService.this.setEnabledOverlayPackages(userId,
                    pendingChanges, outUpdatedPackageNames, outInvalidPackageNames);
        }

        @Override
        public void addIsolatedUid(int isolatedUid, int ownerUid) {
            synchronized (mLock) {
                mIsolatedOwners.put(isolatedUid, ownerUid);
            }
        }

        @Override
        public void removeIsolatedUid(int isolatedUid) {
            synchronized (mLock) {
                mIsolatedOwners.delete(isolatedUid);
            }
        }

        @Override
        public void notifyPackageUse(String packageName, int reason) {
            PackageManagerService.this.notifyPackageUseInternal(packageName, reason);
        }

        @Nullable
        @Override
        public String removeLegacyDefaultBrowserPackageName(int userId) {
            synchronized (mLock) {
                return mSettings.removePendingDefaultBrowserLPw(userId);
            }
        }

        @Override
        public void uninstallApex(String packageName, long versionCode, int userId,
                IntentSender intentSender, int flags) {
            final int callerUid = Binder.getCallingUid();
            if (!PackageManagerServiceUtils.isRootOrShell(callerUid)) {
                throw new SecurityException("Not allowed to uninstall apexes");
            }
            PackageInstallerService.PackageDeleteObserverAdapter adapter =
                    new PackageInstallerService.PackageDeleteObserverAdapter(
                            PackageManagerService.this.mContext, intentSender, packageName,
                            false, userId);
            if ((flags & PackageManager.DELETE_ALL_USERS) == 0) {
                adapter.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_ABORTED,
                        "Can't uninstall an apex for a single user");
                return;
            }
            final ApexManager am = PackageManagerService.this.mApexManager;
            PackageInfo activePackage = snapshot().getPackageInfo(
                    packageName, PackageManager.MATCH_APEX, UserHandle.USER_SYSTEM);
            if (activePackage == null) {
                adapter.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_ABORTED,
                        packageName + " is not an apex package");
                return;
            }
            if (versionCode != PackageManager.VERSION_CODE_HIGHEST
                    && activePackage.getLongVersionCode() != versionCode) {
                adapter.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_ABORTED,
                        "Active version " + activePackage.getLongVersionCode()
                                + " is not equal to " + versionCode + "]");
                return;
            }
            if (!am.uninstallApex(activePackage.applicationInfo.sourceDir)) {
                adapter.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_ABORTED,
                        "Failed to uninstall apex " + packageName);
            } else {
                adapter.onPackageDeleted(packageName, PackageManager.DELETE_SUCCEEDED,
                        null);
            }
        }

        @Override
        @SuppressWarnings("GuardedBy")
        public void updateRuntimePermissionsFingerprint(@UserIdInt int userId) {
            mSettings.updateRuntimePermissionsFingerprint(userId);
        }

        @Override
        public void migrateLegacyObbData() {
            try {
                mInstaller.migrateLegacyObbData();
            } catch (Exception e) {
                Slog.wtf(TAG, e);
            }
        }

        @Override
        public void writeSettings(boolean async) {
            final List<UserInfo> activeUsers = Settings.getActiveUsers(mUserManager);
            synchronized (mLock) {
                if (async) {
                    scheduleWriteSettings();
                } else {
                    writeSettingsLPrTEMP(activeUsers, /* sync= */ false);
                }
            }
        }

        @Override
        public void writePermissionSettings(int[] userIds, boolean async) {
            synchronized (mLock) {
                for (int userId : userIds) {
                    mSettings.writePermissionStateForUserLPr(userId, !async);
                }
            }
        }

        @Override
        public LegacyPermissionSettings getLegacyPermissions() {
            synchronized (mLock) {
                return mSettings.mPermissions;
            }
        }

        /**
         * Read legacy permission states for permissions migration to new permission subsystem.
         */
        @Override
        public RuntimePermissionsState getLegacyPermissionsState(int userId) {
            synchronized (mLock) {
                return mSettings.getLegacyPermissionsState(userId);
            }
        }

        @Override
        public int getLegacyPermissionsVersion(@UserIdInt int userId) {
            synchronized (mLock) {
                return mSettings.getDefaultRuntimePermissionsVersion(userId);
            }
        }

        @Override
        @SuppressWarnings("GuardedBy")
        public boolean isPermissionUpgradeNeeded(int userId) {
            return mSettings.isPermissionUpgradeNeeded(userId);
        }

        @Deprecated
        @Override
        public void setIntegrityVerificationResult(int verificationId, int verificationResult) {
          // Do nothing.
        }

        @Override
        public void setVisibilityLogging(String packageName, boolean enable) {
            PackageManagerServiceUtils.enforceSystemOrRootOrShell(
                    "Only the system or shell can set visibility logging.");
            final PackageStateInternal packageState =
                    snapshot().getPackageStateInternal(packageName);
            if (packageState == null) {
                throw new IllegalStateException("No package found for " + packageName);
            }
            mAppsFilter.getFeatureConfig().enableLogging(packageState.getAppId(), enable);
        }

        @Override
        public void clearBlockUninstallForUser(@UserIdInt int userId) {
            synchronized (mLock) {
                mSettings.clearBlockUninstallLPw(userId);
                mSettings.writePackageRestrictionsLPr(userId);
            }
        }

        @Override
        public boolean registerInstalledLoadingProgressCallback(String packageName,
                PackageManagerInternal.InstalledLoadingProgressCallback callback, int userId) {
            final Computer snapshot = snapshotComputer();
            final PackageStateInternal ps = snapshot.getPackageStateForInstalledAndFiltered(
                    packageName, Binder.getCallingUid(), userId);
            if (ps == null) {
                return false;
            }
            if (!ps.isLoading()) {
                Slog.w(TAG,
                        "Failed registering loading progress callback. Package is fully loaded.");
                return false;
            }
            if (mIncrementalManager == null) {
                Slog.w(TAG,
                        "Failed registering loading progress callback. Incremental is not enabled");
                return false;
            }
            return mIncrementalManager.registerLoadingProgressCallback(ps.getPathString(),
                    (IPackageLoadingProgressCallback) callback.getBinder());
        }

        @Override
        public IncrementalStatesInfo getIncrementalStatesInfo(
                @NonNull String packageName, int filterCallingUid, int userId) {
            final Computer snapshot = snapshotComputer();
            final PackageStateInternal ps = snapshot.getPackageStateForInstalledAndFiltered(
                    packageName, filterCallingUid, userId);
            if (ps == null) {
                return null;
            }
            return new IncrementalStatesInfo(ps.isLoading(), ps.getLoadingProgress(),
                    ps.getLoadingCompletedTime());
        }

        @Override
        public boolean isSameApp(@Nullable String packageName, int callingUid, int userId) {
            return isSameApp(packageName, /*flags=*/0, callingUid, userId);
        }

        @Override
        public boolean isSameApp(@Nullable String packageName,
                @PackageManager.PackageInfoFlagsBits long flags, int callingUid, int userId) {
            if (packageName == null) {
                return false;
            }

            if (Process.isSdkSandboxUid(callingUid)) {
                return packageName.equals(mRequiredSdkSandboxPackage);
            }
            Computer snapshot = snapshot();
            int uid = snapshot.getPackageUid(packageName, flags, userId);
            return UserHandle.isSameApp(uid, callingUid);
        }

        @Override
        public void onPackageProcessKilledForUninstall(String packageName) {
            mHandler.post(() -> PackageManagerService.this.notifyInstallObserver(packageName,
                    true /* killApp */));
        }

        @Override
        public int[] getDistractingPackageRestrictionsAsUser(
                @NonNull String[] packageNames, int userId) {
            final int callingUid = Binder.getCallingUid();
            final Computer snapshot = snapshotComputer();
            Objects.requireNonNull(packageNames, "packageNames cannot be null");
            return mDistractingPackageHelper.getDistractingPackageRestrictionsAsUser(snapshot,
                    packageNames, userId, callingUid);
        }

        @Override
        public ParceledListSlice<PackageInstaller.SessionInfo> getHistoricalSessions(int userId) {
            return mInstallerService.getHistoricalSessions(userId);
        }

        @Override
        public PackageArchiver getPackageArchiver() {
            return mInstallerService.mPackageArchiver;
        }

        @Override
        public void sendPackageRestartedBroadcast(@NonNull String packageName,
                int uid, @Intent.Flags int flags) {
            final int userId = UserHandle.getUserId(uid);
            final int [] userIds = resolveUserIds(userId);
            final SparseArray<int[]> broadcastAllowList =
                    snapshotComputer().getVisibilityAllowLists(packageName, userIds);
            final Bundle extras = new Bundle();
            extras.putInt(Intent.EXTRA_UID, uid);
            extras.putInt(Intent.EXTRA_USER_HANDLE, userId);
            if (android.content.pm.Flags.stayStopped()) {
                extras.putLong(Intent.EXTRA_TIME, SystemClock.elapsedRealtime());
                // Sent async using the PM handler, to maintain ordering with PACKAGE_UNSTOPPED
                mHandler.post(() -> {
                    mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_RESTARTED,
                            packageName, extras, flags, null /* targetPkg */,
                            null /* finishedReceiver */, userIds, null /* instantUserIds */,
                            broadcastAllowList, null /* filterExtrasForReceiver */,
                            null /* bOptions */, null /* requiredPermissions */);
                });
            } else {
                mBroadcastHelper.sendPackageBroadcast(Intent.ACTION_PACKAGE_RESTARTED,
                        packageName, extras, flags, null /* targetPkg */,
                        null /* finishedReceiver */, userIds, null /* instantUserIds */,
                        broadcastAllowList, null /* filterExtrasForReceiver */, null /* bOptions */,
                        null /* requiredPermissions */);
            }
            mPackageMonitorCallbackHelper.notifyPackageMonitor(Intent.ACTION_PACKAGE_RESTARTED,
                    packageName, extras, userIds, null /* instantUserIds */,
                    broadcastAllowList, mHandler, null /* filterExtras */);
        }

        @Override
        public void sendPackageDataClearedBroadcast(@NonNull String packageName,
                int uid, int userId, boolean isRestore, boolean isInstantApp) {
            int[] visibilityAllowList =
                    snapshotComputer().getVisibilityAllowList(packageName, userId);
            final Intent intent = new Intent(Intent.ACTION_PACKAGE_DATA_CLEARED,
                    Uri.fromParts("package", packageName, null /* fragment */));
            intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND
                    | Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
            intent.putExtra(Intent.EXTRA_UID, uid);
            intent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
            if (isRestore) {
                intent.putExtra(Intent.EXTRA_IS_RESTORE, true);
            }
            if (isInstantApp) {
                intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
            }
            mBroadcastHelper.sendPackageBroadcastWithIntent(intent, userId, isInstantApp,
                    0 /* flags */, visibilityAllowList, null /* finishedReceiver */,
                    null /* filterExtrasForReceiver */, null /* bOptions */);
            mPackageMonitorCallbackHelper.notifyPackageMonitorWithIntent(intent, userId,
                    visibilityAllowList, mHandler);
        }

        @Override
        public boolean isUpgradingFromLowerThan(int sdkVersion) {
            final boolean isUpgrading = mPriorSdkVersion != -1;
            return isUpgrading && mPriorSdkVersion < sdkVersion;
        }

        @Override
        public boolean isUpgradingFromLowerThanBySdkVersionFull(
                @Build.SdkIntFull int sdkVersionFull) {
            final boolean isUpgrading = mPriorSdkVersionFull != -1;
            return isUpgrading && (mPriorSdkVersionFull < sdkVersionFull);
        }

        @NonNull
        @Override
        public GosPackageState getGosPackageState(String packageName, int userId) {
            return GosPackageStatePmHooks.getUnfiltered(PackageManagerService.this, packageName, userId);
        }
    }

    private void setEnabledOverlayPackages(@UserIdInt int userId,
            @NonNull ArrayMap<String, OverlayPaths> pendingChanges,
            @NonNull Set<String> outUpdatedPackageNames,
            @NonNull Set<String> outInvalidPackageNames) {
        final ArrayMap<String, ArrayMap<String, ArraySet<String>>>
                targetPkgToLibNameToModifiedDependents = new ArrayMap<>();
        final int numberOfPendingChanges = pendingChanges.size();

        synchronized (mOverlayPathsLock) {
            Computer computer = snapshotComputer();
            for (int i = 0; i < numberOfPendingChanges; i++) {
                final String targetPackageName = pendingChanges.keyAt(i);
                final OverlayPaths newOverlayPaths = pendingChanges.valueAt(i);
                final PackageStateInternal packageState = computer.getPackageStateInternal(
                        targetPackageName);
                final AndroidPackage targetPkg =
                        packageState == null ? null : packageState.getPkg();
                if (targetPackageName == null || targetPkg == null) {
                    Slog.e(TAG, "failed to find package " + targetPackageName);
                    outInvalidPackageNames.add(targetPackageName);
                    continue;
                }

                if (Objects.equals(packageState.getUserStateOrDefault(userId).getOverlayPaths(),
                        newOverlayPaths)) {
                    continue;
                }

                if (targetPkg.getLibraryNames() != null) {
                    // Set the overlay paths for dependencies of the shared library.
                    List<String> libraryNames = targetPkg.getLibraryNames();
                    for (int j = 0; j < libraryNames.size(); j++) {
                        final String libName = libraryNames.get(j);
                        ArraySet<String> modifiedDependents = null;

                        final SharedLibraryInfo info = computer.getSharedLibraryInfo(libName,
                                SharedLibraryInfo.VERSION_UNDEFINED);
                        if (info == null) {
                            continue;
                        }
                        var usingSharedLibraryPair = computer.getPackagesUsingSharedLibrary(info, 0,
                                Process.SYSTEM_UID, userId);
                        final List<VersionedPackage> dependents = usingSharedLibraryPair.first;
                        if (dependents == null) {
                            continue;
                        }
                        for (int k = 0; k < dependents.size(); k++) {
                            final VersionedPackage dependent = dependents.get(k);
                            final PackageStateInternal dependentState =
                                    computer.getPackageStateInternal(dependent.getPackageName());
                            if (dependentState == null) {
                                continue;
                            }
                            if (canSetOverlayPaths(dependentState.getUserStateOrDefault(userId)
                                    .getSharedLibraryOverlayPaths()
                                    .get(libName), newOverlayPaths)) {
                                String dependentPackageName = dependent.getPackageName();
                                modifiedDependents = ArrayUtils.add(modifiedDependents,
                                        dependentPackageName);
                                outUpdatedPackageNames.add(dependentPackageName);
                            }
                        }

                        if (modifiedDependents != null) {
                            ArrayMap<String, ArraySet<String>> libNameToModifiedDependents =
                                    targetPkgToLibNameToModifiedDependents.get(
                                            targetPackageName);
                            if (libNameToModifiedDependents == null) {
                                libNameToModifiedDependents = new ArrayMap<>();
                                targetPkgToLibNameToModifiedDependents.put(targetPackageName,
                                        libNameToModifiedDependents);
                            }
                            libNameToModifiedDependents.put(libName, modifiedDependents);
                        }
                    }
                }

                if (canSetOverlayPaths(packageState.getUserStateOrDefault(userId).getOverlayPaths(),
                        newOverlayPaths)) {
                    outUpdatedPackageNames.add(targetPackageName);
                }
            }

            commitPackageStateMutation(null, mutator -> {
                for (int i = 0; i < numberOfPendingChanges; i++) {
                    final String targetPackageName = pendingChanges.keyAt(i);
                    final OverlayPaths newOverlayPaths = pendingChanges.valueAt(i);

                    if (!outUpdatedPackageNames.contains(targetPackageName)) {
                        continue;
                    }

                    mutator.forPackage(targetPackageName)
                            .userState(userId)
                            .setOverlayPaths(newOverlayPaths);

                    final ArrayMap<String, ArraySet<String>> libNameToModifiedDependents =
                            targetPkgToLibNameToModifiedDependents.get(
                                    targetPackageName);
                    if (libNameToModifiedDependents == null) {
                        continue;
                    }

                    for (int mapIndex = 0; mapIndex < libNameToModifiedDependents.size();
                            mapIndex++) {
                        String libName = libNameToModifiedDependents.keyAt(mapIndex);
                        ArraySet<String> modifiedDependents =
                                libNameToModifiedDependents.valueAt(mapIndex);
                        for (int setIndex = 0; setIndex < modifiedDependents.size(); setIndex++) {
                            mutator.forPackage(modifiedDependents.valueAt(setIndex))
                                    .userState(userId)
                                    .setOverlayPathsForLibrary(libName, newOverlayPaths);
                        }
                    }
                }
            });
        }

        if (userId == UserHandle.USER_SYSTEM) {
            // Keep the overlays in the system application info (and anything special cased as well)
            // up to date to make sure system ui is themed correctly.
            for (int i = 0; i < numberOfPendingChanges; i++) {
                final String targetPackageName = pendingChanges.keyAt(i);
                final OverlayPaths newOverlayPaths = pendingChanges.valueAt(i);
                maybeUpdateSystemOverlays(targetPackageName, newOverlayPaths);
            }
        }

        invalidatePackageInfoCache(
                PackageMetrics.INVALIDATION_REASON_ENABLE_OVERLAY_PACKAGES);
    }

    private boolean canSetOverlayPaths(OverlayPaths origPaths, OverlayPaths newPaths) {
        if (Objects.equals(origPaths, newPaths)) {
            return false;
        }
        if ((origPaths == null && newPaths.isEmpty())
                || (newPaths == null && origPaths.isEmpty())) {
            return false;
        }
        return true;
    }

    private void maybeUpdateSystemOverlays(String targetPackageName, OverlayPaths newOverlayPaths) {
        if (!mResolverReplaced) {
            if (targetPackageName.equals("android")) {
                if (newOverlayPaths == null) {
                    mPlatformPackageOverlayPaths = null;
                    mPlatformPackageOverlayResourceDirs = null;
                } else {
                    mPlatformPackageOverlayPaths = newOverlayPaths.getOverlayPaths().toArray(
                            new String[0]);
                    mPlatformPackageOverlayResourceDirs = newOverlayPaths.getResourceDirs().toArray(
                            new String[0]);
                }
                applyUpdatedSystemOverlayPaths();
            }
        } else {
            if (targetPackageName.equals(mResolveActivity.applicationInfo.packageName)) {
                if (newOverlayPaths == null) {
                    mReplacedResolverPackageOverlayPaths = null;
                    mReplacedResolverPackageOverlayResourceDirs = null;
                } else {
                    mReplacedResolverPackageOverlayPaths =
                            newOverlayPaths.getOverlayPaths().toArray(new String[0]);
                    mReplacedResolverPackageOverlayResourceDirs =
                            newOverlayPaths.getResourceDirs().toArray(new String[0]);
                }
                applyUpdatedSystemOverlayPaths();
            }
        }
    }

    private void applyUpdatedSystemOverlayPaths() {
        if (mAndroidApplication == null) {
            Slog.i(TAG, "Skipped the AndroidApplication overlay paths update - no app yet");
        } else {
            mAndroidApplication.overlayPaths = mPlatformPackageOverlayPaths;
            mAndroidApplication.resourceDirs = mPlatformPackageOverlayResourceDirs;
        }
        if (mResolverReplaced) {
            mResolveActivity.applicationInfo.overlayPaths = mReplacedResolverPackageOverlayPaths;
            mResolveActivity.applicationInfo.resourceDirs =
                    mReplacedResolverPackageOverlayResourceDirs;
        }
    }

    private void enforceAdjustRuntimePermissionsPolicyOrUpgradeRuntimePermissions(
            @NonNull String message) {
        if (mContext.checkCallingOrSelfPermission(
                Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY)
                != PackageManager.PERMISSION_GRANTED
                && mContext.checkCallingOrSelfPermission(
                Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS)
                != PackageManager.PERMISSION_GRANTED) {
            throw new SecurityException(message + " requires "
                    + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY + " or "
                    + Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS);
        }
    }

    // TODO: Remove
    @Deprecated
    @Nullable
    @GuardedBy("mLock")
    PackageSetting getPackageSettingForMutation(String packageName) {
        return mSettings.getPackageLPr(packageName);
    }

    // TODO: Remove
    @Deprecated
    @Nullable
    @GuardedBy("mLock")
    PackageSetting getDisabledPackageSettingForMutation(String packageName) {
        return mSettings.getDisabledSystemPkgLPr(packageName);
    }

    @Deprecated
    void forEachPackageSetting(Consumer<PackageSetting> actionLocked) {
        synchronized (mLock) {
            int size = mSettings.getPackagesLocked().size();
            for (int index = 0; index < size; index++) {
                actionLocked.accept(mSettings.getPackagesLocked().valueAt(index));
            }
        }
    }

    void forEachPackageState(@NonNull Computer snapshot, Consumer<PackageStateInternal> consumer) {
        forEachPackageState(snapshot.getPackageStates(), consumer);
    }

    void forEachPackage(@NonNull Computer snapshot, Consumer<AndroidPackage> consumer) {
        final ArrayMap<String, ? extends PackageStateInternal> packageStates =
                snapshot.getPackageStates();
        int size = packageStates.size();
        for (int index = 0; index < size; index++) {
            PackageStateInternal packageState = packageStates.valueAt(index);
            if (packageState.getPkg() != null) {
                consumer.accept(packageState.getPkg());
            }
        }
    }

    void forEachPackageInternal(@NonNull Computer snapshot,
            @NonNull Consumer<AndroidPackageInternal> consumer) {
        final ArrayMap<String, ? extends PackageStateInternal> packageStates =
                snapshot.getPackageStates();
        int size = packageStates.size();
        for (int index = 0; index < size; index++) {
            PackageStateInternal packageState = packageStates.valueAt(index);
            if (packageState.getPkg() != null) {
                consumer.accept(packageState.getPkg());
            }
        }
    }

    private void forEachPackageState(
            @NonNull ArrayMap<String, ? extends PackageStateInternal> packageStates,
            @NonNull Consumer<PackageStateInternal> consumer) {
        int size = packageStates.size();
        for (int index = 0; index < size; index++) {
            PackageStateInternal packageState = packageStates.valueAt(index);
            consumer.accept(packageState);
        }
    }

    void forEachInstalledPackage(@NonNull Computer snapshot, @NonNull Consumer<AndroidPackage> action,
            @UserIdInt int userId) {
        Consumer<PackageStateInternal> actionWrapped = packageState -> {
            if (packageState.getPkg() != null
                    && packageState.getUserStateOrDefault(userId).isInstalled()) {
                action.accept(packageState.getPkg());
            }
        };
        forEachPackageState(snapshot.getPackageStates(), actionWrapped);
    }

    void grantImplicitAccess(@NonNull Computer snapshot, @UserIdInt int userId,
            Intent intent, @AppIdInt int recipientAppId, int visibleUid, boolean direct,
            boolean retainOnUpdate) {
        final AndroidPackage visiblePackage = snapshot.getPackage(visibleUid);
        final int recipientUid = UserHandle.getUid(userId, recipientAppId);
        final AndroidPackage recipientPackage = snapshot.getPackage(recipientUid);
        if (visiblePackage == null || recipientPackage == null) {
            return;
        }

        final PackageStateInternal recipientPsi = snapshot.getPackageStateInternal(
                recipientPackage.getPackageName(), Process.SYSTEM_UID);
        final PackageStateInternal visiblePsi = snapshot.getPackageStateInternal(
                visiblePackage.getPackageName(), Process.SYSTEM_UID);
        if (recipientPsi == null || visiblePsi == null) {
            return;
        }

        if (PackageManagerHooks.shouldFilterApplication(recipientPsi, null, userId,
                visiblePsi, UserHandle.getUserId(visibleUid))) {
            return;
        }

        final boolean instantApp = snapshot.isInstantAppInternal(
                visiblePackage.getPackageName(), userId, visibleUid);
        final boolean accessGranted;
        if (instantApp) {
            if (!direct) {
                // if the interaction that lead to this granting access to an instant app
                // was indirect (i.e.: URI permission grant), do not actually execute the
                // grant.
                return;
            }
            accessGranted = mInstantAppRegistry.grantInstantAccess(userId, intent,
                    recipientAppId, UserHandle.getAppId(visibleUid) /*instantAppId*/);
        } else {
            accessGranted = mAppsFilter.grantImplicitAccess2(recipientUid, visibleUid,
                    retainOnUpdate);
        }

        if (accessGranted) {
            invalidateGetPackagesForUidCache(
                    PackageMetrics.INVALIDATION_REASON_GRANT_IMPLICIT_ACCESS);
            AppOpsManager.invalidateCheckPackageCache();
        }
    }

    long deleteOatArtifactsOfPackage(@NonNull Computer snapshot, String packageName) {
        PackageManagerServiceUtils.enforceSystemOrRootOrShell(
                "Only the system or shell can delete oat artifacts");

        try (PackageManagerLocal.FilteredSnapshot filteredSnapshot =
                        PackageManagerServiceUtils.getPackageManagerLocal()
                                .withUnownedFilteredSnapshot(snapshot)) {
            try {
                DeleteResult res = DexOptHelper.getArtManagerLocal().deleteDexoptArtifacts(
                        filteredSnapshot, packageName);
                return res.getFreedBytes();
            } catch (IllegalArgumentException e) {
                Log.e(TAG, e.toString());
                return -1;
            } catch (IllegalStateException e) {
                Slog.wtfStack(TAG, e.toString());
                return -1;
            }
        }
    }

    List<String> getMimeGroupInternal(@NonNull Computer snapshot, String packageName,
            String mimeGroup) {
        final PackageStateInternal packageState = snapshot.getPackageStateInternal(packageName);
        if (packageState == null) {
            return Collections.emptyList();
        }

        final Map<String, Set<String>> mimeGroups = packageState.getMimeGroups();
        Set<String> mimeTypes = mimeGroups != null ? mimeGroups.get(mimeGroup) : null;
        if (mimeTypes == null) {
            throw new IllegalArgumentException("Unknown MIME group " + mimeGroup
                    + " for package " + packageName);
        }
        return new ArrayList<>(mimeTypes);
    }

    /**
     * Temporary method that wraps mSettings.writeLPr() and calls mPermissionManager's
     * writeLegacyPermissionsTEMP() beforehand.
     *
     * TODO: In the meantime, can this be moved to a schedule call?
     * TODO(b/182523293): This should be removed once we finish migration of permission storage.
     */
    @SuppressWarnings("GuardedBy")
    void writeSettingsLPrTEMP(List<UserInfo> users, boolean sync) {
        snapshotComputer(false);
        mPermissionManager.writeLegacyPermissionsTEMP(mSettings.mPermissions);
        mSettings.writeLPr(mLiveComputer, users, sync);
    }

    // Default async version.
    void writeSettingsLPrTEMP(List<UserInfo> users) {
        writeSettingsLPrTEMP(users, /*sync=*/false);
    }

    @Override
    public void verifyHoldLockToken(IBinder token) {
        if (!Build.IS_DEBUGGABLE) {
            throw new SecurityException("holdLock requires a debuggable build");
        }

        if (token == null) {
            throw new SecurityException("null holdLockToken");
        }

        if (token.queryLocalInterface("holdLock:" + Binder.getCallingUid()) != this) {
            throw new SecurityException("Invalid holdLock() token");
        }
    }

    static String getDefaultTimeouts() {
        final long token = Binder.clearCallingIdentity();
        try {
            return DeviceConfig.getString(NAMESPACE_PACKAGE_MANAGER_SERVICE,
                    PROPERTY_INCFS_DEFAULT_TIMEOUTS, "");
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    static String getKnownDigestersList() {
        final long token = Binder.clearCallingIdentity();
        try {
            return DeviceConfig.getString(NAMESPACE_PACKAGE_MANAGER_SERVICE,
                    PROPERTY_KNOWN_DIGESTERS_LIST, "");
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    static boolean isPreapprovalRequestAvailable() {
        final long token = Binder.clearCallingIdentity();
        try {
            if (!Resources.getSystem().getBoolean(
                    com.android.internal.R.bool.config_isPreApprovalRequestAvailable)) {
                return false;
            }
            return DeviceConfig.getBoolean(NAMESPACE_PACKAGE_MANAGER_SERVICE,
                    PROPERTY_IS_PRE_APPROVAL_REQUEST_AVAILABLE, true /* defaultValue */);
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    static boolean isUpdateOwnershipEnforcementAvailable() {
        final long token = Binder.clearCallingIdentity();
        try {
            return DeviceConfig.getBoolean(NAMESPACE_PACKAGE_MANAGER_SERVICE,
                    PROPERTY_IS_UPDATE_OWNERSHIP_ENFORCEMENT_AVAILABLE, true /* defaultValue */);
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    /**
     * Returns the array containing per-uid timeout configuration.
     * This is derived from DeviceConfig flags.
     */
    public @NonNull PerUidReadTimeouts[] getPerUidReadTimeouts(@NonNull Computer snapshot) {
        PerUidReadTimeouts[] result = mPerUidReadTimeoutsCache;
        if (result == null) {
            result = parsePerUidReadTimeouts(snapshot);
            mPerUidReadTimeoutsCache = result;
        }
        return result;
    }

    private @NonNull PerUidReadTimeouts[] parsePerUidReadTimeouts(@NonNull Computer snapshot) {
        final String defaultTimeouts = getDefaultTimeouts();
        final String knownDigestersList = getKnownDigestersList();
        final List<PerPackageReadTimeouts> perPackageReadTimeouts =
                PerPackageReadTimeouts.parseDigestersList(defaultTimeouts, knownDigestersList);

        if (perPackageReadTimeouts.size() == 0) {
            return EMPTY_PER_UID_READ_TIMEOUTS_ARRAY;
        }

        final int[] allUsers = mInjector.getUserManagerService().getUserIds();
        final List<PerUidReadTimeouts> result = new ArrayList<>(perPackageReadTimeouts.size());
        for (int i = 0, size = perPackageReadTimeouts.size(); i < size; ++i) {
            final PerPackageReadTimeouts perPackage = perPackageReadTimeouts.get(i);
            final PackageStateInternal ps =
                    snapshot.getPackageStateInternal(perPackage.packageName);
            if (ps == null) {
                if (DEBUG_PER_UID_READ_TIMEOUTS) {
                    Slog.i(TAG, "PerUidReadTimeouts: package not found = "
                            + perPackage.packageName);
                }
                continue;
            }
            if (ps.getAppId() < Process.FIRST_APPLICATION_UID) {
                if (DEBUG_PER_UID_READ_TIMEOUTS) {
                    Slog.i(TAG, "PerUidReadTimeouts: package is system, appId="
                            + ps.getAppId());
                }
                continue;
            }

            final AndroidPackage pkg = ps.getPkg();
            if (pkg.getLongVersionCode() < perPackage.versionCodes.minVersionCode
                    || pkg.getLongVersionCode() > perPackage.versionCodes.maxVersionCode) {
                if (DEBUG_PER_UID_READ_TIMEOUTS) {
                    Slog.i(TAG, "PerUidReadTimeouts: version code is not in range = "
                            + perPackage.packageName + ":" + pkg.getLongVersionCode());
                }
                continue;
            }
            if (perPackage.sha256certificate != null
                    && !pkg.getSigningDetails().hasSha256Certificate(
                    perPackage.sha256certificate)) {
                if (DEBUG_PER_UID_READ_TIMEOUTS) {
                    Slog.i(TAG, "PerUidReadTimeouts: invalid certificate = "
                            + perPackage.packageName + ":" + pkg.getLongVersionCode());
                }
                continue;
            }
            for (int userId : allUsers) {
                if (!ps.getUserStateOrDefault(userId).isInstalled()) {
                    continue;
                }
                final int uid = UserHandle.getUid(userId, ps.getAppId());
                final PerUidReadTimeouts perUid = new PerUidReadTimeouts();
                perUid.uid = uid;
                perUid.minTimeUs = perPackage.timeouts.minTimeUs;
                perUid.minPendingTimeUs = perPackage.timeouts.minPendingTimeUs;
                perUid.maxPendingTimeUs = perPackage.timeouts.maxPendingTimeUs;
                result.add(perUid);
            }
        }
        return result.toArray(new PerUidReadTimeouts[result.size()]);
    }

    void setKeepUninstalledPackagesInternal(@NonNull Computer snapshot, List<String> packageList) {
        Preconditions.checkNotNull(packageList);
        synchronized (mKeepUninstalledPackages) {
            List<String> toRemove = new ArrayList<>(mKeepUninstalledPackages);
            toRemove.removeAll(packageList); // Do not remove anything still in the list

            mKeepUninstalledPackages.clear();
            mKeepUninstalledPackages.addAll(packageList);

            for (int i = 0; i < toRemove.size(); i++) {
                deletePackageIfUnused(snapshot, toRemove.get(i));
            }
        }
    }

    boolean shouldKeepUninstalledPackageLPr(String packageName) {
        synchronized (mKeepUninstalledPackages) {
            return mKeepUninstalledPackages.contains(packageName);
        }
    }

    boolean getSafeMode() {
        return mSafeMode;
    }

    ComponentName getResolveComponentName() {
        return mResolveComponentName;
    }

    DefaultAppProvider getDefaultAppProvider() {
        return mDefaultAppProvider;
    }

    File getCacheDir() {
        return mCacheDir;
    }

    boolean hasCacheDir() {
        return !"/dev/null".equals(mCacheDir.getPath());
    }

    PackageProperty getPackageProperty() {
        return mPackageProperty;
    }

    WatchedArrayMap<ComponentName, ParsedInstrumentation> getInstrumentation() {
        return mInstrumentation;
    }

    int getSdkVersion() {
        return mSdkVersion;
    }

    int getSdkVersionFull() {
        return mSdkVersionFull;
    }

    void addAllPackageProperties(@NonNull AndroidPackage pkg) {
        mPackageProperty.addAllProperties(pkg);
    }

    void addInstrumentation(ComponentName name, ParsedInstrumentation instrumentation) {
        mInstrumentation.put(name, instrumentation);
    }

    String[] getKnownPackageNamesInternal(@NonNull Computer snapshot, int knownPackage,
            int userId) {
        return new KnownPackages(
                mDefaultAppProvider,
                mRequiredInstallerPackage,
                mRequiredUninstallerPackage,
                mSetupWizardPackage,
                mRequiredVerifierPackages,
                mDefaultTextClassifierPackage,
                mSystemTextClassifierPackageName,
                mRequiredPermissionControllerPackage,
                mConfiguratorPackage,
                mIncidentReportApproverPackage,
                mAmbientContextDetectionPackage,
                mWearableSensingPackage,
                mAppPredictionServicePackage,
                COMPANION_PACKAGE_NAME,
                mRetailDemoPackage,
                mOverlayConfigSignaturePackage,
                mRecentsPackage,
                mDeveloperVerificationServiceProvider == null
                        ? null : mDeveloperVerificationServiceProvider.getPackageName())
                .getKnownPackageNames(snapshot, knownPackage, userId);
    }

    String getActiveLauncherPackageName(int userId) {
        return mDefaultAppProvider.getDefaultHome(userId);
    }

    boolean setActiveLauncherPackage(@NonNull String packageName, @UserIdInt int userId,
            @NonNull Consumer<Boolean> callback) {
        return mDefaultAppProvider.setDefaultHome(packageName, userId, mContext.getMainExecutor(),
                callback);
    }

    @Nullable
    String getDefaultBrowser(@UserIdInt int userId) {
        return mDefaultAppProvider.getDefaultBrowser(userId);
    }

    void setDefaultBrowser(@Nullable String packageName, @UserIdInt int userId) {
        mDefaultAppProvider.setDefaultBrowser(packageName, userId);
    }

    String getModuleMetadataPackageName() {
        return mModuleInfoProvider.getPackageName();
    }

    File getAppInstallDir() {
        return mAppInstallDir;
    }

    boolean isExpectingBetter(String packageName) {
        return mInitAppsHelper.isExpectingBetter(packageName);
    }

    int getDefParseFlags() {
        return mDefParseFlags;
    }

    void setUpCustomResolverActivity(AndroidPackage pkg, PackageSetting pkgSetting) {
        synchronized (mLock) {
            mResolverReplaced = true;

            // The instance created in PackageManagerService is special cased to be non-user
            // specific, so initialize all the needed fields here.
            ApplicationInfo appInfo = PackageInfoUtils.generateApplicationInfo(pkg, 0,
                    PackageUserStateInternal.DEFAULT, UserHandle.USER_SYSTEM, pkgSetting);

            // Set up information for custom user intent resolution activity.
            mResolveActivity.applicationInfo = appInfo;
            mResolveActivity.name = mCustomResolverComponentName.getClassName();
            mResolveActivity.packageName = pkg.getPackageName();
            mResolveActivity.processName = pkg.getProcessName();
            mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
            mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS
                    | ActivityInfo.FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS
                    | ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES
                    | ActivityInfo.FLAG_HARDWARE_ACCELERATED;
            mResolveActivity.theme = 0;
            mResolveActivity.exported = true;
            mResolveActivity.enabled = true;
            mResolveInfo.activityInfo = mResolveActivity;
            mResolveInfo.priority = 0;
            mResolveInfo.preferredOrder = 0;
            mResolveInfo.match = 0;
            mResolveComponentName = mCustomResolverComponentName;
            PackageManagerService.onChanged();
            Slog.i(TAG, "Replacing default ResolverActivity with custom activity: "
                    + mResolveComponentName);
        }
    }

    void setPlatformPackage(AndroidPackage pkg, PackageSetting pkgSetting) {
        synchronized (mLock) {
            // Set up information for our fall-back user intent resolution activity.
            mPlatformPackage = pkg;

            // The instance stored in PackageManagerService is special cased to be non-user
            // specific, so initialize all the needed fields here.
            mAndroidApplication = PackageInfoUtils.generateApplicationInfo(pkg, 0,
                    PackageUserStateInternal.DEFAULT, UserHandle.USER_SYSTEM, pkgSetting);

            if (!mResolverReplaced) {
                mResolveActivity.applicationInfo = mAndroidApplication;
                mResolveActivity.name = ResolverActivity.class.getName();
                mResolveActivity.packageName = mAndroidApplication.packageName;
                mResolveActivity.processName = "system:ui";
                mResolveActivity.launchMode = ActivityInfo.LAUNCH_MULTIPLE;
                mResolveActivity.documentLaunchMode = ActivityInfo.DOCUMENT_LAUNCH_NEVER;
                mResolveActivity.flags = ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS
                        | ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY
                        | ActivityInfo.FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES
                        | ActivityInfo.FLAG_HARDWARE_ACCELERATED;
                mResolveActivity.theme = R.style.Theme_Material_Dialog_Alert;
                mResolveActivity.exported = true;
                mResolveActivity.enabled = true;
                mResolveActivity.resizeMode = ActivityInfo.RESIZE_MODE_RESIZEABLE;
                mResolveActivity.configChanges = ActivityInfo.CONFIG_SCREEN_SIZE
                        | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE
                        | ActivityInfo.CONFIG_SCREEN_LAYOUT
                        | ActivityInfo.CONFIG_ORIENTATION
                        | ActivityInfo.CONFIG_KEYBOARD
                        | ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
                mResolveInfo.activityInfo = mResolveActivity;
                mResolveInfo.priority = 0;
                mResolveInfo.preferredOrder = 0;
                mResolveInfo.match = 0;
                mResolveComponentName = new ComponentName(
                        mAndroidApplication.packageName, mResolveActivity.name);
            }
            PackageManagerService.onChanged();
        }
        applyUpdatedSystemOverlayPaths();
    }

    ApplicationInfo getCoreAndroidApplication() {
        return mAndroidApplication;
    }

    boolean isSystemReady() {
        return mSystemReady;
    }

    AndroidPackage getPlatformPackage() {
        return mPlatformPackage;
    }

    boolean isPreNMR1Upgrade() {
        return mIsPreNMR1Upgrade;
    }

    boolean isOverlayMutable(String packageName) {
        return mOverlayConfig.isMutable(packageName);
    }

    @ScanFlags int getSystemPackageScanFlags(File codePath) {
        List<ScanPartition> dirsToScanAsSystem =
                mInitAppsHelper.getDirsToScanAsSystem();
        @PackageManagerService.ScanFlags int scanFlags = SCAN_AS_SYSTEM;
        for (int i = dirsToScanAsSystem.size() - 1; i >= 0; i--) {
            ScanPartition partition = dirsToScanAsSystem.get(i);
            if (partition.containsFile(codePath)) {
                scanFlags |= partition.scanFlag;
                if (partition.containsPrivApp(codePath)) {
                    scanFlags |= SCAN_AS_PRIVILEGED;
                }
                break;
            }
        }
        return scanFlags;
    }

    Pair<Integer, Integer> getSystemPackageRescanFlagsAndReparseFlags(File scanFile,
            int systemScanFlags, int systemParseFlags) {
        List<ScanPartition> dirsToScanAsSystem =
                mInitAppsHelper.getDirsToScanAsSystem();
        @ParsingPackageUtils.ParseFlags int reparseFlags = 0;
        @PackageManagerService.ScanFlags int rescanFlags = 0;
        for (int i1 = dirsToScanAsSystem.size() - 1; i1 >= 0; i1--) {
            final ScanPartition partition = dirsToScanAsSystem.get(i1);
            if (partition.containsPrivApp(scanFile)) {
                reparseFlags = systemParseFlags;
                rescanFlags = systemScanFlags | SCAN_AS_PRIVILEGED
                        | partition.scanFlag;
                break;
            }
            if (partition.containsApp(scanFile)) {
                reparseFlags = systemParseFlags;
                rescanFlags = systemScanFlags | partition.scanFlag;
                break;
            }
        }
        return new Pair<>(rescanFlags, reparseFlags);
    }


    /**
     * @see PackageManagerInternal#recordInitialState()
     */
    @NonNull
    public PackageStateMutator.InitialState recordInitialState() {
        return mPackageStateMutator.initialState(mChangedPackagesTracker.getSequenceNumber());
    }

    /**
     * @see PackageManagerInternal#commitPackageStateMutation(PackageStateMutator.InitialState,
     * Consumer)
     */
    @NonNull
    public PackageStateMutator.Result commitPackageStateMutation(
            @Nullable PackageStateMutator.InitialState initialState,
            @NonNull Consumer<PackageStateMutator> consumer) {
        synchronized (mPackageStateWriteLock) {
            final PackageStateMutator.Result result = mPackageStateMutator.generateResult(
                    initialState, mChangedPackagesTracker.getSequenceNumber());
            if (result != PackageStateMutator.Result.SUCCESS) {
                return result;
            }

            consumer.accept(mPackageStateMutator);
            mPackageStateMutator.onFinished();
        }

        return PackageStateMutator.Result.SUCCESS;
    }

    /**
     * @see PackageManagerInternal#commitPackageStateMutation(PackageStateMutator.InitialState,
     * Consumer)
     */
    @NonNull
    public PackageStateMutator.Result commitPackageStateMutation(
            @Nullable PackageStateMutator.InitialState initialState, @NonNull String packageName,
            @NonNull Consumer<PackageStateWrite> consumer) {
        PackageStateMutator.Result result = null;
        if (Thread.holdsLock(mPackageStateWriteLock)) {
            // If the thread is already holding the lock, this is likely a retry based on a prior
            // failure, and re-calculating whether a state change occurred can be skipped.
            result = PackageStateMutator.Result.SUCCESS;
        }
        synchronized (mPackageStateWriteLock) {
            if (result == null) {
                // If the thread wasn't previously holding, this is a first-try commit and so a
                // state change may have happened.
                result = mPackageStateMutator.generateResult(
                        initialState, mChangedPackagesTracker.getSequenceNumber());
            }
            if (result != PackageStateMutator.Result.SUCCESS) {
                return result;
            }

            PackageStateWrite state = mPackageStateMutator.forPackage(packageName);
            if (state == null) {
                return PackageStateMutator.Result.SPECIFIC_PACKAGE_NULL;
            } else {
                consumer.accept(state);
            }

            state.onChanged();
        }

        return PackageStateMutator.Result.SUCCESS;
    }

    void notifyInstantAppPackageInstalled(String packageName, int[] newUsers) {
        mInstantAppRegistry.onPackageInstalled(snapshotComputer(), packageName, newUsers);
    }

    void addInstallerPackageName(InstallSource installSource) {
        synchronized (mLock) {
            mSettings.addInstallerPackageNames(installSource);
        }
    }

    public void reconcileSdkData(@Nullable String volumeUuid, @NonNull String packageName,
            @NonNull List<String> subDirNames, int userId, int appId, int previousAppId,
            @NonNull String seInfo, int flags) throws IOException {
        ReconcileSdkDataArgs args = mInstaller.buildReconcileSdkDataArgs(volumeUuid,
                packageName, subDirNames, userId, appId, seInfo,
                flags);
        args.previousAppId = previousAppId;
        try (PackageManagerTracedLock installLock = mInstallLock.acquireLock()) {
            mInstaller.reconcileSdkData(args);
        } catch (InstallerException e) {
            throw new IOException(e.getMessage());
        }
    }

    void removeCodePath(@Nullable File codePath) {
        mRemovePackageHelper.removeCodePath(codePath);
    }

    void cleanUpResources(@NonNull String packageName, @NonNull File codeFile) {
        mRemovePackageHelper.cleanUpResources(packageName, codeFile);
    }

    void cleanUpForMoveInstall(String volumeUuid, String packageName, String fromCodePath) {
        mRemovePackageHelper.cleanUpForMoveInstall(volumeUuid, packageName, fromCodePath);
    }

    void sendPendingBroadcasts(String reasonForTrace, int callingUidForTrace) {
        mInstallPackageHelper.sendPendingBroadcasts(reasonForTrace, callingUidForTrace);
    }

    void handlePackagePostInstall(@NonNull InstallRequest request, boolean launchedForRestore) {
        mInstallPackageHelper.handlePackagePostInstall(request, launchedForRestore);
    }

    Pair<Integer, IntentSender> installExistingPackageAsUser(
            @Nullable String packageName,
            @UserIdInt int userId, @PackageManager.InstallFlags int installFlags,
            @PackageManager.InstallReason int installReason,
            @Nullable List<String> allowlistedRestrictedPermissions,
            @Nullable IntentSender intentSender) {
        return mInstallPackageHelper.installExistingPackageAsUser(packageName, userId, installFlags,
                installReason, allowlistedRestrictedPermissions, intentSender);
    }
    AndroidPackage initPackageTracedLI(File scanFile, final int parseFlags, int scanFlags)
            throws PackageManagerException {
        return mInstallPackageHelper.initPackageTracedLI(scanFile, parseFlags, scanFlags);
    }

    void restoreDisabledSystemPackageLIF(@NonNull DeletePackageAction action,
                                         @NonNull int[] allUserHandles,
                                         boolean writeSettings) throws SystemDeleteException {
        mInstallPackageHelper.restoreDisabledSystemPackageLIF(
                action, allUserHandles, writeSettings);
    }
    boolean enableCompressedPackage(@NonNull AndroidPackage stubPkg,
                                    @NonNull PackageSetting stubPs) {
        return mInstallPackageHelper.enableCompressedPackage(stubPkg, stubPs);
    }

    void installPackagesTraced(List<InstallRequest> requests, MoveInfo moveInfo) {
        mInstallPackageHelper.installPackagesTraced(requests, moveInfo);
    }

    void onInstallerPackageDeleted(int installerAppId, int userId) {
        mInstallerService.onInstallerPackageDeleted(installerAppId, userId);
    }

    void restoreAndPostInstall(InstallRequest request) {
        mInstallPackageHelper.restoreAndPostInstall(request);
    }

    Pair<Integer, String> verifyReplacingVersionCode(@NonNull PackageInfoLite pkgLite,
                                    long requiredInstalledVersionCode,
                                    int installFlags) {
        return mInstallPackageHelper.verifyReplacingVersionCode(
                pkgLite, requiredInstalledVersionCode, installFlags);
    }

    int getUidForVerifier(VerifierInfo verifierInfo) {
        return mInstallPackageHelper.getUidForVerifier(verifierInfo);
    }

    int deletePackageX(String packageName, long versionCode, int userId, int deleteFlags,
                        boolean removedBySystem) {
        return mDeletePackageHelper.deletePackageX(packageName,
                PackageManager.VERSION_CODE_HIGHEST, UserHandle.USER_SYSTEM,
                PackageManager.DELETE_ALL_USERS, true /*removedBySystem*/);
    }

    private static boolean isSystemOrPhone(int uid) {
        return UserHandle.isSameApp(uid, Process.SYSTEM_UID)
                || UserHandle.isSameApp(uid, Process.PHONE_UID);
    }

    /**
     * @hide
     */
    @NonNull
    public Set<String> getAllPlatformRestrictedPermissions() {
        if (sRestrictedPermissions == null) {
            sRestrictedPermissions = new HashSet<>();
            final long token = Binder.clearCallingIdentity();
            try {
                PackageInfo pi = snapshotComputer().getPackageInfo(
                        PLATFORM_PACKAGE_NAME, GET_PERMISSIONS, UserHandle.USER_SYSTEM);
                if (pi.permissions != null) {
                    for (int i = 0; i < pi.permissions.length; i++) {
                        if (pi.permissions[i].isRestricted()) {
                            sRestrictedPermissions.add(pi.permissions[i].name);
                        }
                    }
                }
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }
        return sRestrictedPermissions;
    }

    private CompatChange.ChangeListener selinuxChangeListener;

    public void updateSeInfo(String packageName) {
        // use the same procedure that is used for SELinux compat changes
        selinuxChangeListener.onCompatChange(packageName);
    }

    @NonNull
    public Context getContext() {
        return mContext;
    }

    @Nullable
    String getDeveloperVerificationPolicyDelegatePackageName() {
        return mDeveloperVerificationPolicyDelegatePackage;
    }

    /**
     * @return The update-owner of the given package name as specified in the system config file.
     */
    @Nullable
    public String getSystemAppUpdateOwnerPackageName(String packageName) {
        return mInjector.getSystemConfig().getSystemAppUpdateOwnerPackageName(packageName);
    }
}
