All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.robolectric.shadows.ShadowPackageManager Maven / Gradle / Ivy

package org.robolectric.shadows;

import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.EXTRA_VERIFICATION_ID;
import static android.content.pm.PackageManager.GET_ACTIVITIES;
import static android.content.pm.PackageManager.GET_CONFIGURATIONS;
import static android.content.pm.PackageManager.GET_GIDS;
import static android.content.pm.PackageManager.GET_INSTRUMENTATION;
import static android.content.pm.PackageManager.GET_INTENT_FILTERS;
import static android.content.pm.PackageManager.GET_META_DATA;
import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.content.pm.PackageManager.GET_PROVIDERS;
import static android.content.pm.PackageManager.GET_RECEIVERS;
import static android.content.pm.PackageManager.GET_RESOLVED_FILTER;
import static android.content.pm.PackageManager.GET_SERVICES;
import static android.content.pm.PackageManager.GET_SHARED_LIBRARY_FILES;
import static android.content.pm.PackageManager.GET_SIGNATURES;
import static android.content.pm.PackageManager.GET_URI_PERMISSION_PATTERNS;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
import static android.content.pm.PackageManager.SIGNATURE_FIRST_NOT_SIGNED;
import static android.content.pm.PackageManager.SIGNATURE_MATCH;
import static android.content.pm.PackageManager.SIGNATURE_NEITHER_SIGNED;
import static android.content.pm.PackageManager.SIGNATURE_NO_MATCH;
import static android.content.pm.PackageManager.SIGNATURE_SECOND_NOT_SIGNED;
import static android.content.pm.PackageManager.VERIFICATION_ALLOW;
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
import static android.os.Build.VERSION_CODES.KITKAT;
import static android.os.Build.VERSION_CODES.N;
import static android.os.Build.VERSION_CODES.TIRAMISU;
import static java.util.Arrays.asList;
import static org.robolectric.util.reflector.Reflector.reflector;

import android.Manifest;
import android.annotation.UserIdInt;
import android.app.Application;
import android.content.BroadcastReceiver;
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.ComponentInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.InstallSourceInfo;
import android.content.pm.ModuleInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.Component;
import android.content.pm.PackageParser.IntentInfo;
import android.content.pm.PackageParser.Package;
import android.content.pm.PackageParser.PermissionGroup;
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.Signature;
import android.content.pm.pkg.FrameworkPackageUserState;
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.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
import android.util.Pair;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import java.lang.reflect.Array;
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.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.function.BiConsumer;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.RealObject;
import org.robolectric.annotation.Resetter;
import org.robolectric.shadows.ShadowPackageParser._PackageParser_;
import org.robolectric.util.reflector.Direct;
import org.robolectric.util.reflector.ForType;

@SuppressWarnings("NewApi")
@Implements(PackageManager.class)
public class ShadowPackageManager {
  static final String TAG = "PackageManager";

  @RealObject PackageManager realPackageManager;

  // The big-lock to control concurrency in this class.
  // Note: not all APIs in this class have been made thread safe yet.
  static final Object lock = new Object();
  static Map permissionRationaleMap = new HashMap<>();
  static List systemAvailableFeatures = new ArrayList<>();
  static final List systemSharedLibraryNames = new ArrayList<>();

  @GuardedBy("lock")
  static final Map packageInfos = new LinkedHashMap<>();

  @GuardedBy("lock")
  static final Map moduleInfos = new LinkedHashMap<>();

  static final Set permissionListeners = new CopyOnWriteArraySet<>();

  // Those maps contain filter for components. If component exists but doesn't have filters,
  // it will have an entry in the map with an empty list.
  static final SortedMap> activityFilters = new TreeMap<>();
  static final SortedMap> serviceFilters = new TreeMap<>();
  static final SortedMap> providerFilters = new TreeMap<>();
  static final SortedMap> receiverFilters = new TreeMap<>();

  private static Map packageArchiveInfo = new HashMap<>();
  static final Map packageStatsMap = new HashMap<>();
  static final Map packageInstallerMap = new HashMap<>();
  static final Map packageInstallSourceInfoMap = new HashMap<>();
  static final Map packagesForUid = new HashMap<>();
  static final Map uidForPackage = new HashMap<>();
  static final Map namesForUid = new HashMap<>();
  static final Map verificationResults = new HashMap<>();

  @GuardedBy("lock")
  static final Map verificationTimeoutExtension = new HashMap<>();

  @GuardedBy("lock")
  static final Map verificationCodeAtTimeoutExtension = new HashMap<>();

  static final Map currentToCanonicalNames = new HashMap<>();
  static final Map canonicalToCurrentNames = new HashMap<>();
  static final Map componentList = new LinkedHashMap<>();
  static final Map drawableList = new LinkedHashMap<>();
  static final Map applicationIcons = new HashMap<>();
  static final Map unbadgedApplicationIcons = new HashMap<>();
  static final Map systemFeatureList =
      new LinkedHashMap<>(SystemFeatureListInitializer.getSystemFeatures());
  static final SortedMap> preferredActivities = new TreeMap<>();
  static final SortedMap> persistentPreferredActivities =
      new TreeMap<>();
  static final Map, Drawable> drawables = new LinkedHashMap<>();
  /**
   * Map of package names to an inner map where the key is the resource id which fetches its
   * corresponding text.
   */
  static final Map> stringResources = new HashMap<>();

  static final Map applicationEnabledSettingMap = new HashMap<>();
  static Map extraPermissions = new HashMap<>();
  static Map permissionGroups = new HashMap<>();
  /**
   * Map of package names to an inner map where the key is the permission and the integer represents
   * the permission flags set for that particular permission
   */
  static Map> permissionFlags = new HashMap<>();

  public static Map resources = new HashMap<>();
  static final Map> resolveInfoForIntent =
      new TreeMap<>(new IntentComparator());

  @GuardedBy("lock")
  static Set deletedPackages = new HashSet<>();

  static Map pendingDeleteCallbacks = new HashMap<>();
  static Set hiddenPackages = new HashSet<>();
  static Multimap sequenceNumberChangedPackagesMap = HashMultimap.create();
  static boolean canRequestPackageInstalls = false;
  static boolean safeMode = false;
  static boolean whitelisted = false;
  boolean shouldShowActivityChooser = false;
  static final Map distractingPackageRestrictions = new ConcurrentHashMap<>();

  /**
   * Makes sure that given activity exists.
   *
   * If the activity doesn't exist yet, it will be created with {@code applicationInfo} set to an
   * existing application, or if it doesn't exist, a new package will be created.
   *
   * @return existing or newly created activity info.
   */
  public ActivityInfo addActivityIfNotPresent(ComponentName componentName) {
    return addComponent(
        activityFilters,
        p -> p.activities,
        (p, a) -> p.activities = a,
        updateName(componentName, new ActivityInfo()),
        false);
  }

  /**
   * Makes sure that given service exists.
   *
   * If the service doesn't exist yet, it will be created with {@code applicationInfo} set to an
   * existing application, or if it doesn't exist, a new package will be created.
   *
   * @return existing or newly created service info.
   */
  public ServiceInfo addServiceIfNotPresent(ComponentName componentName) {
    return addComponent(
        serviceFilters,
        p -> p.services,
        (p, a) -> p.services = a,
        updateName(componentName, new ServiceInfo()),
        false);
  }

  /**
   * Makes sure that given receiver exists.
   *
   * If the receiver doesn't exist yet, it will be created with {@code applicationInfo} set to an
   * existing application, or if it doesn't exist, a new package will be created.
   *
   * @return existing or newly created receiver info.
   */
  public ActivityInfo addReceiverIfNotPresent(ComponentName componentName) {
    return addComponent(
        receiverFilters,
        p -> p.receivers,
        (p, a) -> p.receivers = a,
        updateName(componentName, new ActivityInfo()),
        false);
  }

  /**
   * Makes sure that given provider exists.
   *
   * If the provider doesn't exist yet, it will be created with {@code applicationInfo} set to an
   * existing application, or if it doesn't exist, a new package will be created.
   *
   * @return existing or newly created provider info.
   */
  public ProviderInfo addProviderIfNotPresent(ComponentName componentName) {
    return addComponent(
        providerFilters,
        p -> p.providers,
        (p, a) -> p.providers = a,
        updateName(componentName, new ProviderInfo()),
        false);
  }

  private  C updateName(ComponentName name, C component) {
    component.name = name.getClassName();
    component.packageName = name.getPackageName();
    if (component.applicationInfo != null) {
      component.applicationInfo.packageName = component.packageName;
    }
    return component;
  }

  /**
   * Adds or updates given activity in the system.
   *
   * If activity with the same {@link ComponentInfo#name} and {@code ComponentInfo#packageName}
   * exists it will be updated. Its {@link ComponentInfo#applicationInfo} is always set to {@link
   * ApplicationInfo} already existing in the system, but if no application exists a new one will
   * be created using {@link ComponentInfo#applicationInfo} in this component.
   */
  public void addOrUpdateActivity(ActivityInfo activityInfo) {
    addComponent(
        activityFilters,
        p -> p.activities,
        (p, a) -> p.activities = a,
        new ActivityInfo(activityInfo),
        true);
  }

  /**
   * Adds or updates given service in the system.
   *
   * If service with the same {@link ComponentInfo#name} and {@code ComponentInfo#packageName}
   * exists it will be updated. Its {@link ComponentInfo#applicationInfo} is always set to {@link
   * ApplicationInfo} already existing in the system, but if no application exists a new one will be
   * created using {@link ComponentInfo#applicationInfo} in this component.
   */
  public void addOrUpdateService(ServiceInfo serviceInfo) {
    addComponent(
        serviceFilters,
        p -> p.services,
        (p, a) -> p.services = a,
        new ServiceInfo(serviceInfo),
        true);
  }

  /**
   * Adds or updates given broadcast receiver in the system.
   *
   * If broadcast receiver with the same {@link ComponentInfo#name} and {@code
   * ComponentInfo#packageName} exists it will be updated. Its {@link ComponentInfo#applicationInfo}
   * is always set to {@link ApplicationInfo} already existing in the system, but if no
   * application exists a new one will be created using {@link ComponentInfo#applicationInfo} in
   * this component.
   */
  public void addOrUpdateReceiver(ActivityInfo receiverInfo) {
    addComponent(
        receiverFilters,
        p -> p.receivers,
        (p, a) -> p.receivers = a,
        new ActivityInfo(receiverInfo),
        true);
  }

  /**
   * Adds or updates given content provider in the system.
   *
   * If content provider with the same {@link ComponentInfo#name} and {@code
   * ComponentInfo#packageName} exists it will be updated. Its {@link ComponentInfo#applicationInfo}
   * is always set to {@link ApplicationInfo} already existing in the system, but if no
   * application exists a new one will be created using {@link ComponentInfo#applicationInfo} in
   * this component.
   */
  public void addOrUpdateProvider(ProviderInfo providerInfo) {
    addComponent(
        providerFilters,
        p -> p.providers,
        (p, a) -> p.providers = a,
        new ProviderInfo(providerInfo),
        true);
  }

  /**
   * Removes activity from the package manager.
   *
   * @return the removed component or {@code null} if no such component existed.
   */
  @Nullable
  public ActivityInfo removeActivity(ComponentName componentName) {
    return removeComponent(
        componentName, activityFilters, p -> p.activities, (p, a) -> p.activities = a);
  }

  /**
   * Removes service from the package manager.
   *
   * @return the removed component or {@code null} if no such component existed.
   */
  @Nullable
  public ServiceInfo removeService(ComponentName componentName) {
    return removeComponent(
        componentName, serviceFilters, p -> p.services, (p, a) -> p.services = a);
  }

  /**
   * Removes content provider from the package manager.
   *
   * @return the removed component or {@code null} if no such component existed.
   */
  @Nullable
  public ProviderInfo removeProvider(ComponentName componentName) {
    return removeComponent(
        componentName, providerFilters, p -> p.providers, (p, a) -> p.providers = a);
  }

  /**
   * Removes broadcast receiver from the package manager.
   *
   * @return the removed component or {@code null} if no such component existed.
   */
  @Nullable
  public ActivityInfo removeReceiver(ComponentName componentName) {
    return removeComponent(
        componentName, receiverFilters, p -> p.receivers, (p, a) -> p.receivers = a);
  }

  private  C addComponent(
      SortedMap> filtersMap,
      Function componentArrayInPackage,
      BiConsumer componentsSetter,
      C newComponent,
      boolean updateIfExists) {
    synchronized (lock) {
      String packageName = newComponent.packageName;
      if (packageName == null && newComponent.applicationInfo != null) {
        packageName = newComponent.applicationInfo.packageName;
      }
      if (packageName == null) {
        throw new IllegalArgumentException("Component needs a package name");
      }
      if (newComponent.name == null) {
        throw new IllegalArgumentException("Component needs a name");
      }
      PackageInfo packageInfo = packageInfos.get(packageName);
      if (packageInfo == null) {
        packageInfo = new PackageInfo();
        packageInfo.packageName = packageName;
        packageInfo.applicationInfo = newComponent.applicationInfo;
        installPackage(packageInfo);
        packageInfo = packageInfos.get(packageName);
      }
      newComponent.applicationInfo = packageInfo.applicationInfo;
      C[] components = componentArrayInPackage.apply(packageInfo);
      if (components == null) {
        @SuppressWarnings("unchecked")
        C[] newComponentArray = (C[]) Array.newInstance(newComponent.getClass(), 0);
        components = newComponentArray;
      } else {
        for (int i = 0; i < components.length; i++) {
          if (newComponent.name.equals(components[i].name)) {
            if (updateIfExists) {
              components[i] = newComponent;
            }
            return components[i];
          }
        }
      }
      components = Arrays.copyOf(components, components.length + 1);
      componentsSetter.accept(packageInfo, components);
      components[components.length - 1] = newComponent;

      filtersMap.put(
          new ComponentName(newComponent.packageName, newComponent.name), new ArrayList<>());
      return newComponent;
    }
  }

  @Nullable
  private  C removeComponent(
      ComponentName componentName,
      SortedMap> filtersMap,
      Function componentArrayInPackage,
      BiConsumer componentsSetter) {
    synchronized (lock) {
      filtersMap.remove(componentName);
      String packageName = componentName.getPackageName();
      PackageInfo packageInfo = packageInfos.get(packageName);
      if (packageInfo == null) {
        return null;
      }
      C[] components = componentArrayInPackage.apply(packageInfo);
      if (components == null) {
        return null;
      }
      for (int i = 0; i < components.length; i++) {
        C component = components[i];
        if (componentName.getClassName().equals(component.name)) {
          C[] newComponents;
          if (components.length == 1) {
            newComponents = null;
          } else {
            newComponents = Arrays.copyOf(components, components.length - 1);
            System.arraycopy(components, i + 1, newComponents, i, components.length - i - 1);
          }
          componentsSetter.accept(packageInfo, newComponents);
          return component;
        }
      }
      return null;
    }
  }

  /**
   * Settings for a particular package.
   *
   * This class mirrors {@link com.android.server.pm.PackageSetting}, which is used by {@link
   * PackageManager}.
   */
  public static class PackageSetting {

    /** Whether the package is suspended in {@link PackageManager}. */
    private boolean suspended = false;

    /** The message to be displayed to the user when they try to launch the app. */
    private String dialogMessage = null;

    /**
     * The info for how to display the dialog that shows to the user when they try to launch the
     * app. On Q, one of this field or dialogMessage will be present when a package is suspended.
     */
    private Object dialogInfo = null;

    /** An optional {@link PersistableBundle} shared with the app. */
    private PersistableBundle suspendedAppExtras = null;

    /** An optional {@link PersistableBundle} shared with the launcher. */
    private PersistableBundle suspendedLauncherExtras = null;

    public PackageSetting() {}

    public PackageSetting(PackageSetting that) {
      this.suspended = that.suspended;
      this.dialogMessage = that.dialogMessage;
      this.dialogInfo = that.dialogInfo;
      this.suspendedAppExtras = deepCopyNullablePersistableBundle(that.suspendedAppExtras);
      this.suspendedLauncherExtras =
          deepCopyNullablePersistableBundle(that.suspendedLauncherExtras);
    }

    /**
     * Sets the suspension state of the package.
     *
     * 

If {@code suspended} is false, {@code dialogInfo}, {@code appExtras}, and {@code * launcherExtras} will be ignored. */ void setSuspended( boolean suspended, String dialogMessage, /* SuspendDialogInfo */ Object dialogInfo, PersistableBundle appExtras, PersistableBundle launcherExtras) { Preconditions.checkArgument(dialogMessage == null || dialogInfo == null); this.suspended = suspended; this.dialogMessage = suspended ? dialogMessage : null; this.dialogInfo = suspended ? dialogInfo : null; this.suspendedAppExtras = suspended ? deepCopyNullablePersistableBundle(appExtras) : null; this.suspendedLauncherExtras = suspended ? deepCopyNullablePersistableBundle(launcherExtras) : null; } public boolean isSuspended() { return suspended; } public String getDialogMessage() { return dialogMessage; } public Object getDialogInfo() { return dialogInfo; } public PersistableBundle getSuspendedAppExtras() { return suspendedAppExtras; } public PersistableBundle getSuspendedLauncherExtras() { return suspendedLauncherExtras; } private static PersistableBundle deepCopyNullablePersistableBundle(PersistableBundle bundle) { return bundle == null ? null : bundle.deepCopy(); } } static final Map packageSettings = new HashMap<>(); // From com.android.server.pm.PackageManagerService.compareSignatures(). 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 signatures1set = new HashSet<>(asList(signatures1)); HashSet signatures2set = new HashSet<>(asList(signatures2)); return signatures1set.equals(signatures2set) ? SIGNATURE_MATCH : SIGNATURE_NO_MATCH; } // TODO(christianw): reconcile with AndroidTestEnvironment.setUpPackageStorage private static void setUpPackageStorage(ApplicationInfo applicationInfo) { if (applicationInfo.sourceDir == null) { applicationInfo.sourceDir = createTempDir(applicationInfo.packageName + "-sourceDir"); } if (applicationInfo.dataDir == null) { applicationInfo.dataDir = createTempDir(applicationInfo.packageName + "-dataDir"); } if (applicationInfo.publicSourceDir == null) { applicationInfo.publicSourceDir = applicationInfo.sourceDir; } if (RuntimeEnvironment.getApiLevel() >= N) { applicationInfo.credentialProtectedDataDir = createTempDir("userDataDir"); applicationInfo.deviceProtectedDataDir = createTempDir("deviceDataDir"); } } private static String createTempDir(String name) { return RuntimeEnvironment.getTempDirectory() .createIfNotExists(name) .toAbsolutePath() .toString(); } /** * Sets extra resolve infos for an intent. * * Those entries are added to whatever might be in the manifest already. * * Note that all resolve infos will have {@link ResolveInfo#isDefault} field set to {@code * true} to allow their resolution for implicit intents. If this is not what you want, then you * still have the reference to those ResolveInfos, and you can set the field back to {@code * false}. * * @deprecated see the note on {@link #addResolveInfoForIntent(Intent, ResolveInfo)}. */ @Deprecated public void setResolveInfosForIntent(Intent intent, List info) { resolveInfoForIntent.remove(intent); for (ResolveInfo resolveInfo : info) { addResolveInfoForIntent(intent, resolveInfo); } } /** @deprecated see note on {@link #addResolveInfoForIntent(Intent, ResolveInfo)}. */ @Deprecated public void addResolveInfoForIntent(Intent intent, List info) { setResolveInfosForIntent(intent, info); } /** * Adds extra resolve info for an intent. * * Note that this resolve info will have {@link ResolveInfo#isDefault} field set to {@code * true} to allow its resolution for implicit intents. If this is not what you want, then please * use {@link #addResolveInfoForIntentNoDefaults} instead. * * @deprecated use {@link #addIntentFilterForComponent} instead and if the component doesn't exist * add it using any of {@link #installPackage}, {@link #addOrUpdateActivity}, {@link * #addActivityIfNotPresent} or their counterparts for other types of components. */ @Deprecated public void addResolveInfoForIntent(Intent intent, ResolveInfo info) { info.isDefault = true; ComponentInfo[] componentInfos = new ComponentInfo[] { info.activityInfo, info.serviceInfo, Build.VERSION.SDK_INT >= KITKAT ? info.providerInfo : null }; for (ComponentInfo component : componentInfos) { if (component != null && component.applicationInfo != null) { component.applicationInfo.flags |= ApplicationInfo.FLAG_INSTALLED; if (component.applicationInfo.processName == null) { component.applicationInfo.processName = component.applicationInfo.packageName; } } } if (info.match == 0) { info.match = Integer.MAX_VALUE; // make sure, that this is as good match as possible. } addResolveInfoForIntentNoDefaults(intent, info); } /** * Adds the {@code info} as {@link ResolveInfo} for the intent but without applying any default * values. * * In particular it will not make the {@link ResolveInfo#isDefault} field {@code true}, that * means that this resolve info will not resolve for {@link Intent#resolveActivity} and {@link * Context#startActivity}. * * @deprecated see the note on {@link #addResolveInfoForIntent(Intent, ResolveInfo)}. */ @Deprecated public void addResolveInfoForIntentNoDefaults(Intent intent, ResolveInfo info) { Preconditions.checkNotNull(info); List infoList = resolveInfoForIntent.get(intent); if (infoList == null) { infoList = new ArrayList<>(); resolveInfoForIntent.put(intent, infoList); } infoList.add(info); } /** * Removes {@link ResolveInfo}s registered using {@link #addResolveInfoForIntent}. * * @deprecated see note on {@link #addResolveInfoForIntent(Intent, ResolveInfo)}. */ @Deprecated public void removeResolveInfosForIntent(Intent intent, String packageName) { List infoList = resolveInfoForIntent.get(intent); if (infoList == null) { infoList = new ArrayList<>(); resolveInfoForIntent.put(intent, infoList); } for (Iterator iterator = infoList.iterator(); iterator.hasNext(); ) { ResolveInfo resolveInfo = iterator.next(); if (getPackageName(resolveInfo).equals(packageName)) { iterator.remove(); } } } private static String getPackageName(ResolveInfo resolveInfo) { if (resolveInfo.resolvePackageName != null) { return resolveInfo.resolvePackageName; } else if (resolveInfo.activityInfo != null) { return resolveInfo.activityInfo.packageName; } else if (resolveInfo.serviceInfo != null) { return resolveInfo.serviceInfo.packageName; } else if (resolveInfo.providerInfo != null) { return resolveInfo.providerInfo.packageName; } throw new IllegalStateException( "Could not find package name for ResolveInfo " + resolveInfo.toString()); } public void addActivityIcon(ComponentName component, Drawable drawable) { drawableList.put(component, drawable); } public void addActivityIcon(Intent intent, Drawable drawable) { drawableList.put(intent.getComponent(), drawable); } public void setApplicationIcon(String packageName, Drawable drawable) { applicationIcons.put(packageName, drawable); } public void setUnbadgedApplicationIcon(String packageName, Drawable drawable) { unbadgedApplicationIcons.put(packageName, drawable); } /** * Return the flags set in call to {@link * android.app.ApplicationPackageManager#setComponentEnabledSetting(ComponentName, int, int)}. * * @param componentName The component name. * @return The flags. */ public int getComponentEnabledSettingFlags(ComponentName componentName) { ComponentState state = componentList.get(componentName); return state != null ? state.flags : 0; } /** * Installs a module with the {@link PackageManager} as long as it is not {@code null} * *

In order to create ModuleInfo objects in a valid state please use {@link ModuleInfoBuilder}. */ public void installModule(Object moduleInfoObject) { synchronized (lock) { ModuleInfo moduleInfo = (ModuleInfo) moduleInfoObject; if (moduleInfo != null) { moduleInfos.put(moduleInfo.getPackageName(), moduleInfo); // Checking to see if package exists in the system if (packageInfos.get(moduleInfo.getPackageName()) == null) { ApplicationInfo applicationInfo = new ApplicationInfo(); applicationInfo.packageName = moduleInfo.getPackageName(); applicationInfo.name = moduleInfo.getName().toString(); PackageInfo packageInfo = new PackageInfo(); packageInfo.applicationInfo = applicationInfo; packageInfo.packageName = moduleInfo.getPackageName(); installPackage(packageInfo); } } } } /** * Deletes a module when given the module's package name {@link ModuleInfo} be sure to give the * correct name as this method does not ensure existence of the module before deletion. Since * module installation ensures that a package exists in the device, also delete the package for * full deletion. * * @param packageName should be the value of {@link ModuleInfo#getPackageName}. * @return deleted module of {@code null} if no module with this name exists. */ public Object deleteModule(String packageName) { synchronized (lock) { // Removes the accompanying package installed with the module return moduleInfos.remove(packageName); } } /** * Installs a package with the {@link PackageManager}. * * In order to create PackageInfo objects in a valid state please use {@link * androidx.test.core.content.pm.PackageInfoBuilder}. * * This method automatically simulates instalation of a package in the system, so it adds a * flag {@link ApplicationInfo#FLAG_INSTALLED} to the application info and makes sure it exits. It * will update applicationInfo in package components as well. * * If you don't want the package to be installed, use {@link #addPackageNoDefaults} instead. */ public void installPackage(PackageInfo packageInfo) { ApplicationInfo appInfo = packageInfo.applicationInfo; if (appInfo == null) { appInfo = new ApplicationInfo(); packageInfo.applicationInfo = appInfo; } if (appInfo.packageName == null) { appInfo.packageName = packageInfo.packageName; } if (appInfo.processName == null) { appInfo.processName = appInfo.packageName; } if (appInfo.targetSdkVersion == 0) { appInfo.targetSdkVersion = RuntimeEnvironment.getApiLevel(); } appInfo.flags |= ApplicationInfo.FLAG_INSTALLED; ComponentInfo[][] componentInfoArrays = new ComponentInfo[][] { packageInfo.activities, packageInfo.services, packageInfo.providers, packageInfo.receivers, }; int uniqueNameCounter = 0; for (ComponentInfo[] componentInfos : componentInfoArrays) { if (componentInfos == null) { continue; } for (ComponentInfo componentInfo : componentInfos) { if (componentInfo.name == null) { componentInfo.name = appInfo.packageName + ".DefaultName" + uniqueNameCounter++; componentInfo.packageName = packageInfo.packageName; } componentInfo.applicationInfo = appInfo; componentInfo.packageName = appInfo.packageName; if (componentInfo.processName == null) { componentInfo.processName = appInfo.processName; } } } addPackageNoDefaults(packageInfo); } /** Adds install source information for a package. */ public void setInstallSourceInfo( String packageName, String initiatingPackage, String installerPackage) { packageInstallSourceInfoMap.put( packageName, new InstallSourceInfo(initiatingPackage, null, null, installerPackage)); } /** * Adds a package to the {@link PackageManager}, but doesn't set any default values on it. * *

Right now it will not set {@link ApplicationInfo#FLAG_INSTALLED} flag on its application, so * if not set explicitly, it will be treated as not installed. */ public void addPackageNoDefaults(PackageInfo packageInfo) { PackageStats packageStats = new PackageStats(packageInfo.packageName); addPackage(packageInfo, packageStats); } /** * Installs a package with its stats with the {@link PackageManager}. * *

This method doesn't add any defaults to the {@code packageInfo} parameters. You should make * sure it is valid (see {@link #installPackage(PackageInfo)}). */ public void addPackage(PackageInfo packageInfo, PackageStats packageStats) { synchronized (lock) { if (packageInfo.applicationInfo != null && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_INSTALLED) == 0) { Log.w(TAG, "Adding not installed package: " + packageInfo.packageName); } Preconditions.checkArgument(packageInfo.packageName.equals(packageStats.packageName)); packageInfos.put(packageInfo.packageName, packageInfo); packageStatsMap.put(packageInfo.packageName, packageStats); packageSettings.put(packageInfo.packageName, new PackageSetting()); applicationEnabledSettingMap.put( packageInfo.packageName, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT); if (packageInfo.applicationInfo != null) { uidForPackage.put(packageInfo.packageName, packageInfo.applicationInfo.uid); namesForUid.put(packageInfo.applicationInfo.uid, packageInfo.packageName); } } } /** @deprecated Use {@link #installPackage(PackageInfo)} instead. */ @Deprecated public void addPackage(String packageName) { PackageInfo packageInfo = new PackageInfo(); packageInfo.packageName = packageName; ApplicationInfo applicationInfo = new ApplicationInfo(); applicationInfo.packageName = packageName; // TODO: setUpPackageStorage should be in installPackage but we need to fix all tests first setUpPackageStorage(applicationInfo); packageInfo.applicationInfo = applicationInfo; installPackage(packageInfo); } /** This method is getting renamed to {link {@link #installPackage}. */ @Deprecated public void addPackage(PackageInfo packageInfo) { installPackage(packageInfo); } /** * Testing API allowing to retrieve internal package representation. * * This will allow to modify the package in a way visible to Robolectric, as this is * Robolectric's internal full package representation. * * Note that maybe a better way is to just modify the test manifest to make those modifications * in a standard way. * * Retrieving package info using {@link PackageManager#getPackageInfo} / {@link * PackageManager#getApplicationInfo} will return defensive copies that will be stripped out of * information according to provided flags. Don't use it to modify Robolectric state. */ public PackageInfo getInternalMutablePackageInfo(String packageName) { synchronized (lock) { return packageInfos.get(packageName); } } public void addPermissionInfo(PermissionInfo permissionInfo) { extraPermissions.put(permissionInfo.name, permissionInfo); } /** * Adds {@code packageName} to the list of changed packages for the particular {@code * sequenceNumber}. * * @param sequenceNumber has to be >= 0 * @param packageName name of the package that was changed */ public void addChangedPackage(int sequenceNumber, String packageName) { if (sequenceNumber < 0) { return; } sequenceNumberChangedPackagesMap.put(sequenceNumber, packageName); } /** * Allows overriding or adding permission-group elements. These would be otherwise specified by * either (the * system)[https://developer.android.com/guide/topics/permissions/requesting.html#perm-groups] or * by (the app * itself)[https://developer.android.com/guide/topics/manifest/permission-group-element.html], as * part of its manifest * * {@link android.content.pm.PackageParser.PermissionGroup}s added through this method have * precedence over those specified with the same name by one of the aforementioned methods. * * @see PackageManager#getAllPermissionGroups(int) * @see PackageManager#getPermissionGroupInfo(String, int) */ public void addPermissionGroupInfo(PermissionGroupInfo permissionGroupInfo) { permissionGroups.put(permissionGroupInfo.name, permissionGroupInfo); } public void removePackage(String packageName) { synchronized (lock) { packageInfos.remove(packageName); packageSettings.remove(packageName); } } public void setSystemFeature(String name, boolean supported) { systemFeatureList.put(name, supported); } public void addDrawableResolution(String packageName, int resourceId, Drawable drawable) { drawables.put(new Pair(packageName, resourceId), drawable); } public void setNameForUid(int uid, String name) { namesForUid.put(uid, name); } public void setPackagesForCallingUid(String... packagesForCallingUid) { packagesForUid.put(Binder.getCallingUid(), packagesForCallingUid); for (String packageName : packagesForCallingUid) { uidForPackage.put(packageName, Binder.getCallingUid()); } } public void setPackagesForUid(int uid, String... packagesForCallingUid) { packagesForUid.put(uid, packagesForCallingUid); for (String packageName : packagesForCallingUid) { uidForPackage.put(packageName, uid); } } @Implementation @Nullable protected String[] getPackagesForUid(int uid) { return packagesForUid.get(uid); } public void setPackageArchiveInfo(String archiveFilePath, PackageInfo packageInfo) { packageArchiveInfo.put(archiveFilePath, packageInfo); } public int getVerificationResult(int id) { Integer result = verificationResults.get(id); if (result == null) { // 0 isn't a "valid" result, so we can check for the case when verification isn't // called, if needed return 0; } return result; } public long getVerificationExtendedTimeout(int id) { synchronized (lock) { return verificationTimeoutExtension.getOrDefault(id, 0L); } } public int getVerificationCodeAtTimeoutExtension(int id) { synchronized (lock) { return verificationCodeAtTimeoutExtension.getOrDefault(id, VERIFICATION_ALLOW); } } public void triggerInstallVerificationTimeout(Application appContext, int id) { Intent intent = new Intent(Intent.ACTION_PACKAGE_VERIFIED); intent.putExtra(EXTRA_VERIFICATION_ID, id); intent.putExtra( PackageManager.EXTRA_VERIFICATION_RESULT, getVerificationCodeAtTimeoutExtension(id)); // Send PACKAGE_VERIFIED broadcast to trigger the verification timeout. // Replacement api does not return the actual receiver objects. @SuppressWarnings("deprecation") List receivers = ShadowApplication.getShadowInstrumentation().getReceiversForIntent(intent); for (BroadcastReceiver receiver : receivers) { receiver.onReceive(appContext, intent); } } public void setShouldShowRequestPermissionRationale(String permission, boolean show) { permissionRationaleMap.put(permission, show); } public void addSystemAvailableFeature(FeatureInfo featureInfo) { systemAvailableFeatures.add(featureInfo); } public void clearSystemAvailableFeatures() { systemAvailableFeatures.clear(); } /** Adds a value to be returned by {@link PackageManager#getSystemSharedLibraryNames()}. */ public void addSystemSharedLibraryName(String name) { systemSharedLibraryNames.add(name); } /** Clears the values returned by {@link PackageManager#getSystemSharedLibraryNames()}. */ public void clearSystemSharedLibraryNames() { systemSharedLibraryNames.clear(); } @Deprecated /** @deprecated use {@link #addCanonicalName} instead.} */ public void addCurrentToCannonicalName(String currentName, String canonicalName) { currentToCanonicalNames.put(currentName, canonicalName); } /** * Adds a canonical package name for a package. * *

This will be reflected when calling {@link * PackageManager#currentToCanonicalPackageNames(String[])} or {@link * PackageManager#canonicalToCurrentPackageNames(String[])} (String[])}. */ public void addCanonicalName(String currentName, String canonicalName) { currentToCanonicalNames.put(currentName, canonicalName); canonicalToCurrentNames.put(canonicalName, currentName); } /** * Sets if the {@link PackageManager} is allowed to request package installs through package * installer. */ public void setCanRequestPackageInstalls(boolean canRequestPackageInstalls) { ShadowPackageManager.canRequestPackageInstalls = canRequestPackageInstalls; } @Implementation(minSdk = N) protected List queryBroadcastReceiversAsUser( Intent intent, int flags, UserHandle userHandle) { return null; } @Implementation(minSdk = JELLY_BEAN_MR1) protected List queryBroadcastReceivers( Intent intent, int flags, @UserIdInt int userId) { return null; } @Implementation protected PackageInfo getPackageArchiveInfo(String archiveFilePath, int flags) { PackageInfo shadowPackageInfo = getShadowPackageArchiveInfo(archiveFilePath, flags); if (shadowPackageInfo != null) { return shadowPackageInfo; } else { return reflector(PackageManagerReflector.class, realPackageManager) .getPackageArchiveInfo(archiveFilePath, flags); } } protected PackageInfo getShadowPackageArchiveInfo(String archiveFilePath, int flags) { synchronized (lock) { if (packageArchiveInfo.containsKey(archiveFilePath)) { return packageArchiveInfo.get(archiveFilePath); } List result = new ArrayList<>(); for (PackageInfo packageInfo : packageInfos.values()) { if (applicationEnabledSettingMap.get(packageInfo.packageName) != COMPONENT_ENABLED_STATE_DISABLED || (flags & MATCH_UNINSTALLED_PACKAGES) == MATCH_UNINSTALLED_PACKAGES) { result.add(packageInfo); } } List packages = result; for (PackageInfo aPackage : packages) { ApplicationInfo appInfo = aPackage.applicationInfo; if (appInfo != null && archiveFilePath.equals(appInfo.sourceDir)) { return aPackage; } } return null; } } @Implementation protected void freeStorageAndNotify(long freeStorageSize, IPackageDataObserver observer) {} @Implementation protected void freeStorage(long freeStorageSize, IntentSender pi) {} /** * Uninstalls the package from the system in a way, that will allow its discovery through {@link * PackageManager#MATCH_UNINSTALLED_PACKAGES}. */ public void deletePackage(String packageName) { synchronized (lock) { deletedPackages.add(packageName); packageInfos.remove(packageName); mapForPackage(activityFilters, packageName).clear(); mapForPackage(serviceFilters, packageName).clear(); mapForPackage(providerFilters, packageName).clear(); mapForPackage(receiverFilters, packageName).clear(); moduleInfos.remove(packageName); } } @Implementation protected void deletePackage(String packageName, IPackageDeleteObserver observer, int flags) { pendingDeleteCallbacks.put(packageName, observer); } /** * Runs the callbacks pending from calls to {@link PackageManager#deletePackage(String, * IPackageDeleteObserver, int)} */ public void doPendingUninstallCallbacks() { synchronized (lock) { boolean hasDeletePackagesPermission = false; String[] requestedPermissions = packageInfos.get(RuntimeEnvironment.getApplication().getPackageName()) .requestedPermissions; if (requestedPermissions != null) { for (String permission : requestedPermissions) { if (Manifest.permission.DELETE_PACKAGES.equals(permission)) { hasDeletePackagesPermission = true; break; } } } for (String packageName : pendingDeleteCallbacks.keySet()) { int resultCode = PackageManager.DELETE_FAILED_INTERNAL_ERROR; PackageInfo removed = packageInfos.get(packageName); if (hasDeletePackagesPermission && removed != null) { deletePackage(packageName); resultCode = PackageManager.DELETE_SUCCEEDED; } try { pendingDeleteCallbacks.get(packageName).packageDeleted(packageName, resultCode); } catch (RemoteException e) { throw new RuntimeException(e); } } pendingDeleteCallbacks.clear(); } } /** * Returns package names successfully deleted with {@link PackageManager#deletePackage(String, * IPackageDeleteObserver, int)} Note that like real {@link PackageManager} the calling context * must have {@link android.Manifest.permission#DELETE_PACKAGES} permission set. */ public Set getDeletedPackages() { synchronized (lock) { return deletedPackages; } } protected List queryOverriddenIntents(Intent intent, int flags) { List overrides = resolveInfoForIntent.get(intent); if (overrides == null) { return Collections.emptyList(); } List result = new ArrayList<>(overrides.size()); for (ResolveInfo resolveInfo : overrides) { result.add(ShadowResolveInfo.newResolveInfo(resolveInfo)); } return result; } /** * Internal use only. * * @param appPackage */ public void addPackageInternal(Package appPackage) { int flags = GET_ACTIVITIES | GET_RECEIVERS | GET_SERVICES | GET_PROVIDERS | GET_INSTRUMENTATION | GET_INTENT_FILTERS | GET_SIGNATURES | GET_RESOLVED_FILTER | GET_META_DATA | GET_GIDS | MATCH_DISABLED_COMPONENTS | GET_SHARED_LIBRARY_FILES | GET_URI_PERMISSION_PATTERNS | GET_PERMISSIONS | MATCH_UNINSTALLED_PACKAGES | GET_CONFIGURATIONS | MATCH_DISABLED_UNTIL_USED_COMPONENTS | MATCH_DIRECT_BOOT_UNAWARE | MATCH_DIRECT_BOOT_AWARE; for (PermissionGroup permissionGroup : appPackage.permissionGroups) { PermissionGroupInfo permissionGroupInfo = PackageParser.generatePermissionGroupInfo(permissionGroup, flags); addPermissionGroupInfo(permissionGroupInfo); } PackageInfo packageInfo = generatePackageInfo(appPackage, flags); packageInfo.applicationInfo.uid = Process.myUid(); packageInfo.applicationInfo.dataDir = createTempDir(packageInfo.packageName + "-dataDir"); installPackage(packageInfo); addFilters(activityFilters, appPackage.activities); addFilters(serviceFilters, appPackage.services); addFilters(providerFilters, appPackage.providers); addFilters(receiverFilters, appPackage.receivers); } protected PackageInfo generatePackageInfo(Package appPackage, int flags) { if (RuntimeEnvironment.getApiLevel() >= TIRAMISU) { return PackageParser.generatePackageInfo( appPackage, new int[] {0}, flags, 0, 0, Collections.emptySet(), FrameworkPackageUserState.DEFAULT, 0); } else { return reflector(_PackageParser_.class) .generatePackageInfo(appPackage, new int[] {0}, flags, 0, 0); } } private void addFilters( Map> componentMap, List> components) { if (components == null) { return; } for (Component component : components) { ComponentName componentName = component.getComponentName(); List registeredFilters = componentMap.get(componentName); if (registeredFilters == null) { registeredFilters = new ArrayList<>(); componentMap.put(componentName, registeredFilters); } for (IntentInfo intentInfo : component.intents) { registeredFilters.add(new IntentFilter(intentInfo)); } } } public static class IntentComparator implements Comparator { @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 categories1 = i1.getCategories(); Set 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; } } /** * Compares {@link ResolveInfo}s, ordering better matches before worse ones. This is the order in * which resolve infos should be returned to the user. */ static class ResolveInfoComparator implements Comparator { @Override public int compare(ResolveInfo o1, ResolveInfo o2) { if (o1 == null && o2 == null) { return 0; } if (o1 == null) { return -1; } if (o2 == null) { return 1; } if (o1.preferredOrder != o2.preferredOrder) { // higher priority is before lower return -Integer.compare(o1.preferredOrder, o2.preferredOrder); } if (o1.priority != o2.priority) { // higher priority is before lower return -Integer.compare(o1.priority, o2.priority); } if (o1.match != o2.match) { // higher match is before lower return -Integer.compare(o1.match, o2.match); } return 0; } } protected static class ComponentState { public int newState; public int flags; public ComponentState(int newState, int flags) { this.newState = newState; this.flags = flags; } } /** * Get list of intent filters defined for given activity. * * @param componentName Name of the activity whose intent filters are to be retrieved * @return the activity's intent filters * @throws IllegalArgumentException if component with given name doesn't exist. */ public List getIntentFiltersForActivity(ComponentName componentName) { return getIntentFiltersForComponent(componentName, activityFilters); } /** * Get list of intent filters defined for given service. * * @param componentName Name of the service whose intent filters are to be retrieved * @return the service's intent filters * @throws IllegalArgumentException if component with given name doesn't exist. */ public List getIntentFiltersForService(ComponentName componentName) { return getIntentFiltersForComponent(componentName, serviceFilters); } /** * Get list of intent filters defined for given receiver. * * @param componentName Name of the receiver whose intent filters are to be retrieved * @return the receiver's intent filters * @throws IllegalArgumentException if component with given name doesn't exist. */ public List getIntentFiltersForReceiver(ComponentName componentName) { return getIntentFiltersForComponent(componentName, receiverFilters); } /** * Get list of intent filters defined for given provider. * * @param componentName Name of the provider whose intent filters are to be retrieved * @return the provider's intent filters * @throws IllegalArgumentException if component with given name doesn't exist. */ public List getIntentFiltersForProvider(ComponentName componentName) { return getIntentFiltersForComponent(componentName, providerFilters); } /** * Add intent filter for given activity. * * @throws IllegalArgumentException if component with given name doesn't exist. */ public void addIntentFilterForActivity(ComponentName componentName, IntentFilter filter) { addIntentFilterForComponent(componentName, filter, activityFilters); } /** * Add intent filter for given service. * * @throws IllegalArgumentException if component with given name doesn't exist. */ public void addIntentFilterForService(ComponentName componentName, IntentFilter filter) { addIntentFilterForComponent(componentName, filter, serviceFilters); } /** * Add intent filter for given receiver. * * @throws IllegalArgumentException if component with given name doesn't exist. */ public void addIntentFilterForReceiver(ComponentName componentName, IntentFilter filter) { addIntentFilterForComponent(componentName, filter, receiverFilters); } /** * Add intent filter for given provider. * * @throws IllegalArgumentException if component with given name doesn't exist. */ public void addIntentFilterForProvider(ComponentName componentName, IntentFilter filter) { addIntentFilterForComponent(componentName, filter, providerFilters); } /** * Clears intent filters for given activity. * * @throws IllegalArgumentException if component with given name doesn't exist. */ public void clearIntentFilterForActivity(ComponentName componentName) { clearIntentFilterForComponent(componentName, activityFilters); } /** * Clears intent filters for given service. * * @throws IllegalArgumentException if component with given name doesn't exist. */ public void clearIntentFilterForService(ComponentName componentName) { clearIntentFilterForComponent(componentName, serviceFilters); } /** * Clears intent filters for given receiver. * * @throws IllegalArgumentException if component with given name doesn't exist. */ public void clearIntentFilterForReceiver(ComponentName componentName) { clearIntentFilterForComponent(componentName, receiverFilters); } /** * Clears intent filters for given provider. * * @throws IllegalArgumentException if component with given name doesn't exist. */ public void clearIntentFilterForProvider(ComponentName componentName) { clearIntentFilterForComponent(componentName, providerFilters); } private void addIntentFilterForComponent( ComponentName componentName, IntentFilter filter, Map> filterMap) { // Existing components should have an entry in respective filterMap. // It is OK to search over all filter maps, as it is impossible to have the same component name // being of two comopnent types (like activity and service at the same time). List filters = filterMap.get(componentName); if (filters != null) { filters.add(filter); return; } throw new IllegalArgumentException(componentName + " doesn't exist"); } private void clearIntentFilterForComponent( ComponentName componentName, Map> filterMap) { List filters = filterMap.get(componentName); if (filters != null) { filters.clear(); return; } throw new IllegalArgumentException(componentName + " doesn't exist"); } private List getIntentFiltersForComponent( ComponentName componentName, Map> filterMap) { List filters = filterMap.get(componentName); if (filters != null) { return new ArrayList<>(filters); } throw new IllegalArgumentException(componentName + " doesn't exist"); } /** * Method to retrieve persistent preferred activities as set by {@link * android.app.admin.DevicePolicyManager#addPersistentPreferredActivity}. * *

Works the same way as analogous {@link PackageManager#getPreferredActivities} for regular * preferred activities. */ public int getPersistentPreferredActivities( List outFilters, List outActivities, String packageName) { return getPreferredActivitiesInternal( outFilters, outActivities, packageName, persistentPreferredActivities); } protected static int getPreferredActivitiesInternal( List outFilters, List outActivities, String packageName, SortedMap> preferredActivitiesMap) { SortedMap> preferredMap = preferredActivitiesMap; if (packageName != null) { preferredMap = mapForPackage(preferredActivitiesMap, packageName); } int result = 0; for (Entry> entry : preferredMap.entrySet()) { int filterCount = entry.getValue().size(); result += filterCount; ComponentName[] componentNames = new ComponentName[filterCount]; Arrays.fill(componentNames, entry.getKey()); outActivities.addAll(asList(componentNames)); outFilters.addAll(entry.getValue()); } return result; } void clearPackagePersistentPreferredActivities(String packageName) { clearPackagePreferredActivitiesInternal(packageName, persistentPreferredActivities); } protected static void clearPackagePreferredActivitiesInternal( String packageName, SortedMap> preferredActivitiesMap) { mapForPackage(preferredActivitiesMap, packageName).clear(); } void addPersistentPreferredActivity(IntentFilter filter, ComponentName activity) { addPreferredActivityInternal(filter, activity, persistentPreferredActivities); } protected static void addPreferredActivityInternal( IntentFilter filter, ComponentName activity, SortedMap> preferredActivitiesMap) { List filters = preferredActivitiesMap.get(activity); if (filters == null) { filters = new ArrayList<>(); preferredActivitiesMap.put(activity, filters); } filters.add(filter); } protected static SortedMap mapForPackage( SortedMap input, @Nullable String packageName) { if (packageName == null) { return input; } if (packageName == null) { return input; } return input.subMap( new ComponentName(packageName, ""), new ComponentName(packageName + " ", "")); } static boolean isComponentEnabled(@Nullable ComponentInfo componentInfo) { if (componentInfo == null) { return true; } if (componentInfo.applicationInfo == null || componentInfo.applicationInfo.packageName == null || componentInfo.name == null) { return componentInfo.enabled; } ComponentName name = new ComponentName(componentInfo.applicationInfo.packageName, componentInfo.name); ComponentState componentState = componentList.get(name); if (componentState == null || componentState.newState == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) { return componentInfo.enabled; } return componentState.newState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED; } /** * Returns the current {@link PackageSetting} of {@code packageName}. * * If {@code packageName} is not present in this {@link ShadowPackageManager}, this method will * return null. */ public PackageSetting getPackageSetting(String packageName) { PackageSetting setting = packageSettings.get(packageName); return setting == null ? null : new PackageSetting(setting); } /** * If this method has been called with true, then in cases where many activities match a filter, * an activity chooser will be resolved instead of just the first pick. */ public void setShouldShowActivityChooser(boolean shouldShowActivityChooser) { this.shouldShowActivityChooser = shouldShowActivityChooser; } /** Set value to be returned by {@link PackageManager#isSafeMode}. */ public void setSafeMode(boolean safeMode) { ShadowPackageManager.safeMode = safeMode; } /** * Returns the last value provided to {@code setDistractingPackageRestrictions} for {@code pkg}. * * Defaults to {@code PackageManager.RESTRICTION_NONE} if {@code * setDistractingPackageRestrictions} has not been called for {@code pkg}. */ public int getDistractingPackageRestrictions(String pkg) { return distractingPackageRestrictions.getOrDefault(pkg, PackageManager.RESTRICTION_NONE); } /** * Adds a String resource with {@code resId} corresponding to {@code packageName}. This is * retrieved in shadow implementation of {@link PackageManager#getText(String, int, * ApplicationInfo)}. */ public void addStringResource(String packageName, int resId, String text) { if (!stringResources.containsKey(packageName)) { stringResources.put(packageName, new HashMap<>()); } stringResources.get(packageName).put(resId, text); } /** Set value to be returned by {@link PackageManager#isAutoRevokeWhitelisted}. */ public void setAutoRevokeWhitelisted(boolean whitelisted) { ShadowPackageManager.whitelisted = whitelisted; } @Resetter public static void reset() { synchronized (lock) { permissionRationaleMap.clear(); systemAvailableFeatures.clear(); systemSharedLibraryNames.clear(); packageInfos.clear(); packageArchiveInfo.clear(); packageStatsMap.clear(); packageInstallerMap.clear(); packageInstallSourceInfoMap.clear(); packagesForUid.clear(); uidForPackage.clear(); namesForUid.clear(); verificationResults.clear(); verificationTimeoutExtension.clear(); verificationCodeAtTimeoutExtension.clear(); currentToCanonicalNames.clear(); canonicalToCurrentNames.clear(); componentList.clear(); drawableList.clear(); applicationIcons.clear(); unbadgedApplicationIcons.clear(); systemFeatureList.clear(); systemFeatureList.putAll(SystemFeatureListInitializer.getSystemFeatures()); preferredActivities.clear(); persistentPreferredActivities.clear(); drawables.clear(); stringResources.clear(); applicationEnabledSettingMap.clear(); extraPermissions.clear(); permissionGroups.clear(); permissionFlags.clear(); resources.clear(); resolveInfoForIntent.clear(); deletedPackages.clear(); pendingDeleteCallbacks.clear(); hiddenPackages.clear(); sequenceNumberChangedPackagesMap.clear(); activityFilters.clear(); serviceFilters.clear(); providerFilters.clear(); receiverFilters.clear(); packageSettings.clear(); safeMode = false; whitelisted = false; } } @ForType(PackageManager.class) interface PackageManagerReflector { @Direct PackageInfo getPackageArchiveInfo(String archiveFilePath, int flags); } }