Take screenshot right away when invoked with -P.

Traditionally, the screenshot was not taken right away because it could
skew other services like the Surface Flinger, and such approach often
resulted on irrelevant screenshots.

Now there is more user-friendly workflow, where the bugreport progress
is shown in the UI and the user can take further actions. In this
workflow, it's ok to take the screeshot right away, since the other
features might already be causing interference: if the user doesn't want
interference, the old workflow should be used instead.

Since the new workflow requires the -P (for Progress) parameter, such
parameter can also be used to determine when to take the screenshot.

Also added progress statistics at the end of the bugreport.

BUG: 8420215
Change-Id: I9dad8ea59d47848f217182ccec0f527b42e07f50
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 812d6fa..dbc53cf 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -52,8 +52,6 @@
 static char cmdline_buf[16384] = "(unknown)";
 static const char *dump_traces_path = NULL;
 
-static std::string screenshot_path;
-
 #define PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops"
 
 #define RAFT_DIR "/data/misc/raft/"
@@ -272,7 +270,7 @@
 /* End copy from system/core/logd/LogBuffer.cpp */
 
 /* dumps the current system state to stdout */
-static void dumpstate() {
+static void dumpstate(std::string screenshot_path) {
     unsigned long timeout;
     time_t now = time(NULL);
     char build[PROPERTY_VALUE_MAX], fingerprint[PROPERTY_VALUE_MAX];
@@ -333,9 +331,8 @@
     for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS");
 
     if (!screenshot_path.empty()) {
-        ALOGI("taking screenshot\n");
-        const char *args[] = { "/system/bin/screencap", "-p", screenshot_path.c_str(), NULL };
-        run_command_always(NULL, 10, args);
+        ALOGI("taking late screenshot\n");
+        take_screenshot(screenshot_path);
         ALOGI("wrote screenshot: %s\n", screenshot_path.c_str());
     }
 
@@ -688,6 +685,7 @@
     int use_socket = 0;
     int do_fb = 0;
     int do_broadcast = 0;
+    int do_early_screenshot = 0;
 
     if (getuid() != 0) {
         // Old versions of the adb client would call the
@@ -743,6 +741,8 @@
         exit(1);
     }
 
+    do_early_screenshot = do_update_progress;
+
     // If we are going to use a socket, do it as early as possible
     // to avoid timeouts from bugreport.
     if (use_socket) {
@@ -755,6 +755,9 @@
     /* full path of the temporary file containing the bug report */
     std::string tmp_path;
 
+    /* full path of the temporary file containing the screenshot (when requested) */
+    std::string screenshot_path;
+
     /* base name (without suffix or extensions) of the bug report files */
     std::string base_name;
 
@@ -812,6 +815,16 @@
         }
     }
 
+    if (!screenshot_path.empty() && do_early_screenshot) {
+        ALOGI("taking early screenshot\n");
+        take_screenshot(screenshot_path);
+        ALOGI("wrote screenshot: %s\n", screenshot_path.c_str());
+        if (chown(screenshot_path.c_str(), AID_SHELL, AID_SHELL)) {
+            ALOGE("Unable to change ownership of screenshot file %s: %s\n",
+                    screenshot_path.c_str(), strerror(errno));
+        }
+    }
+
     /* read /proc/cmdline before dropping root */
     FILE *cmdline = fopen("/proc/cmdline", "re");
     if (cmdline) {
@@ -871,7 +884,7 @@
         redirect_to_file(stdout, const_cast<char*>(tmp_path.c_str()));
     }
 
-    dumpstate();
+    dumpstate(do_early_screenshot ? NULL : screenshot_path);
 
     /* done */
     if (vibrator) {
@@ -960,6 +973,7 @@
         }
     }
 
+    ALOGD("Final progress: %d/%d (originally %d)\n", progress, weight_total, WEIGHT_TOTAL);
     ALOGI("done\n");
 
     return 0;
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index c7e7f52..26e16d0 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -69,7 +69,7 @@
  * It would be better to take advantage of the C++ migration and encapsulate the state in an object,
  * but that will be better handled in a major C++ refactoring, which would also get rid of other C
  * idioms (like using std::string instead of char*, removing varargs, etc...) */
-extern int do_update_progress;
+extern int do_update_progress, progress, weight_total;
 
 /* prints the contents of a file */
 int dump_file(const char *title, const char *path);
@@ -139,6 +139,9 @@
 /* Implemented by libdumpstate_board to dump board-specific info */
 void dumpstate_board();
 
+/* Takes a screenshot and save it to the given file */
+void take_screenshot(std::string path);
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index a7637d8..5115e66 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -879,3 +879,8 @@
                 key, value, status);
     }
 }
+
+void take_screenshot(std::string path) {
+    const char *args[] = { "/system/bin/screencap", "-p", path.c_str(), NULL };
+    run_command_always(NULL, 10, args);
+}