dumpstate.cpp revision e9d2c5414c86949f6de12c4291a2d8c34b2d0b34
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#define LOG_TAG "dumpstate"
17
18#include <dirent.h>
19#include <errno.h>
20#include <fcntl.h>
21#include <libgen.h>
22#include <limits.h>
23#include <memory>
24#include <regex>
25#include <set>
26#include <signal.h>
27#include <stdbool.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string>
31#include <string.h>
32#include <sys/prctl.h>
33#include <sys/resource.h>
34#include <sys/stat.h>
35#include <sys/time.h>
36#include <sys/wait.h>
37#include <unistd.h>
38
39#include <android-base/file.h>
40#include <android-base/properties.h>
41#include <android-base/stringprintf.h>
42#include <android-base/strings.h>
43#include <android-base/unique_fd.h>
44#include <cutils/properties.h>
45#include <hardware_legacy/power.h>
46#include <openssl/sha.h>
47#include <private/android_filesystem_config.h>
48#include <private/android_logger.h>
49
50#include "DumpstateService.h"
51#include "dumpstate.h"
52
53/* read before root is shed */
54static char cmdline_buf[16384] = "(unknown)";
55static const char *dump_traces_path = NULL;
56
57// TODO: variables and functions below should be part of dumpstate object
58
59static std::set<std::string> mount_points;
60void add_mountinfo();
61
62#define PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops"
63#define ALT_PSTORE_LAST_KMSG "/sys/fs/pstore/console-ramoops-0"
64
65#define RAFT_DIR "/data/misc/raft"
66#define RECOVERY_DIR "/cache/recovery"
67#define RECOVERY_DATA_DIR "/data/misc/recovery"
68#define LOGPERSIST_DATA_DIR "/data/misc/logd"
69#define PROFILE_DATA_DIR_CUR "/data/misc/profiles/cur"
70#define PROFILE_DATA_DIR_REF "/data/misc/profiles/ref"
71#define TOMBSTONE_DIR "/data/tombstones"
72#define TOMBSTONE_FILE_PREFIX TOMBSTONE_DIR "/tombstone_"
73/* Can accomodate a tombstone number up to 9999. */
74#define TOMBSTONE_MAX_LEN (sizeof(TOMBSTONE_FILE_PREFIX) + 4)
75#define NUM_TOMBSTONES  10
76#define WLUTIL "/vendor/xbin/wlutil"
77#define WAKE_LOCK_NAME "dumpstate_wakelock"
78
79typedef struct {
80  char name[TOMBSTONE_MAX_LEN];
81  int fd;
82} tombstone_data_t;
83
84static tombstone_data_t tombstone_data[NUM_TOMBSTONES];
85
86// TODO: temporary variables and functions used during C++ refactoring
87static Dumpstate& ds = Dumpstate::GetInstance();
88static int RunCommand(const std::string& title, const std::vector<std::string>& fullCommand,
89                      const CommandOptions& options = CommandOptions::DEFAULT) {
90    return ds.RunCommand(title, fullCommand, options);
91}
92static void RunDumpsys(const std::string& title, const std::vector<std::string>& dumpsysArgs,
93                       const CommandOptions& options = CommandOptions::DEFAULT_DUMPSYS,
94                       long dumpsysTimeout = 0) {
95    return ds.RunDumpsys(title, dumpsysArgs, options, dumpsysTimeout);
96}
97static int DumpFile(const std::string& title, const std::string& path) {
98    return ds.DumpFile(title, path);
99}
100bool IsUserBuild() {
101    return ds.IsUserBuild();
102}
103
104// Relative directory (inside the zip) for all files copied as-is into the bugreport.
105static const std::string ZIP_ROOT_DIR = "FS";
106
107static constexpr char PROPERTY_EXTRA_OPTIONS[] = "dumpstate.options";
108static constexpr char PROPERTY_LAST_ID[] = "dumpstate.last_id";
109static constexpr char PROPERTY_VERSION[] = "dumpstate.version";
110
111/* gets the tombstone data, according to the bugreport type: if zipped, gets all tombstones;
112 * otherwise, gets just those modified in the last half an hour. */
113static void get_tombstone_fds(tombstone_data_t data[NUM_TOMBSTONES]) {
114    time_t thirty_minutes_ago = ds.now_ - 60 * 30;
115    for (size_t i = 0; i < NUM_TOMBSTONES; i++) {
116        snprintf(data[i].name, sizeof(data[i].name), "%s%02zu", TOMBSTONE_FILE_PREFIX, i);
117        int fd = TEMP_FAILURE_RETRY(open(data[i].name,
118                                         O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
119        struct stat st;
120        if (fstat(fd, &st) == 0 && S_ISREG(st.st_mode) && st.st_size > 0 &&
121            (ds.IsZipping() || st.st_mtime >= thirty_minutes_ago)) {
122            data[i].fd = fd;
123        } else {
124            close(fd);
125            data[i].fd = -1;
126        }
127    }
128}
129
130// for_each_pid() callback to get mount info about a process.
131void do_mountinfo(int pid, const char* name __attribute__((unused))) {
132    char path[PATH_MAX];
133
134    // Gets the the content of the /proc/PID/ns/mnt link, so only unique mount points
135    // are added.
136    snprintf(path, sizeof(path), "/proc/%d/ns/mnt", pid);
137    char linkname[PATH_MAX];
138    ssize_t r = readlink(path, linkname, PATH_MAX);
139    if (r == -1) {
140        MYLOGE("Unable to read link for %s: %s\n", path, strerror(errno));
141        return;
142    }
143    linkname[r] = '\0';
144
145    if (mount_points.find(linkname) == mount_points.end()) {
146        // First time this mount point was found: add it
147        snprintf(path, sizeof(path), "/proc/%d/mountinfo", pid);
148        if (ds.AddZipEntry(ZIP_ROOT_DIR + path, path)) {
149            mount_points.insert(linkname);
150        } else {
151            MYLOGE("Unable to add mountinfo %s to zip file\n", path);
152        }
153    }
154}
155
156void add_mountinfo() {
157    if (!ds.IsZipping()) return;
158    std::string title = "MOUNT INFO";
159    mount_points.clear();
160    DurationReporter duration_reporter(title, nullptr);
161    for_each_pid(do_mountinfo, nullptr);
162    MYLOGD("%s: %d entries added to zip file\n", title.c_str(), (int)mount_points.size());
163}
164
165static void dump_dev_files(const char *title, const char *driverpath, const char *filename)
166{
167    DIR *d;
168    struct dirent *de;
169    char path[PATH_MAX];
170
171    d = opendir(driverpath);
172    if (d == NULL) {
173        return;
174    }
175
176    while ((de = readdir(d))) {
177        if (de->d_type != DT_LNK) {
178            continue;
179        }
180        snprintf(path, sizeof(path), "%s/%s/%s", driverpath, de->d_name, filename);
181        DumpFile(title, path);
182    }
183
184    closedir(d);
185}
186
187// return pid of a userspace process. If not found or error, return 0.
188static unsigned int pid_of_process(const char* ps_name) {
189    DIR *proc_dir;
190    struct dirent *ps;
191    unsigned int pid;
192    std::string cmdline;
193
194    if (!(proc_dir = opendir("/proc"))) {
195        MYLOGE("Can't open /proc\n");
196        return 0;
197    }
198
199    while ((ps = readdir(proc_dir))) {
200        if (!(pid = atoi(ps->d_name))) {
201            continue;
202        }
203        android::base::ReadFileToString("/proc/"
204                + std::string(ps->d_name) + "/cmdline", &cmdline);
205        if (cmdline.find(ps_name) == std::string::npos) {
206            continue;
207        } else {
208            closedir(proc_dir);
209            return pid;
210        }
211    }
212    closedir(proc_dir);
213    return 0;
214}
215
216// dump anrd's trace and add to the zip file.
217// 1. check if anrd is running on this device.
218// 2. send a SIGUSR1 to its pid which will dump anrd's trace.
219// 3. wait until the trace generation completes and add to the zip file.
220static bool dump_anrd_trace() {
221    unsigned int pid;
222    char buf[50], path[PATH_MAX];
223    struct dirent *trace;
224    struct stat st;
225    DIR *trace_dir;
226    int retry = 5;
227    long max_ctime = 0, old_mtime;
228    long long cur_size = 0;
229    const char *trace_path = "/data/misc/anrd/";
230
231    if (!ds.IsZipping()) {
232        MYLOGE("Not dumping anrd trace because it's not a zipped bugreport\n");
233        return false;
234    }
235
236    // find anrd's pid if it is running.
237    pid = pid_of_process("/system/xbin/anrd");
238
239    if (pid > 0) {
240        if (stat(trace_path, &st) == 0) {
241            old_mtime = st.st_mtime;
242        } else {
243            MYLOGE("Failed to find: %s\n", trace_path);
244            return false;
245        }
246
247        // send SIGUSR1 to the anrd to generate a trace.
248        sprintf(buf, "%u", pid);
249        if (RunCommand("ANRD_DUMP", {"kill", "-SIGUSR1", buf},
250                       CommandOptions::WithTimeout(1).Build())) {
251            MYLOGE("anrd signal timed out. Please manually collect trace\n");
252            return false;
253        }
254
255        while (retry-- > 0 && old_mtime == st.st_mtime) {
256            sleep(1);
257            stat(trace_path, &st);
258        }
259
260        if (retry < 0 && old_mtime == st.st_mtime) {
261            MYLOGE("Failed to stat %s or trace creation timeout\n", trace_path);
262            return false;
263        }
264
265        // identify the trace file by its creation time.
266        if (!(trace_dir = opendir(trace_path))) {
267            MYLOGE("Can't open trace file under %s\n", trace_path);
268        }
269        while ((trace = readdir(trace_dir))) {
270            if (strcmp(trace->d_name, ".") == 0
271                    || strcmp(trace->d_name, "..") == 0) {
272                continue;
273            }
274            sprintf(path, "%s%s", trace_path, trace->d_name);
275            if (stat(path, &st) == 0) {
276                if (st.st_ctime > max_ctime) {
277                    max_ctime = st.st_ctime;
278                    sprintf(buf, "%s", trace->d_name);
279                }
280            }
281        }
282        closedir(trace_dir);
283
284        // Wait until the dump completes by checking the size of the trace.
285        if (max_ctime > 0) {
286            sprintf(path, "%s%s", trace_path, buf);
287            while(true) {
288                sleep(1);
289                if (stat(path, &st) == 0) {
290                    if (st.st_size == cur_size) {
291                        break;
292                    } else if (st.st_size > cur_size) {
293                        cur_size = st.st_size;
294                    } else {
295                        return false;
296                    }
297                } else {
298                    MYLOGE("Cant stat() %s anymore\n", path);
299                    return false;
300                }
301            }
302            // Add to the zip file.
303            if (!ds.AddZipEntry("anrd_trace.txt", path)) {
304                MYLOGE("Unable to add anrd_trace file %s to zip file\n", path);
305            } else {
306                if (remove(path)) {
307                    MYLOGE("Error removing anrd_trace file %s: %s", path, strerror(errno));
308                }
309                return true;
310            }
311        } else {
312            MYLOGE("Can't stats any trace file under %s\n", trace_path);
313        }
314    }
315    return false;
316}
317
318static void dump_systrace() {
319    if (!ds.IsZipping()) {
320        MYLOGD("Not dumping systrace because it's not a zipped bugreport\n");
321        return;
322    }
323    std::string systrace_path = ds.GetPath("-systrace.txt");
324    if (systrace_path.empty()) {
325        MYLOGE("Not dumping systrace because path is empty\n");
326        return;
327    }
328    const char* path = "/sys/kernel/debug/tracing/tracing_on";
329    long int is_tracing;
330    if (read_file_as_long(path, &is_tracing)) {
331        return; // error already logged
332    }
333    if (is_tracing <= 0) {
334        MYLOGD("Skipping systrace because '%s' content is '%ld'\n", path, is_tracing);
335        return;
336    }
337
338    MYLOGD("Running '/system/bin/atrace --async_dump -o %s', which can take several minutes",
339            systrace_path.c_str());
340    if (RunCommand("SYSTRACE", {"/system/bin/atrace", "--async_dump", "-o", systrace_path},
341                   CommandOptions::WithTimeout(120).Build())) {
342        MYLOGE("systrace timed out, its zip entry will be incomplete\n");
343        // TODO: RunCommand tries to kill the process, but atrace doesn't die
344        // peacefully; ideally, we should call strace to stop itself, but there is no such option
345        // yet (just a --async_stop, which stops and dump
346        // if (RunCommand("SYSTRACE", {"/system/bin/atrace", "--kill"})) {
347        //   MYLOGE("could not stop systrace ");
348        // }
349    }
350    if (!ds.AddZipEntry("systrace.txt", systrace_path)) {
351        MYLOGE("Unable to add systrace file %s to zip file\n", systrace_path.c_str());
352    } else {
353        if (remove(systrace_path.c_str())) {
354            MYLOGE("Error removing systrace file %s: %s", systrace_path.c_str(), strerror(errno));
355        }
356    }
357}
358
359static void dump_raft() {
360    if (IsUserBuild()) {
361        return;
362    }
363
364    std::string raft_path = ds.GetPath("-raft_log.txt");
365    if (raft_path.empty()) {
366        MYLOGD("raft_path is empty\n");
367        return;
368    }
369
370    struct stat s;
371    if (stat(RAFT_DIR, &s) != 0 || !S_ISDIR(s.st_mode)) {
372        MYLOGD("%s does not exist or is not a directory\n", RAFT_DIR);
373        return;
374    }
375
376    CommandOptions options = CommandOptions::WithTimeout(600).Build();
377    if (!ds.IsZipping()) {
378        // Write compressed and encoded raft logs to stdout if it's not a zipped bugreport.
379        RunCommand("RAFT LOGS", {"logcompressor", "-r", RAFT_DIR}, options);
380        return;
381    }
382
383    RunCommand("RAFT LOGS", {"logcompressor", "-n", "-r", RAFT_DIR, "-o", raft_path}, options);
384    if (!ds.AddZipEntry("raft_log.txt", raft_path)) {
385        MYLOGE("Unable to add raft log %s to zip file\n", raft_path.c_str());
386    } else {
387        if (remove(raft_path.c_str())) {
388            MYLOGE("Error removing raft file %s: %s\n", raft_path.c_str(), strerror(errno));
389        }
390    }
391}
392
393/**
394 * Finds the last modified file in the directory dir whose name starts with file_prefix.
395 *
396 * Function returns empty string when it does not find a file
397 */
398static std::string GetLastModifiedFileWithPrefix(const std::string& dir,
399                                                 const std::string& file_prefix) {
400    std::unique_ptr<DIR, decltype(&closedir)> d(opendir(dir.c_str()), closedir);
401    if (d == nullptr) {
402        MYLOGD("Error %d opening %s\n", errno, dir.c_str());
403        return "";
404    }
405
406    // Find the newest file matching the file_prefix in dir
407    struct dirent *de;
408    time_t last_modified_time = 0;
409    std::string last_modified_file = "";
410    struct stat s;
411
412    while ((de = readdir(d.get()))) {
413        std::string file = std::string(de->d_name);
414        if (!file_prefix.empty()) {
415            if (!android::base::StartsWith(file, file_prefix.c_str())) continue;
416        }
417        file = dir + "/" + file;
418        int ret = stat(file.c_str(), &s);
419
420        if ((ret == 0) && (s.st_mtime > last_modified_time)) {
421            last_modified_file = file;
422            last_modified_time = s.st_mtime;
423        }
424    }
425
426    return last_modified_file;
427}
428
429static void DumpModemLogs() {
430    DurationReporter durationReporter("DUMP MODEM LOGS");
431    if (IsUserBuild()) {
432        return;
433    }
434
435    if (!ds.IsZipping()) {
436        MYLOGD("Not dumping modem logs. dumpstate is not generating a zipping bugreport\n");
437        return;
438    }
439
440    std::string file_prefix = android::base::GetProperty("ro.radio.log_prefix", "");
441
442    if(file_prefix.empty()) {
443        MYLOGD("No modem log : file_prefix is empty\n");
444        return;
445    }
446
447    // TODO: use bugreport_dir_ directly when this function is moved to Dumpstate class
448    std::string bugreport_dir = dirname(ds.GetPath("").c_str());
449    MYLOGD("DumpModemLogs: directory is %s and file_prefix is %s\n",
450           bugreport_dir.c_str(), file_prefix.c_str());
451
452    std::string modem_log_file = GetLastModifiedFileWithPrefix(bugreport_dir, file_prefix);
453
454    struct stat s;
455    if (modem_log_file.empty() || stat(modem_log_file.c_str(), &s) != 0) {
456        MYLOGD("Modem log %s does not exist\n", modem_log_file.c_str());
457        return;
458    }
459
460    std::string filename = basename(modem_log_file.c_str());
461    if (!ds.AddZipEntry(filename, modem_log_file)) {
462        MYLOGE("Unable to add modem log %s to zip file\n", modem_log_file.c_str());
463    } else {
464        MYLOGD("Modem Log %s is added to zip\n", modem_log_file.c_str());
465    }
466}
467
468static bool skip_not_stat(const char *path) {
469    static const char stat[] = "/stat";
470    size_t len = strlen(path);
471    if (path[len - 1] == '/') { /* Directory? */
472        return false;
473    }
474    return strcmp(path + len - sizeof(stat) + 1, stat); /* .../stat? */
475}
476
477static bool skip_none(const char* path __attribute__((unused))) {
478    return false;
479}
480
481static const char mmcblk0[] = "/sys/block/mmcblk0/";
482unsigned long worst_write_perf = 20000; /* in KB/s */
483
484//
485//  stat offsets
486// Name            units         description
487// ----            -----         -----------
488// read I/Os       requests      number of read I/Os processed
489#define __STAT_READ_IOS      0
490// read merges     requests      number of read I/Os merged with in-queue I/O
491#define __STAT_READ_MERGES   1
492// read sectors    sectors       number of sectors read
493#define __STAT_READ_SECTORS  2
494// read ticks      milliseconds  total wait time for read requests
495#define __STAT_READ_TICKS    3
496// write I/Os      requests      number of write I/Os processed
497#define __STAT_WRITE_IOS     4
498// write merges    requests      number of write I/Os merged with in-queue I/O
499#define __STAT_WRITE_MERGES  5
500// write sectors   sectors       number of sectors written
501#define __STAT_WRITE_SECTORS 6
502// write ticks     milliseconds  total wait time for write requests
503#define __STAT_WRITE_TICKS   7
504// in_flight       requests      number of I/Os currently in flight
505#define __STAT_IN_FLIGHT     8
506// io_ticks        milliseconds  total time this block device has been active
507#define __STAT_IO_TICKS      9
508// time_in_queue   milliseconds  total wait time for all requests
509#define __STAT_IN_QUEUE     10
510#define __STAT_NUMBER_FIELD 11
511//
512// read I/Os, write I/Os
513// =====================
514//
515// These values increment when an I/O request completes.
516//
517// read merges, write merges
518// =========================
519//
520// These values increment when an I/O request is merged with an
521// already-queued I/O request.
522//
523// read sectors, write sectors
524// ===========================
525//
526// These values count the number of sectors read from or written to this
527// block device.  The "sectors" in question are the standard UNIX 512-byte
528// sectors, not any device- or filesystem-specific block size.  The
529// counters are incremented when the I/O completes.
530#define SECTOR_SIZE 512
531//
532// read ticks, write ticks
533// =======================
534//
535// These values count the number of milliseconds that I/O requests have
536// waited on this block device.  If there are multiple I/O requests waiting,
537// these values will increase at a rate greater than 1000/second; for
538// example, if 60 read requests wait for an average of 30 ms, the read_ticks
539// field will increase by 60*30 = 1800.
540//
541// in_flight
542// =========
543//
544// This value counts the number of I/O requests that have been issued to
545// the device driver but have not yet completed.  It does not include I/O
546// requests that are in the queue but not yet issued to the device driver.
547//
548// io_ticks
549// ========
550//
551// This value counts the number of milliseconds during which the device has
552// had I/O requests queued.
553//
554// time_in_queue
555// =============
556//
557// This value counts the number of milliseconds that I/O requests have waited
558// on this block device.  If there are multiple I/O requests waiting, this
559// value will increase as the product of the number of milliseconds times the
560// number of requests waiting (see "read ticks" above for an example).
561#define S_TO_MS 1000
562//
563
564static int dump_stat_from_fd(const char *title __unused, const char *path, int fd) {
565    unsigned long long fields[__STAT_NUMBER_FIELD];
566    bool z;
567    char *cp, *buffer = NULL;
568    size_t i = 0;
569    FILE *fp = fdopen(fd, "rb");
570    getline(&buffer, &i, fp);
571    fclose(fp);
572    if (!buffer) {
573        return -errno;
574    }
575    i = strlen(buffer);
576    while ((i > 0) && (buffer[i - 1] == '\n')) {
577        buffer[--i] = '\0';
578    }
579    if (!*buffer) {
580        free(buffer);
581        return 0;
582    }
583    z = true;
584    for (cp = buffer, i = 0; i < (sizeof(fields) / sizeof(fields[0])); ++i) {
585        fields[i] = strtoull(cp, &cp, 10);
586        if (fields[i] != 0) {
587            z = false;
588        }
589    }
590    if (z) { /* never accessed */
591        free(buffer);
592        return 0;
593    }
594
595    if (!strncmp(path, mmcblk0, sizeof(mmcblk0) - 1)) {
596        path += sizeof(mmcblk0) - 1;
597    }
598
599    printf("%s: %s\n", path, buffer);
600    free(buffer);
601
602    if (fields[__STAT_IO_TICKS]) {
603        unsigned long read_perf = 0;
604        unsigned long read_ios = 0;
605        if (fields[__STAT_READ_TICKS]) {
606            unsigned long long divisor = fields[__STAT_READ_TICKS]
607                                       * fields[__STAT_IO_TICKS];
608            read_perf = ((unsigned long long)SECTOR_SIZE
609                           * fields[__STAT_READ_SECTORS]
610                           * fields[__STAT_IN_QUEUE] + (divisor >> 1))
611                                        / divisor;
612            read_ios = ((unsigned long long)S_TO_MS * fields[__STAT_READ_IOS]
613                           * fields[__STAT_IN_QUEUE] + (divisor >> 1))
614                                        / divisor;
615        }
616
617        unsigned long write_perf = 0;
618        unsigned long write_ios = 0;
619        if (fields[__STAT_WRITE_TICKS]) {
620            unsigned long long divisor = fields[__STAT_WRITE_TICKS]
621                                       * fields[__STAT_IO_TICKS];
622            write_perf = ((unsigned long long)SECTOR_SIZE
623                           * fields[__STAT_WRITE_SECTORS]
624                           * fields[__STAT_IN_QUEUE] + (divisor >> 1))
625                                        / divisor;
626            write_ios = ((unsigned long long)S_TO_MS * fields[__STAT_WRITE_IOS]
627                           * fields[__STAT_IN_QUEUE] + (divisor >> 1))
628                                        / divisor;
629        }
630
631        unsigned queue = (fields[__STAT_IN_QUEUE]
632                             + (fields[__STAT_IO_TICKS] >> 1))
633                                 / fields[__STAT_IO_TICKS];
634
635        if (!write_perf && !write_ios) {
636            printf("%s: perf(ios) rd: %luKB/s(%lu/s) q: %u\n",
637                   path, read_perf, read_ios, queue);
638        } else {
639            printf("%s: perf(ios) rd: %luKB/s(%lu/s) wr: %luKB/s(%lu/s) q: %u\n",
640                   path, read_perf, read_ios, write_perf, write_ios, queue);
641        }
642
643        /* bugreport timeout factor adjustment */
644        if ((write_perf > 1) && (write_perf < worst_write_perf)) {
645            worst_write_perf = write_perf;
646        }
647    }
648    return 0;
649}
650
651/* timeout in ms */
652static unsigned long logcat_timeout(const char *name) {
653    log_id_t id = android_name_to_log_id(name);
654    unsigned long property_size = __android_logger_get_buffer_size(id);
655    /* Engineering margin is ten-fold our guess */
656    return 10 * (property_size + worst_write_perf) / worst_write_perf;
657}
658
659void Dumpstate::PrintHeader() const {
660    std::string build, fingerprint, radio, bootloader, network;
661    char date[80];
662
663    build = android::base::GetProperty("ro.build.display.id", "(unknown)");
664    fingerprint = android::base::GetProperty("ro.build.fingerprint", "(unknown)");
665    radio = android::base::GetProperty("gsm.version.baseband", "(unknown)");
666    bootloader = android::base::GetProperty("ro.bootloader", "(unknown)");
667    network = android::base::GetProperty("gsm.operator.alpha", "(unknown)");
668    strftime(date, sizeof(date), "%Y-%m-%d %H:%M:%S", localtime(&now_));
669
670    printf("========================================================\n");
671    printf("== dumpstate: %s\n", date);
672    printf("========================================================\n");
673
674    printf("\n");
675    printf("Build: %s\n", build.c_str());
676    // NOTE: fingerprint entry format is important for other tools.
677    printf("Build fingerprint: '%s'\n", fingerprint.c_str());
678    printf("Bootloader: %s\n", bootloader.c_str());
679    printf("Radio: %s\n", radio.c_str());
680    printf("Network: %s\n", network.c_str());
681
682    printf("Kernel: ");
683    JustDumpFile("", "/proc/version");
684    printf("Command line: %s\n", strtok(cmdline_buf, "\n"));
685    printf("Bugreport format version: %s\n", version_.c_str());
686    printf("Dumpstate info: id=%d pid=%d dry_run=%d args=%s extra_options=%s\n", id_, pid_,
687           dry_run_, args_.c_str(), extra_options_.c_str());
688    printf("\n");
689}
690
691// List of file extensions that can cause a zip file attachment to be rejected by some email
692// service providers.
693static const std::set<std::string> PROBLEMATIC_FILE_EXTENSIONS = {
694      ".ade", ".adp", ".bat", ".chm", ".cmd", ".com", ".cpl", ".exe", ".hta", ".ins", ".isp",
695      ".jar", ".jse", ".lib", ".lnk", ".mde", ".msc", ".msp", ".mst", ".pif", ".scr", ".sct",
696      ".shb", ".sys", ".vb",  ".vbe", ".vbs", ".vxd", ".wsc", ".wsf", ".wsh"
697};
698
699bool Dumpstate::AddZipEntryFromFd(const std::string& entry_name, int fd) {
700    if (!IsZipping()) {
701        MYLOGD("Not adding zip entry %s from fd because it's not a zipped bugreport\n",
702               entry_name.c_str());
703        return false;
704    }
705    std::string valid_name = entry_name;
706
707    // Rename extension if necessary.
708    size_t idx = entry_name.rfind(".");
709    if (idx != std::string::npos) {
710        std::string extension = entry_name.substr(idx);
711        std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
712        if (PROBLEMATIC_FILE_EXTENSIONS.count(extension) != 0) {
713            valid_name = entry_name + ".renamed";
714            MYLOGI("Renaming entry %s to %s\n", entry_name.c_str(), valid_name.c_str());
715        }
716    }
717
718    // Logging statement  below is useful to time how long each entry takes, but it's too verbose.
719    // MYLOGD("Adding zip entry %s\n", entry_name.c_str());
720    int32_t err = zip_writer_->StartEntryWithTime(valid_name.c_str(), ZipWriter::kCompress,
721                                                  get_mtime(fd, ds.now_));
722    if (err != 0) {
723        MYLOGE("zip_writer_->StartEntryWithTime(%s): %s\n", valid_name.c_str(),
724               ZipWriter::ErrorCodeString(err));
725        return false;
726    }
727
728    std::vector<uint8_t> buffer(65536);
729    while (1) {
730        ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd, buffer.data(), buffer.size()));
731        if (bytes_read == 0) {
732            break;
733        } else if (bytes_read == -1) {
734            MYLOGE("read(%s): %s\n", entry_name.c_str(), strerror(errno));
735            return false;
736        }
737        err = zip_writer_->WriteBytes(buffer.data(), bytes_read);
738        if (err) {
739            MYLOGE("zip_writer_->WriteBytes(): %s\n", ZipWriter::ErrorCodeString(err));
740            return false;
741        }
742    }
743
744    err = zip_writer_->FinishEntry();
745    if (err != 0) {
746        MYLOGE("zip_writer_->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
747        return false;
748    }
749
750    return true;
751}
752
753bool Dumpstate::AddZipEntry(const std::string& entry_name, const std::string& entry_path) {
754    android::base::unique_fd fd(
755        TEMP_FAILURE_RETRY(open(entry_path.c_str(), O_RDONLY | O_NONBLOCK | O_CLOEXEC)));
756    if (fd == -1) {
757        MYLOGE("open(%s): %s\n", entry_path.c_str(), strerror(errno));
758        return false;
759    }
760
761    return AddZipEntryFromFd(entry_name, fd.get());
762}
763
764/* adds a file to the existing zipped bugreport */
765static int _add_file_from_fd(const char* title __attribute__((unused)), const char* path, int fd) {
766    return ds.AddZipEntryFromFd(ZIP_ROOT_DIR + path, fd) ? 0 : 1;
767}
768
769void Dumpstate::AddDir(const std::string& dir, bool recursive) {
770    if (!IsZipping()) {
771        MYLOGD("Not adding dir %s because it's not a zipped bugreport\n", dir.c_str());
772        return;
773    }
774    MYLOGD("Adding dir %s (recursive: %d)\n", dir.c_str(), recursive);
775    DurationReporter duration_reporter(dir, nullptr);
776    dump_files("", dir.c_str(), recursive ? skip_none : is_dir, _add_file_from_fd);
777}
778
779bool Dumpstate::AddTextZipEntry(const std::string& entry_name, const std::string& content) {
780    if (!IsZipping()) {
781        MYLOGD("Not adding text zip entry %s because it's not a zipped bugreport\n",
782               entry_name.c_str());
783        return false;
784    }
785    MYLOGD("Adding zip text entry %s\n", entry_name.c_str());
786    int32_t err = zip_writer_->StartEntryWithTime(entry_name.c_str(), ZipWriter::kCompress, ds.now_);
787    if (err != 0) {
788        MYLOGE("zip_writer_->StartEntryWithTime(%s): %s\n", entry_name.c_str(),
789               ZipWriter::ErrorCodeString(err));
790        return false;
791    }
792
793    err = zip_writer_->WriteBytes(content.c_str(), content.length());
794    if (err != 0) {
795        MYLOGE("zip_writer_->WriteBytes(%s): %s\n", entry_name.c_str(),
796               ZipWriter::ErrorCodeString(err));
797        return false;
798    }
799
800    err = zip_writer_->FinishEntry();
801    if (err != 0) {
802        MYLOGE("zip_writer_->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
803        return false;
804    }
805
806    return true;
807}
808
809static void dump_iptables() {
810    RunCommand("IPTABLES", {"iptables", "-L", "-nvx"});
811    RunCommand("IP6TABLES", {"ip6tables", "-L", "-nvx"});
812    RunCommand("IPTABLES NAT", {"iptables", "-t", "nat", "-L", "-nvx"});
813    /* no ip6 nat */
814    RunCommand("IPTABLES MANGLE", {"iptables", "-t", "mangle", "-L", "-nvx"});
815    RunCommand("IP6TABLES MANGLE", {"ip6tables", "-t", "mangle", "-L", "-nvx"});
816    RunCommand("IPTABLES RAW", {"iptables", "-t", "raw", "-L", "-nvx"});
817    RunCommand("IP6TABLES RAW", {"ip6tables", "-t", "raw", "-L", "-nvx"});
818}
819
820static void AddAnrTraceFiles() {
821    bool add_to_zip = ds.IsZipping() && ds.version_ == VERSION_SPLIT_ANR;
822    std::string dump_traces_dir;
823
824    /* show the traces we collected in main(), if that was done */
825    if (dump_traces_path != nullptr) {
826        if (add_to_zip) {
827            dump_traces_dir = dirname(dump_traces_path);
828            MYLOGD("Adding ANR traces (directory %s) to the zip file\n", dump_traces_dir.c_str());
829            ds.AddDir(dump_traces_dir, true);
830        } else {
831            MYLOGD("Dumping current ANR traces (%s) to the main bugreport entry\n",
832                   dump_traces_path);
833            ds.DumpFile("VM TRACES JUST NOW", dump_traces_path);
834        }
835    }
836
837    std::string anr_traces_path = android::base::GetProperty("dalvik.vm.stack-trace-file", "");
838    std::string anr_traces_dir = dirname(anr_traces_path.c_str());
839
840    // Make sure directory is not added twice.
841    // TODO: this is an overzealous check because it's relying on dump_traces_path - which is
842    // generated by dump_traces() -  and anr_traces_path - which is retrieved from a system
843    // property - but in reality they're the same path (although the former could be nullptr).
844    // Anyways, once dump_traces() is refactored as a private Dumpstate function, this logic should
845    // be revisited.
846    bool already_dumped = anr_traces_dir == dump_traces_dir;
847
848    MYLOGD("AddAnrTraceFiles(): dump_traces_dir=%s, anr_traces_dir=%s, already_dumped=%d\n",
849           dump_traces_dir.c_str(), anr_traces_dir.c_str(), already_dumped);
850
851    if (anr_traces_path.empty()) {
852        printf("*** NO VM TRACES FILE DEFINED (dalvik.vm.stack-trace-file)\n\n");
853    } else {
854        int fd = TEMP_FAILURE_RETRY(
855            open(anr_traces_path.c_str(), O_RDONLY | O_CLOEXEC | O_NOFOLLOW | O_NONBLOCK));
856        if (fd < 0) {
857            printf("*** NO ANR VM TRACES FILE (%s): %s\n\n", anr_traces_path.c_str(),
858                   strerror(errno));
859        } else {
860            if (add_to_zip) {
861                if (!already_dumped) {
862                    MYLOGD("Adding dalvik ANR traces (directory %s) to the zip file\n",
863                           anr_traces_dir.c_str());
864                    ds.AddDir(anr_traces_dir, true);
865                    already_dumped = true;
866                }
867            } else {
868                MYLOGD("Dumping last ANR traces (%s) to the main bugreport entry\n",
869                       anr_traces_path.c_str());
870                dump_file_from_fd("VM TRACES AT LAST ANR", anr_traces_path.c_str(), fd);
871            }
872        }
873    }
874
875    if (add_to_zip && already_dumped) {
876        MYLOGD("Already dumped directory %s to the zip file\n", anr_traces_dir.c_str());
877        return;
878    }
879
880    /* slow traces for slow operations */
881    struct stat st;
882    if (!anr_traces_path.empty()) {
883        int tail = anr_traces_path.size() - 1;
884        while (tail > 0 && anr_traces_path.at(tail) != '/') {
885            tail--;
886        }
887        int i = 0;
888        while (1) {
889            anr_traces_path = anr_traces_path.substr(0, tail + 1) +
890                              android::base::StringPrintf("slow%02d.txt", i);
891            if (stat(anr_traces_path.c_str(), &st)) {
892                // No traces file at this index, done with the files.
893                break;
894            }
895            ds.DumpFile("VM TRACES WHEN SLOW", anr_traces_path.c_str());
896            i++;
897        }
898    }
899}
900
901static void dumpstate() {
902    DurationReporter duration_reporter("DUMPSTATE");
903    unsigned long timeout;
904
905    dump_dev_files("TRUSTY VERSION", "/sys/bus/platform/drivers/trusty", "trusty_version");
906    RunCommand("UPTIME", {"uptime"});
907    dump_files("UPTIME MMC PERF", mmcblk0, skip_not_stat, dump_stat_from_fd);
908    dump_emmc_ecsd("/d/mmc0/mmc0:0001/ext_csd");
909    DumpFile("MEMORY INFO", "/proc/meminfo");
910    RunCommand("CPU INFO", {"top", "-b", "-n", "1", "-H", "-s", "6", "-o",
911                            "pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name"});
912    RunCommand("PROCRANK", {"procrank"}, CommandOptions::AS_ROOT_20);
913    DumpFile("VIRTUAL MEMORY STATS", "/proc/vmstat");
914    DumpFile("VMALLOC INFO", "/proc/vmallocinfo");
915    DumpFile("SLAB INFO", "/proc/slabinfo");
916    DumpFile("ZONEINFO", "/proc/zoneinfo");
917    DumpFile("PAGETYPEINFO", "/proc/pagetypeinfo");
918    DumpFile("BUDDYINFO", "/proc/buddyinfo");
919    DumpFile("FRAGMENTATION INFO", "/d/extfrag/unusable_index");
920
921    DumpFile("KERNEL WAKE SOURCES", "/d/wakeup_sources");
922    DumpFile("KERNEL CPUFREQ", "/sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state");
923    DumpFile("KERNEL SYNC", "/d/sync");
924
925    RunCommand("PROCESSES AND THREADS",
926               {"ps", "-A", "-T", "-Z", "-O", "pri,nice,rtprio,sched,pcy"});
927    RunCommand("LIBRANK", {"librank"}, CommandOptions::AS_ROOT_10);
928
929    RunCommand("PRINTENV", {"printenv"});
930    RunCommand("NETSTAT", {"netstat", "-nW"});
931    struct stat s;
932    if (stat("/proc/modules", &s) != 0) {
933        MYLOGD("Skipping 'lsmod' because /proc/modules does not exist\n");
934    } else {
935        RunCommand("LSMOD", {"lsmod"});
936    }
937
938    do_dmesg();
939
940    RunCommand("LIST OF OPEN FILES", {"lsof"}, CommandOptions::AS_ROOT_10);
941    for_each_pid(do_showmap, "SMAPS OF ALL PROCESSES");
942    for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS");
943    for_each_pid(show_showtime, "PROCESS TIMES (pid cmd user system iowait+percentage)");
944
945    /* Dump Bluetooth HCI logs */
946    ds.AddDir("/data/misc/bluetooth/logs", true);
947
948    if (!ds.do_early_screenshot_) {
949        MYLOGI("taking late screenshot\n");
950        ds.TakeScreenshot();
951    }
952
953    // DumpFile("EVENT LOG TAGS", "/etc/event-log-tags");
954    // calculate timeout
955    timeout = logcat_timeout("main") + logcat_timeout("system") + logcat_timeout("crash");
956    if (timeout < 20000) {
957        timeout = 20000;
958    }
959    RunCommand("SYSTEM LOG", {"logcat", "-v", "threadtime", "-v", "printable", "-d", "*:v"},
960               CommandOptions::WithTimeout(timeout / 1000).Build());
961    timeout = logcat_timeout("events");
962    if (timeout < 20000) {
963        timeout = 20000;
964    }
965    RunCommand("EVENT LOG",
966               {"logcat", "-b", "events", "-v", "threadtime", "-v", "printable", "-d", "*:v"},
967               CommandOptions::WithTimeout(timeout / 1000).Build());
968    timeout = logcat_timeout("radio");
969    if (timeout < 20000) {
970        timeout = 20000;
971    }
972    RunCommand("RADIO LOG",
973               {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-d", "*:v"},
974               CommandOptions::WithTimeout(timeout / 1000).Build());
975
976    RunCommand("LOG STATISTICS", {"logcat", "-b", "all", "-S"});
977
978    AddAnrTraceFiles();
979
980    int dumped = 0;
981    for (size_t i = 0; i < NUM_TOMBSTONES; i++) {
982        if (tombstone_data[i].fd != -1) {
983            const char *name = tombstone_data[i].name;
984            int fd = tombstone_data[i].fd;
985            dumped = 1;
986            if (ds.IsZipping()) {
987                if (!ds.AddZipEntryFromFd(ZIP_ROOT_DIR + name, fd)) {
988                    MYLOGE("Unable to add tombstone %s to zip file\n", name);
989                }
990            } else {
991                dump_file_from_fd("TOMBSTONE", name, fd);
992            }
993            close(fd);
994            tombstone_data[i].fd = -1;
995        }
996    }
997    if (!dumped) {
998        printf("*** NO TOMBSTONES to dump in %s\n\n", TOMBSTONE_DIR);
999    }
1000
1001    DumpFile("NETWORK DEV INFO", "/proc/net/dev");
1002    DumpFile("QTAGUID NETWORK INTERFACES INFO", "/proc/net/xt_qtaguid/iface_stat_all");
1003    DumpFile("QTAGUID NETWORK INTERFACES INFO (xt)", "/proc/net/xt_qtaguid/iface_stat_fmt");
1004    DumpFile("QTAGUID CTRL INFO", "/proc/net/xt_qtaguid/ctrl");
1005    DumpFile("QTAGUID STATS INFO", "/proc/net/xt_qtaguid/stats");
1006
1007    struct stat st;
1008    if (!stat(PSTORE_LAST_KMSG, &st)) {
1009        /* Also TODO: Make console-ramoops CAP_SYSLOG protected. */
1010        DumpFile("LAST KMSG", PSTORE_LAST_KMSG);
1011    } else if (!stat(ALT_PSTORE_LAST_KMSG, &st)) {
1012        DumpFile("LAST KMSG", ALT_PSTORE_LAST_KMSG);
1013    } else {
1014        /* TODO: Make last_kmsg CAP_SYSLOG protected. b/5555691 */
1015        DumpFile("LAST KMSG", "/proc/last_kmsg");
1016    }
1017
1018    /* kernels must set CONFIG_PSTORE_PMSG, slice up pstore with device tree */
1019    RunCommand("LAST LOGCAT",
1020               {"logcat", "-L", "-b", "all", "-v", "threadtime", "-v", "printable", "-d", "*:v"});
1021
1022    /* The following have a tendency to get wedged when wifi drivers/fw goes belly-up. */
1023
1024    RunCommand("NETWORK INTERFACES", {"ip", "link"});
1025
1026    RunCommand("IPv4 ADDRESSES", {"ip", "-4", "addr", "show"});
1027    RunCommand("IPv6 ADDRESSES", {"ip", "-6", "addr", "show"});
1028
1029    RunCommand("IP RULES", {"ip", "rule", "show"});
1030    RunCommand("IP RULES v6", {"ip", "-6", "rule", "show"});
1031
1032    dump_route_tables();
1033
1034    RunCommand("ARP CACHE", {"ip", "-4", "neigh", "show"});
1035    RunCommand("IPv6 ND CACHE", {"ip", "-6", "neigh", "show"});
1036    RunCommand("MULTICAST ADDRESSES", {"ip", "maddr"});
1037    RunCommand("WIFI NETWORKS", {"wpa_cli", "IFNAME=wlan0", "list_networks"},
1038               CommandOptions::WithTimeout(20).Build());
1039
1040#ifdef FWDUMP_bcmdhd
1041    RunCommand("ND OFFLOAD TABLE", {WLUTIL, "nd_hostip"}, CommandOptions::AS_ROOT_5);
1042
1043    RunCommand("DUMP WIFI INTERNAL COUNTERS (1)", {WLUTIL, "counters"}, CommandOptions::AS_ROOT_20);
1044
1045    RunCommand("ND OFFLOAD STATUS (1)", {WLUTIL, "nd_status"}, CommandOptions::AS_ROOT_5);
1046
1047#endif
1048    DumpFile("INTERRUPTS (1)", "/proc/interrupts");
1049
1050    RunDumpsys("NETWORK DIAGNOSTICS", {"connectivity", "--diag"},
1051               CommandOptions::WithTimeout(10).Build());
1052
1053#ifdef FWDUMP_bcmdhd
1054    RunCommand("DUMP WIFI STATUS", {"dhdutil", "-i", "wlan0", "dump"}, CommandOptions::AS_ROOT_20);
1055
1056    RunCommand("DUMP WIFI INTERNAL COUNTERS (2)", {WLUTIL, "counters"}, CommandOptions::AS_ROOT_20);
1057
1058    RunCommand("ND OFFLOAD STATUS (2)", {WLUTIL, "nd_status"}, CommandOptions::AS_ROOT_5);
1059#endif
1060    DumpFile("INTERRUPTS (2)", "/proc/interrupts");
1061
1062    print_properties();
1063
1064    RunCommand("VOLD DUMP", {"vdc", "dump"});
1065    RunCommand("SECURE CONTAINERS", {"vdc", "asec", "list"});
1066
1067    RunCommand("FILESYSTEMS & FREE SPACE", {"df"});
1068
1069    RunCommand("LAST RADIO LOG", {"parse_radio_log", "/proc/last_radio_log"});
1070
1071    printf("------ BACKLIGHTS ------\n");
1072    printf("LCD brightness=");
1073    DumpFile("", "/sys/class/leds/lcd-backlight/brightness");
1074    printf("Button brightness=");
1075    DumpFile("", "/sys/class/leds/button-backlight/brightness");
1076    printf("Keyboard brightness=");
1077    DumpFile("", "/sys/class/leds/keyboard-backlight/brightness");
1078    printf("ALS mode=");
1079    DumpFile("", "/sys/class/leds/lcd-backlight/als");
1080    printf("LCD driver registers:\n");
1081    DumpFile("", "/sys/class/leds/lcd-backlight/registers");
1082    printf("\n");
1083
1084    /* Binder state is expensive to look at as it uses a lot of memory. */
1085    DumpFile("BINDER FAILED TRANSACTION LOG", "/sys/kernel/debug/binder/failed_transaction_log");
1086    DumpFile("BINDER TRANSACTION LOG", "/sys/kernel/debug/binder/transaction_log");
1087    DumpFile("BINDER TRANSACTIONS", "/sys/kernel/debug/binder/transactions");
1088    DumpFile("BINDER STATS", "/sys/kernel/debug/binder/stats");
1089    DumpFile("BINDER STATE", "/sys/kernel/debug/binder/state");
1090
1091    printf("========================================================\n");
1092    printf("== Board\n");
1093    printf("========================================================\n");
1094
1095    {
1096        DurationReporter tmpDr("dumpstate_board()");
1097        dumpstate_board();
1098        printf("\n");
1099    }
1100
1101    /* Migrate the ril_dumpstate to a dumpstate_board()? */
1102    int rilDumpstateTimeout = android::base::GetIntProperty("ril.dumpstate.timeout", 0);
1103    if (rilDumpstateTimeout > 0) {
1104        // su does not exist on user builds, so try running without it.
1105        // This way any implementations of vril-dump that do not require
1106        // root can run on user builds.
1107        CommandOptions::CommandOptionsBuilder options =
1108            CommandOptions::WithTimeout(rilDumpstateTimeout);
1109        if (!IsUserBuild()) {
1110            options.AsRoot();
1111        }
1112        RunCommand("DUMP VENDOR RIL LOGS", {"vril-dump"}, options.Build());
1113    }
1114
1115    printf("========================================================\n");
1116    printf("== Android Framework Services\n");
1117    printf("========================================================\n");
1118
1119    RunDumpsys("DUMPSYS", {"--skip", "meminfo", "cpuinfo"}, CommandOptions::WithTimeout(90).Build(),
1120               10);
1121
1122    printf("========================================================\n");
1123    printf("== Checkins\n");
1124    printf("========================================================\n");
1125
1126    RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"});
1127    RunDumpsys("CHECKIN MEMINFO", {"meminfo", "--checkin"});
1128    RunDumpsys("CHECKIN NETSTATS", {"netstats", "--checkin"});
1129    RunDumpsys("CHECKIN PROCSTATS", {"procstats", "-c"});
1130    RunDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"});
1131    RunDumpsys("CHECKIN PACKAGE", {"package", "--checkin"});
1132
1133    printf("========================================================\n");
1134    printf("== Running Application Activities\n");
1135    printf("========================================================\n");
1136
1137    RunDumpsys("APP ACTIVITIES", {"activity", "all"});
1138
1139    printf("========================================================\n");
1140    printf("== Running Application Services\n");
1141    printf("========================================================\n");
1142
1143    RunDumpsys("APP SERVICES", {"activity", "service", "all"});
1144
1145    printf("========================================================\n");
1146    printf("== Running Application Providers\n");
1147    printf("========================================================\n");
1148
1149    RunDumpsys("APP PROVIDERS", {"activity", "provider", "all"});
1150
1151    // DumpModemLogs adds the modem logs if available to the bugreport.
1152    // Do this at the end to allow for sufficient time for the modem logs to be
1153    // collected.
1154    DumpModemLogs();
1155
1156    printf("========================================================\n");
1157    printf("== Final progress (pid %d): %d/%d (estimated %d)\n", ds.pid_, ds.progress_->Get(),
1158           ds.progress_->GetMax(), ds.progress_->GetInitialMax());
1159    printf("========================================================\n");
1160    printf("== dumpstate: done (id %d)\n", ds.id_);
1161    printf("========================================================\n");
1162}
1163
1164static void ShowUsageAndExit(int exitCode = 1) {
1165    fprintf(stderr,
1166            "usage: dumpstate [-h] [-b soundfile] [-e soundfile] [-o file] [-d] [-p] "
1167            "[-z]] [-s] [-S] [-q] [-B] [-P] [-R] [-V version]\n"
1168            "  -h: display this help message\n"
1169            "  -b: play sound file instead of vibrate, at beginning of job\n"
1170            "  -e: play sound file instead of vibrate, at end of job\n"
1171            "  -o: write to file (instead of stdout)\n"
1172            "  -d: append date to filename (requires -o)\n"
1173            "  -p: capture screenshot to filename.png (requires -o)\n"
1174            "  -z: generate zipped file (requires -o)\n"
1175            "  -s: write output to control socket (for init)\n"
1176            "  -S: write file location to control socket (for init; requires -o and -z)"
1177            "  -q: disable vibrate\n"
1178            "  -B: send broadcast when finished (requires -o)\n"
1179            "  -P: send broadcast when started and update system properties on "
1180            "progress (requires -o and -B)\n"
1181            "  -R: take bugreport in remote mode (requires -o, -z, -d and -B, "
1182            "shouldn't be used with -P)\n"
1183            "  -v: prints the dumpstate header and exit\n");
1184    exit(exitCode);
1185}
1186
1187static void ExitOnInvalidArgs() {
1188    fprintf(stderr, "invalid combination of args\n");
1189    ShowUsageAndExit();
1190}
1191
1192static void wake_lock_releaser() {
1193    if (release_wake_lock(WAKE_LOCK_NAME) < 0) {
1194        MYLOGE("Failed to release wake lock: %s \n", strerror(errno));
1195    } else {
1196        MYLOGD("Wake lock released.\n");
1197    }
1198}
1199
1200static void sig_handler(int signo __attribute__((unused))) {
1201    wake_lock_releaser();
1202    _exit(EXIT_FAILURE);
1203}
1204
1205static void register_sig_handler() {
1206    struct sigaction sa;
1207    sigemptyset(&sa.sa_mask);
1208    sa.sa_flags = 0;
1209    sa.sa_handler = sig_handler;
1210    sigaction(SIGPIPE, &sa, NULL); // broken pipe
1211    sigaction(SIGSEGV, &sa, NULL); // segment fault
1212    sigaction(SIGINT, &sa, NULL); // ctrl-c
1213    sigaction(SIGTERM, &sa, NULL); // killed
1214    sigaction(SIGQUIT, &sa, NULL); // quit
1215}
1216
1217bool Dumpstate::FinishZipFile() {
1218    std::string entry_name = base_name_ + "-" + name_ + ".txt";
1219    MYLOGD("Adding main entry (%s) from %s to .zip bugreport\n", entry_name.c_str(),
1220           tmp_path_.c_str());
1221    // Final timestamp
1222    char date[80];
1223    time_t the_real_now_please_stand_up = time(nullptr);
1224    strftime(date, sizeof(date), "%Y/%m/%d %H:%M:%S", localtime(&the_real_now_please_stand_up));
1225    MYLOGD("dumpstate id %d finished around %s (%ld s)\n", ds.id_, date,
1226           the_real_now_please_stand_up - ds.now_);
1227
1228    if (!ds.AddZipEntry(entry_name, tmp_path_)) {
1229        MYLOGE("Failed to add text entry to .zip file\n");
1230        return false;
1231    }
1232    if (!AddTextZipEntry("main_entry.txt", entry_name)) {
1233        MYLOGE("Failed to add main_entry.txt to .zip file\n");
1234        return false;
1235    }
1236
1237    // Add log file (which contains stderr output) to zip...
1238    fprintf(stderr, "dumpstate_log.txt entry on zip file logged up to here\n");
1239    if (!ds.AddZipEntry("dumpstate_log.txt", ds.log_path_.c_str())) {
1240        MYLOGE("Failed to add dumpstate log to .zip file\n");
1241        return false;
1242    }
1243    // ... and re-opens it for further logging.
1244    redirect_to_existing_file(stderr, const_cast<char*>(ds.log_path_.c_str()));
1245    fprintf(stderr, "\n");
1246
1247    int32_t err = zip_writer_->Finish();
1248    if (err != 0) {
1249        MYLOGE("zip_writer_->Finish(): %s\n", ZipWriter::ErrorCodeString(err));
1250        return false;
1251    }
1252
1253    // TODO: remove once FinishZipFile() is automatically handled by Dumpstate's destructor.
1254    ds.zip_file.reset(nullptr);
1255
1256    MYLOGD("Removing temporary file %s\n", tmp_path_.c_str())
1257    if (remove(tmp_path_.c_str()) != 0) {
1258        MYLOGE("Failed to remove temporary file (%s): %s\n", tmp_path_.c_str(), strerror(errno));
1259    }
1260
1261    return true;
1262}
1263
1264static std::string SHA256_file_hash(std::string filepath) {
1265    android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(filepath.c_str(), O_RDONLY | O_NONBLOCK
1266            | O_CLOEXEC | O_NOFOLLOW)));
1267    if (fd == -1) {
1268        MYLOGE("open(%s): %s\n", filepath.c_str(), strerror(errno));
1269        return NULL;
1270    }
1271
1272    SHA256_CTX ctx;
1273    SHA256_Init(&ctx);
1274
1275    std::vector<uint8_t> buffer(65536);
1276    while (1) {
1277        ssize_t bytes_read = TEMP_FAILURE_RETRY(read(fd.get(), buffer.data(), buffer.size()));
1278        if (bytes_read == 0) {
1279            break;
1280        } else if (bytes_read == -1) {
1281            MYLOGE("read(%s): %s\n", filepath.c_str(), strerror(errno));
1282            return NULL;
1283        }
1284
1285        SHA256_Update(&ctx, buffer.data(), bytes_read);
1286    }
1287
1288    uint8_t hash[SHA256_DIGEST_LENGTH];
1289    SHA256_Final(hash, &ctx);
1290
1291    char hash_buffer[SHA256_DIGEST_LENGTH * 2 + 1];
1292    for(size_t i = 0; i < SHA256_DIGEST_LENGTH; i++) {
1293        sprintf(hash_buffer + (i * 2), "%02x", hash[i]);
1294    }
1295    hash_buffer[sizeof(hash_buffer) - 1] = 0;
1296    return std::string(hash_buffer);
1297}
1298
1299int main(int argc, char *argv[]) {
1300    int do_add_date = 0;
1301    int do_zip_file = 0;
1302    int do_vibrate = 1;
1303    char* use_outfile = 0;
1304    int use_socket = 0;
1305    int use_control_socket = 0;
1306    int do_fb = 0;
1307    int do_broadcast = 0;
1308    int is_remote_mode = 0;
1309    bool show_header_only = false;
1310    bool do_start_service = false;
1311
1312    /* set as high priority, and protect from OOM killer */
1313    setpriority(PRIO_PROCESS, 0, -20);
1314
1315    FILE* oom_adj = fopen("/proc/self/oom_score_adj", "we");
1316    if (oom_adj) {
1317        fputs("-1000", oom_adj);
1318        fclose(oom_adj);
1319    } else {
1320        /* fallback to kernels <= 2.6.35 */
1321        oom_adj = fopen("/proc/self/oom_adj", "we");
1322        if (oom_adj) {
1323            fputs("-17", oom_adj);
1324            fclose(oom_adj);
1325        }
1326    }
1327
1328    /* parse arguments */
1329    int c;
1330    while ((c = getopt(argc, argv, "dho:svqzpPBRSV:")) != -1) {
1331        switch (c) {
1332            // clang-format off
1333            case 'd': do_add_date = 1;            break;
1334            case 'z': do_zip_file = 1;            break;
1335            case 'o': use_outfile = optarg;       break;
1336            case 's': use_socket = 1;             break;
1337            case 'S': use_control_socket = 1;     break;
1338            case 'v': show_header_only = true;    break;
1339            case 'q': do_vibrate = 0;             break;
1340            case 'p': do_fb = 1;                  break;
1341            case 'P': ds.update_progress_ = true; break;
1342            case 'R': is_remote_mode = 1;         break;
1343            case 'B': do_broadcast = 1;           break;
1344            case 'V':                             break; // compatibility no-op
1345            case 'h':
1346                ShowUsageAndExit(0);
1347                break;
1348            default:
1349                fprintf(stderr, "Invalid option: %c\n", c);
1350                ShowUsageAndExit();
1351                // clang-format on
1352        }
1353    }
1354
1355    // TODO: use helper function to convert argv into a string
1356    for (int i = 0; i < argc; i++) {
1357        ds.args_ += argv[i];
1358        if (i < argc - 1) {
1359            ds.args_ += " ";
1360        }
1361    }
1362
1363    ds.extra_options_ = android::base::GetProperty(PROPERTY_EXTRA_OPTIONS, "");
1364    if (!ds.extra_options_.empty()) {
1365        // Framework uses a system property to override some command-line args.
1366        // Currently, it contains the type of the requested bugreport.
1367        if (ds.extra_options_ == "bugreportplus") {
1368            // Currently, the dumpstate binder is only used by Shell to update progress.
1369            do_start_service = true;
1370            ds.update_progress_ = true;
1371            do_fb = 0;
1372        } else if (ds.extra_options_ == "bugreportremote") {
1373            do_vibrate = 0;
1374            is_remote_mode = 1;
1375            do_fb = 0;
1376        } else if (ds.extra_options_ == "bugreportwear") {
1377            ds.update_progress_ = true;
1378        } else {
1379            MYLOGE("Unknown extra option: %s\n", ds.extra_options_.c_str());
1380        }
1381        // Reset the property
1382        android::base::SetProperty(PROPERTY_EXTRA_OPTIONS, "");
1383    }
1384
1385    if ((do_zip_file || do_add_date || ds.update_progress_ || do_broadcast) && !use_outfile) {
1386        ExitOnInvalidArgs();
1387    }
1388
1389    if (use_control_socket && !do_zip_file) {
1390        ExitOnInvalidArgs();
1391    }
1392
1393    if (ds.update_progress_ && !do_broadcast) {
1394        ExitOnInvalidArgs();
1395    }
1396
1397    if (is_remote_mode && (ds.update_progress_ || !do_broadcast || !do_zip_file || !do_add_date)) {
1398        ExitOnInvalidArgs();
1399    }
1400
1401    if (ds.version_ == VERSION_DEFAULT) {
1402        ds.version_ = VERSION_CURRENT;
1403    }
1404
1405    if (ds.version_ != VERSION_CURRENT && ds.version_ != VERSION_SPLIT_ANR) {
1406        MYLOGE("invalid version requested ('%s'); suppported values are: ('%s', '%s', '%s')\n",
1407               ds.version_.c_str(), VERSION_DEFAULT.c_str(), VERSION_CURRENT.c_str(),
1408               VERSION_SPLIT_ANR.c_str());
1409        exit(1);
1410    }
1411
1412    if (show_header_only) {
1413        ds.PrintHeader();
1414        exit(0);
1415    }
1416
1417    /* redirect output if needed */
1418    bool is_redirecting = !use_socket && use_outfile;
1419
1420    // TODO: temporarily set progress until it's part of the Dumpstate constructor
1421    std::string stats_path =
1422        is_redirecting ? android::base::StringPrintf("%s/dumpstate-stats.txt", dirname(use_outfile))
1423                       : "";
1424    ds.progress_.reset(new Progress(stats_path));
1425
1426    /* gets the sequential id */
1427    uint32_t last_id = android::base::GetIntProperty(PROPERTY_LAST_ID, 0);
1428    ds.id_ = ++last_id;
1429    android::base::SetProperty(PROPERTY_LAST_ID, std::to_string(last_id));
1430
1431    MYLOGI("begin\n");
1432
1433    if (acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_NAME) < 0) {
1434        MYLOGE("Failed to acquire wake lock: %s \n", strerror(errno));
1435    } else {
1436        MYLOGD("Wake lock acquired.\n");
1437        atexit(wake_lock_releaser);
1438        register_sig_handler();
1439    }
1440
1441    if (do_start_service) {
1442        MYLOGI("Starting 'dumpstate' service\n");
1443        android::status_t ret;
1444        if ((ret = android::os::DumpstateService::Start()) != android::OK) {
1445            MYLOGE("Unable to start DumpstateService: %d\n", ret);
1446        }
1447    }
1448
1449    if (ds.IsDryRun()) {
1450        MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
1451    }
1452
1453    MYLOGI("dumpstate info: id=%d, args='%s', extra_options= %s)\n", ds.id_, ds.args_.c_str(),
1454           ds.extra_options_.c_str());
1455
1456    MYLOGI("bugreport format version: %s\n", ds.version_.c_str());
1457
1458    ds.do_early_screenshot_ = ds.update_progress_;
1459
1460    // If we are going to use a socket, do it as early as possible
1461    // to avoid timeouts from bugreport.
1462    if (use_socket) {
1463        redirect_to_socket(stdout, "dumpstate");
1464    }
1465
1466    if (use_control_socket) {
1467        MYLOGD("Opening control socket\n");
1468        ds.control_socket_fd_ = open_socket("dumpstate");
1469        ds.update_progress_ = 1;
1470    }
1471
1472    if (is_redirecting) {
1473        ds.bugreport_dir_ = dirname(use_outfile);
1474        ds.base_name_ = basename(use_outfile);
1475        if (do_add_date) {
1476            char date[80];
1477            strftime(date, sizeof(date), "%Y-%m-%d-%H-%M-%S", localtime(&ds.now_));
1478            ds.name_ = date;
1479        } else {
1480            ds.name_ = "undated";
1481        }
1482        std::string buildId = android::base::GetProperty("ro.build.id", "UNKNOWN_BUILD");
1483        ds.base_name_ += "-" + buildId;
1484        if (do_fb) {
1485            ds.screenshot_path_ = ds.GetPath(".png");
1486        }
1487        ds.tmp_path_ = ds.GetPath(".tmp");
1488        ds.log_path_ = ds.GetPath("-dumpstate_log-" + std::to_string(ds.pid_) + ".txt");
1489
1490        MYLOGD(
1491            "Bugreport dir: %s\n"
1492            "Base name: %s\n"
1493            "Suffix: %s\n"
1494            "Log path: %s\n"
1495            "Temporary path: %s\n"
1496            "Screenshot path: %s\n",
1497            ds.bugreport_dir_.c_str(), ds.base_name_.c_str(), ds.name_.c_str(),
1498            ds.log_path_.c_str(), ds.tmp_path_.c_str(), ds.screenshot_path_.c_str());
1499
1500        if (do_zip_file) {
1501            ds.path_ = ds.GetPath(".zip");
1502            MYLOGD("Creating initial .zip file (%s)\n", ds.path_.c_str());
1503            create_parent_dirs(ds.path_.c_str());
1504            ds.zip_file.reset(fopen(ds.path_.c_str(), "wb"));
1505            if (ds.zip_file == nullptr) {
1506                MYLOGE("fopen(%s, 'wb'): %s\n", ds.path_.c_str(), strerror(errno));
1507                do_zip_file = 0;
1508            } else {
1509                ds.zip_writer_.reset(new ZipWriter(ds.zip_file.get()));
1510            }
1511            ds.AddTextZipEntry("version.txt", ds.version_);
1512        }
1513
1514        if (ds.update_progress_) {
1515            if (do_broadcast) {
1516                // clang-format off
1517                std::vector<std::string> am_args = {
1518                     "--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
1519                     "--es", "android.intent.extra.NAME", ds.name_,
1520                     "--ei", "android.intent.extra.ID", std::to_string(ds.id_),
1521                     "--ei", "android.intent.extra.PID", std::to_string(ds.pid_),
1522                     "--ei", "android.intent.extra.MAX", std::to_string(ds.progress_->GetMax()),
1523                };
1524                // clang-format on
1525                send_broadcast("android.intent.action.BUGREPORT_STARTED", am_args);
1526            }
1527            if (use_control_socket) {
1528                dprintf(ds.control_socket_fd_, "BEGIN:%s\n", ds.path_.c_str());
1529            }
1530        }
1531    }
1532
1533    /* read /proc/cmdline before dropping root */
1534    FILE *cmdline = fopen("/proc/cmdline", "re");
1535    if (cmdline) {
1536        fgets(cmdline_buf, sizeof(cmdline_buf), cmdline);
1537        fclose(cmdline);
1538    }
1539
1540    /* open the vibrator before dropping root */
1541    std::unique_ptr<FILE, int(*)(FILE*)> vibrator(NULL, fclose);
1542    if (do_vibrate) {
1543        vibrator.reset(fopen("/sys/class/timed_output/vibrator/enable", "we"));
1544        if (vibrator) {
1545            vibrate(vibrator.get(), 150);
1546        }
1547    }
1548
1549    if (do_fb && ds.do_early_screenshot_) {
1550        if (ds.screenshot_path_.empty()) {
1551            // should not have happened
1552            MYLOGE("INTERNAL ERROR: skipping early screenshot because path was not set\n");
1553        } else {
1554            MYLOGI("taking early screenshot\n");
1555            ds.TakeScreenshot();
1556        }
1557    }
1558
1559    if (do_zip_file) {
1560        if (chown(ds.path_.c_str(), AID_SHELL, AID_SHELL)) {
1561            MYLOGE("Unable to change ownership of zip file %s: %s\n", ds.path_.c_str(),
1562                   strerror(errno));
1563        }
1564    }
1565
1566    if (is_redirecting) {
1567        redirect_to_file(stderr, const_cast<char*>(ds.log_path_.c_str()));
1568        if (chown(ds.log_path_.c_str(), AID_SHELL, AID_SHELL)) {
1569            MYLOGE("Unable to change ownership of dumpstate log file %s: %s\n",
1570                   ds.log_path_.c_str(), strerror(errno));
1571        }
1572        /* TODO: rather than generating a text file now and zipping it later,
1573           it would be more efficient to redirect stdout to the zip entry
1574           directly, but the libziparchive doesn't support that option yet. */
1575        redirect_to_file(stdout, const_cast<char*>(ds.tmp_path_.c_str()));
1576        if (chown(ds.tmp_path_.c_str(), AID_SHELL, AID_SHELL)) {
1577            MYLOGE("Unable to change ownership of temporary bugreport file %s: %s\n",
1578                   ds.tmp_path_.c_str(), strerror(errno));
1579        }
1580    }
1581    // NOTE: there should be no stdout output until now, otherwise it would break the header.
1582    // In particular, DurationReport objects should be created passing 'title, NULL', so their
1583    // duration is logged into MYLOG instead.
1584    ds.PrintHeader();
1585
1586    // Dumps systrace right away, otherwise it will be filled with unnecessary events.
1587    // First try to dump anrd trace if the daemon is running. Otherwise, dump
1588    // the raw trace.
1589    if (!dump_anrd_trace()) {
1590        dump_systrace();
1591    }
1592
1593    // Invoking the following dumpsys calls before dump_traces() to try and
1594    // keep the system stats as close to its initial state as possible.
1595    RunDumpsys("DUMPSYS MEMINFO", {"meminfo", "-a"},
1596               CommandOptions::WithTimeout(90).DropRoot().Build());
1597    RunDumpsys("DUMPSYS CPUINFO", {"cpuinfo", "-a"},
1598               CommandOptions::WithTimeout(10).DropRoot().Build());
1599
1600    // TODO: Drop root user and move into dumpstate() once b/28633932 is fixed.
1601    dump_raft();
1602
1603    /* collect stack traces from Dalvik and native processes (needs root) */
1604    dump_traces_path = dump_traces();
1605
1606    /* Run some operations that require root. */
1607    get_tombstone_fds(tombstone_data);
1608    ds.AddDir(RECOVERY_DIR, true);
1609    ds.AddDir(RECOVERY_DATA_DIR, true);
1610    ds.AddDir(LOGPERSIST_DATA_DIR, false);
1611    if (!IsUserBuild()) {
1612        ds.AddDir(PROFILE_DATA_DIR_CUR, true);
1613        ds.AddDir(PROFILE_DATA_DIR_REF, true);
1614    }
1615    add_mountinfo();
1616    dump_iptables();
1617
1618    // Capture any IPSec policies in play.  No keys are exposed here.
1619    RunCommand("IP XFRM POLICY", {"ip", "xfrm", "policy"},
1620               CommandOptions::WithTimeout(10).Build());
1621
1622    // Run ss as root so we can see socket marks.
1623    RunCommand("DETAILED SOCKET STATE", {"ss", "-eionptu"}, CommandOptions::WithTimeout(10).Build());
1624
1625    if (!drop_root_user()) {
1626        return -1;
1627    }
1628
1629    dumpstate();
1630
1631    /* close output if needed */
1632    if (is_redirecting) {
1633        fclose(stdout);
1634    }
1635
1636    /* rename or zip the (now complete) .tmp file to its final location */
1637    if (use_outfile) {
1638
1639        /* check if user changed the suffix using system properties */
1640        std::string name = android::base::GetProperty(
1641            android::base::StringPrintf("dumpstate.%d.name", ds.pid_), "");
1642        bool change_suffix= false;
1643        if (!name.empty()) {
1644            /* must whitelist which characters are allowed, otherwise it could cross directories */
1645            std::regex valid_regex("^[-_a-zA-Z0-9]+$");
1646            if (std::regex_match(name.c_str(), valid_regex)) {
1647                change_suffix = true;
1648            } else {
1649                MYLOGE("invalid suffix provided by user: %s\n", name.c_str());
1650            }
1651        }
1652        if (change_suffix) {
1653            MYLOGI("changing suffix from %s to %s\n", ds.name_.c_str(), name.c_str());
1654            ds.name_ = name;
1655            if (!ds.screenshot_path_.empty()) {
1656                std::string new_screenshot_path = ds.GetPath(".png");
1657                if (rename(ds.screenshot_path_.c_str(), new_screenshot_path.c_str())) {
1658                    MYLOGE("rename(%s, %s): %s\n", ds.screenshot_path_.c_str(),
1659                           new_screenshot_path.c_str(), strerror(errno));
1660                } else {
1661                    ds.screenshot_path_ = new_screenshot_path;
1662                }
1663            }
1664        }
1665
1666        bool do_text_file = true;
1667        if (do_zip_file) {
1668            if (!ds.FinishZipFile()) {
1669                MYLOGE("Failed to finish zip file; sending text bugreport instead\n");
1670                do_text_file = true;
1671            } else {
1672                do_text_file = false;
1673                // Since zip file is already created, it needs to be renamed.
1674                std::string new_path = ds.GetPath(".zip");
1675                if (ds.path_ != new_path) {
1676                    MYLOGD("Renaming zip file from %s to %s\n", ds.path_.c_str(), new_path.c_str());
1677                    if (rename(ds.path_.c_str(), new_path.c_str())) {
1678                        MYLOGE("rename(%s, %s): %s\n", ds.path_.c_str(), new_path.c_str(),
1679                               strerror(errno));
1680                    } else {
1681                        ds.path_ = new_path;
1682                    }
1683                }
1684            }
1685        }
1686        if (do_text_file) {
1687            ds.path_ = ds.GetPath(".txt");
1688            MYLOGD("Generating .txt bugreport at %s from %s\n", ds.path_.c_str(),
1689                   ds.tmp_path_.c_str());
1690            if (rename(ds.tmp_path_.c_str(), ds.path_.c_str())) {
1691                MYLOGE("rename(%s, %s): %s\n", ds.tmp_path_.c_str(), ds.path_.c_str(),
1692                       strerror(errno));
1693                ds.path_.clear();
1694            }
1695        }
1696        if (use_control_socket) {
1697            if (do_text_file) {
1698                dprintf(ds.control_socket_fd_,
1699                        "FAIL:could not create zip file, check %s "
1700                        "for more details\n",
1701                        ds.log_path_.c_str());
1702            } else {
1703                dprintf(ds.control_socket_fd_, "OK:%s\n", ds.path_.c_str());
1704            }
1705        }
1706    }
1707
1708    /* vibrate a few but shortly times to let user know it's finished */
1709    if (vibrator) {
1710        for (int i = 0; i < 3; i++) {
1711            vibrate(vibrator.get(), 75);
1712            usleep((75 + 50) * 1000);
1713        }
1714    }
1715
1716    /* tell activity manager we're done */
1717    if (do_broadcast) {
1718        if (!ds.path_.empty()) {
1719            MYLOGI("Final bugreport path: %s\n", ds.path_.c_str());
1720            // clang-format off
1721            std::vector<std::string> am_args = {
1722                 "--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
1723                 "--ei", "android.intent.extra.ID", std::to_string(ds.id_),
1724                 "--ei", "android.intent.extra.PID", std::to_string(ds.pid_),
1725                 "--ei", "android.intent.extra.MAX", std::to_string(ds.progress_->GetMax()),
1726                 "--es", "android.intent.extra.BUGREPORT", ds.path_,
1727                 "--es", "android.intent.extra.DUMPSTATE_LOG", ds.log_path_
1728            };
1729            // clang-format on
1730            if (do_fb) {
1731                am_args.push_back("--es");
1732                am_args.push_back("android.intent.extra.SCREENSHOT");
1733                am_args.push_back(ds.screenshot_path_);
1734            }
1735            if (is_remote_mode) {
1736                am_args.push_back("--es");
1737                am_args.push_back("android.intent.extra.REMOTE_BUGREPORT_HASH");
1738                am_args.push_back(SHA256_file_hash(ds.path_));
1739                send_broadcast("android.intent.action.REMOTE_BUGREPORT_FINISHED", am_args);
1740            } else {
1741                send_broadcast("android.intent.action.BUGREPORT_FINISHED", am_args);
1742            }
1743        } else {
1744            MYLOGE("Skipping finished broadcast because bugreport could not be generated\n");
1745        }
1746    }
1747
1748    MYLOGD("Final progress: %d/%d (estimated %d)\n", ds.progress_->Get(), ds.progress_->GetMax(),
1749           ds.progress_->GetInitialMax());
1750    ds.progress_->Save();
1751    MYLOGI("done (id %d)\n", ds.id_);
1752
1753    if (is_redirecting) {
1754        fclose(stderr);
1755    }
1756
1757    if (use_control_socket && ds.control_socket_fd_ != -1) {
1758        MYLOGD("Closing control socket\n");
1759        close(ds.control_socket_fd_);
1760    }
1761
1762    return 0;
1763}
1764