• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server;
18 
19 import android.app.IActivityController;
20 import android.os.Binder;
21 import android.os.Build;
22 import android.os.RemoteException;
23 import android.system.ErrnoException;
24 import android.system.OsConstants;
25 import android.system.StructRlimit;
26 import com.android.internal.os.ZygoteConnectionConstants;
27 import com.android.server.am.ActivityManagerService;
28 
29 import android.content.BroadcastReceiver;
30 import android.content.ContentResolver;
31 import android.content.Context;
32 import android.content.Intent;
33 import android.content.IntentFilter;
34 import android.hidl.manager.V1_0.IServiceManager;
35 import android.os.Debug;
36 import android.os.Handler;
37 import android.os.IPowerManager;
38 import android.os.Looper;
39 import android.os.Process;
40 import android.os.ServiceManager;
41 import android.os.SystemClock;
42 import android.os.SystemProperties;
43 import android.util.EventLog;
44 import android.util.Log;
45 import android.util.Slog;
46 
47 import java.io.File;
48 import java.io.FileWriter;
49 import java.io.IOException;
50 import java.util.ArrayList;
51 import java.util.Arrays;
52 import java.util.Collections;
53 import java.util.HashSet;
54 import java.util.List;
55 
56 /** This class calls its monitor every minute. Killing this process if they don't return **/
57 public class Watchdog extends Thread {
58     static final String TAG = "Watchdog";
59 
60     // Set this to true to use debug default values.
61     static final boolean DB = false;
62 
63     // Set this to true to have the watchdog record kernel thread stacks when it fires
64     static final boolean RECORD_KERNEL_THREADS = true;
65 
66     // Note 1: Do not lower this value below thirty seconds without tightening the invoke-with
67     //         timeout in com.android.internal.os.ZygoteConnection, or wrapped applications
68     //         can trigger the watchdog.
69     // Note 2: The debug value is already below the wait time in ZygoteConnection. Wrapped
70     //         applications may not work with a debug build. CTS will fail.
71     static final long DEFAULT_TIMEOUT = DB ? 10*1000 : 60*1000;
72     static final long CHECK_INTERVAL = DEFAULT_TIMEOUT / 2;
73 
74     // These are temporally ordered: larger values as lateness increases
75     static final int COMPLETED = 0;
76     static final int WAITING = 1;
77     static final int WAITED_HALF = 2;
78     static final int OVERDUE = 3;
79 
80     // Which native processes to dump into dropbox's stack traces
81     public static final String[] NATIVE_STACKS_OF_INTEREST = new String[] {
82         "/system/bin/audioserver",
83         "/system/bin/cameraserver",
84         "/system/bin/drmserver",
85         "/system/bin/mediadrmserver",
86         "/system/bin/mediaserver",
87         "/system/bin/sdcard",
88         "/system/bin/surfaceflinger",
89         "media.extractor", // system/bin/mediaextractor
90         "media.codec", // vendor/bin/hw/android.hardware.media.omx@1.0-service
91         "com.android.bluetooth",  // Bluetooth service
92     };
93 
94     public static final List<String> HAL_INTERFACES_OF_INTEREST = Arrays.asList(
95         "android.hardware.audio@2.0::IDevicesFactory",
96         "android.hardware.bluetooth@1.0::IBluetoothHci",
97         "android.hardware.camera.provider@2.4::ICameraProvider",
98         "android.hardware.graphics.composer@2.1::IComposer",
99         "android.hardware.media.omx@1.0::IOmx",
100         "android.hardware.sensors@1.0::ISensors",
101         "android.hardware.vr@1.0::IVr"
102     );
103 
104     static Watchdog sWatchdog;
105 
106     /* This handler will be used to post message back onto the main thread */
107     final ArrayList<HandlerChecker> mHandlerCheckers = new ArrayList<>();
108     final HandlerChecker mMonitorChecker;
109     ContentResolver mResolver;
110     ActivityManagerService mActivity;
111 
112     int mPhonePid;
113     IActivityController mController;
114     boolean mAllowRestart = true;
115     final OpenFdMonitor mOpenFdMonitor;
116 
117     /**
118      * Used for checking status of handle threads and scheduling monitor callbacks.
119      */
120     public final class HandlerChecker implements Runnable {
121         private final Handler mHandler;
122         private final String mName;
123         private final long mWaitMax;
124         private final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>();
125         private boolean mCompleted;
126         private Monitor mCurrentMonitor;
127         private long mStartTime;
128 
HandlerChecker(Handler handler, String name, long waitMaxMillis)129         HandlerChecker(Handler handler, String name, long waitMaxMillis) {
130             mHandler = handler;
131             mName = name;
132             mWaitMax = waitMaxMillis;
133             mCompleted = true;
134         }
135 
addMonitor(Monitor monitor)136         public void addMonitor(Monitor monitor) {
137             mMonitors.add(monitor);
138         }
139 
scheduleCheckLocked()140         public void scheduleCheckLocked() {
141             if (mMonitors.size() == 0 && mHandler.getLooper().getQueue().isPolling()) {
142                 // If the target looper has recently been polling, then
143                 // there is no reason to enqueue our checker on it since that
144                 // is as good as it not being deadlocked.  This avoid having
145                 // to do a context switch to check the thread.  Note that we
146                 // only do this if mCheckReboot is false and we have no
147                 // monitors, since those would need to be executed at this point.
148                 mCompleted = true;
149                 return;
150             }
151 
152             if (!mCompleted) {
153                 // we already have a check in flight, so no need
154                 return;
155             }
156 
157             mCompleted = false;
158             mCurrentMonitor = null;
159             mStartTime = SystemClock.uptimeMillis();
160             mHandler.postAtFrontOfQueue(this);
161         }
162 
isOverdueLocked()163         public boolean isOverdueLocked() {
164             return (!mCompleted) && (SystemClock.uptimeMillis() > mStartTime + mWaitMax);
165         }
166 
getCompletionStateLocked()167         public int getCompletionStateLocked() {
168             if (mCompleted) {
169                 return COMPLETED;
170             } else {
171                 long latency = SystemClock.uptimeMillis() - mStartTime;
172                 if (latency < mWaitMax/2) {
173                     return WAITING;
174                 } else if (latency < mWaitMax) {
175                     return WAITED_HALF;
176                 }
177             }
178             return OVERDUE;
179         }
180 
getThread()181         public Thread getThread() {
182             return mHandler.getLooper().getThread();
183         }
184 
getName()185         public String getName() {
186             return mName;
187         }
188 
describeBlockedStateLocked()189         public String describeBlockedStateLocked() {
190             if (mCurrentMonitor == null) {
191                 return "Blocked in handler on " + mName + " (" + getThread().getName() + ")";
192             } else {
193                 return "Blocked in monitor " + mCurrentMonitor.getClass().getName()
194                         + " on " + mName + " (" + getThread().getName() + ")";
195             }
196         }
197 
198         @Override
run()199         public void run() {
200             final int size = mMonitors.size();
201             for (int i = 0 ; i < size ; i++) {
202                 synchronized (Watchdog.this) {
203                     mCurrentMonitor = mMonitors.get(i);
204                 }
205                 mCurrentMonitor.monitor();
206             }
207 
208             synchronized (Watchdog.this) {
209                 mCompleted = true;
210                 mCurrentMonitor = null;
211             }
212         }
213     }
214 
215     final class RebootRequestReceiver extends BroadcastReceiver {
216         @Override
onReceive(Context c, Intent intent)217         public void onReceive(Context c, Intent intent) {
218             if (intent.getIntExtra("nowait", 0) != 0) {
219                 rebootSystem("Received ACTION_REBOOT broadcast");
220                 return;
221             }
222             Slog.w(TAG, "Unsupported ACTION_REBOOT broadcast: " + intent);
223         }
224     }
225 
226     /** Monitor for checking the availability of binder threads. The monitor will block until
227      * there is a binder thread available to process in coming IPCs to make sure other processes
228      * can still communicate with the service.
229      */
230     private static final class BinderThreadMonitor implements Watchdog.Monitor {
231         @Override
monitor()232         public void monitor() {
233             Binder.blockUntilThreadAvailable();
234         }
235     }
236 
237     public interface Monitor {
monitor()238         void monitor();
239     }
240 
getInstance()241     public static Watchdog getInstance() {
242         if (sWatchdog == null) {
243             sWatchdog = new Watchdog();
244         }
245 
246         return sWatchdog;
247     }
248 
Watchdog()249     private Watchdog() {
250         super("watchdog");
251         // Initialize handler checkers for each common thread we want to check.  Note
252         // that we are not currently checking the background thread, since it can
253         // potentially hold longer running operations with no guarantees about the timeliness
254         // of operations there.
255 
256         // The shared foreground thread is the main checker.  It is where we
257         // will also dispatch monitor checks and do other work.
258         mMonitorChecker = new HandlerChecker(FgThread.getHandler(),
259                 "foreground thread", DEFAULT_TIMEOUT);
260         mHandlerCheckers.add(mMonitorChecker);
261         // Add checker for main thread.  We only do a quick check since there
262         // can be UI running on the thread.
263         mHandlerCheckers.add(new HandlerChecker(new Handler(Looper.getMainLooper()),
264                 "main thread", DEFAULT_TIMEOUT));
265         // Add checker for shared UI thread.
266         mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(),
267                 "ui thread", DEFAULT_TIMEOUT));
268         // And also check IO thread.
269         mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(),
270                 "i/o thread", DEFAULT_TIMEOUT));
271         // And the display thread.
272         mHandlerCheckers.add(new HandlerChecker(DisplayThread.getHandler(),
273                 "display thread", DEFAULT_TIMEOUT));
274 
275         // Initialize monitor for Binder threads.
276         addMonitor(new BinderThreadMonitor());
277 
278         mOpenFdMonitor = OpenFdMonitor.create();
279 
280         // See the notes on DEFAULT_TIMEOUT.
281         assert DB ||
282                 DEFAULT_TIMEOUT > ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS;
283     }
284 
init(Context context, ActivityManagerService activity)285     public void init(Context context, ActivityManagerService activity) {
286         mResolver = context.getContentResolver();
287         mActivity = activity;
288 
289         context.registerReceiver(new RebootRequestReceiver(),
290                 new IntentFilter(Intent.ACTION_REBOOT),
291                 android.Manifest.permission.REBOOT, null);
292     }
293 
processStarted(String name, int pid)294     public void processStarted(String name, int pid) {
295         synchronized (this) {
296             if ("com.android.phone".equals(name)) {
297                 mPhonePid = pid;
298             }
299         }
300     }
301 
setActivityController(IActivityController controller)302     public void setActivityController(IActivityController controller) {
303         synchronized (this) {
304             mController = controller;
305         }
306     }
307 
setAllowRestart(boolean allowRestart)308     public void setAllowRestart(boolean allowRestart) {
309         synchronized (this) {
310             mAllowRestart = allowRestart;
311         }
312     }
313 
addMonitor(Monitor monitor)314     public void addMonitor(Monitor monitor) {
315         synchronized (this) {
316             if (isAlive()) {
317                 throw new RuntimeException("Monitors can't be added once the Watchdog is running");
318             }
319             mMonitorChecker.addMonitor(monitor);
320         }
321     }
322 
addThread(Handler thread)323     public void addThread(Handler thread) {
324         addThread(thread, DEFAULT_TIMEOUT);
325     }
326 
addThread(Handler thread, long timeoutMillis)327     public void addThread(Handler thread, long timeoutMillis) {
328         synchronized (this) {
329             if (isAlive()) {
330                 throw new RuntimeException("Threads can't be added once the Watchdog is running");
331             }
332             final String name = thread.getLooper().getThread().getName();
333             mHandlerCheckers.add(new HandlerChecker(thread, name, timeoutMillis));
334         }
335     }
336 
337     /**
338      * Perform a full reboot of the system.
339      */
rebootSystem(String reason)340     void rebootSystem(String reason) {
341         Slog.i(TAG, "Rebooting system because: " + reason);
342         IPowerManager pms = (IPowerManager)ServiceManager.getService(Context.POWER_SERVICE);
343         try {
344             pms.reboot(false, reason, false);
345         } catch (RemoteException ex) {
346         }
347     }
348 
evaluateCheckerCompletionLocked()349     private int evaluateCheckerCompletionLocked() {
350         int state = COMPLETED;
351         for (int i=0; i<mHandlerCheckers.size(); i++) {
352             HandlerChecker hc = mHandlerCheckers.get(i);
353             state = Math.max(state, hc.getCompletionStateLocked());
354         }
355         return state;
356     }
357 
getBlockedCheckersLocked()358     private ArrayList<HandlerChecker> getBlockedCheckersLocked() {
359         ArrayList<HandlerChecker> checkers = new ArrayList<HandlerChecker>();
360         for (int i=0; i<mHandlerCheckers.size(); i++) {
361             HandlerChecker hc = mHandlerCheckers.get(i);
362             if (hc.isOverdueLocked()) {
363                 checkers.add(hc);
364             }
365         }
366         return checkers;
367     }
368 
describeCheckersLocked(List<HandlerChecker> checkers)369     private String describeCheckersLocked(List<HandlerChecker> checkers) {
370         StringBuilder builder = new StringBuilder(128);
371         for (int i=0; i<checkers.size(); i++) {
372             if (builder.length() > 0) {
373                 builder.append(", ");
374             }
375             builder.append(checkers.get(i).describeBlockedStateLocked());
376         }
377         return builder.toString();
378     }
379 
getInterestingHalPids()380     private ArrayList<Integer> getInterestingHalPids() {
381         try {
382             IServiceManager serviceManager = IServiceManager.getService();
383             ArrayList<IServiceManager.InstanceDebugInfo> dump =
384                     serviceManager.debugDump();
385             HashSet<Integer> pids = new HashSet<>();
386             for (IServiceManager.InstanceDebugInfo info : dump) {
387                 if (info.pid == IServiceManager.PidConstant.NO_PID) {
388                     continue;
389                 }
390 
391                 if (!HAL_INTERFACES_OF_INTEREST.contains(info.interfaceName)) {
392                     continue;
393                 }
394 
395                 pids.add(info.pid);
396             }
397             return new ArrayList<Integer>(pids);
398         } catch (RemoteException e) {
399             return new ArrayList<Integer>();
400         }
401     }
402 
getInterestingNativePids()403     private ArrayList<Integer> getInterestingNativePids() {
404         ArrayList<Integer> pids = getInterestingHalPids();
405 
406         int[] nativePids = Process.getPidsForCommands(NATIVE_STACKS_OF_INTEREST);
407         if (nativePids != null) {
408             pids.ensureCapacity(pids.size() + nativePids.length);
409             for (int i : nativePids) {
410                 pids.add(i);
411             }
412         }
413 
414         return pids;
415     }
416 
417     @Override
run()418     public void run() {
419         boolean waitedHalf = false;
420         while (true) {
421             final List<HandlerChecker> blockedCheckers;
422             final String subject;
423             final boolean allowRestart;
424             int debuggerWasConnected = 0;
425             synchronized (this) {
426                 long timeout = CHECK_INTERVAL;
427                 // Make sure we (re)spin the checkers that have become idle within
428                 // this wait-and-check interval
429                 for (int i=0; i<mHandlerCheckers.size(); i++) {
430                     HandlerChecker hc = mHandlerCheckers.get(i);
431                     hc.scheduleCheckLocked();
432                 }
433 
434                 if (debuggerWasConnected > 0) {
435                     debuggerWasConnected--;
436                 }
437 
438                 // NOTE: We use uptimeMillis() here because we do not want to increment the time we
439                 // wait while asleep. If the device is asleep then the thing that we are waiting
440                 // to timeout on is asleep as well and won't have a chance to run, causing a false
441                 // positive on when to kill things.
442                 long start = SystemClock.uptimeMillis();
443                 while (timeout > 0) {
444                     if (Debug.isDebuggerConnected()) {
445                         debuggerWasConnected = 2;
446                     }
447                     try {
448                         wait(timeout);
449                     } catch (InterruptedException e) {
450                         Log.wtf(TAG, e);
451                     }
452                     if (Debug.isDebuggerConnected()) {
453                         debuggerWasConnected = 2;
454                     }
455                     timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start);
456                 }
457 
458                 boolean fdLimitTriggered = false;
459                 if (mOpenFdMonitor != null) {
460                     fdLimitTriggered = mOpenFdMonitor.monitor();
461                 }
462 
463                 if (!fdLimitTriggered) {
464                     final int waitState = evaluateCheckerCompletionLocked();
465                     if (waitState == COMPLETED) {
466                         // The monitors have returned; reset
467                         waitedHalf = false;
468                         continue;
469                     } else if (waitState == WAITING) {
470                         // still waiting but within their configured intervals; back off and recheck
471                         continue;
472                     } else if (waitState == WAITED_HALF) {
473                         if (!waitedHalf) {
474                             // We've waited half the deadlock-detection interval.  Pull a stack
475                             // trace and wait another half.
476                             ArrayList<Integer> pids = new ArrayList<Integer>();
477                             pids.add(Process.myPid());
478                             ActivityManagerService.dumpStackTraces(true, pids, null, null,
479                                 getInterestingNativePids());
480                             waitedHalf = true;
481                         }
482                         continue;
483                     }
484 
485                     // something is overdue!
486                     blockedCheckers = getBlockedCheckersLocked();
487                     subject = describeCheckersLocked(blockedCheckers);
488                 } else {
489                     blockedCheckers = Collections.emptyList();
490                     subject = "Open FD high water mark reached";
491                 }
492                 allowRestart = mAllowRestart;
493             }
494 
495             // If we got here, that means that the system is most likely hung.
496             // First collect stack traces from all threads of the system process.
497             // Then kill this process so that the system will restart.
498             EventLog.writeEvent(EventLogTags.WATCHDOG, subject);
499 
500             ArrayList<Integer> pids = new ArrayList<>();
501             pids.add(Process.myPid());
502             if (mPhonePid > 0) pids.add(mPhonePid);
503             // Pass !waitedHalf so that just in case we somehow wind up here without having
504             // dumped the halfway stacks, we properly re-initialize the trace file.
505             final File stack = ActivityManagerService.dumpStackTraces(
506                     !waitedHalf, pids, null, null, getInterestingNativePids());
507 
508             // Give some extra time to make sure the stack traces get written.
509             // The system's been hanging for a minute, another second or two won't hurt much.
510             SystemClock.sleep(2000);
511 
512             // Pull our own kernel thread stacks as well if we're configured for that
513             if (RECORD_KERNEL_THREADS) {
514                 dumpKernelStackTraces();
515             }
516 
517             // Trigger the kernel to dump all blocked threads, and backtraces on all CPUs to the kernel log
518             doSysRq('w');
519             doSysRq('l');
520 
521             // Try to add the error to the dropbox, but assuming that the ActivityManager
522             // itself may be deadlocked.  (which has happened, causing this statement to
523             // deadlock and the watchdog as a whole to be ineffective)
524             Thread dropboxThread = new Thread("watchdogWriteToDropbox") {
525                     public void run() {
526                         mActivity.addErrorToDropBox(
527                                 "watchdog", null, "system_server", null, null,
528                                 subject, null, stack, null);
529                     }
530                 };
531             dropboxThread.start();
532             try {
533                 dropboxThread.join(2000);  // wait up to 2 seconds for it to return.
534             } catch (InterruptedException ignored) {}
535 
536             IActivityController controller;
537             synchronized (this) {
538                 controller = mController;
539             }
540             if (controller != null) {
541                 Slog.i(TAG, "Reporting stuck state to activity controller");
542                 try {
543                     Binder.setDumpDisabled("Service dumps disabled due to hung system process.");
544                     // 1 = keep waiting, -1 = kill system
545                     int res = controller.systemNotResponding(subject);
546                     if (res >= 0) {
547                         Slog.i(TAG, "Activity controller requested to coninue to wait");
548                         waitedHalf = false;
549                         continue;
550                     }
551                 } catch (RemoteException e) {
552                 }
553             }
554 
555             // Only kill the process if the debugger is not attached.
556             if (Debug.isDebuggerConnected()) {
557                 debuggerWasConnected = 2;
558             }
559             if (debuggerWasConnected >= 2) {
560                 Slog.w(TAG, "Debugger connected: Watchdog is *not* killing the system process");
561             } else if (debuggerWasConnected > 0) {
562                 Slog.w(TAG, "Debugger was connected: Watchdog is *not* killing the system process");
563             } else if (!allowRestart) {
564                 Slog.w(TAG, "Restart not allowed: Watchdog is *not* killing the system process");
565             } else {
566                 Slog.w(TAG, "*** WATCHDOG KILLING SYSTEM PROCESS: " + subject);
567                 for (int i=0; i<blockedCheckers.size(); i++) {
568                     Slog.w(TAG, blockedCheckers.get(i).getName() + " stack trace:");
569                     StackTraceElement[] stackTrace
570                             = blockedCheckers.get(i).getThread().getStackTrace();
571                     for (StackTraceElement element: stackTrace) {
572                         Slog.w(TAG, "    at " + element);
573                     }
574                 }
575                 Slog.w(TAG, "*** GOODBYE!");
576                 Process.killProcess(Process.myPid());
577                 System.exit(10);
578             }
579 
580             waitedHalf = false;
581         }
582     }
583 
doSysRq(char c)584     private void doSysRq(char c) {
585         try {
586             FileWriter sysrq_trigger = new FileWriter("/proc/sysrq-trigger");
587             sysrq_trigger.write(c);
588             sysrq_trigger.close();
589         } catch (IOException e) {
590             Slog.w(TAG, "Failed to write to /proc/sysrq-trigger", e);
591         }
592     }
593 
dumpKernelStackTraces()594     private File dumpKernelStackTraces() {
595         String tracesPath = SystemProperties.get("dalvik.vm.stack-trace-file", null);
596         if (tracesPath == null || tracesPath.length() == 0) {
597             return null;
598         }
599 
600         native_dumpKernelStacks(tracesPath);
601         return new File(tracesPath);
602     }
603 
native_dumpKernelStacks(String tracesPath)604     private native void native_dumpKernelStacks(String tracesPath);
605 
606     public static final class OpenFdMonitor {
607         /**
608          * Number of FDs below the soft limit that we trigger a runtime restart at. This was
609          * chosen arbitrarily, but will need to be at least 6 in order to have a sufficient number
610          * of FDs in reserve to complete a dump.
611          */
612         private static final int FD_HIGH_WATER_MARK = 12;
613 
614         private final File mDumpDir;
615         private final File mFdHighWaterMark;
616 
create()617         public static OpenFdMonitor create() {
618             // Only run the FD monitor on debuggable builds (such as userdebug and eng builds).
619             if (!Build.IS_DEBUGGABLE) {
620                 return null;
621             }
622 
623             // Don't run the FD monitor on builds that have a global ANR trace file. We're using
624             // the ANR trace directory as a quick hack in order to get these traces in bugreports
625             // and we wouldn't want to overwrite something important.
626             final String dumpDirStr = SystemProperties.get("dalvik.vm.stack-trace-dir", "");
627             if (dumpDirStr.isEmpty()) {
628                 return null;
629             }
630 
631             final StructRlimit rlimit;
632             try {
633                 rlimit = android.system.Os.getrlimit(OsConstants.RLIMIT_NOFILE);
634             } catch (ErrnoException errno) {
635                 Slog.w(TAG, "Error thrown from getrlimit(RLIMIT_NOFILE)", errno);
636                 return null;
637             }
638 
639             // The assumption we're making here is that FD numbers are allocated (more or less)
640             // sequentially, which is currently (and historically) true since open is currently
641             // specified to always return the lowest-numbered non-open file descriptor for the
642             // current process.
643             //
644             // We do this to avoid having to enumerate the contents of /proc/self/fd in order to
645             // count the number of descriptors open in the process.
646             final File fdThreshold = new File("/proc/self/fd/" + (rlimit.rlim_cur - FD_HIGH_WATER_MARK));
647             return new OpenFdMonitor(new File(dumpDirStr), fdThreshold);
648         }
649 
OpenFdMonitor(File dumpDir, File fdThreshold)650         OpenFdMonitor(File dumpDir, File fdThreshold) {
651             mDumpDir = dumpDir;
652             mFdHighWaterMark = fdThreshold;
653         }
654 
dumpOpenDescriptors()655         private void dumpOpenDescriptors() {
656             try {
657                 File dumpFile = File.createTempFile("anr_fd_", "", mDumpDir);
658                 java.lang.Process proc = new ProcessBuilder()
659                     .command("/system/bin/lsof", "-p", String.valueOf(Process.myPid()))
660                     .redirectErrorStream(true)
661                     .redirectOutput(dumpFile)
662                     .start();
663 
664                 int returnCode = proc.waitFor();
665                 if (returnCode != 0) {
666                     Slog.w(TAG, "Unable to dump open descriptors, lsof return code: "
667                         + returnCode);
668                     dumpFile.delete();
669                 }
670             } catch (IOException | InterruptedException ex) {
671                 Slog.w(TAG, "Unable to dump open descriptors: " + ex);
672             }
673         }
674 
675         /**
676          * @return {@code true} if the high water mark was breached and a dump was written,
677          *     {@code false} otherwise.
678          */
monitor()679         public boolean monitor() {
680             if (mFdHighWaterMark.exists()) {
681                 dumpOpenDescriptors();
682                 return true;
683             }
684 
685             return false;
686         }
687     }
688 }
689