Add SDK 29 sources.
Test: N/A
Change-Id: Iedb7a31029e003928eb16f7e69ed147e72bb6235
diff --git a/android/app/LoadedApk.java b/android/app/LoadedApk.java
new file mode 100644
index 0000000..1ac619c
--- /dev/null
+++ b/android/app/LoadedApk.java
@@ -0,0 +1,2007 @@
+/*
+ * 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;
+ }
+ }
+}