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

Commit e4efe2af authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Improve Packagewatchdog performance"

parents 968285a0 cfaed764
Loading
Loading
Loading
Loading
+100 −106
Original line number Diff line number Diff line
@@ -19,10 +19,7 @@ package com.android.server;
import android.content.Context;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -32,6 +29,7 @@ import android.util.Slog;
import android.util.Xml;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.BackgroundThread;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;

@@ -50,8 +48,6 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * Monitors the health of packages on the system and notifies interested observers when packages
@@ -70,7 +66,6 @@ public class PackageWatchdog {
    private static final String ATTR_VERSION = "version";
    private static final String ATTR_NAME = "name";
    private static final String ATTR_DURATION = "duration";
    private static final int MESSAGE_SAVE_FILE = 1;

    private static PackageWatchdog sPackageWatchdog;

@@ -79,20 +74,21 @@ public class PackageWatchdog {
    private final Context mContext;
    // Handler to run package cleanup runnables
    private final Handler mTimerHandler;
    private final HandlerThread mIoThread = new HandlerThread("package_watchdog_io",
            Process.THREAD_PRIORITY_BACKGROUND);
    private final Handler mIoHandler;
    // Maps observer names to package observers that have been registered since the last boot
    // Contains (observer-name -> external-observer-handle) that have been registered during the
    // current boot.
    // It is populated when observers call #registerHealthObserver and it does not survive reboots.
    @GuardedBy("mLock")
    final Map<String, PackageHealthObserver> mRegisteredObservers = new ArrayMap<>();
    // Maps observer names to internal observers (registered or not) loaded from file
    final ArrayMap<String, PackageHealthObserver> mRegisteredObservers = new ArrayMap<>();
    // Contains (observer-name -> internal-observer-handle) that have ever been registered from
    // previous boots. Observers with all packages expired are periodically pruned.
    // It is saved to disk on system shutdown and repouplated on startup so it survives reboots.
    @GuardedBy("mLock")
    final Map<String, ObserverInternal> mAllObservers = new ArrayMap<>();
    // /data/system/ directory
    private final File mSystemDir = new File(Environment.getDataDirectory(), "system");
    // File containing the XML data of monitored packages
    final ArrayMap<String, ObserverInternal> mAllObservers = new ArrayMap<>();
    // File containing the XML data of monitored packages /data/system/package-watchdog.xml
    private final AtomicFile mPolicyFile =
            new AtomicFile(new File(mSystemDir, "package-watchdog.xml"));
            new AtomicFile(new File(new File(Environment.getDataDirectory(), "system"),
                           "package-watchdog.xml"));
    // Runnable to prune monitored packages that have expired
    private final Runnable mPackageCleanup;
    // Last SystemClock#uptimeMillis a package clean up was executed.
@@ -105,19 +101,20 @@ public class PackageWatchdog {
    private PackageWatchdog(Context context) {
        mContext = context;
        mTimerHandler = new Handler(Looper.myLooper());
        mIoThread.start();
        mIoHandler = new IoHandler(mIoThread.getLooper());
        mIoHandler = BackgroundThread.getHandler();
        mPackageCleanup = this::rescheduleCleanup;
        loadFromFile();
    }

    /** Creates or gets singleton instance of PackageWatchdog. */
    public static synchronized PackageWatchdog getInstance(Context context) {
    public static PackageWatchdog getInstance(Context context) {
        synchronized (PackageWatchdog.class) {
            if (sPackageWatchdog == null) {
                sPackageWatchdog = new PackageWatchdog(context);
            }
            return sPackageWatchdog;
        }
    }

    /**
     * Registers {@code observer} to listen for package failures
@@ -140,21 +137,20 @@ public class PackageWatchdog {
     * {@code observer} of any package failures within the monitoring duration.
     *
     * <p>If {@code observer} is already monitoring a package in {@code packageNames},
     * the monitoring window of that package will be reset to {@code hours}.
     * the monitoring window of that package will be reset to {@code durationMs}.
     *
     * @throws IllegalArgumentException if {@code packageNames} is empty
     * or {@code hours} is less than 1
     * or {@code durationMs} is less than 1
     */
    public void startObservingHealth(PackageHealthObserver observer, List<String> packageNames,
            int hours) {
        if (packageNames.isEmpty() || hours < 1) {
            int durationMs) {
        if (packageNames.isEmpty() || durationMs < 1) {
            throw new IllegalArgumentException("Observation not started, no packages specified"
                    + "or invalid hours");
                    + "or invalid duration");
        }
        long durationMs = TimeUnit.HOURS.toMillis(hours);
        List<MonitoredPackage> packages = new ArrayList<>();
        for (String packageName : packageNames) {
            packages.add(new MonitoredPackage(packageName, durationMs));
        for (int i = 0; i < packageNames.size(); i++) {
            packages.add(new MonitoredPackage(packageNames.get(i), durationMs));
        }
        synchronized (mLock) {
            ObserverInternal oldObserver = mAllObservers.get(observer.getName());
@@ -173,7 +169,7 @@ public class PackageWatchdog {
        // Always reschedule because we may need to expire packages
        // earlier than we are already scheduled for
        rescheduleCleanup();
        sendIoMessage(MESSAGE_SAVE_FILE);
        saveToFileAsync();
    }

    /**
@@ -186,7 +182,7 @@ public class PackageWatchdog {
            mAllObservers.remove(observer.getName());
            mRegisteredObservers.remove(observer.getName());
        }
        sendIoMessage(MESSAGE_SAVE_FILE);
        saveToFileAsync();
    }

    // TODO(zezeozue:) Accept current versionCodes of failing packages?
@@ -194,28 +190,44 @@ public class PackageWatchdog {
     * Called when a process fails either due to a crash or ANR.
     *
     * <p>All registered observers for the packages contained in the process will be notified in
     * order of priority unitl an observer signifies that it has taken action and other observers
     * order of priority until an observer signifies that it has taken action and other observers
     * should not notified.
     *
     * <p>This method could be called frequently if there is a severe problem on the device.
     */
    public void onPackageFailure(String[] packages) {
        ArrayMap<String, List<PackageHealthObserver>> packagesToReport = new ArrayMap<>();
        synchronized (mLock) {
            if (mRegisteredObservers.isEmpty()) {
                return;
            }
            for (String packageName : packages) {
                for (ObserverInternal observer : mAllObservers.values()) {
                    if (observer.onPackageFailure(packageName)) {

            for (int pIndex = 0; pIndex < packages.length; pIndex++) {
                for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) {
                    // Observers interested in receiving packageName failures
                    List<PackageHealthObserver> observersToNotify = new ArrayList<>();
                    PackageHealthObserver activeObserver =
                                mRegisteredObservers.get(observer.mName);
                        if (activeObserver != null
                                && activeObserver.onHealthCheckFailed(packageName)) {
                            // Observer has handled, do not notify other observers
                            break;
                            mRegisteredObservers.get(mAllObservers.valueAt(oIndex).mName);
                    if (activeObserver != null) {
                        observersToNotify.add(activeObserver);
                    }

                    // Save interested observers and notify them outside the lock
                    if (!observersToNotify.isEmpty()) {
                        packagesToReport.put(packages[pIndex], observersToNotify);
                    }
                }
            }
        }

        // Notify observers
        for (int pIndex = 0; pIndex < packagesToReport.size(); pIndex++) {
            List<PackageHealthObserver> observers = packagesToReport.valueAt(pIndex);
            for (int oIndex = 0; oIndex < observers.size(); oIndex++) {
                if (observers.get(oIndex).onHealthCheckFailed(packages[pIndex])) {
                    // Observer has handled, do not notify others
                    break;
                }
            }
        }
    }
@@ -225,7 +237,7 @@ public class PackageWatchdog {
    /** Writes the package information to file during shutdown. */
    public void writeNow() {
        if (!mAllObservers.isEmpty()) {
            mIoHandler.removeMessages(MESSAGE_SAVE_FILE);
            mIoHandler.removeCallbacks(this::saveToFile);
            pruneObservers(SystemClock.uptimeMillis() - mUptimeAtLastRescheduleMs);
            saveToFile();
            Slog.i(TAG, "Last write to update package durations");
@@ -235,7 +247,7 @@ public class PackageWatchdog {
    /** Register instances of this interface to receive notifications on package failure. */
    public interface PackageHealthObserver {
        /**
         * Called when health check fails for the {@code packages}.
         * Called when health check fails for the {@code packageName}.
         * @return {@code true} if action was taken and other observers should not be notified of
         * this failure, {@code false} otherwise.
         */
@@ -283,10 +295,12 @@ public class PackageWatchdog {
     */
    private long getEarliestPackageExpiryLocked() {
        long shortestDurationMs = Long.MAX_VALUE;
        for (ObserverInternal observer : mAllObservers.values()) {
            for (MonitoredPackage p : observer.mPackages.values()) {
                if (p.mDurationMs < shortestDurationMs) {
                    shortestDurationMs = p.mDurationMs;
        for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) {
            ArrayMap<String, MonitoredPackage> packages = mAllObservers.valueAt(oIndex).mPackages;
            for (int pIndex = 0; pIndex < packages.size(); pIndex++) {
                long duration = packages.valueAt(pIndex).mDurationMs;
                if (duration < shortestDurationMs) {
                    shortestDurationMs = duration;
                }
            }
        }
@@ -313,7 +327,7 @@ public class PackageWatchdog {
                }
            }
        }
        sendIoMessage(MESSAGE_SAVE_FILE);
        saveToFileAsync();
    }

    /**
@@ -339,24 +353,19 @@ public class PackageWatchdog {
            }
        } catch (FileNotFoundException e) {
            // Nothing to monitor
        } catch (IOException e) {
            Log.wtf(TAG, "Unable to read monitored packages", e);
        } catch (NumberFormatException e) {
            Log.wtf(TAG, "Unable to parse monitored package windows", e);
        } catch (XmlPullParserException e) {
            Log.wtf(TAG, "Unable to parse monitored packages", e);
        } catch (IOException | NumberFormatException | XmlPullParserException e) {
            Log.wtf(TAG, "Unable to read monitored packages, deleting file", e);
            mPolicyFile.delete();
        } finally {
            IoUtils.closeQuietly(infile);
        }
    }

    /**
     * Persists mAllObservers to file and ignores threshold information.
     *
     * <p>Note that this is <b>not</b> thread safe and should only be called on the
     * single threaded IoHandler.
     * Persists mAllObservers to file. Threshold information is ignored.
     */
    private boolean saveToFile() {
        synchronized (mLock) {
            FileOutputStream stream;
            try {
                stream = mPolicyFile.startWrite();
@@ -371,8 +380,8 @@ public class PackageWatchdog {
                out.startDocument(null, true);
                out.startTag(null, TAG_PACKAGE_WATCHDOG);
                out.attribute(null, ATTR_VERSION, Integer.toString(DB_VERSION));
            for (ObserverInternal observer : mAllObservers.values()) {
                observer.write(out);
                for (int oIndex = 0; oIndex < mAllObservers.size(); oIndex++) {
                    mAllObservers.valueAt(oIndex).write(out);
                }
                out.endTag(null, TAG_PACKAGE_WATCHDOG);
                out.endDocument();
@@ -386,12 +395,11 @@ public class PackageWatchdog {
                IoUtils.closeQuietly(stream);
            }
        }

    private void sendIoMessage(int what) {
        if (!mIoHandler.hasMessages(what)) {
            Message m = Message.obtain(mIoHandler, what);
            mIoHandler.sendMessage(m);
    }

    private void saveToFileAsync() {
        mIoHandler.removeCallbacks(this::saveToFile);
        mIoHandler.post(this::saveToFile);
    }

    /**
@@ -435,7 +443,8 @@ public class PackageWatchdog {

        public void updatePackages(List<MonitoredPackage> packages) {
            synchronized (mName) {
                for (MonitoredPackage p : packages) {
                for (int pIndex = 0; pIndex < packages.size(); pIndex++) {
                    MonitoredPackage p = packages.get(pIndex);
                    mPackages.put(p.mName, p);
                }
            }
@@ -554,19 +563,4 @@ public class PackageWatchdog {
            return mFailures >= TRIGGER_FAILURE_COUNT;
        }
    }

    private class IoHandler extends Handler {
        IoHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MESSAGE_SAVE_FILE:
                    saveToFile();
                    break;
            }
        }
    }
}