dropbox.cc revision c24259a90600741673cb32af9109f80fd7c80783
1/*
2 *
3 * Copyright 2017, The Android Open Source Project
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18#include "dropbox.h"
19
20#include <cstdio>
21#include <cstdlib>
22#include <memory>
23
24#include <inttypes.h>
25#include <unistd.h>
26
27#include <android-base/logging.h>
28#include <android-base/stringprintf.h>
29#include <android-base/unique_fd.h>
30#include <android/os/DropBoxManager.h>
31#include <binder/Status.h>
32#include <utils/String8.h>
33
34#include "perfprofd_record.pb.h"
35
36#include "perfprofd_io.h"
37
38namespace android {
39namespace perfprofd {
40namespace dropbox {
41
42namespace {
43
44bool WriteDropboxFile(android::perfprofd::PerfprofdRecord* encodedProfile,
45                      const std::string& temp_dir,
46                      std::string* error_msg) {
47  android::base::unique_fd tmp_fd;
48  {
49    char path[PATH_MAX];
50    snprintf(path, sizeof(path), "%s/dropboxtmp-XXXXXX", temp_dir.c_str());
51    tmp_fd.reset(mkstemp(path));
52    if (tmp_fd.get() == -1) {
53      *error_msg = android::base::StringPrintf("Could not create temp file %s: %s",
54                                               path,
55                                               strerror(errno));
56      return false;
57    }
58    if (unlink(path) != 0) {
59      PLOG(WARNING) << "Could not unlink binder temp file";
60    }
61  }
62
63  // Dropbox takes ownership of the fd, and if it is not readonly,
64  // a selinux violation will occur. Get a read-only version.
65  android::base::unique_fd read_only;
66  {
67    char fdpath[64];
68    snprintf(fdpath, arraysize(fdpath), "/proc/self/fd/%d", tmp_fd.get());
69    read_only.reset(open(fdpath, O_RDONLY | O_CLOEXEC));
70    if (read_only.get() < 0) {
71      *error_msg = android::base::StringPrintf("Could not create read-only fd: %s",
72                                               strerror(errno));
73      return false;
74    }
75  }
76
77  constexpr bool kCompress = true;  // Ignore the config here. Dropbox will always end up
78                                    // compressing the data, might as well make the temp
79                                    // file smaller and help it out.
80  using DropBoxManager = android::os::DropBoxManager;
81  constexpr int kDropboxFlags = DropBoxManager::IS_GZIPPED;
82
83  if (!SerializeProtobuf(encodedProfile, std::move(tmp_fd), kCompress)) {
84    *error_msg = "Could not serialize to temp file";
85    return false;
86  }
87
88  sp<DropBoxManager> dropbox(new DropBoxManager());
89  android::binder::Status status =  dropbox->addFile(String16("perfprofd"),
90                                                     read_only.release(),
91                                                     kDropboxFlags);
92  if (!status.isOk()) {
93    *error_msg = status.toString8();
94    return false;
95  }
96  return true;
97}
98
99}  // namespace
100
101bool SendToDropbox(android::perfprofd::PerfprofdRecord* profile,
102                   const std::string& temp_directory,
103                   std::string* error_msg) {
104  size_t size = profile->ByteSize();
105  if (size < 1024 * 1024) {
106    // For a small size, send as a byte buffer directly.
107    std::unique_ptr<uint8_t[]> data(new uint8_t[size]);
108    profile->SerializeWithCachedSizesToArray(data.get());
109
110    using DropBoxManager = android::os::DropBoxManager;
111    sp<DropBoxManager> dropbox(new DropBoxManager());
112    android::binder::Status status = dropbox->addData(String16("perfprofd"),
113                                                      data.get(),
114                                                      size,
115                                                      0);
116    if (!status.isOk()) {
117      *error_msg = status.toString8();
118      return false;
119    }
120    return true;
121  } else {
122    // For larger buffers, we need to go through the filesystem.
123    return WriteDropboxFile(profile, temp_directory, error_msg);
124  }
125}
126
127}  // namespace dropbox
128}  // namespace perfprofd
129}  // namespace android
130