Loading services/core/java/com/android/server/PackageWatchdog.java +100 −106 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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 Loading @@ -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; Loading @@ -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. Loading @@ -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 Loading @@ -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()); Loading @@ -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(); } /** Loading @@ -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? Loading @@ -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; } } } } Loading @@ -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"); Loading @@ -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. */ Loading Loading @@ -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; } } } Loading @@ -313,7 +327,7 @@ public class PackageWatchdog { } } } sendIoMessage(MESSAGE_SAVE_FILE); saveToFileAsync(); } /** Loading @@ -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(); Loading @@ -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(); Loading @@ -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); } /** Loading Loading @@ -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); } } Loading Loading @@ -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; } } } } Loading
services/core/java/com/android/server/PackageWatchdog.java +100 −106 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -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 Loading @@ -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; Loading @@ -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. Loading @@ -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 Loading @@ -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()); Loading @@ -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(); } /** Loading @@ -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? Loading @@ -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; } } } } Loading @@ -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"); Loading @@ -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. */ Loading Loading @@ -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; } } } Loading @@ -313,7 +327,7 @@ public class PackageWatchdog { } } } sendIoMessage(MESSAGE_SAVE_FILE); saveToFileAsync(); } /** Loading @@ -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(); Loading @@ -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(); Loading @@ -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); } /** Loading Loading @@ -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); } } Loading Loading @@ -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; } } } }