final void appNotResponding(
      ProcessRecord app,
      ActivityRecord activity,
      ActivityRecord parent,
      boolean aboveSystem,
      final String annotation) {
    ArrayList<Integer> firstPids = new ArrayList<Integer>(5);
    SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20);

    if (mService.mController != null) {
      try {
        // 0 == continue, -1 = kill process immediately
        int res = mService.mController.appEarlyNotResponding(app.processName, app.pid, annotation);
        if (res < 0 && app.pid != MY_PID) {
          app.kill("anr", true);
        }
      } catch (RemoteException e) {
        mService.mController = null;
        Watchdog.getInstance().setActivityController(null);
      }
    }

    long anrTime = SystemClock.uptimeMillis();
    if (ActivityManagerService.MONITOR_CPU_USAGE) {
      mService.updateCpuStatsNow();
    }

    synchronized (mService) {
      // PowerManager.reboot() can block for a long time, so ignore ANRs while shutting down.
      if (mService.mShuttingDown) {
        Slog.i(TAG, "During shutdown skipping ANR: " + app + " " + annotation);
        return;
      } else if (app.notResponding) {
        Slog.i(TAG, "Skipping duplicate ANR: " + app + " " + annotation);
        return;
      } else if (app.crashing) {
        Slog.i(TAG, "Crashing app skipping ANR: " + app + " " + annotation);
        return;
      } else if (app.killedByAm) {
        Slog.i(TAG, "App already killed by AM skipping ANR: " + app + " " + annotation);
        return;
      } else if (app.killed) {
        Slog.i(TAG, "Skipping died app ANR: " + app + " " + annotation);
        return;
      }

      // In case we come through here for the same app before completing
      // this one, mark as anring now so we will bail out.
      app.notResponding = true;

      // Log the ANR to the event log.
      EventLog.writeEvent(
          EventLogTags.AM_ANR, app.userId, app.pid, app.processName, app.info.flags, annotation);

      // Dump thread traces as quickly as we can, starting with "interesting" processes.
      firstPids.add(app.pid);

      int parentPid = app.pid;
      if (parent != null && parent.app != null && parent.app.pid > 0) {
        parentPid = parent.app.pid;
      }
      if (parentPid != app.pid) firstPids.add(parentPid);

      if (MY_PID != app.pid && MY_PID != parentPid) firstPids.add(MY_PID);

      for (int i = mService.mLruProcesses.size() - 1; i >= 0; i--) {
        ProcessRecord r = mService.mLruProcesses.get(i);
        if (r != null && r.thread != null) {
          int pid = r.pid;
          if (pid > 0 && pid != app.pid && pid != parentPid && pid != MY_PID) {
            if (r.persistent) {
              firstPids.add(pid);
              if (DEBUG_ANR) Slog.i(TAG, "Adding persistent proc: " + r);
            } else {
              lastPids.put(pid, Boolean.TRUE);
              if (DEBUG_ANR) Slog.i(TAG, "Adding ANR proc: " + r);
            }
          }
        }
      }
    }

    // Log the ANR to the main log.
    StringBuilder info = new StringBuilder();
    info.setLength(0);
    info.append("ANR in ").append(app.processName);
    if (activity != null && activity.shortComponentName != null) {
      info.append(" (").append(activity.shortComponentName).append(")");
    }
    info.append("\n");
    info.append("PID: ").append(app.pid).append("\n");
    if (annotation != null) {
      info.append("Reason: ").append(annotation).append("\n");
    }
    if (parent != null && parent != activity) {
      info.append("Parent: ").append(parent.shortComponentName).append("\n");
    }

    final ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);

    File tracesFile =
        mService.dumpStackTraces(
            true, firstPids, processCpuTracker, lastPids, NATIVE_STACKS_OF_INTEREST);

    String cpuInfo = null;
    if (ActivityManagerService.MONITOR_CPU_USAGE) {
      mService.updateCpuStatsNow();
      synchronized (mService.mProcessCpuTracker) {
        cpuInfo = mService.mProcessCpuTracker.printCurrentState(anrTime);
      }
      info.append(processCpuTracker.printCurrentLoad());
      info.append(cpuInfo);
    }

    info.append(processCpuTracker.printCurrentState(anrTime));

    Slog.e(TAG, info.toString());
    if (tracesFile == null) {
      // There is no trace file, so dump (only) the alleged culprit's threads to the log
      Process.sendSignal(app.pid, Process.SIGNAL_QUIT);
    }

    mService.addErrorToDropBox(
        "anr", app, app.processName, activity, parent, annotation, cpuInfo, tracesFile, null);

    if (mService.mController != null) {
      try {
        // 0 == show dialog, 1 = keep waiting, -1 = kill process immediately
        int res = mService.mController.appNotResponding(app.processName, app.pid, info.toString());
        if (res != 0) {
          if (res < 0 && app.pid != MY_PID) {
            app.kill("anr", true);
          } else {
            synchronized (mService) {
              mService.mServices.scheduleServiceTimeoutLocked(app);
            }
          }
          return;
        }
      } catch (RemoteException e) {
        mService.mController = null;
        Watchdog.getInstance().setActivityController(null);
      }
    }

    // Unless configured otherwise, swallow ANRs in background processes & kill the process.
    boolean showBackground =
        Settings.Secure.getInt(
                mContext.getContentResolver(), Settings.Secure.ANR_SHOW_BACKGROUND, 0)
            != 0;

    synchronized (mService) {
      mService.mBatteryStatsService.noteProcessAnr(app.processName, app.uid);

      if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) {
        app.kill("bg anr", true);
        return;
      }

      // Set the app's notResponding state, and look up the errorReportReceiver
      makeAppNotRespondingLocked(
          app,
          activity != null ? activity.shortComponentName : null,
          annotation != null ? "ANR " + annotation : "ANR",
          info.toString());

      // Bring up the infamous App Not Responding dialog
      Message msg = Message.obtain();
      HashMap<String, Object> map = new HashMap<String, Object>();
      msg.what = ActivityManagerService.SHOW_NOT_RESPONDING_UI_MSG;
      msg.obj = map;
      msg.arg1 = aboveSystem ? 1 : 0;
      map.put("app", app);
      if (activity != null) {
        map.put("activity", activity);
      }

      mService.mUiHandler.sendMessage(msg);
    }
  }