blob: 821027becc8aacc33ff7c5981d828564fe62da51 [file] [log] [blame]
package org.robolectric.shadows;
import static android.content.IntentFilter.MATCH_CATEGORY_MASK;
import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.GET_META_DATA;
import static android.content.pm.PackageManager.GET_SIGNATURES;
import static android.content.pm.PackageManager.MATCH_ALL;
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 org.robolectric.shadow.api.Shadow.invokeConstructor;
import static org.robolectric.util.ReflectionHelpers.ClassParameter.from;
import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
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.IPackageManager;
import android.content.pm.IPackageStatsObserver;
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.NameNotFoundException;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.Activity;
import android.content.pm.PackageParser.Component;
import android.content.pm.PackageParser.Package;
import android.content.pm.PackageParser.PermissionGroup;
import android.content.pm.PackageParser.Provider;
import android.content.pm.PackageParser.Service;
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.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.Pair;
import com.google.common.base.Function;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Set;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.HiddenApi;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.RealObject;
import org.robolectric.shadow.api.Shadow;
@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 Context context;
@Implementation
protected void __constructor__(Object contextImpl, Object pm) {
try {
invokeConstructor(
ApplicationPackageManager.class,
realObject,
from(Class.forName(ShadowContextImpl.CLASS_NAME), contextImpl),
from(IPackageManager.class, pm));
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
context = (Context) contextImpl;
}
@Implementation
public List<PackageInfo> getInstalledPackages(int flags) {
List<PackageInfo> result = new ArrayList<>();
for (PackageInfo packageInfo : packageInfos.values()) {
String packageName = packageInfo.packageName;
if (applicationEnabledSettingMap.get(packageName) == COMPONENT_ENABLED_STATE_DISABLED
&& (flags & MATCH_UNINSTALLED_PACKAGES) != MATCH_UNINSTALLED_PACKAGES
&& (flags & MATCH_DISABLED_COMPONENTS) != MATCH_DISABLED_COMPONENTS) {
continue;
}
if (hiddenPackages.contains(packageName) && !isFlagSet(flags, MATCH_UNINSTALLED_PACKAGES)) {
continue;
}
result.add(newPackageInfo(packageInfo));
}
return result;
}
@Implementation
protected ActivityInfo getActivityInfo(ComponentName component, int flags)
throws NameNotFoundException {
String activityName = component.getClassName();
String packageName = component.getPackageName();
PackageInfo packageInfo = packageInfos.get(packageName);
if (packageInfo != null) {
if (packageInfo.activities != null) {
for (ActivityInfo activity : packageInfo.activities) {
if (activityName.equals(activity.name)) {
ActivityInfo result = new ActivityInfo(activity);
applyFlagsToComponentInfo(result, flags);
return result;
}
}
}
// Activity is requested is not listed in the AndroidManifest.xml
ActivityInfo result = new ActivityInfo();
result.name = activityName;
result.packageName = packageName;
result.applicationInfo = new ApplicationInfo(packageInfo.applicationInfo);
return result;
}
// TODO: Should throw a NameNotFoundException
// In the cases where an Activity from another package has been requested.
ActivityInfo result = new ActivityInfo();
result.name = activityName;
result.packageName = packageName;
result.applicationInfo = new ApplicationInfo();
result.applicationInfo.packageName = packageName;
result.applicationInfo.flags = ApplicationInfo.FLAG_INSTALLED;
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
protected @Nullable String[] getPackagesForUid(int uid) {
String[] packageNames = packagesForUid.get(uid);
if (packageNames != null) {
return packageNames;
}
Set<String> results = new HashSet<>();
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) {
try {
getPackageInfo(packageName, -1);
} catch (NameNotFoundException e) {
throw new IllegalArgumentException(e);
}
return applicationEnabledSettingMap.get(packageName);
}
@Implementation
protected ProviderInfo getProviderInfo(ComponentName component, int flags)
throws NameNotFoundException {
String packageName = component.getPackageName();
PackageInfo packageInfo = packageInfos.get(packageName);
if (packageInfo != null && packageInfo.providers != null) {
for (ProviderInfo provider : packageInfo.providers) {
if (resolvePackageName(packageName, component).equals(provider.name)) {
ProviderInfo result = new ProviderInfo(provider);
applyFlagsToComponentInfo(result, flags);
return result;
}
}
}
throw new NameNotFoundException("Package not found: " + packageName);
}
@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) {
HashSet<ComponentName> preferredComponents = new HashSet<>();
for (Entry<IntentFilterWrapper, ComponentName> preferred : preferredActivities.entrySet()) {
if ((preferred.getKey().getFilter().match(context.getContentResolver(), intent, false, "robo")
& MATCH_CATEGORY_MASK)
!= 0) {
preferredComponents.add(preferred.getValue());
}
}
List<ResolveInfo> candidates = queryIntentActivities(intent, flags);
return candidates.isEmpty()
? null
: Collections.max(candidates, new ResolveInfoComparator(preferredComponents));
}
@Implementation
protected ProviderInfo resolveContentProvider(String name, int flags) {
if (name == null) {
return null;
}
for (PackageInfo packageInfo : packageInfos.values()) {
if (packageInfo.providers == null) continue;
for (ProviderInfo providerInfo : packageInfo.providers) {
if (name.equals(providerInfo.authority)) { // todo: support multiple authorities
return new ProviderInfo(providerInfo);
}
}
}
return null;
}
@Implementation(minSdk = LOLLIPOP)
protected ProviderInfo resolveContentProviderAsUser(
String name, int flags, @UserIdInt int userId) {
return null;
}
@Implementation
protected synchronized PackageInfo getPackageInfo(String packageName, int flags)
throws NameNotFoundException {
PackageInfo info = packageInfos.get(packageName);
if (info != null) {
if (applicationEnabledSettingMap.get(packageName) == COMPONENT_ENABLED_STATE_DISABLED
&& (flags & MATCH_UNINSTALLED_PACKAGES) != MATCH_UNINSTALLED_PACKAGES
&& (flags & MATCH_DISABLED_COMPONENTS) != MATCH_DISABLED_COMPONENTS) {
throw new NameNotFoundException("Package is disabled, can't find");
}
if (hiddenPackages.contains(packageName) && !isFlagSet(flags, MATCH_UNINSTALLED_PACKAGES)) {
throw new NameNotFoundException("Package is hidden, can't find");
}
return newPackageInfo(info);
} else {
throw new NameNotFoundException(packageName);
}
}
// 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);
}
@Implementation
protected List<ResolveInfo> queryIntentServices(Intent intent, int flags) {
List<ResolveInfo> result = new ArrayList<>();
List<ResolveInfo> resolveInfoList = queryOverriddenIntents(intent, flags);
if (!resolveInfoList.isEmpty()) {
result.addAll(
filterResolvedComponent(
resolveInfoList, flags, (resolveInfo) -> resolveInfo.serviceInfo));
}
if (isExplicitIntent(intent)) {
ResolveInfo resolvedService = resolveServiceForExplicitIntent(intent);
if (resolvedService != null) {
result.addAll(
filterResolvedComponent(
Arrays.asList(resolvedService), flags, (resolveInfo) -> resolveInfo.serviceInfo));
}
} else {
result.addAll(
filterResolvedComponent(
queryImplicitIntentServices(intent),
flags,
(resolveInfo) -> resolveInfo.serviceInfo));
}
return result;
}
private List<ResolveInfo> filterResolvedComponent(
List<ResolveInfo> resolveInfoList,
int flags,
Function<ResolveInfo, ComponentInfo> componentInfoFn) {
// If the flag is set, no further filtering will happen.
if (isFlagSet(flags, PackageManager.MATCH_ALL)) {
return resolveInfoList;
}
// Create a copy of the list for filtering
resolveInfoList = new ArrayList<>(resolveInfoList);
for (Iterator<ResolveInfo> iterator = resolveInfoList.iterator(); iterator.hasNext(); ) {
ResolveInfo resolveInfo = iterator.next();
ComponentInfo componentInfo = componentInfoFn.apply(resolveInfo);
boolean hasSomeComponentInfo =
resolveInfo.activityInfo != null
|| resolveInfo.serviceInfo != null
|| (VERSION.SDK_INT >= VERSION_CODES.KITKAT && resolveInfo.providerInfo != null);
if (componentInfo == null && hasSomeComponentInfo) {
// wrong type of component. For backward compatibility we keep those entries that doesn't
// have any component.
iterator.remove();
continue;
}
if (isFlagSet(flags, PackageManager.MATCH_SYSTEM_ONLY)) {
if (componentInfo == null || componentInfo.applicationInfo == null) {
// TODO: for backwards compatibility just skip filtering. In future should just remove
// invalid resolve infos from list
iterator.remove();
continue;
} else {
final int applicationFlags = componentInfo.applicationInfo.flags;
if ((applicationFlags & ApplicationInfo.FLAG_SYSTEM) != ApplicationInfo.FLAG_SYSTEM) {
iterator.remove();
continue;
}
}
}
if (!isFlagSet(flags, PackageManager.MATCH_DISABLED_COMPONENTS)
&& resolveInfo != null
&& isValidComponentInfo(componentInfo)) {
ComponentName componentName =
new ComponentName(componentInfo.applicationInfo.packageName, componentInfo.name);
if ((getComponentEnabledSetting(componentName)
& PackageManager.COMPONENT_ENABLED_STATE_DISABLED)
!= 0) {
iterator.remove();
continue;
}
}
if (!isFlagSet(flags, MATCH_UNINSTALLED_PACKAGES)
&& resolveInfo != null
&& isValidComponentInfo(componentInfo)
&& hiddenPackages.contains(componentInfo.applicationInfo.packageName)) {
iterator.remove();
continue;
}
}
return resolveInfoList;
}
private static boolean isFlagSet(int flags, int 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) {
List<ResolveInfo> result = new ArrayList<>();
List<ResolveInfo> resolveInfoList = queryOverriddenIntents(intent, flags);
if (!resolveInfoList.isEmpty()) {
result.addAll(
filterResolvedComponent(
resolveInfoList, flags, (resolveInfo) -> resolveInfo.activityInfo));
}
if (isExplicitIntent(intent)) {
ResolveInfo resolvedActivity = resolveActivityForExplicitIntent(intent);
if (resolvedActivity != null) {
result.addAll(
filterResolvedComponent(
Arrays.asList(resolvedActivity), flags, (resolveInfo) -> resolveInfo.activityInfo));
}
} else {
result.addAll(
filterResolvedComponent(
queryImplicitIntentActivities(intent),
flags,
(resolveInfo) -> resolveInfo.activityInfo));
}
return result;
}
/** 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 ResolveInfo resolveActivityForExplicitIntent(Intent intent) {
ComponentName component = getComponentForIntent(intent);
for (Package appPackage : packages.values()) {
Activity activity = findMatchingComponent(component, appPackage.activities);
if (activity != null) {
return buildResolveInfo(activity);
}
}
return null;
}
private ResolveInfo resolveServiceForExplicitIntent(Intent intent) {
ComponentName component = getComponentForIntent(intent);
for (Package appPackage : packages.values()) {
Service service = findMatchingComponent(component, appPackage.services);
if (service != null) {
return buildResolveInfo(service);
}
}
return null;
}
private ResolveInfo resolveReceiverForExplicitIntent(Intent intent) {
ComponentName component = getComponentForIntent(intent);
for (Package appPackage : packages.values()) {
Activity receiver = findMatchingComponent(component, appPackage.receivers);
if (receiver != null) {
return buildResolveInfo(receiver);
}
}
return null;
}
private ResolveInfo resolveContentProviderForExplicitIntent(Intent intent) {
ComponentName component = getComponentForIntent(intent);
for (Package appPackage : packages.values()) {
Provider provider = findMatchingComponent(component, appPackage.providers);
if (provider != null) {
return buildResolveInfo(provider);
}
}
return null;
}
private static <T extends Component> T findMatchingComponent(
ComponentName componentName, List<T> components) {
for (T component : components) {
if (componentName.equals(component.getComponentName())) {
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 List<ResolveInfo> queryImplicitIntentContentProviders(Intent intent) {
List<ResolveInfo> resolveInfoList = new ArrayList<>();
for (Package appPackage : packages.values()) {
if (intent.getPackage() == null || intent.getPackage().equals(appPackage.packageName)) {
for (Provider provider : appPackage.providers) {
IntentFilter intentFilter = matchIntentFilter(intent, provider.intents);
if (intentFilter != null) {
resolveInfoList.add(buildResolveInfo(provider));
}
}
}
}
return resolveInfoList;
}
private List<ResolveInfo> queryImplicitIntentActivities(Intent intent) {
List<ResolveInfo> resolveInfoList = new ArrayList<>();
for (Package appPackage : packages.values()) {
if (intent.getPackage() == null || intent.getPackage().equals(appPackage.packageName)) {
for (Activity activity : appPackage.activities) {
IntentFilter intentFilter = matchIntentFilter(intent, activity.intents);
if (intentFilter != null) {
resolveInfoList.add(buildResolveInfo(activity, intentFilter));
}
}
}
}
return resolveInfoList;
}
private List<ResolveInfo> queryImplicitIntentServices(Intent intent) {
List<ResolveInfo> resolveInfoList = new ArrayList<>();
for (Package appPackage : packages.values()) {
if (intent.getPackage() == null || intent.getPackage().equals(appPackage.packageName)) {
for (Service service : appPackage.services) {
IntentFilter intentFilter = matchIntentFilter(intent, service.intents);
if (intentFilter != null) {
resolveInfoList.add(buildResolveInfo(service, intentFilter));
}
}
}
}
return resolveInfoList;
}
private List<ResolveInfo> queryImplicitIntentReceivers(Intent intent) {
List<ResolveInfo> resolveInfoList = new ArrayList<>();
for (Package appPackage : packages.values()) {
if (intent.getPackage() == null || intent.getPackage().equals(appPackage.packageName)) {
for (Activity activity : appPackage.receivers) {
IntentFilter intentFilter = matchIntentFilter(intent, activity.intents);
if (intentFilter != null) {
resolveInfoList.add(buildResolveInfo(activity, intentFilter));
}
}
}
}
return resolveInfoList;
}
static ResolveInfo buildResolveInfo(Activity activity) {
ResolveInfo resolveInfo = buildResolveInfo(activity.info);
resolveInfo.activityInfo = activity.info;
return resolveInfo;
}
static ResolveInfo buildResolveInfo(Service service) {
ResolveInfo resolveInfo = buildResolveInfo(service.info);
resolveInfo.serviceInfo = service.info;
return resolveInfo;
}
static ResolveInfo buildResolveInfo(Provider provider) {
ResolveInfo resolveInfo = buildResolveInfo(provider.info);
resolveInfo.providerInfo = provider.info;
return resolveInfo;
}
private static ResolveInfo buildResolveInfo(ComponentInfo componentInfo) {
ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.resolvePackageName = componentInfo.applicationInfo.packageName;
return resolveInfo;
}
static ResolveInfo buildResolveInfo(Activity activity, IntentFilter intentFilter) {
ResolveInfo info = buildResolveInfo(activity);
info.isDefault = intentFilter.hasCategory("Intent.CATEGORY_DEFAULT");
info.filter = new IntentFilter(intentFilter);
return info;
}
static ResolveInfo buildResolveInfo(Service service, IntentFilter intentFilter) {
ResolveInfo info = buildResolveInfo(service);
info.isDefault = intentFilter.hasCategory("Intent.CATEGORY_DEFAULT");
info.serviceInfo = service.info;
info.filter = new IntentFilter(intentFilter);
return info;
}
@Implementation
protected int checkPermission(String permName, String pkgName) {
PackageInfo permissionsInfo = packageInfos.get(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.application.getPackageName());
}
@Implementation
protected ActivityInfo getReceiverInfo(ComponentName className, int flags)
throws NameNotFoundException {
String packageName = className.getPackageName();
PackageInfo packageInfo = packageInfos.get(packageName);
if (packageInfo != null && packageInfo.receivers != null) {
for (ActivityInfo receiver : packageInfo.receivers) {
if (resolvePackageName(packageName, className).equals(receiver.name)) {
ActivityInfo result = new ActivityInfo(receiver);
applyFlagsToComponentInfo(result, flags);
return result;
}
}
}
return null;
}
@Implementation
protected List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags) {
List<ResolveInfo> result = new ArrayList<>();
List<ResolveInfo> resolveInfoList = queryOverriddenIntents(intent, flags);
if (!resolveInfoList.isEmpty()) {
result.addAll(
filterResolvedComponent(
resolveInfoList, flags, (resolveInfo) -> resolveInfo.activityInfo));
}
if (isExplicitIntent(intent)) {
ResolveInfo resolvedReceiver = resolveReceiverForExplicitIntent(intent);
if (resolvedReceiver != null) {
result.addAll(
filterResolvedComponent(
Arrays.asList(resolvedReceiver), flags, (resolveInfo) -> resolveInfo.activityInfo));
}
} else {
result.addAll(
filterResolvedComponent(
queryImplicitIntentReceivers(intent),
flags,
(resolveInfo) -> resolveInfo.activityInfo));
}
return result;
}
private static IntentFilter matchIntentFilter(
Intent intent, ArrayList<? extends PackageParser.IntentInfo> intentFilters) {
for (PackageParser.IntentInfo intentInfo : intentFilters) {
if (intentInfo.match(
intent.getAction(),
intent.getType(),
intent.getScheme(),
intent.getData(),
intent.getCategories(),
"ShadowPackageManager")
>= 0) {
return intentInfo;
}
}
return null;
}
@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 className, int flags)
throws NameNotFoundException {
String packageName = className.getPackageName();
PackageInfo packageInfo = packageInfos.get(packageName);
if (packageInfo != null) {
String serviceName = className.getClassName();
if (packageInfo.services != null) {
for (ServiceInfo service : packageInfo.services) {
if (serviceName.equals(service.name)) {
ServiceInfo result = new ServiceInfo(service);
applyFlagsToComponentInfo(result, flags);
result.applicationInfo = new ApplicationInfo(service.applicationInfo);
if (result.processName == null) {
result.processName = result.applicationInfo.processName;
}
return result;
}
}
}
throw new NameNotFoundException(serviceName);
}
return null;
}
private void applyFlagsToComponentInfo(ComponentInfo result, int flags)
throws NameNotFoundException {
if ((flags & GET_META_DATA) == 0) {
result.metaData = null;
}
if ((flags & MATCH_ALL) != 0) {
return;
}
}
@Implementation
protected Resources getResourcesForApplication(@NonNull ApplicationInfo applicationInfo)
throws PackageManager.NameNotFoundException {
return getResourcesForApplication(applicationInfo.packageName);
}
@Implementation
protected List<ApplicationInfo> getInstalledApplications(int flags) {
List<ApplicationInfo> result = new ArrayList<>();
for (PackageInfo packageInfo : packageInfos.values()) {
result.add(packageInfo.applicationInfo);
}
return result;
}
@Implementation
protected String getInstallerPackageName(String packageName) {
return packageInstallerMap.get(packageName);
}
@Implementation
protected PermissionInfo getPermissionInfo(String name, int flags) throws NameNotFoundException {
PermissionInfo permissionInfo = extraPermissions.get(name);
if (permissionInfo != null) {
return permissionInfo;
}
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) {
List<ResolveInfo> result = new ArrayList<>();
List<ResolveInfo> resolveInfoList = queryOverriddenIntents(intent, flags);
if (!resolveInfoList.isEmpty()) {
result.addAll(
filterResolvedComponent(
resolveInfoList, flags, (resolveInfo) -> resolveInfo.providerInfo));
}
if (isExplicitIntent(intent)) {
ResolveInfo resolvedProvider = resolveContentProviderForExplicitIntent(intent);
if (resolvedProvider != null) {
result.addAll(
filterResolvedComponent(
Arrays.asList(resolvedProvider), flags, (resolveInfo) -> resolveInfo.providerInfo));
}
} else {
result.addAll(
filterResolvedComponent(
queryImplicitIntentContentProviders(intent),
flags,
(resolveInfo) -> resolveInfo.providerInfo));
}
return result;
}
@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();
}
});
}
@Implementation
protected void deletePackage(String packageName, IPackageDeleteObserver observer, int flags) {
pendingDeleteCallbacks.put(packageName, observer);
}
@Implementation
protected String[] currentToCanonicalPackageNames(String[] names) {
String[] out = new String[names.length];
for (int i = names.length - 1; i >= 0; i--) {
if (currentToCanonicalNames.containsKey(names[i])) {
out[i] = currentToCanonicalNames.get(names[i]);
} else {
out[i] = names[i];
}
}
return out;
}
@Implementation
protected boolean isSafeMode() {
return false;
}
@Implementation
protected Drawable getApplicationIcon(String packageName) throws NameNotFoundException {
return applicationIcons.get(packageName);
}
@Implementation
protected Drawable getApplicationIcon(ApplicationInfo info) {
return null;
}
@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);
}
}
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;
}
@Implementation
protected Intent getLaunchIntentForPackage(String packageName) {
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(Intent.CATEGORY_LAUNCHER);
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(minSdk = N)
protected PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
throws NameNotFoundException {
return null;
}
@Implementation
protected String[] canonicalToCurrentPackageNames(String[] names) {
return new String[0];
}
@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 = 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 (extraPermissionGroups.containsKey(name)) {
return new PermissionGroupInfo(extraPermissionGroups.get(name));
}
for (Package pkg : packages.values()) {
for (PermissionGroup permissionGroup : pkg.permissionGroups) {
if (name.equals(permissionGroup.info.name)) {
return PackageParser.generatePermissionGroupInfo(permissionGroup, flags);
}
}
}
throw new NameNotFoundException(name);
}
/** @see ShadowPackageManager#addPermissionGroupInfo(android.content.pm.PermissionGroupInfo) */
@Implementation
protected List<PermissionGroupInfo> getAllPermissionGroups(int flags) {
ArrayList<PermissionGroupInfo> allPermissionGroups = new ArrayList<PermissionGroupInfo>();
// To be consistent with Android's implementation, return at most one PermissionGroupInfo object
// per permission group string
HashSet<String> handledPermissionGroups = new HashSet<>();
for (PermissionGroupInfo permissionGroupInfo : extraPermissionGroups.values()) {
allPermissionGroups.add(new PermissionGroupInfo(permissionGroupInfo));
handledPermissionGroups.add(permissionGroupInfo.name);
}
for (Package pkg : packages.values()) {
for (PermissionGroup permissionGroup : pkg.permissionGroups) {
if (!handledPermissionGroups.contains(permissionGroup.info.name)) {
PermissionGroupInfo permissionGroupInfo =
PackageParser.generatePermissionGroupInfo(permissionGroup, flags);
allPermissionGroups.add(new PermissionGroupInfo(permissionGroupInfo));
handledPermissionGroups.add(permissionGroup.info.name);
}
}
}
return allPermissionGroups;
}
@Implementation
protected ApplicationInfo getApplicationInfo(String packageName, int flags)
throws NameNotFoundException {
PackageInfo info = packageInfos.get(packageName);
if (info != null) {
try {
getPackageInfo(packageName, -1);
} catch (NameNotFoundException e) {
throw new IllegalArgumentException(e);
}
if (applicationEnabledSettingMap.get(packageName) == COMPONENT_ENABLED_STATE_DISABLED
&& (flags & MATCH_UNINSTALLED_PACKAGES) != MATCH_UNINSTALLED_PACKAGES
&& (flags & MATCH_DISABLED_COMPONENTS) != MATCH_DISABLED_COMPONENTS) {
throw new NameNotFoundException("Package is disabled, can't find");
}
if (hiddenPackages.contains(packageName) && !isFlagSet(flags, MATCH_UNINSTALLED_PACKAGES)) {
throw new NameNotFoundException("Package is hidden, can't find");
}
if (info.applicationInfo != null) {
return new ApplicationInfo(info.applicationInfo);
}
}
throw new NameNotFoundException(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) {
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;
}
@Implementation(minSdk = M)
protected void revokeRuntimePermission(
String packageName, String permissionName, UserHandle user) {
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;
}
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;
}
@Implementation(minSdk = M)
protected int getPermissionFlags(String permissionName, String packageName, UserHandle user) {
return 0;
}
@Implementation(minSdk = M)
protected void updatePermissionFlags(
String permissionName, String packageName, int flagMask, int flagValues, UserHandle user) {}
@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) {
return null;
}
@Implementation(minSdk = JELLY_BEAN_MR1)
protected ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId) {
return null;
}
@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 Shadow.directlyOn(realObject, ApplicationPackageManager.class)
.getDrawable(packageName, resId, appInfo);
}
@Implementation
protected Drawable getActivityIcon(ComponentName activityName) throws NameNotFoundException {
Drawable result = drawableList.get(activityName);
if (result != null) {
return result;
}
return Shadow.directlyOn(realObject, ApplicationPackageManager.class)
.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 {
if (context.getPackageName().equals(appPackageName)) {
return context.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) {}
@Implementation(minSdk = M)
protected void removeOnPermissionsChangeListener(Object 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) {}
@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) {
preferredActivities.put(new IntentFilterWrapper(filter), activity);
}
@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) {
if (outFilters == null) {
return 0;
}
Set<IntentFilterWrapper> filters = preferredActivities.keySet();
for (IntentFilter filter : outFilters) {
step:
for (IntentFilterWrapper testFilterWrapper : filters) {
ComponentName name = preferredActivities.get(testFilterWrapper);
IntentFilter testFilter = testFilterWrapper.getFilter();
// filter out based on the given packageName;
if (packageName != null && !name.getPackageName().equals(packageName)) {
continue step;
}
// Check actions
Iterator<String> iterator = filter.actionsIterator();
while (iterator.hasNext()) {
if (!testFilter.matchAction(iterator.next())) {
continue step;
}
}
iterator = filter.categoriesIterator();
while (iterator.hasNext()) {
if (!filter.hasCategory(iterator.next())) {
continue step;
}
}
if (outActivities == null) {
outActivities = new ArrayList<>();
}
outActivities.add(name);
}
}
return 0;
}
@Implementation
protected void clearPackagePreferredActivities(String packageName) {
Iterator<ComponentName> entryIterator = preferredActivities.values().iterator();
while (entryIterator.hasNext()) {
ComponentName next = entryIterator.next();
if (next.getPackageName().equals(packageName)) {
entryIterator.remove();
}
}
}
@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) {
// 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
if (!packageInfos.containsKey(packageName)) {
// Match Android behaviour of returning true if package isn't found
return true;
}
return hiddenPackages.contains(packageName);
}
@Implementation(minSdk = LOLLIPOP)
protected Object getKeySetByAlias(String packageName, String alias) {
return null;
}
@Implementation(minSdk = LOLLIPOP)
protected Object getSigningKeySet(String packageName) {
return null;
}
@Implementation(minSdk = LOLLIPOP)
protected boolean isSignedBy(String packageName, Object ks) {
return false;
}
@Implementation(minSdk = LOLLIPOP)
protected boolean isSignedByExactly(String packageName, Object ks) {
return false;
}
@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 Shadow.directlyOn(realObject, ApplicationPackageManager.class)
.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) {
if (hasProfileOwnerOrDeviceOwnerOnCurrentUser()) {
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, appExtras, launcherExtras);
}
return unupdatedPackages.toArray(new String[0]);
}
/** Returns whether the current user profile has a profile owner or a device owner. */
private boolean hasProfileOwnerOrDeviceOwnerOnCurrentUser() {
DevicePolicyManager devicePolicyManager =
(DevicePolicyManager) context.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(context.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) context.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) context.getSystemService(Context.TELECOM_SERVICE);
return packageName.equals(telecomManager.getDefaultDialerPackage());
}
@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) {
return false;
}
@Implementation(minSdk = Q)
protected List<ModuleInfo> getInstalledModules(int flags) {
return new ArrayList<>();
}
}