Implement DropBox uploading

This patch adds a --dropbox flag to the perfetto tool for uploading the
recorded trace file into DropBox.

Change-Id: I42ce9080292aa45f2442033a5c1d23c83e6e3855
diff --git a/Android.bp b/Android.bp
index 8db1700..1ecc372 100644
--- a/Android.bp
+++ b/Android.bp
@@ -62,8 +62,10 @@
   ],
   shared_libs: [
     "libandroid",
+    "libbinder",
     "liblog",
     "libprotobuf-cpp-lite",
+    "libservices",
     "libutils",
   ],
   static_libs: [
@@ -95,7 +97,9 @@
   ],
   shared_libs: [
     "libandroid",
+    "libbinder",
     "liblog",
+    "libservices",
     "libtraced_shared",
     "libutils",
   ],
@@ -835,7 +839,9 @@
   ],
   shared_libs: [
     "libandroid",
+    "libbinder",
     "liblog",
+    "libservices",
     "libtraced_shared",
     "libutils",
   ],
@@ -855,7 +861,9 @@
   ],
   shared_libs: [
     "libandroid",
+    "libbinder",
     "liblog",
+    "libservices",
     "libtraced_shared",
     "libutils",
   ],
diff --git a/BUILD.gn b/BUILD.gn
index 218a71a..408a682 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -82,7 +82,11 @@
     if (build_with_android) {
       cflags = [ "-DPERFETTO_BUILD_WITH_ANDROID" ]
       deps += [ "src/base:android_task_runner" ]
-      libs = [ "utils" ]
+      libs = [
+        "binder",
+        "services",
+        "utils",
+      ]
     }
   }
 
diff --git a/src/traced/perfetto_cmd/perfetto_cmd.cc b/src/traced/perfetto_cmd/perfetto_cmd.cc
index c93f7a6..ee6ee9d 100644
--- a/src/traced/perfetto_cmd/perfetto_cmd.cc
+++ b/src/traced/perfetto_cmd/perfetto_cmd.cc
@@ -15,6 +15,7 @@
  */
 
 #include <getopt.h>
+#include <sys/stat.h>
 #include <unistd.h>
 
 #include <fstream>
@@ -39,6 +40,7 @@
 #if defined(PERFETTO_BUILD_WITH_ANDROID)
 #include "perfetto/base/android_task_runner.h"
 
+#include <android/os/DropBoxManager.h>
 #include <utils/Looper.h>
 #include <utils/StrongPointer.h>
 #endif  // defined(PERFETTO_BUILD_WITH_ANDROID)
@@ -47,6 +49,10 @@
 // traced service instead of receiving a copy of the chunks and writing them
 // from this process.
 namespace perfetto {
+namespace {
+const char kTempTraceDir[] = "/data/misc/perfetto-traces";
+const char kDefaultDropBoxTag[] = "perfetto";
+}  // namespace
 
 #if defined(PERFETTO_BUILD_WITH_ANDROID)
 using PlatformTaskRunner = base::AndroidTaskRunner;
@@ -71,16 +77,19 @@
   std::unique_ptr<TraceConfig> trace_config_;
   std::ofstream trace_out_stream_;
   std::string trace_out_path_;
-  bool did_receive_full_trace_ = false;
+  std::string tmp_trace_out_path_;
+  std::string dropbox_tag_;
+  bool did_process_full_trace_ = false;
 };
 
 int PerfettoCmd::PrintUsage(const char* argv0) {
   fprintf(stderr, R"(Usage: %s
-  --background  -b : exits immediately and continues the trace in background
-  --config      -c : /path/to/trace/config/file or - for stdin
-  --out         -o : /path/to/out/trace/file
-  --help
-)", argv0);
+  --background  -b     : Exits immediately and continues tracing in background
+  --config      -c     : /path/to/trace/config/file or - for stdin
+  --out         -o     : /path/to/out/trace/file
+  --dropbox     -d TAG : Upload trace into DropBox using tag TAG (default: %s)
+  --help        -h
+)", argv0, kDefaultDropBoxTag);
   return 1;
 }
 
@@ -91,13 +100,15 @@
       {"config", required_argument, 0, 'c'},
       {"out", required_argument, 0, 'o'},
       {"background", no_argument, 0, 'b'},
+      {"dropbox", optional_argument, 0, 'd'},
       {nullptr, 0, nullptr, 0}};
 
   int option_index = 0;
   std::string trace_config_raw;
   bool background = false;
   for (;;) {
-    int option = getopt_long(argc, argv, "c:o:b", long_options, &option_index);
+    int option =
+        getopt_long(argc, argv, "c:o:bd::", long_options, &option_index);
 
     if (option == -1)
       break;  // EOF.
@@ -112,11 +123,11 @@
         test_config.add_buffers()->set_size_kb(4096 * 10);
         test_config.set_duration_ms(3000);
         auto* ds_config = test_config.add_data_sources()->mutable_config();
-        ds_config->set_name("perfetto.test");
+        ds_config->set_name("com.google.perfetto.ftrace");
+        ds_config->mutable_ftrace_config()->add_event_names("sched_switch");
         // TODO(primiano): At the moment this must always be 1.
         // Once the target_buffer situation is fixed this can be any number.
         ds_config->set_target_buffer(1);
-        ds_config->set_trace_category_filters("foo,bar");
         test_config.SerializeToString(&trace_config_raw);
       } else {
         std::ifstream file_stream;
@@ -133,15 +144,19 @@
 
     if (option == 'o') {
       trace_out_path_ = optarg;
-      trace_out_stream_.open(trace_out_path_ + ".tmp",
-                             std::ios_base::out | std::ios_base::binary);
-      if (!trace_out_stream_.is_open()) {
-        PERFETTO_ELOG("Could not open %s", trace_out_path_.c_str());
-        return 1;
-      }
       continue;
     }
 
+    if (option == 'd') {
+#if defined(PERFETTO_BUILD_WITH_ANDROID)
+      dropbox_tag_ = optarg ? optarg : kDefaultDropBoxTag;
+      continue;
+#else
+      PERFETTO_ELOG("DropBox is only supported with Android tree builds");
+      return 1;
+#endif
+    }
+
     if (option == 'b') {
       background = true;
       continue;
@@ -149,8 +164,43 @@
     return PrintUsage(argv[0]);
   }
 
-  if (trace_config_raw.empty())
+  if (!trace_out_path_.empty() && !dropbox_tag_.empty()) {
+    PERFETTO_ELOG(
+        "Can't log to a file (--out) and DropBox (--dropbox) at the same "
+        "time");
+    return 1;
+  }
+
+  if (trace_config_raw.empty() ||
+      (trace_out_path_.empty() && dropbox_tag_.empty())) {
     return PrintUsage(argv[0]);
+  }
+
+  if (access(kTempTraceDir, F_OK) == -1 && mkdir(kTempTraceDir, 0770) == -1) {
+    PERFETTO_ELOG("Could not create temporary trace directory: %s",
+                  kTempTraceDir);
+    return 1;
+  }
+
+  {
+    tmp_trace_out_path_ = std::string(kTempTraceDir) + "/perfetto-traceXXXXXX";
+    // TODO(skyostil): Use open(O_TMPFILE) + linkat so we don't leave partial
+    // trace files lying around in case of unexpected termination.
+    base::ScopedFile tmp_file(mkstemp(&tmp_trace_out_path_[0]));
+    if (!tmp_file) {
+      PERFETTO_ELOG("Could not create a temporary trace file in %s",
+                    kTempTraceDir);
+      return 1;
+    }
+  }
+
+  trace_out_stream_.open(tmp_trace_out_path_,
+                         std::ios_base::out | std::ios_base::binary);
+  if (!trace_out_stream_.is_open()) {
+    PERFETTO_ELOG("Could not open %s", tmp_trace_out_path_.c_str());
+    return 1;
+  }
+
   perfetto::protos::TraceConfig trace_config_proto;
   PERFETTO_DLOG("Parsing TraceConfig, %zu bytes", trace_config_raw.size());
   bool parsed = trace_config_proto.ParseFromString(trace_config_raw);
@@ -169,16 +219,8 @@
 
   consumer_endpoint_ = ConsumerIPCClient::Connect(PERFETTO_CONSUMER_SOCK_NAME,
                                                   this, &task_runner_);
-
-#if defined(PERFETTO_BUILD_WITH_ANDROID)
-  android::sp<android::Looper> looper(android::Looper::prepare(0 /* opts */));
-  while (true) {
-    looper->pollAll(-1 /* timeoutMillis */);
-  }
-#else   // defined(PERFETTO_BUILD_WITH_ANDROID)
   task_runner_.Run();
-#endif  // defined(PERFETTO_BUILD_WITH_ANDROID)
-  return did_receive_full_trace_ ? 0 : 1;
+  return did_process_full_trace_ ? 0 : 1;
 }  // namespace perfetto
 
 void PerfettoCmd::OnConnect() {
@@ -214,15 +256,33 @@
     return;
 
   // Reached end of trace.
-  long bytes_written = trace_out_stream_.tellp();
-  trace_out_stream_.close();
-  std::string tmp_path = trace_out_path_ + ".tmp";
-  PERFETTO_CHECK(rename(tmp_path.c_str(), trace_out_path_.c_str()) == 0);
-  did_receive_full_trace_ = true;
-  PERFETTO_ILOG("Wrote %ld bytes into %s", bytes_written,
-                trace_out_path_.c_str());
   consumer_endpoint_->FreeBuffers();
   task_runner_.Quit();
+
+  long bytes_written = trace_out_stream_.tellp();
+  trace_out_stream_.close();
+  if (!dropbox_tag_.empty()) {
+#if defined(PERFETTO_BUILD_WITH_ANDROID)
+    android::sp<android::os::DropBoxManager> dropbox =
+        new android::os::DropBoxManager();
+    android::binder::Status status =
+        dropbox->addFile(android::String16(dropbox_tag_.c_str()),
+                         tmp_trace_out_path_, 0 /* flags */);
+    unlink(tmp_trace_out_path_.c_str());
+    if (!status.isOk()) {
+      PERFETTO_ELOG("DropBox upload failed: %s", status.toString8().c_str());
+      return;
+    }
+    PERFETTO_ILOG("Uploaded %ld bytes into DropBox with tag %s", bytes_written,
+                  dropbox_tag_.c_str());
+#endif  // defined(PERFETTO_BUILD_WITH_ANDROID)
+  } else {
+    PERFETTO_CHECK(
+        rename(tmp_trace_out_path_.c_str(), trace_out_path_.c_str()) == 0);
+    PERFETTO_ILOG("Wrote %ld bytes into %s", bytes_written,
+                  trace_out_path_.c_str());
+  }
+  did_process_full_trace_ = true;
 }
 
 int __attribute__((visibility("default")))
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index 6b3b126..83ca6a3 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -58,7 +58,9 @@
 # Shared libraries which are directly translated to Android system equivalents.
 library_whitelist = [
     'android',
+    'binder',
     'log',
+    'services',
     'utils',
 ]