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

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

package org.robolectric.shadows;

import static android.content.IntentFilter.MATCH_CATEGORY_MASK;
import static android.content.pm.ApplicationInfo.FLAG_INSTALLED;
import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
import static android.content.pm.PackageInfo.REQUESTED_PERMISSION_GRANTED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
import static android.content.pm.PackageManager.DONT_KILL_APP;
import static android.content.pm.PackageManager.GET_ACTIVITIES;
import static android.content.pm.PackageManager.GET_META_DATA;
import static android.content.pm.PackageManager.GET_PROVIDERS;
import static android.content.pm.PackageManager.GET_RECEIVERS;
import static android.content.pm.PackageManager.GET_RESOLVED_FILTER;
import static android.content.pm.PackageManager.GET_SERVICES;
import static android.content.pm.PackageManager.GET_SIGNATURES;
import static android.content.pm.PackageManager.MATCH_ALL;
import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY;
import static android.content.pm.PackageManager.MATCH_DISABLED_COMPONENTS;
import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES;
import static android.content.pm.PackageManager.SIGNATURE_UNKNOWN_PACKAGE;
import static android.os.Build.VERSION_CODES.JELLY_BEAN;
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR2;
import static android.os.Build.VERSION_CODES.KITKAT;
import static android.os.Build.VERSION_CODES.LOLLIPOP;
import static android.os.Build.VERSION_CODES.LOLLIPOP_MR1;
import static android.os.Build.VERSION_CODES.M;
import static android.os.Build.VERSION_CODES.N;
import static android.os.Build.VERSION_CODES.O;
import static android.os.Build.VERSION_CODES.O_MR1;
import static android.os.Build.VERSION_CODES.P;
import static android.os.Build.VERSION_CODES.Q;
import static android.os.Build.VERSION_CODES.R;
import static android.os.Build.VERSION_CODES.S;
import static android.os.Build.VERSION_CODES.S_V2;
import static android.os.Build.VERSION_CODES.TIRAMISU;
import static com.google.common.base.Preconditions.checkNotNull;
import static org.robolectric.annotation.GetInstallerPackageNameMode.Mode.REALISTIC;
import static org.robolectric.util.reflector.Reflector.reflector;

import android.Manifest.permission;
import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.StringRes;
import android.annotation.UserIdInt;
import android.app.ApplicationPackageManager;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.IntentSender;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ChangedPackages;
import android.content.pm.ComponentInfo;
import android.content.pm.FeatureInfo;
import android.content.pm.IPackageDataObserver;
import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageStatsObserver;
import android.content.pm.InstallSourceInfo;
import android.content.pm.InstrumentationInfo;
import android.content.pm.IntentFilterVerificationInfo;
import android.content.pm.ModuleInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageItemInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.ComponentEnabledSetting;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManager.OnPermissionsChangedListener;
import android.content.pm.PackageManager.PackageInfoFlags;
import android.content.pm.PackageManager.ResolveInfoFlags;
import android.content.pm.PackageStats;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.VerifierDeviceIdentity;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.os.Handler;
import android.os.Looper;
import android.os.Parcel;
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.storage.VolumeInfo;
import android.telecom.TelecomManager;
import android.util.Log;
import android.util.Pair;
import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import com.google.common.collect.Sets;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.SortedMap;
import java.util.concurrent.Executor;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.GetInstallerPackageNameMode;
import org.robolectric.annotation.HiddenApi;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.RealObject;
import org.robolectric.config.ConfigurationRegistry;
import org.robolectric.util.reflector.Accessor;
import org.robolectric.util.reflector.Direct;
import org.robolectric.util.reflector.ForType;

@Implements(value = ApplicationPackageManager.class, isInAndroidSdk = false, looseSignatures = true)
public class ShadowApplicationPackageManager extends ShadowPackageManager {
  /** Package name of the Android platform. */
  private static final String PLATFORM_PACKAGE_NAME = "android";

  /** MIME type of Android Packages (APKs). */
  private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive";

  /** {@link Uri} scheme of installed apps. */
  private static final String PACKAGE_SCHEME = "package";

  @RealObject private ApplicationPackageManager realObject;
  private final List clearedApplicationUserDataPackages = new ArrayList<>();

  @Implementation
  public List getInstalledPackages(int flags) {
    return getInstalledPackages((long) flags);
  }

  @Implementation(minSdk = TIRAMISU)
  protected List getInstalledPackages(Object flags) {
    return getInstalledPackages(((PackageInfoFlags) flags).getValue());
  }

  private List getInstalledPackages(long flags) {
    List result = new ArrayList<>();
    synchronized (lock) {
      Set packageNames = null;
      if ((flags & MATCH_UNINSTALLED_PACKAGES) == 0) {
        packageNames = packageInfos.keySet();
      } else {
        packageNames = Sets.union(packageInfos.keySet(), deletedPackages);
      }
      for (String packageName : packageNames) {
        try {
          PackageInfo packageInfo = getPackageInfo(packageName, flags);
          result.add(packageInfo);
        } catch (NameNotFoundException e) {
          Log.i(TAG, String.format("Package %s filtered out: %s", packageName, e.getMessage()));
        }
      }
    }
    return result;
  }

  @Implementation(minSdk = Q)
  protected List getInstalledModules(int flags) {
    synchronized (lock) {
      List result = new ArrayList<>();
      for (String moduleName : moduleInfos.keySet()) {
        try {
          ModuleInfo moduleInfo = (ModuleInfo) getModuleInfo(moduleName, flags);
          result.add(moduleInfo);
        } catch (NameNotFoundException e) {
          Log.i(TAG, String.format("Module %s filtered out: %s", moduleName, e.getMessage()));
        }
      }
      return result;
    }
  }

  @Implementation(minSdk = Q)
  protected Object getModuleInfo(String packageName, int flags) throws NameNotFoundException {
    synchronized (lock) {
      // Double checks that the respective package matches and is not disabled
      getPackageInfo(packageName, flags);
      Object info = moduleInfos.get(packageName);
      if (info == null) {
        throw new NameNotFoundException("Module: " + packageName + " is not installed.");
      }

      return info;
    }
  }

  @Implementation
  protected ActivityInfo getActivityInfo(ComponentName component, int flags)
      throws NameNotFoundException {
    return getComponentInfo(
        component,
        flags,
        packageInfo -> packageInfo.activities,
        resolveInfo -> resolveInfo.activityInfo,
        ActivityInfo::new);
  }

  private  T getComponentInfo(
      ComponentName component,
      int flags,
      Function componentsInPackage,
      Function componentInResolveInfo,
      Function copyConstructor)
      throws NameNotFoundException {
    String activityName = component.getClassName();
    String packageName = component.getPackageName();
    PackageInfo packageInfo = getInternalMutablePackageInfo(packageName);
    T result = null;
    ApplicationInfo appInfo = null;
    // search in the manifest
    if (packageInfo != null) {
      if (packageInfo.applicationInfo != null) {
        appInfo = packageInfo.applicationInfo;
      }
      T[] components = componentsInPackage.apply(packageInfo);
      if (components != null) {
        for (T activity : components) {
          if (activityName.equals(activity.name)) {
            result = copyConstructor.apply(activity);
            break;
          }
        }
      }
    }
    if (result == null) {
      // look in the registered intents
      outer:
      for (List listOfResolveInfo : resolveInfoForIntent.values()) {
        for (ResolveInfo resolveInfo : listOfResolveInfo) {
          T info = componentInResolveInfo.apply(resolveInfo);
          if (isValidComponentInfo(info)
              && component.equals(new ComponentName(info.applicationInfo.packageName, info.name))) {
            result = copyConstructor.apply(info);
            if (appInfo == null) {
              // we found valid app info in the resolve info. Use it.
              appInfo = result.applicationInfo;
            }
            break outer;
          }
        }
      }
    }
    if (result == null) {
      throw new NameNotFoundException("Component not found: " + component);
    }
    if (appInfo == null) {
      appInfo = new ApplicationInfo();
      appInfo.packageName = packageName;
      appInfo.flags = ApplicationInfo.FLAG_INSTALLED;
    } else {
      appInfo = new ApplicationInfo(appInfo);
    }
    result.applicationInfo = appInfo;
    applyFlagsToComponentInfo(result, flags);
    return result;
  }

  @Implementation
  protected boolean hasSystemFeature(String name) {
    return systemFeatureList.containsKey(name) ? systemFeatureList.get(name) : false;
  }

  @Implementation
  protected int getComponentEnabledSetting(ComponentName componentName) {
    ComponentState state = componentList.get(componentName);
    return state != null ? state.newState : PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
  }

  @Implementation
  protected @Nullable String getNameForUid(int uid) {
    return namesForUid.get(uid);
  }

  @Implementation
  @Override
  protected @Nullable String[] getPackagesForUid(int uid) {
    String[] packageNames = packagesForUid.get(uid);
    if (packageNames != null) {
      return packageNames;
    }

    Set results = new HashSet<>();
    synchronized (lock) {
      for (PackageInfo packageInfo : packageInfos.values()) {
        if (packageInfo.applicationInfo != null && packageInfo.applicationInfo.uid == uid) {
          results.add(packageInfo.packageName);
        }
      }
    }

    return results.isEmpty() ? null : results.toArray(new String[results.size()]);
  }

  @Implementation
  protected int getApplicationEnabledSetting(String packageName) {
    synchronized (lock) {
      if (!packageInfos.containsKey(packageName)) {
        throw new IllegalArgumentException("Package doesn't exist: " + packageName);
      }
    }

    return applicationEnabledSettingMap.get(packageName);
  }

  @Implementation
  protected ProviderInfo getProviderInfo(ComponentName component, int flags)
      throws NameNotFoundException {
    return getComponentInfo(
        component,
        flags,
        packageInfo -> packageInfo.providers,
        resolveInfo -> resolveInfo.providerInfo,
        ProviderInfo::new);
  }

  @Implementation
  protected void setComponentEnabledSetting(ComponentName componentName, int newState, int flags) {
    componentList.put(componentName, new ComponentState(newState, flags));
  }

  @Implementation(minSdk = TIRAMISU)
  protected void setComponentEnabledSettings(List settings) {
    for (ComponentEnabledSetting setting : settings) {
      componentList.put(
          setting.getComponentName(),
          new ComponentState(setting.getEnabledState(), setting.getEnabledFlags()));
    }
  }

  @Implementation(minSdk = Q)
  protected void setSyntheticAppDetailsActivityEnabled(String packageName, boolean enabled) {
    ComponentName componentName =
        new ComponentName(packageName, PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME);
    setComponentEnabledSetting(
        componentName,
        enabled ? COMPONENT_ENABLED_STATE_DEFAULT : COMPONENT_ENABLED_STATE_DISABLED,
        DONT_KILL_APP);
  }

  @Implementation(minSdk = Q)
  protected boolean getSyntheticAppDetailsActivityEnabled(String packageName) {
    ComponentName componentName =
        new ComponentName(packageName, PackageManager.APP_DETAILS_ACTIVITY_CLASS_NAME);
    int state = getComponentEnabledSetting(componentName);
    return state == COMPONENT_ENABLED_STATE_ENABLED || state == COMPONENT_ENABLED_STATE_DEFAULT;
  }

  @Implementation
  protected void setApplicationEnabledSetting(String packageName, int newState, int flags) {
    applicationEnabledSettingMap.put(packageName, newState);
  }

  @Implementation
  protected ResolveInfo resolveActivity(Intent intent, int flags) {
    List candidates = queryIntentActivities(intent, flags);
    if (candidates.isEmpty()) {
      return null;
    }
    if (candidates.size() == 1) {
      return candidates.get(0);
    }
    ResolveInfo persistentPreferredResolveInfo =
        resolvePreferredActivity(intent, candidates, persistentPreferredActivities);
    if (persistentPreferredResolveInfo != null) {
      return persistentPreferredResolveInfo;
    }
    ResolveInfo preferredResolveInfo =
        resolvePreferredActivity(intent, candidates, preferredActivities);
    if (preferredResolveInfo != null) {
      return preferredResolveInfo;
    }
    if (!shouldShowActivityChooser) {
      return candidates.get(0);
    }
    ResolveInfo c1 = candidates.get(0);
    ResolveInfo c2 = candidates.get(1);
    if (c1.preferredOrder == c2.preferredOrder
        && isValidComponentInfo(c1.activityInfo)
        && isValidComponentInfo(c2.activityInfo)) {
      // When the top pick is as good as the second and is not preferred explicitly show the
      // chooser
      ResolveInfo result = new ResolveInfo();
      result.activityInfo = new ActivityInfo();
      result.activityInfo.name = "ActivityResolver";
      result.activityInfo.packageName = "android";
      result.activityInfo.applicationInfo = new ApplicationInfo();
      result.activityInfo.applicationInfo.flags = FLAG_INSTALLED | FLAG_SYSTEM;
      result.activityInfo.applicationInfo.packageName = "android";
      return result;
    } else {
      return c1;
    }
  }

  private ResolveInfo resolvePreferredActivity(
      Intent intent,
      List candidates,
      SortedMap> preferredActivities) {
    preferredActivities = mapForPackage(preferredActivities, intent.getPackage());
    for (ResolveInfo candidate : candidates) {
      ActivityInfo activityInfo = candidate.activityInfo;
      if (!isValidComponentInfo(activityInfo)) {
        continue;
      }
      ComponentName candidateName =
          new ComponentName(activityInfo.applicationInfo.packageName, activityInfo.name);
      List intentFilters = preferredActivities.get(candidateName);
      if (intentFilters == null) {
        continue;
      }
      for (IntentFilter filter : intentFilters) {
        if ((filter.match(getContext().getContentResolver(), intent, false, "robo")
                & MATCH_CATEGORY_MASK)
            != 0) {
          return candidate;
        }
      }
    }
    return null;
  }

  @Implementation
  protected ProviderInfo resolveContentProvider(String name, int flags) {
    if (name == null) {
      return null;
    }
    synchronized (lock) {
      for (PackageInfo packageInfo : packageInfos.values()) {
        if (packageInfo.providers == null) {
          continue;
        }

        for (ProviderInfo providerInfo : packageInfo.providers) {
          for (String authority : Splitter.on(';').split(providerInfo.authority)) {
            if (name.equals(authority)) {
              return new ProviderInfo(providerInfo);
            }
          }
        }
      }
    }
    return null;
  }

  @Implementation(minSdk = LOLLIPOP)
  protected ProviderInfo resolveContentProviderAsUser(
      String name, int flags, @UserIdInt int userId) {
    return null;
  }

  @Implementation
  protected PackageInfo getPackageInfo(String packageName, int flags) throws NameNotFoundException {
    return getPackageInfo(packageName, (long) flags);
  }

  @Implementation(minSdk = TIRAMISU)
  protected PackageInfo getPackageInfo(Object packageName, Object flags)
      throws NameNotFoundException {
    return getPackageInfo((String) packageName, ((PackageInfoFlags) flags).getValue());
  }

  private PackageInfo getPackageInfo(String packageName, long flags) throws NameNotFoundException {
    synchronized (lock) {
      PackageInfo info = packageInfos.get(packageName);
      if (info == null
          && (flags & MATCH_UNINSTALLED_PACKAGES) != 0
          && deletedPackages.contains(packageName)) {
        info = new PackageInfo();
        info.packageName = packageName;
        info.applicationInfo = new ApplicationInfo();
        info.applicationInfo.packageName = packageName;
      }
      if (info == null) {
        throw new NameNotFoundException(packageName);
      }
      info = newPackageInfo(info);
      if (info.applicationInfo == null) {
        return info;
      }
      if (hiddenPackages.contains(packageName) && !isFlagSet(flags, MATCH_UNINSTALLED_PACKAGES)) {
        throw new NameNotFoundException("Package is hidden, can't find");
      }
      applyFlagsToApplicationInfo(info.applicationInfo, flags);
      info.activities =
          applyFlagsToComponentInfoList(info.activities, flags, GET_ACTIVITIES, ActivityInfo::new);
      info.services =
          applyFlagsToComponentInfoList(info.services, flags, GET_SERVICES, ServiceInfo::new);
      info.receivers =
          applyFlagsToComponentInfoList(info.receivers, flags, GET_RECEIVERS, ActivityInfo::new);
      info.providers =
          applyFlagsToComponentInfoList(info.providers, flags, GET_PROVIDERS, ProviderInfo::new);
      return info;
    }
  }

  /**
   * Starting in Android S, this method was moved from {@link android.content.pm.PackageManager} to
   * {@link ApplicationPackageManager}. However, it was moved back to {@link
   * android.content.pm.PackageManager} in T.
   */
  @Override
  @Implementation(minSdk = S, maxSdk = S_V2)
  protected PackageInfo getPackageArchiveInfo(String archiveFilePath, int flags) {
    int apiLevel = RuntimeEnvironment.getApiLevel();
    if (apiLevel == S || apiLevel == S_V2) {

      PackageInfo shadowPackageInfo = getShadowPackageArchiveInfo(archiveFilePath, flags);
      if (shadowPackageInfo != null) {
        return shadowPackageInfo;
      } else {
        return reflector(ReflectorApplicationPackageManager.class, realObject)
            .getPackageArchiveInfo(archiveFilePath, flags);
      }
    } else {
      return super.getPackageArchiveInfo(archiveFilePath, flags);
    }
  }

  // There is no copy constructor for PackageInfo
  private static PackageInfo newPackageInfo(PackageInfo orig) {
    Parcel parcel = Parcel.obtain();
    orig.writeToParcel(parcel, 0);
    parcel.setDataPosition(0);
    return PackageInfo.CREATOR.createFromParcel(parcel);
  }

  private  T[] applyFlagsToComponentInfoList(
      T[] components, long flags, int activationFlag, Function copyConstructor) {
    if (components == null || (flags & activationFlag) == 0) {
      return null;
    }
    List returned = new ArrayList<>(components.length);
    for (T component : components) {
      component = copyConstructor.apply(component);
      try {
        applyFlagsToComponentInfo(component, flags);
        returned.add(component);
      } catch (NameNotFoundException e) {
        // skip this component
      }
    }
    if (returned.isEmpty()) {
      return null;
    }
    @SuppressWarnings("unchecked") // component arrays are of their respective types.
    Class componentArrayType = (Class) components.getClass();
    T[] result = Arrays.copyOf(components, returned.size(), componentArrayType);
    return returned.toArray(result);
  }

  @Implementation
  protected List queryIntentServices(Intent intent, int flags) {
    return queryIntentComponents(
        intent,
        flags,
        (pkg) -> pkg.services,
        serviceFilters,
        (resolveInfo, serviceInfo) -> resolveInfo.serviceInfo = serviceInfo,
        (resolveInfo) -> resolveInfo.serviceInfo,
        ServiceInfo::new);
  }

  private boolean hasSomeComponentInfo(ResolveInfo resolveInfo) {

    return resolveInfo.activityInfo != null
        || resolveInfo.serviceInfo != null
        || (VERSION.SDK_INT >= VERSION_CODES.KITKAT && resolveInfo.providerInfo != null);
  }

  private static boolean isFlagSet(long flags, long matchFlag) {
    return (flags & matchFlag) == matchFlag;
  }

  private static boolean isValidComponentInfo(ComponentInfo componentInfo) {
    return componentInfo != null
        && componentInfo.applicationInfo != null
        && componentInfo.applicationInfo.packageName != null
        && componentInfo.name != null;
  }

  /** Behaves as {@link #queryIntentServices(Intent, int)} and currently ignores userId. */
  @Implementation(minSdk = JELLY_BEAN_MR1)
  protected List queryIntentServicesAsUser(Intent intent, int flags, int userId) {
    return queryIntentServices(intent, flags);
  }

  @Implementation
  protected List queryIntentActivities(Intent intent, int flags) {
    return this.queryIntentComponents(
        intent,
        flags,
        (pkg) -> pkg.activities,
        activityFilters,
        (resolveInfo, activityInfo) -> resolveInfo.activityInfo = activityInfo,
        (resolveInfo) -> resolveInfo.activityInfo,
        ActivityInfo::new);
  }

  private  List queryIntentComponents(
      Intent intent,
      int flags,
      Function componentsInPackage,
      SortedMap> filters,
      BiConsumer componentSetter,
      Function componentInResolveInfo,
      Function copyConstructor) {
    synchronized (lock) {
      if (intent.getComponent() != null) {
        flags &= ~MATCH_DEFAULT_ONLY;
      }
      List result = new ArrayList<>();
      List resolveInfoList = queryOverriddenIntents(intent, flags);
      if (!resolveInfoList.isEmpty()) {
        result.addAll(resolveInfoList);
      }

      result.addAll(
          queryComponentsInManifest(intent, componentsInPackage, filters, componentSetter));

      for (Iterator iterator = result.iterator(); iterator.hasNext(); ) {
        ResolveInfo resolveInfo = iterator.next();
        I componentInfo = componentInResolveInfo.apply(resolveInfo);
        if (hasSomeComponentInfo(resolveInfo) && componentInfo == null) {
          Log.d(TAG, "ResolveInfo for different component type");
          // different component type
          iterator.remove();
          continue;
        }
        if (componentInfo == null) {
          // null component? Don't filter this sh...
          continue;
        }
        if (!applyFlagsToResolveInfo(resolveInfo, flags)) {
          Log.d(TAG, "ResolveInfo doesn't match flags");
          iterator.remove();
          continue;
        }
        ApplicationInfo applicationInfo = componentInfo.applicationInfo;
        if (applicationInfo == null) {
          String packageName = null;
          if (getComponentForIntent(intent) != null) {
            packageName = getComponentForIntent(intent).getPackageName();
          } else if (intent.getPackage() != null) {
            packageName = intent.getPackage();
          } else if (componentInfo.packageName != null) {
            packageName = componentInfo.packageName;
          }
          if (packageName != null) {
            PackageInfo packageInfo = packageInfos.get(packageName);
            if (packageInfo != null && packageInfo.applicationInfo != null) {
              applicationInfo = new ApplicationInfo(packageInfo.applicationInfo);
            } else {
              applicationInfo = new ApplicationInfo();
              applicationInfo.packageName = packageName;
              applicationInfo.flags = FLAG_INSTALLED;
            }
          }
        } else {
          applicationInfo = new ApplicationInfo(applicationInfo);
        }
        componentInfo = copyConstructor.apply(componentInfo);
        componentSetter.accept(resolveInfo, componentInfo);
        componentInfo.applicationInfo = applicationInfo;

        try {
          applyFlagsToComponentInfo(componentInfo, flags);
        } catch (NameNotFoundException e) {
          Log.d(TAG, "ComponentInfo doesn't match flags:" + e.getMessage());
          iterator.remove();
          continue;
        }
      }
      Collections.sort(result, new ResolveInfoComparator());
      return result;
    }
  }

  private boolean applyFlagsToResolveInfo(ResolveInfo resolveInfo, int flags) {
    if ((flags & GET_RESOLVED_FILTER) == 0) {
      resolveInfo.filter = null;
    }
    return (flags & MATCH_DEFAULT_ONLY) == 0 || resolveInfo.isDefault;
  }

  private  List queryComponentsInManifest(
      Intent intent,
      Function componentsInPackage,
      SortedMap> filters,
      BiConsumer componentSetter) {
    synchronized (lock) {
      if (isExplicitIntent(intent)) {
        ComponentName component = getComponentForIntent(intent);
        PackageInfo appPackage = packageInfos.get(component.getPackageName());
        if (appPackage == null) {
          return Collections.emptyList();
        }
        I componentInfo = findMatchingComponent(component, componentsInPackage.apply(appPackage));
        if (componentInfo != null) {
          List componentFilters = filters.get(component);
          PackageInfo targetPackage = packageInfos.get(component.getPackageName());
          if (RuntimeEnvironment.getApiLevel() >= TIRAMISU
              && (intent.getAction() != null
                  || intent.getCategories() != null
                  || intent.getData() != null)
              && componentFilters != null
              && !component.getPackageName().equals(getContext().getPackageName())
              && targetPackage.applicationInfo.targetSdkVersion >= TIRAMISU) {
            // Check if the explicit intent matches filters on the target component for T+
            boolean matchFound = false;
            for (IntentFilter filter : componentFilters) {
              if (matchIntentFilter(intent, filter) > 0) {
                matchFound = true;
                break;
              }
            }
            if (!matchFound) {
              Log.w(
                  TAG,
                  "Component "
                      + componentInfo
                      + " doesn't have required intent filters for "
                      + intent);
              return Collections.emptyList();
            }
          }
          ResolveInfo resolveInfo = buildResolveInfo(componentInfo);
          componentSetter.accept(resolveInfo, componentInfo);
          return new ArrayList<>(Collections.singletonList(resolveInfo));
        }

        return Collections.emptyList();
      } else {
        List resolveInfoList = new ArrayList<>();
        Map> filtersForPackage =
            mapForPackage(filters, intent.getPackage());
        components:
        for (Map.Entry> componentEntry :
            filtersForPackage.entrySet()) {
          ComponentName componentName = componentEntry.getKey();
          for (IntentFilter filter : componentEntry.getValue()) {
            int match = matchIntentFilter(intent, filter);
            if (match > 0) {
              PackageInfo packageInfo = packageInfos.get(componentName.getPackageName());
              I[] componentInfoArray = componentsInPackage.apply(packageInfo);
              if (componentInfoArray != null) {
                for (I componentInfo : componentInfoArray) {
                  if (!componentInfo.name.equals(componentName.getClassName())) {
                    continue;
                  }
                  ResolveInfo resolveInfo = buildResolveInfo(componentInfo, filter);
                  resolveInfo.match = match;
                  componentSetter.accept(resolveInfo, componentInfo);
                  resolveInfoList.add(resolveInfo);
                  continue components;
                }
              }
            }
          }
        }
        return resolveInfoList;
      }
    }
  }

  /** Behaves as {@link #queryIntentActivities(Intent, int)} and currently ignores userId. */
  @Implementation(minSdk = JELLY_BEAN_MR1)
  protected List queryIntentActivitiesAsUser(Intent intent, int flags, int userId) {
    return queryIntentActivities(intent, flags);
  }

  /** Returns true if intent has specified a specific component. */
  private static boolean isExplicitIntent(Intent intent) {
    return getComponentForIntent(intent) != null;
  }

  private static  T findMatchingComponent(
      ComponentName componentName, T[] components) {
    if (components == null) {
      return null;
    }
    for (T component : components) {
      if (componentName.equals(new ComponentName(component.packageName, component.name))) {
        return component;
      }
    }
    return null;
  }

  private static ComponentName getComponentForIntent(Intent intent) {
    ComponentName component = intent.getComponent();
    if (component == null) {
      if (intent.getSelector() != null) {
        intent = intent.getSelector();
        component = intent.getComponent();
      }
    }
    return component;
  }

  private static ResolveInfo buildResolveInfo(ComponentInfo componentInfo) {
    ResolveInfo resolveInfo = new ResolveInfo();
    resolveInfo.resolvePackageName = componentInfo.applicationInfo.packageName;
    resolveInfo.labelRes = componentInfo.labelRes;
    resolveInfo.icon = componentInfo.icon;
    resolveInfo.nonLocalizedLabel = componentInfo.nonLocalizedLabel;
    return resolveInfo;
  }

  static ResolveInfo buildResolveInfo(ComponentInfo componentInfo, IntentFilter intentFilter) {
    ResolveInfo info = buildResolveInfo(componentInfo);
    info.isDefault = intentFilter.hasCategory("android.intent.category.DEFAULT");
    info.filter = new IntentFilter(intentFilter);
    info.priority = intentFilter.getPriority();
    return info;
  }

  @Implementation
  protected int checkPermission(String permName, String pkgName) {
    PackageInfo permissionsInfo = getInternalMutablePackageInfo(pkgName);
    if (permissionsInfo == null || permissionsInfo.requestedPermissions == null) {
      return PackageManager.PERMISSION_DENIED;
    }

    String permission;
    for (int i = 0; i < permissionsInfo.requestedPermissions.length; i++) {
      permission = permissionsInfo.requestedPermissions[i];
      if (permission != null && permission.equals(permName)) {
        // The package requests this permission. Now check if it's been granted to the package.
        if (isGrantedForBackwardsCompatibility(pkgName, permissionsInfo)) {
          return PackageManager.PERMISSION_GRANTED;
        }

        if ((permissionsInfo.requestedPermissionsFlags[i] & REQUESTED_PERMISSION_GRANTED)
            == REQUESTED_PERMISSION_GRANTED) {
          return PackageManager.PERMISSION_GRANTED;
        }
      }
    }

    return PackageManager.PERMISSION_DENIED;
  }

  /**
   * Returns whether a permission should be treated as granted to the package for backward
   * compatibility reasons.
   *
   * 

Before Robolectric 4.0 the ShadowPackageManager treated every requested permission as * automatically granted. 4.0 changes this behavior, and only treats a permission as granted if * PackageInfo.requestedPermissionFlags[permissionIndex] & REQUESTED_PERMISSION_GRANTED == * REQUESTED_PERMISSION_GRANTED which matches the real PackageManager's behavior. * *

Since many existing tests didn't set the requestedPermissionFlags on their {@code * PackageInfo} objects, but assumed that all permissions are granted, we auto-grant all * permissions if the requestedPermissionFlags is not set. If the requestedPermissionFlags is set, * we assume that the test is configuring the permission grant state, and we don't override this * setting. */ private boolean isGrantedForBackwardsCompatibility(String pkgName, PackageInfo permissionsInfo) { // Note: it might be cleaner to auto-grant these permissions when the package is added to the // PackageManager. But many existing tests modify the requested permissions _after_ adding the // package to the PackageManager, without updating the requestedPermissionsFlags. return permissionsInfo.requestedPermissionsFlags == null // Robolectric uses the PackageParser to create the current test package's PackageInfo from // the manifest XML. The parser populates the requestedPermissionsFlags, but doesn't grant // the permissions. Several tests rely on the test package being granted all permissions, so // we treat this as a special case. || pkgName.equals(RuntimeEnvironment.getApplication().getPackageName()); } @Implementation protected ActivityInfo getReceiverInfo(ComponentName component, int flags) throws NameNotFoundException { return getComponentInfo( component, flags, packageInfo -> packageInfo.receivers, resolveInfo -> resolveInfo.activityInfo, ActivityInfo::new); } @Implementation protected List queryBroadcastReceivers(Intent intent, int flags) { return this.queryIntentComponents( intent, flags, (pkg) -> pkg.receivers, receiverFilters, (resolveInfo, activityInfo) -> resolveInfo.activityInfo = activityInfo, (resolveInfo) -> resolveInfo.activityInfo, ActivityInfo::new); } @Implementation(minSdk = TIRAMISU) protected List queryBroadcastReceivers(Object intent, @NonNull Object flags) { return queryBroadcastReceivers((Intent) intent, (int) ((ResolveInfoFlags) flags).getValue()); } private static int matchIntentFilter(Intent intent, IntentFilter intentFilter) { return intentFilter.match( intent.getAction(), intent.getType(), intent.getScheme(), intent.getData(), intent.getCategories(), TAG); } @Implementation protected ResolveInfo resolveService(Intent intent, int flags) { List candidates = queryIntentServices(intent, flags); return candidates.isEmpty() ? null : candidates.get(0); } @Implementation protected ServiceInfo getServiceInfo(ComponentName component, int flags) throws NameNotFoundException { return getComponentInfo( component, flags, packageInfo -> packageInfo.services, resolveInfo -> resolveInfo.serviceInfo, ServiceInfo::new); } /** * Modifies the component in place using. * * @throws NameNotFoundException when component is filtered out by a flag */ private void applyFlagsToComponentInfo(ComponentInfo componentInfo, long flags) throws NameNotFoundException { componentInfo.name = (componentInfo.name == null) ? "" : componentInfo.name; ApplicationInfo applicationInfo = componentInfo.applicationInfo; boolean isApplicationEnabled = true; if (applicationInfo != null) { if (applicationInfo.packageName == null) { applicationInfo.packageName = componentInfo.packageName; } applyFlagsToApplicationInfo(componentInfo.applicationInfo, flags); componentInfo.packageName = applicationInfo.packageName; isApplicationEnabled = applicationInfo.enabled; } if ((flags & GET_META_DATA) == 0) { componentInfo.metaData = null; } boolean isComponentEnabled = isComponentEnabled(componentInfo); if ((flags & MATCH_ALL) != 0 && Build.VERSION.SDK_INT >= 23) { return; } // Android don't override the enabled field of component with the actual value. boolean isEnabledForFiltering = isComponentEnabled && (Build.VERSION.SDK_INT >= 24 ? isApplicationEnabled : true); if ((flags & MATCH_DISABLED_COMPONENTS) == 0 && !isEnabledForFiltering) { throw new NameNotFoundException("Disabled component: " + componentInfo); } if (isFlagSet(flags, PackageManager.MATCH_SYSTEM_ONLY)) { if (applicationInfo == null) { // TODO: for backwards compatibility just skip filtering. In future should just remove // invalid resolve infos from list } else { final int applicationFlags = applicationInfo.flags; if ((applicationFlags & ApplicationInfo.FLAG_SYSTEM) != ApplicationInfo.FLAG_SYSTEM) { throw new NameNotFoundException("Not system component: " + componentInfo); } } } if (!isFlagSet(flags, MATCH_UNINSTALLED_PACKAGES) && isValidComponentInfo(componentInfo) && hiddenPackages.contains(componentInfo.applicationInfo.packageName)) { throw new NameNotFoundException("Uninstalled package: " + componentInfo); } } @Implementation protected Resources getResourcesForApplication(@NonNull ApplicationInfo applicationInfo) throws PackageManager.NameNotFoundException { synchronized (lock) { if (getContext().getPackageName().equals(applicationInfo.packageName)) { return getContext().getResources(); } else if (packageInfos.containsKey(applicationInfo.packageName)) { Resources appResources = resources.get(applicationInfo.packageName); if (appResources == null) { appResources = new Resources(new AssetManager(), null, null); resources.put(applicationInfo.packageName, appResources); } return appResources; } Resources resources = null; if (RuntimeEnvironment.useLegacyResources() && (applicationInfo.publicSourceDir == null || !new File(applicationInfo.publicSourceDir).exists())) { // In legacy mode, the underlying getResourcesForApplication implementation just returns an // empty Resources instance in this case. throw new NameNotFoundException(applicationInfo.packageName); } try { resources = reflector(ReflectorApplicationPackageManager.class, realObject) .getResourcesForApplication(applicationInfo); } catch (Exception ex) { // handled below } if (resources == null) { throw new NameNotFoundException(applicationInfo.packageName); } return resources; } } @Implementation protected List getInstalledApplications(int flags) { return getInstalledApplications((long) flags); } @Implementation(minSdk = TIRAMISU) protected List getInstalledApplications(Object flags) { return getInstalledApplications(((PackageManager.ApplicationInfoFlags) flags).getValue()); } private List getInstalledApplications(long flags) { List packageInfos = getInstalledPackages(flags); List result = new ArrayList<>(packageInfos.size()); for (PackageInfo packageInfo : packageInfos) { if (packageInfo.applicationInfo != null) { result.add(packageInfo.applicationInfo); } } return result; } @Implementation protected String getInstallerPackageName(String packageName) { synchronized (lock) { // In REALISTIC mode, throw exception if the package is not installed or installed but // later uninstalled if (ConfigurationRegistry.get(GetInstallerPackageNameMode.Mode.class) == REALISTIC && (!packageInstallerMap.containsKey(packageName) || !packageInfos.containsKey(packageName))) { throw new IllegalArgumentException("Package is not installed: " + packageName); } else if (!packageInstallerMap.containsKey(packageName)) { Log.w( TAG, String.format( "Call to getInstallerPackageName returns null for package: '%s'. Please run" + " setInstallerPackageName to set installer package name before making the" + " call.", packageName)); } return packageInstallerMap.get(packageName); } } @Implementation(minSdk = R) protected Object getInstallSourceInfo(String packageName) { return (InstallSourceInfo) packageInstallSourceInfoMap.get(packageName); } @Implementation protected PermissionInfo getPermissionInfo(String name, int flags) throws NameNotFoundException { PermissionInfo permissionInfo = extraPermissions.get(name); if (permissionInfo != null) { return permissionInfo; } synchronized (lock) { for (PackageInfo packageInfo : packageInfos.values()) { if (packageInfo.permissions != null) { for (PermissionInfo permission : packageInfo.permissions) { if (name.equals(permission.name)) { return createCopyPermissionInfo(permission, flags); } } } } } throw new NameNotFoundException(name); } @Implementation(minSdk = M) protected boolean shouldShowRequestPermissionRationale(String permission) { return permissionRationaleMap.containsKey(permission) ? permissionRationaleMap.get(permission) : false; } @Implementation protected FeatureInfo[] getSystemAvailableFeatures() { return systemAvailableFeatures.isEmpty() ? null : systemAvailableFeatures.toArray(new FeatureInfo[systemAvailableFeatures.size()]); } @Implementation protected void verifyPendingInstall(int id, int verificationCode) { if (verificationResults.containsKey(id)) { throw new IllegalStateException("Multiple verifications for id=" + id); } verificationResults.put(id, verificationCode); } @Implementation(minSdk = JELLY_BEAN_MR1) protected void extendVerificationTimeout( int id, int verificationCodeAtTimeout, long millisecondsToDelay) { synchronized (lock) { verificationTimeoutExtension.put(id, millisecondsToDelay); verificationCodeAtTimeoutExtension.put(id, verificationCodeAtTimeout); } } @Override @Implementation(maxSdk = LOLLIPOP_MR1) protected void freeStorageAndNotify(long freeStorageSize, IPackageDataObserver observer) {} @Implementation(minSdk = M) protected void freeStorageAndNotify( String volumeUuid, long freeStorageSize, IPackageDataObserver observer) {} @Implementation protected void setInstallerPackageName(String targetPackage, String installerPackageName) { synchronized (lock) { packageInstallerMap.put(targetPackage, installerPackageName); } } @Implementation(minSdk = KITKAT) protected List queryIntentContentProviders(Intent intent, int flags) { return this.queryIntentComponents( intent, flags, (pkg) -> pkg.providers, providerFilters, (resolveInfo, providerInfo) -> resolveInfo.providerInfo = providerInfo, (resolveInfo) -> resolveInfo.providerInfo, ProviderInfo::new); } @Implementation(minSdk = KITKAT) protected List queryIntentContentProvidersAsUser( Intent intent, int flags, int userId) { return Collections.emptyList(); } @Implementation(minSdk = M) protected String getPermissionControllerPackageName() { return null; } @Implementation(maxSdk = JELLY_BEAN) protected void getPackageSizeInfo(Object pkgName, Object observer) { final PackageStats packageStats = packageStatsMap.get((String) pkgName); new Handler(Looper.getMainLooper()) .post( () -> { try { ((IPackageStatsObserver) observer) .onGetStatsCompleted(packageStats, packageStats != null); } catch (RemoteException remoteException) { remoteException.rethrowFromSystemServer(); } }); } @Implementation(minSdk = JELLY_BEAN_MR1, maxSdk = M) protected void getPackageSizeInfo(Object pkgName, Object uid, final Object observer) { final PackageStats packageStats = packageStatsMap.get((String) pkgName); new Handler(Looper.getMainLooper()) .post( () -> { try { ((IPackageStatsObserver) observer) .onGetStatsCompleted(packageStats, packageStats != null); } catch (RemoteException remoteException) { remoteException.rethrowFromSystemServer(); } }); } @Implementation(minSdk = N) protected void getPackageSizeInfoAsUser(Object pkgName, Object uid, final Object observer) { final PackageStats packageStats = packageStatsMap.get((String) pkgName); new Handler(Looper.getMainLooper()) .post( () -> { try { ((IPackageStatsObserver) observer) .onGetStatsCompleted(packageStats, packageStats != null); } catch (RemoteException remoteException) { remoteException.rethrowFromSystemServer(); } }); } @Override @Implementation protected void deletePackage(String packageName, IPackageDeleteObserver observer, int flags) { super.deletePackage(packageName, observer, flags); } @Implementation protected String[] currentToCanonicalPackageNames(String[] names) { String[] out = new String[names.length]; for (int i = 0; i < names.length; i++) { out[i] = currentToCanonicalNames.getOrDefault(names[i], names[i]); } return out; } @Implementation protected String[] canonicalToCurrentPackageNames(String[] names) { String[] out = new String[names.length]; for (int i = 0; i < names.length; i++) { out[i] = canonicalToCurrentNames.getOrDefault(names[i], names[i]); } return out; } @Implementation protected boolean isSafeMode() { return safeMode; } @Implementation protected Drawable getApplicationIcon(String packageName) throws NameNotFoundException { return applicationIcons.get(packageName); } @Implementation protected Drawable getApplicationIcon(ApplicationInfo info) throws NameNotFoundException { return getApplicationIcon(info.packageName); } @Implementation(minSdk = LOLLIPOP) protected Drawable getUserBadgeForDensity(UserHandle userHandle, int i) { return null; } @Implementation protected int checkSignatures(String pkg1, String pkg2) { try { PackageInfo packageInfo1 = getPackageInfo(pkg1, GET_SIGNATURES); PackageInfo packageInfo2 = getPackageInfo(pkg2, GET_SIGNATURES); return compareSignature(packageInfo1.signatures, packageInfo2.signatures); } catch (NameNotFoundException e) { return SIGNATURE_UNKNOWN_PACKAGE; } } @Implementation protected int checkSignatures(int uid1, int uid2) { return 0; } @Implementation protected List queryPermissionsByGroup(String group, int flags) throws NameNotFoundException { List result = new ArrayList<>(); for (PermissionInfo permissionInfo : extraPermissions.values()) { if (Objects.equals(permissionInfo.group, group)) { result.add(permissionInfo); } } synchronized (lock) { for (PackageInfo packageInfo : packageInfos.values()) { if (packageInfo.permissions != null) { for (PermissionInfo permission : packageInfo.permissions) { if (Objects.equals(group, permission.group)) { result.add(createCopyPermissionInfo(permission, flags)); } } } } } if (result.isEmpty()) { throw new NameNotFoundException(group); } return result; } private static PermissionInfo createCopyPermissionInfo(PermissionInfo src, int flags) { PermissionInfo matchedPermission = new PermissionInfo(src); if ((flags & GET_META_DATA) != GET_META_DATA) { matchedPermission.metaData = null; } return matchedPermission; } private Intent getLaunchIntentForPackage(String packageName, String launcherCategory) { Intent intentToResolve = new Intent(Intent.ACTION_MAIN); intentToResolve.addCategory(Intent.CATEGORY_INFO); intentToResolve.setPackage(packageName); List ris = queryIntentActivities(intentToResolve, 0); if (ris == null || ris.isEmpty()) { intentToResolve.removeCategory(Intent.CATEGORY_INFO); intentToResolve.addCategory(launcherCategory); intentToResolve.setPackage(packageName); ris = queryIntentActivities(intentToResolve, 0); } if (ris == null || ris.isEmpty()) { return null; } Intent intent = new Intent(intentToResolve); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setClassName(packageName, ris.get(0).activityInfo.name); return intent; } @Implementation protected Intent getLaunchIntentForPackage(String packageName) { return getLaunchIntentForPackage(packageName, Intent.CATEGORY_LAUNCHER); } @Implementation(minSdk = LOLLIPOP) protected Intent getLeanbackLaunchIntentForPackage(String packageName) { return getLaunchIntentForPackage(packageName, Intent.CATEGORY_LEANBACK_LAUNCHER); } /** * In Android T, the type of {@code flags} changed from {@code int} to {@link PackageInfoFlags} */ @Implementation(minSdk = N) protected Object getPackageInfoAsUser(Object packageName, Object flagsObject, Object userId) throws NameNotFoundException { int flags; if (RuntimeEnvironment.getApiLevel() >= TIRAMISU) { flags = (int) ((PackageInfoFlags) flagsObject).getValue(); } else { flags = (int) flagsObject; } return getPackageInfo((String) packageName, flags); } @Implementation protected int[] getPackageGids(String packageName) throws NameNotFoundException { return new int[0]; } @Implementation(minSdk = N) protected int[] getPackageGids(String packageName, int flags) throws NameNotFoundException { return null; } @Implementation(minSdk = JELLY_BEAN_MR2) protected int getPackageUid(String packageName, int flags) throws NameNotFoundException { Integer uid = uidForPackage.get(packageName); if (uid == null) { throw new NameNotFoundException(packageName); } return uid; } @Implementation(minSdk = TIRAMISU) protected Object getPackageUid(Object packageName, Object flags) throws NameNotFoundException { return getPackageUid((String) packageName, (int) ((PackageInfoFlags) flags).getValue()); } @Implementation(minSdk = N) protected int getPackageUidAsUser(String packageName, int userId) throws NameNotFoundException { return 0; } @Implementation(minSdk = N) protected int getPackageUidAsUser(String packageName, int flags, int userId) throws NameNotFoundException { return 0; } /** * @see ShadowPackageManager#addPermissionGroupInfo(android.content.pm.PermissionGroupInfo) */ @Implementation protected PermissionGroupInfo getPermissionGroupInfo(String name, int flags) throws NameNotFoundException { if (permissionGroups.containsKey(name)) { return new PermissionGroupInfo(permissionGroups.get(name)); } throw new NameNotFoundException(name); } /** * @see ShadowPackageManager#addPermissionGroupInfo(android.content.pm.PermissionGroupInfo) */ @Implementation protected List getAllPermissionGroups(int flags) { ArrayList allPermissionGroups = new ArrayList(); for (PermissionGroupInfo permissionGroupInfo : permissionGroups.values()) { allPermissionGroups.add(new PermissionGroupInfo(permissionGroupInfo)); } return allPermissionGroups; } @Implementation protected ApplicationInfo getApplicationInfo(String packageName, int flags) throws NameNotFoundException { PackageInfo packageInfo = getPackageInfo(packageName, flags); if (packageInfo.applicationInfo == null) { throw new NameNotFoundException("Package found but without application info"); } // Maybe query app infos from overridden resolveInfo as well? return packageInfo.applicationInfo; } @Implementation(minSdk = TIRAMISU) protected ApplicationInfo getApplicationInfo(Object packageName, Object flagsObject) throws NameNotFoundException { Preconditions.checkArgument(flagsObject instanceof PackageManager.ApplicationInfoFlags); PackageManager.ApplicationInfoFlags flags = (PackageManager.ApplicationInfoFlags) flagsObject; return getApplicationInfo((String) packageName, (int) (flags).getValue()); } private void applyFlagsToApplicationInfo(@Nullable ApplicationInfo appInfo, long flags) throws NameNotFoundException { if (appInfo == null) { return; } String packageName = appInfo.packageName; Integer stateOverride = applicationEnabledSettingMap.get(packageName); if (stateOverride == null) { stateOverride = COMPONENT_ENABLED_STATE_DEFAULT; } appInfo.enabled = (appInfo.enabled && stateOverride == COMPONENT_ENABLED_STATE_DEFAULT) || stateOverride == COMPONENT_ENABLED_STATE_ENABLED; synchronized (lock) { if (deletedPackages.contains(packageName)) { appInfo.flags &= ~FLAG_INSTALLED; } } if ((flags & MATCH_ALL) != 0 && Build.VERSION.SDK_INT >= 23) { return; } if ((flags & MATCH_UNINSTALLED_PACKAGES) == 0 && (appInfo.flags & FLAG_INSTALLED) == 0) { throw new NameNotFoundException("Package not installed: " + packageName); } if ((flags & MATCH_UNINSTALLED_PACKAGES) == 0 && hiddenPackages.contains(packageName)) { throw new NameNotFoundException("Package hidden: " + packageName); } } /** * Returns all the values added via {@link * ShadowPackageManager#addSystemSharedLibraryName(String)}. */ @Implementation protected String[] getSystemSharedLibraryNames() { return systemSharedLibraryNames.toArray(new String[systemSharedLibraryNames.size()]); } @Implementation(minSdk = N) protected @NonNull String getServicesSystemSharedLibraryPackageName() { return null; } @Implementation(minSdk = N) protected @NonNull String getSharedSystemSharedLibraryPackageName() { return ""; } @Implementation(minSdk = N) protected boolean hasSystemFeature(String name, int version) { return false; } @Implementation(minSdk = M) protected boolean isPermissionRevokedByPolicy(String permName, String pkgName) { return false; } @Implementation protected boolean addPermission(PermissionInfo info) { return false; } @Implementation protected boolean addPermissionAsync(PermissionInfo info) { return false; } @Implementation protected void removePermission(String name) {} @Implementation(minSdk = M) protected void grantRuntimePermission( String packageName, String permissionName, UserHandle user) { Integer uid; synchronized (lock) { if (!packageInfos.containsKey(packageName)) { throw new SecurityException("Package not found: " + packageName); } PackageInfo packageInfo = packageInfos.get(packageName); checkPermissionGrantStateInitialized(packageInfo); int permissionIndex = getPermissionIndex(packageInfo, permissionName); if (permissionIndex < 0) { throw new SecurityException( "Permission " + permissionName + " not requested by package " + packageName); } packageInfo.requestedPermissionsFlags[permissionIndex] |= REQUESTED_PERMISSION_GRANTED; uid = uidForPackage.get(packageName); } if (RuntimeEnvironment.getApiLevel() >= VERSION_CODES.M && uid != null) { for (Object listener : permissionListeners) { ((OnPermissionsChangedListener) listener).onPermissionsChanged(uid); } } } @Implementation(minSdk = M) protected void revokeRuntimePermission( String packageName, String permissionName, UserHandle user) { Integer uid; synchronized (lock) { if (!packageInfos.containsKey(packageName)) { throw new SecurityException("Package not found: " + packageName); } PackageInfo packageInfo = packageInfos.get(packageName); checkPermissionGrantStateInitialized(packageInfo); int permissionIndex = getPermissionIndex(packageInfo, permissionName); if (permissionIndex < 0) { throw new SecurityException( "Permission " + permissionName + " not requested by package " + packageName); } packageInfo.requestedPermissionsFlags[permissionIndex] &= ~REQUESTED_PERMISSION_GRANTED; uid = uidForPackage.get(packageName); } if (RuntimeEnvironment.getApiLevel() >= VERSION_CODES.M && uid != null) { for (Object listener : permissionListeners) { ((OnPermissionsChangedListener) listener).onPermissionsChanged(uid); } } } private void checkPermissionGrantStateInitialized(PackageInfo packageInfo) { if (packageInfo.requestedPermissionsFlags == null) { // In the real OS this would never be null, but tests don't necessarily initialize this // structure. throw new SecurityException( "Permission grant state (PackageInfo.requestedPermissionFlags) " + "is null. This operation requires this variable to be initialized."); } } /** * Returns the index of the given permission in the PackageInfo.requestedPermissions array, or -1 * if it's not found. */ private int getPermissionIndex(PackageInfo packageInfo, String permissionName) { if (packageInfo.requestedPermissions != null) { for (int i = 0; i < packageInfo.requestedPermissions.length; i++) { if (permissionName.equals(packageInfo.requestedPermissions[i])) { return i; } } } return -1; } /** * This method differs from the real implementation in that we only return the permission flags * that were added via updatePermissionFlags, and do not perform any verification of permissions, * packages or users. */ @Implementation(minSdk = M) protected int getPermissionFlags(String permissionName, String packageName, UserHandle user) { if (permissionFlags.containsKey(packageName)) { return permissionFlags.get(packageName).getOrDefault(permissionName, /* defaultValue= */ 0); } return 0; } /** * This method differs from the real implementation in that no permission checking or package * existent checks are performed here. */ @Implementation(minSdk = M) protected void updatePermissionFlags( String permissionName, String packageName, @PackageManager.PermissionFlags int flagMask, @PackageManager.PermissionFlags int flagValues, UserHandle user) { if (!permissionFlags.containsKey(packageName)) { permissionFlags.put(packageName, new HashMap()); } int existingFlags = permissionFlags.get(packageName).getOrDefault(permissionName, /* defaultValue= */ 0); int flagsToKeep = ~flagMask & existingFlags; int flagsToChange = flagMask & flagValues; int newFlags = flagsToKeep | flagsToChange; permissionFlags.get(packageName).put(permissionName, newFlags); } @Implementation protected int getUidForSharedUser(String sharedUserName) throws NameNotFoundException { return 0; } @Implementation(minSdk = N) protected List getInstalledPackagesAsUser(int flags, int userId) { return null; } @Implementation(minSdk = JELLY_BEAN_MR2) protected List getPackagesHoldingPermissions(String[] permissions, int flags) { synchronized (lock) { List packageInfosWithPermissions = new ArrayList<>(); for (PackageInfo packageInfo : packageInfos.values()) { for (String permission : permissions) { int permissionIndex = getPermissionIndex(packageInfo, permission); if (permissionIndex >= 0) { packageInfosWithPermissions.add(packageInfo); break; } } } return packageInfosWithPermissions; } } @Implementation(minSdk = S) public void getGroupOfPlatformPermission( String permissionName, Executor executor, Consumer callback) { String permissionGroup = null; try { PermissionInfo permissionInfo = getPermissionInfo(permissionName, PackageManager.GET_META_DATA); permissionGroup = permissionInfo.group; } catch (NameNotFoundException ignored) { // fall through } final String finalPermissionGroup = permissionGroup; executor.execute(() -> callback.accept(finalPermissionGroup)); } /** Behaves as {@link #resolveActivity(Intent, int)} and currently ignores userId. */ @Implementation(minSdk = JELLY_BEAN_MR1) protected ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId) { return resolveActivity(intent, flags); } @Implementation(minSdk = TIRAMISU) protected ResolveInfo resolveActivityAsUser(Object intent, Object flags, Object userId) { return resolveActivity((Intent) intent, (int) ((ResolveInfoFlags) flags).getValue()); } @Implementation(minSdk = TIRAMISU) protected ResolveInfo resolveServiceAsUser(Object intent, Object flags, Object userId) { return resolveService((Intent) intent, (int) ((ResolveInfoFlags) flags).getValue()); } @Implementation protected List queryIntentActivityOptions( ComponentName caller, Intent[] specifics, Intent intent, int flags) { return null; } @Implementation(minSdk = N) protected List queryBroadcastReceiversAsUser(Intent intent, int flags, int userId) { return null; } @Implementation protected List queryContentProviders(String processName, int uid, int flags) { return null; } @Implementation protected InstrumentationInfo getInstrumentationInfo(ComponentName className, int flags) throws NameNotFoundException { return null; } @Implementation protected List queryInstrumentation(String targetPackage, int flags) { return null; } @Nullable @Implementation protected Drawable getDrawable( String packageName, @DrawableRes int resId, @Nullable ApplicationInfo appInfo) { Drawable result = drawables.get(new Pair<>(packageName, resId)); if (result != null) { return result; } return reflector(ReflectorApplicationPackageManager.class, realObject) .getDrawable(packageName, resId, appInfo); } /** * Returns a user stored String resource with {@code resId} corresponding to {@code packageName}. * User can store this String via {@link #addStringResource(String, int, String)}. * *

Real method is called if the user has not stored a String corresponding to {@code resId} and * {@code packageName}. */ @Nullable @Implementation protected CharSequence getText(String packageName, int resId, ApplicationInfo appInfo) { if (stringResources.containsKey(packageName) && stringResources.get(packageName).containsKey(resId)) { return stringResources.get(packageName).get(resId); } return reflector(ReflectorApplicationPackageManager.class, realObject) .getText(packageName, resId, appInfo); } @Implementation protected Drawable getActivityIcon(ComponentName activityName) throws NameNotFoundException { Drawable result = drawableList.get(activityName); if (result != null) { return result; } return reflector(ReflectorApplicationPackageManager.class, realObject) .getActivityIcon(activityName); } @Implementation protected Drawable getDefaultActivityIcon() { return Resources.getSystem().getDrawable(com.android.internal.R.drawable.sym_def_app_icon); } @Implementation protected Resources getResourcesForActivity(ComponentName activityName) throws NameNotFoundException { return getResourcesForApplication(activityName.getPackageName()); } @Implementation protected Resources getResourcesForApplication(String appPackageName) throws NameNotFoundException { synchronized (lock) { if (getContext().getPackageName().equals(appPackageName)) { return getContext().getResources(); } else if (packageInfos.containsKey(appPackageName)) { Resources appResources = resources.get(appPackageName); if (appResources == null) { appResources = new Resources(new AssetManager(), null, null); resources.put(appPackageName, appResources); } return appResources; } throw new NameNotFoundException(appPackageName); } } @Implementation(minSdk = JELLY_BEAN_MR1) protected Resources getResourcesForApplicationAsUser(String appPackageName, int userId) throws NameNotFoundException { return null; } @Implementation(minSdk = M) protected void addOnPermissionsChangeListener(Object listener) { permissionListeners.add(listener); } @Implementation(minSdk = M) protected void removeOnPermissionsChangeListener(Object listener) { permissionListeners.remove(listener); } @Implementation(maxSdk = O_MR1) protected void installPackage( Object packageURI, Object observer, Object flags, Object installerPackageName) {} @Implementation(minSdk = JELLY_BEAN_MR1) protected int installExistingPackage(String packageName) throws NameNotFoundException { return 0; } @Implementation(minSdk = N) protected int installExistingPackageAsUser(String packageName, int userId) throws NameNotFoundException { return 0; } @Implementation(minSdk = M) protected void verifyIntentFilter(int id, int verificationCode, List failedDomains) {} @Implementation(minSdk = N) protected int getIntentVerificationStatusAsUser(String packageName, int userId) { return 0; } @Implementation(minSdk = N) protected boolean updateIntentVerificationStatusAsUser( String packageName, int status, int userId) { return false; } @Implementation(minSdk = M) protected List getIntentFilterVerifications(String packageName) { return null; } @Implementation(minSdk = M) protected List getAllIntentFilters(String packageName) { return null; } @Implementation(minSdk = N) protected String getDefaultBrowserPackageNameAsUser(int userId) { return null; } @Implementation(minSdk = N) protected boolean setDefaultBrowserPackageNameAsUser(String packageName, int userId) { return false; } @Implementation(minSdk = M) protected int getMoveStatus(int moveId) { return 0; } @Implementation(minSdk = M) protected void registerMoveCallback(Object callback, Object handler) {} @Implementation(minSdk = M) protected void unregisterMoveCallback(Object callback) {} @Implementation(minSdk = M) protected Object movePackage(Object packageName, Object vol) { return 0; } @Implementation(minSdk = M) protected Object getPackageCurrentVolume(Object app) { return null; } @Implementation(minSdk = M) protected List getPackageCandidateVolumes(ApplicationInfo app) { return null; } @Implementation(minSdk = M) protected Object movePrimaryStorage(Object vol) { return 0; } @Implementation(minSdk = M) protected @Nullable Object getPrimaryStorageCurrentVolume() { return null; } @Implementation(minSdk = M) protected @NonNull List getPrimaryStorageCandidateVolumes() { return null; } @Implementation(minSdk = N) protected void deletePackageAsUser( String packageName, IPackageDeleteObserver observer, int flags, int userId) {} @Implementation protected void clearApplicationUserData(String packageName, IPackageDataObserver observer) { clearedApplicationUserDataPackages.add(packageName); } @Implementation protected void deleteApplicationCacheFiles(String packageName, IPackageDataObserver observer) {} @Implementation(minSdk = N) protected void deleteApplicationCacheFilesAsUser( String packageName, int userId, IPackageDataObserver observer) {} @Implementation(minSdk = M) protected void freeStorage(String volumeUuid, long freeStorageSize, IntentSender pi) {} @Implementation(minSdk = N, maxSdk = O_MR1) protected String[] setPackagesSuspendedAsUser( String[] packageNames, boolean suspended, int userId) { return null; } @Implementation(minSdk = N) protected boolean isPackageSuspendedForUser(String packageName, int userId) { return false; } @Implementation protected void addPackageToPreferred(String packageName) {} @Implementation protected void removePackageFromPreferred(String packageName) {} @Implementation protected List getPreferredPackages(int flags) { return null; } @Implementation public void addPreferredActivity( IntentFilter filter, int match, ComponentName[] set, ComponentName activity) { addPreferredActivityInternal(filter, activity, preferredActivities); } @Implementation protected void replacePreferredActivity( IntentFilter filter, int match, ComponentName[] set, ComponentName activity) { addPreferredActivity(filter, match, set, activity); } @Implementation public int getPreferredActivities( List outFilters, List outActivities, String packageName) { return getPreferredActivitiesInternal( outFilters, outActivities, packageName, preferredActivities); } @Implementation protected void clearPackagePreferredActivities(String packageName) { clearPackagePreferredActivitiesInternal(packageName, preferredActivities); } @Implementation(minSdk = KITKAT) protected ComponentName getHomeActivities(List outActivities) { return null; } @Implementation(minSdk = N) protected void flushPackageRestrictionsAsUser(int userId) {} @Implementation(minSdk = LOLLIPOP) protected boolean setApplicationHiddenSettingAsUser( String packageName, boolean hidden, UserHandle user) { synchronized (lock) { // Note that this ignores the UserHandle parameter if (!packageInfos.containsKey(packageName)) { // Package doesn't exist return false; } if (hidden) { hiddenPackages.add(packageName); } else { hiddenPackages.remove(packageName); } return true; } } @Implementation(minSdk = LOLLIPOP) protected boolean getApplicationHiddenSettingAsUser(String packageName, UserHandle user) { // Note that this ignores the UserHandle parameter synchronized (lock) { if (!packageInfos.containsKey(packageName)) { // Match Android behaviour of returning true if package isn't found return true; } return hiddenPackages.contains(packageName); } } @Implementation protected VerifierDeviceIdentity getVerifierDeviceIdentity() { return null; } @Implementation(minSdk = LOLLIPOP_MR1) protected boolean isUpgrade() { return false; } @Implementation(minSdk = LOLLIPOP) protected boolean isPackageAvailable(String packageName) { return false; } @Implementation(minSdk = LOLLIPOP) protected void addCrossProfileIntentFilter( IntentFilter filter, int sourceUserId, int targetUserId, int flags) {} @Implementation(minSdk = LOLLIPOP) protected void clearCrossProfileIntentFilters(int sourceUserId) {} /** * Gets the unbadged icon based on the values set by {@link * ShadowPackageManager#setUnbadgedApplicationIcon} or returns null if nothing has been set. */ @Implementation(minSdk = LOLLIPOP_MR1) protected Drawable loadUnbadgedItemIcon(PackageItemInfo itemInfo, ApplicationInfo appInfo) { Drawable result = unbadgedApplicationIcons.get(itemInfo.packageName); if (result != null) { return result; } return reflector(ReflectorApplicationPackageManager.class, realObject) .loadUnbadgedItemIcon(itemInfo, appInfo); } /** * Adds a profile badge to the icon. * *

This implementation just returns the unbadged icon, as some default implementations add an * internal resource to the icon that is unavailable to Robolectric. */ @Implementation(minSdk = LOLLIPOP) protected Drawable getUserBadgedIcon(Drawable icon, UserHandle user) { return icon; } @Implementation(minSdk = O) protected boolean canRequestPackageInstalls() { return canRequestPackageInstalls; } @Implementation(minSdk = O) protected Object getChangedPackages(int sequenceNumber) { if (sequenceNumber < 0 || sequenceNumberChangedPackagesMap.get(sequenceNumber).isEmpty()) { return null; } return new ChangedPackages( sequenceNumber + 1, new ArrayList<>(sequenceNumberChangedPackagesMap.get(sequenceNumber))); } @Implementation(minSdk = P) public String getSystemTextClassifierPackageName() { return ""; } @Implementation(minSdk = P) @HiddenApi protected String[] setPackagesSuspended( String[] packageNames, boolean suspended, PersistableBundle appExtras, PersistableBundle launcherExtras, String dialogMessage) { return setPackagesSuspended( packageNames, suspended, appExtras, launcherExtras, dialogMessage, /* dialogInfo= */ null); } @Implementation(minSdk = Q) @HiddenApi protected /* String[] */ Object setPackagesSuspended( /* String[] */ Object packageNames, /* boolean */ Object suspended, /* PersistableBundle */ Object appExtras, /* PersistableBundle */ Object launcherExtras, /* SuspendDialogInfo */ Object dialogInfo) { return setPackagesSuspended( (String[]) packageNames, (boolean) suspended, (PersistableBundle) appExtras, (PersistableBundle) launcherExtras, /* dialogMessage= */ null, dialogInfo); } @Implementation(minSdk = R) protected boolean isAutoRevokeWhitelisted() { return whitelisted; } /** * Sets {@code packageNames} suspension status to {@code suspended} in the package manager. * *

At least one of {@code dialogMessage} and {@code dialogInfo} should be null. */ private String[] setPackagesSuspended( String[] packageNames, boolean suspended, PersistableBundle appExtras, PersistableBundle launcherExtras, String dialogMessage, Object dialogInfo) { if (hasProfileOwnerOrDeviceOwnerOnCurrentUser() && (VERSION.SDK_INT < VERSION_CODES.Q || !isCurrentApplicationProfileOwnerOrDeviceOwner())) { throw new UnsupportedOperationException(); } ArrayList unupdatedPackages = new ArrayList<>(); for (String packageName : packageNames) { if (!canSuspendPackage(packageName)) { unupdatedPackages.add(packageName); continue; } PackageSetting setting = packageSettings.get(packageName); if (setting == null) { unupdatedPackages.add(packageName); continue; } setting.setSuspended(suspended, dialogMessage, dialogInfo, appExtras, launcherExtras); } return unupdatedPackages.toArray(new String[0]); } /** Returns whether the current user profile has a profile owner or a device owner. */ private boolean isCurrentApplicationProfileOwnerOrDeviceOwner() { String currentApplication = getContext().getPackageName(); DevicePolicyManager devicePolicyManager = (DevicePolicyManager) getContext().getSystemService(Context.DEVICE_POLICY_SERVICE); return devicePolicyManager.isProfileOwnerApp(currentApplication) || devicePolicyManager.isDeviceOwnerApp(currentApplication); } /** Returns whether the current user profile has a profile owner or a device owner. */ private boolean hasProfileOwnerOrDeviceOwnerOnCurrentUser() { DevicePolicyManager devicePolicyManager = (DevicePolicyManager) getContext().getSystemService(Context.DEVICE_POLICY_SERVICE); return devicePolicyManager.getProfileOwner() != null || (UserHandle.of(UserHandle.myUserId()).isSystem() && devicePolicyManager.getDeviceOwner() != null); } private boolean canSuspendPackage(String packageName) { // This code approximately mirrors PackageManagerService#canSuspendPackageForUserLocked. return !packageName.equals(getContext().getPackageName()) && !isPackageDeviceAdmin(packageName) && !isPackageActiveLauncher(packageName) && !isPackageRequiredInstaller(packageName) && !isPackageRequiredUninstaller(packageName) && !isPackageRequiredVerifier(packageName) && !isPackageDefaultDialer(packageName) && !packageName.equals(PLATFORM_PACKAGE_NAME); } private boolean isPackageDeviceAdmin(String packageName) { DevicePolicyManager devicePolicyManager = (DevicePolicyManager) getContext().getSystemService(Context.DEVICE_POLICY_SERVICE); // Strictly speaking, this should be devicePolicyManager.getDeviceOwnerComponentOnAnyUser(), // but that method is currently not shadowed. return packageName.equals(devicePolicyManager.getDeviceOwner()); } private boolean isPackageActiveLauncher(String packageName) { Intent intent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME); ResolveInfo info = resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); return info != null && packageName.equals(info.activityInfo.packageName); } private boolean isPackageRequiredInstaller(String packageName) { Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.setDataAndType(Uri.fromFile(new File("foo.apk")), PACKAGE_MIME_TYPE); ResolveInfo info = resolveActivity( intent, PackageManager.MATCH_SYSTEM_ONLY | PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); return info != null && packageName.equals(info.activityInfo.packageName); } private boolean isPackageRequiredUninstaller(String packageName) { final Intent intent = new Intent(Intent.ACTION_UNINSTALL_PACKAGE); intent.addCategory(Intent.CATEGORY_DEFAULT); intent.setData(Uri.fromParts(PACKAGE_SCHEME, "foo.bar", null)); ResolveInfo info = resolveActivity( intent, PackageManager.MATCH_SYSTEM_ONLY | PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); return info != null && packageName.equals(info.activityInfo.packageName); } private boolean isPackageRequiredVerifier(String packageName) { final Intent intent = new Intent(Intent.ACTION_PACKAGE_NEEDS_VERIFICATION); List infos = queryBroadcastReceivers( intent, PackageManager.MATCH_SYSTEM_ONLY | PackageManager.MATCH_DIRECT_BOOT_AWARE | PackageManager.MATCH_DIRECT_BOOT_UNAWARE); if (infos != null) { for (ResolveInfo info : infos) { if (packageName.equals(info.activityInfo.packageName)) { return true; } } } return false; } private boolean isPackageDefaultDialer(String packageName) { TelecomManager telecomManager = (TelecomManager) getContext().getSystemService(Context.TELECOM_SERVICE); return packageName.equals(telecomManager.getDefaultDialerPackage()); } @HiddenApi @Implementation(minSdk = Q) @RequiresPermission(permission.SUSPEND_APPS) protected String[] getUnsuspendablePackages(String[] packageNames) { checkNotNull(packageNames, "packageNames cannot be null"); if (getContext().checkSelfPermission(permission.SUSPEND_APPS) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Current process does not have " + permission.SUSPEND_APPS); } ArrayList unsuspendablePackages = new ArrayList<>(); for (String packageName : packageNames) { if (!canSuspendPackage(packageName)) { unsuspendablePackages.add(packageName); } } return unsuspendablePackages.toArray(new String[0]); } @HiddenApi @Implementation(minSdk = P) protected boolean isPackageSuspended(String packageName) throws NameNotFoundException { PackageSetting setting = packageSettings.get(packageName); if (setting == null) { throw new NameNotFoundException(packageName); } return setting.isSuspended(); } @Implementation(minSdk = O) protected boolean isInstantApp(String packageName) { synchronized (lock) { PackageInfo pi = packageInfos.get(packageName); if (pi == null) { return false; } ApplicationInfo ai = pi.applicationInfo; if (ai == null) { return false; } return ai.isInstantApp(); } } @HiddenApi @Implementation(minSdk = Q) protected String[] setDistractingPackageRestrictions(String[] packages, int restrictionFlags) { for (String pkg : packages) { distractingPackageRestrictions.put(pkg, restrictionFlags); } return new String[0]; } private Context getContext() { return reflector(ReflectorApplicationPackageManager.class, realObject).getContext(); } /** Stub that will always throw. */ @Implementation(minSdk = S) protected Object /* PackageManager.Property */ getProperty( String propertyName, String packageName) throws NameNotFoundException { // TODO: in future read this value from parsed manifest throw new NameNotFoundException("unsupported"); } /** Stub that will always throw. */ @Implementation(minSdk = S) protected Object /* PackageManager.Property */ getProperty( String propertyName, ComponentName name) throws NameNotFoundException { // TODO: in future read this value from parsed manifest throw new NameNotFoundException("unsupported"); } /** Reflector interface for {@link ApplicationPackageManager}'s internals. */ @ForType(ApplicationPackageManager.class) private interface ReflectorApplicationPackageManager { @Direct Resources getResourcesForApplication(@NonNull ApplicationInfo applicationInfo); @Direct Drawable getDrawable( String packageName, @DrawableRes int resId, @Nullable ApplicationInfo appInfo); @Direct CharSequence getText(String packageName, @StringRes int resId, ApplicationInfo appInfo); @Direct Drawable getActivityIcon(ComponentName activityName); @Direct Drawable loadUnbadgedItemIcon(PackageItemInfo itemInfo, ApplicationInfo appInfo); @Direct PackageInfo getPackageArchiveInfo(String archiveFilePath, int flags); @Accessor("mContext") Context getContext(); } /** Returns the list of package names that were requested to be cleared. */ public List getClearedApplicationUserDataPackages() { return Collections.unmodifiableList(clearedApplicationUserDataPackages); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy