blob: 8c319338199f9ea8c6b1c09151378d78d7cfeca9 [file] [log] [blame]
package org.robolectric.shadows;
import static android.content.IntentFilter.MATCH_CATEGORY_MASK;
import static android.content.pm.ApplicationInfo.FLAG_INSTALLED;
import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
import static android.content.pm.PackageManager.GET_ACTIVITIES;
import static android.content.pm.PackageManager.GET_META_DATA;
import static android.content.pm.PackageManager.GET_PROVIDERS;
import static android.content.pm.PackageManager.GET_RECEIVERS;
import static android.content.pm.PackageManager.GET_RESOLVED_FILTER;
import static android.content.pm.PackageManager.GET_SERVICES;
import static android.content.pm.PackageManager.GET_SIGNATURES;
import static android.content.pm.PackageManager.MATCH_ALL;
import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
import static android.content.pm.PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
import static android.os.Build.VERSION_CODES.JELLY_BEAN;
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
import static android.os.Build.VERSION_CODES.KITKAT;
import static android.os.Build.VERSION_CODES.LOLLIPOP;
import static android.os.Build.VERSION_CODES.LOLLIPOP_MR1;
import static android.os.Build.VERSION_CODES.M;
import static android.os.Build.VERSION_CODES.N;
import static android.os.Build.VERSION_CODES.O;
import static android.os.Build.VERSION_CODES.O_MR1;
import static android.os.Build.VERSION_CODES.P;
import static android.os.Build.VERSION_CODES.Q;
import static android.os.Build.VERSION_CODES.R;
import static android.os.Build.VERSION_CODES.S;
import static android.os.Build.VERSION_CODES.S_V2;
import static android.os.Build.VERSION_CODES.TIRAMISU;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.robolectric.annotation.GetInstallerPackageNameMode.Mode.REALISTIC;
import static org.robolectric.util.reflector.Reflector.reflector;
import android.Manifest.permission;
import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.StringRes;
import android.annotation.UserIdInt;
import android.app.ApplicationPackageManager;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ChangedPackages;
import android.content.pm.ComponentInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.InstallSourceInfo;
import android.content.pm.InstrumentationInfo;
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.ModuleInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.ApplicationInfoFlags;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManager.OnPermissionsChangedListener;
import android.content.pm.PackageManager.PackageInfoFlags;
import android.content.pm.PackageManager.ResolveInfoFlags;
import android.content.pm.PackageStats;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.VerifierDeviceIdentity;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Handler;
import android.os.Looper;
import android.os.Parcel;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.storage.VolumeInfo;
import android.telecom.TelecomManager;
import android.util.Log;
import android.util.Pair;
import com.google.common.base.Function;
import com.google.common.base.Splitter;
import com.google.common.collect.Sets;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.SortedMap;
import java.util.concurrent.Executor;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.GetInstallerPackageNameMode;
import org.robolectric.annotation.HiddenApi;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.RealObject;
import org.robolectric.config.ConfigurationRegistry;
import org.robolectric.util.reflector.Accessor;
import org.robolectric.util.reflector.Direct;
import org.robolectric.util.reflector.ForType;
@Implements(value = ApplicationPackageManager.class, isInAndroidSdk = false, looseSignatures = true)
public class ShadowApplicationPackageManager extends ShadowPackageManager {
/** Package name of the Android platform. */
private static final String PLATFORM_PACKAGE_NAME = "android";
/** MIME type of Android Packages (APKs). */
private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";
/** {@link Uri} scheme of installed apps. */
private static final String PACKAGE_SCHEME = "package";
@RealObject private ApplicationPackageManager realObject;
private final List<String> clearedApplicationUserDataPackages = new ArrayList<>();
@Implementation
public List<PackageInfo> getInstalledPackages(int flags) {
return getInstalledPackages((long) flags);
}
@Implementation(minSdk = TIRAMISU)
protected List<PackageInfo> getInstalledPackages(Object flags) {
return getInstalledPackages(((PackageInfoFlags) flags).getValue());
}
private List<PackageInfo> getInstalledPackages(long flags) {
List<PackageInfo> result = new ArrayList<>();
synchronized (lock) {
Set<String> packageNames = null;
if ((flags & MATCH_UNINSTALLED_PACKAGES) == 0) {
packageNames = packageInfos.keySet();
} else {
packageNames = Sets.union(packageInfos.keySet(), deletedPackages);
}
for (String packageName : packageNames) {
try {
PackageInfo packageInfo = getPackageInfo(packageName, flags);
result.add(packageInfo);
} catch (NameNotFoundException e) {
Log.i(TAG, String.format("Package %s filtered out: %s", packageName, e.getMessage()));
}
}
}
return result;
}
@Implementation(minSdk = Q)
protected List<ModuleInfo> getInstalledModules(int flags) {
synchronized (lock) {
List<ModuleInfo> result = new ArrayList<>();
for (String moduleName : moduleInfos.keySet()) {
try {
ModuleInfo moduleInfo = (ModuleInfo) getModuleInfo(moduleName, flags);
result.add(moduleInfo);
} catch (NameNotFoundException e) {
Log.i(TAG, String.format("Module %s filtered out: %s", moduleName, e.getMessage()));
}
}
return result;
}
}
@Implementation(minSdk = Q)
protected Object getModuleInfo(String packageName, int flags) throws NameNotFoundException {
synchronized (lock) {
// Double checks that the respective package matches and is not disabled
getPackageInfo(packageName, flags);
Object info = moduleInfos.get(packageName);
if (info == null) {
throw new NameNotFoundException("Module: " + packageName + " is not installed.");
}
return info;
}
}
@Implementation
protected ActivityInfo getActivityInfo(ComponentName component, int flags)
throws NameNotFoundException {
return getComponentInfo(
component,
flags,
packageInfo -> packageInfo.activities,
resolveInfo -> resolveInfo.activityInfo,
ActivityInfo::new);
}
private <T extends ComponentInfo> T getComponentInfo(
ComponentName component,
int flags,
Function<PackageInfo, T[]> componentsInPackage,
Function<ResolveInfo, T> componentInResolveInfo,
Function<T, T> copyConstructor)
throws NameNotFoundException {
String activityName = component.getClassName();
String packageName = component.getPackageName();
PackageInfo packageInfo = getInternalMutablePackageInfo(packageName);
T result = null;
ApplicationInfo appInfo = null;
// search in the manifest
if (packageInfo != null) {
if (packageInfo.applicationInfo != null) {
appInfo = packageInfo.applicationInfo;
}
T[] components = componentsInPackage.apply(packageInfo);
if (components != null) {
for (T activity : components) {
if (activityName.equals(activity.name)) {
result = copyConstructor.apply(activity);
break;
}
}
}
}
if (result == null) {
// look in the registered intents
outer:
for (List<ResolveInfo> listOfResolveInfo : resolveInfoForIntent.values()) {
for (ResolveInfo resolveInfo : listOfResolveInfo) {
T info = componentInResolveInfo.apply(resolveInfo);
if (isValidComponentInfo(info)
&& component.equals(new ComponentName(info.applicationInfo.packageName, info.name))) {
result = copyConstructor.apply(info);
if (appInfo == null) {
// we found valid app info in the resolve info. Use it.
appInfo = result.applicationInfo;
}
break outer;
}
}
}
}
if (result == null) {
throw new NameNotFoundException("Component not found: " + component);
}
if (appInfo == null) {
appInfo = new ApplicationInfo();
appInfo.packageName = packageName;
appInfo.flags = ApplicationInfo.FLAG_INSTALLED;
} else {
appInfo = new ApplicationInfo(appInfo);
}
result.applicationInfo = appInfo;
applyFlagsToComponentInfo(result, flags);
return result;
}
@Implementation
protected boolean hasSystemFeature(String name) {
return systemFeatureList.containsKey(name) ? systemFeatureList.get(name) : false;
}
@Implementation
protected int getComponentEnabledSetting(ComponentName componentName) {
ComponentState state = componentList.get(componentName);
return state != null ? state.newState : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
}
@Implementation
protected @Nullable String getNameForUid(int uid) {
return namesForUid.get(uid);
}
@Implementation
@Override
protected @Nullable String[] getPackagesForUid(int uid) {
String[] packageNames = packagesForUid.get(uid);
if (packageNames != null) {
return packageNames;
}
Set<String> results = new HashSet<>();
synchronized (lock) {
for (PackageInfo packageInfo : packageInfos.values()) {
if (packageInfo.applicationInfo != null && packageInfo.applicationInfo.uid == uid) {
results.add(packageInfo.packageName);
}
}
}
return results.isEmpty() ? null : results.toArray(new String[results.size()]);
}
@Implementation
protected int getApplicationEnabledSetting(String packageName) {
synchronized (lock) {
if (!packageInfos.containsKey(packageName)) {
throw new IllegalArgumentException("Package doesn't exist: " + packageName);
}
}
return applicationEnabledSettingMap.get(packageName);
}
@Implementation
protected ProviderInfo getProviderInfo(ComponentName component, int flags)
throws NameNotFoundException {
return getComponentInfo(
component,
flags,
packageInfo -> packageInfo.providers,
resolveInfo -> resolveInfo.providerInfo,
ProviderInfo::new);
}
@Implementation
protected void setComponentEnabledSetting(ComponentName componentName, int newState, int flags) {
componentList.put(componentName, new ComponentState(newState, flags));
}
@Implementation
protected void setApplicationEnabledSetting(String packageName, int newState, int flags) {
applicationEnabledSettingMap.put(packageName, newState);
}
@Implementation
protected ResolveInfo resolveActivity(Intent intent, int flags) {
List<ResolveInfo> candidates = queryIntentActivities(intent, flags);
if (candidates.isEmpty()) {
return null;
}
if (candidates.size() == 1) {
return candidates.get(0);
}
ResolveInfo persistentPreferredResolveInfo =
resolvePreferredActivity(intent, candidates, persistentPreferredActivities);
if (persistentPreferredResolveInfo != null) {
return persistentPreferredResolveInfo;
}
ResolveInfo preferredResolveInfo =
resolvePreferredActivity(intent, candidates, preferredActivities);
if (preferredResolveInfo != null) {
return preferredResolveInfo;
}
if (!shouldShowActivityChooser) {
return candidates.get(0);
}
ResolveInfo c1 = candidates.get(0);
ResolveInfo c2 = candidates.get(1);
if (c1.preferredOrder == c2.preferredOrder
&& isValidComponentInfo(c1.activityInfo)
&& isValidComponentInfo(c2.activityInfo)) {
// When the top pick is as good as the second and is not preferred explicitly show the
// chooser
ResolveInfo result = new ResolveInfo();
result.activityInfo = new ActivityInfo();
result.activityInfo.name = "ActivityResolver";
result.activityInfo.packageName = "android";
result.activityInfo.applicationInfo = new ApplicationInfo();
result.activityInfo.applicationInfo.flags = FLAG_INSTALLED | FLAG_SYSTEM;
result.activityInfo.applicationInfo.packageName = "android";
return result;
} else {
return c1;
}
}
private ResolveInfo resolvePreferredActivity(
Intent intent,
List<ResolveInfo> candidates,
SortedMap<ComponentName, List<IntentFilter>> preferredActivities) {
preferredActivities = mapForPackage(preferredActivities, intent.getPackage());
for (ResolveInfo candidate : candidates) {
ActivityInfo activityInfo = candidate.activityInfo;
if (!isValidComponentInfo(activityInfo)) {
continue;
}
ComponentName candidateName =
new ComponentName(activityInfo.applicationInfo.packageName, activityInfo.name);
List<IntentFilter> intentFilters = preferredActivities.get(candidateName);
if (intentFilters == null) {
continue;
}
for (IntentFilter filter : intentFilters) {
if ((filter.match(getContext().getContentResolver(), intent, false, "robo")
& MATCH_CATEGORY_MASK)
!= 0) {
return candidate;
}
}
}
return null;
}
@Implementation
protected ProviderInfo resolveContentProvider(String name, int flags) {
if (name == null) {
return null;
}
synchronized (lock) {
for (PackageInfo packageInfo : packageInfos.values()) {
if (packageInfo.providers == null) {
continue;
}
for (ProviderInfo providerInfo : packageInfo.providers) {
for (String authority : Splitter.on(';').split(providerInfo.authority)) {
if (name.equals(authority)) {
return new ProviderInfo(providerInfo);
}
}
}
}
}
return null;
}
@Implementation(minSdk = LOLLIPOP)
protected ProviderInfo resolveContentProviderAsUser(
String name, int flags, @UserIdInt int userId) {
return null;
}
@Implementation
protected PackageInfo getPackageInfo(String packageName, int flags) throws NameNotFoundException {
return getPackageInfo(packageName, (long) flags);
}
@Implementation(minSdk = TIRAMISU)
protected PackageInfo getPackageInfo(Object packageName, Object flags)
throws NameNotFoundException {
return getPackageInfo((String) packageName, ((PackageInfoFlags) flags).getValue());
}
private PackageInfo getPackageInfo(String packageName, long flags) throws NameNotFoundException {
synchronized (lock) {
PackageInfo info = packageInfos.get(packageName);
if (info == null
&& (flags & MATCH_UNINSTALLED_PACKAGES) != 0
&& deletedPackages.contains(packageName)) {
info = new PackageInfo();
info.packageName = packageName;
info.applicationInfo = new ApplicationInfo();
info.applicationInfo.packageName = packageName;
}
if (info == null) {
throw new NameNotFoundException(packageName);
}
info = newPackageInfo(info);
if (info.applicationInfo == null) {
return info;
}
if (hiddenPackages.contains(packageName) && !isFlagSet(flags, MATCH_UNINSTALLED_PACKAGES)) {
throw new NameNotFoundException("Package is hidden, can't find");
}
applyFlagsToApplicationInfo(info.applicationInfo, flags);
info.activities =
applyFlagsToComponentInfoList(info.activities, flags, GET_ACTIVITIES, ActivityInfo::new);
info.services =
applyFlagsToComponentInfoList(info.services, flags, GET_SERVICES, ServiceInfo::new);
info.receivers =
applyFlagsToComponentInfoList(info.receivers, flags, GET_RECEIVERS, ActivityInfo::new);
info.providers =
applyFlagsToComponentInfoList(info.providers, flags, GET_PROVIDERS, ProviderInfo::new);
return info;
}
}
/**
* Starting in Android S, this method was moved from {@link android.content.pm.PackageManager} to
* {@link ApplicationPackageManager}. However, it was moved back to {@link
* android.content.pm.PackageManager} in T.
*/
@Override
@Implementation(minSdk = S, maxSdk = S_V2)
protected PackageInfo getPackageArchiveInfo(String archiveFilePath, int flags) {
int apiLevel = RuntimeEnvironment.getApiLevel();
if (apiLevel == S || apiLevel == S_V2) {
PackageInfo shadowPackageInfo = getShadowPackageArchiveInfo(archiveFilePath, flags);
if (shadowPackageInfo != null) {
return shadowPackageInfo;
} else {
return reflector(ReflectorApplicationPackageManager.class, realObject)
.getPackageArchiveInfo(archiveFilePath, flags);
}
} else {
return super.getPackageArchiveInfo(archiveFilePath, flags);
}
}
// There is no copy constructor for PackageInfo
private static PackageInfo newPackageInfo(PackageInfo orig) {
Parcel parcel = Parcel.obtain();
orig.writeToParcel(parcel, 0);
parcel.setDataPosition(0);
return PackageInfo.CREATOR.createFromParcel(parcel);
}
private <T extends ComponentInfo> T[] applyFlagsToComponentInfoList(
T[] components, long flags, int activationFlag, Function<T, T> copyConstructor) {
if (components == null || (flags & activationFlag) == 0) {
return null;
}
List<T> returned = new ArrayList<>(components.length);
for (T component : components) {
component = copyConstructor.apply(component);
try {
applyFlagsToComponentInfo(component, flags);
returned.add(component);
} catch (NameNotFoundException e) {
// skip this component
}
}
if (returned.isEmpty()) {
return null;
}
@SuppressWarnings("unchecked") // component arrays are of their respective types.
Class<T[]> componentArrayType = (Class<T[]>) components.getClass();
T[] result = Arrays.copyOf(components, returned.size(), componentArrayType);
return returned.toArray(result);
}
@Implementation
protected List<ResolveInfo> queryIntentServices(Intent intent, int flags) {
return queryIntentComponents(
intent,
flags,
(pkg) -> pkg.services,
serviceFilters,
(resolveInfo, serviceInfo) -> resolveInfo.serviceInfo = serviceInfo,
(resolveInfo) -> resolveInfo.serviceInfo,
ServiceInfo::new);
}
private boolean hasSomeComponentInfo(ResolveInfo resolveInfo) {
return resolveInfo.activityInfo != null
|| resolveInfo.serviceInfo != null
|| (VERSION.SDK_INT >= VERSION_CODES.KITKAT && resolveInfo.providerInfo != null);
}
private static boolean isFlagSet(long flags, long matchFlag) {
return (flags & matchFlag) == matchFlag;
}
private static boolean isValidComponentInfo(ComponentInfo componentInfo) {
return componentInfo != null
&& componentInfo.applicationInfo != null
&& componentInfo.applicationInfo.packageName != null
&& componentInfo.name != null;
}
/** Behaves as {@link #queryIntentServices(Intent, int)} and currently ignores userId. */
@Implementation(minSdk = JELLY_BEAN_MR1)
protected List<ResolveInfo> queryIntentServicesAsUser(Intent intent, int flags, int userId) {
return queryIntentServices(intent, flags);
}
@Implementation
protected List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
return this.queryIntentComponents(
intent,
flags,
(pkg) -> pkg.activities,
activityFilters,
(resolveInfo, activityInfo) -> resolveInfo.activityInfo = activityInfo,
(resolveInfo) -> resolveInfo.activityInfo,
ActivityInfo::new);
}
private <I extends ComponentInfo> List<ResolveInfo> queryIntentComponents(
Intent intent,
int flags,
Function<PackageInfo, I[]> componentsInPackage,
SortedMap<ComponentName, List<IntentFilter>> filters,
BiConsumer<ResolveInfo, I> componentSetter,
Function<ResolveInfo, I> componentInResolveInfo,
Function<I, I> copyConstructor) {
synchronized (lock) {
if (intent.getComponent() != null) {
flags &= ~MATCH_DEFAULT_ONLY;
}
List<ResolveInfo> result = new ArrayList<>();
List<ResolveInfo> resolveInfoList = queryOverriddenIntents(intent, flags);
if (!resolveInfoList.isEmpty()) {
result.addAll(resolveInfoList);
}
result.addAll(
queryComponentsInManifest(intent, componentsInPackage, filters, componentSetter));
for (Iterator<ResolveInfo> iterator = result.iterator(); iterator.hasNext(); ) {
ResolveInfo resolveInfo = iterator.next();
I componentInfo = componentInResolveInfo.apply(resolveInfo);
if (hasSomeComponentInfo(resolveInfo) && componentInfo == null) {
Log.d(TAG, "ResolveInfo for different component type");
// different component type
iterator.remove();
continue;
}
if (componentInfo == null) {
// null component? Don't filter this sh...
continue;
}
if (!applyFlagsToResolveInfo(resolveInfo, flags)) {
Log.d(TAG, "ResolveInfo doesn't match flags");
iterator.remove();
continue;
}
ApplicationInfo applicationInfo = componentInfo.applicationInfo;
if (applicationInfo == null) {
String packageName = null;
if (getComponentForIntent(intent) != null) {
packageName = getComponentForIntent(intent).getPackageName();
} else if (intent.getPackage() != null) {
packageName = intent.getPackage();
} else if (componentInfo.packageName != null) {
packageName = componentInfo.packageName;
}
if (packageName != null) {
PackageInfo packageInfo = packageInfos.get(packageName);
if (packageInfo != null && packageInfo.applicationInfo != null) {
applicationInfo = new ApplicationInfo(packageInfo.applicationInfo);
} else {
applicationInfo = new ApplicationInfo();
applicationInfo.packageName = packageName;
applicationInfo.flags = FLAG_INSTALLED;
}
}
} else {
applicationInfo = new ApplicationInfo(applicationInfo);
}
componentInfo = copyConstructor.apply(componentInfo);
componentSetter.accept(resolveInfo, componentInfo);
componentInfo.applicationInfo = applicationInfo;
try {
applyFlagsToComponentInfo(componentInfo, flags);
} catch (NameNotFoundException e) {
Log.d(TAG, "ComponentInfo doesn't match flags:" + e.getMessage());
iterator.remove();
continue;
}
}
Collections.sort(result, new ResolveInfoComparator());
return result;
}
}
private boolean applyFlagsToResolveInfo(ResolveInfo resolveInfo, int flags) {
if ((flags & GET_RESOLVED_FILTER) == 0) {
resolveInfo.filter = null;
}
return (flags & MATCH_DEFAULT_ONLY) == 0 || resolveInfo.isDefault;
}
private <I extends ComponentInfo> List<ResolveInfo> queryComponentsInManifest(
Intent intent,
Function<PackageInfo, I[]> componentsInPackage,
SortedMap<ComponentName, List<IntentFilter>> filters,
BiConsumer<ResolveInfo, I> componentSetter) {
synchronized (lock) {
if (isExplicitIntent(intent)) {
ComponentName component = getComponentForIntent(intent);
PackageInfo appPackage = packageInfos.get(component.getPackageName());
if (appPackage == null) {
return Collections.emptyList();
}
I componentInfo = findMatchingComponent(component, componentsInPackage.apply(appPackage));
if (componentInfo != null) {
List<IntentFilter> componentFilters = filters.get(component);
PackageInfo targetPackage = packageInfos.get(component.getPackageName());
if (RuntimeEnvironment.getApiLevel() >= TIRAMISU
&& (intent.getAction() != null
|| intent.getCategories() != null
|| intent.getData() != null)
&& componentFilters != null
&& !component.getPackageName().equals(getContext().getPackageName())
&& targetPackage.applicationInfo.targetSdkVersion >= TIRAMISU) {
// Check if the explicit intent matches filters on the target component for T+
boolean matchFound = false;
for (IntentFilter filter : componentFilters) {
if (matchIntentFilter(intent, filter) > 0) {
matchFound = true;
break;
}
}
if (!matchFound) {
Log.w(
TAG,
"Component "
+ componentInfo
+ " doesn't have required intent filters for "
+ intent);
return Collections.emptyList();
}
}
ResolveInfo resolveInfo = buildResolveInfo(componentInfo);
componentSetter.accept(resolveInfo, componentInfo);
return new ArrayList<>(Collections.singletonList(resolveInfo));
}
return Collections.emptyList();
} else {
List<ResolveInfo> resolveInfoList = new ArrayList<>();
Map<ComponentName, List<IntentFilter>> filtersForPackage =
mapForPackage(filters, intent.getPackage());
components:
for (Map.Entry<ComponentName, List<IntentFilter>> componentEntry :
filtersForPackage.entrySet()) {
ComponentName componentName = componentEntry.getKey();
for (IntentFilter filter : componentEntry.getValue()) {
int match = matchIntentFilter(intent, filter);
if (match > 0) {
PackageInfo packageInfo = packageInfos.get(componentName.getPackageName());
I[] componentInfoArray = componentsInPackage.apply(packageInfo);
if (componentInfoArray != null) {
for (I componentInfo : componentInfoArray) {
if (!componentInfo.name.equals(componentName.getClassName())) {
continue;
}
ResolveInfo resolveInfo = buildResolveInfo(componentInfo, filter);
resolveInfo.match = match;
componentSetter.accept(resolveInfo, componentInfo);
resolveInfoList.add(resolveInfo);
continue components;
}
}
}
}
}
return resolveInfoList;
}
}
}
/** Behaves as {@link #queryIntentActivities(Intent, int)} and currently ignores userId. */
@Implementation(minSdk = JELLY_BEAN_MR1)
protected List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent, int flags, int userId) {
return queryIntentActivities(intent, flags);
}
/** Returns true if intent has specified a specific component. */
private static boolean isExplicitIntent(Intent intent) {
return getComponentForIntent(intent) != null;
}
private static <T extends ComponentInfo> T findMatchingComponent(
ComponentName componentName, T[] components) {
if (components == null) {
return null;
}
for (T component : components) {
if (componentName.equals(new ComponentName(component.packageName, component.name))) {
return component;
}
}
return null;
}
private static ComponentName getComponentForIntent(Intent intent) {
ComponentName component = intent.getComponent();
if (component == null) {
if (intent.getSelector() != null) {
intent = intent.getSelector();
component = intent.getComponent();
}
}
return component;
}
private static ResolveInfo buildResolveInfo(ComponentInfo componentInfo) {
ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.resolvePackageName = componentInfo.applicationInfo.packageName;
resolveInfo.labelRes = componentInfo.labelRes;
resolveInfo.icon = componentInfo.icon;
resolveInfo.nonLocalizedLabel = componentInfo.nonLocalizedLabel;
return resolveInfo;
}
static ResolveInfo buildResolveInfo(ComponentInfo componentInfo, IntentFilter intentFilter) {
ResolveInfo info = buildResolveInfo(componentInfo);
info.isDefault = intentFilter.hasCategory("android.intent.category.DEFAULT");
info.filter = new IntentFilter(intentFilter);
info.priority = intentFilter.getPriority();
return info;
}
@Implementation
protected int checkPermission(String permName, String pkgName) {
PackageInfo permissionsInfo = getInternalMutablePackageInfo(pkgName);
if (permissionsInfo == null || permissionsInfo.requestedPermissions == null) {
return PackageManager.PERMISSION_DENIED;
}
String permission;
for (int i = 0; i < permissionsInfo.requestedPermissions.length; i++) {
permission = permissionsInfo.requestedPermissions[i];
if (permission != null && permission.equals(permName)) {
// The package requests this permission. Now check if it's been granted to the package.
if (isGrantedForBackwardsCompatibility(pkgName, permissionsInfo)) {
return PackageManager.PERMISSION_GRANTED;
}
if ((permissionsInfo.requestedPermissionsFlags[i] & REQUESTED_PERMISSION_GRANTED)
== REQUESTED_PERMISSION_GRANTED) {
return PackageManager.PERMISSION_GRANTED;
}
}
}
return PackageManager.PERMISSION_DENIED;
}
/**
* Returns whether a permission should be treated as granted to the package for backward
* compatibility reasons.
*
* <p>Before Robolectric 4.0 the ShadowPackageManager treated every requested permission as
* automatically granted. 4.0 changes this behavior, and only treats a permission as granted if
* PackageInfo.requestedPermissionFlags[permissionIndex] & REQUESTED_PERMISSION_GRANTED ==
* REQUESTED_PERMISSION_GRANTED which matches the real PackageManager's behavior.
*
* <p>Since many existing tests didn't set the requestedPermissionFlags on their {@code
* PackageInfo} objects, but assumed that all permissions are granted, we auto-grant all
* permissions if the requestedPermissionFlags is not set. If the requestedPermissionFlags is set,
* we assume that the test is configuring the permission grant state, and we don't override this
* setting.
*/
private boolean isGrantedForBackwardsCompatibility(String pkgName, PackageInfo permissionsInfo) {
// Note: it might be cleaner to auto-grant these permissions when the package is added to the
// PackageManager. But many existing tests modify the requested permissions _after_ adding the
// package to the PackageManager, without updating the requestedPermissionsFlags.
return permissionsInfo.requestedPermissionsFlags == null
// Robolectric uses the PackageParser to create the current test package's PackageInfo from
// the manifest XML. The parser populates the requestedPermissionsFlags, but doesn't grant
// the permissions. Several tests rely on the test package being granted all permissions, so
// we treat this as a special case.
|| pkgName.equals(RuntimeEnvironment.getApplication().getPackageName());
}
@Implementation
protected ActivityInfo getReceiverInfo(ComponentName component, int flags)
throws NameNotFoundException {
return getComponentInfo(
component,
flags,
packageInfo -> packageInfo.receivers,
resolveInfo -> resolveInfo.activityInfo,
ActivityInfo::new);
}
@Implementation
protected List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags) {
return this.queryIntentComponents(
intent,
flags,
(pkg) -> pkg.receivers,
receiverFilters,
(resolveInfo, activityInfo) -> resolveInfo.activityInfo = activityInfo,
(resolveInfo) -> resolveInfo.activityInfo,
ActivityInfo::new);
}
@Implementation(minSdk = TIRAMISU)
protected List<ResolveInfo> queryBroadcastReceivers(Object intent, @NonNull Object flags) {
return queryBroadcastReceivers((Intent) intent, (int) ((ResolveInfoFlags) flags).getValue());
}
private static int matchIntentFilter(Intent intent, IntentFilter intentFilter) {
return intentFilter.match(
intent.getAction(),
intent.getType(),
intent.getScheme(),
intent.getData(),
intent.getCategories(),
TAG);
}
@Implementation
protected ResolveInfo resolveService(Intent intent, int flags) {
List<ResolveInfo> candidates = queryIntentServices(intent, flags);
return candidates.isEmpty() ? null : candidates.get(0);
}
@Implementation
protected ServiceInfo getServiceInfo(ComponentName component, int flags)
throws NameNotFoundException {
return getComponentInfo(
component,
flags,
packageInfo -> packageInfo.services,
resolveInfo -> resolveInfo.serviceInfo,
ServiceInfo::new);
}
/**
* Modifies the component in place using.
*
* @throws NameNotFoundException when component is filtered out by a flag
*/
private void applyFlagsToComponentInfo(ComponentInfo componentInfo, long flags)
throws NameNotFoundException {
componentInfo.name = (componentInfo.name == null) ? "" : componentInfo.name;
ApplicationInfo applicationInfo = componentInfo.applicationInfo;
boolean isApplicationEnabled = true;
if (applicationInfo != null) {
if (applicationInfo.packageName == null) {
applicationInfo.packageName = componentInfo.packageName;
}
applyFlagsToApplicationInfo(componentInfo.applicationInfo, flags);
componentInfo.packageName = applicationInfo.packageName;
isApplicationEnabled = applicationInfo.enabled;
}
if ((flags & GET_META_DATA) == 0) {
componentInfo.metaData = null;
}
boolean isComponentEnabled = isComponentEnabled(componentInfo);
if ((flags & MATCH_ALL) != 0 && Build.VERSION.SDK_INT >= 23) {
return;
}
// Android don't override the enabled field of component with the actual value.
boolean isEnabledForFiltering =
isComponentEnabled && (Build.VERSION.SDK_INT >= 24 ? isApplicationEnabled : true);
if ((flags & MATCH_DISABLED_COMPONENTS) == 0 && !isEnabledForFiltering) {
throw new NameNotFoundException("Disabled component: " + componentInfo);
}
if (isFlagSet(flags, PackageManager.MATCH_SYSTEM_ONLY)) {
if (applicationInfo == null) {
// TODO: for backwards compatibility just skip filtering. In future should just remove
// invalid resolve infos from list
} else {
final int applicationFlags = applicationInfo.flags;
if ((applicationFlags & ApplicationInfo.FLAG_SYSTEM) != ApplicationInfo.FLAG_SYSTEM) {
throw new NameNotFoundException("Not system component: " + componentInfo);
}
}
}
if (!isFlagSet(flags, MATCH_UNINSTALLED_PACKAGES)
&& isValidComponentInfo(componentInfo)
&& hiddenPackages.contains(componentInfo.applicationInfo.packageName)) {
throw new NameNotFoundException("Uninstalled package: " + componentInfo);
}
}
@Implementation
protected Resources getResourcesForApplication(@NonNull ApplicationInfo applicationInfo)
throws PackageManager.NameNotFoundException {
synchronized (lock) {
if (getContext().getPackageName().equals(applicationInfo.packageName)) {
return getContext().getResources();
} else if (packageInfos.containsKey(applicationInfo.packageName)) {
Resources appResources = resources.get(applicationInfo.packageName);
if (appResources == null) {
appResources = new Resources(new AssetManager(), null, null);
resources.put(applicationInfo.packageName, appResources);
}
return appResources;
}
Resources resources = null;
if (RuntimeEnvironment.useLegacyResources()
&& (applicationInfo.publicSourceDir == null
|| !new File(applicationInfo.publicSourceDir).exists())) {
// In legacy mode, the underlying getResourcesForApplication implementation just returns an
// empty Resources instance in this case.
throw new NameNotFoundException(applicationInfo.packageName);
}
try {
resources =
reflector(ReflectorApplicationPackageManager.class, realObject)
.getResourcesForApplication(applicationInfo);
} catch (Exception ex) {
// handled below
}
if (resources == null) {
throw new NameNotFoundException(applicationInfo.packageName);
}
return resources;
}
}
@Implementation
protected List<ApplicationInfo> getInstalledApplications(int flags) {
return getInstalledApplications((long) flags);
}
@Implementation(minSdk = TIRAMISU)
protected List<ApplicationInfo> getInstalledApplications(Object flags) {
return getInstalledApplications(((ApplicationInfoFlags) flags).getValue());
}
private List<ApplicationInfo> getInstalledApplications(long flags) {
List<PackageInfo> packageInfos = getInstalledPackages(flags);
List<ApplicationInfo> result = new ArrayList<>(packageInfos.size());
for (PackageInfo packageInfo : packageInfos) {
if (packageInfo.applicationInfo != null) {
result.add(packageInfo.applicationInfo);
}
}
return result;
}
@Implementation
protected String getInstallerPackageName(String packageName) {
synchronized (lock) {
// In REALISTIC mode, throw exception if the package is not installed or installed but
// later uninstalled
if (ConfigurationRegistry.get(GetInstallerPackageNameMode.Mode.class) == REALISTIC
&& (!packageInstallerMap.containsKey(packageName)
|| !packageInfos.containsKey(packageName))) {
throw new IllegalArgumentException("Package is not installed: " + packageName);
} else if (!packageInstallerMap.containsKey(packageName)) {
Log.w(
TAG,
String.format(
"Call to getInstallerPackageName returns null for package: '%s'. Please run"
+ " setInstallerPackageName to set installer package name before making the"
+ " call.",
packageName));
}
return packageInstallerMap.get(packageName);
}
}
@Implementation(minSdk = R)
protected Object getInstallSourceInfo(String packageName) {
return (InstallSourceInfo) packageInstallSourceInfoMap.get(packageName);
}
@Implementation
protected PermissionInfo getPermissionInfo(String name, int flags) throws NameNotFoundException {
PermissionInfo permissionInfo = extraPermissions.get(name);
if (permissionInfo != null) {
return permissionInfo;
}
synchronized (lock) {
for (PackageInfo packageInfo : packageInfos.values()) {
if (packageInfo.permissions != null) {
for (PermissionInfo permission : packageInfo.permissions) {
if (name.equals(permission.name)) {
return createCopyPermissionInfo(permission, flags);
}
}
}
}
}
throw new NameNotFoundException(name);
}
@Implementation(minSdk = M)
protected boolean shouldShowRequestPermissionRationale(String permission) {
return permissionRationaleMap.containsKey(permission)
? permissionRationaleMap.get(permission)
: false;
}
@Implementation
protected FeatureInfo[] getSystemAvailableFeatures() {
return systemAvailableFeatures.isEmpty()
? null
: systemAvailableFeatures.toArray(new FeatureInfo[systemAvailableFeatures.size()]);
}
@Implementation
protected void verifyPendingInstall(int id, int verificationCode) {
if (verificationResults.containsKey(id)) {
throw new IllegalStateException("Multiple verifications for id=" + id);
}
verificationResults.put(id, verificationCode);
}
@Implementation(minSdk = JELLY_BEAN_MR1)
protected void extendVerificationTimeout(
int id, int verificationCodeAtTimeout, long millisecondsToDelay) {
verificationTimeoutExtension.put(id, millisecondsToDelay);
}
@Override
@Implementation(maxSdk = LOLLIPOP_MR1)
protected void freeStorageAndNotify(long freeStorageSize, IPackageDataObserver observer) {}
@Implementation(minSdk = M)
protected void freeStorageAndNotify(
String volumeUuid, long freeStorageSize, IPackageDataObserver observer) {}
@Implementation
protected void setInstallerPackageName(String targetPackage, String installerPackageName) {
packageInstallerMap.put(targetPackage, installerPackageName);
}
@Implementation(minSdk = KITKAT)
protected List<ResolveInfo> queryIntentContentProviders(Intent intent, int flags) {
return this.queryIntentComponents(
intent,
flags,
(pkg) -> pkg.providers,
providerFilters,
(resolveInfo, providerInfo) -> resolveInfo.providerInfo = providerInfo,
(resolveInfo) -> resolveInfo.providerInfo,
ProviderInfo::new);
}
@Implementation(minSdk = KITKAT)
protected List<ResolveInfo> queryIntentContentProvidersAsUser(
Intent intent, int flags, int userId) {
return Collections.emptyList();
}
@Implementation(minSdk = M)
protected String getPermissionControllerPackageName() {
return null;
}
@Implementation(maxSdk = JELLY_BEAN)
protected void getPackageSizeInfo(Object pkgName, Object observer) {
final PackageStats packageStats = packageStatsMap.get((String) pkgName);
new Handler(Looper.getMainLooper())
.post(
() -> {
try {
((IPackageStatsObserver) observer)
.onGetStatsCompleted(packageStats, packageStats != null);
} catch (RemoteException remoteException) {
remoteException.rethrowFromSystemServer();
}
});
}
@Implementation(minSdk = JELLY_BEAN_MR1, maxSdk = M)
protected void getPackageSizeInfo(Object pkgName, Object uid, final Object observer) {
final PackageStats packageStats = packageStatsMap.get((String) pkgName);
new Handler(Looper.getMainLooper())
.post(
() -> {
try {
((IPackageStatsObserver) observer)
.onGetStatsCompleted(packageStats, packageStats != null);
} catch (RemoteException remoteException) {
remoteException.rethrowFromSystemServer();
}
});
}
@Implementation(minSdk = N)
protected void getPackageSizeInfoAsUser(Object pkgName, Object uid, final Object observer) {
final PackageStats packageStats = packageStatsMap.get((String) pkgName);
new Handler(Looper.getMainLooper())
.post(
() -> {
try {
((IPackageStatsObserver) observer)
.onGetStatsCompleted(packageStats, packageStats != null);
} catch (RemoteException remoteException) {
remoteException.rethrowFromSystemServer();
}
});
}
@Override
@Implementation
protected void deletePackage(String packageName, IPackageDeleteObserver observer, int flags) {
super.deletePackage(packageName, observer, flags);
}
@Implementation
protected String[] currentToCanonicalPackageNames(String[] names) {
String[] out = new String[names.length];
for (int i = 0; i < names.length; i++) {
out[i] = currentToCanonicalNames.getOrDefault(names[i], names[i]);
}
return out;
}
@Implementation
protected String[] canonicalToCurrentPackageNames(String[] names) {
String[] out = new String[names.length];
for (int i = 0; i < names.length; i++) {
out[i] = canonicalToCurrentNames.getOrDefault(names[i], names[i]);
}
return out;
}
@Implementation
protected boolean isSafeMode() {
return safeMode;
}
@Implementation
protected Drawable getApplicationIcon(String packageName) throws NameNotFoundException {
return applicationIcons.get(packageName);
}
@Implementation
protected Drawable getApplicationIcon(ApplicationInfo info) throws NameNotFoundException {
return getApplicationIcon(info.packageName);
}
@Implementation(minSdk = LOLLIPOP)
protected Drawable getUserBadgeForDensity(UserHandle userHandle, int i) {
return null;
}
@Implementation
protected int checkSignatures(String pkg1, String pkg2) {
try {
PackageInfo packageInfo1 = getPackageInfo(pkg1, GET_SIGNATURES);
PackageInfo packageInfo2 = getPackageInfo(pkg2, GET_SIGNATURES);
return compareSignature(packageInfo1.signatures, packageInfo2.signatures);
} catch (NameNotFoundException e) {
return SIGNATURE_UNKNOWN_PACKAGE;
}
}
@Implementation
protected int checkSignatures(int uid1, int uid2) {
return 0;
}
@Implementation
protected List<PermissionInfo> queryPermissionsByGroup(String group, int flags)
throws NameNotFoundException {
List<PermissionInfo> result = new ArrayList<>();
for (PermissionInfo permissionInfo : extraPermissions.values()) {
if (Objects.equals(permissionInfo.group, group)) {
result.add(permissionInfo);
}
}
synchronized (lock) {
for (PackageInfo packageInfo : packageInfos.values()) {
if (packageInfo.permissions != null) {
for (PermissionInfo permission : packageInfo.permissions) {
if (Objects.equals(group, permission.group)) {
result.add(createCopyPermissionInfo(permission, flags));
}
}
}
}
}
if (result.isEmpty()) {
throw new NameNotFoundException(group);
}
return result;
}
private static PermissionInfo createCopyPermissionInfo(PermissionInfo src, int flags) {
PermissionInfo matchedPermission = new PermissionInfo(src);
if ((flags & GET_META_DATA) != GET_META_DATA) {
matchedPermission.metaData = null;
}
return matchedPermission;
}
private Intent getLaunchIntentForPackage(String packageName, String launcherCategory) {
Intent intentToResolve = new Intent(Intent.ACTION_MAIN);
intentToResolve.addCategory(Intent.CATEGORY_INFO);
intentToResolve.setPackage(packageName);
List<ResolveInfo> ris = queryIntentActivities(intentToResolve, 0);
if (ris == null || ris.isEmpty()) {
intentToResolve.removeCategory(Intent.CATEGORY_INFO);
intentToResolve.addCategory(launcherCategory);
intentToResolve.setPackage(packageName);
ris = queryIntentActivities(intentToResolve, 0);
}
if (ris == null || ris.isEmpty()) {
return null;
}
Intent intent = new Intent(intentToResolve);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setClassName(packageName, ris.get(0).activityInfo.name);
return intent;
}
@Implementation
protected Intent getLaunchIntentForPackage(String packageName) {
return getLaunchIntentForPackage(packageName, Intent.CATEGORY_LAUNCHER);
}
@Implementation(minSdk = LOLLIPOP)
protected Intent getLeanbackLaunchIntentForPackage(String packageName) {
return getLaunchIntentForPackage(packageName, Intent.CATEGORY_LEANBACK_LAUNCHER);
}
/**
* In Android T, the type of {@code flags} changed from {@code int} to {@link PackageInfoFlags}
*/
@Implementation(minSdk = N)
protected Object getPackageInfoAsUser(Object packageName, Object flagsObject, Object userId)
throws NameNotFoundException {
int flags;
if (RuntimeEnvironment.getApiLevel() >= TIRAMISU) {
flags = (int) ((PackageInfoFlags) flagsObject).getValue();
} else {
flags = (int) flagsObject;
}
return getPackageInfo((String) packageName, flags);
}
@Implementation
protected int[] getPackageGids(String packageName) throws NameNotFoundException {
return new int[0];
}
@Implementation(minSdk = N)
protected int[] getPackageGids(String packageName, int flags) throws NameNotFoundException {
return null;
}
@Implementation(minSdk = JELLY_BEAN_MR2)
protected int getPackageUid(String packageName, int flags) throws NameNotFoundException {
Integer uid = uidForPackage.get(packageName);
if (uid == null) {
throw new NameNotFoundException(packageName);
}
return uid;
}
@Implementation(minSdk = TIRAMISU)
protected Object getPackageUid(Object packageName, Object flags) throws NameNotFoundException {
return getPackageUid((String) packageName, (int) ((PackageInfoFlags) flags).getValue());
}
@Implementation(minSdk = N)
protected int getPackageUidAsUser(String packageName, int userId) throws NameNotFoundException {
return 0;
}
@Implementation(minSdk = N)
protected int getPackageUidAsUser(String packageName, int flags, int userId)
throws NameNotFoundException {
return 0;
}
/**
* @see ShadowPackageManager#addPermissionGroupInfo(android.content.pm.PermissionGroupInfo)
*/
@Implementation
protected PermissionGroupInfo getPermissionGroupInfo(String name, int flags)
throws NameNotFoundException {
if (permissionGroups.containsKey(name)) {
return new PermissionGroupInfo(permissionGroups.get(name));
}
throw new NameNotFoundException(name);
}
/**
* @see ShadowPackageManager#addPermissionGroupInfo(android.content.pm.PermissionGroupInfo)
*/
@Implementation
protected List<PermissionGroupInfo> getAllPermissionGroups(int flags) {
ArrayList<PermissionGroupInfo> allPermissionGroups = new ArrayList<PermissionGroupInfo>();
for (PermissionGroupInfo permissionGroupInfo : permissionGroups.values()) {
allPermissionGroups.add(new PermissionGroupInfo(permissionGroupInfo));
}
return allPermissionGroups;
}
@Implementation
protected ApplicationInfo getApplicationInfo(String packageName, int flags)
throws NameNotFoundException {
PackageInfo packageInfo = getPackageInfo(packageName, flags);
if (packageInfo.applicationInfo == null) {
throw new NameNotFoundException("Package found but without application info");
}
// Maybe query app infos from overridden resolveInfo as well?
return packageInfo.applicationInfo;
}
private void applyFlagsToApplicationInfo(@Nullable ApplicationInfo appInfo, long flags)
throws NameNotFoundException {
if (appInfo == null) {
return;
}
String packageName = appInfo.packageName;
Integer stateOverride = applicationEnabledSettingMap.get(packageName);
if (stateOverride == null) {
stateOverride = COMPONENT_ENABLED_STATE_DEFAULT;
}
appInfo.enabled =
(appInfo.enabled && stateOverride == COMPONENT_ENABLED_STATE_DEFAULT)
|| stateOverride == COMPONENT_ENABLED_STATE_ENABLED;
synchronized (lock) {
if (deletedPackages.contains(packageName)) {
appInfo.flags &= ~FLAG_INSTALLED;
}
}
if ((flags & MATCH_ALL) != 0 && Build.VERSION.SDK_INT >= 23) {
return;
}
if ((flags & MATCH_UNINSTALLED_PACKAGES) == 0 && (appInfo.flags & FLAG_INSTALLED) == 0) {
throw new NameNotFoundException("Package not installed: " + packageName);
}
if ((flags & MATCH_UNINSTALLED_PACKAGES) == 0 && hiddenPackages.contains(packageName)) {
throw new NameNotFoundException("Package hidden: " + packageName);
}
}
/**
* Returns all the values added via {@link
* ShadowPackageManager#addSystemSharedLibraryName(String)}.
*/
@Implementation
protected String[] getSystemSharedLibraryNames() {
return systemSharedLibraryNames.toArray(new String[systemSharedLibraryNames.size()]);
}
@Implementation(minSdk = N)
protected @NonNull String getServicesSystemSharedLibraryPackageName() {
return null;
}
@Implementation(minSdk = N)
protected @NonNull String getSharedSystemSharedLibraryPackageName() {
return "";
}
@Implementation(minSdk = N)
protected boolean hasSystemFeature(String name, int version) {
return false;
}
@Implementation(minSdk = M)
protected boolean isPermissionRevokedByPolicy(String permName, String pkgName) {
return false;
}
@Implementation
protected boolean addPermission(PermissionInfo info) {
return false;
}
@Implementation
protected boolean addPermissionAsync(PermissionInfo info) {
return false;
}
@Implementation
protected void removePermission(String name) {}
@Implementation(minSdk = M)
protected void grantRuntimePermission(
String packageName, String permissionName, UserHandle user) {
Integer uid;
synchronized (lock) {
if (!packageInfos.containsKey(packageName)) {
throw new SecurityException("Package not found: " + packageName);
}
PackageInfo packageInfo = packageInfos.get(packageName);
checkPermissionGrantStateInitialized(packageInfo);
int permissionIndex = getPermissionIndex(packageInfo, permissionName);
if (permissionIndex < 0) {
throw new SecurityException(
"Permission " + permissionName + " not requested by package " + packageName);
}
packageInfo.requestedPermissionsFlags[permissionIndex] |= REQUESTED_PERMISSION_GRANTED;
uid = uidForPackage.get(packageName);
}
if (RuntimeEnvironment.getApiLevel() >= VERSION_CODES.M && uid != null) {
for (Object listener : permissionListeners) {
((OnPermissionsChangedListener) listener).onPermissionsChanged(uid);
}
}
}
@Implementation(minSdk = M)
protected void revokeRuntimePermission(
String packageName, String permissionName, UserHandle user) {
Integer uid;
synchronized (lock) {
if (!packageInfos.containsKey(packageName)) {
throw new SecurityException("Package not found: " + packageName);
}
PackageInfo packageInfo = packageInfos.get(packageName);
checkPermissionGrantStateInitialized(packageInfo);
int permissionIndex = getPermissionIndex(packageInfo, permissionName);
if (permissionIndex < 0) {
throw new SecurityException(
"Permission " + permissionName + " not requested by package " + packageName);
}
packageInfo.requestedPermissionsFlags[permissionIndex] &= ~REQUESTED_PERMISSION_GRANTED;
uid = uidForPackage.get(packageName);
}
if (RuntimeEnvironment.getApiLevel() >= VERSION_CODES.M && uid != null) {
for (Object listener : permissionListeners) {
((OnPermissionsChangedListener) listener).onPermissionsChanged(uid);
}
}
}
private void checkPermissionGrantStateInitialized(PackageInfo packageInfo) {
if (packageInfo.requestedPermissionsFlags == null) {
// In the real OS this would never be null, but tests don't necessarily initialize this
// structure.
throw new SecurityException(
"Permission grant state (PackageInfo.requestedPermissionFlags) "
+ "is null. This operation requires this variable to be initialized.");
}
}
/**
* Returns the index of the given permission in the PackageInfo.requestedPermissions array, or -1
* if it's not found.
*/
private int getPermissionIndex(PackageInfo packageInfo, String permissionName) {
if (packageInfo.requestedPermissions != null) {
for (int i = 0; i < packageInfo.requestedPermissions.length; i++) {
if (permissionName.equals(packageInfo.requestedPermissions[i])) {
return i;
}
}
}
return -1;
}
/**
* This method differs from the real implementation in that we only return the permission flags
* that were added via updatePermissionFlags, and do not perform any verification of permissions,
* packages or users.
*/
@Implementation(minSdk = M)
protected int getPermissionFlags(String permissionName, String packageName, UserHandle user) {
if (permissionFlags.containsKey(packageName)) {
return permissionFlags.get(packageName).getOrDefault(permissionName, /* defaultValue= */ 0);
}
return 0;
}
/**
* This method differs from the real implementation in that no permission checking or package
* existent checks are performed here.
*/
@Implementation(minSdk = M)
protected void updatePermissionFlags(
String permissionName,
String packageName,
@PackageManager.PermissionFlags int flagMask,
@PackageManager.PermissionFlags int flagValues,
UserHandle user) {
if (!permissionFlags.containsKey(packageName)) {
permissionFlags.put(packageName, new HashMap<String, Integer>());
}
int existingFlags =
permissionFlags.get(packageName).getOrDefault(permissionName, /* defaultValue= */ 0);
int flagsToKeep = ~flagMask & existingFlags;
int flagsToChange = flagMask & flagValues;
int newFlags = flagsToKeep | flagsToChange;
permissionFlags.get(packageName).put(permissionName, newFlags);
}
@Implementation
protected int getUidForSharedUser(String sharedUserName) throws NameNotFoundException {
return 0;
}
@Implementation(minSdk = N)
protected List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {
return null;
}
@Implementation(minSdk = JELLY_BEAN_MR2)
protected List<PackageInfo> getPackagesHoldingPermissions(String[] permissions, int flags) {
synchronized (lock) {
List<PackageInfo> packageInfosWithPermissions = new ArrayList<>();
for (PackageInfo packageInfo : packageInfos.values()) {
for (String permission : permissions) {
int permissionIndex = getPermissionIndex(packageInfo, permission);
if (permissionIndex >= 0) {
packageInfosWithPermissions.add(packageInfo);
break;
}
}
}
return packageInfosWithPermissions;
}
}
@Implementation(minSdk = S)
public void getGroupOfPlatformPermission(
String permissionName, Executor executor, Consumer<String> callback) {
String permissionGroup = null;
try {
PermissionInfo permissionInfo =
getPermissionInfo(permissionName, PackageManager.GET_META_DATA);
permissionGroup = permissionInfo.group;
} catch (NameNotFoundException ignored) {
// fall through
}
final String finalPermissionGroup = permissionGroup;
executor.execute(() -> callback.accept(finalPermissionGroup));
}
/** Behaves as {@link #resolveActivity(Intent, int)} and currently ignores userId. */
@Implementation(minSdk = JELLY_BEAN_MR1)
protected ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId) {
return resolveActivity(intent, flags);
}
@Implementation(minSdk = TIRAMISU)
protected ResolveInfo resolveActivityAsUser(Object intent, Object flags, Object userId) {
return resolveActivity((Intent) intent, (int) ((ResolveInfoFlags) flags).getValue());
}
@Implementation(minSdk = TIRAMISU)
protected ResolveInfo resolveServiceAsUser(Object intent, Object flags, Object userId) {
return resolveService((Intent) intent, (int) ((ResolveInfoFlags) flags).getValue());
}
@Implementation
protected List<ResolveInfo> queryIntentActivityOptions(
ComponentName caller, Intent[] specifics, Intent intent, int flags) {
return null;
}
@Implementation(minSdk = N)
protected List<ResolveInfo> queryBroadcastReceiversAsUser(Intent intent, int flags, int userId) {
return null;
}
@Implementation
protected List<ProviderInfo> queryContentProviders(String processName, int uid, int flags) {
return null;
}
@Implementation
protected InstrumentationInfo getInstrumentationInfo(ComponentName className, int flags)
throws NameNotFoundException {
return null;
}
@Implementation
protected List<InstrumentationInfo> queryInstrumentation(String targetPackage, int flags) {
return null;
}
@Nullable
@Implementation
protected Drawable getDrawable(
String packageName, @DrawableRes int resId, @Nullable ApplicationInfo appInfo) {
Drawable result = drawables.get(new Pair<>(packageName, resId));
if (result != null) {
return result;
}
return reflector(ReflectorApplicationPackageManager.class, realObject)
.getDrawable(packageName, resId, appInfo);
}
/**
* Returns a user stored String resource with {@code resId} corresponding to {@code packageName}.
* User can store this String via {@link #addStringResource(String, int, String)}.
*
* <p>Real method is called if the user has not stored a String corresponding to {@code resId} and
* {@code packageName}.
*/
@Nullable
@Implementation
protected CharSequence getText(String packageName, int resId, ApplicationInfo appInfo) {
if (stringResources.containsKey(packageName)
&& stringResources.get(packageName).containsKey(resId)) {
return stringResources.get(packageName).get(resId);
}
return reflector(ReflectorApplicationPackageManager.class, realObject)
.getText(packageName, resId, appInfo);
}
@Implementation
protected Drawable getActivityIcon(ComponentName activityName) throws NameNotFoundException {
Drawable result = drawableList.get(activityName);
if (result != null) {
return result;
}
return reflector(ReflectorApplicationPackageManager.class, realObject)
.getActivityIcon(activityName);
}
@Implementation
protected Drawable getDefaultActivityIcon() {
return Resources.getSystem().getDrawable(com.android.internal.R.drawable.sym_def_app_icon);
}
@Implementation
protected Resources getResourcesForActivity(ComponentName activityName)
throws NameNotFoundException {
return getResourcesForApplication(activityName.getPackageName());
}
@Implementation
protected Resources getResourcesForApplication(String appPackageName)
throws NameNotFoundException {
synchronized (lock) {
if (getContext().getPackageName().equals(appPackageName)) {
return getContext().getResources();
} else if (packageInfos.containsKey(appPackageName)) {
Resources appResources = resources.get(appPackageName);
if (appResources == null) {
appResources = new Resources(new AssetManager(), null, null);
resources.put(appPackageName, appResources);
}
return appResources;
}
throw new NameNotFoundException(appPackageName);
}
}
@Implementation(minSdk = JELLY_BEAN_MR1)
protected Resources getResourcesForApplicationAsUser(String appPackageName, int userId)
throws NameNotFoundException {
return null;
}
@Implementation(minSdk = M)
protected void addOnPermissionsChangeListener(Object listener) {
permissionListeners.add(listener);
}
@Implementation(minSdk = M)
protected void removeOnPermissionsChangeListener(Object listener) {
permissionListeners.remove(listener);
}
@Implementation(maxSdk = O_MR1)
protected void installPackage(
Object packageURI, Object observer, Object flags, Object installerPackageName) {}
@Implementation(minSdk = JELLY_BEAN_MR1)
protected int installExistingPackage(String packageName) throws NameNotFoundException {
return 0;
}
@Implementation(minSdk = N)
protected int installExistingPackageAsUser(String packageName, int userId)
throws NameNotFoundException {
return 0;
}
@Implementation(minSdk = M)
protected void verifyIntentFilter(int id, int verificationCode, List<String> failedDomains) {}
@Implementation(minSdk = N)
protected int getIntentVerificationStatusAsUser(String packageName, int userId) {
return 0;
}
@Implementation(minSdk = N)
protected boolean updateIntentVerificationStatusAsUser(
String packageName, int status, int userId) {
return false;
}
@Implementation(minSdk = M)
protected List<IntentFilterVerificationInfo> getIntentFilterVerifications(String packageName) {
return null;
}
@Implementation(minSdk = M)
protected List<IntentFilter> getAllIntentFilters(String packageName) {
return null;
}
@Implementation(minSdk = N)
protected String getDefaultBrowserPackageNameAsUser(int userId) {
return null;
}
@Implementation(minSdk = N)
protected boolean setDefaultBrowserPackageNameAsUser(String packageName, int userId) {
return false;
}
@Implementation(minSdk = M)
protected int getMoveStatus(int moveId) {
return 0;
}
@Implementation(minSdk = M)
protected void registerMoveCallback(Object callback, Object handler) {}
@Implementation(minSdk = M)
protected void unregisterMoveCallback(Object callback) {}
@Implementation(minSdk = M)
protected Object movePackage(Object packageName, Object vol) {
return 0;
}
@Implementation(minSdk = M)
protected Object getPackageCurrentVolume(Object app) {
return null;
}
@Implementation(minSdk = M)
protected List<VolumeInfo> getPackageCandidateVolumes(ApplicationInfo app) {
return null;
}
@Implementation(minSdk = M)
protected Object movePrimaryStorage(Object vol) {
return 0;
}
@Implementation(minSdk = M)
protected @Nullable Object getPrimaryStorageCurrentVolume() {
return null;
}
@Implementation(minSdk = M)
protected @NonNull List<VolumeInfo> getPrimaryStorageCandidateVolumes() {
return null;
}
@Implementation(minSdk = N)
protected void deletePackageAsUser(
String packageName, IPackageDeleteObserver observer, int flags, int userId) {}
@Implementation
protected void clearApplicationUserData(String packageName, IPackageDataObserver observer) {
clearedApplicationUserDataPackages.add(packageName);
}
@Implementation
protected void deleteApplicationCacheFiles(String packageName, IPackageDataObserver observer) {}
@Implementation(minSdk = N)
protected void deleteApplicationCacheFilesAsUser(
String packageName, int userId, IPackageDataObserver observer) {}
@Implementation(minSdk = M)
protected void freeStorage(String volumeUuid, long freeStorageSize, IntentSender pi) {}
@Implementation(minSdk = N, maxSdk = O_MR1)
protected String[] setPackagesSuspendedAsUser(
String[] packageNames, boolean suspended, int userId) {
return null;
}
@Implementation(minSdk = N)
protected boolean isPackageSuspendedForUser(String packageName, int userId) {
return false;
}
@Implementation
protected void addPackageToPreferred(String packageName) {}
@Implementation
protected void removePackageFromPreferred(String packageName) {}
@Implementation
protected List<PackageInfo> getPreferredPackages(int flags) {
return null;
}
@Implementation
public void addPreferredActivity(
IntentFilter filter, int match, ComponentName[] set, ComponentName activity) {
addPreferredActivityInternal(filter, activity, preferredActivities);
}
@Implementation
protected void replacePreferredActivity(
IntentFilter filter, int match, ComponentName[] set, ComponentName activity) {
addPreferredActivity(filter, match, set, activity);
}
@Implementation
public int getPreferredActivities(
List<IntentFilter> outFilters, List<ComponentName> outActivities, String packageName) {
return getPreferredActivitiesInternal(
outFilters, outActivities, packageName, preferredActivities);
}
@Implementation
protected void clearPackagePreferredActivities(String packageName) {
clearPackagePreferredActivitiesInternal(packageName, preferredActivities);
}
@Implementation(minSdk = KITKAT)
protected ComponentName getHomeActivities(List<ResolveInfo> outActivities) {
return null;
}
@Implementation(minSdk = N)
protected void flushPackageRestrictionsAsUser(int userId) {}
@Implementation(minSdk = LOLLIPOP)
protected boolean setApplicationHiddenSettingAsUser(
String packageName, boolean hidden, UserHandle user) {
synchronized (lock) {
// Note that this ignores the UserHandle parameter
if (!packageInfos.containsKey(packageName)) {
// Package doesn't exist
return false;
}
if (hidden) {
hiddenPackages.add(packageName);
} else {
hiddenPackages.remove(packageName);
}
return true;
}
}
@Implementation(minSdk = LOLLIPOP)
protected boolean getApplicationHiddenSettingAsUser(String packageName, UserHandle user) {
// Note that this ignores the UserHandle parameter
synchronized (lock) {
if (!packageInfos.containsKey(packageName)) {
// Match Android behaviour of returning true if package isn't found
return true;
}
return hiddenPackages.contains(packageName);
}
}
@Implementation
protected VerifierDeviceIdentity getVerifierDeviceIdentity() {
return null;
}
@Implementation(minSdk = LOLLIPOP_MR1)
protected boolean isUpgrade() {
return false;
}
@Implementation(minSdk = LOLLIPOP)
protected boolean isPackageAvailable(String packageName) {
return false;
}
@Implementation(minSdk = LOLLIPOP)
protected void addCrossProfileIntentFilter(
IntentFilter filter, int sourceUserId, int targetUserId, int flags) {}
@Implementation(minSdk = LOLLIPOP)
protected void clearCrossProfileIntentFilters(int sourceUserId) {}
/**
* Gets the unbadged icon based on the values set by {@link
* ShadowPackageManager#setUnbadgedApplicationIcon} or returns null if nothing has been set.
*/
@Implementation(minSdk = LOLLIPOP_MR1)
protected Drawable loadUnbadgedItemIcon(PackageItemInfo itemInfo, ApplicationInfo appInfo) {
Drawable result = unbadgedApplicationIcons.get(itemInfo.packageName);
if (result != null) {
return result;
}
return reflector(ReflectorApplicationPackageManager.class, realObject)
.loadUnbadgedItemIcon(itemInfo, appInfo);
}
/**
* Adds a profile badge to the icon.
*
* <p>This implementation just returns the unbadged icon, as some default implementations add an
* internal resource to the icon that is unavailable to Robolectric.
*/
@Implementation(minSdk = LOLLIPOP)
protected Drawable getUserBadgedIcon(Drawable icon, UserHandle user) {
return icon;
}
@Implementation(minSdk = O)
protected boolean canRequestPackageInstalls() {
return canRequestPackageInstalls;
}
@Implementation(minSdk = O)
protected Object getChangedPackages(int sequenceNumber) {
if (sequenceNumber < 0 || sequenceNumberChangedPackagesMap.get(sequenceNumber).isEmpty()) {
return null;
}
return new ChangedPackages(
sequenceNumber + 1, new ArrayList<>(sequenceNumberChangedPackagesMap.get(sequenceNumber)));
}
@Implementation(minSdk = P)
public String getSystemTextClassifierPackageName() {
return "";
}
@Implementation(minSdk = P)
@HiddenApi
protected String[] setPackagesSuspended(
String[] packageNames,
boolean suspended,
PersistableBundle appExtras,
PersistableBundle launcherExtras,
String dialogMessage) {
return setPackagesSuspended(
packageNames, suspended, appExtras, launcherExtras, dialogMessage, /* dialogInfo= */ null);
}
@Implementation(minSdk = Q)
@HiddenApi
protected /* String[] */ Object setPackagesSuspended(
/* String[] */ Object packageNames,
/* boolean */ Object suspended,
/* PersistableBundle */ Object appExtras,
/* PersistableBundle */ Object launcherExtras,
/* SuspendDialogInfo */ Object dialogInfo) {
return setPackagesSuspended(
(String[]) packageNames,
(boolean) suspended,
(PersistableBundle) appExtras,
(PersistableBundle) launcherExtras,
/* dialogMessage= */ null,
dialogInfo);
}
@Implementation(minSdk = R)
protected boolean isAutoRevokeWhitelisted() {
return whitelisted;
}
/**
* Sets {@code packageNames} suspension status to {@code suspended} in the package manager.
*
* <p>At least one of {@code dialogMessage} and {@code dialogInfo} should be null.
*/
private String[] setPackagesSuspended(
String[] packageNames,
boolean suspended,
PersistableBundle appExtras,
PersistableBundle launcherExtras,
String dialogMessage,
Object dialogInfo) {
if (hasProfileOwnerOrDeviceOwnerOnCurrentUser()
&& (VERSION.SDK_INT < VERSION_CODES.Q
|| !isCurrentApplicationProfileOwnerOrDeviceOwner())) {
throw new UnsupportedOperationException();
}
ArrayList<String> unupdatedPackages = new ArrayList<>();
for (String packageName : packageNames) {
if (!canSuspendPackage(packageName)) {
unupdatedPackages.add(packageName);
continue;
}
PackageSetting setting = packageSettings.get(packageName);
if (setting == null) {
unupdatedPackages.add(packageName);
continue;
}
setting.setSuspended(suspended, dialogMessage, dialogInfo, appExtras, launcherExtras);
}
return unupdatedPackages.toArray(new String[0]);
}
/** Returns whether the current user profile has a profile owner or a device owner. */
private boolean isCurrentApplicationProfileOwnerOrDeviceOwner() {
String currentApplication = getContext().getPackageName();
DevicePolicyManager devicePolicyManager =
(DevicePolicyManager) getContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
return devicePolicyManager.isProfileOwnerApp(currentApplication)
|| devicePolicyManager.isDeviceOwnerApp(currentApplication);
}
/** Returns whether the current user profile has a profile owner or a device owner. */
private boolean hasProfileOwnerOrDeviceOwnerOnCurrentUser() {
DevicePolicyManager devicePolicyManager =
(DevicePolicyManager) getContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
return devicePolicyManager.getProfileOwner() != null
|| (UserHandle.of(UserHandle.myUserId()).isSystem()
&& devicePolicyManager.getDeviceOwner() != null);
}
private boolean canSuspendPackage(String packageName) {
// This code approximately mirrors PackageManagerService#canSuspendPackageForUserLocked.
return !packageName.equals(getContext().getPackageName())
&& !isPackageDeviceAdmin(packageName)
&& !isPackageActiveLauncher(packageName)
&& !isPackageRequiredInstaller(packageName)
&& !isPackageRequiredUninstaller(packageName)
&& !isPackageRequiredVerifier(packageName)
&& !isPackageDefaultDialer(packageName)
&& !packageName.equals(PLATFORM_PACKAGE_NAME);
}
private boolean isPackageDeviceAdmin(String packageName) {
DevicePolicyManager devicePolicyManager =
(DevicePolicyManager) getContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
// Strictly speaking, this should be devicePolicyManager.getDeviceOwnerComponentOnAnyUser(),
// but that method is currently not shadowed.
return packageName.equals(devicePolicyManager.getDeviceOwner());
}
private boolean isPackageActiveLauncher(String packageName) {
Intent intent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME);
ResolveInfo info = resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
return info != null && packageName.equals(info.activityInfo.packageName);
}
private boolean isPackageRequiredInstaller(String packageName) {
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE);
ResolveInfo info =
resolveActivity(
intent,
PackageManager.MATCH_SYSTEM_ONLY
| PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
return info != null && packageName.equals(info.activityInfo.packageName);
}
private boolean isPackageRequiredUninstaller(String packageName) {
final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setData(Uri.fromParts(PACKAGE_SCHEME, "foo.bar", null));
ResolveInfo info =
resolveActivity(
intent,
PackageManager.MATCH_SYSTEM_ONLY
| PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
return info != null && packageName.equals(info.activityInfo.packageName);
}
private boolean isPackageRequiredVerifier(String packageName) {
final Intent intent = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
List<ResolveInfo> infos =
queryBroadcastReceivers(
intent,
PackageManager.MATCH_SYSTEM_ONLY
| PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
if (infos != null) {
for (ResolveInfo info : infos) {
if (packageName.equals(info.activityInfo.packageName)) {
return true;
}
}
}
return false;
}
private boolean isPackageDefaultDialer(String packageName) {
TelecomManager telecomManager =
(TelecomManager) getContext().getSystemService(Context.TELECOM_SERVICE);
return packageName.equals(telecomManager.getDefaultDialerPackage());
}
@HiddenApi
@Implementation(minSdk = Q)
@RequiresPermission(permission.SUSPEND_APPS)
protected String[] getUnsuspendablePackages(String[] packageNames) {
checkNotNull(packageNames, "packageNames cannot be null");
if (getContext().checkSelfPermission(permission.SUSPEND_APPS)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Current process does not have " + permission.SUSPEND_APPS);
}
ArrayList<String> unsuspendablePackages = new ArrayList<>();
for (String packageName : packageNames) {
if (!canSuspendPackage(packageName)) {
unsuspendablePackages.add(packageName);
}
}
return unsuspendablePackages.toArray(new String[0]);
}
@HiddenApi
@Implementation(minSdk = P)
protected boolean isPackageSuspended(String packageName) throws NameNotFoundException {
PackageSetting setting = packageSettings.get(packageName);
if (setting == null) {
throw new NameNotFoundException(packageName);
}
return setting.isSuspended();
}
@Implementation(minSdk = O)
protected boolean isInstantApp(String packageName) {
synchronized (lock) {
PackageInfo pi = packageInfos.get(packageName);
if (pi == null) {
return false;
}
ApplicationInfo ai = pi.applicationInfo;
if (ai == null) {
return false;
}
return ai.isInstantApp();
}
}
@HiddenApi
@Implementation(minSdk = Q)
protected String[] setDistractingPackageRestrictions(String[] packages, int restrictionFlags) {
for (String pkg : packages) {
distractingPackageRestrictions.put(pkg, restrictionFlags);
}
return new String[0];
}
private Context getContext() {
return reflector(ReflectorApplicationPackageManager.class, realObject).getContext();
}
/** Stub that will always throw. */
@Implementation(minSdk = S)
protected Object /* PackageManager.Property */ getProperty(
String propertyName, String packageName) throws NameNotFoundException {
// TODO: in future read this value from parsed manifest
throw new NameNotFoundException("unsupported");
}
/** Stub that will always throw. */
@Implementation(minSdk = S)
protected Object /* PackageManager.Property */ getProperty(
String propertyName, ComponentName name) throws NameNotFoundException {
// TODO: in future read this value from parsed manifest
throw new NameNotFoundException("unsupported");
}
/** Reflector interface for {@link ApplicationPackageManager}'s internals. */
@ForType(ApplicationPackageManager.class)
private interface ReflectorApplicationPackageManager {
@Direct
Resources getResourcesForApplication(@NonNull ApplicationInfo applicationInfo);
@Direct
Drawable getDrawable(
String packageName, @DrawableRes int resId, @Nullable ApplicationInfo appInfo);
@Direct
CharSequence getText(String packageName, @StringRes int resId, ApplicationInfo appInfo);
@Direct
Drawable getActivityIcon(ComponentName activityName);
@Direct
Drawable loadUnbadgedItemIcon(PackageItemInfo itemInfo, ApplicationInfo appInfo);
@Direct
PackageInfo getPackageArchiveInfo(String archiveFilePath, int flags);
@Accessor("mContext")
Context getContext();
}
/** Returns the list of package names that were requested to be cleared. */
public List<String> getClearedApplicationUserDataPackages() {
return Collections.unmodifiableList(clearedApplicationUserDataPackages);
}
}