| /* |
| * 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_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.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.compareSignatures; |
| import static com.android.server.pm.PackageManagerServiceUtils.isSystemOrRootOrShell; |
| 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.apex.ApexInfo; |
| import android.app.ActivityManager; |
| import android.content.ComponentName; |
| 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.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.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.Slog; |
| import android.util.SparseArray; |
| import android.util.Xml; |
| import android.util.proto.ProtoOutputStream; |
| |
| import com.android.internal.annotations.VisibleForTesting; |
| 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.pm.dex.DexManager; |
| import com.android.server.pm.dex.PackageDexUsage; |
| 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.pkg.AndroidPackage; |
| import com.android.server.pm.pkg.PackageStateInternal; |
| import com.android.server.pm.pkg.PackageStateUtils; |
| 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.pkg.component.ParsedActivity; |
| import com.android.server.pm.pkg.component.ParsedInstrumentation; |
| import com.android.server.pm.pkg.component.ParsedIntentInfo; |
| import com.android.server.pm.pkg.component.ParsedMainComponent; |
| import com.android.server.pm.pkg.component.ParsedProvider; |
| import com.android.server.pm.pkg.component.ParsedService; |
| import com.android.server.pm.resolution.ComponentResolverApi; |
| 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.Collection; |
| 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 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), |
| pkg.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 |
| public Collection<SharedUserSetting> getAllSharedUsers() { |
| return mSettings.getAllSharedUsersLPw(); |
| } |
| |
| @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 ApexPackageInfo mApexPackageInfo; |
| private final PackageManagerServiceInjector mInjector; |
| private final ComponentResolverApi mComponentResolver; |
| private final InstantAppResolverConnection mInstantAppResolverConnection; |
| private final DefaultAppProvider mDefaultAppProvider; |
| private final DomainVerificationManagerInternal mDomainVerificationManager; |
| private final PackageDexOptimizer mPackageDexOptimizer; |
| private final DexManager mDexManager; |
| private final CompilerStats mCompilerStats; |
| private final BackgroundDexOptService mBackgroundDexOptService; |
| 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; |
| mApexPackageInfo = args.service.mApexPackageInfo; |
| mInstantAppResolverConnection = args.service.mInstantAppResolverConnection; |
| mDefaultAppProvider = args.service.getDefaultAppProvider(); |
| mDomainVerificationManager = args.service.mDomainVerificationManager; |
| mPackageDexOptimizer = args.service.mPackageDexOptimizer; |
| mDexManager = args.service.getDexManager(); |
| mCompilerStats = args.service.mCompilerStats; |
| mBackgroundDexOptService = args.service.mBackgroundDexOptService; |
| mExternalSourcesPolicy = args.service.mExternalSourcesPolicy; |
| mCrossProfileIntentResolverEngine = new CrossProfileIntentResolverEngine( |
| mUserManager, mDomainVerificationManager, mDefaultAppProvider); |
| |
| // 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 userId, boolean resolveForStart, |
| boolean allowDynamicSplits) { |
| if (!mUserManager.exists(userId)) return Collections.emptyList(); |
| 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)); |
| 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; |
| list = new ArrayList<>(1); |
| list.add(ri); |
| PackageManagerServiceUtils.applyEnforceIntentFilterMatching( |
| mInjector.getCompatibility(), mComponentResolver, |
| list, false, intent, resolvedType, filterCallingUid); |
| } |
| } |
| } 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; |
| } |
| } |
| |
| if (originalIntent != null) { |
| // We also have to ensure all components match the original intent |
| PackageManagerServiceUtils.applyEnforceIntentFilterMatching( |
| mInjector.getCompatibility(), mComponentResolver, |
| list, false, originalIntent, resolvedType, filterCallingUid); |
| } |
| |
| 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, |
| userId, false /*resolveForStart*/, true /*allowDynamicSplits*/); |
| } |
| |
| 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(), |
| userId, false /*resolveForStart*/, true /*allowDynamicSplits*/); |
| } |
| |
| public final @NonNull List<ResolveInfo> queryIntentServicesInternal(Intent intent, |
| String resolvedType, @PackageManager.ResolveInfoFlagsBits long flags, int userId, |
| int callingUid, boolean includeInstantApps) { |
| if (!mUserManager.exists(userId)) return Collections.emptyList(); |
| enforceCrossUserOrProfilePermission(callingUid, |
| userId, |
| false /*requireFullPermission*/, |
| false /*checkShell*/, |
| "query intent receivers"); |
| final String instantAppPkgName = getInstantAppPackageName(callingUid); |
| flags = updateFlagsForResolve(flags, userId, callingUid, includeInstantApps, |
| false /* isImplicitImageCaptureIntentAndNotSetByDpc */); |
| 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); |
| PackageManagerServiceUtils.applyEnforceIntentFilterMatching( |
| mInjector.getCompatibility(), mComponentResolver, |
| list, false, intent, resolvedType, callingUid); |
| } |
| } |
| } else { |
| list = queryIntentServicesInternalBody(intent, resolvedType, flags, |
| userId, callingUid, instantAppPkgName); |
| } |
| |
| if (originalIntent != null) { |
| // We also have to ensure all components match the original intent |
| PackageManagerServiceUtils.applyEnforceIntentFilterMatching( |
| mInjector.getCompatibility(), mComponentResolver, |
| list, false, originalIntent, resolvedType, callingUid); |
| } |
| |
| 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)) { |
| /* |
| 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(mComponentResolver.queryActivities(this, |
| intent, resolvedType, flags, userId), 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, |
| 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))) { |
| result.addAll(filterIfNotSystemUser(mComponentResolver.queryActivities(this, |
| intent, resolvedType, flags, setting.getAndroidPackage().getActivities(), |
| userId), 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, |
| 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, userId, false /*resolveForStart*/, |
| false /*allowDynamicSplits*/); |
| 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); |
| } |
| |
| /** |
| * 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); |
| |
| 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 (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { |
| 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 (!ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { |
| if (matchApex) { |
| // For APKs, PackageInfo.applicationInfo is not exactly the same as ApplicationInfo |
| // returned from getApplicationInfo, but for APEX packages difference shouldn't be |
| // very big. |
| // TODO(b/155328545): generate proper application info for APEXes as well. |
| int apexFlags = ApexManager.MATCH_ACTIVE_PACKAGE; |
| if ((flags & PackageManager.MATCH_SYSTEM_ONLY) != 0) { |
| apexFlags = ApexManager.MATCH_FACTORY_PACKAGE; |
| } |
| final var pair = mApexPackageInfo.getPackageInfo(packageName, apexFlags); |
| if (pair == null) { |
| return null; |
| } |
| return PackageInfoUtils.generateApplicationInfo(pair.second, flags, |
| PackageUserStateInternal.DEFAULT, userId, null); |
| } |
| } |
| if ("android".equals(packageName) || "system".equals(packageName)) { |
| return androidApplication(); |
| } |
| if ((flags & MATCH_KNOWN_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 granted permissions only if package has requested permissions |
| final Set<String> permissions = ((flags & PackageManager.GET_PERMISSIONS) == 0 |
| || ArrayUtils.isEmpty(p.getRequestedPermissions())) ? Collections.emptySet() |
| : mPermissionManager.getGrantedPermissions(ps.getPackageName(), userId); |
| |
| PackageInfo packageInfo = PackageInfoUtils.generate(p, gids, flags, |
| state.getFirstInstallTime(), ps.getLastUpdateTime(), permissions, state, userId, |
| ps); |
| |
| if (packageInfo == null) { |
| return null; |
| } |
| |
| packageInfo.packageName = packageInfo.applicationInfo.packageName = |
| resolveExternalPackageName(p); |
| |
| return packageInfo; |
| } else if ((flags & MATCH_UNINSTALLED_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.getFirstInstallTime(); |
| 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.setVersionCode(ps.getVersionCode()); |
| ai.flags = ps.getFlags(); |
| ai.privateFlags = ps.getPrivateFlags(); |
| pi.applicationInfo = PackageInfoUtils.generateDelegateApplicationInfo( |
| ai, flags, state, userId); |
| |
| 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 |
| if (!ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { |
| if (matchApex) { |
| final var pair = mApexPackageInfo.getPackageInfo(packageName, |
| ApexManager.MATCH_FACTORY_PACKAGE); |
| if (pair == null) { |
| return null; |
| } |
| return PackageInfoUtils.generate(pair.second, pair.first, flags, null, userId); |
| } |
| } |
| final PackageStateInternal ps = mSettings.getDisabledSystemPkg(packageName); |
| if (ps != null) { |
| if (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { |
| 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); |
| if (matchFactoryOnly && p != null && !p.isSystem()) { |
| return null; |
| } |
| if (DEBUG_PACKAGE_INFO) { |
| Log.v(TAG, "getPackageInfo " + packageName + ": " + p); |
| } |
| if (p != null) { |
| final PackageStateInternal ps = getPackageStateInternal(p.getPackageName()); |
| if (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { |
| 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) != 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); |
| } |
| if (!ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { |
| if (matchApex) { |
| final var pair = mApexPackageInfo.getPackageInfo(packageName, |
| ApexManager.MATCH_ACTIVE_PACKAGE); |
| if (pair == null) { |
| return null; |
| } |
| return PackageInfoUtils.generate(pair.second, pair.first, flags, null, 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; |
| |
| ArrayList<PackageInfo> list; |
| if (listUninstalled) { |
| 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 (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { |
| if (!listApex && ps.getPkg() != null && ps.getPkg().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); |
| } |
| } |
| } else { |
| list = new ArrayList<>(mPackages.size()); |
| for (AndroidPackage p : mPackages.values()) { |
| PackageStateInternal ps = getPackageStateInternal(p.getPackageName()); |
| if (listFactory) { |
| if (!p.isSystem()) { |
| continue; |
| } |
| PackageStateInternal psDisabled = |
| ps == null ? null : mSettings.getDisabledSystemPkg(ps.getPackageName()); |
| if (psDisabled != null) { |
| ps = psDisabled; |
| } |
| } |
| if (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { |
| 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); |
| } |
| } |
| } |
| if (!ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { |
| if (listApex) { |
| List<Pair<ApexInfo, AndroidPackage>> pairs; |
| if (listFactory) { |
| pairs = mApexPackageInfo.getFactoryPackages(); |
| } else { |
| pairs = mApexPackageInfo.getActivePackages(); |
| } |
| |
| for (int index = 0; index < pairs.size(); index++) { |
| var pair = pairs.get(index); |
| list.add(PackageInfoUtils.generate(pair.second, pair.first, flags, null, |
| userId)); |
| } |
| } |
| } |
| 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 = |
| getActivityInfo(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; |
| 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) { |
| throw new IllegalStateException( |
| "No owner UID found for isolated UID " + 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 (callingAppId != Process.SYSTEM_UID && callingAppId != Process.SHELL_UID |
| && callingAppId != Process.ROOT_UID) { |
| 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(); |
| } |
| 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 (appId == Process.SYSTEM_UID || appId == Process.SHELL_UID |
| || appId == Process.ROOT_UID) { |
| 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().getStaticSharedLibVersion()); |
| 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 (appId == Process.SYSTEM_UID || appId == Process.SHELL_UID |
| || appId == Process.ROOT_UID) { |
| 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 (callingUid == Process.SYSTEM_UID || callingUid == Process.ROOT_UID) { |
| return true; |
| } |
| if (requireFullPermission) { |
| return hasPermission(Manifest.permission.INTERACT_ACROSS_USERS_FULL); |
| } |
| return hasPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) |
| || hasPermission(Manifest.permission.INTERACT_ACROSS_USERS); |
| } |
| |
| /** |
| * @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; |
| } |
| |
| 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(); |
| } |
| |
| 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 (!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()); |
| 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; |
| } |
| } |
| } |
| // 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) { |
| 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 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; |
| // 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())) { |
| // 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) |
| */ |
| 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) |
| */ |
| 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) |
| */ |
| 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) |
| */ |
| 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) |
| */ |
| public final boolean shouldFilterApplicationIncludingUninstalled( |
| @NonNull SharedUserSetting sus, int callingUid, int userId) { |
| if (shouldFilterApplication(sus, callingUid, userId)) { |
| return true; |
| } |
| if (isSystemOrRootOrShell(callingUid)) { |
| 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); |
| } |
| |
| public int getPackageUidInternal(String packageName, |
| @PackageManager.PackageInfoFlagsBits long flags, int userId, int callingUid) { |
| // reader |
| final AndroidPackage p = mPackages.get(packageName); |
| if (p != null && AndroidPackageUtils.isMatchForSystemOnly(p, 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) != 0) { |
| final PackageStateInternal ps = mSettings.getPackage(packageName); |
| if (ps != null && PackageStateUtils.isMatch(ps, flags) |
| && !shouldFilterApplication(ps, callingUid, userId)) { |
| return UserHandle.getUid(userId, ps.getAppId()); |
| } |
| } |
| |
| return -1; |
| } |
| |
| /** |
| * 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"); |
| } else if ((flags & PackageManager.MATCH_UNINSTALLED_PACKAGES) != 0 |
| && isCallerSystemUser |
| && mUserManager.hasProfile(UserHandle.USER_SYSTEM)) { |
| // If the caller wants all packages and has a profile associated with it, |
| // then match all users. This is to make sure that launchers that need to access |
| //work |
| // profile apps don't start breaking. TODO: Remove this hack when launchers stop |
| //using |
| // MATCH_UNINSTALLED_PACKAGES to query apps in other profiles. b/31000380 |
| flags |= PackageManager.MATCH_ANY_USER; |
| } |
| 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(); |
| FileOutputStream fout = new FileOutputStream(fd); |
| BufferedOutputStream str = new BufferedOutputStream(fout); |
| TypedXmlSerializer serializer = Xml.newFastSerializer(); |
| try { |
| 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(); |
| Collection<? extends PackageStateInternal> pkgSettings; |
| if (setting != null) { |
| pkgSettings = Collections.singletonList(setting); |
| } else { |
| pkgSettings = mSettings.getPackages().values(); |
| } |
| |
| for (PackageStateInternal pkgSetting : pkgSettings) { |
| final AndroidPackage pkg = pkgSetting.getPkg(); |
| if (pkg == null || pkg.isApex()) { |
| // Skip APEX which is not dex-optimized |
| continue; |
| } |
| final String pkgName = pkg.getPackageName(); |
| ipw.println("[" + pkgName + "]"); |
| ipw.increaseIndent(); |
| |
| mPackageDexOptimizer.dumpDexoptState(ipw, pkg, pkgSetting, |
| mDexManager.getPackageUseInfoOrDefault(pkgName)); |
| ipw.decreaseIndent(); |
| } |
| ipw.println("BgDexopt state:"); |
| ipw.increaseIndent(); |
| mBackgroundDexOptService.dump(ipw); |
| ipw.decreaseIndent(); |
| break; |
| } |
| |
| case DumpState.DUMP_COMPILER_STATS: |
| { |
| final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); |
| if (dumpState.onTitlePrinted()) { |
| pw.println(); |
| } |
| ipw.println("Compiler stats:"); |
| ipw.increaseIndent(); |
| Collection<? extends PackageStateInternal> pkgSettings; |
| if (setting != null) { |
| pkgSettings = Collections.singletonList(setting); |
| } else { |
| pkgSettings = mSettings.getPackages().values(); |
| } |
| |
| for (PackageStateInternal pkgSetting : pkgSettings) { |
| final AndroidPackage pkg = pkgSetting.getPkg(); |
| if (pkg == null) { |
| continue; |
| } |
| final String pkgName = pkg.getPackageName(); |
| ipw.println("[" + pkgName + "]"); |
| ipw.increaseIndent(); |
| |
| final CompilerStats.PackageStats stats = |
| mCompilerStats.getPackageStats(pkgName); |
| if (stats == null) { |
| ipw.println("(No recorded stats)"); |
| } else { |
| stats.dump(ipw); |
| } |
| 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) { |
| if (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { |
| 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:"); |
| ApexPackageInfo.dumpPackageStates(activePackages, true, packageName, ipw); |
| ipw.println("Inactive APEX packages:"); |
| ApexPackageInfo.dumpPackageStates(inactivePackages, false, packageName, ipw); |
| ipw.println("Factory APEX packages:"); |
| ApexPackageInfo.dumpPackageStates(factoryActivePackages, true, packageName, ipw); |
| ApexPackageInfo.dumpPackageStates(factoryInactivePackages, false, packageName, ipw); |
| } else { |
| mApexPackageInfo.dump(pw, packageName); |
| } |
| } |
| |
| // 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 { |
| 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(); |
| } |
| |
| @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 userKeyUnlocked = StorageManager.isUserKeyUnlocked(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 (!userKeyUnlocked && !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) { |
| if (!ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { |
| return mApexPackageInfo.isApexPackage(packageName); |
| } else { |
| 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.getPkg(), flags)) { |
| if (ps.getUserStateOrDefault(userId).isInstalled() |
| && !shouldFilterApplication(ps, callingUid, userId)) { |
| return mPermissionManager.getGidsForUid(UserHandle.getUid(userId, |
| ps.getAppId())); |
| } |
| } |
| if ((flags & MATCH_KNOWN_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; |
| } |
| |
| flags = updateFlagsForPackage(flags, userId); |
| |
| final boolean canSeeStaticAndSdkLibraries = |
| mContext.checkCallingOrSelfPermission(INSTALL_PACKAGES) |
| == PERMISSION_GRANTED |
| || 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, |
| getPackagesUsingSharedLibrary(libInfo, flags, callingUid, userId), |
| (libInfo.getDependencies() == null |
| ? null |
| : new ArrayList<>(libInfo.getDependencies())), |
| libInfo.isNative()); |
| |
| 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 && callingUid != Process.ROOT_UID |
| && callingUid != Process.SYSTEM_UID) { |
| 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 List<VersionedPackage> 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(); |
| 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 int libIdx = ArrayUtils.indexOf(libs, libName); |
| if (libIdx < 0) { |
| continue; |
| } |
| if (libsVersions[libIdx] != libInfo.getLongVersion()) { |
| continue; |
| } |
| if (shouldFilterApplication(ps, callingUid, userId)) { |
| continue; |
| } |
| if (versionedPackages == null) { |
| versionedPackages = 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())); |
| } 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 versionedPackages; |
| } |
| |
| @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); |
| } |
| |
| SharedLibraryInfo resultLibraryInfo = new SharedLibraryInfo( |
| libraryInfo.getPath(), libraryInfo.getPackageName(), |
| libraryInfo.getAllCodePaths(), libraryInfo.getName(), |
| libraryInfo.getLongVersion(), libraryInfo.getType(), |
| libraryInfo.getDeclaringPackage(), |
| getPackagesUsingSharedLibrary( |
| libraryInfo, flags, callingUid, userId), |
| 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; |
| } |
| |
| @Nullable |
| @Override |
| public String[] getSystemSharedLibraryNames() { |
| // allow instant applications |
| final WatchedArrayMap<String, WatchedLongSparseArray<SharedLibraryInfo>> sharedLibraries = |
| getSharedLibraries(); |
| Set<String> libs = 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 libraryInfo = versionedLib.valueAt(j); |
| if (!libraryInfo.isStatic()) { |
| if (libs == null) { |
| libs = new ArraySet<>(); |
| } |
| libs.add(libraryInfo.getName()); |
| 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)) { |
| if (libs == null) { |
| libs = new ArraySet<>(); |
| } |
| libs.add(libraryInfo.getName()); |
| break; |
| } |
| } |
| } |
| |
| if (libs != null) { |
| String[] libsArray = new String[libs.size()]; |
| libs.toArray(libsArray); |
| return libsArray; |
| } |
| |
| return null; |
| } |
| |
| @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.getSignatures(), |
| p2SigningDetails.getSignatures()); |
| 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 = compareSignatures(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); |
| 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(); |
| } |
| 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 Process.INVALID_UID; |
| } |
| final int callingUid = Binder.getCallingUid(); |
| if (getInstantAppPackageName(callingUid) != null) { |
| return Process.INVALID_UID; |
| } |
| final SharedUserSetting suid = mSettings.getSharedUserFromId(sharedUserName); |
| if (suid != null && !shouldFilterApplicationIncludingUninstalled(suid, callingUid, |
| UserHandle.getUserId(callingUid))) { |
| return suid.mAppId; |
| } |
| return Process.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) != 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, 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) { |
| 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; |
| |
| enforceCrossUserPermission( |
| callingUid, |
| userId, |
| false /* requireFullPermission */, |
| false /* checkShell */, |
| "get installed application info"); |
| |
| ArrayList<ApplicationInfo> list; |
| final ArrayMap<String, ? extends PackageStateInternal> packageStates = |
| getPackageStates(); |
| if (listUninstalled) { |
| 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 (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { |
| if (!listApex && ps.getPkg().isApex()) { |
| 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 (ApexPackageInfo.ENABLE_FEATURE_SCAN_APEX) { |
| 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 resolveContentProvider(@NonNull String name, |
| @PackageManager.ResolveInfoFlagsBits long flags, @UserIdInt int userId, |
| int callingUid) { |
| if (!mUserManager.exists(userId)) return null; |
| flags = updateFlagsForComponent(flags, userId); |
| final ProviderInfo providerInfo = mComponentResolver.queryProvider(this, name, 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(name)) { |
| 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 (!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); |
| 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); |
| } |
| |
| @NonNull |
| @Override |
| public List<PackageStateInternal> findSharedNonSystemLibraries( |
| @NonNull PackageStateInternal pkgSetting) { |
| List<SharedLibraryInfo> deps = SharedLibraryUtils.findSharedLibraries(pkgSetting); |
| if (!deps.isEmpty()) { |
| List<PackageStateInternal> retValue = new ArrayList<>(); |
| for (SharedLibraryInfo info : deps) { |
| PackageStateInternal depPackageSetting = |
| getPackageStateInternal(info.getPackageName()); |
| if (depPackageSetting != null && depPackageSetting.getPkg() != null) { |
| retValue.add(depPackageSetting); |
| } |
| } |
| return retValue; |
| } else { |
| return Collections.emptyList(); |
| } |
| } |
| |
| /** |
| * 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); |
| } |
| } |
| |
| @Override |
| public boolean isPackageSuspendedForUser(@NonNull String packageName, int userId) { |
| final int callingUid = Binder.getCallingUid(); |
| enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, |
| false /* checkShell */, "isPackageSuspendedForUser for user " + userId); |
| final PackageStateInternal ps = mSettings.getPackage(packageName); |
| if (ps == null || shouldFilterApplicationIncludingUninstalled(ps, callingUid, userId)) { |
| throw new IllegalArgumentException("Unknown target package: " + packageName); |
| } |
| return ps.getUserStateOrDefault(userId).isSuspended(); |
| } |
| |
| @Override |
| public boolean isSuspendingAnyPackages(@NonNull String suspendingPackage, |
| @UserIdInt int userId) { |
| for (final PackageStateInternal packageState : getPackageStates().values()) { |
| final PackageUserStateInternal state = packageState.getUserStateOrDefault(userId); |
| if (state.getSuspendParams() != null |
| && state.getSuspendParams().containsKey(suspendingPackage)) { |
| 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.installerPackageName; |
| if (installerPackageName != null) { |
| final PackageStateInternal ps = mSettings.getPackage(installerPackageName); |
| if (ps == null || shouldFilterApplicationIncludingUninstalled(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 || shouldFilterApplicationIncludingUninstalled(ps, callingUid, userId)) { |
| return null; |
| } |
| |
| return ps.getInstallSource(); |
| } |
| |
| @Override |
| @Nullable |
| public InstallSourceInfo getInstallSourceInfo(@NonNull String packageName) { |
| final int callingUid = Binder.getCallingUid(); |
| final int userId = UserHandle.getUserId(callingUid); |
| |
| String installerPackageName; |
| String initiatingPackageName; |
| String originatingPackageName; |
| |
| final InstallSource installSource = getInstallSource(packageName, callingUid, userId); |
| if (installSource == null) { |
| return null; |
| } |
| |
| installerPackageName = installSource.installerPackageName; |
| if (installerPackageName != null) { |
| final PackageStateInternal ps = mSettings.getPackage(installerPackageName); |
| if (ps == null |
| || shouldFilterApplicationIncludingUninstalled(ps, callingUid, userId)) { |
| installerPackageName = null; |
| } |
| } |
| |
| if (installSource.isInitiatingPackageUninstalled) { |
| // 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.initiatingPackageName; |
| } else { |
| initiatingPackageName = null; |
| } |
| } else { |
| if (Objects.equals(installSource.initiatingPackageName, |
| installSource.installerPackageName)) { |
| // 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.initiatingPackageName; |
| final PackageStateInternal ps = mSettings.getPackage(initiatingPackageName); |
| if (ps == null |
| || shouldFilterApplicationIncludingUninstalled(ps, callingUid, userId)) { |
| initiatingPackageName = null; |
| } |
| } |
| } |
| |
| originatingPackageName = installSource.originatingPackageName; |
| 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.initiatingPackageSignatures; |
| if (initiatingPackageName != null && signatures != null |
| && signatures.mSigningDetails != SigningDetails.UNKNOWN) { |
| initiatingPackageSigningInfo = new SigningInfo(signatures.mSigningDetails); |
| } else { |
| initiatingPackageSigningInfo = null; |
| } |
| |
| return new InstallSourceInfo(initiatingPackageName, initiatingPackageSigningInfo, |
| originatingPackageName, installerPackageName, installSource.packageSource); |
| } |
| |
| @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(callingUid, 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, |
| @UserIdInt int userId) { |
| try { |
| String packageName = componentInfo.packageName; |
| 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, |
| @UserIdInt int userId) { |
| try { |
| int appEnabledSetting = mSettings.getApplicationEnabledSetting(packageName, userId); |
| 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 (pkg.getUid() != callingUid |
| && 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 != Process.INVALID_UID) { |
| final Object targetSetting = mSettings.getSettingBase(targetAppId); |
| 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().installerPackageName); |
| 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 |
| public boolean canPackageQuery(@NonNull String sourcePackageName, |
| @NonNull String targetPackageName, @UserIdInt int userId) { |
| if (!mUserManager.exists(userId)) return false; |
| final int callingUid = Binder.getCallingUid(); |
| enforceCrossUserPermission(callingUid, userId, false /*requireFullPermission*/, |
| false /*checkShell*/, "may package query"); |
| final PackageStateInternal sourceSetting = getPackageStateInternal(sourcePackageName); |
| final PackageStateInternal targetSetting = getPackageStateInternal(targetPackageName); |
| boolean throwException = sourceSetting == null || targetSetting == null; |
| if (!throwException) { |
| final boolean filterSource = |
| shouldFilterApplicationIncludingUninstalled(sourceSetting, callingUid, userId); |
| final boolean filterTarget = |
| shouldFilterApplicationIncludingUninstalled(targetSetting, callingUid, userId); |
| // The caller must have visibility of the both packages |
| throwException = filterSource || filterTarget; |
| } |
| if (throwException) { |
| throw new ParcelableException(new PackageManager.NameNotFoundException("Package(s) " |
| + sourcePackageName + " and/or " + targetPackageName + " not found.")); |
| } |
| final int sourcePackageUid = UserHandle.getUid(userId, sourceSetting.getAppId()); |
| return !shouldFilterApplication(targetSetting, sourcePackageUid, userId); |
| } |
| |
| /* |
| * 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); |
| |
| 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 || p.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 (SharedUserSetting setting : mSettings.getAllSharedUsers()) { |
| sharedUserIds.put(UserHandle.getAppId(setting.mAppId), setting.name); |
| } |
| 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; |
| } |
| |
| |
| @NonNull |
| @Override |
| public Set<String> getUnusedPackages(long downgradeTimeThresholdMillis) { |
| Set<String> unusedPackages = new ArraySet<>(); |
| long currentTimeInMillis = System.currentTimeMillis(); |
| final ArrayMap<String, ? extends PackageStateInternal> packageStates = |
| mSettings.getPackages(); |
| for (int index = 0; index < packageStates.size(); index++) { |
| final PackageStateInternal packageState = packageStates.valueAt(index); |
| if (packageState.getPkg() == null) { |
| continue; |
| } |
| PackageDexUsage.PackageUseInfo packageUseInfo = |
| mDexManager.getPackageUseInfoOrDefault(packageState.getPackageName()); |
| if (PackageManagerServiceUtils.isUnusedSinceTimeInMillis( |
| PackageStateUtils.getEarliestFirstInstallTime(packageState.getUserStates()), |
| currentTimeInMillis, downgradeTimeThresholdMillis, packageUseInfo, |
| packageState.getTransientState().getLatestPackageUseTimeInMills(), |
| packageState.getTransientState().getLatestForegroundPackageUseTimeInMills())) { |
| unusedPackages.add(packageState.getPackageName()); |
| } |
| } |
| return unusedPackages; |
| } |
| |
| @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 (callingAppId != Process.SYSTEM_UID && callingAppId != Process.ROOT_UID && |
| 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 AndroidPackage#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; |
| } |
| |
| AndroidPackage pkg = getPackage(pkgName); |
| if (pkg == null) { |
| Log.w(TAG, "Could not find package " + pkgName); |
| continue; |
| } |
| |
| if (!pkg.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; |
| return List.of(ps.getPkg()); |
| } 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; |
| return PackageInfoUtils.generateProcessInfo(ps.getPkg().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(); |
| } |
| |
| @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 Collection<SharedUserSetting> getAllSharedUsers() { |
| return mSettings.getAllSharedUsers(); |
| } |
| |
| @Override |
| @NonNull |
| public UserInfo[] getUserInfos() { |
| return mInjector.getUserManagerInternal().getUserInfos(); |
| } |
| } |