content: /*
   * Copyright (C) 2010 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package android.app;
  
  import android.annotation.NonNull;
  import android.annotation.Nullable;
  import android.annotation.UnsupportedAppUsage;
  import android.content.BroadcastReceiver;
  import android.content.ComponentName;
  import android.content.Context;
  import android.content.IIntentReceiver;
  import android.content.Intent;
  import android.content.ServiceConnection;
  import android.content.pm.ApplicationInfo;
  import android.content.pm.IPackageManager;
  import android.content.pm.PackageManager;
  import android.content.pm.PackageManager.NameNotFoundException;
  import android.content.pm.SharedLibraryInfo;
  import android.content.pm.dex.ArtManager;
  import android.content.pm.split.SplitDependencyLoader;
  import android.content.res.AssetManager;
  import android.content.res.CompatibilityInfo;
  import android.content.res.Resources;
  import android.os.Build;
  import android.os.Bundle;
  import android.os.FileUtils;
  import android.os.Handler;
  import android.os.IBinder;
  import android.os.Process;
  import android.os.RemoteException;
  import android.os.StrictMode;
  import android.os.SystemProperties;
  import android.os.Trace;
  import android.os.UserHandle;
  import android.text.TextUtils;
  import android.util.AndroidRuntimeException;
  import android.util.ArrayMap;
  import android.util.Log;
  import android.util.Slog;
  import android.util.SparseArray;
  import android.view.Display;
  import android.view.DisplayAdjustments;
  
  import com.android.internal.util.ArrayUtils;
  
  import dalvik.system.BaseDexClassLoader;
  import dalvik.system.VMRuntime;
  
  import java.io.File;
  import java.io.IOException;
  import java.io.InputStream;
  import java.lang.ref.WeakReference;
  import java.lang.reflect.InvocationTargetException;
  import java.lang.reflect.Method;
  import java.net.URL;
  import java.nio.file.Paths;
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.Enumeration;
  import java.util.LinkedHashSet;
  import java.util.List;
  import java.util.Objects;
  import java.util.Set;
  import java.util.concurrent.Executor;
  
  final class IntentReceiverLeaked extends AndroidRuntimeException {
      @UnsupportedAppUsage
      public IntentReceiverLeaked(String msg) {
          super(msg);
      }
  }
  
  final class ServiceConnectionLeaked extends AndroidRuntimeException {
      @UnsupportedAppUsage
      public ServiceConnectionLeaked(String msg) {
          super(msg);
      }
  }
  
  /**
   * Local state maintained about a currently loaded .apk.
   * @hide
   */
  public final class LoadedApk {
      static final String TAG = "LoadedApk";
      static final boolean DEBUG = false;
      private static final String PROPERTY_NAME_APPEND_NATIVE = "pi.append_native_lib_paths";
  
      @UnsupportedAppUsage
      private final ActivityThread mActivityThread;
      @UnsupportedAppUsage
      final String mPackageName;
      @UnsupportedAppUsage
      private ApplicationInfo mApplicationInfo;
      @UnsupportedAppUsage
      private String mAppDir;
      @UnsupportedAppUsage
      private String mResDir;
      private String[] mOverlayDirs;
      @UnsupportedAppUsage
      private String mDataDir;
      @UnsupportedAppUsage
      private String mLibDir;
      @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
      private File mDataDirFile;
      private File mDeviceProtectedDataDirFile;
      private File mCredentialProtectedDataDirFile;
      @UnsupportedAppUsage
      private final ClassLoader mBaseClassLoader;
      private ClassLoader mDefaultClassLoader;
      private final boolean mSecurityViolation;
      private final boolean mIncludeCode;
      private final boolean mRegisterPackage;
      @UnsupportedAppUsage
      private final DisplayAdjustments mDisplayAdjustments = new DisplayAdjustments();
      /** WARNING: This may change. Don't hold external references to it. */
      @UnsupportedAppUsage
      Resources mResources;
      @UnsupportedAppUsage
      private ClassLoader mClassLoader;
      @UnsupportedAppUsage
      private Application mApplication;
  
      private String[] mSplitNames;
      private String[] mSplitAppDirs;
      @UnsupportedAppUsage
      private String[] mSplitResDirs;
      private String[] mSplitClassLoaderNames;
  
      @UnsupportedAppUsage
      private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers
          = new ArrayMap<>();
      private final ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>> mUnregisteredReceivers
          = new ArrayMap<>();
      @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
      private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices
          = new ArrayMap<>();
      private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mUnboundServices
          = new ArrayMap<>();
      private AppComponentFactory mAppComponentFactory;
  
      Application getApplication() {
          return mApplication;
      }
  
      /**
       * Create information about a new .apk
       *
       * NOTE: This constructor is called with ActivityThread's lock held,
       * so MUST NOT call back out to the activity manager.
       */
      public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo,
              CompatibilityInfo compatInfo, ClassLoader baseLoader,
              boolean securityViolation, boolean includeCode, boolean registerPackage) {
  
          mActivityThread = activityThread;
          setApplicationInfo(aInfo);
          mPackageName = aInfo.packageName;
          mBaseClassLoader = baseLoader;
          mSecurityViolation = securityViolation;
          mIncludeCode = includeCode;
          mRegisterPackage = registerPackage;
          mDisplayAdjustments.setCompatibilityInfo(compatInfo);
          mAppComponentFactory = createAppFactory(mApplicationInfo, mBaseClassLoader);
      }
  
      private static ApplicationInfo adjustNativeLibraryPaths(ApplicationInfo info) {
          // If we're dealing with a multi-arch application that has both
          // 32 and 64 bit shared libraries, we might need to choose the secondary
          // depending on what the current runtime's instruction set is.
          if (info.primaryCpuAbi != null && info.secondaryCpuAbi != null) {
              final String runtimeIsa = VMRuntime.getRuntime().vmInstructionSet();
  
              // Get the instruction set that the libraries of secondary Abi is supported.
              // In presence of a native bridge this might be different than the one secondary Abi used.
              String secondaryIsa = VMRuntime.getInstructionSet(info.secondaryCpuAbi);
              final String secondaryDexCodeIsa = SystemProperties.get("ro.dalvik.vm.isa." + secondaryIsa);
              secondaryIsa = secondaryDexCodeIsa.isEmpty() ? secondaryIsa : secondaryDexCodeIsa;
  
              // If the runtimeIsa is the same as the primary isa, then we do nothing.
              // Everything will be set up correctly because info.nativeLibraryDir will
              // correspond to the right ISA.
              if (runtimeIsa.equals(secondaryIsa)) {
                  final ApplicationInfo modified = new ApplicationInfo(info);
                  modified.nativeLibraryDir = modified.secondaryNativeLibraryDir;
                  modified.primaryCpuAbi = modified.secondaryCpuAbi;
                  return modified;
              }
          }
  
          return info;
      }
  
      /**
       * Create information about the system package.
       * Must call {@link #installSystemApplicationInfo} later.
       */
      LoadedApk(ActivityThread activityThread) {
          mActivityThread = activityThread;
          mApplicationInfo = new ApplicationInfo();
          mApplicationInfo.packageName = "android";
          mPackageName = "android";
          mAppDir = null;
          mResDir = null;
          mSplitAppDirs = null;
          mSplitResDirs = null;
          mSplitClassLoaderNames = null;
          mOverlayDirs = null;
          mDataDir = null;
          mDataDirFile = null;
          mDeviceProtectedDataDirFile = null;
          mCredentialProtectedDataDirFile = null;
          mLibDir = null;
          mBaseClassLoader = null;
          mSecurityViolation = false;
          mIncludeCode = true;
          mRegisterPackage = false;
          mResources = Resources.getSystem();
          mDefaultClassLoader = ClassLoader.getSystemClassLoader();
          mAppComponentFactory = createAppFactory(mApplicationInfo, mDefaultClassLoader);
          mClassLoader = mAppComponentFactory.instantiateClassLoader(mDefaultClassLoader,
                  new ApplicationInfo(mApplicationInfo));
      }
  
      /**
       * Sets application info about the system package.
       */
      void installSystemApplicationInfo(ApplicationInfo info, ClassLoader classLoader) {
          assert info.packageName.equals("android");
          mApplicationInfo = info;
          mDefaultClassLoader = classLoader;
          mAppComponentFactory = createAppFactory(info, mDefaultClassLoader);
          mClassLoader = mAppComponentFactory.instantiateClassLoader(mDefaultClassLoader,
                  new ApplicationInfo(mApplicationInfo));
      }
  
      private AppComponentFactory createAppFactory(ApplicationInfo appInfo, ClassLoader cl) {
          if (appInfo.appComponentFactory != null && cl != null) {
              try {
                  return (AppComponentFactory)
                          cl.loadClass(appInfo.appComponentFactory).newInstance();
              } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
                  Slog.e(TAG, "Unable to instantiate appComponentFactory", e);
              }
          }
          return AppComponentFactory.DEFAULT;
      }
  
      public AppComponentFactory getAppFactory() {
          return mAppComponentFactory;
      }
  
      @UnsupportedAppUsage
      public String getPackageName() {
          return mPackageName;
      }
  
      @UnsupportedAppUsage
      public ApplicationInfo getApplicationInfo() {
          return mApplicationInfo;
      }
  
      public int getTargetSdkVersion() {
          return mApplicationInfo.targetSdkVersion;
      }
  
      public boolean isSecurityViolation() {
          return mSecurityViolation;
      }
  
      @UnsupportedAppUsage
      public CompatibilityInfo getCompatibilityInfo() {
          return mDisplayAdjustments.getCompatibilityInfo();
      }
  
      public void setCompatibilityInfo(CompatibilityInfo compatInfo) {
          mDisplayAdjustments.setCompatibilityInfo(compatInfo);
      }
  
      /**
       * Gets the array of shared libraries that are listed as
       * used by the given package.
       *
       * @param packageName the name of the package (note: not its
       * file name)
       * @return null-ok; the array of shared libraries, each one
       * a fully-qualified path
       */
      private static String[] getLibrariesFor(String packageName) {
          ApplicationInfo ai = null;
          try {
              ai = ActivityThread.getPackageManager().getApplicationInfo(packageName,
                      PackageManager.GET_SHARED_LIBRARY_FILES, UserHandle.myUserId());
          } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
          }
  
          if (ai == null) {
              return null;
          }
  
          return ai.sharedLibraryFiles;
      }
  
      /**
       * Update the ApplicationInfo for an app. If oldPaths is null, all the paths are considered
       * new.
       * @param aInfo The new ApplicationInfo to use for this LoadedApk
       * @param oldPaths The code paths for the old ApplicationInfo object. null means no paths can
       *                 be reused.
       */
      public void updateApplicationInfo(@NonNull ApplicationInfo aInfo,
              @Nullable List<String> oldPaths) {
          setApplicationInfo(aInfo);
  
          final List<String> newPaths = new ArrayList<>();
          makePaths(mActivityThread, aInfo, newPaths);
          final List<String> addedPaths = new ArrayList<>(newPaths.size());
  
          if (oldPaths != null) {
              for (String path : newPaths) {
                  final String apkName = path.substring(path.lastIndexOf(File.separator));
                  boolean match = false;
                  for (String oldPath : oldPaths) {
                      final String oldApkName = oldPath.substring(oldPath.lastIndexOf(File.separator));
                      if (apkName.equals(oldApkName)) {
                          match = true;
                          break;
                      }
                  }
                  if (!match) {
                      addedPaths.add(path);
                  }
              }
          } else {
              addedPaths.addAll(newPaths);
          }
          synchronized (this) {
              createOrUpdateClassLoaderLocked(addedPaths);
              if (mResources != null) {
                  final String[] splitPaths;
                  try {
                      splitPaths = getSplitPaths(null);
                  } catch (NameNotFoundException e) {
                      // This should NEVER fail.
                      throw new AssertionError("null split not found");
                  }
  
                  mResources = ResourcesManager.getInstance().getResources(null, mResDir,
                          splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
                          Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(),
                          getClassLoader());
              }
          }
          mAppComponentFactory = createAppFactory(aInfo, mDefaultClassLoader);
      }
  
      private void setApplicationInfo(ApplicationInfo aInfo) {
          final int myUid = Process.myUid();
          aInfo = adjustNativeLibraryPaths(aInfo);
          mApplicationInfo = aInfo;
          mAppDir = aInfo.sourceDir;
          mResDir = aInfo.uid == myUid ? aInfo.sourceDir : aInfo.publicSourceDir;
          mOverlayDirs = aInfo.resourceDirs;
          mDataDir = aInfo.dataDir;
          mLibDir = aInfo.nativeLibraryDir;
          mDataDirFile = FileUtils.newFileOrNull(aInfo.dataDir);
          mDeviceProtectedDataDirFile = FileUtils.newFileOrNull(aInfo.deviceProtectedDataDir);
          mCredentialProtectedDataDirFile = FileUtils.newFileOrNull(aInfo.credentialProtectedDataDir);
  
          mSplitNames = aInfo.splitNames;
          mSplitAppDirs = aInfo.splitSourceDirs;
          mSplitResDirs = aInfo.uid == myUid ? aInfo.splitSourceDirs : aInfo.splitPublicSourceDirs;
          mSplitClassLoaderNames = aInfo.splitClassLoaderNames;
  
          if (aInfo.requestsIsolatedSplitLoading() && !ArrayUtils.isEmpty(mSplitNames)) {
              mSplitLoader = new SplitDependencyLoaderImpl(aInfo.splitDependencies);
          }
      }
  
      public static void makePaths(ActivityThread activityThread,
                                   ApplicationInfo aInfo,
                                   List<String> outZipPaths) {
          makePaths(activityThread, false, aInfo, outZipPaths, null);
      }
  
      private static void appendSharedLibrariesLibPathsIfNeeded(
              List<SharedLibraryInfo> sharedLibraries, ApplicationInfo aInfo,
              Set<String> outSeenPaths,
              List<String> outLibPaths) {
          if (sharedLibraries == null) {
              return;
          }
          for (SharedLibraryInfo lib : sharedLibraries) {
              List<String> paths = lib.getAllCodePaths();
              outSeenPaths.addAll(paths);
              for (String path : paths) {
                  appendApkLibPathIfNeeded(path, aInfo, outLibPaths);
              }
              appendSharedLibrariesLibPathsIfNeeded(
                      lib.getDependencies(), aInfo, outSeenPaths, outLibPaths);
          }
      }
  
      public static void makePaths(ActivityThread activityThread,
                                   boolean isBundledApp,
                                   ApplicationInfo aInfo,
                                   List<String> outZipPaths,
                                   List<String> outLibPaths) {
          final String appDir = aInfo.sourceDir;
          final String libDir = aInfo.nativeLibraryDir;
  
          outZipPaths.clear();
          outZipPaths.add(appDir);
  
          // Do not load all available splits if the app requested isolated split loading.
          if (aInfo.splitSourceDirs != null && !aInfo.requestsIsolatedSplitLoading()) {
              Collections.addAll(outZipPaths, aInfo.splitSourceDirs);
          }
  
          if (outLibPaths != null) {
              outLibPaths.clear();
          }
  
          /*
           * The following is a bit of a hack to inject
           * instrumentation into the system: If the app
           * being started matches one of the instrumentation names,
           * then we combine both the "instrumentation" and
           * "instrumented" app into the path, along with the
           * concatenation of both apps' shared library lists.
           */
  
          String[] instrumentationLibs = null;
          // activityThread will be null when called from the WebView zygote; just assume
          // no instrumentation applies in this case.
          if (activityThread != null) {
              String instrumentationPackageName = activityThread.mInstrumentationPackageName;
              String instrumentationAppDir = activityThread.mInstrumentationAppDir;
              String[] instrumentationSplitAppDirs = activityThread.mInstrumentationSplitAppDirs;
              String instrumentationLibDir = activityThread.mInstrumentationLibDir;
  
              String instrumentedAppDir = activityThread.mInstrumentedAppDir;
              String[] instrumentedSplitAppDirs = activityThread.mInstrumentedSplitAppDirs;
              String instrumentedLibDir = activityThread.mInstrumentedLibDir;
  
              if (appDir.equals(instrumentationAppDir)
                      || appDir.equals(instrumentedAppDir)) {
                  outZipPaths.clear();
                  outZipPaths.add(instrumentationAppDir);
  
                  // Only add splits if the app did not request isolated split loading.
                  if (!aInfo.requestsIsolatedSplitLoading()) {
                      if (instrumentationSplitAppDirs != null) {
                          Collections.addAll(outZipPaths, instrumentationSplitAppDirs);
                      }
  
                      if (!instrumentationAppDir.equals(instrumentedAppDir)) {
                          outZipPaths.add(instrumentedAppDir);
                          if (instrumentedSplitAppDirs != null) {
                              Collections.addAll(outZipPaths, instrumentedSplitAppDirs);
                          }
                      }
                  }
  
                  if (outLibPaths != null) {
                      outLibPaths.add(instrumentationLibDir);
                      if (!instrumentationLibDir.equals(instrumentedLibDir)) {
                          outLibPaths.add(instrumentedLibDir);
                      }
                  }
  
                  if (!instrumentedAppDir.equals(instrumentationAppDir)) {
                      instrumentationLibs = getLibrariesFor(instrumentationPackageName);
                  }
              }
          }
  
          if (outLibPaths != null) {
              if (outLibPaths.isEmpty()) {
                  outLibPaths.add(libDir);
              }
  
              // Add path to libraries in apk for current abi. Do this now because more entries
              // will be added to zipPaths that shouldn't be part of the library path.
              if (aInfo.primaryCpuAbi != null) {
                  // Add fake libs into the library search path if we target prior to N.
                  if (aInfo.targetSdkVersion < Build.VERSION_CODES.N) {
                      outLibPaths.add("/system/fake-libs" +
                          (VMRuntime.is64BitAbi(aInfo.primaryCpuAbi) ? "64" : ""));
                  }
                  for (String apk : outZipPaths) {
                      outLibPaths.add(apk + "!/lib/" + aInfo.primaryCpuAbi);
                  }
              }
  
              if (isBundledApp) {
                  // Add path to system libraries to libPaths;
                  // Access to system libs should be limited
                  // to bundled applications; this is why updated
                  // system apps are not included.
                  outLibPaths.add(System.getProperty("java.library.path"));
              }
          }
  
          // Add the shared libraries native paths. The dex files in shared libraries will
          // be resolved through shared library loaders, which are setup later.
          Set<String> outSeenPaths = new LinkedHashSet<>();
          appendSharedLibrariesLibPathsIfNeeded(
                  aInfo.sharedLibraryInfos, aInfo, outSeenPaths, outLibPaths);
  
          // ApplicationInfo.sharedLibraryFiles is a public API, so anyone can change it.
          // We prepend shared libraries that the package manager hasn't seen, maintaining their
          // original order where possible.
          if (aInfo.sharedLibraryFiles != null) {
              int index = 0;
              for (String lib : aInfo.sharedLibraryFiles) {
                  if (!outSeenPaths.contains(lib) && !outZipPaths.contains(lib)) {
                      outZipPaths.add(index, lib);
                      index++;
                      appendApkLibPathIfNeeded(lib, aInfo, outLibPaths);
                  }
              }
          }
  
          if (instrumentationLibs != null) {
              for (String lib : instrumentationLibs) {
                  if (!outZipPaths.contains(lib)) {
                      outZipPaths.add(0, lib);
                      appendApkLibPathIfNeeded(lib, aInfo, outLibPaths);
                  }
              }
          }
      }
  
      /**
       * This method appends a path to the appropriate native library folder of a
       * library if this library is hosted in an APK. This allows support for native
       * shared libraries. The library API is determined based on the application
       * ABI.
       *
       * @param path Path to the library.
       * @param applicationInfo The application depending on the library.
       * @param outLibPaths List to which to add the native lib path if needed.
       */
      private static void appendApkLibPathIfNeeded(@NonNull String path,
              @NonNull ApplicationInfo applicationInfo, @Nullable List<String> outLibPaths) {
          // Looking at the suffix is a little hacky but a safe and simple solution.
          // We will be revisiting code in the next release and clean this up.
          if (outLibPaths != null && applicationInfo.primaryCpuAbi != null && path.endsWith(".apk")) {
              if (applicationInfo.targetSdkVersion >= Build.VERSION_CODES.O) {
                  outLibPaths.add(path + "!/lib/" + applicationInfo.primaryCpuAbi);
              }
          }
      }
  
      /*
       * All indices received by the super class should be shifted by 1 when accessing mSplitNames,
       * etc. The super class assumes the base APK is index 0, while the PackageManager APIs don't
       * include the base APK in the list of splits.
       */
      private class SplitDependencyLoaderImpl extends SplitDependencyLoader<NameNotFoundException> {
          private final String[][] mCachedResourcePaths;
          private final ClassLoader[] mCachedClassLoaders;
  
          SplitDependencyLoaderImpl(@NonNull SparseArray<int[]> dependencies) {
              super(dependencies);
              mCachedResourcePaths = new String[mSplitNames.length + 1][];
              mCachedClassLoaders = new ClassLoader[mSplitNames.length + 1];
          }
  
          @Override
          protected boolean isSplitCached(int splitIdx) {
              return mCachedClassLoaders[splitIdx] != null;
          }
  
          @Override
          protected void constructSplit(int splitIdx, @NonNull int[] configSplitIndices,
                  int parentSplitIdx) throws NameNotFoundException {
              final ArrayList<String> splitPaths = new ArrayList<>();
              if (splitIdx == 0) {
                  createOrUpdateClassLoaderLocked(null);
                  mCachedClassLoaders[0] = mClassLoader;
  
                  // Never add the base resources here, they always get added no matter what.
                  for (int configSplitIdx : configSplitIndices) {
                      splitPaths.add(mSplitResDirs[configSplitIdx - 1]);
                  }
                  mCachedResourcePaths[0] = splitPaths.toArray(new String[splitPaths.size()]);
                  return;
              }
  
              // Since we handled the special base case above, parentSplitIdx is always valid.
              final ClassLoader parent = mCachedClassLoaders[parentSplitIdx];
              mCachedClassLoaders[splitIdx] = ApplicationLoaders.getDefault().getClassLoader(
                      mSplitAppDirs[splitIdx - 1], getTargetSdkVersion(), false, null, null, parent,
                      mSplitClassLoaderNames[splitIdx - 1]);
  
              Collections.addAll(splitPaths, mCachedResourcePaths[parentSplitIdx]);
              splitPaths.add(mSplitResDirs[splitIdx - 1]);
              for (int configSplitIdx : configSplitIndices) {
                  splitPaths.add(mSplitResDirs[configSplitIdx - 1]);
              }
              mCachedResourcePaths[splitIdx] = splitPaths.toArray(new String[splitPaths.size()]);
          }
  
          private int ensureSplitLoaded(String splitName) throws NameNotFoundException {
              int idx = 0;
              if (splitName != null) {
                  idx = Arrays.binarySearch(mSplitNames, splitName);
                  if (idx < 0) {
                      throw new PackageManager.NameNotFoundException(
                              "Split name '" + splitName + "' is not installed");
                  }
                  idx += 1;
              }
              loadDependenciesForSplit(idx);
              return idx;
          }
  
          ClassLoader getClassLoaderForSplit(String splitName) throws NameNotFoundException {
              return mCachedClassLoaders[ensureSplitLoaded(splitName)];
          }
  
          String[] getSplitPathsForSplit(String splitName) throws NameNotFoundException {
              return mCachedResourcePaths[ensureSplitLoaded(splitName)];
          }
      }
  
      private SplitDependencyLoaderImpl mSplitLoader;
  
      ClassLoader getSplitClassLoader(String splitName) throws NameNotFoundException {
          if (mSplitLoader == null) {
              return mClassLoader;
          }
          return mSplitLoader.getClassLoaderForSplit(splitName);
      }
  
      String[] getSplitPaths(String splitName) throws NameNotFoundException {
          if (mSplitLoader == null) {
              return mSplitResDirs;
          }
          return mSplitLoader.getSplitPathsForSplit(splitName);
      }
  
      /**
       * Create a class loader for the {@code sharedLibrary}. Shared libraries are canonicalized,
       * so if we already created a class loader with that shared library, we return it.
       *
       * Implementation notes: the canonicalization of shared libraries is something dex2oat
       * also does.
       */
      ClassLoader createSharedLibraryLoader(SharedLibraryInfo sharedLibrary,
              boolean isBundledApp, String librarySearchPath, String libraryPermittedPath) {
          List<String> paths = sharedLibrary.getAllCodePaths();
          List<ClassLoader> sharedLibraries = createSharedLibrariesLoaders(
                  sharedLibrary.getDependencies(), isBundledApp, librarySearchPath,
                  libraryPermittedPath);
          final String jars = (paths.size() == 1) ? paths.get(0) :
                  TextUtils.join(File.pathSeparator, paths);
  
          // Shared libraries get a null parent: this has the side effect of having canonicalized
          // shared libraries using ApplicationLoaders cache, which is the behavior we want.
          return ApplicationLoaders.getDefault().getSharedLibraryClassLoaderWithSharedLibraries(jars,
                      mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
                      libraryPermittedPath, /* parent */ null,
                      /* classLoaderName */ null, sharedLibraries);
      }
  
      private List<ClassLoader> createSharedLibrariesLoaders(List<SharedLibraryInfo> sharedLibraries,
              boolean isBundledApp, String librarySearchPath, String libraryPermittedPath) {
          if (sharedLibraries == null) {
              return null;
          }
          List<ClassLoader> loaders = new ArrayList<>();
          for (SharedLibraryInfo info : sharedLibraries) {
              loaders.add(createSharedLibraryLoader(
                      info, isBundledApp, librarySearchPath, libraryPermittedPath));
          }
          return loaders;
      }
  
      private StrictMode.ThreadPolicy allowThreadDiskReads() {
          if (mActivityThread == null) {
              // When LoadedApk is used without an ActivityThread (usually in a
              // zygote context), don't call into StrictMode, as it initializes
              // the binder subsystem, which we don't want.
              return null;
          }
  
          return StrictMode.allowThreadDiskReads();
      }
  
      private void setThreadPolicy(StrictMode.ThreadPolicy policy) {
          if (mActivityThread != null && policy != null) {
              StrictMode.setThreadPolicy(policy);
          }
      }
  
      private void createOrUpdateClassLoaderLocked(List<String> addedPaths) {
          if (mPackageName.equals("android")) {
              // Note: This branch is taken for system server and we don't need to setup
              // jit profiling support.
              if (mClassLoader != null) {
                  // nothing to update
                  return;
              }
  
              if (mBaseClassLoader != null) {
                  mDefaultClassLoader = mBaseClassLoader;
              } else {
                  mDefaultClassLoader = ClassLoader.getSystemClassLoader();
              }
              mAppComponentFactory = createAppFactory(mApplicationInfo, mDefaultClassLoader);
              mClassLoader = mAppComponentFactory.instantiateClassLoader(mDefaultClassLoader,
                      new ApplicationInfo(mApplicationInfo));
              return;
          }
  
          // Avoid the binder call when the package is the current application package.
          // The activity manager will perform ensure that dexopt is performed before
          // spinning up the process. Similarly, don't call into binder when we don't
          // have an ActivityThread object.
          if (mActivityThread != null
                  && !Objects.equals(mPackageName, ActivityThread.currentPackageName())
                  && mIncludeCode) {
              try {
                  ActivityThread.getPackageManager().notifyPackageUse(mPackageName,
                          PackageManager.NOTIFY_PACKAGE_USE_CROSS_PACKAGE);
              } catch (RemoteException re) {
                  throw re.rethrowFromSystemServer();
              }
          }
  
          if (mRegisterPackage) {
              try {
                  ActivityManager.getService().addPackageDependency(mPackageName);
              } catch (RemoteException e) {
                  throw e.rethrowFromSystemServer();
              }
          }
  
          // Lists for the elements of zip/code and native libraries.
          //
          // Both lists are usually not empty. We expect on average one APK for the zip component,
          // but shared libraries and splits are not uncommon. We expect at least three elements
          // for native libraries (app-based, system, vendor). As such, give both some breathing
          // space and initialize to a small value (instead of incurring growth code).
          final List<String> zipPaths = new ArrayList<>(10);
          final List<String> libPaths = new ArrayList<>(10);
  
          boolean isBundledApp = mApplicationInfo.isSystemApp()
                  && !mApplicationInfo.isUpdatedSystemApp();
  
          // Vendor apks are treated as bundled only when /vendor/lib is in the default search
          // paths. If not, they are treated as unbundled; access to system libs is limited.
          // Having /vendor/lib in the default search paths means that all system processes
          // are allowed to use any vendor library, which in turn means that system is dependent
          // on vendor partition. In the contrary, not having /vendor/lib in the default search
          // paths mean that the two partitions are separated and thus we can treat vendor apks
          // as unbundled.
          final String defaultSearchPaths = System.getProperty("java.library.path");
          final boolean treatVendorApkAsUnbundled = !defaultSearchPaths.contains("/vendor/lib");
          if (mApplicationInfo.getCodePath() != null
                  && mApplicationInfo.isVendor() && treatVendorApkAsUnbundled) {
              isBundledApp = false;
          }
  
          makePaths(mActivityThread, isBundledApp, mApplicationInfo, zipPaths, libPaths);
  
          String libraryPermittedPath = mDataDir;
  
          if (isBundledApp) {
              // For bundled apps, add the base directory of the app (e.g.,
              // /system/app/Foo/) to the permitted paths so that it can load libraries
              // embedded in module apks under the directory. For now, GmsCore is relying
              // on this, but this isn't specific to the app. Also note that, we don't
              // need to do this for unbundled apps as entire /data is already set to
              // the permitted paths for them.
              libraryPermittedPath += File.pathSeparator
                      + Paths.get(getAppDir()).getParent().toString();
  
              // This is necessary to grant bundled apps access to
              // libraries located in subdirectories of /system/lib
              libraryPermittedPath += File.pathSeparator + defaultSearchPaths;
          }
  
          final String librarySearchPath = TextUtils.join(File.pathSeparator, libPaths);
  
          // If we're not asked to include code, we construct a classloader that has
          // no code path included. We still need to set up the library search paths
          // and permitted path because NativeActivity relies on it (it attempts to
          // call System.loadLibrary() on a classloader from a LoadedApk with
          // mIncludeCode == false).
          if (!mIncludeCode) {
              if (mDefaultClassLoader == null) {
                  StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
                  mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoader(
                          "" /* codePath */, mApplicationInfo.targetSdkVersion, isBundledApp,
                          librarySearchPath, libraryPermittedPath, mBaseClassLoader,
                          null /* classLoaderName */);
                  setThreadPolicy(oldPolicy);
                  mAppComponentFactory = AppComponentFactory.DEFAULT;
              }
  
              if (mClassLoader == null) {
                  mClassLoader = mAppComponentFactory.instantiateClassLoader(mDefaultClassLoader,
                          new ApplicationInfo(mApplicationInfo));
              }
  
              return;
          }
  
          /*
           * With all the combination done (if necessary, actually create the java class
           * loader and set up JIT profiling support if necessary.
           *
           * In many cases this is a single APK, so try to avoid the StringBuilder in TextUtils.
           */
          final String zip = (zipPaths.size() == 1) ? zipPaths.get(0) :
                  TextUtils.join(File.pathSeparator, zipPaths);
  
          if (DEBUG) Slog.v(ActivityThread.TAG, "Class path: " + zip +
                      ", JNI path: " + librarySearchPath);
  
          boolean needToSetupJitProfiles = false;
          if (mDefaultClassLoader == null) {
              // Temporarily disable logging of disk reads on the Looper thread
              // as this is early and necessary.
              StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
  
              List<ClassLoader> sharedLibraries = createSharedLibrariesLoaders(
                      mApplicationInfo.sharedLibraryInfos, isBundledApp, librarySearchPath,
                      libraryPermittedPath);
  
              mDefaultClassLoader = ApplicationLoaders.getDefault().getClassLoaderWithSharedLibraries(
                      zip, mApplicationInfo.targetSdkVersion, isBundledApp, librarySearchPath,
                      libraryPermittedPath, mBaseClassLoader,
                      mApplicationInfo.classLoaderName, sharedLibraries);
              mAppComponentFactory = createAppFactory(mApplicationInfo, mDefaultClassLoader);
  
              setThreadPolicy(oldPolicy);
              // Setup the class loader paths for profiling.
              needToSetupJitProfiles = true;
          }
  
          if (!libPaths.isEmpty() && SystemProperties.getBoolean(PROPERTY_NAME_APPEND_NATIVE, true)) {
              // Temporarily disable logging of disk reads on the Looper thread as this is necessary
              StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
              try {
                  ApplicationLoaders.getDefault().addNative(mDefaultClassLoader, libPaths);
              } finally {
                  setThreadPolicy(oldPolicy);
              }
          }
  
          // /aepx/com.android.runtime/lib, /vendor/lib, /odm/lib and /product/lib
          // are added to the native lib search paths of the classloader.
          // Note that this is done AFTER the classloader is
          // created by ApplicationLoaders.getDefault().getClassLoader(...). The
          // reason is because if we have added the paths when creating the classloader
          // above, the paths are also added to the search path of the linker namespace
          // 'classloader-namespace', which will allow ALL libs in the paths to apps.
          // Since only the libs listed in <partition>/etc/public.libraries.txt can be
          // available to apps, we shouldn't add the paths then.
          //
          // However, we need to add the paths to the classloader (Java) though. This
          // is because when a native lib is requested via System.loadLibrary(), the
          // classloader first tries to find the requested lib in its own native libs
          // search paths. If a lib is not found in one of the paths, dlopen() is not
          // called at all. This can cause a problem that a vendor public native lib
          // is accessible when directly opened via dlopen(), but inaccesible via
          // System.loadLibrary(). In order to prevent the problem, we explicitly
          // add the paths only to the classloader, and not to the native loader
          // (linker namespace).
          List<String> extraLibPaths = new ArrayList<>(4);
          String abiSuffix = VMRuntime.getRuntime().is64Bit() ? "64" : "";
          if (!defaultSearchPaths.contains("/apex/com.android.runtime/lib")) {
              extraLibPaths.add("/apex/com.android.runtime/lib" + abiSuffix);
          }
          if (!defaultSearchPaths.contains("/vendor/lib")) {
              extraLibPaths.add("/vendor/lib" + abiSuffix);
          }
          if (!defaultSearchPaths.contains("/odm/lib")) {
              extraLibPaths.add("/odm/lib" + abiSuffix);
          }
          if (!defaultSearchPaths.contains("/product/lib")) {
              extraLibPaths.add("/product/lib" + abiSuffix);
          }
          if (!extraLibPaths.isEmpty()) {
              StrictMode.ThreadPolicy oldPolicy = allowThreadDiskReads();
              try {
                  ApplicationLoaders.getDefault().addNative(mDefaultClassLoader, extraLibPaths);
              } finally {
                  setThreadPolicy(oldPolicy);
              }
          }
  
          if (addedPaths != null && addedPaths.size() > 0) {
              final String add = TextUtils.join(File.pathSeparator, addedPaths);
              ApplicationLoaders.getDefault().addPath(mDefaultClassLoader, add);
              // Setup the new code paths for profiling.
              needToSetupJitProfiles = true;
          }
  
          // Setup jit profile support.
          //
          // It is ok to call this multiple times if the application gets updated with new splits.
          // The runtime only keeps track of unique code paths and can handle re-registration of
          // the same code path. There's no need to pass `addedPaths` since any new code paths
          // are already in `mApplicationInfo`.
          //
          // It is NOT ok to call this function from the system_server (for any of the packages it
          // loads code from) so we explicitly disallow it there.
          //
          // It is not ok to call this in a zygote context where mActivityThread is null.
          if (needToSetupJitProfiles && !ActivityThread.isSystem() && mActivityThread != null) {
              setupJitProfileSupport();
          }
  
          // Call AppComponentFactory to select/create the main class loader of this app.
          // Since this may call code in the app, mDefaultClassLoader must be fully set up
          // before invoking the factory.
          // Invoke with a copy of ApplicationInfo to protect against the app changing it.
          if (mClassLoader == null) {
              mClassLoader = mAppComponentFactory.instantiateClassLoader(mDefaultClassLoader,
                      new ApplicationInfo(mApplicationInfo));
          }
      }
  
      @UnsupportedAppUsage
      public ClassLoader getClassLoader() {
          synchronized (this) {
              if (mClassLoader == null) {
                  createOrUpdateClassLoaderLocked(null /*addedPaths*/);
              }
              return mClassLoader;
          }
      }
  
      private void setupJitProfileSupport() {
          if (!SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false)) {
              return;
          }
  
          // If we use profiles, setup the dex reporter to notify package manager
          // of any relevant dex loads. The idle maintenance job will use the information
          // reported to optimize the loaded dex files.
          // Note that we only need one global reporter per app.
          // Make sure we do this before invoking app code for the first time so that we
          // can capture the complete application startup.
          BaseDexClassLoader.setReporter(DexLoadReporter.getInstance());
  
          // Only set up profile support if the loaded apk has the same uid as the
          // current process.
          // Currently, we do not support profiling across different apps.
          // (e.g. application's uid might be different when the code is
          // loaded by another app via createApplicationContext)
          if (mApplicationInfo.uid != Process.myUid()) {
              return;
          }
  
          final List<String> codePaths = new ArrayList<>();
          if ((mApplicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) != 0) {
              codePaths.add(mApplicationInfo.sourceDir);
          }
          if (mApplicationInfo.splitSourceDirs != null) {
              Collections.addAll(codePaths, mApplicationInfo.splitSourceDirs);
          }
  
          if (codePaths.isEmpty()) {
              // If there are no code paths there's no need to setup a profile file and register with
              // the runtime,
              return;
          }
  
          for (int i = codePaths.size() - 1; i >= 0; i--) {
              String splitName = i == 0 ? null : mApplicationInfo.splitNames[i - 1];
              String profileFile = ArtManager.getCurrentProfilePath(
                      mPackageName, UserHandle.myUserId(), splitName);
              VMRuntime.registerAppInfo(profileFile, new String[] {codePaths.get(i)});
          }
  
          // Register the app data directory with the reporter. It will
          // help deciding whether or not a dex file is the primary apk or a
          // secondary dex.
          DexLoadReporter.getInstance().registerAppDataDir(mPackageName, mDataDir);
      }
  
      /**
       * Setup value for Thread.getContextClassLoader(). If the
       * package will not run in in a VM with other packages, we set
       * the Java context ClassLoader to the
       * PackageInfo.getClassLoader value. However, if this VM can
       * contain multiple packages, we intead set the Java context
       * ClassLoader to a proxy that will warn about the use of Java
       * context ClassLoaders and then fall through to use the
       * system ClassLoader.
       *
       * <p> Note that this is similar to but not the same as the
       * android.content.Context.getClassLoader(). While both
       * context class loaders are typically set to the
       * PathClassLoader used to load the package archive in the
       * single application per VM case, a single Android process
       * may contain several Contexts executing on one thread with
       * their own logical ClassLoaders while the Java context
       * ClassLoader is a thread local. This is why in the case when
       * we have multiple packages per VM we do not set the Java
       * context ClassLoader to an arbitrary but instead warn the
       * user to set their own if we detect that they are using a
       * Java library that expects it to be set.
       */
      private void initializeJavaContextClassLoader() {
          IPackageManager pm = ActivityThread.getPackageManager();
          android.content.pm.PackageInfo pi;
          try {
              pi = pm.getPackageInfo(mPackageName, PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
                      UserHandle.myUserId());
          } catch (RemoteException e) {
              throw e.rethrowFromSystemServer();
          }
          if (pi == null) {
              throw new IllegalStateException("Unable to get package info for "
                      + mPackageName + "; is package not installed?");
          }
          /*
           * Two possible indications that this package could be
           * sharing its virtual machine with other packages:
           *
           * 1.) the sharedUserId attribute is set in the manifest,
           *     indicating a request to share a VM with other
           *     packages with the same sharedUserId.
           *
           * 2.) the application element of the manifest has an
           *     attribute specifying a non-default process name,
           *     indicating the desire to run in another packages VM.
           */
          boolean sharedUserIdSet = (pi.sharedUserId != null);
          boolean processNameNotDefault =
              (pi.applicationInfo != null &&
               !mPackageName.equals(pi.applicationInfo.processName));
          boolean sharable = (sharedUserIdSet || processNameNotDefault);
          ClassLoader contextClassLoader =
              (sharable)
              ? new WarningContextClassLoader()
              : mClassLoader;
          Thread.currentThread().setContextClassLoader(contextClassLoader);
      }
  
      private static class WarningContextClassLoader extends ClassLoader {
  
          private static boolean warned = false;
  
          private void warn(String methodName) {
              if (warned) {
                  return;
              }
              warned = true;
              Thread.currentThread().setContextClassLoader(getParent());
              Slog.w(ActivityThread.TAG, "ClassLoader." + methodName + ": " +
                    "The class loader returned by " +
                    "Thread.getContextClassLoader() may fail for processes " +
                    "that host multiple applications. You should explicitly " +
                    "specify a context class loader. For example: " +
                    "Thread.setContextClassLoader(getClass().getClassLoader());");
          }
  
          @Override public URL getResource(String resName) {
              warn("getResource");
              return getParent().getResource(resName);
          }
  
          @Override public Enumeration<URL> getResources(String resName) throws IOException {
              warn("getResources");
              return getParent().getResources(resName);
          }
  
          @Override public InputStream getResourceAsStream(String resName) {
              warn("getResourceAsStream");
              return getParent().getResourceAsStream(resName);
          }
  
          @Override public Class<?> loadClass(String className) throws ClassNotFoundException {
              warn("loadClass");
              return getParent().loadClass(className);
          }
  
          @Override public void setClassAssertionStatus(String cname, boolean enable) {
              warn("setClassAssertionStatus");
              getParent().setClassAssertionStatus(cname, enable);
          }
  
          @Override public void setPackageAssertionStatus(String pname, boolean enable) {
              warn("setPackageAssertionStatus");
              getParent().setPackageAssertionStatus(pname, enable);
          }
  
          @Override public void setDefaultAssertionStatus(boolean enable) {
              warn("setDefaultAssertionStatus");
              getParent().setDefaultAssertionStatus(enable);
          }
  
          @Override public void clearAssertionStatus() {
              warn("clearAssertionStatus");
              getParent().clearAssertionStatus();
          }
      }
  
      @UnsupportedAppUsage
      public String getAppDir() {
          return mAppDir;
      }
  
      public String getLibDir() {
          return mLibDir;
      }
  
      @UnsupportedAppUsage
      public String getResDir() {
          return mResDir;
      }
  
      public String[] getSplitAppDirs() {
          return mSplitAppDirs;
      }
  
      @UnsupportedAppUsage
      public String[] getSplitResDirs() {
          return mSplitResDirs;
      }
  
      @UnsupportedAppUsage
      public String[] getOverlayDirs() {
          return mOverlayDirs;
      }
  
      public String getDataDir() {
          return mDataDir;
      }
  
      @UnsupportedAppUsage
      public File getDataDirFile() {
          return mDataDirFile;
      }
  
      public File getDeviceProtectedDataDirFile() {
          return mDeviceProtectedDataDirFile;
      }
  
      public File getCredentialProtectedDataDirFile() {
          return mCredentialProtectedDataDirFile;
      }
  
      @UnsupportedAppUsage
      public AssetManager getAssets() {
          return getResources().getAssets();
      }
  
      @UnsupportedAppUsage
      public Resources getResources() {
          if (mResources == null) {
              final String[] splitPaths;
              try {
                  splitPaths = getSplitPaths(null);
              } catch (NameNotFoundException e) {
                  // This should never fail.
                  throw new AssertionError("null split not found");
              }
  
              mResources = ResourcesManager.getInstance().getResources(null, mResDir,
                      splitPaths, mOverlayDirs, mApplicationInfo.sharedLibraryFiles,
                      Display.DEFAULT_DISPLAY, null, getCompatibilityInfo(),
                      getClassLoader());
          }
          return mResources;
      }
  
      @UnsupportedAppUsage
      public Application makeApplication(boolean forceDefaultAppClass,
              Instrumentation instrumentation) {
          if (mApplication != null) {
              return mApplication;
          }
  
          Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");
  
          Application app = null;
  
          String appClass = mApplicationInfo.className;
          if (forceDefaultAppClass || (appClass == null)) {
              appClass = "android.app.Application";
          }
  
          try {
              java.lang.ClassLoader cl = getClassLoader();
              if (!mPackageName.equals("android")) {
                  Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
                          "initializeJavaContextClassLoader");
                  initializeJavaContextClassLoader();
                  Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
              }
              ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
              app = mActivityThread.mInstrumentation.newApplication(
                      cl, appClass, appContext);
              appContext.setOuterContext(app);
          } catch (Exception e) {
              if (!mActivityThread.mInstrumentation.onException(app, e)) {
                  Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                  throw new RuntimeException(
                      "Unable to instantiate application " + appClass
                      + ": " + e.toString(), e);
              }
          }
          mActivityThread.mAllApplications.add(app);
          mApplication = app;
  
          if (instrumentation != null) {
              try {
                  instrumentation.callApplicationOnCreate(app);
              } catch (Exception e) {
                  if (!instrumentation.onException(app, e)) {
                      Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                      throw new RuntimeException(
                          "Unable to create application " + app.getClass().getName()
                          + ": " + e.toString(), e);
                  }
              }
          }
  
          // Rewrite the R 'constants' for all library apks.
          SparseArray<String> packageIdentifiers = getAssets().getAssignedPackageIdentifiers();
          final int N = packageIdentifiers.size();
          for (int i = 0; i < N; i++) {
              final int id = packageIdentifiers.keyAt(i);
              if (id == 0x01 || id == 0x7f) {
                  continue;
              }
  
              rewriteRValues(getClassLoader(), packageIdentifiers.valueAt(i), id);
          }
  
          Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
  
          return app;
      }
  
      @UnsupportedAppUsage
      private void rewriteRValues(ClassLoader cl, String packageName, int id) {
          final Class<?> rClazz;
          try {
              rClazz = cl.loadClass(packageName + ".R");
          } catch (ClassNotFoundException e) {
              // This is not necessarily an error, as some packages do not ship with resources
              // (or they do not need rewriting).
              Log.i(TAG, "No resource references to update in package " + packageName);
              return;
          }
  
          final Method callback;
          try {
              callback = rClazz.getMethod("onResourcesLoaded", int.class);
          } catch (NoSuchMethodException e) {
              // No rewriting to be done.
              return;
          }
  
          Throwable cause;
          try {
              callback.invoke(null, id);
              return;
          } catch (IllegalAccessException e) {
              cause = e;
          } catch (InvocationTargetException e) {
              cause = e.getCause();
          }
  
          throw new RuntimeException("Failed to rewrite resource references for " + packageName,
                  cause);
      }
  
      public void removeContextRegistrations(Context context,
              String who, String what) {
          final boolean reportRegistrationLeaks = StrictMode.vmRegistrationLeaksEnabled();
          synchronized (mReceivers) {
              ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> rmap =
                      mReceivers.remove(context);
              if (rmap != null) {
                  for (int i = 0; i < rmap.size(); i++) {
                      LoadedApk.ReceiverDispatcher rd = rmap.valueAt(i);
                      IntentReceiverLeaked leak = new IntentReceiverLeaked(
                              what + " " + who + " has leaked IntentReceiver "
                              + rd.getIntentReceiver() + " that was " +
                              "originally registered here. Are you missing a " +
                              "call to unregisterReceiver()?");
                      leak.setStackTrace(rd.getLocation().getStackTrace());
                      Slog.e(ActivityThread.TAG, leak.getMessage(), leak);
                      if (reportRegistrationLeaks) {
                          StrictMode.onIntentReceiverLeaked(leak);
                      }
                      try {
                          ActivityManager.getService().unregisterReceiver(
                                  rd.getIIntentReceiver());
                      } catch (RemoteException e) {
                          throw e.rethrowFromSystemServer();
                      }
                  }
              }
              mUnregisteredReceivers.remove(context);
          }
  
          synchronized (mServices) {
              //Slog.i(TAG, "Receiver registrations: " + mReceivers);
              ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> smap =
                      mServices.remove(context);
              if (smap != null) {
                  for (int i = 0; i < smap.size(); i++) {
                      LoadedApk.ServiceDispatcher sd = smap.valueAt(i);
                      ServiceConnectionLeaked leak = new ServiceConnectionLeaked(
                              what + " " + who + " has leaked ServiceConnection "
                              + sd.getServiceConnection() + " that was originally bound here");
                      leak.setStackTrace(sd.getLocation().getStackTrace());
                      Slog.e(ActivityThread.TAG, leak.getMessage(), leak);
                      if (reportRegistrationLeaks) {
                          StrictMode.onServiceConnectionLeaked(leak);
                      }
                      try {
                          ActivityManager.getService().unbindService(
                                  sd.getIServiceConnection());
                      } catch (RemoteException e) {
                          throw e.rethrowFromSystemServer();
                      }
                      sd.doForget();
                  }
              }
              mUnboundServices.remove(context);
              //Slog.i(TAG, "Service registrations: " + mServices);
          }
      }
  
      public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
              Context context, Handler handler,
              Instrumentation instrumentation, boolean registered) {
          synchronized (mReceivers) {
              LoadedApk.ReceiverDispatcher rd = null;
              ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;
              if (registered) {
                  map = mReceivers.get(context);
                  if (map != null) {
                      rd = map.get(r);
                  }
              }
              if (rd == null) {
                  rd = new ReceiverDispatcher(r, context, handler,
                          instrumentation, registered);
                  if (registered) {
                      if (map == null) {
                          map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
                          mReceivers.put(context, map);
                      }
                      map.put(r, rd);
                  }
              } else {
                  rd.validate(context, handler);
              }
              rd.mForgotten = false;
              return rd.getIIntentReceiver();
          }
      }
  
      public IIntentReceiver forgetReceiverDispatcher(Context context,
              BroadcastReceiver r) {
          synchronized (mReceivers) {
              ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = mReceivers.get(context);
              LoadedApk.ReceiverDispatcher rd = null;
              if (map != null) {
                  rd = map.get(r);
                  if (rd != null) {
                      map.remove(r);
                      if (map.size() == 0) {
                          mReceivers.remove(context);
                      }
                      if (r.getDebugUnregister()) {
                          ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> holder
                                  = mUnregisteredReceivers.get(context);
                          if (holder == null) {
                              holder = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
                              mUnregisteredReceivers.put(context, holder);
                          }
                          RuntimeException ex = new IllegalArgumentException(
                                  "Originally unregistered here:");
                          ex.fillInStackTrace();
                          rd.setUnregisterLocation(ex);
                          holder.put(r, rd);
                      }
                      rd.mForgotten = true;
                      return rd.getIIntentReceiver();
                  }
              }
              ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> holder
                      = mUnregisteredReceivers.get(context);
              if (holder != null) {
                  rd = holder.get(r);
                  if (rd != null) {
                      RuntimeException ex = rd.getUnregisterLocation();
                      throw new IllegalArgumentException(
                              "Unregistering Receiver " + r
                              + " that was already unregistered", ex);
                  }
              }
              if (context == null) {
                  throw new IllegalStateException("Unbinding Receiver " + r
                          + " from Context that is no longer in use: " + context);
              } else {
                  throw new IllegalArgumentException("Receiver not registered: " + r);
              }
  
          }
      }
  
      static final class ReceiverDispatcher {
  
          final static class InnerReceiver extends IIntentReceiver.Stub {
              final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;
              final LoadedApk.ReceiverDispatcher mStrongRef;
  
              InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {
                  mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);
                  mStrongRef = strong ? rd : null;
              }
  
              @Override
              public void performReceive(Intent intent, int resultCode, String data,
                      Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
                  final LoadedApk.ReceiverDispatcher rd;
                  if (intent == null) {
                      Log.wtf(TAG, "Null intent received");
                      rd = null;
                  } else {
                      rd = mDispatcher.get();
                  }
                  if (ActivityThread.DEBUG_BROADCAST) {
                      int seq = intent.getIntExtra("seq", -1);
                      Slog.i(ActivityThread.TAG, "Receiving broadcast " + intent.getAction()
                              + " seq=" + seq + " to " + (rd != null ? rd.mReceiver : null));
                  }
                  if (rd != null) {
                      rd.performReceive(intent, resultCode, data, extras,
                              ordered, sticky, sendingUser);
                  } else {
                      // The activity manager dispatched a broadcast to a registered
                      // receiver in this process, but before it could be delivered the
                      // receiver was unregistered.  Acknowledge the broadcast on its
                      // behalf so that the system's broadcast sequence can continue.
                      if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                              "Finishing broadcast to unregistered receiver");
                      IActivityManager mgr = ActivityManager.getService();
                      try {
                          if (extras != null) {
                              extras.setAllowFds(false);
                          }
                          mgr.finishReceiver(this, resultCode, data, extras, false, intent.getFlags());
                      } catch (RemoteException e) {
                          throw e.rethrowFromSystemServer();
                      }
                  }
              }
          }
  
          final IIntentReceiver.Stub mIIntentReceiver;
          @UnsupportedAppUsage
          final BroadcastReceiver mReceiver;
          @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
          final Context mContext;
          final Handler mActivityThread;
          final Instrumentation mInstrumentation;
          final boolean mRegistered;
          final IntentReceiverLeaked mLocation;
          RuntimeException mUnregisterLocation;
          boolean mForgotten;
  
          final class Args extends BroadcastReceiver.PendingResult {
              private Intent mCurIntent;
              private final boolean mOrdered;
              private boolean mDispatched;
              private boolean mRunCalled;
  
              public Args(Intent intent, int resultCode, String resultData, Bundle resultExtras,
                      boolean ordered, boolean sticky, int sendingUser) {
                  super(resultCode, resultData, resultExtras,
                          mRegistered ? TYPE_REGISTERED : TYPE_UNREGISTERED, ordered,
                          sticky, mIIntentReceiver.asBinder(), sendingUser, intent.getFlags());
                  mCurIntent = intent;
                  mOrdered = ordered;
              }
  
              public final Runnable getRunnable() {
                  return () -> {
                      final BroadcastReceiver receiver = mReceiver;
                      final boolean ordered = mOrdered;
  
                      if (ActivityThread.DEBUG_BROADCAST) {
                          int seq = mCurIntent.getIntExtra("seq", -1);
                          Slog.i(ActivityThread.TAG, "Dispatching broadcast " + mCurIntent.getAction()
                                  + " seq=" + seq + " to " + mReceiver);
                          Slog.i(ActivityThread.TAG, "  mRegistered=" + mRegistered
                                  + " mOrderedHint=" + ordered);
                      }
  
                      final IActivityManager mgr = ActivityManager.getService();
                      final Intent intent = mCurIntent;
                      if (intent == null) {
                          Log.wtf(TAG, "Null intent being dispatched, mDispatched=" + mDispatched
                                  + (mRunCalled ? ", run() has already been called" : ""));
                      }
  
                      mCurIntent = null;
                      mDispatched = true;
                      mRunCalled = true;
                      if (receiver == null || intent == null || mForgotten) {
                          if (mRegistered && ordered) {
                              if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                                      "Finishing null broadcast to " + mReceiver);
                              sendFinished(mgr);
                          }
                          return;
                      }
  
                      Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "broadcastReceiveReg");
                      try {
                          ClassLoader cl = mReceiver.getClass().getClassLoader();
                          intent.setExtrasClassLoader(cl);
                          intent.prepareToEnterProcess();
                          setExtrasClassLoader(cl);
                          receiver.setPendingResult(this);
                          receiver.onReceive(mContext, intent);
                      } catch (Exception e) {
                          if (mRegistered && ordered) {
                              if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                                      "Finishing failed broadcast to " + mReceiver);
                              sendFinished(mgr);
                          }
                          if (mInstrumentation == null ||
                                  !mInstrumentation.onException(mReceiver, e)) {
                              Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                              throw new RuntimeException(
                                      "Error receiving broadcast " + intent
                                              + " in " + mReceiver, e);
                          }
                      }
  
                      if (receiver.getPendingResult() != null) {
                          finish();
                      }
                      Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                  };
              }
          }
  
          ReceiverDispatcher(BroadcastReceiver receiver, Context context,
                  Handler activityThread, Instrumentation instrumentation,
                  boolean registered) {
              if (activityThread == null) {
                  throw new NullPointerException("Handler must not be null");
              }
  
              mIIntentReceiver = new InnerReceiver(this, !registered);
              mReceiver = receiver;
              mContext = context;
              mActivityThread = activityThread;
              mInstrumentation = instrumentation;
              mRegistered = registered;
              mLocation = new IntentReceiverLeaked(null);
              mLocation.fillInStackTrace();
          }
  
          void validate(Context context, Handler activityThread) {
              if (mContext != context) {
                  throw new IllegalStateException(
                      "Receiver " + mReceiver +
                      " registered with differing Context (was " +
                      mContext + " now " + context + ")");
              }
              if (mActivityThread != activityThread) {
                  throw new IllegalStateException(
                      "Receiver " + mReceiver +
                      " registered with differing handler (was " +
                      mActivityThread + " now " + activityThread + ")");
              }
          }
  
          IntentReceiverLeaked getLocation() {
              return mLocation;
          }
  
          @UnsupportedAppUsage
          BroadcastReceiver getIntentReceiver() {
              return mReceiver;
          }
  
          @UnsupportedAppUsage
          IIntentReceiver getIIntentReceiver() {
              return mIIntentReceiver;
          }
  
          void setUnregisterLocation(RuntimeException ex) {
              mUnregisterLocation = ex;
          }
  
          RuntimeException getUnregisterLocation() {
              return mUnregisterLocation;
          }
  
          public void performReceive(Intent intent, int resultCode, String data,
                  Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
              final Args args = new Args(intent, resultCode, data, extras, ordered,
                      sticky, sendingUser);
              if (intent == null) {
                  Log.wtf(TAG, "Null intent received");
              } else {
                  if (ActivityThread.DEBUG_BROADCAST) {
                      int seq = intent.getIntExtra("seq", -1);
                      Slog.i(ActivityThread.TAG, "Enqueueing broadcast " + intent.getAction()
                              + " seq=" + seq + " to " + mReceiver);
                  }
              }
              if (intent == null || !mActivityThread.post(args.getRunnable())) {
                  if (mRegistered && ordered) {
                      IActivityManager mgr = ActivityManager.getService();
                      if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                              "Finishing sync broadcast to " + mReceiver);
                      args.sendFinished(mgr);
                  }
              }
          }
  
      }
  
      @UnsupportedAppUsage
      public final IServiceConnection getServiceDispatcher(ServiceConnection c,
              Context context, Handler handler, int flags) {
          return getServiceDispatcherCommon(c, context, handler, null, flags);
      }
  
      public final IServiceConnection getServiceDispatcher(ServiceConnection c,
              Context context, Executor executor, int flags) {
          return getServiceDispatcherCommon(c, context, null, executor, flags);
      }
  
      private IServiceConnection getServiceDispatcherCommon(ServiceConnection c,
              Context context, Handler handler, Executor executor, int flags) {
          synchronized (mServices) {
              LoadedApk.ServiceDispatcher sd = null;
              ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
              if (map != null) {
                  if (DEBUG) Slog.d(TAG, "Returning existing dispatcher " + sd + " for conn " + c);
                  sd = map.get(c);
              }
              if (sd == null) {
                  if (executor != null) {
                      sd = new ServiceDispatcher(c, context, executor, flags);
                  } else {
                      sd = new ServiceDispatcher(c, context, handler, flags);
                  }
                  if (DEBUG) Slog.d(TAG, "Creating new dispatcher " + sd + " for conn " + c);
                  if (map == null) {
                      map = new ArrayMap<>();
                      mServices.put(context, map);
                  }
                  map.put(c, sd);
              } else {
                  sd.validate(context, handler, executor);
              }
              return sd.getIServiceConnection();
          }
      }
  
      @UnsupportedAppUsage
      public IServiceConnection lookupServiceDispatcher(ServiceConnection c,
              Context context) {
          synchronized (mServices) {
              LoadedApk.ServiceDispatcher sd = null;
              ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map = mServices.get(context);
              if (map != null) {
                  sd = map.get(c);
              }
              return sd != null ? sd.getIServiceConnection() : null;
          }
      }
  
      public final IServiceConnection forgetServiceDispatcher(Context context,
              ServiceConnection c) {
          synchronized (mServices) {
              ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> map
                      = mServices.get(context);
              LoadedApk.ServiceDispatcher sd = null;
              if (map != null) {
                  sd = map.get(c);
                  if (sd != null) {
                      if (DEBUG) Slog.d(TAG, "Removing dispatcher " + sd + " for conn " + c);
                      map.remove(c);
                      sd.doForget();
                      if (map.size() == 0) {
                          mServices.remove(context);
                      }
                      if ((sd.getFlags()&Context.BIND_DEBUG_UNBIND) != 0) {
                          ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> holder
                                  = mUnboundServices.get(context);
                          if (holder == null) {
                              holder = new ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>();
                              mUnboundServices.put(context, holder);
                          }
                          RuntimeException ex = new IllegalArgumentException(
                                  "Originally unbound here:");
                          ex.fillInStackTrace();
                          sd.setUnbindLocation(ex);
                          holder.put(c, sd);
                      }
                      return sd.getIServiceConnection();
                  }
              }
              ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> holder
                      = mUnboundServices.get(context);
              if (holder != null) {
                  sd = holder.get(c);
                  if (sd != null) {
                      RuntimeException ex = sd.getUnbindLocation();
                      throw new IllegalArgumentException(
                              "Unbinding Service " + c
                              + " that was already unbound", ex);
                  }
              }
              if (context == null) {
                  throw new IllegalStateException("Unbinding Service " + c
                          + " from Context that is no longer in use: " + context);
              } else {
                  throw new IllegalArgumentException("Service not registered: " + c);
              }
          }
      }
  
      static final class ServiceDispatcher {
          private final ServiceDispatcher.InnerConnection mIServiceConnection;
          @UnsupportedAppUsage
          private final ServiceConnection mConnection;
          @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
          private final Context mContext;
          private final Handler mActivityThread;
          private final Executor mActivityExecutor;
          private final ServiceConnectionLeaked mLocation;
          private final int mFlags;
  
          private RuntimeException mUnbindLocation;
  
          private boolean mForgotten;
  
          private static class ConnectionInfo {
              IBinder binder;
              IBinder.DeathRecipient deathMonitor;
          }
  
          private static class InnerConnection extends IServiceConnection.Stub {
              @UnsupportedAppUsage
              final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;
  
              InnerConnection(LoadedApk.ServiceDispatcher sd) {
                  mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
              }
  
              public void connected(ComponentName name, IBinder service, boolean dead)
                      throws RemoteException {
                  LoadedApk.ServiceDispatcher sd = mDispatcher.get();
                  if (sd != null) {
                      sd.connected(name, service, dead);
                  }
              }
          }
  
          private final ArrayMap<ComponentName, ServiceDispatcher.ConnectionInfo> mActiveConnections
              = new ArrayMap<ComponentName, ServiceDispatcher.ConnectionInfo>();
  
          @UnsupportedAppUsage
          ServiceDispatcher(ServiceConnection conn,
                  Context context, Handler activityThread, int flags) {
              mIServiceConnection = new InnerConnection(this);
              mConnection = conn;
              mContext = context;
              mActivityThread = activityThread;
              mActivityExecutor = null;
              mLocation = new ServiceConnectionLeaked(null);
              mLocation.fillInStackTrace();
              mFlags = flags;
          }
  
          ServiceDispatcher(ServiceConnection conn,
                  Context context, Executor activityExecutor, int flags) {
              mIServiceConnection = new InnerConnection(this);
              mConnection = conn;
              mContext = context;
              mActivityThread = null;
              mActivityExecutor = activityExecutor;
              mLocation = new ServiceConnectionLeaked(null);
              mLocation.fillInStackTrace();
              mFlags = flags;
          }
  
          void validate(Context context, Handler activityThread, Executor activityExecutor) {
              if (mContext != context) {
                  throw new RuntimeException(
                      "ServiceConnection " + mConnection +
                      " registered with differing Context (was " +
                      mContext + " now " + context + ")");
              }
              if (mActivityThread != activityThread) {
                  throw new RuntimeException(
                      "ServiceConnection " + mConnection +
                      " registered with differing handler (was " +
                      mActivityThread + " now " + activityThread + ")");
              }
              if (mActivityExecutor != activityExecutor) {
                  throw new RuntimeException(
                      "ServiceConnection " + mConnection +
                      " registered with differing executor (was " +
                      mActivityExecutor + " now " + activityExecutor + ")");
              }
          }
  
          void doForget() {
              synchronized(this) {
                  for (int i=0; i<mActiveConnections.size(); i++) {
                      ServiceDispatcher.ConnectionInfo ci = mActiveConnections.valueAt(i);
                      ci.binder.unlinkToDeath(ci.deathMonitor, 0);
                  }
                  mActiveConnections.clear();
                  mForgotten = true;
              }
          }
  
          ServiceConnectionLeaked getLocation() {
              return mLocation;
          }
  
          ServiceConnection getServiceConnection() {
              return mConnection;
          }
  
          @UnsupportedAppUsage
          IServiceConnection getIServiceConnection() {
              return mIServiceConnection;
          }
  
          int getFlags() {
              return mFlags;
          }
  
          void setUnbindLocation(RuntimeException ex) {
              mUnbindLocation = ex;
          }
  
          RuntimeException getUnbindLocation() {
              return mUnbindLocation;
          }
  
          public void connected(ComponentName name, IBinder service, boolean dead) {
              if (mActivityExecutor != null) {
                  mActivityExecutor.execute(new RunConnection(name, service, 0, dead));
              } else if (mActivityThread != null) {
                  mActivityThread.post(new RunConnection(name, service, 0, dead));
              } else {
                  doConnected(name, service, dead);
              }
          }
  
          public void death(ComponentName name, IBinder service) {
              if (mActivityExecutor != null) {
                  mActivityExecutor.execute(new RunConnection(name, service, 1, false));
              } else if (mActivityThread != null) {
                  mActivityThread.post(new RunConnection(name, service, 1, false));
              } else {
                  doDeath(name, service);
              }
          }
  
          public void doConnected(ComponentName name, IBinder service, boolean dead) {
              ServiceDispatcher.ConnectionInfo old;
              ServiceDispatcher.ConnectionInfo info;
  
              synchronized (this) {
                  if (mForgotten) {
                      // We unbound before receiving the connection; ignore
                      // any connection received.
                      return;
                  }
                  old = mActiveConnections.get(name);
                  if (old != null && old.binder == service) {
                      // Huh, already have this one.  Oh well!
                      return;
                  }
  
                  if (service != null) {
                      // A new service is being connected... set it all up.
                      info = new ConnectionInfo();
                      info.binder = service;
                      info.deathMonitor = new DeathMonitor(name, service);
                      try {
                          service.linkToDeath(info.deathMonitor, 0);
                          mActiveConnections.put(name, info);
                      } catch (RemoteException e) {
                          // This service was dead before we got it...  just
                          // don't do anything with it.
                          mActiveConnections.remove(name);
                          return;
                      }
  
                  } else {
                      // The named service is being disconnected... clean up.
                      mActiveConnections.remove(name);
                  }
  
                  if (old != null) {
                      old.binder.unlinkToDeath(old.deathMonitor, 0);
                  }
              }
  
              // If there was an old service, it is now disconnected.
              if (old != null) {
                  mConnection.onServiceDisconnected(name);
              }
              if (dead) {
                  mConnection.onBindingDied(name);
              }
              // If there is a new viable service, it is now connected.
              if (service != null) {
                  mConnection.onServiceConnected(name, service);
              } else {
                  // The binding machinery worked, but the remote returned null from onBind().
                  mConnection.onNullBinding(name);
              }
          }
  
          public void doDeath(ComponentName name, IBinder service) {
              synchronized (this) {
                  ConnectionInfo old = mActiveConnections.get(name);
                  if (old == null || old.binder != service) {
                      // Death for someone different than who we last
                      // reported...  just ignore it.
                      return;
                  }
                  mActiveConnections.remove(name);
                  old.binder.unlinkToDeath(old.deathMonitor, 0);
              }
  
              mConnection.onServiceDisconnected(name);
          }
  
          private final class RunConnection implements Runnable {
              RunConnection(ComponentName name, IBinder service, int command, boolean dead) {
                  mName = name;
                  mService = service;
                  mCommand = command;
                  mDead = dead;
              }
  
              public void run() {
                  if (mCommand == 0) {
                      doConnected(mName, mService, mDead);
                  } else if (mCommand == 1) {
                      doDeath(mName, mService);
                  }
              }
  
              final ComponentName mName;
              final IBinder mService;
              final int mCommand;
              final boolean mDead;
          }
  
          private final class DeathMonitor implements IBinder.DeathRecipient
          {
              DeathMonitor(ComponentName name, IBinder service) {
                  mName = name;
                  mService = service;
              }
  
              public void binderDied() {
                  death(mName, mService);
              }
  
              final ComponentName mName;
              final IBinder mService;
          }
      }
  }
  