/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.pm;

import static android.Manifest.permission.DELETE_PACKAGES;
import static android.Manifest.permission.INSTALL_PACKAGES;
import static android.Manifest.permission.REQUEST_DELETE_PACKAGES;
import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS;
import static android.content.ContentProvider.isAuthorityRedirectedForCloneProfile;
import static android.content.Intent.ACTION_MAIN;
import static android.content.Intent.CATEGORY_DEFAULT;
import static android.content.Intent.CATEGORY_HOME;
import static android.content.pm.PackageManager.CERT_INPUT_RAW_X509;
import static android.content.pm.PackageManager.CERT_INPUT_SHA256;
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_ENABLED;
import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER;
import static android.content.pm.PackageManager.MATCH_ANY_USER;
import static android.content.pm.PackageManager.MATCH_APEX;
import static android.content.pm.PackageManager.MATCH_ARCHIVED_PACKAGES;
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_KNOWN_PACKAGES;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.pm.PackageManager.TYPE_ACTIVITY;
import static android.content.pm.PackageManager.TYPE_PROVIDER;
import static android.content.pm.PackageManager.TYPE_RECEIVER;
import static android.content.pm.PackageManager.TYPE_SERVICE;
import static android.content.pm.PackageManager.TYPE_UNKNOWN;
import static android.os.Process.INVALID_UID;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;

import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE;
import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO_PARENT;
import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
import static com.android.server.pm.PackageManagerService.DEBUG_INSTANT;
import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_INFO;
import static com.android.server.pm.PackageManagerService.DEBUG_PREFERRED;
import static com.android.server.pm.PackageManagerService.EMPTY_INT_ARRAY;
import static com.android.server.pm.PackageManagerService.HIDE_EPHEMERAL_APIS;
import static com.android.server.pm.PackageManagerService.TAG;
import static com.android.server.pm.PackageManagerServiceUtils.compareSignatureArrays;
import static com.android.server.pm.PackageManagerServiceUtils.compareSignatures;
import static com.android.server.pm.PackageManagerServiceUtils.isSystemOrRootOrShell;
import static com.android.server.pm.parsing.PackageInfoUtils.getDeprecatedSignatures;
import static com.android.server.pm.resolution.ComponentResolver.RESOLVE_PRIORITY_SORTER;

import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.companion.virtual.VirtualDeviceManager;
import android.content.ComponentName;
import android.content.ContentProvider;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.PermissionChecker;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.AuxiliaryResolveInfo;
import android.content.pm.ComponentInfo;
import android.content.pm.Flags;
import android.content.pm.InstallSourceInfo;
import android.content.pm.InstantAppRequest;
import android.content.pm.InstantAppResolveInfo;
import android.content.pm.InstrumentationInfo;
import android.content.pm.KeySet;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ProcessInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.SharedLibraryInfo;
import android.content.pm.Signature;
import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
import android.content.pm.UserInfo;
import android.content.pm.UserPackage;
import android.content.pm.VersionedPackage;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.ParcelableException;
import android.os.PatternMatcher;
import android.os.Process;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManager;
import android.provider.ContactsContract;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
import android.util.LogPrinter;
import android.util.LongSparseLongArray;
import android.util.MathUtils;
import android.util.Pair;
import android.util.PrintWriterPrinter;
import android.util.Slog;
import android.util.SparseArray;
import android.util.Xml;
import android.util.proto.ProtoOutputStream;

import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.pm.pkg.component.ParsedActivity;
import com.android.internal.pm.pkg.component.ParsedInstrumentation;
import com.android.internal.pm.pkg.component.ParsedIntentInfo;
import com.android.internal.pm.pkg.component.ParsedMainComponent;
import com.android.internal.pm.pkg.component.ParsedProvider;
import com.android.internal.pm.pkg.component.ParsedService;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.Preconditions;
import com.android.modules.utils.TypedXmlSerializer;
import com.android.server.LocalManagerRegistry;
import com.android.server.ondeviceintelligence.OnDeviceIntelligenceManagerLocal;
import com.android.server.pm.parsing.PackageInfoUtils;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
import com.android.server.pm.permission.PermissionManagerServiceInternal;
import com.android.server.pm.permission.PermissionManagerServiceInternal.HotwordDetectionServiceProvider;
import com.android.server.pm.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageStateUtils;
import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.pkg.PackageUserStateInternal;
import com.android.server.pm.pkg.PackageUserStateUtils;
import com.android.server.pm.pkg.SharedUserApi;
import com.android.server.pm.resolution.ComponentResolverApi;
import com.android.server.pm.verify.developer.DeveloperVerificationStatusInternal;
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal;
import com.android.server.uri.UriGrantsManagerInternal;
import com.android.server.utils.WatchedArrayMap;
import com.android.server.utils.WatchedLongSparseArray;
import com.android.server.utils.WatchedSparseBooleanArray;
import com.android.server.utils.WatchedSparseIntArray;
import com.android.server.wm.ActivityTaskManagerInternal;

import libcore.util.EmptyArray;

import java.io.BufferedOutputStream;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;

/**
 * This class contains the implementation of the Computer functions.  It
 * is entirely self-contained - it has no implicit access to
 * PackageManagerService.
 */
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PRIVATE)
public class ComputerEngine implements Computer {

    // TODO: Move this to its own interface implemented by the pm.Settings implementation
    protected class Settings {

        @NonNull
        private final com.android.server.pm.Settings mSettings;

        public ArrayMap<String, ? extends PackageStateInternal> getPackages() {
            return mSettings.getPackagesLocked().untrackedStorage();
        }

        public ArrayMap<String, ? extends PackageStateInternal> getDisabledSystemPackages() {
            return mSettings.getDisabledSystemPackagesLocked().untrackedStorage();
        }

        public Settings(@NonNull com.android.server.pm.Settings settings) {
            mSettings = settings;
        }

        @Nullable
        public PackageStateInternal getPackage(@NonNull String packageName) {
            return mSettings.getPackageLPr(packageName);
        }

        @Nullable
        public PackageStateInternal getDisabledSystemPkg(@NonNull String packageName) {
            return mSettings.getDisabledSystemPkgLPr(packageName);
        }

        public boolean isEnabledAndMatch(ComponentInfo componentInfo, int flags, int userId) {
            PackageStateInternal pkgState = getPackage(componentInfo.packageName);
            if (pkgState == null) {
                return false;
            }

            return PackageUserStateUtils.isMatch(pkgState.getUserStateOrDefault(userId),
                    componentInfo, flags);
        }

        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
        public boolean isEnabledAndMatch(AndroidPackage pkg, ParsedMainComponent component,
                long flags, int userId) {
            PackageStateInternal pkgState = getPackage(component.getPackageName());
            if (pkgState == null) {
                return false;
            }

            return PackageUserStateUtils.isMatch(pkgState.getUserStateOrDefault(userId),
                    pkgState.isSystem(), pkg.isEnabled(), component, flags);
        }

        @Nullable
        public CrossProfileIntentResolver getCrossProfileIntentResolver(@UserIdInt int userId) {
            return mSettings.getCrossProfileIntentResolver(userId);
        }

        // TODO: Find replacement
        @Nullable
        public SettingBase getSettingBase(int appId) {
            return mSettings.getSettingLPr(appId);
        }

        @Nullable
        public String getRenamedPackageLPr(String packageName) {
            return mSettings.getRenamedPackageLPr(packageName);
        }

        @Nullable
        public PersistentPreferredIntentResolver getPersistentPreferredActivities(
                @UserIdInt int userId) {
            return mSettings.getPersistentPreferredActivities(userId);
        }

        public void dumpVersionLPr(@NonNull IndentingPrintWriter indentingPrintWriter) {
            mSettings.dumpVersionLPr(indentingPrintWriter);
        }

        public void dumpPreferred(PrintWriter pw, DumpState dumpState, String packageName) {
            mSettings.dumpPreferred(pw, dumpState, packageName);
        }

        // TODO: Move to separate utility
        public void writePreferredActivitiesLPr(@NonNull TypedXmlSerializer serializer, int userId,
                boolean full) throws IllegalArgumentException, IllegalStateException, IOException {
            mSettings.writePreferredActivitiesLPr(serializer, userId, full);
        }

        public PreferredIntentResolver getPreferredActivities(@UserIdInt int userId) {
            return mSettings.getPreferredActivities(userId);
        }

        @Nullable
        public SharedUserSetting getSharedUserFromId(String name) {
            try {
                return mSettings.getSharedUserLPw(name, 0, 0, false /*create*/);
            } catch (PackageManagerException ignored) {
                // This is impossible do to create being false
                throw new RuntimeException(ignored);
            }
        }

        public boolean getBlockUninstall(@UserIdInt int userId, @NonNull String packageName) {
            return mSettings.getBlockUninstallLPr(userId, packageName);
        }

        @PackageManager.EnabledState
        public int getApplicationEnabledSetting(@NonNull String packageName,
                @UserIdInt int userId) throws PackageManager.NameNotFoundException {
            return mSettings.getApplicationEnabledSettingLPr(packageName, userId);
        }

        @PackageManager.EnabledState
        public int getComponentEnabledSetting(@NonNull ComponentName component,
                @UserIdInt int userId) throws PackageManager.NameNotFoundException {
            return mSettings.getComponentEnabledSettingLPr(component, userId);
        }

        @NonNull
        public KeySetManagerService getKeySetManagerService() {
            return mSettings.getKeySetManagerService();
        }

        @NonNull
        ArrayMap<String, ? extends SharedUserApi> getSharedUsers() {
            return mSettings.getSharedUsersLocked().untrackedStorage();
        }

        @Nullable
        public SharedUserApi getSharedUserFromPackageName(String packageName) {
            return mSettings.getSharedUserSettingLPr(packageName);
        }

        @Nullable
        public SharedUserApi getSharedUserFromAppId(int sharedUserAppId) {
            return (SharedUserSetting) mSettings.getSettingLPr(sharedUserAppId);
        }

        @NonNull
        public ArraySet<PackageStateInternal> getSharedUserPackages(int sharedUserAppId) {
            final ArraySet<PackageStateInternal> res = new ArraySet<>();
            final SharedUserSetting sharedUserSetting =
                    (SharedUserSetting) mSettings.getSettingLPr(sharedUserAppId);
            if (sharedUserSetting != null) {
                final ArraySet<? extends PackageStateInternal> sharedUserPackages =
                        sharedUserSetting.getPackageStates();
                for (PackageStateInternal ps : sharedUserPackages) {
                    res.add(ps);
                }
            }
            return res;
        }

        public void dumpPackagesProto(ProtoOutputStream proto) {
            mSettings.dumpPackagesProto(proto);
        }

        public void dumpPermissions(PrintWriter pw, String packageName,
                ArraySet<String> permissionNames, DumpState dumpState) {
            mSettings.dumpPermissions(pw, packageName, permissionNames, dumpState);
        }

        public void dumpPackages(PrintWriter pw, String packageName,
                ArraySet<String> permissionNames, DumpState dumpState, boolean checkin) {
            mSettings.dumpPackagesLPr(pw, packageName, permissionNames, dumpState, checkin);
        }

        public void dumpKeySet(PrintWriter pw, String packageName, DumpState dumpState) {
            mSettings.getKeySetManagerService().dumpLPr(pw, packageName, dumpState);
        }

        public void dumpSharedUsers(PrintWriter pw, String packageName,
                ArraySet<String> permissionNames, DumpState dumpState, boolean checkin) {
            mSettings.dumpSharedUsersLPr(pw, packageName, permissionNames, dumpState, checkin);
        }

        public void dumpReadMessages(PrintWriter pw, DumpState dumpState) {
            mSettings.dumpReadMessages(pw, dumpState);
        }

        public void dumpSharedUsersProto(ProtoOutputStream proto) {
            mSettings.dumpSharedUsersProto(proto);
        }

        public List<? extends PackageStateInternal> getVolumePackages(
                @NonNull String volumeUuid) {
            return mSettings.getVolumePackagesLPr(volumeUuid);
        }
    }

    private static final Comparator<ProviderInfo> sProviderInitOrderSorter = (p1, p2) -> {
        final int v1 = p1.initOrder;
        final int v2 = p2.initOrder;
        return (v1 > v2) ? -1 : ((v1 < v2) ? 1 : 0);
    };

    private final int mVersion;

    // The administrative use counter.
    private int mUsed = 0;

    // Cached attributes.  The names in this class are the same as the
    // names in PackageManagerService; see that class for documentation.
    protected final Settings mSettings;
    private final WatchedSparseIntArray mIsolatedOwners;
    private final WatchedArrayMap<String, AndroidPackage> mPackages;
    private final WatchedArrayMap<ComponentName, ParsedInstrumentation>
            mInstrumentation;
    private final SharedLibrariesRead mSharedLibraries;
    private final ComponentName mLocalResolveComponentName;
    private final ActivityInfo mResolveActivity;
    private final WatchedSparseBooleanArray mWebInstantAppsDisabled;
    private final ActivityInfo mLocalInstantAppInstallerActivity;
    private final ResolveInfo mInstantAppInstallerInfo;
    private final InstantAppRegistry mInstantAppRegistry;
    private final ApplicationInfo mLocalAndroidApplication;
    private final AppsFilterSnapshot mAppsFilter;
    private final WatchedArrayMap<String, Integer> mFrozenPackages;

    // Immutable service attribute
    private final String mAppPredictionServicePackage;

    // The following are not cloned since changes to these have never
    // been guarded by the PMS lock.
    private final Context mContext;
    private final UserManagerService mUserManager;
    private final PermissionManagerServiceInternal mPermissionManager;
    private final ApexManager mApexManager;
    private final PackageManagerServiceInjector mInjector;
    private final ComponentResolverApi mComponentResolver;
    private final InstantAppResolverConnection mInstantAppResolverConnection;
    private final DefaultAppProvider mDefaultAppProvider;
    private final DomainVerificationManagerInternal mDomainVerificationManager;
    private final PackageManagerInternal.ExternalSourcesPolicy mExternalSourcesPolicy;
    private final CrossProfileIntentResolverEngine mCrossProfileIntentResolverEngine;

    // PackageManagerService attributes that are primitives are referenced through the
    // pms object directly.  Primitives are the only attributes so referenced.
    protected final PackageManagerService mService;
    private boolean safeMode() {
        return mService.getSafeMode();
    }
    protected ComponentName resolveComponentName() {
        return mLocalResolveComponentName;
    }
    protected ActivityInfo instantAppInstallerActivity() {
        return mLocalInstantAppInstallerActivity;
    }
    protected ApplicationInfo androidApplication() {
        return mLocalAndroidApplication;
    }

    ComputerEngine(PackageManagerService.Snapshot args, int version) {
        mVersion = version;
        mSettings = new Settings(args.settings);
        mIsolatedOwners = args.isolatedOwners;
        mPackages = args.packages;
        mSharedLibraries = args.sharedLibraries;
        mInstrumentation = args.instrumentation;
        mWebInstantAppsDisabled = args.webInstantAppsDisabled;
        mLocalResolveComponentName = args.resolveComponentName;
        mResolveActivity = args.resolveActivity;
        mLocalInstantAppInstallerActivity = args.instantAppInstallerActivity;
        mInstantAppInstallerInfo = args.instantAppInstallerInfo;
        mInstantAppRegistry = args.instantAppRegistry;
        mLocalAndroidApplication = args.androidApplication;
        mAppsFilter = args.appsFilter;
        mFrozenPackages = args.frozenPackages;
        mComponentResolver = args.componentResolver;

        mAppPredictionServicePackage = args.appPredictionServicePackage;

        // The following are not cached copies.  Instead they are
        // references to outside services.
        mPermissionManager = args.service.mPermissionManager;
        mUserManager = args.service.mUserManager;
        mContext = args.service.mContext;
        mInjector = args.service.mInjector;
        mApexManager = args.service.mApexManager;
        mInstantAppResolverConnection = args.service.mInstantAppResolverConnection;
        mDefaultAppProvider = args.service.getDefaultAppProvider();
        mDomainVerificationManager = args.service.mDomainVerificationManager;
        mExternalSourcesPolicy = args.service.mExternalSourcesPolicy;
        mCrossProfileIntentResolverEngine = new CrossProfileIntentResolverEngine(
                mUserManager, mDomainVerificationManager, mDefaultAppProvider, mContext);

        // Used to reference PMS attributes that are primitives and which are not
        // updated under control of the PMS lock.
        mService = args.service;
    }

    @Override
    public int getVersion() {
        return mVersion;
    }

    /**
     * Record that the snapshot was used.
     */
    public final Computer use() {
        mUsed++;
        return this;
    }

    /**
     * Return the usage counter.
     */
    public final int getUsed() {
        return mUsed;
    }

    public final @NonNull List<ResolveInfo> queryIntentActivitiesInternal(
            Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
            @PackageManagerInternal.PrivateResolveFlags long privateResolveFlags,
            int filterCallingUid, int callingPid, int userId, boolean resolveForStart,
            boolean allowDynamicSplits) {
        if (!mUserManager.exists(userId)) return Collections.emptyList();

        // Allow to match activities of quarantined packages.
        flags |= PackageManager.MATCH_QUARANTINED_COMPONENTS;

        final String instantAppPkgName = getInstantAppPackageName(filterCallingUid);
        enforceCrossUserPermission(Binder.getCallingUid(), userId,
                false /* requireFullPermission */, false /* checkShell */,
                "query intent activities");
        final String pkgName = intent.getPackage();
        Intent originalIntent = null;
        ComponentName comp = intent.getComponent();
        if (comp == null) {
            if (intent.getSelector() != null) {
                originalIntent = intent;
                intent = intent.getSelector();
                comp = intent.getComponent();
            }
        }

        flags = updateFlagsForResolve(flags, userId, filterCallingUid, resolveForStart,
                comp != null || pkgName != null /*onlyExposedExplicitly*/,
                isImplicitImageCaptureIntentAndNotSetByDpc(intent, userId, resolvedType,
                        flags));

        var args = new SaferIntentUtils.IntentArgs(intent, resolvedType,
                false /* isReceiver */, resolveForStart, filterCallingUid, callingPid);
        args.platformCompat = mInjector.getCompatibility();
        args.snapshot = this;

        List<ResolveInfo> list = Collections.emptyList();
        boolean skipPostResolution = false;
        if (comp != null) {
            final ActivityInfo ai = getActivityInfo(comp, flags, userId);
            if (ai != null) {
                // When specifying an explicit component, we prevent the activity from being
                // used when either 1) the calling package is normal and the activity is within
                // an ephemeral application or 2) the calling package is ephemeral and the
                // activity is not visible to ephemeral applications.
                final boolean matchInstantApp =
                        (flags & PackageManager.MATCH_INSTANT) != 0;
                final boolean matchVisibleToInstantAppOnly =
                        (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
                final boolean matchExplicitlyVisibleOnly =
                        (flags & PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY) != 0;
                final boolean isCallerInstantApp =
                        instantAppPkgName != null;
                final boolean isTargetSameInstantApp =
                        comp.getPackageName().equals(instantAppPkgName);
                final boolean isTargetInstantApp =
                        (ai.applicationInfo.privateFlags
                                & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
                final boolean isTargetVisibleToInstantApp =
                        (ai.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
                final boolean isTargetExplicitlyVisibleToInstantApp =
                        isTargetVisibleToInstantApp
                                && (ai.flags & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP)
                                == 0;
                final boolean isTargetHiddenFromInstantApp =
                        !isTargetVisibleToInstantApp
                                || (matchExplicitlyVisibleOnly
                                && !isTargetExplicitlyVisibleToInstantApp);
                final boolean blockInstantResolution =
                        !isTargetSameInstantApp
                                && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
                                || (matchVisibleToInstantAppOnly && isCallerInstantApp
                                && isTargetHiddenFromInstantApp));
                final boolean resolveForStartNonExported = resolveForStart
                                && !ai.exported
                                && !isCallerSameApp(pkgName, filterCallingUid);
                final boolean blockNormalResolution =
                        (!resolveForStart || resolveForStartNonExported)
                                && !isTargetInstantApp
                                && !isCallerInstantApp
                                && shouldFilterApplication(
                                getPackageStateInternal(ai.applicationInfo.packageName,
                                        Process.SYSTEM_UID), filterCallingUid, userId);
                if (!blockInstantResolution && !blockNormalResolution) {
                    final ResolveInfo ri = new ResolveInfo();
                    ri.activityInfo = ai;
                    ri.userHandle = UserHandle.of(userId);
                    list = new ArrayList<>(1);
                    list.add(ri);
                    SaferIntentUtils.enforceIntentFilterMatching(args, list);
                }
            }
        } else {
            QueryIntentActivitiesResult lockedResult =
                    queryIntentActivitiesInternalBody(
                            intent, resolvedType, flags, filterCallingUid, userId,
                            resolveForStart, allowDynamicSplits, pkgName, instantAppPkgName);
            if (lockedResult.answer != null) {
                skipPostResolution = true;
                list = lockedResult.answer;
            } else {
                if (lockedResult.addInstant) {
                    String callingPkgName = getInstantAppPackageName(filterCallingUid);
                    boolean isRequesterInstantApp = isInstantApp(callingPkgName, userId);
                    lockedResult.result = maybeAddInstantAppInstaller(
                            lockedResult.result, intent, resolvedType, flags,
                            userId, resolveForStart, isRequesterInstantApp);
                }
                if (lockedResult.sortResult) {
                    lockedResult.result.sort(RESOLVE_PRIORITY_SORTER);
                }
                list = lockedResult.result;
            }
            SaferIntentUtils.blockNullAction(args, list);
        }

        if (originalIntent != null) {
            // We also have to ensure all components match the original intent
            args.intent = originalIntent;
            SaferIntentUtils.enforceIntentFilterMatching(args, list);
        }

        return skipPostResolution ? list : applyPostResolutionFilter(
                list, instantAppPkgName, allowDynamicSplits, filterCallingUid,
                resolveForStart, userId, intent);
    }

    @NonNull
    @Override
    public final List<ResolveInfo> queryIntentActivitiesInternal(Intent intent, String resolvedType,
            @PackageManager.ResolveInfoFlagsBits long flags, int filterCallingUid, int userId) {
        return queryIntentActivitiesInternal(
                intent, resolvedType, flags, 0 /*privateResolveFlags*/, filterCallingUid,
                Process.INVALID_PID, userId,
                /*resolveForStart*/ false, /*allowDynamicSplits*/ true);
    }

    public final @NonNull List<ResolveInfo> queryIntentActivitiesInternal(Intent intent,
            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
        return queryIntentActivitiesInternal(
                intent, resolvedType, flags, 0 /*privateResolveFlags*/,
                Binder.getCallingUid(), Process.INVALID_PID, userId,
                /*resolveForStart*/ false, /*allowDynamicSplits*/ true);
    }

    public final @NonNull List<ResolveInfo> queryIntentServicesInternal(
            Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
            int userId, int callingUid, int callingPid,
            boolean includeInstantApps, boolean resolveForStart) {
        if (!mUserManager.exists(userId)) return Collections.emptyList();
        enforceCrossUserOrProfilePermission(Binder.getCallingUid(),
                userId,
                false /*requireFullPermission*/,
                false /*checkShell*/,
                "query intent services");
        final String instantAppPkgName = getInstantAppPackageName(callingUid);
        flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps,
                false /* isImplicitImageCaptureIntentAndNotSetByDpc */);

        var args = new SaferIntentUtils.IntentArgs(intent, resolvedType,
                false /* isReceiver */, resolveForStart, callingUid, callingPid);
        args.platformCompat = mInjector.getCompatibility();
        args.snapshot = this;

        Intent originalIntent = null;
        ComponentName comp = intent.getComponent();
        if (comp == null) {
            if (intent.getSelector() != null) {
                originalIntent = intent;
                intent = intent.getSelector();
                comp = intent.getComponent();
            }
        }
        List<ResolveInfo> list = Collections.emptyList();
        if (comp != null) {
            final ServiceInfo si = getServiceInfo(comp, flags, userId);
            if (si != null) {
                // When specifying an explicit component, we prevent the service from being
                // used when either 1) the service is in an instant application and the
                // caller is not the same instant application or 2) the calling package is
                // ephemeral and the activity is not visible to ephemeral applications.
                final boolean matchInstantApp =
                        (flags & PackageManager.MATCH_INSTANT) != 0;
                final boolean matchVisibleToInstantAppOnly =
                        (flags & PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY) != 0;
                final boolean isCallerInstantApp =
                        instantAppPkgName != null;
                final boolean isTargetSameInstantApp =
                        comp.getPackageName().equals(instantAppPkgName);
                final boolean isTargetInstantApp =
                        (si.applicationInfo.privateFlags
                                & ApplicationInfo.PRIVATE_FLAG_INSTANT) != 0;
                final boolean isTargetHiddenFromInstantApp =
                        (si.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) == 0;
                final boolean blockInstantResolution =
                        !isTargetSameInstantApp
                                && ((!matchInstantApp && !isCallerInstantApp && isTargetInstantApp)
                                || (matchVisibleToInstantAppOnly && isCallerInstantApp
                                && isTargetHiddenFromInstantApp));

                final boolean blockNormalResolution = !isTargetInstantApp && !isCallerInstantApp
                        && shouldFilterApplication(
                        getPackageStateInternal(si.applicationInfo.packageName,
                                Process.SYSTEM_UID), callingUid, userId);
                if (!blockInstantResolution && !blockNormalResolution) {
                    final ResolveInfo ri = new ResolveInfo();
                    ri.serviceInfo = si;
                    list = new ArrayList<>(1);
                    list.add(ri);
                    SaferIntentUtils.enforceIntentFilterMatching(args, list);
                }
            }
        } else {
            list = queryIntentServicesInternalBody(intent, resolvedType, flags,
                    userId, callingUid, instantAppPkgName);
            SaferIntentUtils.blockNullAction(args, list);
        }

        if (originalIntent != null) {
            // We also have to ensure all components match the original intent
            args.intent = originalIntent;
            SaferIntentUtils.enforceIntentFilterMatching(args, list);
        }

        return list;
    }

    protected @NonNull List<ResolveInfo> queryIntentServicesInternalBody(Intent intent,
            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId,
            int callingUid, String instantAppPkgName) {
        // reader
        String pkgName = intent.getPackage();
        if (pkgName == null) {
            final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(this, intent,
                    resolvedType, flags, userId);
            if (resolveInfos == null) {
                return Collections.emptyList();
            }
            return applyPostServiceResolutionFilter(
                    resolveInfos, instantAppPkgName, userId, callingUid);
        }
        final AndroidPackage pkg = mPackages.get(pkgName);
        if (pkg != null) {
            final List<ResolveInfo> resolveInfos = mComponentResolver.queryServices(this, intent,
                    resolvedType, flags, pkg.getServices(),
                    userId);
            if (resolveInfos == null) {
                return Collections.emptyList();
            }
            return applyPostServiceResolutionFilter(
                    resolveInfos, instantAppPkgName, userId, callingUid);
        }
        return Collections.emptyList();
    }

    public @NonNull QueryIntentActivitiesResult queryIntentActivitiesInternalBody(
            Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
            int filterCallingUid, int userId, boolean resolveForStart, boolean allowDynamicSplits,
            String pkgName, String instantAppPkgName) {
        // reader
        boolean sortResult = false;
        boolean addInstant = false;
        List<ResolveInfo> result = new ArrayList<>();
        // crossProfileResults will hold resolve infos from resolution across profiles.
        List<CrossProfileDomainInfo> crossProfileResults = new ArrayList<>();
        if (pkgName == null) {
            if (!mCrossProfileIntentResolverEngine.shouldSkipCurrentProfile(this, intent,
                    resolvedType, userId)) {

                final List<ResolveInfo> queryResult = mComponentResolver.queryActivities(this,
                        intent, resolvedType, flags, userId);
                // If the user doesn't exist, the queryResult is null
                if (queryResult != null) {
                    /*
                     Check for results in the current profile only if there is no
                     {@link CrossProfileIntentFilter} for user with flag
                     {@link PackageManager.SKIP_CURRENT_PROFILE} set.
                     */
                    result.addAll(filterIfNotSystemUser(queryResult, userId));
                }
            }
            addInstant = isInstantAppResolutionAllowed(intent, result, userId,
                    false /*skipPackageCheck*/, flags);

            boolean hasNonNegativePriorityResult = hasNonNegativePriority(result);

            /*
             Calling {@link com.android.server.pm.CrossProfileIntentResolverEngine#resolveIntent} to
             get list of {@link CrossProfileDomainInfo} which have {@link ResolveInfo}s from linked
             profiles.
             */
            crossProfileResults = mCrossProfileIntentResolverEngine.resolveIntent(this, intent,
                    resolvedType, userId, flags, pkgName, hasNonNegativePriorityResult,
                    resolveForStart, mSettings::getPackage);
            if (intent.hasWebURI() || !crossProfileResults.isEmpty()) sortResult = true;
        } else {
            final PackageStateInternal setting =
                    getPackageStateInternal(pkgName, Process.SYSTEM_UID);

            if (setting != null && setting.getAndroidPackage() != null && (resolveForStart
                    || !shouldFilterApplication(setting, filterCallingUid, userId))) {
                final List<ResolveInfo> queryResult = mComponentResolver.queryActivities(this,
                        intent, resolvedType, flags, setting.getAndroidPackage().getActivities(),
                        userId);
                // If the user doesn't exist, the queryResult is null
                if (queryResult != null) {
                    result.addAll(filterIfNotSystemUser(queryResult, userId));
                }
            }
            if (result == null || result.size() == 0) {
                // the caller wants to resolve for a particular package; however, there
                // were no installed results, so, try to find an ephemeral result
                addInstant = isInstantAppResolutionAllowed(intent, null /*result*/, userId,
                        true /*skipPackageCheck*/, flags);
            }
            /*
             Calling {@link com.android.server.pm.CrossProfileIntentResolverEngine#resolveIntent} to
             get list of {@link CrossProfileDomainInfo} which have {@link ResolveInfo}s from linked
             profiles.
             */
            crossProfileResults = mCrossProfileIntentResolverEngine.resolveIntent(this, intent,
                    resolvedType, userId, flags, pkgName, false,
                    resolveForStart, mSettings::getPackage);
        }

        /*
             Calling {@link com.android.server.pm.
             CrossProfileIntentResolverEngine#combineFilterAndCreateQueryAcitivitesResponse} to
             combine results from current and cross profiles. This also filters any resolve info
             based on domain preference(if required).
         */
        return mCrossProfileIntentResolverEngine
                .combineFilterAndCreateQueryActivitiesResponse(this, intent, resolvedType,
                        instantAppPkgName, pkgName, allowDynamicSplits, flags, userId,
                        filterCallingUid, resolveForStart, result, crossProfileResults,
                        areWebInstantAppsDisabled(userId), addInstant, sortResult,
                        mSettings::getPackage);
    }

    /**
     * Returns the activity component that can handle install failures.
     * <p>By default, the instant application installer handles failures. However, an
     * application may want to handle failures on its own. Applications do this by
     * creating an activity with an intent filter that handles the action
     * {@link Intent#ACTION_INSTALL_FAILURE}.
     */
    private @Nullable ComponentName findInstallFailureActivity(
            String packageName, int filterCallingUid, int userId) {
        final Intent failureActivityIntent = new Intent(Intent.ACTION_INSTALL_FAILURE);
        failureActivityIntent.setPackage(packageName);
        // IMPORTANT: disallow dynamic splits to avoid an infinite loop
        final List<ResolveInfo> result = queryIntentActivitiesInternal(
                failureActivityIntent, null /*resolvedType*/, 0 /*flags*/,
                0 /*privateResolveFlags*/, filterCallingUid, Process.INVALID_PID, userId,
                /*resolveForStart*/ false, /*allowDynamicSplits*/ false);
        final int numResults = result.size();
        if (numResults > 0) {
            for (int i = 0; i < numResults; i++) {
                final ResolveInfo info = result.get(i);
                if (info.activityInfo.splitName != null) {
                    continue;
                }
                return new ComponentName(packageName, info.activityInfo.name);
            }
        }
        return null;
    }

    public final ActivityInfo getActivityInfo(ComponentName component,
            @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
        return getActivityInfoInternal(component, flags, Binder.getCallingUid(), userId);
    }

    /**
     * Similar to {@link Computer#getActivityInfo(android.content.ComponentName, long, int)} but
     * only visible as internal service. This method bypass INTERACT_ACROSS_USERS or
     * INTERACT_ACROSS_USERS_FULL permission checks and only to be used for intent resolution across
     * chained cross profiles
     * @param component application's component
     * @param flags resolve info flags
     * @param userId user id where activity resides
     * @return ActivityInfo corresponding to requested component.
     */
    public final ActivityInfo getActivityInfoCrossProfile(ComponentName component,
            @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
        if (!mUserManager.exists(userId)) return null;
        flags = updateFlagsForComponent(flags, userId);

        return getActivityInfoInternalBody(component, flags, Binder.getCallingUid(), userId);
    }

    /**
     * Important: The provided filterCallingUid is used exclusively to filter out activities
     * that can be seen based on user state. It's typically the original caller uid prior
     * to clearing. Because it can only be provided by trusted code, its value can be
     * trusted and will be used as-is; unlike userId which will be validated by this method.
     */
    public final ActivityInfo getActivityInfoInternal(ComponentName component,
            @PackageManager.ResolveInfoFlagsBits long flags, int filterCallingUid, int userId) {
        if (!mUserManager.exists(userId)) return null;
        flags = updateFlagsForComponent(flags, userId);

        if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) {
            enforceCrossUserPermission(Binder.getCallingUid(), userId,
                    false /* requireFullPermission */, false /* checkShell */,
                    "get activity info");
        }

        return getActivityInfoInternalBody(component, flags, filterCallingUid, userId);
    }

    protected ActivityInfo getActivityInfoInternalBody(ComponentName component,
            @PackageManager.ResolveInfoFlagsBits long flags, int filterCallingUid, int userId) {
        ParsedActivity a = mComponentResolver.getActivity(component);

        // Allow to match activities of quarantined packages.
        flags |= PackageManager.MATCH_QUARANTINED_COMPONENTS;

        if (DEBUG_PACKAGE_INFO) Log.v(TAG, "getActivityInfo " + component + ": " + a);

        AndroidPackage pkg = a == null ? null : mPackages.get(a.getPackageName());
        if (pkg != null && mSettings.isEnabledAndMatch(pkg, a, flags, userId)) {
            PackageStateInternal ps = mSettings.getPackage(component.getPackageName());
            if (ps == null) return null;
            if (shouldFilterApplication(
                    ps, filterCallingUid, component, TYPE_ACTIVITY, userId)) {
                return null;
            }
            return PackageInfoUtils.generateActivityInfo(pkg,
                    a, flags, ps.getUserStateOrDefault(userId), userId, ps);
        }
        if (resolveComponentName().equals(component)) {
            return PackageInfoUtils.generateDelegateActivityInfo(mResolveActivity,
                    flags, PackageUserStateInternal.DEFAULT, userId);
        }
        return null;
    }

    public AndroidPackage getPackage(String packageName) {
        packageName = resolveInternalPackageName(
                packageName, PackageManager.VERSION_CODE_HIGHEST);
        return mPackages.get(packageName);
    }

    public AndroidPackage getPackage(int uid) {
        final String[] packageNames = getPackagesForUidInternal(uid, Process.SYSTEM_UID);
        AndroidPackage pkg = null;
        final int numPackages = packageNames == null ? 0 : packageNames.length;
        for (int i = 0; pkg == null && i < numPackages; i++) {
            pkg = mPackages.get(packageNames[i]);
        }
        return pkg;
    }

    public final ApplicationInfo generateApplicationInfoFromSettings(String packageName,
            long flags, int filterCallingUid, int userId) {
        if (!mUserManager.exists(userId)) return null;
        PackageStateInternal ps = mSettings.getPackage(packageName);
        if (ps != null) {
            if (filterSharedLibPackage(ps, filterCallingUid, userId, flags)) {
                return null;
            }
            if (shouldFilterApplication(ps, filterCallingUid, userId)) {
                return null;
            }
            if (ps.getAndroidPackage() == null) {
                final PackageInfo pInfo = generatePackageInfo(ps, flags, userId);
                if (pInfo != null) {
                    return pInfo.applicationInfo;
                }
                return null;
            }
            ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(ps.getPkg(),
                    flags, ps.getUserStateOrDefault(userId), userId, ps);
            if (ai != null) {
                ai.packageName = resolveExternalPackageName(ps.getPkg());
            }
            return ai;
        }
        return null;
    }

    public final ApplicationInfo getApplicationInfo(String packageName,
            @PackageManager.ApplicationInfoFlagsBits long flags, int userId) {
        return getApplicationInfoInternal(packageName, flags, Binder.getCallingUid(), userId);
    }

    /**
     * Important: The provided filterCallingUid is used exclusively to filter out applications
     * that can be seen based on user state. It's typically the original caller uid prior
     * to clearing. Because it can only be provided by trusted code, its value can be
     * trusted and will be used as-is; unlike userId which will be validated by this method.
     */
    public final ApplicationInfo getApplicationInfoInternal(String packageName,
            @PackageManager.ApplicationInfoFlagsBits long flags,
            int filterCallingUid, int userId) {
        if (!mUserManager.exists(userId)) return null;
        flags = updateFlagsForApplication(flags, userId);

        if (!isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId)) {
            enforceCrossUserPermission(Binder.getCallingUid(), userId,
                    false /* requireFullPermission */, false /* checkShell */,
                    "get application info");
        }

        return getApplicationInfoInternalBody(packageName, flags, filterCallingUid, userId);
    }

    protected ApplicationInfo getApplicationInfoInternalBody(String packageName,
            @PackageManager.ApplicationInfoFlagsBits long flags,
            int filterCallingUid, int userId) {
        // writer
        // Normalize package name to handle renamed packages and static libs
        packageName = resolveInternalPackageName(packageName,
                PackageManager.VERSION_CODE_HIGHEST);

        AndroidPackage p = mPackages.get(packageName);
        if (DEBUG_PACKAGE_INFO) {
            Log.v(
                    TAG, "getApplicationInfo " + packageName
                            + ": " + p);
        }
        final boolean matchApex = (flags & MATCH_APEX) != 0;
        if (p != null) {
            PackageStateInternal ps = mSettings.getPackage(packageName);
            if (ps == null) return null;
            if (!matchApex && p.isApex()) {
                return null;
            }
            if (filterSharedLibPackage(ps, filterCallingUid, userId, flags)) {
                return null;
            }
            if (shouldFilterApplication(ps, filterCallingUid, userId)) {
                return null;
            }
            // Note: isEnabledLP() does not apply here - always return info
            ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(
                    p, flags, ps.getUserStateOrDefault(userId), userId, ps);
            if (ai != null) {
                ai.packageName = resolveExternalPackageName(p);
            }
            return ai;
        }
        if ("android".equals(packageName) || "system".equals(packageName)) {
            return androidApplication();
        }
        if ((flags & (MATCH_KNOWN_PACKAGES | MATCH_ARCHIVED_PACKAGES)) != 0) {
            // Already generates the external package name
            return generateApplicationInfoFromSettings(packageName,
                    flags, filterCallingUid, userId);
        }
        return null;
    }

    /**
     * Report the 'Home' activity which is currently set as "always use this one". If non is set
     * then reports the most likely home activity or null if there are more than one.
     */
    public final ComponentName getDefaultHomeActivity(int userId) {
        List<ResolveInfo> allHomeCandidates = new ArrayList<>();
        ComponentName cn = getHomeActivitiesAsUser(allHomeCandidates, userId);
        if (cn != null) {
            return cn;
        }
        // TODO: This should not happen since there should always be a default package set for
        //  ROLE_HOME in RoleManager. Continue with a warning log for now.
        Slog.w(TAG, "Default package for ROLE_HOME is not set in RoleManager");

        // Find the launcher with the highest priority and return that component if there are no
        // other home activity with the same priority.
        int lastPriority = Integer.MIN_VALUE;
        ComponentName lastComponent = null;
        final int size = allHomeCandidates.size();
        for (int i = 0; i < size; i++) {
            final ResolveInfo ri = allHomeCandidates.get(i);
            if (ri.priority > lastPriority) {
                lastComponent = ri.activityInfo.getComponentName();
                lastPriority = ri.priority;
            } else if (ri.priority == lastPriority) {
                // Two components found with same priority.
                lastComponent = null;
            }
        }
        return lastComponent;
    }

    public final ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
            int userId) {
        Intent intent  = getHomeIntent();
        List<ResolveInfo> resolveInfos = queryIntentActivitiesInternal(intent, null,
                PackageManager.GET_META_DATA, userId);
        allHomeCandidates.clear();
        if (resolveInfos == null) {
            return null;
        }
        allHomeCandidates.addAll(resolveInfos);

        String packageName = mDefaultAppProvider.getDefaultHome(userId);
        if (packageName == null) {
            // Role changes are not and cannot be atomic because its implementation lives inside
            // a system app, so when the home role changes, there is a window when the previous
            // role holder is removed and the new role holder is granted the preferred activity,
            // but hasn't become the role holder yet. However, this case may be easily hit
            // because the preferred activity change triggers a broadcast and receivers may try
            // to get the default home activity there. So we need to fix it for this time
            // window, and an easy workaround is to fallback to the current preferred activity.
            final int appId = UserHandle.getAppId(Binder.getCallingUid());
            final boolean filtered = appId >= Process.FIRST_APPLICATION_UID;
            PackageManagerService.FindPreferredActivityBodyResult result =
                    findPreferredActivityInternal(intent, null, 0, resolveInfos, true, false,
                            false, userId, filtered);
            ResolveInfo preferredResolveInfo =  result.mPreferredResolveInfo;
            if (preferredResolveInfo != null && preferredResolveInfo.activityInfo != null) {
                packageName = preferredResolveInfo.activityInfo.packageName;
            }
        }
        if (packageName == null) {
            return null;
        }

        int resolveInfosSize = resolveInfos.size();
        for (int i = 0; i < resolveInfosSize; i++) {
            ResolveInfo resolveInfo = resolveInfos.get(i);

            if (resolveInfo.activityInfo != null && TextUtils.equals(
                    resolveInfo.activityInfo.packageName, packageName)) {
                return new ComponentName(resolveInfo.activityInfo.packageName,
                        resolveInfo.activityInfo.name);
            }
        }
        return null;
    }

    public final CrossProfileDomainInfo getCrossProfileDomainPreferredLpr(Intent intent,
            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int sourceUserId,
            int parentUserId) {
        if (!mUserManager.hasUserRestriction(UserManager.ALLOW_PARENT_PROFILE_APP_LINKING,
                sourceUserId)) {
            return null;
        }
        List<ResolveInfo> resultTargetUser = mComponentResolver.queryActivities(this, intent,
                resolvedType, flags, parentUserId);

        if (resultTargetUser == null || resultTargetUser.isEmpty()) {
            return null;
        }
        CrossProfileDomainInfo result = null;
        int size = resultTargetUser.size();
        for (int i = 0; i < size; i++) {
            ResolveInfo riTargetUser = resultTargetUser.get(i);
            // Intent filter verification is only for filters that specify a host. So don't
            //return
            // those that handle all web uris.
            if (riTargetUser.handleAllWebDataURI) {
                continue;
            }
            String packageName = riTargetUser.activityInfo.packageName;
            PackageStateInternal ps = mSettings.getPackage(packageName);
            if (ps == null) {
                continue;
            }

            int approvalLevel = mDomainVerificationManager
                    .approvalLevelForDomain(ps, intent, flags, parentUserId);

            if (result == null) {
                result = new CrossProfileDomainInfo(createForwardingResolveInfoUnchecked(
                        new WatchedIntentFilter(), sourceUserId, parentUserId), approvalLevel,
                        parentUserId);
            } else {
                result.mHighestApprovalLevel =
                        Math.max(approvalLevel, result.mHighestApprovalLevel);
            }
        }
        if (result != null && result.mHighestApprovalLevel
                <= DomainVerificationManagerInternal.APPROVAL_LEVEL_NONE) {
            return null;
        }
        return result;
    }

    public final Intent getHomeIntent() {
        Intent intent = new Intent(Intent.ACTION_MAIN);
        intent.addCategory(Intent.CATEGORY_HOME);
        intent.addCategory(Intent.CATEGORY_DEFAULT);
        return intent;
    }

    public final List<CrossProfileIntentFilter> getMatchingCrossProfileIntentFilters(
            Intent intent, String resolvedType, int userId) {
        CrossProfileIntentResolver resolver = mSettings.getCrossProfileIntentResolver(userId);
        if (resolver != null) {
            return resolver.queryIntent(this, intent, resolvedType, false /*defaultOnly*/,
                    userId);
        }
        return null;
    }

    /**
     * Filters out ephemeral activities.
     * <p>When resolving for an ephemeral app, only activities that 1) are defined in the
     * ephemeral app or 2) marked with {@code visibleToEphemeral} are returned.
     *
     * @param resolveInfos The pre-filtered list of resolved activities
     * @param ephemeralPkgName The ephemeral package name. If {@code null}, no filtering
     *          is performed.
     * @param intent
     * @return A filtered list of resolved activities.
     */
    public final List<ResolveInfo> applyPostResolutionFilter(
            @NonNull List<ResolveInfo> resolveInfos,
            String ephemeralPkgName, boolean allowDynamicSplits, int filterCallingUid,
            boolean resolveForStart, int userId, Intent intent) {
        final boolean blockInstant = intent.isWebIntent() && areWebInstantAppsDisabled(userId);
        for (int i = resolveInfos.size() - 1; i >= 0; i--) {
            final ResolveInfo info = resolveInfos.get(i);
            // remove locally resolved instant app web results when disabled
            if (info.isInstantAppAvailable && blockInstant) {
                resolveInfos.remove(i);
                continue;
            }
            // allow activities that are defined in the provided package
            if (allowDynamicSplits
                    && info.activityInfo != null
                    && info.activityInfo.splitName != null
                    && !ArrayUtils.contains(info.activityInfo.applicationInfo.splitNames,
                    info.activityInfo.splitName)) {
                if (instantAppInstallerActivity() == null) {
                    if (DEBUG_INSTALL) {
                        Slog.v(TAG, "No installer - not adding it to the ResolveInfo list");
                    }
                    resolveInfos.remove(i);
                    continue;
                }
                if (blockInstant && isInstantAppInternal(
                        info.activityInfo.packageName, userId, Process.SYSTEM_UID)) {
                    resolveInfos.remove(i);
                    continue;
                }
                // requested activity is defined in a split that hasn't been installed yet.
                // add the installer to the resolve list
                if (DEBUG_INSTALL) {
                    Slog.v(TAG, "Adding installer to the ResolveInfo list");
                }
                final ResolveInfo installerInfo = new ResolveInfo(
                        mInstantAppInstallerInfo);
                final ComponentName installFailureActivity = findInstallFailureActivity(
                        info.activityInfo.packageName,  filterCallingUid, userId);
                installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
                        installFailureActivity,
                        info.activityInfo.packageName,
                        info.activityInfo.applicationInfo.longVersionCode,
                        info.activityInfo.splitName);
                // add a non-generic filter
                installerInfo.filter = new IntentFilter();

                // This resolve info may appear in the chooser UI, so let us make it
                // look as the one it replaces as far as the user is concerned which
                // requires loading the correct label and icon for the resolve info.
                installerInfo.resolvePackageName = info.getComponentInfo().packageName;
                installerInfo.labelRes = info.resolveLabelResId();
                installerInfo.icon = info.resolveIconResId();
                installerInfo.isInstantAppAvailable = true;
                resolveInfos.set(i, installerInfo);
                continue;
            }
            if (ephemeralPkgName == null) {
                // caller is a full app
                SettingBase callingSetting =
                        mSettings.getSettingBase(UserHandle.getAppId(filterCallingUid));
                PackageStateInternal resolvedSetting =
                        getPackageStateInternal(info.activityInfo.packageName, 0);
                if (resolveForStart
                        || !mAppsFilter.shouldFilterApplication(this,
                        filterCallingUid, callingSetting, resolvedSetting, userId)) {
                    continue;
                }
            } else if (ephemeralPkgName.equals(info.activityInfo.packageName)) {
                // caller is same app; don't need to apply any other filtering
                continue;
            } else if (resolveForStart
                    && (intent.isWebIntent()
                    || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) != 0)
                    && intent.getPackage() == null
                    && intent.getComponent() == null) {
                // ephemeral apps can launch other ephemeral apps indirectly
                continue;
            } else if (((info.activityInfo.flags & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP)
                    != 0)
                    && !info.activityInfo.applicationInfo.isInstantApp()) {
                // allow activities that have been explicitly exposed to ephemeral apps
                continue;
            }
            resolveInfos.remove(i);
        }
        return resolveInfos;
    }

    private List<ResolveInfo> applyPostServiceResolutionFilter(List<ResolveInfo> resolveInfos,
            String instantAppPkgName, @UserIdInt int userId, int filterCallingUid) {
        for (int i = resolveInfos.size() - 1; i >= 0; i--) {
            final ResolveInfo info = resolveInfos.get(i);
            if (instantAppPkgName == null) {
                SettingBase callingSetting =
                        mSettings.getSettingBase(UserHandle.getAppId(filterCallingUid));
                PackageStateInternal resolvedSetting =
                        getPackageStateInternal(info.serviceInfo.packageName, 0);
                if (!mAppsFilter.shouldFilterApplication(this,
                        filterCallingUid, callingSetting, resolvedSetting, userId)) {
                    continue;
                }
            }
            final boolean isEphemeralApp = info.serviceInfo.applicationInfo.isInstantApp();
            // allow services that are defined in the provided package
            if (isEphemeralApp && instantAppPkgName.equals(info.serviceInfo.packageName)) {
                if (info.serviceInfo.splitName != null
                        && !ArrayUtils.contains(info.serviceInfo.applicationInfo.splitNames,
                        info.serviceInfo.splitName)) {
                    if (instantAppInstallerActivity() == null) {
                        if (DEBUG_INSTANT) {
                            Slog.v(TAG, "No installer - not adding it to the ResolveInfo"
                                    + "list");
                        }
                        resolveInfos.remove(i);
                        continue;
                    }
                    // requested service is defined in a split that hasn't been installed yet.
                    // add the installer to the resolve list
                    if (DEBUG_INSTANT) {
                        Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
                    }
                    final ResolveInfo installerInfo = new ResolveInfo(
                            mInstantAppInstallerInfo);
                    installerInfo.auxiliaryInfo = new AuxiliaryResolveInfo(
                            null /* installFailureActivity */,
                            info.serviceInfo.packageName,
                            info.serviceInfo.applicationInfo.longVersionCode,
                            info.serviceInfo.splitName);
                    // add a non-generic filter
                    installerInfo.filter = new IntentFilter();
                    // load resources from the correct package
                    installerInfo.resolvePackageName = info.getComponentInfo().packageName;
                    resolveInfos.set(i, installerInfo);
                }
                continue;
            }
            // allow services that have been explicitly exposed to ephemeral apps
            if (!isEphemeralApp
                    && ((info.serviceInfo.flags & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP)
                    != 0)) {
                continue;
            }
            resolveInfos.remove(i);
        }
        return resolveInfos;
    }

    /**
     * Filter out activities with systemUserOnly flag set, when current user is not System.
     *
     * @return filtered list
     */
    private List<ResolveInfo> filterIfNotSystemUser(List<ResolveInfo> resolveInfos,
            int userId) {
        if (userId == UserHandle.USER_SYSTEM) {
            return resolveInfos;
        }

        for (int i = CollectionUtils.size(resolveInfos) - 1; i >= 0; i--) {
            ResolveInfo info = resolveInfos.get(i);
            if ((info.activityInfo.flags & ActivityInfo.FLAG_SYSTEM_USER_ONLY) != 0) {
                resolveInfos.remove(i);
            }
        }
        return resolveInfos;
    }

    private List<ResolveInfo> maybeAddInstantAppInstaller(List<ResolveInfo> result,
            Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
            int userId, boolean resolveForStart, boolean isRequesterInstantApp) {
        // first, check to see if we've got an instant app already installed
        final boolean alreadyResolvedLocally = (flags & PackageManager.MATCH_INSTANT) != 0;
        ResolveInfo localInstantApp = null;
        boolean blockResolution = false;
        if (!alreadyResolvedLocally) {
            final List<ResolveInfo> instantApps = mComponentResolver.queryActivities(this,
                    intent,
                    resolvedType,
                    flags
                            | PackageManager.GET_RESOLVED_FILTER
                            | PackageManager.MATCH_INSTANT
                            | PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY,
                    userId);
            for (int i = instantApps.size() - 1; i >= 0; --i) {
                final ResolveInfo info = instantApps.get(i);
                final String packageName = info.activityInfo.packageName;
                final PackageStateInternal ps = mSettings.getPackage(packageName);
                if (ps.getUserStateOrDefault(userId).isInstantApp()) {
                    if (PackageManagerServiceUtils.hasAnyDomainApproval(
                            mDomainVerificationManager, ps, intent, flags, userId)) {
                        if (DEBUG_INSTANT) {
                            Slog.v(TAG, "Instant app approved for intent; pkg: "
                                    + packageName);
                        }
                        localInstantApp = info;
                    } else {
                        if (DEBUG_INSTANT) {
                            Slog.v(TAG, "Instant app not approved for intent; pkg: "
                                    + packageName);
                        }
                        blockResolution = true;
                    }
                    break;
                }
            }
        }
        // no app installed, let's see if one's available
        AuxiliaryResolveInfo auxiliaryResponse = null;
        if (!blockResolution) {
            if (localInstantApp == null) {
                // we don't have an instant app locally, resolve externally
                Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "resolveEphemeral");
                String token = UUID.randomUUID().toString();
                InstantAppResolveInfo.InstantAppDigest digest =
                        InstantAppResolver.parseDigest(intent);
                final InstantAppRequest requestObject =
                        new InstantAppRequest(null /*responseObj*/,
                                intent /*origIntent*/, resolvedType, null /*callingPackage*/,
                                null /*callingFeatureId*/, isRequesterInstantApp, userId,
                                null /*verificationBundle*/, resolveForStart,
                                digest.getDigestPrefixSecure(), token);
                auxiliaryResponse = InstantAppResolver.doInstantAppResolutionPhaseOne(this,
                        mUserManager, mInstantAppResolverConnection, requestObject);
                Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
            } else {
                // we have an instant application locally, but, we can't admit that since
                // callers shouldn't be able to determine prior browsing. create a placeholder
                // auxiliary response so the downstream code behaves as if there's an
                // instant application available externally. when it comes time to start
                // the instant application, we'll do the right thing.
                final ApplicationInfo ai = localInstantApp.activityInfo.applicationInfo;
                auxiliaryResponse = new AuxiliaryResolveInfo(null /* failureActivity */,
                        ai.packageName, ai.longVersionCode,
                        null /* splitName */);
            }
        }
        if (intent.isWebIntent() && auxiliaryResponse == null) {
            return result;
        }
        final PackageStateInternal ps =
                mSettings.getPackage(instantAppInstallerActivity().packageName);
        if (ps == null || !PackageUserStateUtils.isEnabled(ps.getUserStateOrDefault(userId),
                instantAppInstallerActivity(), 0)) {
            return result;
        }
        final ResolveInfo ephemeralInstaller = new ResolveInfo(mInstantAppInstallerInfo);
        ephemeralInstaller.activityInfo = PackageInfoUtils.generateDelegateActivityInfo(
                instantAppInstallerActivity(), 0 /*flags*/,
                ps.getUserStateOrDefault(userId), userId);
        ephemeralInstaller.match = IntentFilter.MATCH_CATEGORY_SCHEME_SPECIFIC_PART
                | IntentFilter.MATCH_ADJUSTMENT_NORMAL;
        // add a non-generic filter
        ephemeralInstaller.filter = new IntentFilter();
        if (intent.getAction() != null) {
            ephemeralInstaller.filter.addAction(intent.getAction());
        }
        if (intent.getData() != null && intent.getData().getPath() != null) {
            ephemeralInstaller.filter.addDataPath(
                    intent.getData().getPath(), PatternMatcher.PATTERN_LITERAL);
        }
        ephemeralInstaller.isInstantAppAvailable = true;
        // make sure this resolver is the default
        ephemeralInstaller.isDefault = true;
        ephemeralInstaller.auxiliaryInfo = auxiliaryResponse;
        if (DEBUG_INSTANT) {
            Slog.v(TAG, "Adding ephemeral installer to the ResolveInfo list");
        }

        result.add(ephemeralInstaller);
        return result;
    }

    public final PackageInfo generatePackageInfo(PackageStateInternal ps,
            @PackageManager.PackageInfoFlagsBits long flags, int userId) {
        if (!mUserManager.exists(userId)) return null;
        if (ps == null) {
            return null;
        }
        final int callingUid = Binder.getCallingUid();
        // Filter out ephemeral app metadata:
        //   * The system/shell/root can see metadata for any app
        //   * An installed app can see metadata for 1) other installed apps
        //     and 2) ephemeral apps that have explicitly interacted with it
        //   * Ephemeral apps can only see their own data and exposed installed apps
        //   * Holding a signature permission allows seeing instant apps
        if (shouldFilterApplication(ps, callingUid, userId)) {
            return null;
        }

        if ((flags & MATCH_UNINSTALLED_PACKAGES) != 0
                && ps.isSystem()) {
            flags |= MATCH_ANY_USER;
        }

        final PackageUserStateInternal state = ps.getUserStateOrDefault(userId);
        AndroidPackage p = ps.getPkg();
        if (p != null) {
            // Compute GIDs only if requested
            final int[] gids = (flags & PackageManager.GET_GIDS) == 0 ? EMPTY_INT_ARRAY
                    : mPermissionManager.getGidsForUid(UserHandle.getUid(userId, ps.getAppId()));
            // Compute installed permissions only if requested
            final Set<String> installedPermissions = ((flags & PackageManager.GET_PERMISSIONS) == 0
                    || ArrayUtils.isEmpty(p.getPermissions())) ? Collections.emptySet()
                    : mPermissionManager.getInstalledPermissions(ps.getPackageName());
            // Compute granted permissions only if package has requested permissions
            final Set<String> grantedPermissions = ((flags & PackageManager.GET_PERMISSIONS) == 0
                    || ArrayUtils.isEmpty(p.getRequestedPermissions())) ? Collections.emptySet()
                    : mPermissionManager.getGrantedPermissions(ps.getPackageName(), userId);

            PackageInfo packageInfo = PackageInfoUtils.generate(p, gids, flags,
                    state.getFirstInstallTimeMillis(), ps.getLastUpdateTime(), installedPermissions,
                    grantedPermissions, state, userId, ps);

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

            packageInfo.packageName = packageInfo.applicationInfo.packageName =
                    resolveExternalPackageName(p);

            if (Flags.provideInfoOfApkInApex()) {
                final String apexModuleName =  ps.getApexModuleName();
                if (apexModuleName != null) {
                    packageInfo.setApexPackageName(
                            mApexManager.getActivePackageNameForApexModuleName(apexModuleName));
                }
            }
            if (Flags.verificationService()) {
                final DeveloperVerificationStatusInternal developerVerificationStatusInternal =
                        ps.getDeveloperVerificationStatusInternal();
                if (developerVerificationStatusInternal != null) {
                    packageInfo.setIsAppMetadataVerified(
                            developerVerificationStatusInternal.isAppMetadataVerified());
                }
            }
            return packageInfo;
        } else if ((flags & (MATCH_UNINSTALLED_PACKAGES | MATCH_ARCHIVED_PACKAGES)) != 0
                && PackageUserStateUtils.isAvailable(state, flags)) {
            PackageInfo pi = new PackageInfo();
            pi.packageName = ps.getPackageName();
            pi.setLongVersionCode(ps.getVersionCode());
            SharedUserApi sharedUser = mSettings.getSharedUserFromPackageName(pi.packageName);
            pi.sharedUserId = (sharedUser != null) ? sharedUser.getName() : null;
            pi.firstInstallTime = state.getFirstInstallTimeMillis();
            pi.lastUpdateTime = ps.getLastUpdateTime();

            ApplicationInfo ai = new ApplicationInfo();
            ai.packageName = ps.getPackageName();
            ai.uid = UserHandle.getUid(userId, ps.getAppId());
            ai.primaryCpuAbi = ps.getPrimaryCpuAbiLegacy();
            ai.secondaryCpuAbi = ps.getSecondaryCpuAbiLegacy();
            ai.volumeUuid = ps.getVolumeUuid();
            ai.storageUuid = StorageManager.convert(ai.volumeUuid);
            ai.setVersionCode(ps.getVersionCode());
            ai.targetSdkVersion = ps.getTargetSdkVersion();
            ai.flags = ps.getFlags();
            ai.privateFlags = ps.getPrivateFlags();
            pi.applicationInfo = PackageInfoUtils.generateDelegateApplicationInfo(
                    ai, flags, state, userId);
            pi.signingInfo = ps.getSigningInfo();
            pi.signatures = getDeprecatedSignatures(pi.signingInfo.getSigningDetails(), flags);
            if (state.getArchiveState() != null) {
                pi.setArchiveTimeMillis(state.getArchiveState().getArchiveTimeMillis());
            }

            if (DEBUG_PACKAGE_INFO) {
                Log.v(TAG, "ps.pkg is n/a for ["
                        + ps.getPackageName() + "]. Provides a minimum info.");
            }
            return pi;
        } else {
            return null;
        }
    }

    public final PackageInfo getPackageInfo(String packageName,
            @PackageManager.PackageInfoFlagsBits long flags, int userId) {
        return getPackageInfoInternal(packageName, PackageManager.VERSION_CODE_HIGHEST,
                flags, Binder.getCallingUid(), userId);
    }

    /**
     * Important: The provided filterCallingUid is used exclusively to filter out packages
     * that can be seen based on user state. It's typically the original caller uid prior
     * to clearing. Because it can only be provided by trusted code, its value can be
     * trusted and will be used as-is; unlike userId which will be validated by this method.
     */
    public final PackageInfo getPackageInfoInternal(String packageName, long versionCode,
            long flags, int filterCallingUid, int userId) {
        if (!mUserManager.exists(userId)) return null;
        flags = updateFlagsForPackage(flags, userId);
        enforceCrossUserPermission(Binder.getCallingUid(), userId,
                false /* requireFullPermission */, false /* checkShell */, "get package info");

        return getPackageInfoInternalBody(packageName, versionCode, flags, filterCallingUid,
                userId);
    }

    protected PackageInfo getPackageInfoInternalBody(String packageName, long versionCode,
            long flags, int filterCallingUid, int userId) {
        // reader
        // Normalize package name to handle renamed packages and static libs
        packageName = resolveInternalPackageName(packageName, versionCode);

        final boolean matchFactoryOnly = (flags & MATCH_FACTORY_ONLY) != 0;
        final boolean matchApex = (flags & MATCH_APEX) != 0;
        if (matchFactoryOnly) {
            // Instant app filtering for APEX modules is ignored
            final PackageStateInternal ps = mSettings.getDisabledSystemPkg(packageName);
            if (ps != null) {
                if (!matchApex && ps.getPkg() != null && ps.getPkg().isApex()) {
                    return null;
                }
                if (filterSharedLibPackage(ps, filterCallingUid, userId, flags)) {
                    return null;
                }
                if (shouldFilterApplication(ps, filterCallingUid, userId)) {
                    return null;
                }
                return generatePackageInfo(ps, flags, userId);
            }
        }

        AndroidPackage p = mPackages.get(packageName);
        var packageState = mSettings.getPackage(packageName);
        if (matchFactoryOnly && p != null && !packageState.isSystem()) {
            return null;
        }
        if (DEBUG_PACKAGE_INFO) {
            Log.v(TAG, "getPackageInfo " + packageName + ": " + p);
        }
        if (p != null) {
            final PackageStateInternal ps = getPackageStateInternal(p.getPackageName());
            if (!matchApex && p.isApex()) {
                return null;
            }
            if (filterSharedLibPackage(ps, filterCallingUid, userId, flags)) {
                return null;
            }
            if (ps != null && shouldFilterApplication(ps, filterCallingUid, userId)) {
                return null;
            }

            return generatePackageInfo(ps, flags, userId);
        }
        if (!matchFactoryOnly && (flags & (MATCH_KNOWN_PACKAGES | MATCH_ARCHIVED_PACKAGES)) != 0) {
            final PackageStateInternal ps = mSettings.getPackage(packageName);
            if (ps == null) return null;
            if (filterSharedLibPackage(ps, filterCallingUid, userId, flags)) {
                return null;
            }
            if (shouldFilterApplication(ps, filterCallingUid, userId)) {
                return null;
            }
            return generatePackageInfo(ps, flags, userId);
        }
        return null;
    }

    @Override
    public String[] getAllAvailablePackageNames() {
        return mPackages.keySet().toArray(new String[0]);
    }

    @Nullable
    public final PackageStateInternal getPackageStateInternal(String packageName) {
        return getPackageStateInternal(packageName, Binder.getCallingUid());
    }

    public PackageStateInternal getPackageStateInternal(String packageName,
            int callingUid) {
        packageName = resolveInternalPackageNameInternalLocked(
                packageName, PackageManager.VERSION_CODE_HIGHEST, callingUid);
        return mSettings.getPackage(packageName);
    }

    @Override
    public PackageStateInternal getPackageStateFiltered(@NonNull String packageName,
            int callingUid, @UserIdInt int userId) {
        packageName = resolveInternalPackageNameInternalLocked(
                packageName, PackageManager.VERSION_CODE_HIGHEST, callingUid);
        var packageState = mSettings.getPackage(packageName);
        if (shouldFilterApplication(packageState, callingUid, userId)) {
            return null;
        } else {
            return packageState;
        }
    }

    public final ParceledListSlice<PackageInfo> getInstalledPackages(long flags, int userId) {
        final int callingUid = Binder.getCallingUid();
        if (getInstantAppPackageName(callingUid) != null) {
            return ParceledListSlice.emptyList();
        }
        if (!mUserManager.exists(userId)) return ParceledListSlice.emptyList();
        flags = updateFlagsForPackage(flags, userId);

        enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
                false /* checkShell */, "get installed packages");

        return getInstalledPackagesBody(flags, userId, callingUid);
    }

    protected ParceledListSlice<PackageInfo> getInstalledPackagesBody(long flags, int userId,
            int callingUid) {
        // writer
        final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
        final boolean listApex = (flags & MATCH_APEX) != 0;
        final boolean listFactory = (flags & MATCH_FACTORY_ONLY) != 0;
        // Only list archived apps, not fully uninstalled ones. Other entries are unaffected.
        final boolean listArchivedOnly = !listUninstalled && (flags & MATCH_ARCHIVED_PACKAGES) != 0;

        ArrayList<PackageInfo> list;
        if (listUninstalled || listArchivedOnly) {
            list = new ArrayList<>(mSettings.getPackages().size());
            for (PackageStateInternal ps : mSettings.getPackages().values()) {
                if (listFactory) {
                    if (!ps.isSystem()) {
                        continue;
                    }
                    PackageStateInternal psDisabled =
                            mSettings.getDisabledSystemPkg(ps.getPackageName());
                    if (psDisabled != null) {
                        ps = psDisabled;
                    }
                }
                if (!listApex && ps.getPkg() != null && ps.getPkg().isApex()) {
                    continue;
                }
                PackageUserStateInternal userState = ps.getUserStateOrDefault(userId);
                if (listArchivedOnly && !userState.isInstalled()
                        && userState.getArchiveState() == null) {
                    continue;
                }
                if (filterSharedLibPackage(ps, callingUid, userId, flags)) {
                    continue;
                }
                if (shouldFilterApplication(ps, callingUid, userId)) {
                    continue;
                }
                final PackageInfo pi = generatePackageInfo(ps, flags, userId);
                if (pi != null) {
                    list.add(pi);
                }
            }
        } else {
            list = new ArrayList<>(mPackages.size());
            for (AndroidPackage p : mPackages.values()) {
                PackageStateInternal ps = getPackageStateInternal(p.getPackageName());
                if (listFactory) {
                    if (!ps.isSystem()) {
                        continue;
                    }
                    PackageStateInternal psDisabled =
                            ps == null ? null : mSettings.getDisabledSystemPkg(ps.getPackageName());
                    if (psDisabled != null) {
                        ps = psDisabled;
                    }
                }
                if (!listApex && p.isApex()) {
                    continue;
                }
                if (filterSharedLibPackage(ps, callingUid, userId, flags)) {
                    continue;
                }
                if (shouldFilterApplication(ps, callingUid, userId)) {
                    continue;
                }
                final PackageInfo pi = generatePackageInfo(ps, flags, userId);
                if (pi != null) {
                    list.add(pi);
                }
            }
        }
        return new ParceledListSlice<>(list);
    }

    public final ResolveInfo createForwardingResolveInfoUnchecked(WatchedIntentFilter filter,
            int sourceUserId, int targetUserId) {
        ResolveInfo forwardingResolveInfo = new ResolveInfo();
        final long ident = Binder.clearCallingIdentity();
        boolean targetIsProfile;
        try {
            targetIsProfile = mUserManager.getUserInfo(targetUserId).isManagedProfile();
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
        String className;
        if (targetIsProfile) {
            className = FORWARD_INTENT_TO_MANAGED_PROFILE;
        } else {
            className = FORWARD_INTENT_TO_PARENT;
        }
        ComponentName forwardingActivityComponentName = new ComponentName(
                androidApplication().packageName, className);
        ActivityInfo forwardingActivityInfo =
                getActivityInfoCrossProfile(forwardingActivityComponentName, 0,
                        sourceUserId);
        if (!targetIsProfile) {
            forwardingActivityInfo.showUserIcon = targetUserId;
            forwardingResolveInfo.noResourceId = true;
        }
        forwardingResolveInfo.activityInfo = forwardingActivityInfo;
        forwardingResolveInfo.priority = 0;
        forwardingResolveInfo.preferredOrder = 0;
        forwardingResolveInfo.match = 0;
        forwardingResolveInfo.isDefault = true;
        forwardingResolveInfo.filter = new IntentFilter(filter.getIntentFilter());
        forwardingResolveInfo.targetUserId = targetUserId;
        forwardingResolveInfo.userHandle = UserHandle.of(sourceUserId);
        return forwardingResolveInfo;
    }

    public final ServiceInfo getServiceInfo(ComponentName component,
            @PackageManager.ResolveInfoFlagsBits long flags, int userId) {
        if (!mUserManager.exists(userId)) return null;
        final int callingUid = Binder.getCallingUid();
        flags = updateFlagsForComponent(flags, userId);
        enforceCrossUserOrProfilePermission(callingUid, userId,
                false /* requireFullPermission */,
                false /* checkShell */, "get service info");
        return getServiceInfoBody(component, flags, userId, callingUid);
    }

    protected ServiceInfo getServiceInfoBody(ComponentName component,
            @PackageManager.ResolveInfoFlagsBits long flags, int userId, int callingUid) {
        ParsedService s = mComponentResolver.getService(component);
        if (DEBUG_PACKAGE_INFO) {
            Log.v(
                    TAG, "getServiceInfo " + component + ": " + s);
        }
        if (s == null) {
            return null;
        }

        AndroidPackage pkg = mPackages.get(s.getPackageName());
        if (mSettings.isEnabledAndMatch(pkg, s, flags, userId)) {
            PackageStateInternal ps = mSettings.getPackage(component.getPackageName());
            if (ps == null) return null;
            if (shouldFilterApplication(
                    ps, callingUid, component, TYPE_SERVICE, userId)) {
                return null;
            }
            return PackageInfoUtils.generateServiceInfo(pkg,
                    s, flags, ps.getUserStateOrDefault(userId), userId, ps);
        }
        return null;
    }

    @Nullable
    public final SharedLibraryInfo getSharedLibraryInfo(String name, long version) {
        return mSharedLibraries.getSharedLibraryInfo(name, version);
    }

    /**
     * Returns the package name of the calling Uid if it's an instant app. If it isn't
     * instant, returns {@code null}.
     */
    public String getInstantAppPackageName(int callingUid) {
        // If the caller is an isolated app use the owner's uid for the lookup.
        if (Process.isIsolated(callingUid)) {
            callingUid = getIsolatedOwner(callingUid);
        }
        final int appId = UserHandle.getAppId(callingUid);
        final Object obj = mSettings.getSettingBase(appId);
        if (obj instanceof PackageStateInternal) {
            final PackageStateInternal ps = (PackageStateInternal) obj;
            final boolean isInstantApp = ps.getUserStateOrDefault(UserHandle.getUserId(callingUid))
                    .isInstantApp();
            return isInstantApp ? ps.getPkg().getPackageName() : null;
        }
        return null;
    }

    /**
     * Finds the owner for the provided isolated UID. Throws IllegalStateException if no such
     * isolated UID is found.
     */
    private int getIsolatedOwner(int isolatedUid) {
        final int ownerUid = mIsolatedOwners.get(isolatedUid, -1);
        if (ownerUid == -1) {
            Slog.wtf(TAG, "No owner UID found for isolated UID " + isolatedUid);
            return isolatedUid;
        }
        return ownerUid;
    }

    public final String resolveExternalPackageName(AndroidPackage pkg) {
        if (pkg.getStaticSharedLibraryName() != null) {
            return pkg.getManifestPackageName();
        }
        return pkg.getPackageName();
    }

    private String resolveInternalPackageNameInternalLocked(
            String packageName, long versionCode, int callingUid) {
        // Handle renamed packages
        String normalizedPackageName = mSettings.getRenamedPackageLPr(packageName);
        packageName = normalizedPackageName != null ? normalizedPackageName : packageName;

        // Is this a static library?
        WatchedLongSparseArray<SharedLibraryInfo> versionedLib =
                mSharedLibraries.getStaticLibraryInfos(packageName);
        if (versionedLib == null || versionedLib.size() <= 0) {
            return packageName;
        }

        // Figure out which lib versions the caller can see
        LongSparseLongArray versionsCallerCanSee = null;
        final int callingAppId = UserHandle.getAppId(callingUid);
        if (!PackageManagerServiceUtils.isSystemOrRootOrShell(callingAppId)) {
            versionsCallerCanSee = new LongSparseLongArray();
            String libName = versionedLib.valueAt(0).getName();
            String[] uidPackages = getPackagesForUidInternal(callingUid, callingUid);
            if (uidPackages != null) {
                for (String uidPackage : uidPackages) {
                    PackageStateInternal ps = mSettings.getPackage(uidPackage);
                    final int libIdx = ArrayUtils.indexOf(ps.getUsesStaticLibraries(), libName);
                    if (libIdx >= 0) {
                        final long libVersion = ps.getUsesStaticLibrariesVersions()[libIdx];
                        versionsCallerCanSee.append(libVersion, libVersion);
                    }
                }
            }
        }

        // Caller can see nothing - done
        if (versionsCallerCanSee != null && versionsCallerCanSee.size() <= 0) {
            return packageName;
        }

        // Find the version the caller can see and the app version code
        SharedLibraryInfo highestVersion = null;
        final int versionCount = versionedLib.size();
        for (int i = 0; i < versionCount; i++) {
            SharedLibraryInfo libraryInfo = versionedLib.valueAt(i);
            if (versionsCallerCanSee != null && versionsCallerCanSee.indexOfKey(
                    libraryInfo.getLongVersion()) < 0) {
                continue;
            }
            final long libVersionCode = libraryInfo.getDeclaringPackage().getLongVersionCode();
            if (versionCode != PackageManager.VERSION_CODE_HIGHEST) {
                if (libVersionCode == versionCode) {
                    return libraryInfo.getPackageName();
                }
            } else if (highestVersion == null) {
                highestVersion = libraryInfo;
            } else if (libVersionCode  > highestVersion
                    .getDeclaringPackage().getLongVersionCode()) {
                highestVersion = libraryInfo;
            }
        }

        if (highestVersion != null) {
            return highestVersion.getPackageName();
        }

        return packageName;
    }

    public final String resolveInternalPackageName(String packageName, long versionCode) {
        final int callingUid = Binder.getCallingUid();
        return resolveInternalPackageNameInternalLocked(packageName, versionCode,
                callingUid);
    }

    /**
     * <em>IMPORTANT:</em> Not all packages returned by this method may be known
     * to the system. There are two conditions in which this may occur:
     * <ol>
     *   <li>The package is on adoptable storage and the device has been removed</li>
     *   <li>The package is being removed and the internal structures are partially updated</li>
     * </ol>
     * The second is an artifact of the current data structures and should be fixed. See
     * b/111075456 for one such instance.
     * This binder API is cached.  If the algorithm in this method changes,
     * or if the underlying objecs (as returned by getSettingLPr()) change
     * then the logic that invalidates the cache must be revisited.  See
     * calls to invalidateGetPackagesForUidCache() to locate the points at
     * which the cache is invalidated.
     */
    public final String[] getPackagesForUid(int uid) {
        return getPackagesForUidInternal(uid, Binder.getCallingUid());
    }

    private String[] getPackagesForUidInternal(int uid, int callingUid) {
        final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
        final int userId = UserHandle.getUserId(uid);
        if (Process.isSdkSandboxUid(uid)) {
            uid = getBaseSdkSandboxUid();
        }
        if(isKnownIsolatedComputeApp(uid)) {
            uid = getIsolatedOwner(uid);
        }
        final int appId = UserHandle.getAppId(uid);
        return getPackagesForUidInternalBody(callingUid, userId, appId, isCallerInstantApp);
    }

    protected String[] getPackagesForUidInternalBody(int callingUid, int userId, int appId,
            boolean isCallerInstantApp) {
        // reader
        final Object obj = mSettings.getSettingBase(appId);
        if (obj instanceof SharedUserSetting) {
            if (isCallerInstantApp) {
                return null;
            }
            final SharedUserSetting sus = (SharedUserSetting) obj;
            final ArraySet<PackageStateInternal> packageStates =
                    (ArraySet<PackageStateInternal>) sus.getPackageStates();
            final int n = packageStates.size();
            String[] res = new String[n];
            int i = 0;
            for (int index = 0; index < n; index++) {
                final PackageStateInternal ps = packageStates.valueAt(index);
                if (ps.getUserStateOrDefault(userId).isInstalled()
                        && !shouldFilterApplication(ps, callingUid, userId)) {
                    res[i++] = ps.getPackageName();
                }
            }
            return ArrayUtils.trimToSize(res, i);
        } else if (obj instanceof PackageStateInternal) {
            final PackageStateInternal ps = (PackageStateInternal) obj;
            if (ps.getUserStateOrDefault(userId).isInstalled()
                    && !shouldFilterApplication(ps, callingUid, userId)) {
                return new String[]{ps.getPackageName()};
            }
        }
        return null;
    }

    public final UserInfo getProfileParent(int userId) {
        final long identity = Binder.clearCallingIdentity();
        try {
            return mUserManager.getProfileParent(userId);
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    }

    /**
     * Returns whether or not instant apps have been disabled remotely.
     */
    private boolean areWebInstantAppsDisabled(int userId) {
        return mWebInstantAppsDisabled.get(userId);
    }

    /**
     * Returns whether or not a full application can see an instant application.
     * <p>
     * Currently, there are four cases in which this can occur:
     * <ol>
     * <li>The calling application is a "special" process. Special processes
     *     are those with a UID < {@link Process#FIRST_APPLICATION_UID}.</li>
     * <li>The calling application has the permission
     *     {@link android.Manifest.permission#ACCESS_INSTANT_APPS}.</li>
     * <li>The calling application is the default launcher on the
     *     system partition.</li>
     * <li>The calling application is the default app prediction service.</li>
     * </ol>
     */
    public final boolean canViewInstantApps(int callingUid, int userId) {
        if (callingUid < Process.FIRST_APPLICATION_UID) {
            return true;
        }
        if (mContext.checkCallingOrSelfPermission(
                android.Manifest.permission.ACCESS_INSTANT_APPS) == PERMISSION_GRANTED) {
            return true;
        }
        if (mContext.checkCallingOrSelfPermission(
                android.Manifest.permission.VIEW_INSTANT_APPS) == PERMISSION_GRANTED) {
            final ComponentName homeComponent = getDefaultHomeActivity(userId);
            if (homeComponent != null
                    && isCallerSameApp(homeComponent.getPackageName(), callingUid)) {
                return true;
            }
            // TODO(b/122900055) Change/Remove this and replace with new permission role.
            return mAppPredictionServicePackage != null
                    && isCallerSameApp(mAppPredictionServicePackage, callingUid);
        }
        return false;
    }

    private boolean filterStaticSharedLibPackage(@Nullable PackageStateInternal ps, int uid,
            int userId, @PackageManager.ComponentInfoFlagsBits long flags) {
        // Callers can access only the static shared libs they depend on, otherwise they need to
        // explicitly ask for the static shared libraries given the caller is allowed to access
        // all static libs.
        if ((flags & PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES) != 0) {
            // System/shell/root get to see all static libs
            final int appId = UserHandle.getAppId(uid);
            if (PackageManagerServiceUtils.isSystemOrRootOrShell(appId)) {
                return false;
            }
            // Installer gets to see all static libs.
            if (PackageManager.PERMISSION_GRANTED
                    == checkUidPermission(Manifest.permission.INSTALL_PACKAGES, uid)) {
                return false;
            }
        }

        // No package means no static lib as it is always on internal storage
        if (ps == null || ps.getPkg() == null || !ps.getPkg().isStaticSharedLibrary()) {
            return false;
        }

        final SharedLibraryInfo libraryInfo = getSharedLibraryInfo(
                ps.getPkg().getStaticSharedLibraryName(),
                ps.getPkg().getStaticSharedLibraryVersion());
        if (libraryInfo == null) {
            return false;
        }

        final int resolvedUid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
        final String[] uidPackageNames = getPackagesForUid(resolvedUid);
        if (uidPackageNames == null) {
            return true;
        }

        for (String uidPackageName : uidPackageNames) {
            if (ps.getPackageName().equals(uidPackageName)) {
                return false;
            }
            PackageStateInternal uidPs = mSettings.getPackage(uidPackageName);
            if (uidPs != null) {
                final int index = ArrayUtils.indexOf(uidPs.getUsesStaticLibraries(),
                        libraryInfo.getName());
                if (index < 0) {
                    continue;
                }
                if (uidPs.getPkg().getUsesStaticLibrariesVersions()[index]
                        == libraryInfo.getLongVersion()) {
                    return false;
                }
            }
        }
        return true;
    }

    private boolean filterSdkLibPackage(@Nullable PackageStateInternal ps, int uid,
            int userId, @PackageManager.ComponentInfoFlagsBits long flags) {
        // Callers can access only the SDK libs they depend on, otherwise they need to
        // explicitly ask for the SDKs given the caller is allowed to access
        // all shared libs.
        if ((flags & PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES) != 0) {
            // System/shell/root get to see all SDK libs.
            final int appId = UserHandle.getAppId(uid);
            if (PackageManagerServiceUtils.isSystemOrRootOrShell(appId)) {
                return false;
            }
            // Installer gets to see all SDK libs.
            if (PackageManager.PERMISSION_GRANTED
                    == checkUidPermission(Manifest.permission.INSTALL_PACKAGES, uid)) {
                return false;
            }
        }

        // No package means no static lib as it is always on internal storage
        if (ps == null || ps.getPkg() == null || !ps.getPkg().isSdkLibrary()) {
            return false;
        }

        final SharedLibraryInfo libraryInfo = getSharedLibraryInfo(
                ps.getPkg().getSdkLibraryName(), ps.getPkg().getSdkLibVersionMajor());
        if (libraryInfo == null) {
            return false;
        }

        final int resolvedUid = UserHandle.getUid(userId, UserHandle.getAppId(uid));
        final String[] uidPackageNames = getPackagesForUid(resolvedUid);
        if (uidPackageNames == null) {
            return true;
        }

        for (String uidPackageName : uidPackageNames) {
            if (ps.getPackageName().equals(uidPackageName)) {
                return false;
            }
            PackageStateInternal uidPs = mSettings.getPackage(uidPackageName);
            if (uidPs != null) {
                final int index = ArrayUtils.indexOf(uidPs.getUsesSdkLibraries(),
                        libraryInfo.getName());
                if (index < 0) {
                    continue;
                }
                if (uidPs.getPkg().getUsesSdkLibrariesVersionsMajor()[index]
                        == libraryInfo.getLongVersion()) {
                    return false;
                }
            }
        }
        return true;
    }

    @Override
    public final boolean filterSharedLibPackage(@Nullable PackageStateInternal ps, int uid,
            int userId, @PackageManager.ComponentInfoFlagsBits long flags) {
        return filterStaticSharedLibPackage(ps, uid, userId, flags) || filterSdkLibPackage(ps, uid,
                userId, flags);
    }

    private boolean hasCrossUserPermission(
            int callingUid, int callingUserId, int userId, boolean requireFullPermission,
            boolean requirePermissionWhenSameUser) {
        if (!requirePermissionWhenSameUser && userId == callingUserId) {
            return true;
        }
        if (PackageManagerServiceUtils.isSystemOrRoot(callingUid)) {
            return true;
        }
        boolean permissionGranted = requireFullPermission ? hasPermission(
                Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingUid)
                : (hasPermission(
                        android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, callingUid)
                        || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS, callingUid));
        if (!permissionGranted) {
            if (Process.isIsolatedUid(callingUid) && isKnownIsolatedComputeApp(callingUid)) {
                return checkIsolatedOwnerHasPermission(callingUid, requireFullPermission);
            }
        }
        return permissionGranted;
    }

    /**
     * @param resolveInfos list of resolve infos in descending priority order
     * @return if the list contains a resolve info with non-negative priority
     */
    private boolean hasNonNegativePriority(List<ResolveInfo> resolveInfos) {
        return resolveInfos.size() > 0 && resolveInfos.get(0).priority >= 0;
    }

    private boolean hasPermission(String permission) {
        return mContext.checkCallingOrSelfPermission(permission)
                == PackageManager.PERMISSION_GRANTED;
    }

    private boolean hasPermission(String permission, int uid) {
        return mContext.checkPermission(permission, Process.INVALID_PID, uid)
                == PackageManager.PERMISSION_GRANTED;
    }

    /**
     * Since isolated process cannot hold permissions, we check the permissions on the owner app
     * for known isolated_compute_app cases because they belong to the same package.
     */
    private boolean checkIsolatedOwnerHasPermission(int callingUid, boolean requireFullPermission) {
        int ownerUid = getIsolatedOwner(callingUid);
        if (requireFullPermission) {
            return hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, ownerUid);
        }
        return hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL, ownerUid)
                || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS, ownerUid);
    }

    public final boolean isCallerSameApp(String packageName, int uid) {
        return isCallerSameApp(packageName, uid, false /* resolveIsolatedUid */);
    }

    @Override
    public final boolean isCallerSameApp(String packageName, int uid, boolean resolveIsolatedUid) {
        if (Process.isSdkSandboxUid(uid)) {
            return (packageName != null
                    && packageName.equals(mService.getSdkSandboxPackageName()));
        }
        AndroidPackage pkg = mPackages.get(packageName);
        if (resolveIsolatedUid && Process.isIsolated(uid)) {
            uid = getIsolatedOwner(uid);
        }
        return pkg != null
                && UserHandle.getAppId(uid) == pkg.getUid();
    }

    private boolean isCallerFromManagedUserOrProfile(@UserIdInt int userId) {
        final var dpmi = mInjector.getLocalService(DevicePolicyManagerInternal.class);
        return dpmi != null && dpmi.isUserOrganizationManaged(userId);
    }

    public final boolean isComponentVisibleToInstantApp(@Nullable ComponentName component) {
        if (isComponentVisibleToInstantApp(component, TYPE_ACTIVITY)) {
            return true;
        }
        if (isComponentVisibleToInstantApp(component, TYPE_SERVICE)) {
            return true;
        }
        return isComponentVisibleToInstantApp(component, TYPE_PROVIDER);
    }

    public final boolean isComponentVisibleToInstantApp(
            @Nullable ComponentName component, @PackageManager.ComponentType int type) {
        if (type == TYPE_ACTIVITY) {
            final ParsedActivity activity = mComponentResolver.getActivity(component);
            if (activity == null) {
                return false;
            }
            final boolean visibleToInstantApp =
                    (activity.getFlags() & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
            final boolean explicitlyVisibleToInstantApp =
                    (activity.getFlags() & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP)
                            == 0;
            return visibleToInstantApp && explicitlyVisibleToInstantApp;
        } else if (type == TYPE_RECEIVER) {
            final ParsedActivity activity = mComponentResolver.getReceiver(component);
            if (activity == null) {
                return false;
            }
            final boolean visibleToInstantApp =
                    (activity.getFlags() & ActivityInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
            final boolean explicitlyVisibleToInstantApp =
                    (activity.getFlags() & ActivityInfo.FLAG_IMPLICITLY_VISIBLE_TO_INSTANT_APP)
                            == 0;
            return visibleToInstantApp && !explicitlyVisibleToInstantApp;
        } else if (type == TYPE_SERVICE) {
            final ParsedService service = mComponentResolver.getService(component);
            return service != null
                    && (service.getFlags() & ServiceInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
        } else if (type == TYPE_PROVIDER) {
            final ParsedProvider provider = mComponentResolver.getProvider(component);
            return provider != null
                    && (provider.getFlags() & ProviderInfo.FLAG_VISIBLE_TO_INSTANT_APP) != 0;
        } else if (type == TYPE_UNKNOWN) {
            return isComponentVisibleToInstantApp(component);
        }
        return false;
    }

    /**
     * From Android R,
     *  camera intents have to match system apps. The only exception to this is if
     * the DPC has set the camera persistent preferred activity. This case was introduced
     * because it is important that the DPC has the ability to set both system and non-system
     * camera persistent preferred activities.
     *
     * @return {@code true} if the intent is a camera intent and the persistent preferred
     * activity was not set by the DPC.
     */
    public final boolean isImplicitImageCaptureIntentAndNotSetByDpc(Intent intent,
            int userId, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags) {
        return intent.isImplicitImageCaptureIntent() && !isPersistentPreferredActivitySetByDpm(
                intent, userId, resolvedType, flags);
    }

    public final boolean isInstantApp(String packageName, int userId) {
        final int callingUid = Binder.getCallingUid();
        enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
                false /* checkShell */, "isInstantApp");

        return isInstantAppInternal(packageName, userId, callingUid);
    }

    public final boolean isInstantAppInternal(String packageName, @UserIdInt int userId,
            int callingUid) {
        if (HIDE_EPHEMERAL_APIS) {
            return false;
        }
        return isInstantAppInternalBody(packageName, userId, callingUid);
    }

    protected boolean isInstantAppInternalBody(String packageName, @UserIdInt int userId,
            int callingUid) {
        if (Process.isIsolated(callingUid)) {
            callingUid = getIsolatedOwner(callingUid);
        }
        final PackageStateInternal ps = mSettings.getPackage(packageName);
        final boolean returnAllowed =
                ps != null
                        && (isCallerSameApp(packageName, callingUid)
                        || canViewInstantApps(callingUid, userId)
                        || mInstantAppRegistry.isInstantAccessGranted(
                        userId, UserHandle.getAppId(callingUid), ps.getAppId()));
        if (returnAllowed) {
            return ps.getUserStateOrDefault(userId).isInstantApp();
        }
        return false;
    }

    private boolean isInstantAppResolutionAllowed(
            Intent intent, List<ResolveInfo> resolvedActivities, int userId,
            boolean skipPackageCheck, @PackageManager.ResolveInfoFlagsBits long flags) {
        if (mInstantAppResolverConnection == null) {
            return false;
        }
        if (instantAppInstallerActivity() == null) {
            return false;
        }
        if (intent.getComponent() != null) {
            return false;
        }
        if ((intent.getFlags() & Intent.FLAG_IGNORE_EPHEMERAL) != 0) {
            return false;
        }
        if ((intent.getFlags() & Intent.FLAG_ACTIVITY_REQUIRE_NON_BROWSER) != 0) {
            return false;
        }
        if (!skipPackageCheck && intent.getPackage() != null) {
            return false;
        }
        if (!intent.isWebIntent()) {
            // for non web intents, we should not resolve externally if an app already exists to
            // handle it or if the caller didn't explicitly request it.
            if ((resolvedActivities != null && resolvedActivities.size() != 0)
                    || (intent.getFlags() & Intent.FLAG_ACTIVITY_MATCH_EXTERNAL) == 0) {
                return false;
            }
        } else {
            if (intent.getData() == null || TextUtils.isEmpty(intent.getData().getHost())) {
                return false;
            } else if (areWebInstantAppsDisabled(userId)) {
                return false;
            }
        }
        // Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution.
        // Or if there's already an ephemeral app installed that handles the action
        return isInstantAppResolutionAllowedBody(intent, resolvedActivities, userId,
                skipPackageCheck, flags);
    }

    // Deny ephemeral apps if the user chose _ALWAYS or _ALWAYS_ASK for intent resolution.
    // Or if there's already an ephemeral app installed that handles the action
    protected boolean isInstantAppResolutionAllowedBody(
            Intent intent, List<ResolveInfo> resolvedActivities, int userId,
            boolean skipPackageCheck, @PackageManager.ResolveInfoFlagsBits long flags) {
        final int count = (resolvedActivities == null ? 0 : resolvedActivities.size());
        var debug = (intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0;
        if (debug) {
            Slog.d(TAG, "Checking if instant app resolution allowed, resolvedActivities = "
                    + resolvedActivities);
        }
        for (int n = 0; n < count; n++) {
            final ResolveInfo info = resolvedActivities.get(n);
            final String packageName = info.activityInfo.packageName;
            final PackageStateInternal ps = mSettings.getPackage(packageName);
            if (ps != null) {
                // only check domain verification status if the app is not a browser
                if (!info.handleAllWebDataURI) {
                    if (PackageManagerServiceUtils.hasAnyDomainApproval(
                            mDomainVerificationManager, ps, intent, flags, userId)) {
                        if (DEBUG_INSTANT) {
                            Slog.v(TAG, "DENY instant app;" + " pkg: " + packageName
                                    + ", approved");
                        }
                        return false;
                    }
                }
                if (ps.getUserStateOrDefault(userId).isInstantApp()) {
                    if (DEBUG_INSTANT) {
                        Slog.v(TAG, "DENY instant app installed;"
                                + " pkg: " + packageName);
                    }
                    return false;
                }
            } else if (debug) {
                Slog.d(TAG, "Could not find package " + packageName);
            }
        }
        // We've exhausted all ways to deny ephemeral application; let the system look for them.
        return true;
    }

    private boolean isPersistentPreferredActivitySetByDpm(Intent intent, int userId,
            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags) {
        PersistentPreferredIntentResolver ppir =
                mSettings.getPersistentPreferredActivities(userId);
        //TODO(b/158003772): Remove double query
        List<PersistentPreferredActivity> pprefs = ppir != null
                ? ppir.queryIntent(this, intent, resolvedType,
                (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
                userId)
                : new ArrayList<>();
        for (PersistentPreferredActivity ppa : pprefs) {
            if (ppa.mIsSetByDpm) {
                return true;
            }
        }
        return false;
    }

    private boolean isRecentsAccessingChildProfiles(int callingUid, int targetUserId) {
        if (!mInjector.getLocalService(ActivityTaskManagerInternal.class)
                .isCallerRecents(callingUid)) {
            return false;
        }
        final long token = Binder.clearCallingIdentity();
        try {
            final int callingUserId = UserHandle.getUserId(callingUid);
            if (ActivityManager.getCurrentUser() != callingUserId) {
                return false;
            }
            return mUserManager.isSameProfileGroup(callingUserId, targetUserId);
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    public final boolean isSameProfileGroup(@UserIdInt int callerUserId,
            @UserIdInt int userId) {
        final long identity = Binder.clearCallingIdentity();
        try {
            return UserManagerService.getInstance().isSameProfileGroup(callerUserId, userId);
        } finally {
            Binder.restoreCallingIdentity(identity);
        }
    }

    /**
     * Returns whether or not access to the application should be filtered.
     * <p>
     * Access may be limited based upon whether the calling or target applications
     * are instant applications.
     *
     * @see #canViewInstantApps(int, int)
     */
    public final boolean shouldFilterApplication(@Nullable PackageStateInternal ps,
            int callingUid, @Nullable ComponentName component,
            @PackageManager.ComponentType int componentType, int userId, boolean filterUninstall,
            boolean filterArchived) {
        if (Process.isSdkSandboxUid(callingUid)) {
            int clientAppUid = Process.getAppUidForSdkSandboxUid(callingUid);
            // SDK sandbox should be able to see it's client app
            if (ps != null && clientAppUid == UserHandle.getUid(userId, ps.getAppId())) {
                return false;
            }
        }

        if (UserHandle.getAppId(callingUid) == Process.PHONE_UID) {
            return false;
        }

        // if we're in an isolated process, get the real calling UID
        if (Process.isIsolated(callingUid)) {
            callingUid = getIsolatedOwner(callingUid);
        }
        final String instantAppPkgName = getInstantAppPackageName(callingUid);
        final boolean callerIsInstantApp = instantAppPkgName != null;
        final boolean packageArchivedForUser = ps != null && PackageArchiver.isArchived(
                ps.getUserStateOrDefault(userId));
        // Don't treat hiddenUntilInstalled as an uninstalled state, phone app needs to access
        // these hidden application details to customize carrier apps. Also, allowing the system
        // caller accessing to application across users.
        if (ps == null
                || (filterUninstall
                && !isSystemOrRootOrShell(callingUid)
                && !ps.isHiddenUntilInstalled()
                && !ps.getUserStateOrDefault(userId).isInstalled()
                // Archived packages behave like uninstalled packages. So if filterUninstall is
                // set to true, we dismiss filtering some uninstalled package only if it is
                // archived and filterArchived is set as false.
                && (!packageArchivedForUser || filterArchived))) {
            // If caller is instant app or sdk sandbox and ps is null, pretend the application
            // exists, but, needs to be filtered
            return (callerIsInstantApp || filterUninstall || Process.isSdkSandboxUid(callingUid));
        }
        // if the target and caller are the same application, don't filter
        if (isCallerSameApp(ps.getPackageName(), callingUid)) {
            return false;
        }
        if (callerIsInstantApp) {
            // both caller and target are both instant, but, different applications, filter
            if (ps.getUserStateOrDefault(userId).isInstantApp()) {
                return true;
            }
            // request for a specific component; if it hasn't been explicitly exposed through
            // property or instrumentation target, filter
            if (component != null) {
                final ParsedInstrumentation instrumentation =
                        mInstrumentation.get(component);
                if (instrumentation != null
                        && isCallerSameApp(instrumentation.getTargetPackage(), callingUid)) {
                    return false;
                }
                return !isComponentVisibleToInstantApp(component, componentType);
            }
            // request for application; if no components have been explicitly exposed, filter
            return !ps.getPkg().isVisibleToInstantApps();
        }
        if (ps.getUserStateOrDefault(userId).isInstantApp()) {
            // caller can see all components of all instant applications, don't filter
            if (canViewInstantApps(callingUid, userId)) {
                return false;
            }
            // request for a specific instant application component, filter
            if (component != null) {
                return true;
            }
            // request for an instant application; if the caller hasn't been granted access,
            //filter
            return !mInstantAppRegistry.isInstantAccessGranted(
                    userId, UserHandle.getAppId(callingUid), ps.getAppId());
        }
        int appId = UserHandle.getAppId(callingUid);
        final SettingBase callingPs = mSettings.getSettingBase(appId);
        return mAppsFilter.shouldFilterApplication(this, callingUid, callingPs, ps, userId);
    }

    /**
     * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean,
     * boolean)
     */
    public final boolean shouldFilterApplication(@Nullable PackageStateInternal ps,
            int callingUid, @Nullable ComponentName component,
            @PackageManager.ComponentType int componentType, int userId, boolean filterUninstall) {
        return shouldFilterApplication(
                ps, callingUid, component, componentType, userId, filterUninstall,
                true /* filterArchived */);
    }

    /**
     * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean,
     * boolean)
     */
    public final boolean shouldFilterApplication(@Nullable PackageStateInternal ps,
            int callingUid, @Nullable ComponentName component,
            @PackageManager.ComponentType int componentType, int userId) {
        return shouldFilterApplication(
                ps, callingUid, component, componentType, userId, false /* filterUninstall */);
    }

    /**
     * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean,
     * boolean)
     */
    public final boolean shouldFilterApplication(
            @Nullable PackageStateInternal ps, int callingUid, int userId) {
        return shouldFilterApplication(
                ps, callingUid, null, TYPE_UNKNOWN, userId, false /* filterUninstall */);
    }

    /**
     * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean,
     * boolean)
     */
    public final boolean shouldFilterApplication(@NonNull SharedUserSetting sus,
            int callingUid, int userId) {
        boolean filterApp = true;
        final ArraySet<PackageStateInternal> packageStates =
                (ArraySet<PackageStateInternal>) sus.getPackageStates();
        for (int index = packageStates.size() - 1; index >= 0 && filterApp; index--) {
            filterApp &= shouldFilterApplication(packageStates.valueAt(index), callingUid,
                    null /* component */, TYPE_UNKNOWN, userId, false /* filterUninstall */);
        }
        return filterApp;
    }

    /**
     * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean,
     * boolean)
     */
    public final boolean shouldFilterApplicationIncludingUninstalled(
            @Nullable PackageStateInternal ps, int callingUid, int userId) {
        return shouldFilterApplication(
                ps, callingUid, null, TYPE_UNKNOWN, userId, true /* filterUninstall */);
    }

    /**
     * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean,
     * boolean)
     */
    public final boolean shouldFilterApplicationIncludingUninstalledNotArchived(
            @Nullable PackageStateInternal ps, int callingUid, int userId) {
        return shouldFilterApplication(
                ps, callingUid, null, TYPE_UNKNOWN, userId, true /* filterUninstall */,
                false /* filterArchived */);
    }

    /**
     * @see #shouldFilterApplication(PackageStateInternal, int, ComponentName, int, int, boolean,
     * boolean)
     */
    public final boolean shouldFilterApplicationIncludingUninstalled(
            @NonNull SharedUserSetting sus, int callingUid, int userId) {
        if (shouldFilterApplication(sus, callingUid, userId)) {
            return true;
        }
        if (isSystemOrRootOrShell(callingUid)) {
            return false;
        }
        if (UserHandle.getAppId(callingUid) == Process.PHONE_UID) {
            return false;
        }
        final ArraySet<PackageStateInternal> packageStates =
                (ArraySet<PackageStateInternal>) sus.getPackageStates();
        for (int index = 0; index < packageStates.size(); index++) {
            final PackageStateInternal ps = packageStates.valueAt(index);
            if (ps.getUserStateOrDefault(userId).isInstalled() || ps.isHiddenUntilInstalled()) {
                return false;
            }
        }
        // Filter it, all packages with the same shared uid are uninstalled.
        return true;
    }

    /**
     * Verification statuses are ordered from the worse to the best, except for
     * INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER, which is the worse.
     */
    private int bestDomainVerificationStatus(int status1, int status2) {
        if (status1 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
            return status2;
        }
        if (status2 == INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER) {
            return status1;
        }
        return (int) MathUtils.max(status1, status2);
    }

    // NOTE: Can't remove without a major refactor. Keep around for now.
    public final int checkUidPermission(String permName, int uid) {
        return mPermissionManager.checkUidPermission(uid, permName, Context.DEVICE_ID_DEFAULT);
    }

    public int getPackageUidInternal(String packageName,
            @PackageManager.PackageInfoFlagsBits long flags, int userId, int callingUid) {
        // reader
        var packageState = mSettings.getPackage(packageName);
        final AndroidPackage p = mPackages.get(packageName);
        if (p != null && AndroidPackageUtils.isMatchForSystemOnly(packageState, flags)) {
            final PackageStateInternal ps = getPackageStateInternal(p.getPackageName(), callingUid);
            if (ps != null && ps.getUserStateOrDefault(userId).isInstalled()
                    && !shouldFilterApplication(ps, callingUid, userId)) {
                return UserHandle.getUid(userId, p.getUid());
            }
        }
        if ((flags & (MATCH_KNOWN_PACKAGES | MATCH_ARCHIVED_PACKAGES)) != 0) {
            final PackageStateInternal ps = mSettings.getPackage(packageName);

            if (ps != null &&
                    (flags & MATCH_KNOWN_PACKAGES) == 0 &&
                    (flags & MATCH_ARCHIVED_PACKAGES) != 0) {
                PackageUserState us = ps.getUserStateOrDefault(userId);
                if (!us.isInstalled() && us.getArchiveState() == null) {
                    return INVALID_UID;
                }
            }

            if (ps != null && PackageStateUtils.isMatch(ps, flags)
                    && !shouldFilterApplication(ps, callingUid, userId)) {
                return UserHandle.getUid(userId, ps.getAppId());
            }
        }

        return INVALID_UID;
    }

    /**
     * Update given flags based on encryption status of current user.
     */
    private long updateFlags(long flags, int userId) {
        if ((flags & (PackageManager.MATCH_DIRECT_BOOT_UNAWARE
                | PackageManager.MATCH_DIRECT_BOOT_AWARE)) != 0) {
            // Caller expressed an explicit opinion about what encryption
            // aware/unaware components they want to see, so fall through and
            // give them what they want
        } else {
            final UserManagerInternal umInternal = mInjector.getUserManagerInternal();
            // Caller expressed no opinion, so match based on user state
            if (umInternal.isUserUnlockingOrUnlocked(userId)) {
                flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE;
            } else {
                flags |= PackageManager.MATCH_DIRECT_BOOT_AWARE;
            }
        }
        return flags;
    }

    /**
     * Update given flags when being used to request {@link ApplicationInfo}.
     */
    public final long updateFlagsForApplication(long flags, int userId) {
        return updateFlagsForPackage(flags, userId);
    }

    /**
     * Update given flags when being used to request {@link ComponentInfo}.
     */
    public final long updateFlagsForComponent(long flags, int userId) {
        return updateFlags(flags, userId);
    }

    /**
     * Update given flags when being used to request {@link PackageInfo}.
     */
    public final long updateFlagsForPackage(long flags, int userId) {
        final boolean isCallerSystemUser = UserHandle.getCallingUserId()
                == UserHandle.USER_SYSTEM;
        if ((flags & PackageManager.MATCH_ANY_USER) != 0) {
            // require the permission to be held; the calling uid and given user id referring
            // to the same user is not sufficient
            enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false,
                    !isRecentsAccessingChildProfiles(Binder.getCallingUid(), userId),
                    "MATCH_ANY_USER flag requires INTERACT_ACROSS_USERS permission");
        }
        return updateFlags(flags, userId);
    }

    /**
     * Update given flags when being used to request {@link ResolveInfo}.
     * <p>Instant apps are resolved specially, depending upon context. Minimally,
     * {@code}flags{@code} must have the {@link PackageManager#MATCH_INSTANT}
     * flag set. However, this flag is only honoured in three circumstances:
     * <ul>
     * <li>when called from a system process</li>
     * <li>when the caller holds the permission {@code
     * android.permission.ACCESS_INSTANT_APPS}</li>
     * <li>when resolution occurs to start an activity with a {@code android.intent.action.VIEW}
     * action and a {@code android.intent.category.BROWSABLE} category</li>
     * </ul>
     */
    public final long updateFlagsForResolve(long flags, int userId, int callingUid,
            boolean wantInstantApps, boolean isImplicitImageCaptureIntentAndNotSetByDpc) {
        return updateFlagsForResolve(flags, userId, callingUid,
                wantInstantApps, false /*onlyExposedExplicitly*/,
                isImplicitImageCaptureIntentAndNotSetByDpc);
    }

    public final long updateFlagsForResolve(long flags, int userId, int callingUid,
            boolean wantInstantApps, boolean onlyExposedExplicitly,
            boolean isImplicitImageCaptureIntentAndNotSetByDpc) {
        // Safe mode means we shouldn't match any third-party components
        if (safeMode() || isImplicitImageCaptureIntentAndNotSetByDpc) {
            flags |= PackageManager.MATCH_SYSTEM_ONLY;
        }
        if (getInstantAppPackageName(callingUid) != null) {
            // But, ephemeral apps see both ephemeral and exposed, non-ephemeral components
            if (onlyExposedExplicitly) {
                flags |= PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY;
            }
            flags |= PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY;
            flags |= PackageManager.MATCH_INSTANT;
        } else {
            final boolean wantMatchInstant = (flags & PackageManager.MATCH_INSTANT) != 0;
            final boolean allowMatchInstant = wantInstantApps
                    || (wantMatchInstant && canViewInstantApps(callingUid, userId));
            flags &= ~(PackageManager.MATCH_VISIBLE_TO_INSTANT_APP_ONLY
                    | PackageManager.MATCH_EXPLICITLY_VISIBLE_ONLY);
            if (!allowMatchInstant) {
                flags &= ~PackageManager.MATCH_INSTANT;
            }
        }
        return updateFlagsForComponent(flags, userId);
    }

    /**
     * Checks if the request is from the system or an app that has the appropriate cross-user
     * permissions defined as follows:
     * <ul>
     * <li>INTERACT_ACROSS_USERS_FULL if {@code requireFullPermission} is true.</li>
     * <li>INTERACT_ACROSS_USERS if the given {@code userId} is in a different profile group
     * to the caller.</li>
     * <li>Otherwise,
     *  INTERACT_ACROSS_PROFILES if the given {@code userId} is in the same profile
     * group as the caller.</li>
     * </ul>
     *
     * @param checkShell whether to prevent shell from access if there's a debugging restriction
     * @param message the message to log on security exception
     */
    public final void enforceCrossUserOrProfilePermission(int callingUid, @UserIdInt int userId,
            boolean requireFullPermission, boolean checkShell, String message) {
        if (userId < 0) {
            throw new IllegalArgumentException("Invalid userId " + userId);
        }
        if (checkShell) {
            PackageManagerServiceUtils.enforceShellRestriction(
                    mInjector.getUserManagerInternal(),
                    UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
        }
        final int callingUserId = UserHandle.getUserId(callingUid);
        if (hasCrossUserPermission(callingUid, callingUserId, userId, requireFullPermission,
                /*requirePermissionWhenSameUser= */ false)) {
            return;
        }
        final boolean isSameProfileGroup = isSameProfileGroup(callingUserId, userId);
        if (isSameProfileGroup && PermissionChecker.checkPermissionForPreflight(
                mContext,
                android.Manifest.permission.INTERACT_ACROSS_PROFILES,
                PermissionChecker.PID_UNKNOWN,
                callingUid,
                getPackage(callingUid).getPackageName())
                == PermissionChecker.PERMISSION_GRANTED) {
            return;
        }
        String errorMessage = buildInvalidCrossUserOrProfilePermissionMessage(
                callingUid, userId, message, requireFullPermission, isSameProfileGroup);
        Slog.w(TAG, errorMessage);
        throw new SecurityException(errorMessage);
    }

    private static String buildInvalidCrossUserOrProfilePermissionMessage(int callingUid,
            @UserIdInt int userId, String message, boolean requireFullPermission,
            boolean isSameProfileGroup) {
        StringBuilder builder = new StringBuilder();
        if (message != null) {
            builder.append(message);
            builder.append(": ");
        }
        builder.append("UID ");
        builder.append(callingUid);
        builder.append(" requires ");
        builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
        if (!requireFullPermission) {
            builder.append(" or ");
            builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS);
            if (isSameProfileGroup) {
                builder.append(" or ");
                builder.append(android.Manifest.permission.INTERACT_ACROSS_PROFILES);
            }
        }
        builder.append(" to access user ");
        builder.append(userId);
        builder.append(".");
        return builder.toString();
    }

    /**
     * Enforces the request is from the system or an app that has INTERACT_ACROSS_USERS
     * or INTERACT_ACROSS_USERS_FULL permissions, if the {@code userId} is not for the caller.
     *
     * @param checkShell whether to prevent shell from access if there's a debugging restriction
     * @param message the message to log on security exception
     */
    public final void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
            boolean requireFullPermission, boolean checkShell, String message) {
        enforceCrossUserPermission(callingUid, userId, requireFullPermission, checkShell, false,
                message);
    }

    /**
     * Enforces the request is from the system or an app that has INTERACT_ACROSS_USERS
     * or INTERACT_ACROSS_USERS_FULL permissions, if the {@code userId} is not for the caller.
     *
     * @param checkShell whether to prevent shell from access if there's a debugging restriction
     * @param requirePermissionWhenSameUser When {@code true}, still require the cross user
     *                                      permission to be held even if the callingUid and
     * userId
     *                                      reference the same user.
     * @param message the message to log on security exception
     */
    public final void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
            boolean requireFullPermission, boolean checkShell,
            boolean requirePermissionWhenSameUser, String message) {
        if (userId < 0) {
            throw new IllegalArgumentException("Invalid userId " + userId);
        }
        if (checkShell) {
            PackageManagerServiceUtils.enforceShellRestriction(
                    mInjector.getUserManagerInternal(),
                    UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
        }
        final int callingUserId = UserHandle.getUserId(callingUid);
        if (hasCrossUserPermission(
                callingUid, callingUserId, userId, requireFullPermission,
                requirePermissionWhenSameUser)) {
            return;
        }
        String errorMessage = buildInvalidCrossUserPermissionMessage(
                callingUid, userId, message, requireFullPermission);
        Slog.w(TAG, errorMessage);
        throw new SecurityException(errorMessage);
    }

    private static String buildInvalidCrossUserPermissionMessage(int callingUid,
            @UserIdInt int userId, String message, boolean requireFullPermission) {
        StringBuilder builder = new StringBuilder();
        if (message != null) {
            builder.append(message);
            builder.append(": ");
        }
        builder.append("UID ");
        builder.append(callingUid);
        builder.append(" requires ");
        builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL);
        if (!requireFullPermission) {
            builder.append(" or ");
            builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS);
        }
        builder.append(" to access user ");
        builder.append(userId);
        builder.append(".");
        return builder.toString();
    }

    public SigningDetails getSigningDetails(@NonNull String packageName) {
        AndroidPackage p = mPackages.get(packageName);
        if (p == null) {
            return null;
        }
        return p.getSigningDetails();
    }

    public SigningDetails getSigningDetails(int uid) {
        final int appId = UserHandle.getAppId(uid);
        final Object obj = mSettings.getSettingBase(appId);
        if (obj != null) {
            if (obj instanceof SharedUserSetting) {
                return ((SharedUserSetting) obj).signatures.mSigningDetails;
            } else if (obj instanceof PackageStateInternal) {
                final PackageStateInternal ps = (PackageStateInternal) obj;
                return ps.getSigningDetails();
            }
        }
        return SigningDetails.UNKNOWN;
    }

    public boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) {
        PackageStateInternal ps = getPackageStateInternal(pkg.getPackageName());
        return shouldFilterApplicationIncludingUninstalled(ps, callingUid, userId);
    }

    public boolean filterAppAccess(String packageName, int callingUid, int userId,
            boolean filterUninstalled) {
        PackageStateInternal ps = getPackageStateInternal(packageName);
        return shouldFilterApplication(
                ps, callingUid, null /* component */, TYPE_UNKNOWN, userId, filterUninstalled);
    }

    public boolean filterAppAccess(int uid, int callingUid) {
        if (Process.isSdkSandboxUid(uid)) {
            // Sdk sandbox instance should be able to see itself.
            if (callingUid == uid) {
                return false;
            }
            final int clientAppUid = Process.getAppUidForSdkSandboxUid(uid);
            // Client app of this sdk sandbox process should be able to see it.
            if (clientAppUid == uid) {
                return false;
            }
            // Nobody else should be able to see the sdk sandbox process.
            return true;
        }
        final int userId = UserHandle.getUserId(uid);
        final int appId = UserHandle.getAppId(uid);
        final Object setting = mSettings.getSettingBase(appId);
        if (setting == null) {
            return true;
        }
        if (setting instanceof SharedUserSetting) {
            return shouldFilterApplicationIncludingUninstalled(
                    (SharedUserSetting) setting, callingUid, userId);
        } else if (setting instanceof PackageStateInternal) {
            return shouldFilterApplicationIncludingUninstalled(
                    (PackageStateInternal) setting, callingUid, userId);
        }
        return true;
    }

    public void dump(int type, FileDescriptor fd, PrintWriter pw, DumpState dumpState) {
        final String packageName = dumpState.getTargetPackageName();
        final PackageStateInternal setting = mSettings.getPackage(packageName);
        final boolean checkin = dumpState.isCheckIn();

        // Return if the package doesn't exist.
        if (packageName != null && setting == null && !isApexPackage(packageName)) {
            return;
        }

        switch (type) {
            case DumpState.DUMP_VERSION:
            {
                if (dumpState.onTitlePrinted()) {
                    pw.println();
                }
                pw.println("Database versions:");
                mSettings.dumpVersionLPr(new IndentingPrintWriter(pw, "  "));
                break;
            }

            case DumpState.DUMP_LIBS:
                mSharedLibraries.dump(pw, dumpState);
                break;

            case DumpState.DUMP_PREFERRED:
                mSettings.dumpPreferred(pw, dumpState, packageName);
                break;

            case DumpState.DUMP_PREFERRED_XML:
            {
                pw.flush();
                TypedXmlSerializer serializer = Xml.newFastSerializer();
                try (BufferedOutputStream str =
                             new BufferedOutputStream(new FileOutputStream(fd))) {
                    serializer.setOutput(str, StandardCharsets.UTF_8.name());
                    serializer.startDocument(null, true);
                    serializer.setFeature(
                            "http://xmlpull.org/v1/doc/features.html#indent-output", true);
                    mSettings.writePreferredActivitiesLPr(serializer, 0,
                            dumpState.isFullPreferred());
                    serializer.endDocument();
                    serializer.flush();
                } catch (IllegalArgumentException e) {
                    pw.println("Failed writing: " + e);
                } catch (IllegalStateException e) {
                    pw.println("Failed writing: " + e);
                } catch (IOException e) {
                    pw.println("Failed writing: " + e);
                }
                break;
            }

            case DumpState.DUMP_QUERIES:
            {
                final Integer filteringAppId = setting == null ? null : setting.getAppId();
                mAppsFilter.dumpQueries(
                        pw, filteringAppId, dumpState, mUserManager.getUserIds(),
                        this::getPackagesForUidInternalBody);
                break;
            }

            case DumpState.DUMP_DOMAIN_PREFERRED:
            {
                final android.util.IndentingPrintWriter writer =
                        new android.util.IndentingPrintWriter(pw);
                if (dumpState.onTitlePrinted()) {
                    pw.println();
                }
                writer.println("Domain verification status:");
                writer.increaseIndent();
                try {
                    mDomainVerificationManager.printState(this, writer, packageName,
                            UserHandle.USER_ALL);
                } catch (Exception e) {
                    pw.println("Failure printing domain verification information");
                    Slog.e(TAG, "Failure printing domain verification information", e);
                }
                writer.decreaseIndent();
                break;
            }

            case DumpState.DUMP_DEXOPT:
            {
                final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ");
                if (dumpState.onTitlePrinted()) {
                    pw.println();
                }
                ipw.println("Dexopt state:");
                ipw.increaseIndent();
                DexOptHelper.dumpDexoptState(ipw, this, packageName);
                ipw.decreaseIndent();
                break;
            }

            case DumpState.DUMP_MESSAGES: {
                mSettings.dumpReadMessages(pw, dumpState);
                break;
            }

            case DumpState.DUMP_FROZEN: {
                // XXX should handle packageName != null by dumping only install data that
                // the given package is involved with.
                if (dumpState.onTitlePrinted()) {
                    pw.println();
                }
                final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120);
                ipw.println();
                ipw.println("Frozen packages:");
                ipw.increaseIndent();
                if (mFrozenPackages.size() == 0) {
                    ipw.println("(none)");
                } else {
                    for (int i = 0; i < mFrozenPackages.size(); i++) {
                        ipw.print("package=");
                        ipw.print(mFrozenPackages.keyAt(i));
                        ipw.print(", refCounts=");
                        ipw.println(mFrozenPackages.valueAt(i));
                    }
                }
                ipw.decreaseIndent();
                break;
            }

            case DumpState.DUMP_APEX: {
                if (packageName == null || isApexPackage(packageName)) {
                    mApexManager.dump(pw);
                    dumpApex(pw, packageName);
                }
                break;
            }
        } // switch
    }

    private void generateApexPackageInfo(@NonNull List<PackageStateInternal> activePackages,
            @NonNull List<PackageStateInternal> inactivePackages,
            @NonNull List<PackageStateInternal> factoryActivePackages,
            @NonNull List<PackageStateInternal> factoryInactivePackages) {
        for (AndroidPackage p : mPackages.values()) {
            final String packageName = p.getPackageName();
            PackageStateInternal ps = mSettings.getPackage(packageName);
            if (!p.isApex() || ps == null) {
                continue;
            }
            activePackages.add(ps);
            if (!ps.isUpdatedSystemApp()) {
                factoryActivePackages.add(ps);
            } else {
                PackageStateInternal psDisabled = mSettings.getDisabledSystemPkg(packageName);
                factoryInactivePackages.add(psDisabled);
                inactivePackages.add(psDisabled);
            }
        }
    }

    private void dumpApex(PrintWriter pw, String packageName) {
        final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "  ", 120);
        List<PackageStateInternal> activePackages = new ArrayList<>();
        List<PackageStateInternal> inactivePackages = new ArrayList<>();
        List<PackageStateInternal> factoryActivePackages = new ArrayList<>();
        List<PackageStateInternal> factoryInactivePackages = new ArrayList<>();
        generateApexPackageInfo(activePackages, inactivePackages, factoryActivePackages,
                factoryInactivePackages);
        ipw.println("Active APEX packages:");
        dumpApexPackageStates(activePackages, true, packageName, ipw);
        ipw.println("Inactive APEX packages:");
        dumpApexPackageStates(inactivePackages, false, packageName, ipw);
        ipw.println("Factory APEX packages:");
        dumpApexPackageStates(factoryActivePackages, true, packageName, ipw);
        dumpApexPackageStates(factoryInactivePackages, false, packageName, ipw);
    }


    /**
     * Dump information about the packages contained in a particular cache
     * @param packageStates the states to print information about.
     * @param packageName a {@link String} containing a package name, or {@code null}. If set,
     *                    only information about that specific package will be dumped.
     * @param ipw the {@link IndentingPrintWriter} object to send information to.
     */
    private static void dumpApexPackageStates(List<PackageStateInternal> packageStates,
            boolean isActive, @Nullable String packageName, IndentingPrintWriter ipw) {
        ipw.println();
        ipw.increaseIndent();
        for (int i = 0, size = packageStates.size(); i < size; i++) {
            final var packageState = packageStates.get(i);
            var pkg = packageState.getPkg();
            if (packageName != null && !packageName.equals(pkg.getPackageName())) {
                continue;
            }
            ipw.println(pkg.getPackageName());
            ipw.increaseIndent();
            ipw.println("Version: " + pkg.getLongVersionCode());
            ipw.println("Path: " + pkg.getBaseApkPath());
            ipw.println("IsActive: " + isActive);
            ipw.println("IsFactory: " + !packageState.isUpdatedSystemApp());
            ipw.println("ApplicationInfo: ");
            ipw.increaseIndent();
            // TODO: Dump the package manually
            AndroidPackageUtils.generateAppInfoWithoutState(pkg)
                    .dump(new PrintWriterPrinter(ipw), "");
            ipw.decreaseIndent();
            ipw.decreaseIndent();
        }
        ipw.decreaseIndent();
        ipw.println();
    }

    // The body of findPreferredActivity.
    protected PackageManagerService.FindPreferredActivityBodyResult findPreferredActivityBody(
            Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
            List<ResolveInfo> query, boolean always,
            boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered,
            int callingUid, boolean isDeviceProvisioned) {
        PackageManagerService.FindPreferredActivityBodyResult
                result = new PackageManagerService.FindPreferredActivityBodyResult();

        flags = updateFlagsForResolve(
                flags, userId, callingUid, false /*includeInstantApps*/,
                isImplicitImageCaptureIntentAndNotSetByDpc(intent, userId,
                        resolvedType, flags));
        intent = PackageManagerServiceUtils.updateIntentForResolve(intent);

        // Try to find a matching persistent preferred activity.
        result.mPreferredResolveInfo = findPersistentPreferredActivity(intent,
                resolvedType, flags, query, debug, userId);

        // If a persistent preferred activity matched, use it.
        if (result.mPreferredResolveInfo != null) {
            return result;
        }

        PreferredIntentResolver pir = mSettings.getPreferredActivities(userId);
        // Get the list of preferred activities that handle the intent
        if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Looking for preferred activities...");
        List<PreferredActivity> prefs = pir != null
                ? pir.queryIntent(this, intent, resolvedType,
                (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
                userId)
                : null;
        if (prefs != null && prefs.size() > 0) {

            // First figure out how good the original match set is.
            // We will only allow preferred activities that came
            // from the same match quality.
            int match = 0;

            if (DEBUG_PREFERRED || debug) {
                Slog.v(TAG, "Figuring out best match...");
            }

            final int n = query.size();
            for (int j = 0; j < n; j++) {
                final ResolveInfo ri = query.get(j);
                if (DEBUG_PREFERRED || debug) {
                    Slog.v(TAG, "Match for " + ri.activityInfo
                            + ": 0x" + Integer.toHexString(ri.match));
                }
                if (ri.match > match) {
                    match = ri.match;
                }
            }

            if (DEBUG_PREFERRED || debug) {
                Slog.v(TAG, "Best match: 0x" + Integer.toHexString(match));
            }
            match &= IntentFilter.MATCH_CATEGORY_MASK;
            final int m = prefs.size();
            for (int i = 0; i < m; i++) {
                final PreferredActivity pa = prefs.get(i);
                if (DEBUG_PREFERRED || debug) {
                    Slog.v(TAG, "Checking PreferredActivity ds="
                            + (pa.countDataSchemes() > 0 ? pa.getDataScheme(0) : "<none>")
                            + "\n  component=" + pa.mPref.mComponent);
                    pa.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), "  ");
                }
                if (pa.mPref.mMatch != match) {
                    if (DEBUG_PREFERRED || debug) {
                        Slog.v(TAG, "Skipping bad match "
                                + Integer.toHexString(pa.mPref.mMatch));
                    }
                    continue;
                }
                // If it's not an "always" type preferred activity and that's what we're
                // looking for, skip it.
                if (always && !pa.mPref.mAlways) {
                    if (DEBUG_PREFERRED || debug) Slog.v(TAG, "Skipping mAlways=false entry");
                    continue;
                }
                final ActivityInfo ai = getActivityInfo(
                        pa.mPref.mComponent, flags | MATCH_DISABLED_COMPONENTS
                                | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
                        userId);
                if (DEBUG_PREFERRED || debug) {
                    Slog.v(TAG, "Found preferred activity:");
                    if (ai != null) {
                        ai.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), "  ");
                    } else {
                        Slog.v(TAG, "  null");
                    }
                }
                final boolean excludeSetupWizardHomeActivity = isHomeIntent(intent)
                        && !isDeviceProvisioned;
                final boolean allowSetMutation = !excludeSetupWizardHomeActivity
                        && !queryMayBeFiltered;
                if (ai == null) {
                    // Do not remove launcher's preferred activity during SetupWizard
                    // due to it may not install yet
                    if (!allowSetMutation) {
                        continue;
                    }

                    // This previously registered preferred activity
                    // component is no longer known.  Most likely an update
                    // to the app was installed and in the new version this
                    // component no longer exists.  Clean it up by removing
                    // it from the preferred activities list, and skip it.
                    Slog.w(TAG, "Removing dangling preferred activity: "
                            + pa.mPref.mComponent);
                    pir.removeFilter(pa);
                    result.mChanged = true;
                    continue;
                }
                for (int j = 0; j < n; j++) {
                    final ResolveInfo ri = query.get(j);
                    if (!ri.activityInfo.applicationInfo.packageName
                            .equals(ai.applicationInfo.packageName)) {
                        continue;
                    }
                    if (!ri.activityInfo.name.equals(ai.name)) {
                        continue;
                    }

                    if (removeMatches && allowSetMutation) {
                        pir.removeFilter(pa);
                        result.mChanged = true;
                        if (DEBUG_PREFERRED) {
                            Slog.v(TAG, "Removing match " + pa.mPref.mComponent);
                        }
                        break;
                    }

                    // Okay we found a previously set preferred or last chosen app.
                    // If the result set is different from when this
                    // was created, and is not a subset of the preferred set, we need to
                    // clear it and re-ask the user their preference, if we're looking for
                    // an "always" type entry.

                    if (always
                            && !pa.mPref.sameSet(query, excludeSetupWizardHomeActivity, userId)) {
                        if (pa.mPref.isSuperset(query, excludeSetupWizardHomeActivity)) {
                            if (allowSetMutation) {
                                // some components of the set are no longer present in
                                // the query, but the preferred activity can still be reused
                                if (DEBUG_PREFERRED) {
                                    Slog.i(TAG, "Result set changed, but PreferredActivity"
                                            + " is still valid as only non-preferred"
                                            + " components were removed for " + intent
                                            + " type " + resolvedType);
                                }
                                // remove obsolete components and re-add the up-to-date
                                // filter
                                PreferredActivity freshPa = new PreferredActivity(pa,
                                        pa.mPref.mMatch,
                                        pa.mPref.discardObsoleteComponents(query),
                                        pa.mPref.mComponent,
                                        pa.mPref.mAlways);
                                pir.removeFilter(pa);
                                pir.addFilter(this, freshPa);
                                result.mChanged = true;
                            } else {
                                if (DEBUG_PREFERRED) {
                                    Slog.i(TAG, "Do not remove preferred activity");
                                }
                            }
                        } else {
                            final boolean isHomeActivity = ACTION_MAIN.equals(intent.getAction())
                                    && intent.hasCategory(CATEGORY_HOME);
                            if (!isHomeActivity) {
                                // Don't reset the preferred activity just for the home intent, we
                                // should respect the default home app even though there any new
                                // home activity is enabled.
                                if (allowSetMutation) {
                                    Slog.i(TAG,
                                            "Result set changed, dropping preferred activity "
                                                    + "for " + intent + " type "
                                                    + resolvedType);
                                    if (DEBUG_PREFERRED) {
                                        Slog.v(TAG,
                                                "Removing preferred activity since set changed "
                                                        + pa.mPref.mComponent);
                                    }
                                    pir.removeFilter(pa);
                                    // Re-add the filter as a "last chosen" entry (!always)
                                    PreferredActivity lastChosen = new PreferredActivity(
                                            pa, pa.mPref.mMatch, null, pa.mPref.mComponent,
                                            false);
                                    pir.addFilter(this, lastChosen);
                                    result.mChanged = true;
                                }
                                result.mPreferredResolveInfo = null;
                                return result;
                            }
                        }
                    }

                    // Yay! Either the set matched or we're looking for the last chosen
                    if (DEBUG_PREFERRED || debug) {
                        Slog.v(TAG, "Returning preferred activity: "
                                + ri.activityInfo.packageName + "/" + ri.activityInfo.name);
                    }
                    result.mPreferredResolveInfo = ri;
                    return result;
                }
            }
        }
        return result;
    }

    private static boolean isHomeIntent(Intent intent) {
        return ACTION_MAIN.equals(intent.getAction())
                && intent.hasCategory(CATEGORY_HOME)
                && intent.hasCategory(CATEGORY_DEFAULT);
    }

    public final PackageManagerService.FindPreferredActivityBodyResult findPreferredActivityInternal(
            Intent intent, String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
            List<ResolveInfo> query, boolean always,
            boolean removeMatches, boolean debug, int userId, boolean queryMayBeFiltered) {

        final int callingUid = Binder.getCallingUid();
        // Do NOT hold the packages lock; this calls up into the settings provider which
        // could cause a deadlock.
        final boolean isDeviceProvisioned =
                android.provider.Settings.Global.getInt(mContext.getContentResolver(),
                        android.provider.Settings.Global.DEVICE_PROVISIONED, 0) == 1;
        // Find the preferred activity - the lock is held inside the method.
        return findPreferredActivityBody(
                intent, resolvedType, flags, query, always, removeMatches, debug,
                userId, queryMayBeFiltered, callingUid, isDeviceProvisioned);
    }

    public final ResolveInfo findPersistentPreferredActivity(Intent intent,
            String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags,
            List<ResolveInfo> query, boolean debug, int userId) {
        final int n = query.size();
        PersistentPreferredIntentResolver ppir =
                mSettings.getPersistentPreferredActivities(userId);
        // Get the list of persistent preferred activities that handle the intent
        if (DEBUG_PREFERRED || debug) {
            Slog.v(TAG, "Looking for persistent preferred activities...");
        }
        List<PersistentPreferredActivity> pprefs = ppir != null
                ? ppir.queryIntent(this, intent, resolvedType,
                (flags & PackageManager.MATCH_DEFAULT_ONLY) != 0,
                userId)
                : null;
        if (pprefs != null && pprefs.size() > 0) {
            final int m = pprefs.size();
            for (int i = 0; i < m; i++) {
                final PersistentPreferredActivity ppa = pprefs.get(i);
                if (DEBUG_PREFERRED || debug) {
                    Slog.v(TAG, "Checking PersistentPreferredActivity ds="
                            + (ppa.countDataSchemes() > 0 ? ppa.getDataScheme(0) : "<none>")
                            + "\n  component=" + ppa.mComponent);
                    ppa.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), "  ");
                }
                final ActivityInfo ai = getActivityInfo(ppa.mComponent,
                        flags | MATCH_DISABLED_COMPONENTS, userId);
                if (DEBUG_PREFERRED || debug) {
                    Slog.v(TAG, "Found persistent preferred activity:");
                    if (ai != null) {
                        ai.dump(new LogPrinter(Log.VERBOSE, TAG, Log.LOG_ID_SYSTEM), "  ");
                    } else {
                        Slog.v(TAG, "  null");
                    }
                }
                if (ai == null) {
                    // This previously registered persistent preferred activity
                    // component is no longer known. Ignore it and do NOT remove it.
                    continue;
                }
                for (int j = 0; j < n; j++) {
                    final ResolveInfo ri = query.get(j);
                    if (!ri.activityInfo.applicationInfo.packageName
                            .equals(ai.applicationInfo.packageName)) {
                        continue;
                    }
                    if (!ri.activityInfo.name.equals(ai.name)) {
                        continue;
                    }
                    //  Found a persistent preference that can handle the intent.
                    if (DEBUG_PREFERRED || debug) {
                        Slog.v(TAG, "Returning persistent preferred activity: "
                                + ri.activityInfo.packageName + "/" + ri.activityInfo.name);
                    }
                    return ri;
                }
            }
        }
        return null;
    }

    @Override
    public PreferredIntentResolver getPreferredActivities(int userId) {
        return mSettings.getPreferredActivities(userId);
    }

    @NonNull
    @Override
    public ArrayMap<String, ? extends PackageStateInternal> getPackageStates() {
        return mSettings.getPackages();
    }

    @NonNull
    @Override
    public ArrayMap<String, ? extends PackageStateInternal> getDisabledSystemPackageStates() {
        return mSettings.getDisabledSystemPackages();
    }

    @Nullable
    @Override
    public String getRenamedPackage(@NonNull String packageName) {
        return mSettings.getRenamedPackageLPr(packageName);
    }

    @NonNull
    @Override
    public WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>>
            getSharedLibraries() {
        return mSharedLibraries.getAll();
    }

    @NonNull
    @Override
    public ArraySet<String> getNotifyPackagesForReplacedReceived(@NonNull String[] packages) {
        final int callingUid = Binder.getCallingUid();
        final int callingUserId = UserHandle.getUserId(callingUid);

        ArraySet<String> packagesToNotify = new ArraySet<>();
        for (String packageName : packages) {
            final PackageStateInternal packageState = getPackageStateInternal(packageName);
            if (!shouldFilterApplication(packageState, callingUid, callingUserId)) {
                packagesToNotify.add(packageName);
            }
        }

        return packagesToNotify;
    }

    @PackageManagerService.PackageStartability
    @Override
    public int getPackageStartability(boolean safeMode, @NonNull String packageName, int callingUid,
            @UserIdInt int userId) {
        final boolean ceStorageUnlocked = StorageManager.isCeStorageUnlocked(userId);
        final PackageStateInternal ps = getPackageStateInternal(packageName);
        if (ps == null || shouldFilterApplication(ps, callingUid, userId)
                || !ps.getUserStateOrDefault(userId).isInstalled()) {
            return PackageManagerService.PACKAGE_STARTABILITY_NOT_FOUND;
        }

        if (safeMode && !ps.isSystem()) {
            return PackageManagerService.PACKAGE_STARTABILITY_NOT_SYSTEM;
        }

        if (mFrozenPackages.containsKey(packageName)) {
            return PackageManagerService.PACKAGE_STARTABILITY_FROZEN;
        }

        if (!ceStorageUnlocked && !AndroidPackageUtils.isEncryptionAware(ps.getPkg())) {
            return PackageManagerService.PACKAGE_STARTABILITY_DIRECT_BOOT_UNSUPPORTED;
        }
        return PackageManagerService.PACKAGE_STARTABILITY_OK;
    }

    @Override
    public boolean isPackageAvailable(String packageName, int userId) {
        if (!mUserManager.exists(userId)) return false;
        final int callingUid = Binder.getCallingUid();
        enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
                false /*checkShell*/, "is package available");

        final PackageStateInternal ps = getPackageStateInternal(packageName);
        if (ps != null && ps.getPkg() != null) {
            if (shouldFilterApplication(ps, callingUid, userId)) {
                return false;
            }
            final PackageUserStateInternal state = ps.getUserStateOrDefault(userId);
            if (state != null) {
                return PackageUserStateUtils.isAvailable(state, 0);
            }
        }
        return false;
    }

    @Override
    public boolean isApexPackage(String packageName) {
        final AndroidPackage pkg = mPackages.get(packageName);
        return pkg != null && pkg.isApex();
    }

    @Override
    public String[] currentToCanonicalPackageNames(String[] names) {
        final int callingUid = Binder.getCallingUid();
        if (getInstantAppPackageName(callingUid) != null) {
            return names;
        }
        final String[] out = new String[names.length];
        final int callingUserId = UserHandle.getUserId(callingUid);
        final boolean canViewInstantApps = canViewInstantApps(callingUid, callingUserId);
        for (int i=names.length-1; i>=0; i--) {
            final PackageStateInternal ps = getPackageStateInternal(names[i]);
            boolean translateName = false;
            if (ps != null && ps.getRealName() != null) {
                final boolean targetIsInstantApp = ps.getUserStateOrDefault(callingUserId)
                        .isInstantApp();
                translateName = !targetIsInstantApp
                        || canViewInstantApps
                        || mInstantAppRegistry.isInstantAccessGranted(callingUserId,
                        UserHandle.getAppId(callingUid), ps.getAppId());
            }
            out[i] = translateName ? ps.getRealName() : names[i];
        }
        return out;
    }

    @Override
    public String[] canonicalToCurrentPackageNames(String[] names) {
        final int callingUid = Binder.getCallingUid();
        if (getInstantAppPackageName(callingUid) != null) {
            return names;
        }
        final String[] out = new String[names.length];
        final int callingUserId = UserHandle.getUserId(callingUid);
        final boolean canViewInstantApps = canViewInstantApps(callingUid, callingUserId);
        for (int i=names.length-1; i>=0; i--) {
            final String cur = getRenamedPackage(names[i]);
            boolean translateName = false;
            if (cur != null) {
                final PackageStateInternal ps = getPackageStateInternal(names[i]);
                final boolean targetIsInstantApp =
                        ps != null && ps.getUserStateOrDefault(callingUserId).isInstantApp();
                translateName = !targetIsInstantApp
                        || canViewInstantApps
                        || mInstantAppRegistry.isInstantAccessGranted(callingUserId,
                        UserHandle.getAppId(callingUid), ps.getAppId());
            }
            out[i] = translateName ? cur : names[i];
        }
        return out;
    }

    @Override
    public int[] getPackageGids(@NonNull String packageName,
            @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId) {
        if (!mUserManager.exists(userId)) return null;
        final int callingUid = Binder.getCallingUid();
        flags = updateFlagsForPackage(flags, userId);
        enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
                false /*checkShell*/, "getPackageGids");

        final PackageStateInternal ps = getPackageStateInternal(packageName);
        if (ps == null) {
            return null;
        }
        if (ps.getPkg() != null
                && AndroidPackageUtils.isMatchForSystemOnly(ps, flags)) {
            if (ps.getUserStateOrDefault(userId).isInstalled()
                    && !shouldFilterApplication(ps, callingUid, userId)) {
                return mPermissionManager.getGidsForUid(UserHandle.getUid(userId,
                        ps.getAppId()));
            }
        }
        if ((flags & (MATCH_KNOWN_PACKAGES | MATCH_ARCHIVED_PACKAGES)) != 0) {
            if (PackageStateUtils.isMatch(ps, flags)
                    && !shouldFilterApplication(ps, callingUid, userId)) {
                return mPermissionManager.getGidsForUid(
                        UserHandle.getUid(userId, ps.getAppId()));
            }
        }

        return null;
    }

    @Override
    public int getTargetSdkVersion(@NonNull String packageName)  {
        final PackageStateInternal ps = getPackageStateInternal(packageName);
        if (ps == null || ps.getPkg() == null) {
            return -1;
        }
        if (shouldFilterApplicationIncludingUninstalled(ps, Binder.getCallingUid(),
                UserHandle.getCallingUserId())) {
            return -1;
        }
        return ps.getPkg().getTargetSdkVersion();
    }

    @Override
    public boolean activitySupportsIntentAsUser(@NonNull ComponentName resolveComponentName,
            @NonNull ComponentName component, @NonNull Intent intent, String resolvedType,
            int userId) {
        final int callingUid = Binder.getCallingUid();
        enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
                false /* checkShell */, "activitySupportsIntentAsUser");
        if (component.equals(resolveComponentName)) {
            // The resolver supports EVERYTHING!
            return true;
        }
        ParsedActivity a = mComponentResolver.getActivity(component);
        if (a == null) {
            return false;
        }
        final PackageStateInternal ps = getPackageStateInternal(component.getPackageName());
        if (ps == null) {
            return false;
        }
        if (shouldFilterApplication(
                ps, callingUid, component, TYPE_ACTIVITY, userId, true /* filterUninstall */)) {
            return false;
        }
        for (int i=0; i< a.getIntents().size(); i++) {
            if (a.getIntents().get(i).getIntentFilter()
                    .match(intent.getAction(), resolvedType, intent.getScheme(),
                            intent.getData(), intent.getCategories(), TAG) >= 0) {
                return true;
            }
        }
        return false;
    }

    @Nullable
    @Override
    public ActivityInfo getReceiverInfo(@NonNull ComponentName component,
            @PackageManager.ComponentInfoFlagsBits long flags, @UserIdInt int userId) {
        if (!mUserManager.exists(userId)) return null;
        final int callingUid = Binder.getCallingUid();
        flags = updateFlagsForComponent(flags, userId);
        enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
                false /* checkShell */, "get receiver info");

        ParsedActivity a = mComponentResolver.getReceiver(component);
        if (DEBUG_PACKAGE_INFO) Log.v(
                TAG, "getReceiverInfo " + component + ": " + a);

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

        final PackageStateInternal ps = getPackageStateInternal(a.getPackageName());
        if (ps == null || ps.getPkg() == null) {
            return null;
        }

        if (PackageStateUtils.isEnabledAndMatches(ps, a, flags, userId)) {
            if (shouldFilterApplication(ps, callingUid, component, TYPE_RECEIVER, userId)) {
                return null;
            }
            return PackageInfoUtils.generateActivityInfo(ps.getPkg(),
                    a, flags, ps.getUserStateOrDefault(userId), userId, ps);
        }
        return null;
    }

    @Nullable
    @Override
    public ParceledListSlice<SharedLibraryInfo> getSharedLibraries(@NonNull String packageName,
            @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId) {
        if (!mUserManager.exists(userId)) return null;
        Preconditions.checkArgumentNonnegative(userId, "userId must be >= 0");
        final int callingUid = Binder.getCallingUid();
        if (getInstantAppPackageName(callingUid) != null) {
            return null;
        }

        final boolean isPrivilegedInstaller = mContext.checkCallingOrSelfPermission(INSTALL_PACKAGES)
                == PERMISSION_GRANTED;

        long exemptedFlags = 0L;
        if (isPrivilegedInstaller) {
            exemptedFlags = flags & PackageManager.MATCH_ANY_USER;
            flags &= ~PackageManager.MATCH_ANY_USER;
            // updateFlagsForPackage() below throws SecurityException if caller doesn't have
            // INTERACT_ACROSS_USERS perm and tries to use the MATCH_ANY_USER flag.
            //
            // In this particular codepath, MATCH_ANY_USER reveals only the global list of shared
            // libraries and the global list of packages that depend on those libraries.
            //
            // Access to global list of libraries is required to properly handle shared library
            // updates in multi-user scenarios, since installing the same version of library for the
            // second time is not allowed, but the installer doesn't know whether that version is
            // already installed if it was installed in another user.
            //
            // It's preferable to add this minor exemption instead of requiring installer to hold
            // the INTERACT_ACROSS_USERS permission, which grants much more privileges.
        }
        flags = updateFlagsForPackage(flags, userId);
        flags |= exemptedFlags;

        final boolean canSeeStaticAndSdkLibraries =
                isPrivilegedInstaller
                        || mContext.checkCallingOrSelfPermission(DELETE_PACKAGES)
                        == PERMISSION_GRANTED
                        || canRequestPackageInstalls(packageName, callingUid, userId,
                        false  /* throwIfPermNotDeclared*/)
                        || mContext.checkCallingOrSelfPermission(REQUEST_DELETE_PACKAGES)
                        == PERMISSION_GRANTED
                        || mContext.checkCallingOrSelfPermission(
                        Manifest.permission.ACCESS_SHARED_LIBRARIES) == PERMISSION_GRANTED;

        final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibraries =
                getSharedLibraries();
        List<SharedLibraryInfo> result = null;
        final int libCount = sharedLibraries.size();
        for (int i = 0; i < libCount; i++) {
            WatchedLongSparseArray<SharedLibraryInfo> versionedLib = sharedLibraries.valueAt(i);
            if (versionedLib == null) {
                continue;
            }

            final int versionCount = versionedLib.size();
            for (int j = 0; j < versionCount; j++) {
                SharedLibraryInfo libInfo = versionedLib.valueAt(j);
                if (!canSeeStaticAndSdkLibraries && (libInfo.isStatic() || libInfo.isSdk())) {
                    break;
                }
                final long identity = Binder.clearCallingIdentity();
                final VersionedPackage declaringPackage = libInfo.getDeclaringPackage();
                try {
                    PackageInfo packageInfo = getPackageInfoInternal(
                            declaringPackage.getPackageName(),
                            declaringPackage.getLongVersionCode(),
                            flags | PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES,
                            Binder.getCallingUid(), userId);
                    if (packageInfo == null) {
                        continue;
                    }
                } finally {
                    Binder.restoreCallingIdentity(identity);
                }
                SharedLibraryInfo resLibInfo = new SharedLibraryInfo(libInfo.getPath(),
                        libInfo.getPackageName(), libInfo.getAllCodePaths(),
                        libInfo.getName(), libInfo.getLongVersion(),
                        libInfo.getType(), declaringPackage,
                        (libInfo.getDependencies() == null
                                ? null
                                : new ArrayList<>(libInfo.getDependencies())),
                        libInfo.isNative(),
                        getPackagesUsingSharedLibrary(libInfo, flags, callingUid, userId));
                if (result == null) {
                    result = new ArrayList<>();
                }
                result.add(resLibInfo);
            }
        }

        return result != null ? new ParceledListSlice<>(result) : null;
    }

    @Override
    public boolean canRequestPackageInstalls(@NonNull String packageName, int callingUid,
            int userId, boolean throwIfPermNotDeclared) {
        int uid = getPackageUidInternal(packageName, 0, userId, callingUid);
        if (callingUid != uid && !PackageManagerServiceUtils.isSystemOrRoot(callingUid)) {
            throw new SecurityException(
                    "Caller uid " + callingUid + " does not own package " + packageName);
        }
        if (isInstantAppInternal(packageName, userId, Process.SYSTEM_UID)) {
            return false;
        }
        final AndroidPackage pkg =  mPackages.get(packageName);
        if (pkg == null) {
            return false;
        }
        if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.O) {
            return false;
        }
        if (!pkg.getRequestedPermissions().contains(
                android.Manifest.permission.REQUEST_INSTALL_PACKAGES)) {
            final String message = "Need to declare "
                    + android.Manifest.permission.REQUEST_INSTALL_PACKAGES
                    + " to call this api";
            if (throwIfPermNotDeclared) {
                throw new SecurityException(message);
            } else {
                Slog.e(TAG, message);
                return false;
            }
        }

        return !isInstallDisabledForPackage(packageName, uid, userId);
    }

    /**
     * Returns true if the system or user is explicitly preventing an otherwise valid installer to
     * complete an install. This includes checks like unknown sources and user restrictions.
     */
    @Override
    public final boolean isInstallDisabledForPackage(@NonNull String packageName, int uid,
            @UserIdInt int userId) {
        if (mUserManager.hasUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, userId)
                || mUserManager.hasUserRestriction(
                UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, userId)) {
            return true;
        }
        if (mExternalSourcesPolicy != null) {
            int isTrusted = mExternalSourcesPolicy.getPackageTrustedToInstallApps(packageName, uid);
            return isTrusted != PackageManagerInternal.ExternalSourcesPolicy.USER_TRUSTED;
        }
        return false;
    }


    @Override
    public Pair<List<VersionedPackage>, List<Boolean>> getPackagesUsingSharedLibrary(
            @NonNull SharedLibraryInfo libInfo, @PackageManager.PackageInfoFlagsBits long flags,
            int callingUid, @UserIdInt int userId) {
        List<VersionedPackage> versionedPackages = null;
        final ArrayMap<String, ? extends PackageStateInternal> packageStates = getPackageStates();
        final int packageCount = packageStates.size();
        List<Boolean> usesLibsOptional = null;
        for (int i = 0; i < packageCount; i++) {
            PackageStateInternal ps = packageStates.valueAt(i);
            if (ps == null) {
                continue;
            }

            if (!PackageUserStateUtils.isAvailable(ps.getUserStateOrDefault(userId), flags)) {
                continue;
            }

            final String libName = libInfo.getName();
            if (libInfo.isStatic() || libInfo.isSdk()) {
                final String[] libs =
                        libInfo.isStatic() ? ps.getUsesStaticLibraries() : ps.getUsesSdkLibraries();
                final long[] libsVersions = libInfo.isStatic() ? ps.getUsesStaticLibrariesVersions()
                        : ps.getUsesSdkLibrariesVersionsMajor();
                final boolean[] libsOptional = libInfo.isSdk()
                        ? ps.getUsesSdkLibrariesOptional() : null;

                final int libIdx = ArrayUtils.indexOf(libs, libName);
                if (libIdx < 0) {
                    continue;
                }
                if (libsVersions[libIdx] != libInfo.getLongVersion()) {
                    // Not expected StaticLib/SdkLib version
                    continue;
                }
                if (shouldFilterApplication(ps, callingUid, userId)) {
                    continue;
                }
                if (versionedPackages == null) {
                    versionedPackages = new ArrayList<>();
                }
                if (usesLibsOptional == null) {
                    usesLibsOptional = new ArrayList<>();
                }
                // If the dependent is a static shared lib, use the public package name
                String dependentPackageName = ps.getPackageName();
                if (ps.getPkg() != null && ps.getPkg().isStaticSharedLibrary()) {
                    dependentPackageName = ps.getPkg().getManifestPackageName();
                }
                versionedPackages.add(new VersionedPackage(dependentPackageName,
                        ps.getVersionCode()));
                usesLibsOptional.add(libsOptional != null && libsOptional[libIdx]);
            } else if (ps.getPkg() != null) {
                if (ArrayUtils.contains(ps.getPkg().getUsesLibraries(), libName)
                        || ArrayUtils.contains(ps.getPkg().getUsesOptionalLibraries(), libName)) {
                    if (shouldFilterApplication(ps, callingUid, userId)) {
                        continue;
                    }
                    if (versionedPackages == null) {
                        versionedPackages = new ArrayList<>();
                    }
                    versionedPackages.add(new VersionedPackage(ps.getPackageName(),
                            ps.getVersionCode()));
                }
            }
        }

        return new Pair<>(versionedPackages, usesLibsOptional);
    }

    @Nullable
    @Override
    public ParceledListSlice<SharedLibraryInfo> getDeclaredSharedLibraries(
            @NonNull String packageName, @PackageManager.PackageInfoFlagsBits long flags,
            @UserIdInt int userId) {
        mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_SHARED_LIBRARIES,
                "getDeclaredSharedLibraries");
        int callingUid = Binder.getCallingUid();
        enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
                false /* checkShell */, "getDeclaredSharedLibraries");

        Preconditions.checkNotNull(packageName, "packageName cannot be null");
        Preconditions.checkArgumentNonnegative(userId, "userId must be >= 0");
        if (!mUserManager.exists(userId)) {
            return null;
        }

        if (getInstantAppPackageName(callingUid) != null) {
            return null;
        }

        final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibraries =
                getSharedLibraries();
        List<SharedLibraryInfo> result = null;

        int libraryCount = sharedLibraries.size();
        for (int i = 0; i < libraryCount; i++) {
            WatchedLongSparseArray<SharedLibraryInfo> versionedLibrary =
                    sharedLibraries.valueAt(i);
            if (versionedLibrary == null) {
                continue;
            }

            int versionCount = versionedLibrary.size();
            for (int j = 0; j < versionCount; j++) {
                SharedLibraryInfo libraryInfo = versionedLibrary.valueAt(j);

                VersionedPackage declaringPackage = libraryInfo.getDeclaringPackage();
                if (!Objects.equals(declaringPackage.getPackageName(), packageName)) {
                    continue;
                }

                final long identity = Binder.clearCallingIdentity();
                try {
                    PackageInfo packageInfo = getPackageInfoInternal(
                            declaringPackage.getPackageName(),
                            declaringPackage.getLongVersionCode(),
                            flags | PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES,
                            Binder.getCallingUid(), userId);
                    if (packageInfo == null) {
                        continue;
                    }
                } finally {
                    Binder.restoreCallingIdentity(identity);
                }

                var usingSharedLibraryPair =
                        getPackagesUsingSharedLibrary(libraryInfo, flags, callingUid, userId);
                SharedLibraryInfo resultLibraryInfo = new SharedLibraryInfo(
                        libraryInfo.getPath(), libraryInfo.getPackageName(),
                        libraryInfo.getAllCodePaths(), libraryInfo.getName(),
                        libraryInfo.getLongVersion(), libraryInfo.getType(),
                        libraryInfo.getDeclaringPackage(),
                        usingSharedLibraryPair.first,
                        libraryInfo.getDependencies() == null
                                ? null : new ArrayList<>(libraryInfo.getDependencies()),
                        libraryInfo.isNative());

                if (result == null) {
                    result = new ArrayList<>();
                }
                result.add(resultLibraryInfo);
            }
        }

        return result != null ? new ParceledListSlice<>(result) : null;
    }

    @Nullable
    @Override
    public ProviderInfo getProviderInfo(@NonNull ComponentName component,
            @PackageManager.ComponentInfoFlagsBits long flags, @UserIdInt int userId) {
        if (!mUserManager.exists(userId)) return null;
        final int callingUid = Binder.getCallingUid();
        flags = updateFlagsForComponent(flags, userId);
        enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
                false /* checkShell */, "get provider info");
        ParsedProvider p = mComponentResolver.getProvider(component);
        if (DEBUG_PACKAGE_INFO) Log.v(
                TAG, "getProviderInfo " + component + ": " + p);
        if (p == null) {
            return null;
        }

        final PackageStateInternal ps = getPackageStateInternal(p.getPackageName());
        if (ps == null || ps.getPkg() == null) {
            return null;
        }

        if (PackageStateUtils.isEnabledAndMatches(ps, p, flags, userId)) {
            if (shouldFilterApplication(
                    ps, callingUid, component, TYPE_PROVIDER, userId)) {
                return null;
            }
            PackageUserStateInternal state = ps.getUserStateOrDefault(userId);
            final ApplicationInfo appInfo =
                    PackageInfoUtils.generateApplicationInfo(ps.getPkg(), flags, state, userId, ps);
            if (appInfo == null) {
                return null;
            }
            return PackageInfoUtils.generateProviderInfo(ps.getPkg(), p, flags, state, appInfo,
                    userId, ps);
        }
        return null;
    }

    @Override
    public ArrayMap<String, String> getSystemSharedLibraryNamesAndPaths() {
        // allow instant applications
        final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibraries =
                getSharedLibraries();
        final ArrayMap<String, String> libs = new ArrayMap<>();
        final int libCount = sharedLibraries.size();
        for (int i = 0; i < libCount; i++) {
            WatchedLongSparseArray<SharedLibraryInfo> versionedLib = sharedLibraries.valueAt(i);
            if (versionedLib == null) {
                continue;
            }
            final int versionCount = versionedLib.size();
            for (int j = 0; j < versionCount; j++) {
                SharedLibraryInfo libraryInfo = versionedLib.valueAt(j);
                if (!libraryInfo.isStatic()) {
                    libs.put(libraryInfo.getName(), libraryInfo.getPath());
                    break;
                }
                final PackageStateInternal ps =
                        getPackageStateInternal(libraryInfo.getPackageName());
                if (ps != null && !filterSharedLibPackage(ps, Binder.getCallingUid(),
                        UserHandle.getUserId(Binder.getCallingUid()),
                        PackageManager.MATCH_STATIC_SHARED_AND_SDK_LIBRARIES)) {
                    libs.put(libraryInfo.getName(), libraryInfo.getPath());
                    break;
                }
            }
        }
        return libs;
    }

    @Override
    public PackageStateInternal getPackageStateForInstalledAndFiltered(@NonNull String packageName,
            int callingUid, @UserIdInt int userId) {
        final PackageStateInternal packageState = getPackageStateInternal(packageName);
        if (packageState == null
                || shouldFilterApplicationIncludingUninstalled(packageState, callingUid, userId)) {
            return null;
        }
        return packageState;
    }

    @Override
    public int checkSignatures(@NonNull String pkg1, @NonNull String pkg2, int userId) {
        final int callingUid = Binder.getCallingUid();
        enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
                false /* checkShell */, "checkSignatures");

        final AndroidPackage p1 = mPackages.get(pkg1);
        final AndroidPackage p2 = mPackages.get(pkg2);
        final PackageStateInternal ps1 =
                p1 == null ? null : getPackageStateInternal(p1.getPackageName());
        final PackageStateInternal ps2 =
                p2 == null ? null : getPackageStateInternal(p2.getPackageName());
        if (p1 == null || ps1 == null || p2 == null || ps2 == null) {
            return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
        }
        if (shouldFilterApplicationIncludingUninstalled(ps1, callingUid, userId)
                || shouldFilterApplicationIncludingUninstalled(ps2, callingUid, userId)) {
            return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
        }
        return checkSignaturesInternal(p1.getSigningDetails(), p2.getSigningDetails());
    }

    @Override
    public int checkUidSignatures(int uid1, int uid2) {
        final int callingUid = Binder.getCallingUid();
        final int callingUserId = UserHandle.getUserId(callingUid);
        final SigningDetails p1SigningDetails =
                getSigningDetailsAndFilterAccess(uid1, callingUid, callingUserId);
        final SigningDetails p2SigningDetails =
                getSigningDetailsAndFilterAccess(uid2, callingUid, callingUserId);
        if (p1SigningDetails == null || p2SigningDetails == null) {
            return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
        }
        return checkSignaturesInternal(p1SigningDetails, p2SigningDetails);
    }

    @Override
    public int checkUidSignaturesForAllUsers(int uid1, int uid2) {
        final int callingUid = Binder.getCallingUid();
        final int userId1 = UserHandle.getUserId(uid1);
        final int userId2 = UserHandle.getUserId(uid2);
        enforceCrossUserPermission(callingUid, userId1, false /* requireFullPermission */,
                false /* checkShell */, "checkUidSignaturesForAllUsers");
        enforceCrossUserPermission(callingUid, userId2, false /* requireFullPermission */,
                false /* checkShell */, "checkUidSignaturesForAllUsers");
        final SigningDetails p1SigningDetails =
                getSigningDetailsAndFilterAccess(uid1, callingUid, userId1);
        final SigningDetails p2SigningDetails =
                getSigningDetailsAndFilterAccess(uid2, callingUid, userId2);
        if (p1SigningDetails == null || p2SigningDetails == null) {
            return PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
        }
        return checkSignaturesInternal(p1SigningDetails, p2SigningDetails);
    }

    private SigningDetails getSigningDetailsAndFilterAccess(int uid, int callingUid, int userId) {
        // Map to base uids.
        final int appId = UserHandle.getAppId(uid);
        final Object obj = mSettings.getSettingBase(appId);
        if (obj == null) {
            return null;
        }
        if (obj instanceof SharedUserSetting) {
            final SharedUserSetting sus = (SharedUserSetting) obj;
            if (shouldFilterApplicationIncludingUninstalled(sus, callingUid, userId)) {
                return null;
            }
            return sus.signatures.mSigningDetails;
        } else if (obj instanceof PackageSetting) {
            final PackageSetting ps = (PackageSetting) obj;
            if (shouldFilterApplicationIncludingUninstalled(ps, callingUid, userId)) {
                return null;
            }
            return ps.getSigningDetails();
        }
        return null;
    }

    private int checkSignaturesInternal(SigningDetails p1SigningDetails,
            SigningDetails p2SigningDetails) {
        if (p1SigningDetails == null) {
            return p2SigningDetails == null
                    ? PackageManager.SIGNATURE_NEITHER_SIGNED
                    : PackageManager.SIGNATURE_FIRST_NOT_SIGNED;
        }
        if (p2SigningDetails == null) {
            return PackageManager.SIGNATURE_SECOND_NOT_SIGNED;
        }
        int result = compareSignatures(p1SigningDetails, p2SigningDetails);
        if (result == PackageManager.SIGNATURE_MATCH) {
            return result;
        }
        // To support backwards compatibility with clients of this API expecting pre-key
        // rotation results if either of the packages has a signing lineage the oldest signer
        // in the lineage is used for signature verification.
        if (p1SigningDetails.hasPastSigningCertificates()
                || p2SigningDetails.hasPastSigningCertificates()) {
            Signature[] p1Signatures = p1SigningDetails.hasPastSigningCertificates()
                    ? new Signature[]{p1SigningDetails.getPastSigningCertificates()[0]}
                    : p1SigningDetails.getSignatures();
            Signature[] p2Signatures = p2SigningDetails.hasPastSigningCertificates()
                    ? new Signature[]{p2SigningDetails.getPastSigningCertificates()[0]}
                    : p2SigningDetails.getSignatures();
            result = compareSignatureArrays(p1Signatures, p2Signatures);
        }
        return result;
    }

    @Override
    public boolean hasSigningCertificate(@NonNull String packageName, @NonNull byte[] certificate,
        @PackageManager.CertificateInputType int type) {
        final AndroidPackage p = mPackages.get(packageName);
        if (p == null) {
            return false;
        }
        final int callingUid = Binder.getCallingUid();
        final int callingUserId = UserHandle.getUserId(callingUid);
        final PackageStateInternal ps = getPackageStateInternal(p.getPackageName());
        if (ps == null
                || shouldFilterApplicationIncludingUninstalled(ps, callingUid, callingUserId)) {
            return false;
        }
        switch (type) {
            case CERT_INPUT_RAW_X509:
                return p.getSigningDetails().hasCertificate(certificate);
            case CERT_INPUT_SHA256:
                return p.getSigningDetails().hasSha256Certificate(certificate);
            default:
                return false;
        }
    }

    @Override
    public boolean hasUidSigningCertificate(int uid, @NonNull byte[] certificate,
            @PackageManager.CertificateInputType int type) {
        final int callingUid = Binder.getCallingUid();
        final int callingUserId = UserHandle.getUserId(callingUid);
        final SigningDetails signingDetails =
                getSigningDetailsAndFilterAccess(uid, callingUid, callingUserId);
        if (signingDetails == null) {
            return false;
        }
        switch (type) {
            case CERT_INPUT_RAW_X509:
                return signingDetails.hasCertificate(certificate);
            case CERT_INPUT_SHA256:
                return signingDetails.hasSha256Certificate(certificate);
            default:
                return false;
        }
    }

    @Override
    public List<String> getAllPackages() {
        PackageManagerServiceUtils.enforceSystemOrRootOrShell(
                "getAllPackages is limited to privileged callers");
        final int callingUid = Binder.getCallingUid();
        final int callingUserId = UserHandle.getUserId(callingUid);
        if (canViewInstantApps(callingUid, callingUserId)) {
            return new ArrayList<>(mPackages.keySet());
        }
        final String instantAppPkgName = getInstantAppPackageName(callingUid);
        final List<String> result = new ArrayList<>();
        if (instantAppPkgName != null) {
            // caller is an instant application; filter unexposed applications
            for (AndroidPackage pkg : mPackages.values()) {
                if (!pkg.isVisibleToInstantApps()) {
                    continue;
                }
                result.add(pkg.getPackageName());
            }
        } else {
            // caller is a normal application; filter instant applications
            for (AndroidPackage pkg : mPackages.values()) {
                final PackageStateInternal ps = getPackageStateInternal(pkg.getPackageName());
                if (ps != null
                        && ps.getUserStateOrDefault(callingUserId).isInstantApp()
                        && !mInstantAppRegistry.isInstantAccessGranted(callingUserId,
                        UserHandle.getAppId(callingUid), ps.getAppId())) {
                    continue;
                }
                result.add(pkg.getPackageName());
            }
        }
        return result;
    }

    @Nullable
    @Override
    public String getNameForUid(int uid) {
        final int callingUid = Binder.getCallingUid();
        if (getInstantAppPackageName(callingUid) != null) {
            return null;
        }
        if (Process.isSdkSandboxUid(uid)) {
            uid = getBaseSdkSandboxUid();
        }
        final int callingUserId = UserHandle.getUserId(callingUid);
        if (isKnownIsolatedComputeApp(uid)) {
            uid = getIsolatedOwner(uid);
        }
        final int appId = UserHandle.getAppId(uid);
        final Object obj = mSettings.getSettingBase(appId);
        if (obj instanceof SharedUserSetting) {
            final SharedUserSetting sus = (SharedUserSetting) obj;
            if (shouldFilterApplicationIncludingUninstalled(sus, callingUid, callingUserId)) {
                return null;
            }
            return sus.name + ":" + sus.mAppId;
        } else if (obj instanceof PackageSetting) {
            final PackageSetting ps = (PackageSetting) obj;
            if (shouldFilterApplicationIncludingUninstalled(ps, callingUid, callingUserId)) {
                return null;
            }
            return ps.getPackageName();
        }
        return null;
    }

    @Nullable
    @Override
    public String[] getNamesForUids(int[] uids) {
        if (uids == null || uids.length == 0) {
            return null;
        }
        final int callingUid = Binder.getCallingUid();
        if (getInstantAppPackageName(callingUid) != null) {
            return null;
        }
        final int callingUserId = UserHandle.getUserId(callingUid);
        final String[] names = new String[uids.length];
        for (int i = uids.length - 1; i >= 0; i--) {
            int uid = uids[i];
            if (Process.isSdkSandboxUid(uid)) {
                uid = getBaseSdkSandboxUid();
            }
            if (isKnownIsolatedComputeApp(uid)) {
                uid = getIsolatedOwner(uid);
            }
            final int appId = UserHandle.getAppId(uid);
            final Object obj = mSettings.getSettingBase(appId);
            if (obj instanceof SharedUserSetting) {
                final SharedUserSetting sus = (SharedUserSetting) obj;
                if (shouldFilterApplicationIncludingUninstalled(sus, callingUid, callingUserId)) {
                    names[i] = null;
                } else {
                    names[i] = "shared:" + sus.name;
                }
            } else if (obj instanceof PackageSetting) {
                final PackageSetting ps = (PackageSetting) obj;
                if (shouldFilterApplicationIncludingUninstalled(ps, callingUid, callingUserId)) {
                    names[i] = null;
                } else {
                    names[i] = ps.getPackageName();
                }
            } else {
                names[i] = null;
            }
        }
        return names;
    }

    @Override
    public int getUidForSharedUser(@NonNull String sharedUserName) {
        if (sharedUserName == null) {
            return INVALID_UID;
        }
        final int callingUid = Binder.getCallingUid();
        if (getInstantAppPackageName(callingUid) != null) {
            return INVALID_UID;
        }
        final SharedUserSetting suid = mSettings.getSharedUserFromId(sharedUserName);
        if (suid != null && !shouldFilterApplicationIncludingUninstalled(suid, callingUid,
                UserHandle.getUserId(callingUid))) {
            return suid.mAppId;
        }
        return INVALID_UID;
    }

    @Override
    public int getFlagsForUid(int uid) {
        final int callingUid = Binder.getCallingUid();
        if (getInstantAppPackageName(callingUid) != null) {
            return 0;
        }
        if (Process.isSdkSandboxUid(uid)) {
            uid = getBaseSdkSandboxUid();
        }
        final int callingUserId = UserHandle.getUserId(callingUid);
        final int appId = UserHandle.getAppId(uid);
        final Object obj = mSettings.getSettingBase(appId);
        if (obj instanceof SharedUserSetting) {
            final SharedUserSetting sus = (SharedUserSetting) obj;
            if (shouldFilterApplicationIncludingUninstalled(sus, callingUid, callingUserId)) {
                return 0;
            }
            return sus.getFlags();
        } else if (obj instanceof PackageSetting) {
            final PackageSetting ps = (PackageSetting) obj;
            if (shouldFilterApplicationIncludingUninstalled(ps, callingUid, callingUserId)) {
                return 0;
            }
            return ps.getFlags();
        }
        return 0;
    }

    @Override
    public int getPrivateFlagsForUid(int uid) {
        final int callingUid = Binder.getCallingUid();
        if (getInstantAppPackageName(callingUid) != null) {
            return 0;
        }
        if (Process.isSdkSandboxUid(uid)) {
            uid = getBaseSdkSandboxUid();
        }
        final int callingUserId = UserHandle.getUserId(callingUid);
        final int appId = UserHandle.getAppId(uid);
        final Object obj = mSettings.getSettingBase(appId);
        if (obj instanceof SharedUserSetting) {
            final SharedUserSetting sus = (SharedUserSetting) obj;
            if (shouldFilterApplicationIncludingUninstalled(sus, callingUid, callingUserId)) {
                return 0;
            }
            return sus.getPrivateFlags();
        } else if (obj instanceof PackageSetting) {
            final PackageSetting ps = (PackageSetting) obj;
            if (shouldFilterApplicationIncludingUninstalled(ps, callingUid, callingUserId)) {
                return 0;
            }
            return ps.getPrivateFlags();
        }
        return 0;
    }

    @Override
    public boolean isUidPrivileged(int uid) {
        if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
            return false;
        }
        if (Process.isSdkSandboxUid(uid)) {
            uid = getBaseSdkSandboxUid();
        }
        final int appId = UserHandle.getAppId(uid);
        final Object obj = mSettings.getSettingBase(appId);
        if (obj instanceof SharedUserSetting) {
            final SharedUserSetting sus = (SharedUserSetting) obj;
            final ArraySet<PackageStateInternal> packageStates =
                    (ArraySet<PackageStateInternal>) sus.getPackageStates();
            final int numPackages = packageStates.size();
            for (int index = 0; index < numPackages; index++) {
                final PackageStateInternal ps = packageStates.valueAt(index);
                if (ps.isPrivileged()) {
                    return true;
                }
            }
        } else if (obj instanceof PackageSetting) {
            final PackageSetting ps = (PackageSetting) obj;
            return ps.isPrivileged();
        }
        return false;
    }

    // NOTE: Can't remove due to unsupported app usage
    @NonNull
    @Override
    public String[] getAppOpPermissionPackages(@NonNull String permissionName, int userId) {
        final int callingUid = Binder.getCallingUid();
        enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
                false /* checkShell */, "getAppOpPermissionPackages");
        if (permissionName == null || getInstantAppPackageName(callingUid) != null
                || !mUserManager.exists(userId)) {
            return EmptyArray.STRING;
        }

        final ArraySet<String> packageNames = new ArraySet(
                mPermissionManager.getAppOpPermissionPackages(permissionName));
        for (int i = packageNames.size() - 1; i >= 0; i--) {
            final String packageName = packageNames.valueAt(i);
            if (!shouldFilterApplicationIncludingUninstalled(
                    mSettings.getPackage(packageName), callingUid, userId)) {
                continue;
            }
            packageNames.removeAt(i);
        }
        return packageNames.toArray(new String[packageNames.size()]);
    }

    @NonNull
    @Override
    public ParceledListSlice<PackageInfo> getPackagesHoldingPermissions(
            @NonNull String[] permissions, @PackageManager.PackageInfoFlagsBits long flags,
            @UserIdInt int userId) {
        if (!mUserManager.exists(userId)) return ParceledListSlice.emptyList();
        flags = updateFlagsForPackage(flags, userId);
        enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */,
                false /* checkShell */, "get packages holding permissions");
        final boolean listUninstalled =
                (flags & (MATCH_KNOWN_PACKAGES | MATCH_ARCHIVED_PACKAGES)) != 0;

        ArrayList<PackageInfo> list = new ArrayList<>();
        boolean[] tmpBools = new boolean[permissions.length];
        for (PackageStateInternal ps : getPackageStates().values()) {
            if (ps.getPkg() == null && !listUninstalled) {
                continue;
            }
            addPackageHoldingPermissions(list, ps, permissions, tmpBools, flags, userId);
        }

        return new ParceledListSlice<>(list);
    }

    private void addPackageHoldingPermissions(ArrayList<PackageInfo> list, PackageStateInternal ps,
            String[] permissions, boolean[] tmp, @PackageManager.PackageInfoFlagsBits long flags,
            int userId) {
        int numMatch = 0;
        for (int i=0; i<permissions.length; i++) {
            final String permission = permissions[i];
            if (mPermissionManager.checkPermission(ps.getPackageName(), permission,
                    VirtualDeviceManager.PERSISTENT_DEVICE_ID_DEFAULT, userId)
                    == PERMISSION_GRANTED) {
                tmp[i] = true;
                numMatch++;
            } else {
                tmp[i] = false;
            }
        }
        if (numMatch == 0) {
            return;
        }
        final PackageInfo pi = generatePackageInfo(ps, flags, userId);

        // The above might return null in cases of uninstalled apps or install-state
        // skew across users/profiles.
        if (pi != null) {
            if ((flags & PackageManager.GET_PERMISSIONS) == 0) {
                if (numMatch == permissions.length) {
                    pi.requestedPermissions = permissions;
                } else {
                    pi.requestedPermissions = new String[numMatch];
                    numMatch = 0;
                    for (int i=0; i<permissions.length; i++) {
                        if (tmp[i]) {
                            pi.requestedPermissions[numMatch] = permissions[i];
                            numMatch++;
                        }
                    }
                }
            }
            list.add(pi);
        }
    }

    @NonNull
    @Override
    public List<ApplicationInfo> getInstalledApplications(
            @PackageManager.ApplicationInfoFlagsBits long flags, @UserIdInt int userId,
            int callingUid, boolean forceAllowCrossUser) {
        if (getInstantAppPackageName(callingUid) != null) {
            return Collections.emptyList();
        }
        if (!mUserManager.exists(userId)) return Collections.emptyList();
        flags = updateFlagsForApplication(flags, userId);
        final boolean listUninstalled = (flags & MATCH_KNOWN_PACKAGES) != 0;
        final boolean listApex = (flags & MATCH_APEX) != 0;
        final boolean listArchivedOnly = !listUninstalled && (flags & MATCH_ARCHIVED_PACKAGES) != 0;

        if (!forceAllowCrossUser) {
            enforceCrossUserPermission(
                    Binder.getCallingUid(),
                    userId,
                    false /* requireFullPermission */,
                    false /* checkShell */,
                    "get installed application info");
        }

        ArrayList<ApplicationInfo> list;
        final ArrayMap<String, ? extends PackageStateInternal> packageStates =
                getPackageStates();
        if (listUninstalled || listArchivedOnly) {
            list = new ArrayList<>(packageStates.size());
            for (PackageStateInternal ps : packageStates.values()) {
                ApplicationInfo ai;
                long effectiveFlags = flags;
                if (ps.isSystem()) {
                    effectiveFlags |= PackageManager.MATCH_ANY_USER;
                }
                if (ps.getPkg() != null) {
                    if (!listApex && ps.getPkg().isApex()) {
                        continue;
                    }
                    PackageUserStateInternal userState = ps.getUserStateOrDefault(userId);
                    if (listArchivedOnly && !userState.isInstalled()
                            && userState.getArchiveState() == null) {
                        continue;
                    }
                    if (filterSharedLibPackage(ps, callingUid, userId, flags)) {
                        continue;
                    }
                    if (shouldFilterApplication(ps, callingUid, userId)) {
                        continue;
                    }
                    ai = PackageInfoUtils.generateApplicationInfo(ps.getPkg(), effectiveFlags,
                            ps.getUserStateOrDefault(userId), userId, ps);
                    if (ai != null) {
                        ai.packageName = resolveExternalPackageName(ps.getPkg());
                    }
                } else {
                    // Shared lib filtering done in generateApplicationInfoFromSettingsLPw
                    // and already converts to externally visible package name
                    ai = generateApplicationInfoFromSettings(ps.getPackageName(),
                            effectiveFlags, callingUid, userId);
                }
                if (ai != null) {
                    list.add(ai);
                }
            }
        } else {
            list = new ArrayList<>(mPackages.size());
            for (PackageStateInternal packageState : packageStates.values()) {
                final AndroidPackage pkg = packageState.getPkg();
                if (pkg == null) {
                    continue;
                }
                if (!listApex && pkg.isApex()) {
                    continue;
                }
                if (filterSharedLibPackage(packageState, Binder.getCallingUid(), userId, flags)) {
                    continue;
                }
                if (shouldFilterApplication(packageState, callingUid, userId)) {
                    continue;
                }
                ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(pkg, flags,
                        packageState.getUserStateOrDefault(userId), userId, packageState);
                if (ai != null) {
                    ai.packageName = resolveExternalPackageName(pkg);
                    list.add(ai);
                }
            }
        }

        return list;
    }

    @Nullable
    @Override
    public ProviderInfo resolveContentProviderForUid(@NonNull String name,
            @PackageManager.ResolveInfoFlagsBits long flags, @UserIdInt int userId,
            int filterCallingUid) {
        mContext.enforceCallingOrSelfPermission(Manifest.permission.RESOLVE_COMPONENT_FOR_UID,
                "resolveContentProviderForUid");

        int callingUid = Binder.getCallingUid();
        int filterUserId = UserHandle.getUserId(filterCallingUid);
        enforceCrossUserPermission(callingUid, filterUserId, false, false,
                "resolveContentProviderForUid");

        // Real callingUid should be able to see filterCallingUid
        if (filterAppAccess(filterCallingUid, callingUid)) {
            return null;
        }

        ProviderInfo pInfo = resolveContentProvider(name, flags, userId, filterCallingUid);
        if (pInfo == null) {
            return null;
        }
        // Real callingUid should be able to see the ContentProvider accessible to filterCallingUid
        ProviderInfo pInfo2 = resolveContentProvider(name, flags, userId, callingUid);
        if (pInfo2 != null
                && Objects.equals(pInfo.name, pInfo2.name)
                && Objects.equals(pInfo.authority, pInfo2.authority)) {
            return pInfo;
        }
        return null;
    }

    @Nullable
    @Override
    public ProviderInfo resolveContentProvider(@NonNull String name,
            @PackageManager.ResolveInfoFlagsBits long flags, @UserIdInt int userId,
            int callingUid) {
        if (!mUserManager.exists(userId)) return null;
        flags = updateFlagsForComponent(flags, userId);

        // Callers of this API may not always separate the userID and authority. Let's parse it
        // before resolving
        String authorityWithoutUserId = ContentProvider.getAuthorityWithoutUserId(name);
        userId = ContentProvider.getUserIdFromAuthority(name, userId);

        final ProviderInfo providerInfo = mComponentResolver.queryProvider(this,
                authorityWithoutUserId, flags, userId);
        boolean checkedGrants = false;
        if (providerInfo != null) {
            // Looking for cross-user grants before enforcing the typical cross-users permissions
            if (userId != UserHandle.getUserId(callingUid)) {
                final UriGrantsManagerInternal ugmInternal =
                        mInjector.getLocalService(UriGrantsManagerInternal.class);
                checkedGrants =
                        ugmInternal.checkAuthorityGrants(callingUid, providerInfo, userId, true);
            }
        }
        if (!checkedGrants) {
            boolean enforceCrossUser = true;

            if (isAuthorityRedirectedForCloneProfile(authorityWithoutUserId)) {
                final UserManagerInternal umInternal = mInjector.getUserManagerInternal();

                UserInfo userInfo = umInternal.getUserInfo(UserHandle.getUserId(callingUid));
                if (userInfo != null && userInfo.isCloneProfile()
                        && userInfo.profileGroupId == userId) {
                    enforceCrossUser = false;
                }
            }

            if (enforceCrossUser) {
                enforceCrossUserPermission(callingUid, userId, false, false,
                        "resolveContentProvider");
            }
        }

        if (providerInfo == null) {
            return null;
        }
        final PackageStateInternal packageState = getPackageStateInternal(
                providerInfo.packageName);
        if (com.android.server.pm.ext.PackageHooks.shouldHideContentProvider(packageState, providerInfo)) {
            return null;
        }
        if (!PackageStateUtils.isEnabledAndMatches(packageState, providerInfo, flags, userId)) {
            return null;
        }
        final ComponentName component =
                new ComponentName(providerInfo.packageName, providerInfo.name);
        if (shouldFilterApplication(packageState, callingUid, component, TYPE_PROVIDER, userId)) {
            return null;
        }
        return providerInfo;
    }

    @Nullable
    @Override
    public ProviderInfo getGrantImplicitAccessProviderInfo(int recipientUid,
            @NonNull String visibleAuthority) {
        final int callingUid = Binder.getCallingUid();
        final int recipientUserId = UserHandle.getUserId(recipientUid);
        // This API is exposed temporarily to only the contacts provider. (b/158688602)
        ProviderInfo contactsProvider = resolveContentProvider(
                ContactsContract.AUTHORITY, 0, UserHandle.getUserId(callingUid), callingUid);
        if (contactsProvider == null || contactsProvider.applicationInfo == null
                || !UserHandle.isSameApp(contactsProvider.applicationInfo.uid, callingUid)) {
            throw new SecurityException(
                    callingUid + " is not allow to call grantImplicitAccess");
        }
        final long token = Binder.clearCallingIdentity();
        try {
            return resolveContentProvider(visibleAuthority, 0 /*flags*/, recipientUserId,
                    callingUid);
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    @Deprecated
    public void querySyncProviders(boolean safeMode, @NonNull List<String> outNames,
            @NonNull List<ProviderInfo> outInfo) {
        if (getInstantAppPackageName(Binder.getCallingUid()) != null) {
            return;
        }
        final List<String> names = new ArrayList<>();
        final List<ProviderInfo> infos = new ArrayList<>();
        final int callingUserId = UserHandle.getCallingUserId();
        mComponentResolver.querySyncProviders(this, names, infos, safeMode, callingUserId);
        for (int i = infos.size() - 1; i >= 0; i--) {
            final ProviderInfo providerInfo = infos.get(i);
            final PackageStateInternal ps = mSettings.getPackage(providerInfo.packageName);
            final ComponentName component =
                    new ComponentName(providerInfo.packageName, providerInfo.name);
            if (!shouldFilterApplication(ps, Binder.getCallingUid(), component,
                    TYPE_PROVIDER, callingUserId)) {
                continue;
            }
            infos.remove(i);
            names.remove(i);
        }
        if (!names.isEmpty()) {
            outNames.addAll(names);
        }
        if (!infos.isEmpty()) {
            outInfo.addAll(infos);
        }
    }

    @NonNull
    @Override
    public ParceledListSlice<ProviderInfo> queryContentProviders(@Nullable String processName,
            int uid, @PackageManager.ComponentInfoFlagsBits long flags,
            @Nullable String metaDataKey) {
        final int callingUid = Binder.getCallingUid();
        final int userId = processName != null ? UserHandle.getUserId(uid)
                : UserHandle.getCallingUserId();
        enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
                false /* checkShell */, "queryContentProviders");
        if (!mUserManager.exists(userId)) return ParceledListSlice.emptyList();
        flags = updateFlagsForComponent(flags, userId);
        ArrayList<ProviderInfo> finalList = null;
        final List<ProviderInfo> matchList = mComponentResolver.queryProviders(this, processName,
                metaDataKey, uid, flags, userId);
        final int listSize = (matchList == null ? 0 : matchList.size());
        for (int i = 0; i < listSize; i++) {
            final ProviderInfo providerInfo = matchList.get(i);
            if (!PackageStateUtils.isEnabledAndMatches(
                    mSettings.getPackage(providerInfo.packageName), providerInfo,
                    flags, userId)) {
                continue;
            }
            final PackageStateInternal ps = mSettings.getPackage(providerInfo.packageName);
            if (com.android.server.pm.ext.PackageHooks.shouldHideContentProvider(ps, providerInfo)) {
                continue;
            }
            final ComponentName component =
                    new ComponentName(providerInfo.packageName, providerInfo.name);
            if (shouldFilterApplication(
                    ps, callingUid, component, TYPE_PROVIDER, userId)) {
                continue;
            }
            if (finalList == null) {
                finalList = new ArrayList<>(listSize - i);
            }
            finalList.add(providerInfo);
        }

        if (finalList != null) {
            finalList.sort(sProviderInitOrderSorter);
            return new ParceledListSlice<>(finalList);
        }

        return ParceledListSlice.emptyList();
    }

    @Nullable
    @Override
    public InstrumentationInfo getInstrumentationInfoAsUser(@NonNull ComponentName component,
            int flags, int userId) {
        final int callingUid = Binder.getCallingUid();
        enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
                false /* checkShell */, "getInstrumentationInfoAsUser");
        if (!mUserManager.exists(userId)) return null;
        String packageName = component.getPackageName();
        final PackageStateInternal ps = mSettings.getPackage(packageName);
        AndroidPackage pkg = mPackages.get(packageName);
        if (ps == null || pkg == null) return null;
        if (shouldFilterApplication(
                ps, callingUid, component, TYPE_UNKNOWN, userId)) {
            return null;
        }
        final ParsedInstrumentation i = mInstrumentation.get(component);
        final PackageUserStateInternal state = ps.getUserStateOrDefault(userId);
        return PackageInfoUtils.generateInstrumentationInfo(i, pkg, flags, state, userId, ps);
    }

    @NonNull
    @Override
    public ParceledListSlice<InstrumentationInfo> queryInstrumentationAsUser(
            @NonNull String targetPackage, int flags, int userId) {
        final int callingUid = Binder.getCallingUid();
        enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
                false /* checkShell */, "queryInstrumentationAsUser");
        if (!mUserManager.exists(userId)) return ParceledListSlice.emptyList();
        ArrayList<InstrumentationInfo> finalList = new ArrayList<>();

        final int numInstrumentations = mInstrumentation.size();
        for (int index = 0; index < numInstrumentations; index++) {
            final ParsedInstrumentation p = mInstrumentation.valueAt(index);
            if (targetPackage == null
                    || targetPackage.equals(p.getTargetPackage())) {
                String packageName = p.getPackageName();
                AndroidPackage pkg = mPackages.get(packageName);
                PackageStateInternal pkgSetting = getPackageStateInternal(packageName);
                if (pkg == null || pkgSetting == null
                        || shouldFilterApplication(pkgSetting, callingUid, userId)) {
                    continue;
                }
                final PackageUserStateInternal state = pkgSetting.getUserStateOrDefault(userId);
                InstrumentationInfo ii = PackageInfoUtils.generateInstrumentationInfo(p,
                        pkg, flags, state, userId, pkgSetting);
                if (ii != null) {
                    finalList.add(ii);
                }
            }
        }

        return new ParceledListSlice<>(finalList);
    }

    /**
     * Returns true if application is not found or there was an error. Otherwise it returns
     * the hidden state of the package for the given user.
     */
    @Override
    public boolean getApplicationHiddenSettingAsUser(@NonNull String packageName,
            @UserIdInt int userId) {
        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USERS, null);
        final int callingUid = Binder.getCallingUid();
        enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
                false /* checkShell */, "getApplicationHidden for user " + userId);
        final long callingId = Binder.clearCallingIdentity();
        try {
            PackageStateInternal ps = mSettings.getPackage(packageName);
            if (ps == null) {
                return true;
            }
            if (shouldFilterApplicationIncludingUninstalled(ps, callingUid, userId)) {
                return true;
            }
            return ps.getUserStateOrDefault(userId).isHidden();
        } finally {
            Binder.restoreCallingIdentity(callingId);
        }
    }

    private PackageUserStateInternal getUserStateOrDefaultForUser(@NonNull String packageName,
            int userId) throws PackageManager.NameNotFoundException {
        final int callingUid = Binder.getCallingUid();
        enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
                false /* checkShell */, "when asking about packages for user " + userId);
        final PackageStateInternal ps = mSettings.getPackage(packageName);
        if (ps == null || shouldFilterApplicationIncludingUninstalled(ps, callingUid, userId)) {
            throw new PackageManager.NameNotFoundException(packageName);
        }
        return ps.getUserStateOrDefault(userId);
    }

    @Override
    public boolean isPackageSuspendedForUser(@NonNull String packageName, int userId)
            throws PackageManager.NameNotFoundException {
        return getUserStateOrDefaultForUser(packageName, userId).isSuspended();
    }

    @Override
    public boolean isPackageQuarantinedForUser(@NonNull String packageName, @UserIdInt int userId)
            throws PackageManager.NameNotFoundException {
        return getUserStateOrDefaultForUser(packageName, userId).isQuarantined();
    }

    @Override
    public boolean isPackageStoppedForUser(@NonNull String packageName, @UserIdInt int userId)
            throws PackageManager.NameNotFoundException {
        return getUserStateOrDefaultForUser(packageName, userId).isStopped();
    }

    @Override
    public boolean isSuspendingAnyPackages(@NonNull String suspendingPackage,
            @UserIdInt int suspendingUserId, int targetUserId) {
        final UserPackage suspender = UserPackage.of(suspendingUserId, suspendingPackage);
        for (final PackageStateInternal packageState : getPackageStates().values()) {
            final PackageUserStateInternal state =
                    packageState.getUserStateOrDefault(targetUserId);
            if (state.getSuspendParams() != null
                    && state.getSuspendParams().containsKey(suspender)) {
                return true;
            }
        }
        return false;
    }

    @NonNull
    @Override
    public ParceledListSlice<IntentFilter> getAllIntentFilters(@NonNull String packageName) {
        if (TextUtils.isEmpty(packageName)) {
            return ParceledListSlice.emptyList();
        }
        final int callingUid = Binder.getCallingUid();
        final int callingUserId = UserHandle.getUserId(callingUid);
        final PackageStateInternal ps = getPackageStateInternal(packageName);
        final AndroidPackage pkg = ps == null ? null : ps.getPkg();
        if (pkg == null || ArrayUtils.isEmpty(pkg.getActivities())) {
            return ParceledListSlice.emptyList();
        }
        if (shouldFilterApplicationIncludingUninstalled(ps, callingUid, callingUserId)) {
            return ParceledListSlice.emptyList();
        }
        final int count = ArrayUtils.size(pkg.getActivities());
        ArrayList<IntentFilter> result = new ArrayList<>();
        for (int n=0; n<count; n++) {
            ParsedActivity activity = pkg.getActivities().get(n);
            List<ParsedIntentInfo> intentInfos = activity.getIntents();
            for (int index = 0; index < intentInfos.size(); index++) {
                result.add(new IntentFilter(intentInfos.get(index).getIntentFilter()));
            }
        }
        return new ParceledListSlice<>(result);
    }

    @Override
    public boolean getBlockUninstallForUser(@NonNull String packageName, @UserIdInt int userId) {
        final PackageStateInternal ps = mSettings.getPackage(packageName);
        final int callingUid = Binder.getCallingUid();
        if (ps == null || shouldFilterApplicationIncludingUninstalled(ps, callingUid, userId)) {
            return false;
        }
        return mSettings.getBlockUninstall(userId, packageName);
    }

    @Nullable
    @Override
    public String getInstallerPackageName(@NonNull String packageName, @UserIdInt int userId) {
        final int callingUid = Binder.getCallingUid();
        final InstallSource installSource = getInstallSource(packageName, callingUid, userId);
        if (installSource == null) {
            throw new IllegalArgumentException("Unknown package: " + packageName);
        }
        String installerPackageName = installSource.mInstallerPackageName;
        if (installerPackageName != null) {
            final PackageStateInternal ps = mSettings.getPackage(installerPackageName);
            if (ps == null || shouldFilterApplicationIncludingUninstalledNotArchived(ps, callingUid,
                    UserHandle.getUserId(callingUid))) {
                installerPackageName = null;
            }
        }
        return installerPackageName;
    }

    @Nullable
    private InstallSource getInstallSource(@NonNull String packageName, int callingUid,
            int userId) {
        final PackageStateInternal ps = mSettings.getPackage(packageName);

        // Installer info for Apex is not stored in PackageManager
        if (isApexPackage(packageName)) {
            return InstallSource.EMPTY;
        }

        if (ps == null || shouldFilterApplicationIncludingUninstalledNotArchived(ps, callingUid,
                userId)) {
            return null;
        }

        return ps.getInstallSource();
    }

    @Override
    @Nullable
    public InstallSourceInfo getInstallSourceInfo(@NonNull String packageName,
            @UserIdInt int userId) {
        final int callingUid = Binder.getCallingUid();
        enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
                false /* checkShell */, "getInstallSourceInfo");

        String installerPackageName;
        String initiatingPackageName;
        String originatingPackageName;
        String updateOwnerPackageName;

        final InstallSource installSource = getInstallSource(packageName, callingUid, userId);
        if (installSource == null) {
            return null;
        }

        installerPackageName = installSource.mInstallerPackageName;
        if (installerPackageName != null) {
            final PackageStateInternal ps = mSettings.getPackage(installerPackageName);
            if (ps == null
                    || shouldFilterApplicationIncludingUninstalled(ps, callingUid, userId)) {
                installerPackageName = null;
            }
        }

        updateOwnerPackageName = installSource.mUpdateOwnerPackageName;
        if (updateOwnerPackageName != null) {
            final PackageStateInternal ps = mSettings.getPackage(updateOwnerPackageName);
            final boolean isCallerSystemOrUpdateOwner = callingUid == Process.SYSTEM_UID
                            || isCallerSameApp(updateOwnerPackageName, callingUid);
            // Except for package visibility filtering, we also hide update owner if the installer
            // is in the managed user or profile. As we don't enforce the update ownership for the
            // managed user and profile, knowing there's an update owner is meaningless in that
            // user unless the installer is the owner.
            if (ps == null
                    || shouldFilterApplicationIncludingUninstalled(ps, callingUid, userId)
                    || (!isCallerSystemOrUpdateOwner && isCallerFromManagedUserOrProfile(userId))) {
                updateOwnerPackageName = null;
            }
        }

        if (installSource.mIsInitiatingPackageUninstalled) {
            // We can't check visibility in the usual way, since the initiating package is no
            // longer present. So we apply simpler rules to whether to expose the info:
            // 1. Instant apps can't see it.
            // 2. Otherwise only the installed app itself can see it.
            final boolean isInstantApp = getInstantAppPackageName(callingUid) != null;
            if (!isInstantApp && isCallerSameApp(packageName, callingUid)) {
                initiatingPackageName = installSource.mInitiatingPackageName;
            } else {
                initiatingPackageName = null;
            }
        } else {
            if (Objects.equals(installSource.mInitiatingPackageName,
                    installSource.mInstallerPackageName)) {
                // The installer and initiator will often be the same, and when they are
                // we can skip doing the same check again.
                initiatingPackageName = installerPackageName;
            } else {
                initiatingPackageName = installSource.mInitiatingPackageName;
                final PackageStateInternal ps = mSettings.getPackage(initiatingPackageName);
                if (ps == null
                        || shouldFilterApplicationIncludingUninstalled(ps, callingUid, userId)) {
                    initiatingPackageName = null;
                }
            }
        }

        originatingPackageName = installSource.mOriginatingPackageName;
        if (originatingPackageName != null) {
            final PackageStateInternal ps = mSettings.getPackage(originatingPackageName);
            if (ps == null
                    || shouldFilterApplicationIncludingUninstalled(ps, callingUid, userId)) {
                originatingPackageName = null;
            }
        }

        // Remaining work can safely be done outside the lock. (Note that installSource is
        // immutable so it's ok to carry on reading from it.)

        if (originatingPackageName != null && mContext.checkCallingOrSelfPermission(
                Manifest.permission.INSTALL_PACKAGES) != PackageManager.PERMISSION_GRANTED) {
            originatingPackageName = null;
        }

        // If you can see the initiatingPackageName, and we have valid signing info for it,
        // then we let you see that too.
        final SigningInfo initiatingPackageSigningInfo;
        final PackageSignatures signatures = installSource.mInitiatingPackageSignatures;
        if (initiatingPackageName != null && signatures != null
                && signatures.mSigningDetails != SigningDetails.UNKNOWN) {
            initiatingPackageSigningInfo = new SigningInfo(signatures.mSigningDetails);
        } else {
            initiatingPackageSigningInfo = null;
        }

        return new InstallSourceInfo(initiatingPackageName, initiatingPackageSigningInfo,
                originatingPackageName, installerPackageName, updateOwnerPackageName,
                installSource.mPackageSource);
    }

    @PackageManager.EnabledState
    @Override
    public int getApplicationEnabledSetting(@NonNull String packageName, @UserIdInt int userId) {
        if (!mUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;
        int callingUid = Binder.getCallingUid();
        enforceCrossUserPermission(callingUid, userId, false /* requireFullPermission */,
                false /* checkShell */, "get enabled");
        try {
            if (shouldFilterApplicationIncludingUninstalled(
                    mSettings.getPackage(packageName), callingUid, userId)) {
                throw new PackageManager.NameNotFoundException(packageName);
            }
            return mSettings.getApplicationEnabledSetting(packageName, userId);
        } catch (PackageManager.NameNotFoundException e) {
            throw new IllegalArgumentException("Unknown package: " + packageName);
        }
    }

    @PackageManager.EnabledState
    @Override
    public int getComponentEnabledSetting(@NonNull ComponentName component, int callingUid,
            @UserIdInt int userId) {
        enforceCrossUserPermission(Binder.getCallingUid(), userId, false /*requireFullPermission*/,
                false /*checkShell*/, "getComponentEnabled");
        return getComponentEnabledSettingInternal(component, callingUid, userId);
    }

    @PackageManager.EnabledState
    @Override
    public int getComponentEnabledSettingInternal(@NonNull ComponentName component, int callingUid,
            @UserIdInt int userId) {
        if (component == null) return COMPONENT_ENABLED_STATE_DEFAULT;
        if (!mUserManager.exists(userId)) return COMPONENT_ENABLED_STATE_DISABLED;

        try {
            if (shouldFilterApplication(
                    mSettings.getPackage(component.getPackageName()), callingUid,
                    component, TYPE_UNKNOWN, userId, true /* filterUninstall */)) {
                throw new PackageManager.NameNotFoundException(component.getPackageName());
            }
            return mSettings.getComponentEnabledSetting(component, userId);
        } catch (PackageManager.NameNotFoundException e) {
            throw new IllegalArgumentException("Unknown component: " + component);
        }
    }

    @Override
    public boolean isComponentEffectivelyEnabled(@NonNull ComponentInfo componentInfo,
            @NonNull UserHandle userHandle) {
        try {
            String packageName = componentInfo.packageName;
            int userId = userHandle.getIdentifier();
            int appEnabledSetting =
                    mSettings.getApplicationEnabledSetting(packageName, userId);
            if (appEnabledSetting == COMPONENT_ENABLED_STATE_DEFAULT) {
                if (!componentInfo.applicationInfo.enabled) {
                    return false;
                }
            } else if (appEnabledSetting != COMPONENT_ENABLED_STATE_ENABLED) {
                return false;
            }

            int componentEnabledSetting = mSettings.getComponentEnabledSetting(
                    componentInfo.getComponentName(), userId);
            if (componentEnabledSetting == COMPONENT_ENABLED_STATE_DEFAULT) {
                return componentInfo.isEnabled();
            } else return componentEnabledSetting == COMPONENT_ENABLED_STATE_ENABLED;
        } catch (PackageManager.NameNotFoundException ignored) {
            return false;
        }
    }

    @Override
    public boolean isApplicationEffectivelyEnabled(@NonNull String packageName,
            @NonNull UserHandle userHandle) {
        try {
            int appEnabledSetting = mSettings.getApplicationEnabledSetting(packageName,
                    userHandle.getIdentifier());
            if (appEnabledSetting == COMPONENT_ENABLED_STATE_DEFAULT) {
                final AndroidPackage pkg = getPackage(packageName);
                if (pkg == null) {
                    // Should not happen because getApplicationEnabledSetting would have thrown
                    return false;
                }
                return pkg.isEnabled();
            } else {
                return appEnabledSetting == COMPONENT_ENABLED_STATE_ENABLED;
            }
        } catch (PackageManager.NameNotFoundException ignored) {
            return false;
        }
    }

    @Nullable
    @Override
    public KeySet getKeySetByAlias(@NonNull String packageName, @NonNull String alias) {
        if (packageName == null || alias == null) {
            return null;
        }
        final int callingUid = Binder.getCallingUid();
        final int callingUserId = UserHandle.getUserId(callingUid);
        final AndroidPackage pkg = mPackages.get(packageName);
        if (pkg == null || shouldFilterApplicationIncludingUninstalled(
                getPackageStateInternal(pkg.getPackageName()), callingUid, callingUserId)) {
            Slog.w(TAG, "KeySet requested for unknown package: " + packageName);
            throw new IllegalArgumentException("Unknown package: " + packageName);
        }
        final KeySetManagerService ksms = mSettings.getKeySetManagerService();
        return new KeySet(ksms.getKeySetByAliasAndPackageNameLPr(packageName, alias));
    }

    @Nullable
    @Override
    public KeySet getSigningKeySet(@NonNull String packageName) {
        if (packageName == null) {
            return null;
        }
        final int callingUid = Binder.getCallingUid();
        final int callingUserId = UserHandle.getUserId(callingUid);
        final AndroidPackage pkg = mPackages.get(packageName);
        if (pkg == null || shouldFilterApplicationIncludingUninstalled(
                getPackageStateInternal(pkg.getPackageName()), callingUid, callingUserId)) {
            Slog.w(TAG, "KeySet requested for unknown package: " + packageName
                    + ", uid:" + callingUid);
            throw new IllegalArgumentException("Unknown package: " + packageName);
        }
        if (!UserHandle.isSameApp(callingUid, pkg.getUid())
                && Process.SYSTEM_UID != callingUid) {
            throw new SecurityException("May not access signing KeySet of other apps.");
        }
        final KeySetManagerService ksms = mSettings.getKeySetManagerService();
        return new KeySet(ksms.getSigningKeySetByPackageNameLPr(packageName));
    }

    @Override
    public boolean isPackageSignedByKeySet(@NonNull String packageName, @NonNull KeySet ks) {
        final int callingUid = Binder.getCallingUid();
        if (getInstantAppPackageName(callingUid) != null) {
            return false;
        }
        if (packageName == null || ks == null) {
            return false;
        }
        final AndroidPackage pkg = mPackages.get(packageName);
        final int callingUserId = UserHandle.getUserId(callingUid);
        if (pkg == null
                || shouldFilterApplicationIncludingUninstalled(
                        getPackageStateInternal(pkg.getPackageName()), callingUid, callingUserId)) {
            Slog.w(TAG, "KeySet requested for unknown package: " + packageName);
            throw new IllegalArgumentException("Unknown package: " + packageName);
        }
        IBinder ksh = ks.getToken();
        if (ksh instanceof KeySetHandle) {
            final KeySetManagerService ksms = mSettings.getKeySetManagerService();
            return ksms.packageIsSignedByLPr(packageName, (KeySetHandle) ksh);
        }
        return false;
    }

    @Override
    public boolean isPackageSignedByKeySetExactly(@NonNull String packageName, @NonNull KeySet ks) {
        final int callingUid = Binder.getCallingUid();
        if (getInstantAppPackageName(callingUid) != null) {
            return false;
        }
        if (packageName == null || ks == null) {
            return false;
        }
        final AndroidPackage pkg = mPackages.get(packageName);
        final int callingUserId = UserHandle.getUserId(callingUid);
        if (pkg == null
                || shouldFilterApplicationIncludingUninstalled(
                        getPackageStateInternal(pkg.getPackageName()), callingUid, callingUserId)) {
            Slog.w(TAG, "KeySet requested for unknown package: " + packageName);
            throw new IllegalArgumentException("Unknown package: " + packageName);
        }
        IBinder ksh = ks.getToken();
        if (ksh instanceof KeySetHandle) {
            final KeySetManagerService ksms = mSettings.getKeySetManagerService();
            return ksms.packageIsSignedByExactlyLPr(packageName, (KeySetHandle) ksh);
        }
        return false;
    }

    @Nullable
    @Override
    public SparseArray<int[]> getVisibilityAllowLists(@NonNull String packageName,
            @UserIdInt int[] userIds) {
        final PackageStateInternal ps =
                getPackageStateInternal(packageName, Process.SYSTEM_UID);
        if (ps == null) {
            return null;
        }
        return mAppsFilter.getVisibilityAllowList(this, ps, userIds, getPackageStates());
    }

    @Nullable
    @Override
    public int[] getVisibilityAllowList(@NonNull String packageName, @UserIdInt int userId) {
        final SparseArray<int[]> visibilityAllowList = getVisibilityAllowLists(packageName,
                new int[]{userId});
        return visibilityAllowList != null ? visibilityAllowList.get(userId) : null;
    }

    @Override
    public boolean canQueryPackage(int callingUid, @Nullable String targetPackageName) {
        // Since getSettingLPr returns null for ROOT_UID, add an extra check for it here.
        if (callingUid == Process.ROOT_UID || targetPackageName == null) {
            return true;
        }
        final Object setting = mSettings.getSettingBase(UserHandle.getAppId(callingUid));
        if (setting == null) {
            return false;
        }

        final int userId = UserHandle.getUserId(callingUid);
        final int targetAppId = UserHandle.getAppId(
                getPackageUid(targetPackageName, 0 /* flags */, userId));
        // For update or already installed case, leverage the existing visibility rule.
        if (targetAppId != INVALID_UID) {
            final Object targetSetting = mSettings.getSettingBase(targetAppId);
            if (targetSetting == null) {
                return false;
            }
            if (targetSetting instanceof PackageSetting) {
                return !shouldFilterApplication(
                        (PackageSetting) targetSetting, callingUid, userId);
            } else {
                return !shouldFilterApplication(
                        (SharedUserSetting) targetSetting, callingUid, userId);
            }
        }

        // For new installing case, check if caller declares <queries> element with the
        // target package name or has proper permission.
        if (setting instanceof PackageSetting) {
            final AndroidPackage pkg = ((PackageSetting) setting).getPkg();
            return pkg != null && mAppsFilter.canQueryPackage(pkg, targetPackageName);
        } else {
            final ArraySet<PackageStateInternal> callingSharedPkgSettings =
                    (ArraySet<PackageStateInternal>)
                            ((SharedUserSetting) setting).getPackageStates();
            for (int i = callingSharedPkgSettings.size() - 1; i >= 0; i--) {
                final AndroidPackage pkg = callingSharedPkgSettings.valueAt(i).getPkg();
                if (pkg != null && mAppsFilter.canQueryPackage(pkg, targetPackageName)) {
                    return true;
                }
            }
            return false;
        }
    }

    @Override
    public int getPackageUid(@NonNull String packageName,
            @PackageManager.PackageInfoFlagsBits long flags, @UserIdInt int userId) {
        if (!mUserManager.exists(userId)) return -1;
        final int callingUid = Binder.getCallingUid();
        flags = updateFlagsForPackage(flags, userId);
        enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
                false /*checkShell*/, "getPackageUid");
        return getPackageUidInternal(packageName, flags, userId, callingUid);
    }

    @Override
    public boolean canAccessComponent(int callingUid, @NonNull ComponentName component,
            @UserIdInt int userId) {
        final PackageStateInternal packageState =
                getPackageStateInternal(component.getPackageName());
        return packageState != null && !shouldFilterApplication(packageState, callingUid,
                component, TYPE_UNKNOWN, userId, true /* filterUninstall */);
    }

    @Override
    public boolean isCallerInstallerOfRecord(@NonNull AndroidPackage pkg, int callingUid) {
        if (pkg == null) {
            return false;
        }
        final PackageStateInternal packageState = getPackageStateInternal(pkg.getPackageName());
        if (packageState == null) {
            return false;
        }

        final PackageStateInternal installerPackageState = getPackageStateInternal(
                packageState.getInstallSource().mInstallerPackageName);
        return installerPackageState != null
                && UserHandle.isSameApp(installerPackageState.getAppId(), callingUid);
    }

    @PackageManager.InstallReason
    @Override
    public int getInstallReason(@NonNull String packageName, @UserIdInt int userId) {
        final int callingUid = Binder.getCallingUid();
        enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */,
                false /* checkShell */, "get install reason");
        final PackageStateInternal ps = mSettings.getPackage(packageName);
        if (ps == null || shouldFilterApplicationIncludingUninstalled(ps, callingUid, userId)) {
            return PackageManager.INSTALL_REASON_UNKNOWN;
        }
        return ps.getUserStateOrDefault(userId).getInstallReason();
    }

    @Override
    @NonNull
    public boolean[] canPackageQuery(@NonNull String sourcePackageName,
            @NonNull String[] targetPackageNames, @UserIdInt int userId) {
        final int targetSize = targetPackageNames.length;
        final boolean[] results = new boolean[targetSize];
        if (!mUserManager.exists(userId)) {
            return results;
        }
        final int callingUid = Binder.getCallingUid();
        enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/,
                false /*checkShell*/, "can package query");

        final PackageStateInternal sourceSetting = getPackageStateInternal(sourcePackageName);
        final PackageStateInternal[] targetSettings = new PackageStateInternal[targetSize];
        // Throw exception if the caller without the visibility of source package
        boolean throwException =
                (sourceSetting == null || shouldFilterApplicationIncludingUninstalled(
                        sourceSetting, callingUid, userId));
        for (int i = 0; !throwException && i < targetSize; i++) {
            targetSettings[i] = getPackageStateInternal(targetPackageNames[i]);
            // Throw exception if the caller without the visibility of target package
            throwException =
                    (targetSettings[i] == null || shouldFilterApplicationIncludingUninstalled(
                            targetSettings[i], callingUid, userId));
        }
        if (throwException) {
            throw new ParcelableException(new PackageManager.NameNotFoundException("Package(s) "
                    + sourcePackageName + " and/or " + Arrays.toString(targetPackageNames)
                    + " not found."));
        }

        final int sourcePackageUid = UserHandle.getUid(userId, sourceSetting.getAppId());
        for (int i = 0; i < targetSize; i++) {
            results[i] = !shouldFilterApplication(targetSettings[i], sourcePackageUid, userId);
        }
        return results;
    }

    /*
     * Returns if intent can be forwarded from the sourceUserId to the targetUserId
     */
    @Override
    public boolean canForwardTo(@NonNull Intent intent, @Nullable String resolvedType,
            @UserIdInt int sourceUserId, @UserIdInt int targetUserId) {
        mContext.enforceCallingOrSelfPermission(
                android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, null);
        if (mCrossProfileIntentResolverEngine.canReachTo(this, intent, resolvedType,
                sourceUserId, targetUserId)) {
            return true;
        }
        if (intent.hasWebURI()) {
            // cross-profile app linking works only towards the parent.
            final int callingUid = Binder.getCallingUid();
            final UserInfo parent = getProfileParent(sourceUserId);
            if (parent == null) {
                return false;
            }
            long flags = updateFlagsForResolve(0, parent.id, callingUid,
                    false /*includeInstantApps*/,
                    isImplicitImageCaptureIntentAndNotSetByDpc(intent, parent.id,
                            resolvedType, 0));
            flags |= PackageManager.MATCH_DEFAULT_ONLY;
            CrossProfileDomainInfo xpDomainInfo = getCrossProfileDomainPreferredLpr(
                    intent, resolvedType, flags, sourceUserId, parent.id);
            return xpDomainInfo != null;
        }
        return false;
    }

    @NonNull
    @Override
    public List<ApplicationInfo> getPersistentApplications(boolean safeMode, int flags) {
        final ArrayList<ApplicationInfo> finalList = new ArrayList<>();

        final int numPackages = mPackages.size();
        final int userId = UserHandle.getCallingUserId();
        for (int index = 0; index < numPackages; index++) {
            final AndroidPackage p = mPackages.valueAt(index);
            var packageState = mSettings.getPackage(p.getPackageName());

            final boolean matchesUnaware = ((flags & MATCH_DIRECT_BOOT_UNAWARE) != 0)
                    && !p.isDirectBootAware();
            final boolean matchesAware = ((flags & MATCH_DIRECT_BOOT_AWARE) != 0)
                    && p.isDirectBootAware();

            if (p.isPersistent()
                    && (!safeMode || packageState.isSystem())
                    && (matchesUnaware || matchesAware)) {
                PackageStateInternal ps = mSettings.getPackage(p.getPackageName());
                if (ps != null) {
                    ApplicationInfo ai = PackageInfoUtils.generateApplicationInfo(p, flags,
                            ps.getUserStateOrDefault(userId), userId, ps);
                    if (ai != null) {
                        finalList.add(ai);
                    }
                }
            }
        }

        return finalList;
    }

    @NonNull
    @Override
    public SparseArray<String> getAppsWithSharedUserIds() {
        final SparseArray<String> sharedUserIds = new SparseArray<>();
        for (SharedUserApi sharedUser : mSettings.getSharedUsers().values()) {
            sharedUserIds.put(UserHandle.getAppId(sharedUser.getAppId()), sharedUser.getName());
        }
        return sharedUserIds;
    }

    @NonNull
    @Override
    public String[] getSharedUserPackagesForPackage(@NonNull String packageName,
            @UserIdInt int userId) {
        final PackageStateInternal packageSetting = mSettings.getPackage(packageName);
        if (packageSetting == null || mSettings.getSharedUserFromPackageName(packageName) == null) {
            return EmptyArray.STRING;
        }

        ArraySet<? extends PackageStateInternal> packages =
                mSettings.getSharedUserFromPackageName(packageName).getPackageStates();
        final int numPackages = packages.size();
        String[] res = new String[numPackages];
        int i = 0;
        for (int index = 0; index < numPackages; index++) {
            final PackageStateInternal ps = packages.valueAt(index);
            if (ps.getUserStateOrDefault(userId).isInstalled()) {
                res[i++] = ps.getPackageName();
            }
        }
        res = ArrayUtils.trimToSize(res, i);
        return res != null ? res : EmptyArray.STRING;
    }

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

        enforceCrossUserPermission(callingUid, userId, true /*requireFullPermission*/,
                true /*checkShell*/, "getHarmfulAppInfo");

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

        final PackageStateInternal packageState = getPackageStateInternal(packageName);
        if (packageState == null) {
            throw new IllegalArgumentException("Unknown package: " + packageName);
        }
        return packageState.getUserStateOrDefault(userId).getHarmfulAppWarning();
    }

    /**
     * Only keep package names that refer to {@link PackageState#isSystem system} packages.
     *
     * @param pkgNames The packages to filter
     *
     * @return The filtered packages
     */
    @NonNull
    @Override
    public String[] filterOnlySystemPackages(@Nullable String... pkgNames) {
        if (pkgNames == null) {
            return ArrayUtils.emptyArray(String.class);
        }

        ArrayList<String> systemPackageNames = new ArrayList<>(pkgNames.length);

        for (String pkgName: pkgNames) {
            if (pkgName == null) {
                continue;
            }

            var packageState = getPackageStateInternal(pkgName);
            if (packageState == null || packageState.getAndroidPackage() == null) {
                Log.w(TAG, "Could not find package " + pkgName);
                continue;
            }

            if (!packageState.isSystem()) {
                Log.w(TAG, pkgName + " is not system");
                continue;
            }

            systemPackageNames.add(pkgName);
        }

        return systemPackageNames.toArray(new String[]{});
    }

    @NonNull
    @Override
    public List<AndroidPackage> getPackagesForAppId(int appId) {
        final SettingBase settingBase = mSettings.getSettingBase(appId);
        if (settingBase instanceof SharedUserSetting) {
            final SharedUserSetting sus = (SharedUserSetting) settingBase;
            return sus.getPackages();
        } else if (settingBase instanceof PackageSetting) {
            final PackageSetting ps = (PackageSetting) settingBase;
            final AndroidPackage pkg = ps.getPkg();
            if (pkg != null) {
                return Collections.singletonList(pkg);
            } else {
                return Collections.emptyList();
            }
        } else {
            return Collections.emptyList();
        }
    }

    @Override
    public int getUidTargetSdkVersion(int uid) {
        if (Process.isSdkSandboxUid(uid)) {
            uid = getBaseSdkSandboxUid();
        }
        final int appId = UserHandle.getAppId(uid);
        final SettingBase settingBase = mSettings.getSettingBase(appId);
        if (settingBase instanceof SharedUserSetting) {
            final SharedUserSetting sus = (SharedUserSetting) settingBase;
            final ArraySet<PackageStateInternal> packageStates =
                    (ArraySet<PackageStateInternal>) sus.getPackageStates();
            int vers = Build.VERSION_CODES.CUR_DEVELOPMENT;
            final int numPackages = packageStates.size();
            for (int index = 0; index < numPackages; index++) {
                final PackageStateInternal ps = packageStates.valueAt(index);
                if (ps.getPkg() != null) {
                    int v = ps.getPkg().getTargetSdkVersion();
                    if (v < vers) vers = v;
                }
            }
            return vers;
        } else if (settingBase instanceof PackageSetting) {
            final PackageSetting ps = (PackageSetting) settingBase;
            if (ps.getPkg() != null) {
                return ps.getPkg().getTargetSdkVersion();
            }
        }
        return Build.VERSION_CODES.CUR_DEVELOPMENT;
    }

    @Nullable
    @Override
    public ArrayMap<String, ProcessInfo> getProcessesForUid(int uid) {
        if (Process.isSdkSandboxUid(uid)) {
            uid = getBaseSdkSandboxUid();
        }
        final int appId = UserHandle.getAppId(uid);
        final SettingBase settingBase = mSettings.getSettingBase(appId);
        if (settingBase instanceof SharedUserSetting) {
            final SharedUserSetting sus = (SharedUserSetting) settingBase;
            return PackageInfoUtils.generateProcessInfo(sus.processes, 0);
        } else if (settingBase instanceof PackageSetting) {
            final PackageSetting ps = (PackageSetting) settingBase;
            final AndroidPackage pkg = ps.getPkg();
            return pkg == null ? null : PackageInfoUtils.generateProcessInfo(pkg.getProcesses(), 0);
        }
        return null;
    }

    @Override
    public boolean getBlockUninstall(@UserIdInt int userId, @NonNull String packageName) {
        return mSettings.getBlockUninstall(userId, packageName);
    }

    @Nullable
    @Override
    public Pair<PackageStateInternal, SharedUserApi> getPackageOrSharedUser(int appId) {
        final SettingBase settingBase = mSettings.getSettingBase(appId);
        if (settingBase instanceof SharedUserSetting) {
            return Pair.create(null, (SharedUserApi) settingBase);
        } else if (settingBase instanceof PackageSetting) {
            return Pair.create((PackageStateInternal) settingBase, null);
        } else {
            return null;
        }
    }

    private int getBaseSdkSandboxUid() {
        return getPackage(mService.getSdkSandboxPackageName()).getUid();
    }


    private boolean isKnownIsolatedComputeApp(int uid) {
        if (!Process.isIsolatedUid(uid)) {
            return false;
        }
        final HotwordDetectionServiceProvider hotwordDetectionServiceProvider =
                mPermissionManager.getHotwordDetectionServiceProvider();
        final boolean isHotword =
                 hotwordDetectionServiceProvider != null
                        && uid == hotwordDetectionServiceProvider.getUid();
        if (isHotword) {
            return true;
        }
        OnDeviceIntelligenceManagerLocal onDeviceIntelligenceManagerLocal =
                LocalManagerRegistry.getManager(OnDeviceIntelligenceManagerLocal.class);
        return onDeviceIntelligenceManagerLocal != null
                && uid == onDeviceIntelligenceManagerLocal.getInferenceServiceUid();
    }

    @Nullable
    @Override
    public SharedUserApi getSharedUser(int sharedUserAppId) {
        return mSettings.getSharedUserFromAppId(sharedUserAppId);
    }

    @NonNull
    @Override
    public ArraySet<PackageStateInternal> getSharedUserPackages(int sharedUserAppId) {
        return mSettings.getSharedUserPackages(sharedUserAppId);
    }

    @NonNull
    @Override
    public ComponentResolverApi getComponentResolver() {
        return mComponentResolver;
    }

    @Nullable
    @Override
    public PackageStateInternal getDisabledSystemPackage(@NonNull String packageName) {
        return mSettings.getDisabledSystemPkg(packageName);
    }

    @Nullable
    @Override
    public ResolveInfo getInstantAppInstallerInfo() {
        return mInstantAppInstallerInfo;
    }

    @NonNull
    @Override
    public WatchedArrayMap<String, Integer> getFrozenPackages() {
        return mFrozenPackages;
    }

    @Override
    public void checkPackageFrozen(@NonNull String packageName) {
        if (!mFrozenPackages.containsKey(packageName)) {
            Slog.wtf(TAG, "Expected " + packageName + " to be frozen!", new Throwable());
        }
    }

    @Nullable
    @Override
    public ComponentName getInstantAppInstallerComponent() {
        return mLocalInstantAppInstallerActivity == null
                ? null : mLocalInstantAppInstallerActivity.getComponentName();
    }

    @Override
    public void dumpPermissions(@NonNull PrintWriter pw, @NonNull String packageName,
            @NonNull ArraySet<String> permissionNames, @NonNull DumpState dumpState) {
        mSettings.dumpPermissions(pw, packageName, permissionNames, dumpState);
    }

    @Override
    public void dumpPackages(@NonNull PrintWriter pw, @NonNull String packageName,
            @NonNull ArraySet<String> permissionNames, @NonNull DumpState dumpState,
            boolean checkin) {
        mSettings.dumpPackages(pw, packageName, permissionNames, dumpState, checkin);
    }

    @Override
    public void dumpKeySet(@NonNull PrintWriter pw, @NonNull String packageName,
            @NonNull DumpState dumpState) {
        mSettings.dumpKeySet(pw, packageName, dumpState);
    }

    @Override
    public void dumpSharedUsers(@NonNull PrintWriter pw, @NonNull String packageName,
            @NonNull ArraySet<String> permissionNames, @NonNull DumpState dumpState,
            boolean checkin) {
        mSettings.dumpSharedUsers(pw, packageName, permissionNames, dumpState, checkin);
    }

    @Override
    public void dumpSharedUsersProto(@NonNull ProtoOutputStream proto) {
        mSettings.dumpSharedUsersProto(proto);
    }

    @Override
    public void dumpPackagesProto(@NonNull ProtoOutputStream proto) {
        mSettings.dumpPackagesProto(proto);
    }

    @Override
    public void dumpSharedLibrariesProto(@NonNull ProtoOutputStream proto) {
        mSharedLibraries.dumpProto(proto);
    }

    @NonNull
    @Override
    public List<? extends PackageStateInternal> getVolumePackages(@NonNull String volumeUuid) {
        return mSettings.getVolumePackages(volumeUuid);
    }

    @Override
    @NonNull
    public ArrayMap<String, ? extends SharedUserApi> getSharedUsers() {
        return mSettings.getSharedUsers();
    }

    @Override
    @NonNull
    public UserInfo[] getUserInfos() {
        return mInjector.getUserManagerInternal().getUserInfos();
    }
}
