diff options
14 files changed, 189 insertions, 163 deletions
diff --git a/api/system-current.txt b/api/system-current.txt index 6708b47f2821..202d51ff9d92 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -7179,6 +7179,7 @@ package android.provider { field public static final String ACTION_REQUEST_ENABLE_CONTENT_CAPTURE = "android.settings.REQUEST_ENABLE_CONTENT_CAPTURE"; field public static final String ACTION_SHOW_ADMIN_SUPPORT_DETAILS = "android.settings.SHOW_ADMIN_SUPPORT_DETAILS"; field public static final String ACTION_TETHER_PROVISIONING_UI = "android.settings.TETHER_PROVISIONING_UI"; + field public static final String ACTION_TETHER_SETTINGS = "android.settings.TETHER_SETTINGS"; } public static final class Settings.Global extends android.provider.Settings.NameValueTable { diff --git a/core/java/android/app/DexLoadReporter.java b/core/java/android/app/DexLoadReporter.java index 229bee55e911..5bc999240a66 100644 --- a/core/java/android/app/DexLoadReporter.java +++ b/core/java/android/app/DexLoadReporter.java @@ -28,9 +28,8 @@ import dalvik.system.VMRuntime; import java.io.File; import java.io.IOException; -import java.util.ArrayList; import java.util.HashSet; -import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -87,50 +86,32 @@ import java.util.Set; } @Override - public void report(List<ClassLoader> classLoadersChain, List<String> classPaths) { - if (classLoadersChain.size() != classPaths.size()) { - Slog.wtf(TAG, "Bad call to DexLoadReporter: argument size mismatch"); - return; - } - if (classPaths.isEmpty()) { - Slog.wtf(TAG, "Bad call to DexLoadReporter: empty dex paths"); - return; - } - - // The first element of classPaths is the list of dex files that should be registered. - // The classpath is represented as a list of dex files separated by File.pathSeparator. - String[] dexPathsForRegistration = classPaths.get(0).split(File.pathSeparator); - if (dexPathsForRegistration.length == 0) { - // No dex files to register. + public void report(Map<String, String> classLoaderContextMap) { + if (classLoaderContextMap.isEmpty()) { + Slog.wtf(TAG, "Bad call to DexLoadReporter: empty classLoaderContextMap"); return; } // Notify the package manager about the dex loads unconditionally. // The load might be for either a primary or secondary dex file. - notifyPackageManager(classLoadersChain, classPaths); + notifyPackageManager(classLoaderContextMap); // Check for secondary dex files and register them for profiling if possible. // Note that we only register the dex paths belonging to the first class loader. - registerSecondaryDexForProfiling(dexPathsForRegistration); + registerSecondaryDexForProfiling(classLoaderContextMap.keySet()); } - private void notifyPackageManager(List<ClassLoader> classLoadersChain, - List<String> classPaths) { + private void notifyPackageManager(Map<String, String> classLoaderContextMap) { // Get the class loader names for the binder call. - List<String> classLoadersNames = new ArrayList<>(classPaths.size()); - for (ClassLoader classLoader : classLoadersChain) { - classLoadersNames.add(classLoader.getClass().getName()); - } String packageName = ActivityThread.currentPackageName(); try { - ActivityThread.getPackageManager().notifyDexLoad( - packageName, classLoadersNames, classPaths, - VMRuntime.getRuntime().vmInstructionSet()); + ActivityThread.getPackageManager().notifyDexLoad(packageName, + classLoaderContextMap, VMRuntime.getRuntime().vmInstructionSet()); } catch (RemoteException re) { Slog.e(TAG, "Failed to notify PM about dex load for package " + packageName, re); } } - private void registerSecondaryDexForProfiling(String[] dexPaths) { + private void registerSecondaryDexForProfiling(Set<String> dexPaths) { if (!SystemProperties.getBoolean("dalvik.vm.dexopt.secondary", false)) { return; } diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 429a6e51ccb4..6d051e47c716 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -529,19 +529,12 @@ interface IPackageManager { * Notify the package manager that a list of dex files have been loaded. * * @param loadingPackageName the name of the package who performs the load - * @param classLoadersNames the names of the class loaders present in the loading chain. The - * list encodes the class loader chain in the natural order. The first class loader has - * the second one as its parent and so on. The dex files present in the class path of the - * first class loader will be recorded in the usage file. - * @param classPaths the class paths corresponding to the class loaders names from - * {@param classLoadersNames}. The the first element corresponds to the first class loader - * and so on. A classpath is represented as a list of dex files separated by - * {@code File.pathSeparator}, or null if the class loader's classpath is not known. - * The dex files found in the first class path will be recorded in the usage file. + * @param classLoaderContextMap a map from file paths to dex files that have been loaded to + * the class loader context that was used to load them. * @param loaderIsa the ISA of the loader process */ - oneway void notifyDexLoad(String loadingPackageName, in List<String> classLoadersNames, - in List<String> classPaths, String loaderIsa); + oneway void notifyDexLoad(String loadingPackageName, + in Map<String, String> classLoaderContextMap, String loaderIsa); /** * Register an application dex module with the package manager. diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index b22db2e24624..3842def8751d 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1793,6 +1793,19 @@ public final class Settings { public static final String ACTION_MANAGE_DOMAIN_URLS = "android.settings.MANAGE_DOMAIN_URLS"; /** + * Activity Action: Show screen that let user select enable (or disable) tethering. + * <p> + * Input: Nothing. + * <p> + * Output: Nothing + * + * @hide + */ + @SystemApi + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_TETHER_SETTINGS = "android.settings.TETHER_SETTINGS"; + + /** * Broadcast to trigger notification of asking user to enable MMS. * Need to specify {@link #EXTRA_ENABLE_MMS_DATA_REQUEST_REASON} and {@link #EXTRA_SUB_ID}. * diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java index bcb6c0f9ae87..72eb32a05da9 100644 --- a/core/java/com/android/internal/os/Zygote.java +++ b/core/java/com/android/internal/os/Zygote.java @@ -24,7 +24,6 @@ import android.content.pm.ApplicationInfo; import android.net.Credentials; import android.net.LocalServerSocket; import android.net.LocalSocket; -import android.os.Build; import android.os.FactoryTest; import android.os.IVold; import android.os.Process; @@ -254,16 +253,13 @@ public final class Zygote { */ public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose, - int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir, - int targetSdkVersion) { + int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir) { ZygoteHooks.preFork(); int pid = nativeForkAndSpecialize( uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose, fdsToIgnore, startChildZygote, instructionSet, appDataDir); if (pid == 0) { - Zygote.disableExecuteOnly(targetSdkVersion); - // Note that this event ends at the end of handleChildProc, Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork"); } @@ -649,8 +645,6 @@ public final class Zygote { args.mSeInfo, args.mNiceName, args.mStartChildZygote, args.mInstructionSet, args.mAppDataDir); - disableExecuteOnly(args.mTargetSdkVersion); - Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER); return ZygoteInit.zygoteInit(args.mTargetSdkVersion, @@ -730,17 +724,6 @@ public final class Zygote { } /** - * Mark execute-only segments of libraries read+execute for apps with targetSdkVersion<Q. - */ - protected static void disableExecuteOnly(int targetSdkVersion) { - if ((targetSdkVersion < Build.VERSION_CODES.Q) && !nativeDisableExecuteOnly()) { - Log.e("Zygote", "Failed to set libraries to read+execute."); - } - } - - private static native boolean nativeDisableExecuteOnly(); - - /** * @return Raw file descriptors for the read-end of USAP reporting pipes. */ protected static int[] getUsapPipeFDs() { diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java index 2666d5278a90..24ea59ae6ac0 100644 --- a/core/java/com/android/internal/os/ZygoteConnection.java +++ b/core/java/com/android/internal/os/ZygoteConnection.java @@ -257,7 +257,7 @@ class ZygoteConnection { pid = Zygote.forkAndSpecialize(parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids, parsedArgs.mRuntimeFlags, rlimits, parsedArgs.mMountExternal, parsedArgs.mSeInfo, parsedArgs.mNiceName, fdsToClose, fdsToIgnore, parsedArgs.mStartChildZygote, - parsedArgs.mInstructionSet, parsedArgs.mAppDataDir, parsedArgs.mTargetSdkVersion); + parsedArgs.mInstructionSet, parsedArgs.mAppDataDir); try { if (pid == 0) { diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java index 1b81a06f8b9a..decc92cfc0c4 100644 --- a/core/java/com/android/internal/os/ZygoteInit.java +++ b/core/java/com/android/internal/os/ZygoteInit.java @@ -647,17 +647,18 @@ public class ZygoteInit { String classPathForElement = ""; boolean compiledSomething = false; for (String classPathElement : classPathElements) { - // System server is fully AOTed and never profiled - // for profile guided compilation. + // We default to the verify filter because the compilation will happen on /data and + // system server cannot load executable code outside /system. String systemServerFilter = SystemProperties.get( - "dalvik.vm.systemservercompilerfilter", "speed"); + "dalvik.vm.systemservercompilerfilter", "verify"); + String classLoaderContext = + getSystemServerClassLoaderContext(classPathForElement); int dexoptNeeded; try { dexoptNeeded = DexFile.getDexOptNeeded( classPathElement, instructionSet, systemServerFilter, - null /* classLoaderContext */, false /* newProfile */, - false /* downgrade */); + classLoaderContext, false /* newProfile */, false /* downgrade */); } catch (FileNotFoundException ignored) { // Do not add to the classpath. Log.w(TAG, "Missing classpath element for system server: " + classPathElement); @@ -678,8 +679,6 @@ public class ZygoteInit { final String compilerFilter = systemServerFilter; final String uuid = StorageManager.UUID_PRIVATE_INTERNAL; final String seInfo = null; - final String classLoaderContext = - getSystemServerClassLoaderContext(classPathForElement); final int targetSdkVersion = 0; // SystemServer targets the system's SDK version try { installd.dexopt(classPathElement, Process.SYSTEM_UID, packageName, diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index 7a93d8db0931..7e4a16dc6dcc 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -46,7 +46,6 @@ #include <fcntl.h> #include <grp.h> #include <inttypes.h> -#include <link.h> #include <malloc.h> #include <mntent.h> #include <paths.h> @@ -55,7 +54,6 @@ #include <sys/capability.h> #include <sys/cdefs.h> #include <sys/eventfd.h> -#include <sys/mman.h> #include <sys/personality.h> #include <sys/prctl.h> #include <sys/resource.h> @@ -72,10 +70,8 @@ #include <android-base/properties.h> #include <android-base/file.h> #include <android-base/stringprintf.h> -#include <android-base/strings.h> #include <android-base/unique_fd.h> #include <bionic/malloc.h> -#include <bionic/page.h> #include <cutils/fs.h> #include <cutils/multiuser.h> #include <private/android_filesystem_config.h> @@ -1783,31 +1779,6 @@ static void com_android_internal_os_Zygote_nativeEmptyUsapPool(JNIEnv* env, jcla } } -static int disable_execute_only(struct dl_phdr_info *info, size_t size, void *data) { - // Search for any execute-only segments and mark them read+execute. - for (int i = 0; i < info->dlpi_phnum; i++) { - const auto& phdr = info->dlpi_phdr[i]; - if ((phdr.p_type == PT_LOAD) && (phdr.p_flags == PF_X)) { - auto addr = reinterpret_cast<void*>(info->dlpi_addr + PAGE_START(phdr.p_vaddr)); - size_t len = PAGE_OFFSET(phdr.p_vaddr) + phdr.p_memsz; - if (mprotect(addr, len, PROT_READ | PROT_EXEC) == -1) { - ALOGE("mprotect(%p, %zu, PROT_READ | PROT_EXEC) failed: %m", addr, len); - return -1; - } - } - } - // Return non-zero to exit dl_iterate_phdr. - return 0; -} - -/** - * @param env Managed runtime environment - * @return True if disable was successful. - */ -static jboolean com_android_internal_os_Zygote_nativeDisableExecuteOnly(JNIEnv* env, jclass) { - return dl_iterate_phdr(disable_execute_only, nullptr) == 0; -} - static void com_android_internal_os_Zygote_nativeBlockSigTerm(JNIEnv* env, jclass) { auto fail_fn = std::bind(ZygoteFailure, env, "usap", nullptr, _1); BlockSignal(SIGTERM, fail_fn); @@ -1889,8 +1860,6 @@ static const JNINativeMethod gMethods[] = { (void *) com_android_internal_os_Zygote_nativeGetUsapPoolCount }, { "nativeEmptyUsapPool", "()V", (void *) com_android_internal_os_Zygote_nativeEmptyUsapPool }, - { "nativeDisableExecuteOnly", "()Z", - (void *) com_android_internal_os_Zygote_nativeDisableExecuteOnly }, { "nativeBlockSigTerm", "()V", (void* ) com_android_internal_os_Zygote_nativeBlockSigTerm }, { "nativeUnblockSigTerm", "()V", diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml index 57821c58f768..5e6ccc07c4da 100644 --- a/packages/Shell/AndroidManifest.xml +++ b/packages/Shell/AndroidManifest.xml @@ -109,7 +109,6 @@ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" /> - <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" /> <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" /> <uses-permission android:name="android.permission.CREATE_USERS" /> diff --git a/services/core/java/com/android/server/net/NetworkStatsService.java b/services/core/java/com/android/server/net/NetworkStatsService.java index 2af6e349ff0c..a7f861836202 100644 --- a/services/core/java/com/android/server/net/NetworkStatsService.java +++ b/services/core/java/com/android/server/net/NetworkStatsService.java @@ -155,6 +155,8 @@ import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; /** * Collect and persist detailed network statistics, and provide this data to @@ -279,8 +281,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private final DropBoxNonMonotonicObserver mNonMonotonicObserver = new DropBoxNonMonotonicObserver(); + private static final int MAX_STATS_PROVIDER_POLL_WAIT_TIME_MS = 100; private final RemoteCallbackList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList = new RemoteCallbackList<>(); + /** Semaphore used to wait for stats provider to respond to request stats update. */ + private final Semaphore mStatsProviderSem = new Semaphore(0, true); @GuardedBy("mStatsLock") private NetworkStatsRecorder mDevRecorder; @@ -1336,6 +1341,25 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final boolean persistUid = (flags & FLAG_PERSIST_UID) != 0; final boolean persistForce = (flags & FLAG_PERSIST_FORCE) != 0; + // Request asynchronous stats update from all providers for next poll. And wait a bit of + // time to allow providers report-in given that normally binder call should be fast. + // TODO: request with a valid token. + Trace.traceBegin(TRACE_TAG_NETWORK, "provider.requestStatsUpdate"); + final int registeredCallbackCount = mStatsProviderCbList.getRegisteredCallbackCount(); + mStatsProviderSem.drainPermits(); + invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.requestStatsUpdate(0 /* unused */)); + try { + mStatsProviderSem.tryAcquire(registeredCallbackCount, + MAX_STATS_PROVIDER_POLL_WAIT_TIME_MS, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + // Strictly speaking it's possible a provider happened to deliver between the timeout + // and the log, and that doesn't matter too much as this is just a debug log. + Log.d(TAG, "requestStatsUpdate - providers responded " + + mStatsProviderSem.availablePermits() + + "/" + registeredCallbackCount + " : " + e); + } + Trace.traceEnd(TRACE_TAG_NETWORK); + // TODO: consider marking "untrusted" times in historical stats final long currentTime = mClock.millis(); @@ -1373,10 +1397,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { performSampleLocked(); } - // request asynchronous stats update from all providers for next poll. - // TODO: request with a valid token. - invokeForAllStatsProviderCallbacks((cb) -> cb.mProvider.requestStatsUpdate(0 /* unused */)); - // finally, dispatch updated event to any listeners final Intent updatedIntent = new Intent(ACTION_NETWORK_STATS_UPDATED); updatedIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); @@ -1797,7 +1817,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { Objects.requireNonNull(tag, "tag is null"); try { NetworkStatsProviderCallbackImpl callback = new NetworkStatsProviderCallbackImpl( - tag, provider, mAlertObserver, mStatsProviderCbList); + tag, provider, mStatsProviderSem, mAlertObserver, + mStatsProviderCbList); mStatsProviderCbList.register(callback); Log.d(TAG, "registerNetworkStatsProvider from " + callback.mTag + " uid/pid=" + getCallingUid() + "/" + getCallingPid()); @@ -1845,6 +1866,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { @NonNull final String mTag; @NonNull final INetworkStatsProvider mProvider; + @NonNull private final Semaphore mSemaphore; @NonNull final INetworkManagementEventObserver mAlertObserver; @NonNull final RemoteCallbackList<NetworkStatsProviderCallbackImpl> mStatsProviderCbList; @@ -1858,12 +1880,14 @@ public class NetworkStatsService extends INetworkStatsService.Stub { NetworkStatsProviderCallbackImpl( @NonNull String tag, @NonNull INetworkStatsProvider provider, + @NonNull Semaphore semaphore, @NonNull INetworkManagementEventObserver alertObserver, @NonNull RemoteCallbackList<NetworkStatsProviderCallbackImpl> cbList) throws RemoteException { mTag = tag; mProvider = provider; mProvider.asBinder().linkToDeath(this, 0); + mSemaphore = semaphore; mAlertObserver = alertObserver; mStatsProviderCbList = cbList; } @@ -1897,6 +1921,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { if (ifaceStats != null) mIfaceStats.combineAllValues(ifaceStats); if (uidStats != null) mUidStats.combineAllValues(uidStats); } + mSemaphore.release(); } @Override diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index d8f5dfbbaf48..709811ed520d 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -9764,8 +9764,8 @@ public class PackageManagerService extends IPackageManager.Stub } @Override - public void notifyDexLoad(String loadingPackageName, List<String> classLoaderNames, - List<String> classPaths, String loaderIsa) { + public void notifyDexLoad(String loadingPackageName, Map<String, String> classLoaderContextMap, + String loaderIsa) { int userId = UserHandle.getCallingUserId(); ApplicationInfo ai = getApplicationInfo(loadingPackageName, /*flags*/ 0, userId); if (ai == null) { @@ -9773,7 +9773,7 @@ public class PackageManagerService extends IPackageManager.Stub + loadingPackageName + ", user=" + userId); return; } - mDexManager.notifyDexLoad(ai, classLoaderNames, classPaths, loaderIsa, userId); + mDexManager.notifyDexLoad(ai, classLoaderContextMap, loaderIsa, userId); } @Override diff --git a/services/core/java/com/android/server/pm/dex/DexManager.java b/services/core/java/com/android/server/pm/dex/DexManager.java index 9e86a4bd2880..441fa75cd912 100644 --- a/services/core/java/com/android/server/pm/dex/DexManager.java +++ b/services/core/java/com/android/server/pm/dex/DexManager.java @@ -44,6 +44,8 @@ import com.android.server.pm.PackageDexOptimizer; import com.android.server.pm.PackageManagerService; import com.android.server.pm.PackageManagerServiceUtils; +import dalvik.system.VMRuntime; + import java.io.File; import java.io.IOException; import java.util.Arrays; @@ -143,22 +145,15 @@ public class DexManager { * return as fast as possible. * * @param loadingAppInfo the package performing the load - * @param classLoadersNames the names of the class loaders present in the loading chain. The - * list encodes the class loader chain in the natural order. The first class loader has - * the second one as its parent and so on. The dex files present in the class path of the - * first class loader will be recorded in the usage file. - * @param classPaths the class paths corresponding to the class loaders names from - * {@param classLoadersNames}. The the first element corresponds to the first class loader - * and so on. A classpath is represented as a list of dex files separated by - * {@code File.pathSeparator}, or null if the class loader's classpath is not known. - * The dex files found in the first class path will be recorded in the usage file. + * @param classLoaderContextMap a map from file paths to dex files that have been loaded to + * the class loader context that was used to load them. * @param loaderIsa the ISA of the app loading the dex files * @param loaderUserId the user id which runs the code loading the dex files */ - public void notifyDexLoad(ApplicationInfo loadingAppInfo, List<String> classLoadersNames, - List<String> classPaths, String loaderIsa, int loaderUserId) { + public void notifyDexLoad(ApplicationInfo loadingAppInfo, + Map<String, String> classLoaderContextMap, String loaderIsa, int loaderUserId) { try { - notifyDexLoadInternal(loadingAppInfo, classLoadersNames, classPaths, loaderIsa, + notifyDexLoadInternal(loadingAppInfo, classLoaderContextMap, loaderIsa, loaderUserId); } catch (Exception e) { Slog.w(TAG, "Exception while notifying dex load for package " + @@ -168,46 +163,23 @@ public class DexManager { @VisibleForTesting /*package*/ void notifyDexLoadInternal(ApplicationInfo loadingAppInfo, - List<String> classLoaderNames, List<String> classPaths, String loaderIsa, + Map<String, String> classLoaderContextMap, String loaderIsa, int loaderUserId) { - if (classLoaderNames.size() != classPaths.size()) { - Slog.wtf(TAG, "Bad call to noitfyDexLoad: args have different size"); + if (classLoaderContextMap == null) { return; } - if (classLoaderNames.isEmpty()) { + if (classLoaderContextMap.isEmpty()) { Slog.wtf(TAG, "Bad call to notifyDexLoad: class loaders list is empty"); return; } if (!PackageManagerServiceUtils.checkISA(loaderIsa)) { - Slog.w(TAG, "Loading dex files " + classPaths + " in unsupported ISA: " + - loaderIsa + "?"); + Slog.w(TAG, "Loading dex files " + classLoaderContextMap.keySet() + + " in unsupported ISA: " + loaderIsa + "?"); return; } - // The first classpath should never be null because the first classloader - // should always be an instance of BaseDexClassLoader. - String firstClassPath = classPaths.get(0); - if (firstClassPath == null) { - return; - } - // The classpath is represented as a list of dex files separated by File.pathSeparator. - String[] dexPathsToRegister = firstClassPath.split(File.pathSeparator); - - // Encode the class loader contexts for the dexPathsToRegister. - String[] classLoaderContexts = DexoptUtils.processContextForDexLoad( - classLoaderNames, classPaths); - - // A null classLoaderContexts means that there are unsupported class loaders in the - // chain. - if (classLoaderContexts == null) { - if (DEBUG) { - Slog.i(TAG, loadingAppInfo.packageName + - " uses unsupported class loader in " + classLoaderNames); - } - } - - int dexPathIndex = 0; - for (String dexPath : dexPathsToRegister) { + for (Map.Entry<String, String> mapping : classLoaderContextMap.entrySet()) { + String dexPath = mapping.getKey(); // Find the owning package name. DexSearchResult searchResult = getDexPackage(loadingAppInfo, dexPath, loaderUserId); @@ -229,7 +201,6 @@ public class DexManager { // If the dex file is the primary apk (or a split) and not isUsedByOtherApps // do not record it. This case does not bring any new usable information // and can be safely skipped. - dexPathIndex++; continue; } @@ -239,13 +210,13 @@ public class DexManager { searchResult.mOwningPackageName, loadingAppInfo.packageName); } - if (classLoaderContexts != null) { - + String classLoaderContext = mapping.getValue(); + if (classLoaderContext != null + && VMRuntime.isValidClassLoaderContext(classLoaderContext)) { // Record dex file usage. If the current usage is a new pattern (e.g. new // secondary, or UsedByOtherApps), record will return true and we trigger an // async write to disk to make sure we don't loose the data in case of a reboot. - String classLoaderContext = classLoaderContexts[dexPathIndex]; if (mPackageDexUsage.record(searchResult.mOwningPackageName, dexPath, loaderUserId, loaderIsa, isUsedByOtherApps, primaryOrSplit, loadingAppInfo.packageName, classLoaderContext)) { @@ -259,7 +230,6 @@ public class DexManager { Slog.i(TAG, "Could not find owning package for dex file: " + dexPath); } } - dexPathIndex++; } } diff --git a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java index e68c2386e03b..08763e729c71 100644 --- a/services/core/java/com/android/server/pm/dex/PackageDexUsage.java +++ b/services/core/java/com/android/server/pm/dex/PackageDexUsage.java @@ -83,8 +83,9 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { "=UnknownClassLoaderContext="; // The marker used for unsupported class loader contexts (no longer written, may occur in old - // files so discarded on read). - private static final String UNSUPPORTED_CLASS_LOADER_CONTEXT = + // files so discarded on read). Note: this matches + // ClassLoaderContext::kUnsupportedClassLoaderContextEncoding in the runtime. + /*package*/ static final String UNSUPPORTED_CLASS_LOADER_CONTEXT = "=UnsupportedClassLoaderContext="; /** @@ -133,6 +134,9 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { if (classLoaderContext == null) { throw new IllegalArgumentException("Null classLoaderContext"); } + if (classLoaderContext.equals(UNSUPPORTED_CLASS_LOADER_CONTEXT)) { + return false; + } synchronized (mPackageUseInfoMap) { PackageUseInfo packageUseInfo = mPackageUseInfoMap.get(owningPackageName); @@ -843,10 +847,11 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { boolean updateLoadingPackages = mLoadingPackages.addAll(dexUseInfo.mLoadingPackages); String oldClassLoaderContext = mClassLoaderContext; - if (UNKNOWN_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext)) { + if (isUnknownOrUnsupportedContext(mClassLoaderContext)) { // Can happen if we read a previous version. mClassLoaderContext = dexUseInfo.mClassLoaderContext; - } else if (!Objects.equals(mClassLoaderContext, dexUseInfo.mClassLoaderContext)) { + } else if (!isUnknownOrUnsupportedContext(dexUseInfo.mClassLoaderContext) + && !Objects.equals(mClassLoaderContext, dexUseInfo.mClassLoaderContext)) { // We detected a context change. mClassLoaderContext = VARIABLE_CLASS_LOADER_CONTEXT; } @@ -857,6 +862,13 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { || !Objects.equals(oldClassLoaderContext, mClassLoaderContext); } + private static boolean isUnknownOrUnsupportedContext(String context) { + // TODO: Merge UNKNOWN_CLASS_LOADER_CONTEXT & UNSUPPORTED_CLASS_LOADER_CONTEXT cases + // into UNSUPPORTED_CLASS_LOADER_CONTEXT. + return UNKNOWN_CLASS_LOADER_CONTEXT.equals(context) + || UNSUPPORTED_CLASS_LOADER_CONTEXT.equals(context); + } + public boolean isUsedByOtherApps() { return mIsUsedByOtherApps; } @@ -878,7 +890,7 @@ public class PackageDexUsage extends AbstractStatsBase<Void> { public boolean isUnknownClassLoaderContext() { // The class loader context may be unknown if we loaded the data from a previous version // which didn't save the context. - return UNKNOWN_CLASS_LOADER_CONTEXT.equals(mClassLoaderContext); + return isUnknownOrUnsupportedContext(mClassLoaderContext); } public boolean isVariableClassLoaderContext() { diff --git a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java index a4ba056b96a8..cb20b65c9f9b 100644 --- a/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java +++ b/services/tests/servicestests/src/com/android/server/pm/dex/DexManagerTests.java @@ -532,6 +532,61 @@ public class DexManagerTests { assertHasDclInfo(mBarUser0, mBarUser0, secondaries); } + @Test + public void testPrimaryAndSecondaryDexLoad() { + // Foo loads both primary and secondary dexes + List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths(); + List<String> fooDexes = new ArrayList<>(mFooUser0.getBaseAndSplitDexPaths()); + int primaryCount = fooDexes.size(); + fooDexes.addAll(fooSecondaries); + + notifyDexLoad(mFooUser0, fooDexes, mUser0); + + PackageUseInfo pui = getPackageUseInfo(mFooUser0); + assertIsUsedByOtherApps(mFooUser0, pui, false); + assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size()); + + // Below we want to verify that the secondary dex files within fooDexes have been correctly + // reported and their class loader contexts were correctly recorded. + // + // In order to achieve this we first use DexoptUtils.processContextForDexLoad to compute the + // class loader contexts for all the dex files. + String[] allClassLoaderContexts = DexoptUtils.processContextForDexLoad( + Arrays.asList(mFooUser0.mClassLoader), + Arrays.asList(String.join(File.pathSeparator, fooDexes))); + // Next we filter out the class loader contexts corresponding to non-secondary dex files. + String[] secondaryClassLoaderContexts = Arrays.copyOfRange(allClassLoaderContexts, + primaryCount, allClassLoaderContexts.length); + assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0, + secondaryClassLoaderContexts); + + assertHasDclInfo(mFooUser0, mFooUser0, fooSecondaries); + } + + @Test + public void testNotifySecondary_withSharedLibrary() { + // Foo loads its own secondary files. + List<String> fooSecondaries = mFooUser0.getSecondaryDexPaths(); + + String contextSuffix = "{PCL[/system/framework/org.apache.http.legacy.jar]}"; + String[] expectedContexts = DexoptUtils.processContextForDexLoad( + Arrays.asList(mFooUser0.mClassLoader), + Arrays.asList(String.join(File.pathSeparator, fooSecondaries))); + for (int i = 0; i < expectedContexts.length; i++) { + expectedContexts[i] += contextSuffix; + } + + notifyDexLoad(mFooUser0, fooSecondaries, expectedContexts, mUser0); + + PackageUseInfo pui = getPackageUseInfo(mFooUser0); + assertIsUsedByOtherApps(mFooUser0, pui, false); + assertEquals(fooSecondaries.size(), pui.getDexUseInfoMap().size()); + assertSecondaryUse(mFooUser0, pui, fooSecondaries, /*isUsedByOtherApps*/false, mUser0, + expectedContexts); + + assertHasDclInfo(mFooUser0, mFooUser0, fooSecondaries); + } + private void assertSecondaryUse(TestData testData, PackageUseInfo pui, List<String> secondaries, boolean isUsedByOtherApps, int ownerUserId, String[] expectedContexts) { @@ -572,17 +627,43 @@ public class DexManagerTests { // By default, assume a single class loader in the chain. // This makes writing tests much easier. List<String> classLoaders = Arrays.asList(testData.mClassLoader); - List<String> classPaths = (dexPaths == null) - ? Arrays.asList((String) null) - : Arrays.asList(String.join(File.pathSeparator, dexPaths)); + List<String> classPaths = dexPaths != null + ? Arrays.<String>asList(String.join(File.pathSeparator, dexPaths)) : null; notifyDexLoad(testData, classLoaders, classPaths, loaderUserId); } private void notifyDexLoad(TestData testData, List<String> classLoaders, List<String> classPaths, int loaderUserId) { + String[] classLoaderContexts = computeClassLoaderContexts(classLoaders, classPaths); // We call the internal function so any exceptions thrown cause test failures. - mDexManager.notifyDexLoadInternal(testData.mPackageInfo.applicationInfo, classLoaders, - classPaths, testData.mLoaderIsa, loaderUserId); + List<String> dexPaths = classPaths != null + ? Arrays.asList(classPaths.get(0).split(File.pathSeparator)) : Arrays.asList(); + notifyDexLoad(testData, dexPaths, classLoaderContexts, loaderUserId); + } + + private void notifyDexLoad(TestData testData, List<String> dexPaths, + String[] classLoaderContexts, int loaderUserId) { + assertTrue(dexPaths.size() == classLoaderContexts.length); + HashMap<String, String> dexPathMapping = new HashMap<>(dexPaths.size()); + for (int i = 0; i < dexPaths.size(); i++) { + dexPathMapping.put(dexPaths.get(i), classLoaderContexts != null + ? classLoaderContexts[i] : PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT); + } + mDexManager.notifyDexLoadInternal(testData.mPackageInfo.applicationInfo, dexPathMapping, + testData.mLoaderIsa, loaderUserId); + } + + private String[] computeClassLoaderContexts(List<String> classLoaders, + List<String> classPaths) { + if (classPaths == null) { + return new String[0]; + } + String[] results = DexoptUtils.processContextForDexLoad(classLoaders, classPaths); + if (results == null) { + results = new String[classPaths.get(0).split(File.pathSeparator).length]; + Arrays.fill(results, PackageDexUsage.UNSUPPORTED_CLASS_LOADER_CONTEXT); + } + return results; } private PackageUseInfo getPackageUseInfo(TestData testData) { |