Created a dumpstate service.
For now this is still a limited service:
- It's only created when running an interactive bugreport.
- It only provides a listener to get updates.
- It will be just used by Shell to get updates.
Test: dumpstate_test passes
BUG: 31636879
Change-Id: Iae820261d220523c979bf905030456fcf0b2b618
diff --git a/cmds/dumpstate/Android.mk b/cmds/dumpstate/Android.mk
index da39d3a..3987fce 100644
--- a/cmds/dumpstate/Android.mk
+++ b/cmds/dumpstate/Android.mk
@@ -15,10 +15,13 @@
utils.cpp
COMMON_SHARED_LIBRARIES := \
libbase \
+ libbinder \
libcutils \
+ libdumpstateaidl \
libhardware_legacy \
liblog \
- libselinux
+ libselinux \
+ libutils
# ====================#
# libdumpstateheaders #
@@ -39,6 +42,27 @@
include $(BUILD_STATIC_LIBRARY)
+# ================ #
+# libdumpstateaidl #
+# =================#
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libdumpstateaidl
+
+LOCAL_CFLAGS := $(COMMON_LOCAL_CFLAGS)
+
+LOCAL_SHARED_LIBRARIES := \
+ libbinder \
+ libutils
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/binder
+LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/binder
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/binder
+LOCAL_SRC_FILES := \
+ binder/android/os/IDumpstateListener.aidl \
+ binder/android/os/IDumpstate.aidl
+
+include $(BUILD_SHARED_LIBRARY)
+
# ==========#
# dumpstate #
# ==========#
@@ -49,6 +73,7 @@
endif
LOCAL_SRC_FILES := $(COMMON_SRC_FILES) \
+ DumpstateService.cpp \
dumpstate.cpp
LOCAL_MODULE := dumpstate
@@ -77,6 +102,7 @@
LOCAL_CFLAGS := $(COMMON_LOCAL_CFLAGS)
LOCAL_SRC_FILES := $(COMMON_SRC_FILES) \
+ DumpstateService.cpp \
tests/dumpstate_test.cpp
LOCAL_STATIC_LIBRARIES := $(COMMON_ZIP_LIBRARIES) \
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
new file mode 100644
index 0000000..ce78ec6
--- /dev/null
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -0,0 +1,92 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "dumpstate"
+
+#include "DumpstateService.h"
+
+#include <android-base/stringprintf.h>
+
+#include "android/os/BnDumpstate.h"
+
+namespace android {
+namespace os {
+
+DumpstateService::DumpstateService() : ds_(Dumpstate::GetInstance()) {
+}
+
+char const* DumpstateService::getServiceName() {
+ return "dumpstate";
+}
+
+status_t DumpstateService::Start() {
+ IPCThreadState::self()->disableBackgroundScheduling(true);
+ status_t ret = BinderService<DumpstateService>::publish();
+ if (ret != android::OK) {
+ return ret;
+ }
+ sp<ProcessState> ps(ProcessState::self());
+ ps->startThreadPool();
+ ps->giveThreadPoolName();
+ return android::OK;
+}
+
+binder::Status DumpstateService::setListener(const std::string& name,
+ const sp<IDumpstateListener>& listener, bool* set) {
+ if (name.empty()) {
+ MYLOGE("setListener(): name not set\n");
+ *set = false;
+ return binder::Status::ok();
+ }
+ if (listener == nullptr) {
+ MYLOGE("setListener(): listener not set\n");
+ *set = false;
+ return binder::Status::ok();
+ }
+ std::lock_guard<std::mutex> lock(lock_);
+ if (ds_.listener_ != nullptr) {
+ MYLOGE("setListener(%s): already set (%s)\n", name.c_str(), ds_.listener_name_.c_str());
+ *set = false;
+ return binder::Status::ok();
+ }
+ ds_.listener_name_ = name;
+ ds_.listener_ = listener;
+ *set = true;
+ return binder::Status::ok();
+}
+
+status_t DumpstateService::dump(int fd, const Vector<String16>&) {
+ dprintf(fd, "id: %lu\n", ds_.id_);
+ dprintf(fd, "pid: %d\n", ds_.pid_);
+ dprintf(fd, "progress: %d / %d\n", ds_.progress_, ds_.weight_total_);
+ dprintf(fd, "args: %s\n", ds_.args_.c_str());
+ dprintf(fd, "extra_options: %s\n", ds_.extra_options_.c_str());
+ dprintf(fd, "version: %s\n", ds_.version_.c_str());
+ dprintf(fd, "bugreport_dir: %s\n", ds_.bugreport_dir_.c_str());
+ dprintf(fd, "screenshot_path: %s\n", ds_.screenshot_path_.c_str());
+ dprintf(fd, "log_path: %s\n", ds_.log_path_.c_str());
+ dprintf(fd, "tmp_path: %s\n", ds_.tmp_path_.c_str());
+ dprintf(fd, "path: %s\n", ds_.extra_options_.c_str());
+ dprintf(fd, "base_name: %s\n", ds_.base_name_.c_str());
+ dprintf(fd, "name: %s\n", ds_.name_.c_str());
+ dprintf(fd, "now: %ld\n", ds_.now_);
+ dprintf(fd, "is_zipping: %s\n", ds_.IsZipping() ? "true" : "false");
+ dprintf(fd, "listener: %s\n", ds_.listener_name_.c_str());
+
+ return NO_ERROR;
+}
+} // namespace os
+} // namespace android
diff --git a/cmds/dumpstate/DumpstateService.h b/cmds/dumpstate/DumpstateService.h
new file mode 100644
index 0000000..544a7b6
--- /dev/null
+++ b/cmds/dumpstate/DumpstateService.h
@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_OS_DUMPSTATE_H_
+#define ANDROID_OS_DUMPSTATE_H_
+
+#include <mutex>
+#include <vector>
+
+#include <binder/BinderService.h>
+
+#include "android/os/BnDumpstate.h"
+#include "dumpstate.h"
+
+namespace android {
+namespace os {
+
+class DumpstateService : public BinderService<DumpstateService>, public BnDumpstate {
+ public:
+ DumpstateService();
+
+ static status_t Start();
+ static char const* getServiceName();
+
+ status_t dump(int fd, const Vector<String16>& args) override;
+ binder::Status setListener(const std::string& name, const sp<IDumpstateListener>& listener,
+ bool* set) override;
+
+ private:
+ Dumpstate& ds_;
+ std::mutex lock_;
+};
+
+} // namespace os
+} // namespace android
+
+#endif // ANDROID_OS_DUMPSTATE_H_
diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
new file mode 100644
index 0000000..e585a0e
--- /dev/null
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -0,0 +1,33 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+import android.os.IDumpstateListener;
+
+/**
+ * Binder interface for the currently running dumpstate process.
+ * {@hide}
+ */
+interface IDumpstate {
+
+ /*
+ * Sets the listener for dumpstate progress.
+ *
+ * Returns true if the listener was set (it's like a Highlander: There Can be Only One).
+ */
+ boolean setListener(@utf8InCpp String name, IDumpstateListener listener);
+}
diff --git a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
new file mode 100644
index 0000000..32717f4
--- /dev/null
+++ b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2016, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.os;
+
+/**
+ * Listener for dumpstate events.
+ *
+ * {@hide}
+ */
+interface IDumpstateListener {
+ void onProgressUpdated(int progress);
+ void onMaxProgressUpdated(int maxProgress);
+}
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 740eb19..80a1b07 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -43,14 +43,13 @@
#include <android-base/unique_fd.h>
#include <cutils/properties.h>
#include <hardware_legacy/power.h>
-
+#include <openssl/sha.h>
#include <private/android_filesystem_config.h>
#include <private/android_logger.h>
+#include "DumpstateService.h"
#include "dumpstate.h"
-#include <openssl/sha.h>
-
/* read before root is shed */
static char cmdline_buf[16384] = "(unknown)";
static const char *dump_traces_path = NULL;
@@ -684,15 +683,11 @@
JustDumpFile("", "/proc/version");
printf("Command line: %s\n", strtok(cmdline_buf, "\n"));
printf("Bugreport format version: %s\n", version_.c_str());
- printf("Dumpstate info: id=%lu pid=%d dry_run=%d args=%s extra_options=%s\n", id_, getpid(),
+ printf("Dumpstate info: id=%lu pid=%d dry_run=%d args=%s extra_options=%s\n", id_, pid_,
dry_run_, args_.c_str(), extra_options_.c_str());
printf("\n");
}
-bool Dumpstate::IsZipping() const {
- return zip_writer_ != nullptr;
-}
-
// List of file extensions that can cause a zip file attachment to be rejected by some email
// service providers.
static const std::set<std::string> PROBLEMATIC_FILE_EXTENSIONS = {
@@ -1159,7 +1154,7 @@
DumpModemLogs();
printf("========================================================\n");
- printf("== Final progress (pid %d): %d/%d (originally %d)\n", getpid(), ds.progress_,
+ printf("== Final progress (pid %d): %d/%d (originally %d)\n", ds.pid_, ds.progress_,
ds.weight_total_, WEIGHT_TOTAL);
printf("========================================================\n");
printf("== dumpstate: done (id %lu)\n", ds.id_);
@@ -1316,6 +1311,7 @@
int do_broadcast = 0;
int is_remote_mode = 0;
bool show_header_only = false;
+ bool do_start_service = false;
/* set as high priority, and protect from OOM killer */
setpriority(PRIO_PROCESS, 0, -20);
@@ -1373,6 +1369,8 @@
// Framework uses a system property to override some command-line args.
// Currently, it contains the type of the requested bugreport.
if (ds.extra_options_ == "bugreportplus") {
+ // Currently, the dumpstate binder is only used by Shell to update progress.
+ do_start_service = true;
ds.update_progress_ = true;
do_fb = 0;
} else if (ds.extra_options_ == "bugreportremote") {
@@ -1435,6 +1433,14 @@
register_sig_handler();
}
+ if (do_start_service) {
+ MYLOGI("Starting 'dumpstate' service\n");
+ android::status_t ret;
+ if ((ret = android::os::DumpstateService::Start()) != android::OK) {
+ MYLOGE("Unable to start DumpstateService: %d\n", ret);
+ }
+ }
+
if (ds.IsDryRun()) {
MYLOGI("Running on dry-run mode (to disable it, call 'setprop dumpstate.dry_run false')\n");
}
@@ -1477,7 +1483,7 @@
ds.screenshot_path_ = ds.GetPath(".png");
}
ds.tmp_path_ = ds.GetPath(".tmp");
- ds.log_path_ = ds.GetPath("-dumpstate_log-" + std::to_string(getpid()) + ".txt");
+ ds.log_path_ = ds.GetPath("-dumpstate_log-" + std::to_string(ds.pid_) + ".txt");
MYLOGD(
"Bugreport dir: %s\n"
@@ -1510,7 +1516,7 @@
"--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
"--es", "android.intent.extra.NAME", ds.name_,
"--ei", "android.intent.extra.ID", std::to_string(ds.id_),
- "--ei", "android.intent.extra.PID", std::to_string(getpid()),
+ "--ei", "android.intent.extra.PID", std::to_string(ds.pid_),
"--ei", "android.intent.extra.MAX", std::to_string(WEIGHT_TOTAL),
};
// clang-format on
@@ -1630,7 +1636,7 @@
/* check if user changed the suffix using system properties */
std::string name = android::base::GetProperty(
- android::base::StringPrintf("dumpstate.%d.name", getpid()), "");
+ android::base::StringPrintf("dumpstate.%d.name", ds.pid_), "");
bool change_suffix= false;
if (!name.empty()) {
/* must whitelist which characters are allowed, otherwise it could cross directories */
@@ -1713,7 +1719,7 @@
std::vector<std::string> am_args = {
"--receiver-permission", "android.permission.DUMP", "--receiver-foreground",
"--ei", "android.intent.extra.ID", std::to_string(ds.id_),
- "--ei", "android.intent.extra.PID", std::to_string(getpid()),
+ "--ei", "android.intent.extra.PID", std::to_string(ds.pid_),
"--ei", "android.intent.extra.MAX", std::to_string(ds.weight_total_),
"--es", "android.intent.extra.BUGREPORT", ds.path_,
"--es", "android.intent.extra.DUMPSTATE_LOG", ds.log_path_
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 950f185..0680db2 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -40,6 +40,8 @@
#include <android-base/macros.h>
#include <ziparchive/zip_writer.h>
+#include "android/os/BnDumpstate.h"
+
// Workaround for const char *args[MAX_ARGS_ARRAY_SIZE] variables until they're converted to
// std::vector<std::string>
// TODO: remove once not used
@@ -332,6 +334,9 @@
// dumpstate id - unique after each device reboot.
unsigned long id_;
+ // dumpstate pid
+ pid_t pid_;
+
// Whether progress updates should be published.
bool update_progress_ = false;
@@ -387,6 +392,10 @@
// Pointer to the zip structure.
std::unique_ptr<ZipWriter> zip_writer_;
+ // Binder object listing to progress.
+ android::sp<android::os::IDumpstateListener> listener_;
+ std::string listener_name_;
+
private:
// Used by GetInstance() only.
Dumpstate(const std::string& version = VERSION_CURRENT, bool dry_run = false,
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index 4073c3c..d692fda 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -14,6 +14,11 @@
* limitations under the License.
*/
+#define LOG_TAG "dumpstate"
+#include <cutils/log.h>
+
+#include "DumpstateService.h"
+#include "android/os/BnDumpstate.h"
#include "dumpstate.h"
#include <gmock/gmock.h>
@@ -30,8 +35,7 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
-#define LOG_TAG "dumpstate"
-#include <cutils/log.h>
+using namespace android;
using ::testing::EndsWith;
using ::testing::IsEmpty;
@@ -43,10 +47,22 @@
using ::testing::internal::GetCapturedStderr;
using ::testing::internal::GetCapturedStdout;
+using os::DumpstateService;
+using os::IDumpstateListener;
+
// Not used on test cases yet...
void dumpstate_board(void) {
}
+class DumpstateListenerMock : public IDumpstateListener {
+ public:
+ MOCK_METHOD1(onProgressUpdated, binder::Status(int32_t progress));
+ MOCK_METHOD1(onMaxProgressUpdated, binder::Status(int32_t max_progress));
+
+ protected:
+ MOCK_METHOD0(onAsBinder, IBinder*());
+};
+
class DumpstateTest : public Test {
public:
void SetUp() {
@@ -102,26 +118,46 @@
EXPECT_THAT(expected_value, StrEq(actualValue)) << "invalid value for property " << key;
}
- std::string GetProgressMessage(int progress, int weigh_total, int old_weigh_total = 0) {
+ // TODO: remove when progress is set by Binder callbacks.
+ std::string GetProgressMessageAndAssertSystemProperties(int progress, int weight_total,
+ int old_weight_total = 0) {
EXPECT_EQ(progress, ds.progress_) << "invalid progress";
- EXPECT_EQ(weigh_total, ds.weight_total_) << "invalid weigh_total";
+ EXPECT_EQ(weight_total, ds.weight_total_) << "invalid weight_total";
AssertSystemProperty(android::base::StringPrintf("dumpstate.%d.progress", getpid()),
std::to_string(progress));
- bool max_increased = old_weigh_total > 0;
+ bool max_increased = old_weight_total > 0;
std::string adjustment_message = "";
if (max_increased) {
AssertSystemProperty(android::base::StringPrintf("dumpstate.%d.max", getpid()),
- std::to_string(weigh_total));
+ std::to_string(weight_total));
adjustment_message = android::base::StringPrintf(
- "Adjusting total weight from %d to %d\n", old_weigh_total, weigh_total);
+ "Adjusting total weight from %d to %d\n", old_weight_total, weight_total);
}
return android::base::StringPrintf("%sSetting progress (dumpstate.%d.progress): %d/%d\n",
adjustment_message.c_str(), getpid(), progress,
- weigh_total);
+ weight_total);
+ }
+
+ std::string GetProgressMessage(const std::string& listener_name, int progress, int weight_total,
+ int old_weight_total = 0) {
+ EXPECT_EQ(progress, ds.progress_) << "invalid progress";
+ EXPECT_EQ(weight_total, ds.weight_total_) << "invalid weight_total";
+
+ bool max_increased = old_weight_total > 0;
+
+ std::string adjustment_message = "";
+ if (max_increased) {
+ adjustment_message = android::base::StringPrintf(
+ "Adjusting total weight from %d to %d\n", old_weight_total, weight_total);
+ }
+
+ return android::base::StringPrintf("%sSetting progress (%s): %d/%d\n",
+ adjustment_message.c_str(), listener_name.c_str(),
+ progress, weight_total);
}
// `stdout` and `stderr` from the last command ran.
@@ -276,35 +312,75 @@
" --pid --sleep 20' failed: killed by signal 15\n"));
}
-TEST_F(DumpstateTest, RunCommandProgress) {
+TEST_F(DumpstateTest, RunCommandProgressNoListener) {
ds.update_progress_ = true;
ds.progress_ = 0;
ds.weight_total_ = 30;
EXPECT_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(20).Build()));
- std::string progress_message = GetProgressMessage(20, 30);
+ std::string progress_message = GetProgressMessageAndAssertSystemProperties(20, 30);
EXPECT_THAT(out, StrEq("stdout\n"));
EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
EXPECT_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(10).Build()));
- progress_message = GetProgressMessage(30, 30);
+ progress_message = GetProgressMessageAndAssertSystemProperties(30, 30);
EXPECT_THAT(out, StrEq("stdout\n"));
EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
// Run a command that will increase maximum timeout.
EXPECT_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(1).Build()));
- progress_message = GetProgressMessage(31, 36, 30); // 20% increase
+ progress_message = GetProgressMessageAndAssertSystemProperties(31, 36, 30); // 20% increase
EXPECT_THAT(out, StrEq("stdout\n"));
EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
// Make sure command ran while in dry_run is counted.
SetDryRun(true);
EXPECT_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(4).Build()));
- progress_message = GetProgressMessage(35, 36);
+ progress_message = GetProgressMessageAndAssertSystemProperties(35, 36);
EXPECT_THAT(out, IsEmpty());
EXPECT_THAT(err, StrEq(progress_message));
}
+TEST_F(DumpstateTest, RunCommandProgress) {
+ sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
+ ds.listener_ = listener;
+ ds.listener_name_ = "FoxMulder";
+
+ ds.update_progress_ = true;
+ ds.progress_ = 0;
+ ds.weight_total_ = 30;
+
+ EXPECT_CALL(*listener, onProgressUpdated(20));
+ EXPECT_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(20).Build()));
+ std::string progress_message = GetProgressMessage(ds.listener_name_, 20, 30);
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+ EXPECT_CALL(*listener, onProgressUpdated(30));
+ EXPECT_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(10).Build()));
+ progress_message = GetProgressMessage(ds.listener_name_, 30, 30);
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+ // Run a command that will increase maximum timeout.
+ EXPECT_CALL(*listener, onProgressUpdated(31));
+ EXPECT_CALL(*listener, onMaxProgressUpdated(36));
+ EXPECT_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(1).Build()));
+ progress_message = GetProgressMessage(ds.listener_name_, 31, 36, 30); // 20% increase
+ EXPECT_THAT(out, StrEq("stdout\n"));
+ EXPECT_THAT(err, StrEq("stderr\n" + progress_message));
+
+ // Make sure command ran while in dry_run is counted.
+ SetDryRun(true);
+ EXPECT_CALL(*listener, onProgressUpdated(35));
+ EXPECT_EQ(0, RunCommand("", {simple_command}, CommandOptions::WithTimeout(4).Build()));
+ progress_message = GetProgressMessage(ds.listener_name_, 35, 36);
+ EXPECT_THAT(out, IsEmpty());
+ EXPECT_THAT(err, StrEq(progress_message));
+
+ ds.listener_.clear();
+}
+
TEST_F(DumpstateTest, RunCommandDropRoot) {
// First check root case - only available when running with 'adb root'.
uid_t uid = getuid();
@@ -410,15 +486,65 @@
EXPECT_THAT(err, IsEmpty());
}
-TEST_F(DumpstateTest, DumpFileUpdateProgress) {
+TEST_F(DumpstateTest, DumpFileUpdateProgressNoListener) {
ds.update_progress_ = true;
ds.progress_ = 0;
ds.weight_total_ = 30;
EXPECT_EQ(0, DumpFile("", test_data_path + "single-line.txt"));
- std::string progress_message = GetProgressMessage(5, 30); // TODO: unhardcode WEIGHT_FILE (5)?
+ std::string progress_message =
+ GetProgressMessageAndAssertSystemProperties(5, 30); // TODO: unhardcode WEIGHT_FILE (5)?
EXPECT_THAT(err, StrEq(progress_message));
EXPECT_THAT(out, StrEq("I AM LINE1\n")); // dumpstate adds missing newline
}
+
+TEST_F(DumpstateTest, DumpFileUpdateProgress) {
+ sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
+ ds.listener_ = listener;
+ ds.listener_name_ = "FoxMulder";
+ ds.update_progress_ = true;
+ ds.progress_ = 0;
+ ds.weight_total_ = 30;
+
+ EXPECT_CALL(*listener, onProgressUpdated(5));
+ EXPECT_EQ(0, DumpFile("", test_data_path + "single-line.txt"));
+
+ std::string progress_message =
+ GetProgressMessage(ds.listener_name_, 5, 30); // TODO: unhardcode WEIGHT_FILE (5)?
+ EXPECT_THAT(err, StrEq(progress_message));
+ EXPECT_THAT(out, StrEq("I AM LINE1\n")); // dumpstate adds missing newline
+
+ ds.listener_.clear();
+}
+
+class DumpstateServiceTest : public Test {
+ public:
+ DumpstateService dss;
+};
+
+TEST_F(DumpstateServiceTest, SetListenerNoName) {
+ sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
+ bool result;
+ EXPECT_TRUE(dss.setListener("", listener, &result).isOk());
+ EXPECT_FALSE(result);
+}
+
+TEST_F(DumpstateServiceTest, SetListenerNoPointer) {
+ bool result;
+ EXPECT_TRUE(dss.setListener("whatever", nullptr, &result).isOk());
+ EXPECT_FALSE(result);
+}
+
+TEST_F(DumpstateServiceTest, SetListenerTwice) {
+ sp<DumpstateListenerMock> listener(new DumpstateListenerMock());
+ bool result;
+ EXPECT_TRUE(dss.setListener("whatever", listener, &result).isOk());
+ EXPECT_TRUE(result);
+
+ EXPECT_THAT(Dumpstate::GetInstance().listener_name_, StrEq("whatever"));
+
+ EXPECT_TRUE(dss.setListener("whatever", listener, &result).isOk());
+ EXPECT_FALSE(result);
+}
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index c41cca4..34e09d7 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -165,7 +165,11 @@
}
Dumpstate::Dumpstate(const std::string& version, bool dry_run, const std::string& build_type)
- : version_(version), now_(time(nullptr)), dry_run_(dry_run), build_type_(build_type) {
+ : pid_(getpid()),
+ version_(version),
+ now_(time(nullptr)),
+ dry_run_(dry_run),
+ build_type_(build_type) {
}
Dumpstate& Dumpstate::GetInstance() {
@@ -211,6 +215,10 @@
return "user" == build_type_;
}
+bool Dumpstate::IsZipping() const {
+ return zip_writer_ != nullptr;
+}
+
std::string Dumpstate::GetPath(const std::string& suffix) const {
return android::base::StringPrintf("%s/%s-%s%s", bugreport_dir_.c_str(), base_name_.c_str(),
name_.c_str(), suffix.c_str());
@@ -1317,6 +1325,7 @@
progress_ += delta;
+ // TODO: remove property support once Shell uses IDumpstateListener
char key[PROPERTY_KEY_MAX];
char value[PROPERTY_VALUE_MAX];
@@ -1325,36 +1334,55 @@
int new_total = weight_total_ * 1.2;
MYLOGD("Adjusting total weight from %d to %d\n", weight_total_, new_total);
weight_total_ = new_total;
- snprintf(key, sizeof(key), "dumpstate.%d.max", getpid());
- snprintf(value, sizeof(value), "%d", weight_total_);
- int status = property_set(key, value);
- if (status != 0) {
- MYLOGE("Could not update max weight by setting system property %s to %s: %d\n",
- key, value, status);
+
+ if (listener_ != nullptr) {
+ listener_->onMaxProgressUpdated(weight_total_);
+ } else {
+ snprintf(key, sizeof(key), "dumpstate.%d.max", pid_);
+ snprintf(value, sizeof(value), "%d", weight_total_);
+ int status = property_set(key, value);
+ if (status != 0) {
+ MYLOGE("Could not update max weight by setting system property %s to %s: %d\n", key,
+ value, status);
+ }
}
}
- snprintf(key, sizeof(key), "dumpstate.%d.progress", getpid());
- snprintf(value, sizeof(value), "%d", progress_);
-
- if (progress_ % 100 == 0) {
- // We don't want to spam logcat, so only log multiples of 100.
- MYLOGD("Setting progress (%s): %s/%d\n", key, value, weight_total_);
- } else {
- // stderr is ignored on normal invocations, but useful when calling /system/bin/dumpstate
- // directly for debuggging.
- fprintf(stderr, "Setting progress (%s): %s/%d\n", key, value, weight_total_);
- }
-
if (control_socket_fd_ >= 0) {
dprintf(control_socket_fd_, "PROGRESS:%d/%d\n", progress_, weight_total_);
fsync(control_socket_fd_);
}
- int status = property_set(key, value);
- if (status) {
- MYLOGE("Could not update progress by setting system property %s to %s: %d\n",
- key, value, status);
+ if (listener_ != nullptr) {
+ if (progress_ % 100 == 0) {
+ // We don't want to spam logcat, so only log multiples of 100.
+ MYLOGD("Setting progress (%s): %d/%d\n", listener_name_.c_str(), progress_,
+ weight_total_);
+ } else {
+ // stderr is ignored on normal invocations, but useful when calling
+ // /system/bin/dumpstate directly for debuggging.
+ fprintf(stderr, "Setting progress (%s): %d/%d\n", listener_name_.c_str(), progress_,
+ weight_total_);
+ }
+ listener_->onProgressUpdated(progress_);
+ } else {
+ snprintf(key, sizeof(key), "dumpstate.%d.progress", pid_);
+ snprintf(value, sizeof(value), "%d", progress_);
+
+ if (progress_ % 100 == 0) {
+ // We don't want to spam logcat, so only log multiples of 100.
+ MYLOGD("Setting progress (%s): %s/%d\n", key, value, weight_total_);
+ } else {
+ // stderr is ignored on normal invocations, but useful when calling
+ // /system/bin/dumpstate directly for debuggging.
+ fprintf(stderr, "Setting progress (%s): %s/%d\n", key, value, weight_total_);
+ }
+
+ int status = property_set(key, value);
+ if (status) {
+ MYLOGE("Could not update progress by setting system property %s to %s: %d\n", key,
+ value, status);
+ }
}
}