Merge "Allow pausing Watchdog on the current thread" into qt-dev
diff --git a/services/core/java/com/android/server/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index f9aaf11..a7fb99f 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -140,6 +140,7 @@
private boolean mCompleted;
private Monitor mCurrentMonitor;
private long mStartTime;
+ private int mPauseCount;
HandlerChecker(Handler handler, String name, long waitMaxMillis) {
mHandler = handler;
@@ -160,17 +161,18 @@
mMonitors.addAll(mMonitorQueue);
mMonitorQueue.clear();
}
- if (mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling()) {
+ if ((mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling())
+ || (mPauseCount > 0)) {
+ // Don't schedule until after resume OR
// If the target looper has recently been polling, then
// there is no reason to enqueue our checker on it since that
// is as good as it not being deadlocked. This avoid having
- // to do a context switch to check the thread. Note that we
- // only do this if mCheckReboot is false and we have no
- // monitors, since those would need to be executed at this point.
+ // to do a context switch to check the thread. Note that we
+ // only do this if we have no monitors since those would need to
+ // be executed at this point.
mCompleted = true;
return;
}
-
if (!mCompleted) {
// we already have a check in flight, so no need
return;
@@ -236,6 +238,28 @@
mCurrentMonitor = null;
}
}
+
+ /** Pause the HandlerChecker. */
+ public void pauseLocked(String reason) {
+ mPauseCount++;
+ // Mark as completed, because there's a chance we called this after the watchog
+ // thread loop called Object#wait after 'WAITED_HALF'. In that case we want to ensure
+ // the next call to #getCompletionStateLocked for this checker returns 'COMPLETED'
+ mCompleted = true;
+ Slog.i(TAG, "Pausing HandlerChecker: " + mName + " for reason: "
+ + reason + ". Pause count: " + mPauseCount);
+ }
+
+ /** Resume the HandlerChecker from the last {@link #pauseLocked}. */
+ public void resumeLocked(String reason) {
+ if (mPauseCount > 0) {
+ mPauseCount--;
+ Slog.i(TAG, "Resuming HandlerChecker: " + mName + " for reason: "
+ + reason + ". Pause count: " + mPauseCount);
+ } else {
+ Slog.wtf(TAG, "Already resumed HandlerChecker: " + mName);
+ }
+ }
}
final class RebootRequestReceiver extends BroadcastReceiver {
@@ -364,6 +388,51 @@
}
/**
+ * Pauses Watchdog action for the currently running thread. Useful before executing long running
+ * operations that could falsely trigger the watchdog. Each call to this will require a matching
+ * call to {@link #resumeWatchingCurrentThread}.
+ *
+ * <p>If the current thread has not been added to the Watchdog, this call is a no-op.
+ *
+ * <p>If the Watchdog is already paused for the current thread, this call adds
+ * adds another pause and will require an additional {@link #resumeCurrentThread} to resume.
+ *
+ * <p>Note: Use with care, as any deadlocks on the current thread will be undetected until all
+ * pauses have been resumed.
+ */
+ public void pauseWatchingCurrentThread(String reason) {
+ synchronized (this) {
+ for (HandlerChecker hc : mHandlerCheckers) {
+ if (Thread.currentThread().equals(hc.getThread())) {
+ hc.pauseLocked(reason);
+ }
+ }
+ }
+ }
+
+ /**
+ * Resumes the last pause from {@link #pauseWatchingCurrentThread} for the currently running
+ * thread.
+ *
+ * <p>If the current thread has not been added to the Watchdog, this call is a no-op.
+ *
+ * <p>If the Watchdog action for the current thread is already resumed, this call logs a wtf.
+ *
+ * <p>If all pauses have been resumed, the Watchdog action is finally resumed, otherwise,
+ * the Watchdog action for the current thread remains paused until resume is called at least
+ * as many times as the calls to pause.
+ */
+ public void resumeWatchingCurrentThread(String reason) {
+ synchronized (this) {
+ for (HandlerChecker hc : mHandlerCheckers) {
+ if (Thread.currentThread().equals(hc.getThread())) {
+ hc.resumeLocked(reason);
+ }
+ }
+ }
+ }
+
+ /**
* Perform a full reboot of the system.
*/
void rebootSystem(String reason) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 8c65fa8..2b5cd01 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1167,9 +1167,12 @@
if (!mOnlyCore) {
traceBeginAndSlog("UpdatePackagesIfNeeded");
try {
+ Watchdog.getInstance().pauseWatchingCurrentThread("dexopt");
mPackageManagerService.updatePackagesIfNeeded();
} catch (Throwable e) {
reportWtf("update packages", e);
+ } finally {
+ Watchdog.getInstance().resumeWatchingCurrentThread("dexopt");
}
traceEnd();
}