package org.robolectric.res.builder;
import static android.content.pm.ApplicationInfo.FLAG_ALLOW_BACKUP;
import static android.content.pm.ApplicationInfo.FLAG_ALLOW_CLEAR_USER_DATA;
import static android.content.pm.ApplicationInfo.FLAG_ALLOW_TASK_REPARENTING;
import static android.content.pm.ApplicationInfo.FLAG_DEBUGGABLE;
import static android.content.pm.ApplicationInfo.FLAG_HAS_CODE;
import static android.content.pm.ApplicationInfo.FLAG_KILL_AFTER_RESTORE;
import static android.content.pm.ApplicationInfo.FLAG_PERSISTENT;
import static android.content.pm.ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS;
import static android.content.pm.ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
import static android.content.pm.ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS;
import static android.content.pm.ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS;
import static android.content.pm.ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES;
import static android.content.pm.ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS;
import static android.content.pm.ApplicationInfo.FLAG_TEST_ONLY;
import static android.content.pm.ApplicationInfo.FLAG_VM_SAFE_MODE;
import static android.os.Build.VERSION_CODES.N;
import static java.util.Arrays.asList;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageInstaller;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.PackageInfo;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.PackageStats;
import android.content.pm.PathPermission;
import android.content.pm.PermissionInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.Signature;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.PatternMatcher;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Pair;
import com.google.common.base.Preconditions;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.android.StubPackageManager;
import org.robolectric.manifest.ActivityData;
import org.robolectric.manifest.AndroidManifest;
import org.robolectric.manifest.ContentProviderData;
import org.robolectric.manifest.IntentFilterData;
import org.robolectric.manifest.PackageItemData;
import org.robolectric.manifest.PathPermissionData;
import org.robolectric.manifest.PermissionItemData;
import org.robolectric.manifest.ServiceData;
import org.robolectric.res.AttributeResource;
import org.robolectric.res.ResName;
import org.robolectric.res.ResourceTable;
import org.robolectric.util.ReflectionHelpers;
import org.robolectric.util.ReflectionHelpers.ClassParameter;
import org.robolectric.util.TempDirectory;
/**
* @deprecated use @{link ShadowPackageManager} instead.
*/
@Deprecated
public class DefaultPackageManager extends StubPackageManager implements RobolectricPackageManager {
private final Map<String, AndroidManifest> androidManifests = new LinkedHashMap<>();
private final Map<String, PackageInfo> packageInfos = new LinkedHashMap<>();
private final Map<String, PackageStats> packageStatsMap = new HashMap<>();
private final Map<Intent, List<ResolveInfo>> resolveInfoForIntent = new TreeMap<>(new IntentComparator());
private final Map<ComponentName, ComponentState> componentList = new LinkedHashMap<>();
private final Map<ComponentName, Drawable> drawableList = new LinkedHashMap<>();
private final Map<String, Drawable> applicationIcons = new HashMap<>();
private final Map<String, Boolean> systemFeatureList = new LinkedHashMap<>();
private final Map<IntentFilter, ComponentName> preferredActivities = new LinkedHashMap<>();
private final Map<Pair<String, Integer>, Drawable> drawables = new LinkedHashMap<>();
private final Map<String, Integer> applicationEnabledSettingMap = new HashMap<>();
private final Map<Integer, String> namesForUid = new HashMap<>();
private final Map<Integer, String[]> packagesForUid = new HashMap<>();
private final Map<String, String> packageInstallerMap = new HashMap<>();
private boolean queryIntentImplicitly = false;
private PackageInstaller packageInstaller;
private AndroidManifest applicationManifest;
private ResourceTable appResourceTable;
private Map<String, PermissionInfo> extraPermissions = new HashMap<>();
@Override
public PackageInstaller getPackageInstaller() {
if (packageInstaller == null) {
if (RuntimeEnvironment.getApiLevel() == VERSION_CODES.O) {
packageInstaller = new PackageInstaller(ReflectionHelpers.createNullProxy(
IPackageInstaller.class),
null, UserHandle.myUserId());
} else {
packageInstaller = ReflectionHelpers.callConstructor(PackageInstaller.class,
ClassParameter.from(Context.class, RuntimeEnvironment.application),
ClassParameter.from(PackageManager.class, this),
ClassParameter.from(IPackageInstaller.class,
ReflectionHelpers.createNullProxy(IPackageInstaller.class)),
ClassParameter.from(String.class, null),
ClassParameter.from(int.class, UserHandle.myUserId()));
}
}
return packageInstaller;
}
@Override
public Resources getResourcesForApplication(ApplicationInfo applicationInfo) throws NameNotFoundException {
return getResourcesForApplication(applicationInfo.packageName);
}
@Override
public Resources getResourcesForApplication(String appPackageName) throws NameNotFoundException {
if (RuntimeEnvironment.application.getPackageName().equals(appPackageName)) {
return RuntimeEnvironment.application.getResources();
}
throw new PackageManager.NameNotFoundException(appPackageName);
}
@Override
public PackageInfo getPackageInfo(String packageName, int flags) throws NameNotFoundException {
if (packageInfos.containsKey(packageName)) {
return packageInfos.get(packageName);
}
throw new NameNotFoundException(packageName);
}
@Override
public ApplicationInfo getApplicationInfo(String packageName, int flags) throws NameNotFoundException {
PackageInfo info = packageInfos.get(packageName);
if (info != null) {
return info.applicationInfo;
} else {
throw new NameNotFoundException(packageName);
}
}
@Override public ActivityInfo getActivityInfo(ComponentName className, int flags) throws NameNotFoundException {
ActivityInfo activityInfo = new ActivityInfo();
String packageName = className.getPackageName();
String activityName = className.getClassName();
activityInfo.name = activityName;
activityInfo.packageName = packageName;
AndroidManifest androidManifest = androidManifests.get(packageName);
// In the cases where there is no manifest entry for the activity, e.g: a test that creates
// simply an android.app.Activity just return what we have.
if (androidManifest == null) {
return activityInfo;
}
ActivityData activityData = androidManifest.getActivityData(activityName);
if (activityData != null) {
activityInfo.configChanges = getConfigChanges(activityData);
activityInfo.parentActivityName = activityData.getParentActivityName();
activityInfo.metaData = metaDataToBundle(activityData.getMetaData().getValueMap());
String themeRef;
// Based on ShadowActivity
if (activityData.getThemeRef() != null) {
themeRef = activityData.getThemeRef();
} else {
themeRef = androidManifest.getThemeRef();
}
if (themeRef != null) {
activityInfo.theme = RuntimeEnvironment.application.getResources().getIdentifier(themeRef.replace("@", ""), "style", packageName);
}
}
activityInfo.applicationInfo = getApplicationInfo(packageName, flags);
return activityInfo;
}
private static final List<Pair<String, Integer>> CONFIG_OPTIONS = asList(
Pair.create("mcc", ActivityInfo.CONFIG_MCC),
Pair.create("mnc", ActivityInfo.CONFIG_MNC),
Pair.create("locale", ActivityInfo.CONFIG_LOCALE),
Pair.create("touchscreen", ActivityInfo.CONFIG_TOUCHSCREEN),
Pair.create("keyboard", ActivityInfo.CONFIG_KEYBOARD),
Pair.create("keyboardHidden", ActivityInfo.CONFIG_KEYBOARD_HIDDEN),
Pair.create("navigation", ActivityInfo.CONFIG_NAVIGATION),
Pair.create("screenLayout", ActivityInfo.CONFIG_SCREEN_LAYOUT),
Pair.create("fontScale", ActivityInfo.CONFIG_FONT_SCALE),
Pair.create("uiMode", ActivityInfo.CONFIG_UI_MODE),
Pair.create("orientation", ActivityInfo.CONFIG_ORIENTATION),
Pair.create("screenSize", ActivityInfo.CONFIG_SCREEN_SIZE),
Pair.create("smallestScreenSize", ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE)
);
private int getConfigChanges(ActivityData activityData) {
String s = activityData.getConfigChanges();
int res = 0;
//quick sanity check.
if (s == null || "".equals(s)) {
return res;
}
String[] pieces = s.split("\\|");
for(String s1 : pieces) {
s1 = s1.trim();
for (Pair<String, Integer> pair : CONFIG_OPTIONS) {
if (s1.equals(pair.first)) {
res |= pair.second;
break;
}
}
}
return res;
}
@Override
public ProviderInfo getProviderInfo(ComponentName className, int flags) throws NameNotFoundException {
String packageName = className.getPackageName();
AndroidManifest androidManifest = androidManifests.get(packageName);
String classString = resolvePackageName(packageName, className);
for (ContentProviderData contentProviderData : androidManifest.getContentProviders()) {
if (contentProviderData.getClassName().equals(classString)) {
ProviderInfo providerInfo = new ProviderInfo();
providerInfo.packageName = packageName;
providerInfo.name = contentProviderData.getClassName();
providerInfo.authority = contentProviderData.getAuthorities(); // todo: support multiple authorities
providerInfo.readPermission = contentProviderData.getReadPermission();
providerInfo.writePermission = contentProviderData.getWritePermission();
providerInfo.pathPermissions = createPathPermissions(contentProviderData.getPathPermissionDatas());
providerInfo.metaData = metaDataToBundle(contentProviderData.getMetaData().getValueMap());
if ((flags & GET_META_DATA) != 0) {
providerInfo.metaData = metaDataToBundle(contentProviderData.getMetaData().getValueMap());
}
return providerInfo;
}
}
return null;
}
private PathPermission[] createPathPermissions(List<PathPermissionData> pathPermissionDatas) {
PathPermission[] pathPermissions = new PathPermission[pathPermissionDatas.size()];
for (int i = 0; i < pathPermissions.length; i++) {
PathPermissionData data = pathPermissionDatas.get(i);
final String path;
final int type;
if (data.pathPrefix != null) {
path = data.pathPrefix;
type = PathPermission.PATTERN_PREFIX;
} else if (data.pathPattern != null) {
path = data.pathPattern;
type = PathPermission.PATTERN_SIMPLE_GLOB;
} else {
path = data.path;
type = PathPermission.PATTERN_LITERAL;
}
pathPermissions[i] = new PathPermission(path, type, data.readPermission, data.writePermission);
}
return pathPermissions;
}
@Override
public ActivityInfo getReceiverInfo(ComponentName className, int flags) throws NameNotFoundException {
String packageName = className.getPackageName();
AndroidManifest androidManifest = androidManifests.get(packageName);
String classString = resolvePackageName(packageName, className);
for (PackageItemData receiver : androidManifest.getBroadcastReceivers()) {
if (receiver.getClassName().equals(classString)) {
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.packageName = packageName;
activityInfo.name = classString;
if ((flags & GET_META_DATA) != 0) {
activityInfo.metaData = metaDataToBundle(receiver.getMetaData().getValueMap());
}
return activityInfo;
}
}
return null;
}
private String resolvePackageName(String packageName, ComponentName componentName) {
String classString = componentName.getClassName();
int index = classString.indexOf('.');
if (index == -1) {
classString = packageName + "." + classString;
} else if (index == 0) {
classString = packageName + classString;
}
return classString;
}
@Override
public ServiceInfo getServiceInfo(ComponentName className, int flags) throws NameNotFoundException {
String packageName = className.getPackageName();
AndroidManifest androidManifest = androidManifests.get(packageName);
String serviceName = className.getClassName();
ServiceData serviceData = androidManifest.getServiceData(serviceName);
if (serviceData == null) {
throw new NameNotFoundException(serviceName);
}
ServiceInfo serviceInfo = new ServiceInfo();
serviceInfo.packageName = packageName;
serviceInfo.name = serviceName;
serviceInfo.applicationInfo = getApplicationInfo(packageName, flags);
serviceInfo.permission = serviceData.getPermission();
if ((flags & GET_META_DATA) != 0) {
serviceInfo.metaData = metaDataToBundle(serviceData.getMetaData().getValueMap());
}
return serviceInfo;
}
@Override
public List<PackageInfo> getInstalledPackages(int flags) {
return new ArrayList<>(packageInfos.values());
}
@Override
public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) {
List<ResolveInfo> resolveInfoList = queryIntent(intent, flags);
if (resolveInfoList.isEmpty() && isQueryIntentImplicitly()) {
resolveInfoList = queryImplicitIntent(intent, flags);
}
return resolveInfoList;
}
@Override
public List<ResolveInfo> queryIntentServices(Intent intent, int flags) {
return queryIntent(intent, flags);
}
@Override
public List<ResolveInfo> queryBroadcastReceivers(Intent intent, int flags) {
return queryIntent(intent, flags);
}
@Override
public ResolveInfo resolveActivity(Intent intent, int flags) {
List<ResolveInfo> candidates = queryIntentActivities(intent, flags);
return candidates.isEmpty() ? null : candidates.get(0);
}
@Override
public ResolveInfo resolveService(Intent intent, int flags) {
return resolveActivity(intent, flags);
}
@Override
public ProviderInfo resolveContentProvider(String name, int flags) {
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 providerInfo;
}
}
}
return null;
}
@Override
public void addResolveInfoForIntent(Intent intent, List<ResolveInfo> info) {
resolveInfoForIntent.put(intent, info);
}
@Override
public void addResolveInfoForIntent(Intent intent, ResolveInfo info) {
List<ResolveInfo> infoList = findOrCreateInfoList(intent);
infoList.add(info);
}
@Override
public void removeResolveInfosForIntent(Intent intent, String packageName) {
List<ResolveInfo> infoList = findOrCreateInfoList(intent);
for (Iterator<ResolveInfo> iterator = infoList.iterator(); iterator.hasNext(); ) {
ResolveInfo resolveInfo = iterator.next();
if (resolveInfo.activityInfo.packageName.equals(packageName)) {
iterator.remove();
}
}
}
@Override
public Drawable getDefaultActivityIcon() {
return Resources.getSystem().getDrawable(
com.android.internal.R.drawable.sym_def_app_icon);
}
@Override
public Drawable getActivityIcon(Intent intent) {
return drawableList.get(intent.getComponent());
}
@Override
public Drawable getActivityIcon(ComponentName componentName) {
return drawableList.get(componentName);
}
@Override
public void addActivityIcon(ComponentName component, Drawable d) {
drawableList.put(component, d);
}
@Override
public void addActivityIcon(Intent intent, Drawable d) {
drawableList.put(intent.getComponent(), d);
}
@Override
public 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(ris.get(0).activityInfo.packageName, ris.get(0).activityInfo.name);
return intent;
}
@Override
public void addPermissionInfo(PermissionInfo permissionInfo) {
extraPermissions.put(permissionInfo.name, permissionInfo);
}
@Override
public PermissionInfo getPermissionInfo(String name, int flags) throws NameNotFoundException {
PermissionInfo permissionInfo = extraPermissions.get(name);
if (permissionInfo != null) {
return permissionInfo;
}
PermissionItemData permissionItemData = applicationManifest.getPermissions().get(name);
if (permissionItemData == null) {
throw new NameNotFoundException(name);
}
permissionInfo = new PermissionInfo();
String packageName = applicationManifest.getPackageName();
permissionInfo.packageName = packageName;
permissionInfo.name = name;
permissionInfo.group = permissionItemData.getPermissionGroup();
permissionInfo.protectionLevel = decodeProtectionLevel(permissionItemData.getProtectionLevel());
String descriptionRef = permissionItemData.getDescription();
if (descriptionRef != null) {
ResName descResName = AttributeResource.getResourceReference(descriptionRef, packageName, "string");
permissionInfo.descriptionRes = appResourceTable.getResourceId(descResName);
}
String labelRefOrString = permissionItemData.getLabel();
if (labelRefOrString != null) {
if (AttributeResource.isResourceReference(labelRefOrString)) {
ResName labelResName = AttributeResource.getResourceReference(labelRefOrString, packageName, "string");
permissionInfo.labelRes = appResourceTable.getResourceId(labelResName);
} else {
permissionInfo.nonLocalizedLabel = labelRefOrString;
}
}
if ((flags & GET_META_DATA) != 0) {
permissionInfo.metaData = metaDataToBundle(permissionItemData.getMetaData().getValueMap());
}
return permissionInfo;
}
private int decodeProtectionLevel(String protectionLevel) {
if (protectionLevel == null) {
return PermissionInfo.PROTECTION_NORMAL;
}
switch (protectionLevel) {
case "normal":
return PermissionInfo.PROTECTION_NORMAL;
case "dangerous":
return PermissionInfo.PROTECTION_DANGEROUS;
case "signature":
return PermissionInfo.PROTECTION_SIGNATURE;
case "signatureOrSystem":
return PermissionInfo.PROTECTION_SIGNATURE_OR_SYSTEM;
default:
throw new IllegalArgumentException("unknown protection level " + protectionLevel);
}
}
@Override
public CharSequence getApplicationLabel(ApplicationInfo info) {
return info.name;
}
@Override
public Drawable getApplicationIcon(String packageName) {
return applicationIcons.get(packageName);
}
@Override
public void setApplicationIcon(String packageName, Drawable drawable) {
applicationIcons.put(packageName, drawable);
}
@Override
public void setComponentEnabledSetting(ComponentName componentName, int newState, int flags) {
componentList.put(componentName, new ComponentState(newState, flags));
}
@Override
public int getComponentEnabledSetting(ComponentName componentName) {
ComponentState state = componentList.get(componentName);
return state != null ? state.newState : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
}
@Override
public void addPreferredActivity(IntentFilter filter, int match, ComponentName[] set, ComponentName activity) {
preferredActivities.put(filter, activity);
}
@Override
public int getPreferredActivities(List<IntentFilter> outFilters, List<ComponentName> outActivities, String packageName) {
if (outFilters == null) {
return 0;
}
Set<IntentFilter> filters = preferredActivities.keySet();
for (IntentFilter filter : outFilters) {
step:
for (IntentFilter testFilter : filters) {
ComponentName name = preferredActivities.get(testFilter);
// 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;
}
@Override
public PackageInfo getPackageArchiveInfo(String archiveFilePath, int flags) {
List<PackageInfo> packages = getInstalledPackages(flags);
for (PackageInfo aPackage : packages) {
ApplicationInfo appInfo = aPackage.applicationInfo;
if (archiveFilePath.equals(appInfo.sourceDir)) {
return aPackage;
}
}
return null;
}
/**
* Use to make assertions on values passed to setComponentEnabledSetting.
*
* @param componentName Component name.
* @return Component state.
*/
@Override
public ComponentState getComponentState(ComponentName componentName) {
return componentList.get(componentName);
}
/**
* Adds a package to the list of those already 'installed' on system.
*
* @param packageInfo New package info.
*/
@Override
public void addPackage(PackageInfo packageInfo) {
addPackage(packageInfo, new PackageStats(packageInfo.packageName));
}
public void addPackage(PackageInfo packageInfo, PackageStats packageStats) {
Preconditions.checkArgument(packageInfo.packageName.equals(packageStats.packageName));
packageInfos.put(packageInfo.packageName, packageInfo);
packageStatsMap.put(packageInfo.packageName, packageStats);
applicationEnabledSettingMap.put(packageInfo.packageName, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT);
}
@Override
public void addPackage(String packageName) {
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = packageName;
ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.packageName = packageName;
applicationInfo.sourceDir = new File(".").getAbsolutePath();
applicationInfo.dataDir = TempDirectory.create().toAbsolutePath().toString();
packageInfo.applicationInfo = applicationInfo;
addPackage(packageInfo);
}
@Override
public void addManifest(AndroidManifest androidManifest, int labelRes) {
androidManifests.put(androidManifest.getPackageName(), androidManifest);
PackageInfo packageInfo = new PackageInfo();
packageInfo.packageName = androidManifest.getPackageName();
packageInfo.versionName = androidManifest.getVersionName();
packageInfo.versionCode = androidManifest.getVersionCode();
Map<String,ActivityData> activityDatas = androidManifest.getActivityDatas();
for (ActivityData data : activityDatas.values()) {
String name = data.getName();
String activityName = name.startsWith(".") ? androidManifest.getPackageName() + name : name;
addResolveInfoForIntent(new Intent(activityName), new ResolveInfo());
}
ContentProviderData[] cpdata = androidManifest.getContentProviders().toArray(new ContentProviderData[]{});
if (cpdata.length == 0) {
packageInfo.providers = null;
} else {
packageInfo.providers = new ProviderInfo[cpdata.length];
for (int i = 0; i < cpdata.length; i++) {
ProviderInfo info = new ProviderInfo();
info.authority = cpdata[i].getAuthorities(); // todo: support multiple authorities
info.name = cpdata[i].getClassName();
info.packageName = androidManifest.getPackageName();
info.metaData = metaDataToBundle(cpdata[i].getMetaData().getValueMap());
packageInfo.providers[i] = info;
}
}
// Populate information related to BroadcastReceivers. Broadcast receivers can be queried in two
// possible ways,
// 1. PackageManager#getPackageInfo(...),
// 2. PackageManager#queryBroadcastReceivers(...)
// The following piece of code will let you enable querying receivers through both the methods.
List<ActivityInfo> receiverActivityInfos = new ArrayList<>();
for (int i = 0; i < androidManifest.getBroadcastReceivers().size(); ++i) {
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.name = androidManifest.getBroadcastReceivers().get(i).getClassName();
activityInfo.permission = androidManifest.getBroadcastReceivers().get(i).getPermission();
receiverActivityInfos.add(activityInfo);
ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.activityInfo = activityInfo;
IntentFilter filter = new IntentFilter();
for (String action : androidManifest.getBroadcastReceivers().get(i).getActions()) {
filter.addAction(action);
}
resolveInfo.filter = filter;
for (String action : androidManifest.getBroadcastReceivers().get(i).getActions()) {
Intent intent = new Intent(action);
intent.setPackage(androidManifest.getPackageName());
addResolveInfoForIntent(intent, resolveInfo);
}
}
packageInfo.receivers = receiverActivityInfos.toArray(new ActivityInfo[0]);
String[] usedPermissions = androidManifest.getUsedPermissions().toArray(new String[]{});
if (usedPermissions.length == 0) {
packageInfo.requestedPermissions = null;
} else {
packageInfo.requestedPermissions = usedPermissions;
}
ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.flags = decodeFlags(androidManifest.getApplicationAttributes());
applicationInfo.targetSdkVersion = androidManifest.getTargetSdkVersion();
applicationInfo.packageName = androidManifest.getPackageName();
applicationInfo.processName = androidManifest.getProcessName();
applicationInfo.name = androidManifest.getApplicationName();
applicationInfo.metaData = metaDataToBundle(androidManifest.getApplicationMetaData());
applicationInfo.sourceDir = new File(".").getAbsolutePath();
applicationInfo.dataDir = TempDirectory.create().toAbsolutePath().toString();
if (RuntimeEnvironment.getApiLevel() >= N) {
applicationInfo.credentialProtectedDataDir = TempDirectory.create().toAbsolutePath().toString();
applicationInfo.deviceProtectedDataDir = TempDirectory.create().toAbsolutePath().toString();
}
applicationInfo.labelRes = labelRes;
String labelRef = androidManifest.getLabelRef();
if (labelRef != null && !labelRef.startsWith("@")) {
applicationInfo.nonLocalizedLabel = labelRef;
}
packageInfo.applicationInfo = applicationInfo;
addPackage(packageInfo);
}
private static final List<Pair<String, Integer>> APPLICATION_FLAGS = asList(
Pair.create("android:allowBackup", FLAG_ALLOW_BACKUP),
Pair.create("android:allowClearUserData", FLAG_ALLOW_CLEAR_USER_DATA),
Pair.create("android:allowTaskReparenting", FLAG_ALLOW_TASK_REPARENTING),
Pair.create("android:debuggable", FLAG_DEBUGGABLE),
Pair.create("android:hasCode", FLAG_HAS_CODE),
Pair.create("android:killAfterRestore", FLAG_KILL_AFTER_RESTORE),
Pair.create("android:persistent", FLAG_PERSISTENT),
Pair.create("android:resizeable", FLAG_RESIZEABLE_FOR_SCREENS),
Pair.create("android:restoreAnyVersion", FLAG_RESTORE_ANY_VERSION),
Pair.create("android:largeScreens", FLAG_SUPPORTS_LARGE_SCREENS),
Pair.create("android:normalScreens", FLAG_SUPPORTS_NORMAL_SCREENS),
Pair.create("android:anyDensity", FLAG_SUPPORTS_SCREEN_DENSITIES),
Pair.create("android:smallScreens", FLAG_SUPPORTS_SMALL_SCREENS),
Pair.create("android:testOnly", FLAG_TEST_ONLY),
Pair.create("android:vmSafeMode", FLAG_VM_SAFE_MODE)
);
private int decodeFlags(Map<String, String> applicationAttributes) {
int applicationFlags = 0;
for (Pair<String, Integer> pair : APPLICATION_FLAGS) {
if ("true".equals(applicationAttributes.get(pair.first))) {
applicationFlags |= pair.second;
}
}
return applicationFlags;
}
@Override
public void removePackage(String packageName) {
packageInfos.remove(packageName);
}
@Override
public boolean hasSystemFeature(String name) {
return systemFeatureList.containsKey(name) ? systemFeatureList.get(name) : false;
}
/**
* Used to declare a system feature is or is not supported.
*
* @param name Feature name.
* @param supported Is the feature supported?
*/
@Override
public void setSystemFeature(String name, boolean supported) {
systemFeatureList.put(name, supported);
}
@Override
public void addDrawableResolution(String packageName, int resourceId, Drawable drawable) {
drawables.put(new Pair(packageName, resourceId), drawable);
}
@Override
public Drawable getDrawable(String packageName, int resourceId, ApplicationInfo applicationInfo) {
return drawables.get(new Pair(packageName, resourceId));
}
private List<ResolveInfo> findOrCreateInfoList(Intent intent) {
List<ResolveInfo> infoList = resolveInfoForIntent.get(intent);
if (infoList == null) {
infoList = new ArrayList<>();
resolveInfoForIntent.put(intent, infoList);
}
return infoList;
}
private List<ResolveInfo> queryIntent(Intent intent, int flags) {
List<ResolveInfo> result = resolveInfoForIntent.get(intent);
if (result == null) {
return Collections.emptyList();
} else {
return result;
}
}
private List<ResolveInfo> queryImplicitIntent(Intent intent, int flags) {
List<ResolveInfo> resolveInfoList = new ArrayList<>();
for (Map.Entry<String, AndroidManifest> androidManifest : androidManifests.entrySet()) {
String packageName = androidManifest.getKey();
AndroidManifest appManifest = androidManifest.getValue();
for (Map.Entry<String, ActivityData> activity : appManifest.getActivityDatas().entrySet()) {
String activityName = activity.getKey();
ActivityData activityData = activity.getValue();
if (activityData.getTargetActivity() != null) {
activityName = activityData.getTargetActivityName();
}
if (matchIntentFilter(activityData, intent)) {
ResolveInfo resolveInfo = new ResolveInfo();
resolveInfo.resolvePackageName = packageName;
resolveInfo.activityInfo = new ActivityInfo();
resolveInfo.activityInfo.targetActivity = activityName;
resolveInfoList.add(resolveInfo);
}
}
}
return resolveInfoList;
}
private boolean matchIntentFilter(ActivityData activityData, Intent intent) {
for (IntentFilterData intentFilterData : activityData.getIntentFilters()) {
List<String> actionList = intentFilterData.getActions();
List<String> categoryList = intentFilterData.getCategories();
IntentFilter intentFilter = new IntentFilter();
for (String action : actionList) {
intentFilter.addAction(action);
}
for (String category : categoryList) {
intentFilter.addCategory(category);
}
for (String scheme : intentFilterData.getSchemes()) {
intentFilter.addDataScheme(scheme);
}
for (String mimeType : intentFilterData.getMimeTypes()) {
try {
intentFilter.addDataType(mimeType);
} catch (IntentFilter.MalformedMimeTypeException ex) {
throw new RuntimeException(ex);
}
}
for (String path : intentFilterData.getPaths()) {
intentFilter.addDataPath(path, PatternMatcher.PATTERN_LITERAL);
}
for (String pathPattern : intentFilterData.getPathPatterns()) {
intentFilter.addDataPath(pathPattern, PatternMatcher.PATTERN_SIMPLE_GLOB);
}
for (String pathPrefix : intentFilterData.getPathPrefixes()) {
intentFilter.addDataPath(pathPrefix, PatternMatcher.PATTERN_PREFIX);
}
for (IntentFilterData.DataAuthority authority : intentFilterData.getAuthorities()) {
intentFilter.addDataAuthority(authority.getHost(), authority.getPort());
}
// match action
boolean matchActionResult = intentFilter.matchAction(intent.getAction());
// match category
String matchCategoriesResult = intentFilter.matchCategories(intent.getCategories());
// match data
int matchResult = intentFilter.matchData(intent.getType(),
(intent.getData() != null ? intent.getData().getScheme() : null),
intent.getData());
if (matchActionResult && (matchCategoriesResult == null) &&
(matchResult != IntentFilter.NO_MATCH_DATA && matchResult != IntentFilter.NO_MATCH_TYPE)){
return true;
}
}
return false;
}
@Override
public boolean isQueryIntentImplicitly() {
return queryIntentImplicitly;
}
@Override
public void setQueryIntentImplicitly(boolean queryIntentImplicitly) {
this.queryIntentImplicitly = queryIntentImplicitly;
}
@Override
public void setApplicationEnabledSetting(String packageName, int newState, int flags) {
applicationEnabledSettingMap.put(packageName, newState);
}
@Override
public int getApplicationEnabledSetting(String packageName) {
try {
PackageInfo packageInfo = getPackageInfo(packageName, -1);
} catch (NameNotFoundException e) {
throw new IllegalArgumentException(e);
}
return applicationEnabledSettingMap.get(packageName);
}
@Override
public int checkPermission(String permName, String pkgName) {
PackageInfo permissionsInfo = packageInfos.get(pkgName);
if (permissionsInfo == null || permissionsInfo.requestedPermissions == null) {
return PackageManager.PERMISSION_DENIED;
}
for (String permission : permissionsInfo.requestedPermissions) {
if (permission != null && permission.equals(permName)) {
return PackageManager.PERMISSION_GRANTED;
}
}
return PackageManager.PERMISSION_DENIED;
}
@Override
public void reset() {
for (PackageInfo info : packageInfos.values()) {
if (info.applicationInfo != null && info.applicationInfo.dataDir != null) {
TempDirectory.destroy(Paths.get(info.applicationInfo.dataDir));
}
}
}
@Override
public void setNameForUid(int uid, String name) {
namesForUid.put(uid, name);
}
@Override
public String getNameForUid(int uid) {
return namesForUid.get(uid);
}
@Override
public List<ApplicationInfo> getInstalledApplications(int flags) {
List<ApplicationInfo> result = new LinkedList<>();
for (PackageInfo packageInfo : packageInfos.values()) {
result.add(packageInfo.applicationInfo);
}
return result;
}
public void setPackagesForCallingUid(String... packagesForCallingUid) {
setPackagesForUid(Binder.getCallingUid(), packagesForCallingUid);
}
/**
* Override value returned by {@link #getPackagesForUid(int)}.
*/
public void setPackagesForUid(int uid, String... packagesForCallingUid) {
this.packagesForUid.put(uid, packagesForCallingUid);
}
@Override
public 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()]);
}
/**
* Goes through the meta data and puts each value in to a
* bundle as the correct type.
*
* Note that this will convert resource identifiers specified
* via the value attribute as well.
* @param meta Meta data to put in to a bundle
* @return bundle containing the meta data
*/
private Bundle metaDataToBundle(Map<String, Object> meta) {
if (meta.size() == 0) {
return null;
}
Bundle bundle = new Bundle();
for (Map.Entry<String,Object> entry : meta.entrySet()) {
if (Boolean.class.isInstance(entry.getValue())) {
bundle.putBoolean(entry.getKey(), (Boolean) entry.getValue());
} else if (Float.class.isInstance(entry.getValue())) {
bundle.putFloat(entry.getKey(), (Float) entry.getValue());
} else if (Integer.class.isInstance(entry.getValue())) {
bundle.putInt(entry.getKey(), (Integer) entry.getValue());
} else {
bundle.putString(entry.getKey(), entry.getValue().toString());
}
}
return bundle;
}
@Override
public void getPackageSizeInfo(String pkgName, int uid, final IPackageStatsObserver callback) {
this.getPackageSizeInfoAsUser(pkgName, uid, callback);
}
@Override
public void getPackageSizeInfoAsUser(String pkgName, int uid, final IPackageStatsObserver callback) {
final PackageStats packageStats = packageStatsMap.get(pkgName);
new Handler(Looper.getMainLooper()).post(new Runnable() {
@Override
public void run() {
try {
callback.onGetStatsCompleted(packageStats, packageStats != null);
} catch (RemoteException remoteException) {
remoteException.rethrowFromSystemServer();
}
}
});
}
@Override
public int checkSignatures(String packageName1, String packageName2) {
try {
PackageInfo packageInfo1 = getPackageInfo(packageName1, GET_SIGNATURES);
PackageInfo packageInfo2 = getPackageInfo(packageName2, GET_SIGNATURES);
return compareSignature(packageInfo1.signatures, packageInfo2.signatures);
} catch (NameNotFoundException e) {
return SIGNATURE_UNKNOWN_PACKAGE;
}
}
// From com.android.server.pm.PackageManagerService.compareSignatures().
private static int compareSignature(Signature[] signatures1, Signature[] signatures2) {
if (signatures1 == null) {
return (signatures2 == null) ? SIGNATURE_NEITHER_SIGNED
: SIGNATURE_FIRST_NOT_SIGNED;
}
if (signatures2 == null) {
return SIGNATURE_SECOND_NOT_SIGNED;
}
if (signatures1.length != signatures2.length) {
return SIGNATURE_NO_MATCH;
}
HashSet<Signature> signatures1set = new HashSet<>(Arrays.asList(signatures1));
HashSet<Signature> signatures2set = new HashSet<>(Arrays.asList(signatures2));
return signatures1set.equals(signatures2set) ? SIGNATURE_MATCH : SIGNATURE_NO_MATCH;
}
@Override
public String getInstallerPackageName(String packageName) {
return packageInstallerMap.get(packageName);
}
@Override
public void setInstallerPackageName(String targetPackage, String installerPackageName) {
packageInstallerMap.put(targetPackage, installerPackageName);
}
public void setDependencies(AndroidManifest applicationManifest, ResourceTable appResourceTable) {
this.applicationManifest = applicationManifest;
this.appResourceTable = appResourceTable;
}
public static class IntentComparator implements Comparator<Intent> {
@Override
public int compare(Intent i1, Intent i2) {
if (i1 == null && i2 == null) return 0;
if (i1 == null && i2 != null) return -1;
if (i1 != null && i2 == null) return 1;
if (i1.equals(i2)) return 0;
String action1 = i1.getAction();
String action2 = i2.getAction();
if (action1 == null && action2 != null) return -1;
if (action1 != null && action2 == null) return 1;
if (action1 != null && action2 != null) {
if (!action1.equals(action2)) {
return action1.compareTo(action2);
}
}
Uri data1 = i1.getData();
Uri data2 = i2.getData();
if (data1 == null && data2 != null) return -1;
if (data1 != null && data2 == null) return 1;
if (data1 != null && data2 != null) {
if (!data1.equals(data2)) {
return data1.compareTo(data2);
}
}
ComponentName component1 = i1.getComponent();
ComponentName component2 = i2.getComponent();
if (component1 == null && component2 != null) return -1;
if (component1 != null && component2 == null) return 1;
if (component1 != null && component2 != null) {
if (!component1.equals(component2)) {
return component1.compareTo(component2);
}
}
String package1 = i1.getPackage();
String package2 = i2.getPackage();
if (package1 == null && package2 != null) return -1;
if (package1 != null && package2 == null) return 1;
if (package1 != null && package2 != null) {
if (!package1.equals(package2)) {
return package1.compareTo(package2);
}
}
Set<String> categories1 = i1.getCategories();
Set<String> categories2 = i2.getCategories();
if (categories1 == null) return categories2 == null ? 0 : -1;
if (categories2 == null) return 1;
if (categories1.size() > categories2.size()) return 1;
if (categories1.size() < categories2.size()) return -1;
String[] array1 = categories1.toArray(new String[0]);
String[] array2 = categories2.toArray(new String[0]);
Arrays.sort(array1);
Arrays.sort(array2);
for (int i = 0; i < array1.length; ++i) {
int val = array1[i].compareTo(array2[i]);
if (val != 0) return val;
}
return 0;
}
}
}