Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f665781c authored by Calin Juravle's avatar Calin Juravle Committed by Gerrit Code Review
Browse files

Merge changes from topic 'dex'

* changes:
  Compile secondary dex files according to REASON_BACKGROUND_DEXOPT
  Register secondary dex files for JIT profiling
  Move DexLoadReporter out of LoadedApk
  Set pm.BackgroundDexOptService as the source of true
  Add missing return in DexManager
  Update package use info when the app data is updated
parents 383391c5 c480bdaf
Loading
Loading
Loading
Loading
+11 −0
Original line number Diff line number Diff line
@@ -143,6 +143,7 @@ import libcore.io.DropBox;
import libcore.io.EventLogger;
import libcore.io.IoUtils;
import libcore.net.event.NetworkEventDispatcher;
import dalvik.system.BaseDexClassLoader;
import dalvik.system.CloseGuard;
import dalvik.system.VMDebug;
import dalvik.system.VMRuntime;
@@ -5346,6 +5347,16 @@ public final class ActivityThread {
            }
        }

        // 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 calling onCreate so that we can capture the
        // complete application startup.
        if (SystemProperties.getBoolean("dalvik.vm.usejitprofiles", false)) {
            BaseDexClassLoader.setReporter(DexLoadReporter.getInstance());
        }

        // Install the Network Security Config Provider. This must happen before the application
        // code is loaded to prevent issues with instances of TLS objects being created before
        // the provider is installed.
+168 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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.os.FileUtils;
import android.os.RemoteException;
import android.os.SystemProperties;
import android.util.Slog;

import com.android.internal.annotations.GuardedBy;

import dalvik.system.BaseDexClassLoader;
import dalvik.system.VMRuntime;

import java.io.File;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * A dex load reporter which will notify package manager of any dex file loaded
 * with {@code BaseDexClassLoader}.
 * The goals are:
 *     1) discover secondary dex files so that they can be optimized during the
 *        idle maintenance job.
 *     2) determine whether or not a dex file is used by an app which does not
 *        own it (in order to select the optimal compilation method).
 * @hide
 */
/*package*/ class DexLoadReporter implements BaseDexClassLoader.Reporter {
    private static final String TAG = "DexLoadReporter";

    private static final DexLoadReporter INSTANCE = new DexLoadReporter();

    private static final boolean DEBUG = false;

    // We must guard the access to the list of data directories because
    // we might have concurrent accesses. Apps might load dex files while
    // new data dirs are registered (due to creation of LoadedApks via
    // create createApplicationContext).
    @GuardedBy("mDataDirs")
    private final Set<String> mDataDirs;

    private DexLoadReporter() {
        mDataDirs = new HashSet<>();
    }

    /*package*/ static DexLoadReporter getInstance() {
        return INSTANCE;
    }

    /**
     * Register an application data directory with the reporter.
     * The data directories are used to determine if a dex file is secondary dex or not.
     * Note that this method may be called multiple times for the same app, registering
     * different data directories. This may happen when apps share the same user id
     * ({@code android:sharedUserId}). For example, if app1 and app2 share the same user
     * id, and app1 loads app2 apk, then both data directories will be registered.
     */
    /*package*/ void registerAppDataDir(String packageName, String dataDir) {
        if (DEBUG) {
            Slog.i(TAG, "Package " + packageName + " registering data dir: " + dataDir);
        }
        // TODO(calin): A few code paths imply that the data dir
        // might be null. Investigate when that can happen.
        if (dataDir != null) {
            synchronized (mDataDirs) {
                mDataDirs.add(dataDir);
            }
        }
    }

    @Override
    public void report(List<String> dexPaths) {
        if (dexPaths.isEmpty()) {
            return;
        }
        // Notify the package manager about the dex loads unconditionally.
        // The load might be for either a primary or secondary dex file.
        notifyPackageManager(dexPaths);
        // Check for secondary dex files and register them for profiling if
        // possible.
        registerSecondaryDexForProfiling(dexPaths);
    }

    private void notifyPackageManager(List<String> dexPaths) {
        String packageName = ActivityThread.currentPackageName();
        try {
            ActivityThread.getPackageManager().notifyDexLoad(
                    packageName, dexPaths, VMRuntime.getRuntime().vmInstructionSet());
        } catch (RemoteException re) {
            Slog.e(TAG, "Failed to notify PM about dex load for package " + packageName, re);
        }
    }

    private void registerSecondaryDexForProfiling(List<String> dexPaths) {
        if (!SystemProperties.getBoolean("dalvik.vm.dexopt.secondary", false)) {
            return;
        }
        // Make a copy of the current data directories so that we don't keep the lock
        // while registering for profiling. The registration will perform I/O to
        // check for or create the profile.
        String[] dataDirs;
        synchronized (mDataDirs) {
            dataDirs = mDataDirs.toArray(new String[0]);
        }
        for (String dexPath : dexPaths) {
            registerSecondaryDexForProfiling(dexPath, dataDirs);
        }
    }

    private void registerSecondaryDexForProfiling(String dexPath, String[] dataDirs) {
        if (!isSecondaryDexFile(dexPath, dataDirs)) {
            // The dex path is not a secondary dex file. Nothing to do.
            return;
        }
        File secondaryProfile = getSecondaryProfileFile(dexPath);
        try {
            // Create the profile if not already there.
            // Returns true if the file was created, false if the file already exists.
            // or throws exceptions in case of errors.
            boolean created = secondaryProfile.createNewFile();
            if (DEBUG && created) {
                Slog.i(TAG, "Created profile for secondary dex: " + secondaryProfile);
            }
        } catch (IOException ex) {
            Slog.e(TAG, "Failed to create profile for secondary dex " + secondaryProfile +
                    ":" + ex.getMessage());
            // Don't move forward with the registration if we failed to create the profile.
            return;
        }

        VMRuntime.registerAppInfo(secondaryProfile.getPath(), new String[] { dexPath });
    }

    // A dex file is a secondary dex file if it is in any of the registered app
    // data directories.
    private boolean isSecondaryDexFile(String dexPath, String[] dataDirs) {
        for (String dataDir : dataDirs) {
            if (FileUtils.contains(dataDir, dexPath)) {
                return true;
            }
        }
        return false;
    }

    // Secondary dex profiles are stored next to the dex file and have the same
    // name with '.prof' appended.
    // NOTE: Keep in sync with installd.
    private File getSecondaryProfileFile(String dexPath) {
        return new File(dexPath + ".prof");
    }
}
+4 −34
Original line number Diff line number Diff line
@@ -51,7 +51,6 @@ import android.util.SparseArray;
import android.view.Display;
import android.view.DisplayAdjustments;

import dalvik.system.BaseDexClassLoader;
import dalvik.system.VMRuntime;

import java.io.File;
@@ -610,39 +609,10 @@ public final class LoadedApk {
        VMRuntime.registerAppInfo(profileFile.getPath(),
                codePaths.toArray(new String[codePaths.size()]));

        // Setup the reporter to notify package manager of any relevant dex loads.
        // At this point the primary apk is loaded and will not be reported.
        // Anything loaded from now on will be tracked as a potential secondary
        // or foreign dex file. The goal is to enable:
        //    1) monitoring and compilation of secondary dex file
        //    2) track whether or not a dex file is used by other apps (used to
        //       determined the compilation filter of apks).
        if (BaseDexClassLoader.getReporter() != DexLoadReporter.INSTANCE) {
            // Set the dex load reporter if not already set.
            // Note that during the app's life cycle different LoadedApks may be
            // created and loaded (e.g. if two different apps share the same runtime).
            BaseDexClassLoader.setReporter(DexLoadReporter.INSTANCE);
        }
    }

    private static class DexLoadReporter implements BaseDexClassLoader.Reporter {
        private static final DexLoadReporter INSTANCE = new DexLoadReporter();

        private DexLoadReporter() {}

        @Override
        public void report(List<String> dexPaths) {
            if (dexPaths.isEmpty()) {
                return;
            }
            String packageName = ActivityThread.currentPackageName();
            try {
                ActivityThread.getPackageManager().notifyDexLoad(
                        packageName, dexPaths, VMRuntime.getRuntime().vmInstructionSet());
            } catch (RemoteException re) {
                Slog.e(TAG, "Failed to notify PM about dex load for package " + packageName, re);
            }
        }
        // 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);
    }

    /**
+3 −4
Original line number Diff line number Diff line
@@ -428,14 +428,13 @@ public class FileUtils {
     */
    public static boolean contains(File dir, File file) {
        if (dir == null || file == null) return false;
        return contains(dir.getAbsolutePath(), file.getAbsolutePath());
    }

        String dirPath = dir.getAbsolutePath();
        String filePath = file.getAbsolutePath();

    public static boolean contains(String dirPath, String filePath) {
        if (dirPath.equals(filePath)) {
            return true;
        }

        if (!dirPath.endsWith("/")) {
            dirPath += "/";
        }
+2 −4
Original line number Diff line number Diff line
@@ -49,8 +49,6 @@ public class BackgroundDexOptService extends JobService {

    private static final boolean DEBUG = false;

    private static final long RETRY_LATENCY = 4 * AlarmManager.INTERVAL_HOUR;

    private static final int JOB_IDLE_OPTIMIZE = 800;
    private static final int JOB_POST_BOOT_UPDATE = 801;

@@ -292,8 +290,8 @@ public class BackgroundDexOptService extends JobService {
                            PackageManagerService.REASON_BACKGROUND_DEXOPT,
                            /* force */ false)
                    : pm.performDexOptSecondary(pkg,
                            PackageManagerServiceCompilerMapping.getFullCompilerFilter(),
                            /* force */ true);
                            PackageManagerService.REASON_BACKGROUND_DEXOPT,
                            /* force */ false);
            if (success) {
                // Dexopt succeeded, remove package from the list of failing ones.
                synchronized (failedPackageNames) {
Loading