[DO NOT MERGE] Use FUSE_SHORTCIRCUIT if available
am: 738e0dcf0a
Change-Id: I9de236b53b4f4f449de65bfb856771278b92fa9e
diff --git a/CleanSpec.mk b/CleanSpec.mk
index b3661e4..5d335b5 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -56,3 +56,4 @@
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/EXECUTABLES/lmkd_intermediates/import_includes)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/SHARED_LIBRARIES/libsysutils_intermediates/import_includes)
$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/bin/grep $(PRODUCT_OUT)/system/bin/toolbox)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/root/vendor)
diff --git a/adb/bugreport.cpp b/adb/bugreport.cpp
index 64f01bd..c348dd5 100644
--- a/adb/bugreport.cpp
+++ b/adb/bugreport.cpp
@@ -14,28 +14,40 @@
* limitations under the License.
*/
+#define TRACE_TAG ADB
+
+#include "bugreport.h"
+
#include <string>
+#include <vector>
#include <android-base/strings.h>
-#include "bugreport.h"
+#include "sysdeps.h"
+#include "adb_utils.h"
#include "file_sync_service.h"
-static constexpr char BUGZ_OK_PREFIX[] = "OK:";
-static constexpr char BUGZ_FAIL_PREFIX[] = "FAIL:";
+static constexpr char BUGZ_BEGIN_PREFIX[] = "BEGIN:";
static constexpr char BUGZ_PROGRESS_PREFIX[] = "PROGRESS:";
static constexpr char BUGZ_PROGRESS_SEPARATOR[] = "/";
+static constexpr char BUGZ_OK_PREFIX[] = "OK:";
+static constexpr char BUGZ_FAIL_PREFIX[] = "FAIL:";
// Custom callback used to handle the output of zipped bugreports.
class BugreportStandardStreamsCallback : public StandardStreamsCallbackInterface {
public:
- BugreportStandardStreamsCallback(const std::string& dest_file, bool show_progress, Bugreport* br)
+ BugreportStandardStreamsCallback(const std::string& dest_dir, const std::string& dest_file,
+ bool show_progress, Bugreport* br)
: br_(br),
+ src_file_(),
+ dest_dir_(dest_dir),
dest_file_(dest_file),
- line_message_(android::base::StringPrintf("generating %s", dest_file_.c_str())),
+ line_message_(),
+ invalid_lines_(),
show_progress_(show_progress),
- status_(-1),
+ status_(0),
line_() {
+ SetLineMessage("generating");
}
void OnStdout(const char* buffer, int length) {
@@ -54,28 +66,72 @@
OnStream(nullptr, stderr, buffer, length);
}
int Done(int unused_) {
- // Process remaining line, if any...
+ // Process remaining line, if any.
ProcessLine(line_);
- // ..then return.
+
+ // Warn about invalid lines, if any.
+ if (!invalid_lines_.empty()) {
+ fprintf(stderr,
+ "WARNING: bugreportz generated %zu line(s) with unknown commands, "
+ "device might not support zipped bugreports:\n",
+ invalid_lines_.size());
+ for (const auto& line : invalid_lines_) {
+ fprintf(stderr, "\t%s\n", line.c_str());
+ }
+ fprintf(stderr,
+ "If the zipped bugreport was not generated, try 'adb bugreport' instead.\n");
+ }
+
+ // Pull the generated bug report.
+ if (status_ == 0) {
+ if (src_file_.empty()) {
+ fprintf(stderr, "bugreportz did not return a '%s' or '%s' line\n", BUGZ_OK_PREFIX,
+ BUGZ_FAIL_PREFIX);
+ return -1;
+ }
+ std::string destination;
+ if (dest_dir_.empty()) {
+ destination = dest_file_;
+ } else {
+ destination = android::base::StringPrintf("%s%c%s", dest_dir_.c_str(),
+ OS_PATH_SEPARATOR, dest_file_.c_str());
+ }
+ std::vector<const char*> srcs{src_file_.c_str()};
+ SetLineMessage("pulling");
+ status_ =
+ br_->DoSyncPull(srcs, destination.c_str(), true, line_message_.c_str()) ? 0 : 1;
+ if (status_ != 0) {
+ fprintf(stderr,
+ "Bug report finished but could not be copied to '%s'.\n"
+ "Try to run 'adb pull %s <directory>'\n"
+ "to copy it to a directory that can be written.\n",
+ destination.c_str(), src_file_.c_str());
+ }
+ }
return status_;
}
private:
+ void SetLineMessage(const std::string& action) {
+ line_message_ = action + " " + adb_basename(dest_file_);
+ }
+
+ void SetSrcFile(const std::string path) {
+ src_file_ = path;
+ if (!dest_dir_.empty()) {
+ // Only uses device-provided name when user passed a directory.
+ dest_file_ = adb_basename(path);
+ SetLineMessage("generating");
+ }
+ }
+
void ProcessLine(const std::string& line) {
if (line.empty()) return;
- if (android::base::StartsWith(line, BUGZ_OK_PREFIX)) {
- if (show_progress_) {
- // Make sure pull message doesn't conflict with generation message.
- br_->UpdateProgress(line_message_, 100, 100);
- }
-
- const char* zip_file = &line[strlen(BUGZ_OK_PREFIX)];
- std::vector<const char*> srcs{zip_file};
- status_ = br_->DoSyncPull(srcs, dest_file_.c_str(), true, line_message_.c_str()) ? 0 : 1;
- if (status_ != 0) {
- fprintf(stderr, "Could not copy file '%s' to '%s'\n", zip_file, dest_file_.c_str());
- }
+ if (android::base::StartsWith(line, BUGZ_BEGIN_PREFIX)) {
+ SetSrcFile(&line[strlen(BUGZ_BEGIN_PREFIX)]);
+ } else if (android::base::StartsWith(line, BUGZ_OK_PREFIX)) {
+ SetSrcFile(&line[strlen(BUGZ_OK_PREFIX)]);
} else if (android::base::StartsWith(line, BUGZ_FAIL_PREFIX)) {
const char* error_message = &line[strlen(BUGZ_FAIL_PREFIX)];
fprintf(stderr, "Device failed to take a zipped bugreport: %s\n", error_message);
@@ -89,24 +145,38 @@
size_t idx2 = line.rfind(BUGZ_PROGRESS_SEPARATOR);
int progress = std::stoi(line.substr(idx1, (idx2 - idx1)));
int total = std::stoi(line.substr(idx2 + 1));
- br_->UpdateProgress(dest_file_, progress, total);
+ br_->UpdateProgress(line_message_, progress, total);
} else {
- fprintf(stderr,
- "WARNING: unexpected line (%s) returned by bugreportz, "
- "device probably does not support zipped bugreports.\n"
- "Try 'adb bugreport' instead.",
- line.c_str());
+ invalid_lines_.push_back(line);
}
}
Bugreport* br_;
- const std::string dest_file_;
- const std::string line_message_;
+
+ // Path of bugreport on device.
+ std::string src_file_;
+
+ // Bugreport destination on host, depending on argument passed on constructor:
+ // - if argument is a directory, dest_dir_ is set with it and dest_file_ will be the name
+ // of the bugreport reported by the device.
+ // - if argument is empty, dest_dir is set as the current directory and dest_file_ will be the
+ // name of the bugreport reported by the device.
+ // - otherwise, dest_dir_ is not set and dest_file_ is set with the value passed on constructor.
+ std::string dest_dir_, dest_file_;
+
+ // Message displayed on LinePrinter, it's updated every time the destination above change.
+ std::string line_message_;
+
+ // Lines sent by bugreportz that contain invalid commands; will be displayed at the end.
+ std::vector<std::string> invalid_lines_;
+
+ // Whether PROGRESS_LINES should be interpreted as progress.
bool show_progress_;
+
+ // Overall process of the operation, as returned by Done().
int status_;
- // Temporary buffer containing the characters read since the last newline
- // (\n).
+ // Temporary buffer containing the characters read since the last newline (\n).
std::string line_;
DISALLOW_COPY_AND_ASSIGN(BugreportStandardStreamsCallback);
@@ -116,34 +186,62 @@
int usage();
int Bugreport::DoIt(TransportType transport_type, const char* serial, int argc, const char** argv) {
- if (argc == 1) return SendShellCommand(transport_type, serial, "bugreport", false);
- if (argc != 2) return usage();
-
- // Zipped bugreport option - will call 'bugreportz', which prints the location
- // of the generated
- // file, then pull it to the destination file provided by the user.
- std::string dest_file = argv[1];
- if (!android::base::EndsWith(argv[1], ".zip")) {
- // TODO: use a case-insensitive comparison (like EndsWithIgnoreCase
- dest_file += ".zip";
- }
+ if (argc > 2) return usage();
// Gets bugreportz version.
- std::string bugz_stderr;
- DefaultStandardStreamsCallback version_callback(nullptr, &bugz_stderr);
+ std::string bugz_stdout, bugz_stderr;
+ DefaultStandardStreamsCallback version_callback(&bugz_stdout, &bugz_stderr);
int status = SendShellCommand(transport_type, serial, "bugreportz -v", false, &version_callback);
-
- if (status != 0) {
- fprintf(stderr,
- "Failed to get bugreportz version: 'bugreport -v' returned '%s' "
- "(code %d)."
- "\nIf the device does not support it, try running 'adb bugreport' "
- "to get a "
- "flat-file bugreport.",
- bugz_stderr.c_str(), status);
- return status;
- }
std::string bugz_version = android::base::Trim(bugz_stderr);
+ std::string bugz_output = android::base::Trim(bugz_stdout);
+
+ if (status != 0 || bugz_version.empty()) {
+ D("'bugreportz' -v results: status=%d, stdout='%s', stderr='%s'", status,
+ bugz_output.c_str(), bugz_version.c_str());
+ if (argc == 1) {
+ // Device does not support bugreportz: if called as 'adb bugreport', just falls out to
+ // the flat-file version.
+ fprintf(stderr,
+ "Failed to get bugreportz version, which is only available on devices "
+ "running Android 7.0 or later.\nTrying a plain-text bug report instead.\n");
+ return SendShellCommand(transport_type, serial, "bugreport", false);
+ }
+
+ // But if user explicitly asked for a zipped bug report, fails instead (otherwise calling
+ // 'bugreport' would generate a lot of output the user might not be prepared to handle).
+ fprintf(stderr,
+ "Failed to get bugreportz version: 'bugreportz -v' returned '%s' (code %d).\n"
+ "If the device does not run Android 7.0 or above, try 'adb bugreport' instead.\n",
+ bugz_output.c_str(), status);
+ return status != 0 ? status : -1;
+ }
+
+ std::string dest_file, dest_dir;
+
+ if (argc == 1) {
+ // No args - use current directory
+ if (!getcwd(&dest_dir)) {
+ perror("adb: getcwd failed");
+ return 1;
+ }
+ } else {
+ // Check whether argument is a directory or file
+ if (directory_exists(argv[1])) {
+ dest_dir = argv[1];
+ } else {
+ dest_file = argv[1];
+ }
+ }
+
+ if (dest_file.empty()) {
+ // Uses a default value until device provides the proper name
+ dest_file = "bugreport.zip";
+ } else {
+ if (!android::base::EndsWith(dest_file, ".zip")) {
+ // TODO: use a case-insensitive comparison (like EndsWithIgnoreCase
+ dest_file += ".zip";
+ }
+ }
bool show_progress = true;
std::string bugz_command = "bugreportz -p";
@@ -153,12 +251,11 @@
fprintf(stderr,
"Bugreport is in progress and it could take minutes to complete.\n"
"Please be patient and do not cancel or disconnect your device "
- "until it completes."
- "\n");
+ "until it completes.\n");
show_progress = false;
bugz_command = "bugreportz";
}
- BugreportStandardStreamsCallback bugz_callback(dest_file, show_progress, this);
+ BugreportStandardStreamsCallback bugz_callback(dest_dir, dest_file, show_progress, this);
return SendShellCommand(transport_type, serial, bugz_command, false, &bugz_callback);
}
diff --git a/adb/bugreport_test.cpp b/adb/bugreport_test.cpp
index a89d8dc..1129285 100644
--- a/adb/bugreport_test.cpp
+++ b/adb/bugreport_test.cpp
@@ -19,6 +19,12 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
+#include <android-base/strings.h>
+#include <android-base/test_utils.h>
+
+#include "sysdeps.h"
+#include "adb_utils.h"
+
using ::testing::_;
using ::testing::Action;
using ::testing::ActionInterface;
@@ -30,7 +36,9 @@
using ::testing::StrEq;
using ::testing::WithArg;
using ::testing::internal::CaptureStderr;
+using ::testing::internal::CaptureStdout;
using ::testing::internal::GetCapturedStderr;
+using ::testing::internal::GetCapturedStdout;
// Empty function so tests don't need to be linked against file_sync_service.cpp, which requires
// SELinux and its transitive dependencies...
@@ -122,81 +130,139 @@
class BugreportTest : public ::testing::Test {
public:
- void SetBugreportzVersion(const std::string& version) {
+ void SetUp() {
+ if (!getcwd(&cwd_)) {
+ ADD_FAILURE() << "getcwd failed: " << strerror(errno);
+ return;
+ }
+ }
+
+ void ExpectBugreportzVersion(const std::string& version) {
EXPECT_CALL(br_,
SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _))
.WillOnce(DoAll(WithArg<4>(WriteOnStderr(version.c_str())),
WithArg<4>(ReturnCallbackDone(0))));
}
- void ExpectProgress(int progress, int total) {
- EXPECT_CALL(br_, UpdateProgress(HasSubstr("file.zip"), progress, total));
+ void ExpectProgress(int progress, int total, const std::string& file = "file.zip") {
+ EXPECT_CALL(br_, UpdateProgress(StrEq("generating " + file), progress, total));
}
BugreportMock br_;
+ std::string cwd_; // TODO: make it static
};
-// Tests when called with invalid number of argumnts
+// Tests when called with invalid number of arguments
TEST_F(BugreportTest, InvalidNumberArgs) {
- const char* args[1024] = {"bugreport", "to", "principal"};
+ const char* args[] = {"bugreport", "to", "principal"};
ASSERT_EQ(-42, br_.DoIt(kTransportLocal, "HannibalLecter", 3, args));
}
-// Tests the legacy 'adb bugreport' option
-TEST_F(BugreportTest, FlatFileFormat) {
+// Tests the 'adb bugreport' option when the device does not support 'bugreportz' - it falls back
+// to the flat-file format ('bugreport' binary on device)
+TEST_F(BugreportTest, NoArgumentsPreNDevice) {
+ // clang-format off
+ EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _))
+ .WillOnce(DoAll(WithArg<4>(WriteOnStderr("")),
+ // Write some bogus output on stdout to make sure it's ignored
+ WithArg<4>(WriteOnStdout("Dude, where is my bugreportz?")),
+ WithArg<4>(ReturnCallbackDone(0))));
+ // clang-format on
+ std::string bugreport = "Reported the bug was.";
+ CaptureStdout();
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreport", false, _))
- .WillOnce(Return(0));
+ .WillOnce(DoAll(WithArg<4>(WriteOnStdout(bugreport)), Return(0)));
- const char* args[1024] = {"bugreport"};
+ const char* args[] = {"bugreport"};
+ ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
+ ASSERT_THAT(GetCapturedStdout(), StrEq(bugreport));
+}
+
+// Tests the 'adb bugreport' option when the device supports 'bugreportz' version 1.0 - it will
+// save the bugreport in the current directory with the name provided by the device.
+TEST_F(BugreportTest, NoArgumentsNDevice) {
+ ExpectBugreportzVersion("1.0");
+
+ std::string dest_file =
+ android::base::StringPrintf("%s%cda_bugreport.zip", cwd_.c_str(), OS_PATH_SEPARATOR);
+ EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
+ .WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
+ WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
+ true, StrEq("pulling da_bugreport.zip")))
+ .WillOnce(Return(true));
+
+ const char* args[] = {"bugreport"};
ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
}
-// Tests 'adb bugreport file.zip' when it succeeds and device does not support
-// progress.
-TEST_F(BugreportTest, OkLegacy) {
- SetBugreportzVersion("1.0");
+// Tests the 'adb bugreport' option when the device supports 'bugreportz' version 1.1 - it will
+// save the bugreport in the current directory with the name provided by the device.
+TEST_F(BugreportTest, NoArgumentsPostNDevice) {
+ ExpectBugreportzVersion("1.1");
+ std::string dest_file =
+ android::base::StringPrintf("%s%cda_bugreport.zip", cwd_.c_str(), OS_PATH_SEPARATOR);
+ ExpectProgress(50, 100, "da_bugreport.zip");
+ EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+ .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
+ WithArg<4>(WriteOnStdout("PROGRESS:50/100\n")),
+ WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip\n")),
+ WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
+ true, StrEq("pulling da_bugreport.zip")))
+ .WillOnce(Return(true));
+
+ const char* args[] = {"bugreport"};
+ ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 1, args));
+}
+
+// Tests 'adb bugreport file.zip' when it succeeds and device does not support progress.
+TEST_F(BugreportTest, OkNDevice) {
+ ExpectBugreportzVersion("1.0");
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
.WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
WithArg<4>(ReturnCallbackDone())));
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
- true, HasSubstr("file.zip")))
+ true, StrEq("pulling file.zip")))
.WillOnce(Return(true));
- const char* args[1024] = {"bugreport", "file.zip"};
+ const char* args[] = {"bugreport", "file.zip"};
ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
}
// Tests 'adb bugreport file.zip' when it succeeds but response was sent in
// multiple buffer writers and without progress updates.
-TEST_F(BugreportTest, OkLegacySplitBuffer) {
- SetBugreportzVersion("1.0");
+TEST_F(BugreportTest, OkNDeviceSplitBuffer) {
+ ExpectBugreportzVersion("1.0");
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
.WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device")),
WithArg<4>(WriteOnStdout("/bugreport.zip")),
WithArg<4>(ReturnCallbackDone())));
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
- true, HasSubstr("file.zip")))
+ true, StrEq("pulling file.zip")))
.WillOnce(Return(true));
- const char* args[1024] = {"bugreport", "file.zip"};
+ const char* args[] = {"bugreport", "file.zip"};
ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
}
// Tests 'adb bugreport file.zip' when it succeeds and displays progress.
-TEST_F(BugreportTest, Ok) {
- SetBugreportzVersion("1.1");
+TEST_F(BugreportTest, OkProgress) {
+ ExpectBugreportzVersion("1.1");
ExpectProgress(1, 100);
ExpectProgress(10, 100);
ExpectProgress(50, 100);
ExpectProgress(99, 100);
- ExpectProgress(100, 100);
// clang-format off
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+ // NOTE: DoAll accepts at most 10 arguments, and we're almost reached that limit...
.WillOnce(DoAll(
+ // Name might change on OK, so make sure the right one is picked.
+ WithArg<4>(WriteOnStdout("BEGIN:/device/bugreport___NOT.zip\n")),
// Progress line in one write
WithArg<4>(WriteOnStdout("PROGRESS:1/100\n")),
// Add some bogus lines
- WithArg<4>(WriteOnStdout("\nDUDE:SWEET\n\n")),
+ WithArg<4>(WriteOnStdout("\nDUDE:SWEET\n\nBLA\n\nBLA\nBLA\n\n")),
// Multiple progress lines in one write
WithArg<4>(WriteOnStdout("PROGRESS:10/100\nPROGRESS:50/100\n")),
// Progress line in multiple writes
@@ -207,38 +273,76 @@
WithArg<4>(WriteOnStdout("OK:/device/bugreport")),
WithArg<4>(WriteOnStdout(".zip")),
WithArg<4>(ReturnCallbackDone())));
+ // clang-format on
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
- true, HasSubstr("file.zip")))
+ true, StrEq("pulling file.zip")))
.WillOnce(Return(true));
- const char* args[1024] = {"bugreport", "file.zip"};
+ const char* args[] = {"bugreport", "file.zip"};
+ ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
+
+// Tests 'adb bugreport dir' when it succeeds and destination is a directory.
+TEST_F(BugreportTest, OkDirectory) {
+ ExpectBugreportzVersion("1.1");
+ TemporaryDir td;
+ std::string dest_file =
+ android::base::StringPrintf("%s%cda_bugreport.zip", td.path, OS_PATH_SEPARATOR);
+
+ EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
+ .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
+ WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
+ WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
+ true, StrEq("pulling da_bugreport.zip")))
+ .WillOnce(Return(true));
+
+ const char* args[] = {"bugreport", td.path};
ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
}
// Tests 'adb bugreport file' when it succeeds
TEST_F(BugreportTest, OkNoExtension) {
- SetBugreportzVersion("1.1");
- ExpectProgress(100, 100);
+ ExpectBugreportzVersion("1.1");
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
.WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip\n")),
WithArg<4>(ReturnCallbackDone())));
EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/bugreport.zip")), StrEq("file.zip"),
- true, HasSubstr("file.zip")))
+ true, StrEq("pulling file.zip")))
.WillOnce(Return(true));
- const char* args[1024] = {"bugreport", "file"};
+ const char* args[] = {"bugreport", "file"};
+ ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
+
+// Tests 'adb bugreport dir' when it succeeds and destination is a directory and device runs N.
+TEST_F(BugreportTest, OkNDeviceDirectory) {
+ ExpectBugreportzVersion("1.0");
+ TemporaryDir td;
+ std::string dest_file =
+ android::base::StringPrintf("%s%cda_bugreport.zip", td.path, OS_PATH_SEPARATOR);
+
+ EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz", false, _))
+ .WillOnce(DoAll(WithArg<4>(WriteOnStdout("BEGIN:/device/da_bugreport.zip\n")),
+ WithArg<4>(WriteOnStdout("OK:/device/da_bugreport.zip")),
+ WithArg<4>(ReturnCallbackDone())));
+ EXPECT_CALL(br_, DoSyncPull(ElementsAre(StrEq("/device/da_bugreport.zip")), StrEq(dest_file),
+ true, StrEq("pulling da_bugreport.zip")))
+ .WillOnce(Return(true));
+
+ const char* args[] = {"bugreport", td.path};
ASSERT_EQ(0, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
}
// Tests 'adb bugreport file.zip' when the bugreport itself failed
TEST_F(BugreportTest, BugreportzReturnedFail) {
- SetBugreportzVersion("1.1");
+ ExpectBugreportzVersion("1.1");
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
.WillOnce(
DoAll(WithArg<4>(WriteOnStdout("FAIL:D'OH!\n")), WithArg<4>(ReturnCallbackDone())));
CaptureStderr();
- const char* args[1024] = {"bugreport", "file.zip"};
+ const char* args[] = {"bugreport", "file.zip"};
ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
ASSERT_THAT(GetCapturedStderr(), HasSubstr("D'OH!"));
}
@@ -247,13 +351,13 @@
// was sent in
// multiple buffer writes
TEST_F(BugreportTest, BugreportzReturnedFailSplitBuffer) {
- SetBugreportzVersion("1.1");
+ ExpectBugreportzVersion("1.1");
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
.WillOnce(DoAll(WithArg<4>(WriteOnStdout("FAIL")), WithArg<4>(WriteOnStdout(":D'OH!\n")),
WithArg<4>(ReturnCallbackDone())));
CaptureStderr();
- const char* args[1024] = {"bugreport", "file.zip"};
+ const char* args[] = {"bugreport", "file.zip"};
ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
ASSERT_THAT(GetCapturedStderr(), HasSubstr("D'OH!"));
}
@@ -261,13 +365,13 @@
// Tests 'adb bugreport file.zip' when the bugreportz returned an unsupported
// response.
TEST_F(BugreportTest, BugreportzReturnedUnsupported) {
- SetBugreportzVersion("1.1");
+ ExpectBugreportzVersion("1.1");
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
.WillOnce(DoAll(WithArg<4>(WriteOnStdout("bugreportz? What am I, a zombie?")),
WithArg<4>(ReturnCallbackDone())));
CaptureStderr();
- const char* args[1024] = {"bugreport", "file.zip"};
+ const char* args[] = {"bugreport", "file.zip"};
ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
ASSERT_THAT(GetCapturedStderr(), HasSubstr("bugreportz? What am I, a zombie?"));
}
@@ -277,24 +381,31 @@
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -v", false, _))
.WillOnce(Return(666));
- const char* args[1024] = {"bugreport", "file.zip"};
+ const char* args[] = {"bugreport", "file.zip"};
ASSERT_EQ(666, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
}
+// Tests 'adb bugreport file.zip' when the bugreportz -v returns status 0 but with no output.
+TEST_F(BugreportTest, BugreportzVersionEmpty) {
+ ExpectBugreportzVersion("");
+
+ const char* args[] = {"bugreport", "file.zip"};
+ ASSERT_EQ(-1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
+}
+
// Tests 'adb bugreport file.zip' when the main bugreportz command failed
TEST_F(BugreportTest, BugreportzFailed) {
- SetBugreportzVersion("1.1");
+ ExpectBugreportzVersion("1.1");
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
.WillOnce(Return(666));
- const char* args[1024] = {"bugreport", "file.zip"};
+ const char* args[] = {"bugreport", "file.zip"};
ASSERT_EQ(666, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
}
// Tests 'adb bugreport file.zip' when the bugreport could not be pulled
TEST_F(BugreportTest, PullFails) {
- SetBugreportzVersion("1.1");
- ExpectProgress(100, 100);
+ ExpectBugreportzVersion("1.1");
EXPECT_CALL(br_, SendShellCommand(kTransportLocal, "HannibalLecter", "bugreportz -p", false, _))
.WillOnce(DoAll(WithArg<4>(WriteOnStdout("OK:/device/bugreport.zip")),
WithArg<4>(ReturnCallbackDone())));
@@ -302,6 +413,6 @@
true, HasSubstr("file.zip")))
.WillOnce(Return(false));
- const char* args[1024] = {"bugreport", "file.zip"};
+ const char* args[] = {"bugreport", "file.zip"};
ASSERT_EQ(1, br_.DoIt(kTransportLocal, "HannibalLecter", 2, args));
}
diff --git a/adb/commandline.cpp b/adb/commandline.cpp
index 0685b70..51d828a 100644
--- a/adb/commandline.cpp
+++ b/adb/commandline.cpp
@@ -81,6 +81,7 @@
static void help() {
fprintf(stderr, "%s\n", adb_version().c_str());
+ // clang-format off
fprintf(stderr,
" -a - directs adb to listen on all interfaces for a connection\n"
" -d - directs command to the only connected USB device\n"
@@ -173,9 +174,11 @@
" (-g: grant all runtime permissions)\n"
" adb uninstall [-k] <package> - remove this app package from the device\n"
" ('-k' means keep the data and cache directories)\n"
- " adb bugreport [<zip_file>] - return all information from the device\n"
- " that should be included in a bug report.\n"
- "\n"
+ " adb bugreport [<path>] - return all information from the device that should be included in a zipped bug report.\n"
+ " If <path> is a file, the bug report will be saved as that file.\n"
+ " If <path> is a directory, the bug report will be saved in that directory with the name provided by the device.\n"
+ " If <path> is omitted, the bug report will be saved in the current directory with the name provided by the device.\n"
+ " NOTE: if the device does not support zipped bug reports, the bug report will be output on stdout.\n"
" adb backup [-f <file>] [-apk|-noapk] [-obb|-noobb] [-shared|-noshared] [-all] [-system|-nosystem] [<packages...>]\n"
" - write an archive of the device's data to <file>.\n"
" If no -f option is supplied then the data is written\n"
@@ -249,8 +252,8 @@
" ADB_TRACE - Print debug information. A comma separated list of the following values\n"
" 1 or all, adb, sockets, packets, rwx, usb, sync, sysdeps, transport, jdwp\n"
" ANDROID_SERIAL - The serial number to connect to. -s takes priority over this if given.\n"
- " ANDROID_LOG_TAGS - When used with the logcat option, only these debug tags are printed.\n"
- );
+ " ANDROID_LOG_TAGS - When used with the logcat option, only these debug tags are printed.\n");
+ // clang-format on
}
int usage() {
@@ -1327,7 +1330,7 @@
if (hint.find_first_of(OS_PATH_SEPARATORS) != std::string::npos) {
std::string cwd;
if (!getcwd(&cwd)) {
- fprintf(stderr, "adb: getcwd failed: %s\n", strerror(errno));
+ perror("adb: getcwd failed");
return "";
}
return android::base::StringPrintf("%s%c%s", cwd.c_str(), OS_PATH_SEPARATOR, hint.c_str());
@@ -1427,6 +1430,16 @@
#endif
}
+static bool _use_legacy_install() {
+ FeatureSet features;
+ std::string error;
+ if (!adb_get_feature_set(&features, &error)) {
+ fprintf(stderr, "error: %s\n", error.c_str());
+ return true;
+ }
+ return !CanUseFeature(features, kFeatureCmd);
+}
+
int adb_commandline(int argc, const char **argv) {
int no_daemon = 0;
int is_daemon = 0;
@@ -1794,17 +1807,10 @@
}
else if (!strcmp(argv[0], "install")) {
if (argc < 2) return usage();
- FeatureSet features;
- std::string error;
- if (!adb_get_feature_set(&features, &error)) {
- fprintf(stderr, "error: %s\n", error.c_str());
- return 1;
+ if (_use_legacy_install()) {
+ return install_app_legacy(transport_type, serial, argc, argv);
}
-
- if (CanUseFeature(features, kFeatureCmd)) {
- return install_app(transport_type, serial, argc, argv);
- }
- return install_app_legacy(transport_type, serial, argc, argv);
+ return install_app(transport_type, serial, argc, argv);
}
else if (!strcmp(argv[0], "install-multiple")) {
if (argc < 2) return usage();
@@ -1812,17 +1818,10 @@
}
else if (!strcmp(argv[0], "uninstall")) {
if (argc < 2) return usage();
- FeatureSet features;
- std::string error;
- if (!adb_get_feature_set(&features, &error)) {
- fprintf(stderr, "error: %s\n", error.c_str());
- return 1;
+ if (_use_legacy_install()) {
+ return uninstall_app_legacy(transport_type, serial, argc, argv);
}
-
- if (CanUseFeature(features, kFeatureCmd)) {
- return uninstall_app(transport_type, serial, argc, argv);
- }
- return uninstall_app_legacy(transport_type, serial, argc, argv);
+ return uninstall_app(transport_type, serial, argc, argv);
}
else if (!strcmp(argv[0], "sync")) {
std::string src;
@@ -2028,7 +2027,6 @@
int i;
struct stat sb;
uint64_t total_size = 0;
-
// Find all APK arguments starting at end.
// All other arguments passed through verbatim.
int first_apk = -1;
@@ -2053,7 +2051,14 @@
return 1;
}
- std::string cmd = android::base::StringPrintf("exec:pm install-create -S %" PRIu64, total_size);
+ std::string install_cmd;
+ if (_use_legacy_install()) {
+ install_cmd = "exec:pm";
+ } else {
+ install_cmd = "exec:cmd package";
+ }
+
+ std::string cmd = android::base::StringPrintf("%s install-create -S %" PRIu64, install_cmd.c_str(), total_size);
for (i = 1; i < first_apk; i++) {
cmd += " " + escape_arg(argv[i]);
}
@@ -2095,8 +2100,8 @@
}
std::string cmd = android::base::StringPrintf(
- "exec:pm install-write -S %" PRIu64 " %d %d_%s -",
- static_cast<uint64_t>(sb.st_size), session_id, i, adb_basename(file).c_str());
+ "%s install-write -S %" PRIu64 " %d %d_%s -",
+ install_cmd.c_str(), static_cast<uint64_t>(sb.st_size), session_id, i, adb_basename(file).c_str());
int localFd = adb_open(file, O_RDONLY);
if (localFd < 0) {
@@ -2131,8 +2136,8 @@
finalize_session:
// Commit session if we streamed everything okay; otherwise abandon
std::string service =
- android::base::StringPrintf("exec:pm install-%s %d",
- success ? "commit" : "abandon", session_id);
+ android::base::StringPrintf("%s install-%s %d",
+ install_cmd.c_str(), success ? "commit" : "abandon", session_id);
fd = adb_connect(service, &error);
if (fd < 0) {
fprintf(stderr, "Connect error for finalize: %s\n", error.c_str());
diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp
index 26e0ffc..d547c19 100644
--- a/bootstat/bootstat.cpp
+++ b/bootstat/bootstat.cpp
@@ -169,7 +169,19 @@
void RecordBootComplete() {
BootEventRecordStore boot_event_store;
BootEventRecordStore::BootEventRecord record;
+
time_t uptime = bootstat::ParseUptime();
+ time_t current_time_utc = time(nullptr);
+
+ if (boot_event_store.GetBootEvent("last_boot_time_utc", &record)) {
+ time_t last_boot_time_utc = record.second;
+ time_t time_since_last_boot = difftime(current_time_utc,
+ last_boot_time_utc);
+ boot_event_store.AddBootEventWithValue("time_since_last_boot",
+ time_since_last_boot);
+ }
+
+ boot_event_store.AddBootEventWithValue("last_boot_time_utc", current_time_utc);
// The boot_complete metric has two variants: boot_complete and
// ota_boot_complete. The latter signifies that the device is booting after
diff --git a/debuggerd/tombstone.cpp b/debuggerd/tombstone.cpp
index fa983fa..dfdf29c 100644
--- a/debuggerd/tombstone.cpp
+++ b/debuggerd/tombstone.cpp
@@ -368,6 +368,7 @@
ALOGE("Cannot get siginfo for %d: %s\n", tid, strerror(errno));
}
+ ScopedBacktraceMapIteratorLock lock(map);
_LOG(log, logtype::MAPS, "\n");
if (!print_fault_address_marker) {
_LOG(log, logtype::MAPS, "memory map:\n");
diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp
index c1c3174..2b6cad1 100644
--- a/fastboot/fastboot.cpp
+++ b/fastboot/fastboot.cpp
@@ -116,16 +116,18 @@
};
static std::string find_item_given_name(const char* img_name, const char* product) {
- char *dir;
- char path[PATH_MAX + 128];
+ char path_c_str[PATH_MAX + 128];
if(product) {
- get_my_path(path);
- return android::base::StringPrintf("../../../target/product/%s/%s", product, img_name);
+ get_my_path(path_c_str);
+ std::string path = path_c_str;
+ path.erase(path.find_last_of('/'));
+ return android::base::StringPrintf("%s/../../../target/product/%s/%s",
+ path.c_str(), product, img_name);
}
- dir = getenv("ANDROID_PRODUCT_OUT");
- if((dir == 0) || (dir[0] == 0)) {
+ char *dir = getenv("ANDROID_PRODUCT_OUT");
+ if (dir == nullptr || dir[0] == '\0') {
die("neither -p product specified nor ANDROID_PRODUCT_OUT set");
}
diff --git a/fs_mgr/fs_mgr.c b/fs_mgr/fs_mgr.c
index 6de8817..387f708 100644
--- a/fs_mgr/fs_mgr.c
+++ b/fs_mgr/fs_mgr.c
@@ -489,7 +489,7 @@
* first successful mount.
* Returns -1 on error, and FS_MGR_MNTALL_* otherwise.
*/
-int fs_mgr_mount_all(struct fstab *fstab)
+int fs_mgr_mount_all(struct fstab *fstab, int mount_mode)
{
int i = 0;
int encryptable = FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE;
@@ -503,8 +503,10 @@
}
for (i = 0; i < fstab->num_entries; i++) {
- /* Don't mount entries that are managed by vold */
- if (fstab->recs[i].fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) {
+ /* Don't mount entries that are managed by vold or not for the mount mode*/
+ if ((fstab->recs[i].fs_mgr_flags & (MF_VOLDMANAGED | MF_RECOVERYONLY)) ||
+ ((mount_mode == MOUNT_MODE_LATE) && !fs_mgr_is_latemount(&fstab->recs[i])) ||
+ ((mount_mode == MOUNT_MODE_EARLY) && fs_mgr_is_latemount(&fstab->recs[i]))) {
continue;
}
diff --git a/fs_mgr/fs_mgr_fstab.c b/fs_mgr/fs_mgr_fstab.c
index 45adb34..21b4c74 100644
--- a/fs_mgr/fs_mgr_fstab.c
+++ b/fs_mgr/fs_mgr_fstab.c
@@ -78,6 +78,7 @@
{ "formattable", MF_FORMATTABLE },
{ "slotselect", MF_SLOTSELECT },
{ "nofail", MF_NOFAIL },
+ { "latemount", MF_LATEMOUNT },
{ "defaults", 0 },
{ 0, 0 },
};
@@ -545,3 +546,8 @@
{
return fstab->fs_mgr_flags & MF_NOFAIL;
}
+
+int fs_mgr_is_latemount(struct fstab_rec *fstab)
+{
+ return fstab->fs_mgr_flags & MF_LATEMOUNT;
+}
diff --git a/fs_mgr/fs_mgr_main.c b/fs_mgr/fs_mgr_main.c
index e5a00d5..776c13e 100644
--- a/fs_mgr/fs_mgr_main.c
+++ b/fs_mgr/fs_mgr_main.c
@@ -96,7 +96,7 @@
fstab = fs_mgr_read_fstab(fstab_file);
if (a_flag) {
- return fs_mgr_mount_all(fstab);
+ return fs_mgr_mount_all(fstab, MOUNT_MODE_DEFAULT);
} else if (n_flag) {
return fs_mgr_do_mount(fstab, n_name, n_blk_dev, 0);
} else if (u_flag) {
diff --git a/fs_mgr/fs_mgr_priv.h b/fs_mgr/fs_mgr_priv.h
index 46975f1..6d9492b 100644
--- a/fs_mgr/fs_mgr_priv.h
+++ b/fs_mgr/fs_mgr_priv.h
@@ -48,7 +48,7 @@
*
* <fs_mgr_options> is a comma separated list of flags that control the operation of
* the fs_mgr program. The list includes "wait", which will wait till
- * the <source> file exists, and "check", which requests that the fs_mgr
+ * the <source> file exists, and "check", which requests that the fs_mgr
* run an fscheck program on the <source> before mounting the filesystem.
* If check is specifed on a read-only filesystem, it is ignored.
* Also, "encryptable" means that filesystem can be encrypted.
@@ -83,6 +83,7 @@
#define MF_FORMATTABLE 0x4000
#define MF_SLOTSELECT 0x8000
#define MF_FORCEFDEORFBE 0x10000
+#define MF_LATEMOUNT 0x20000
#define MF_NOFAIL 0x40000
#define DM_BUF_SIZE 4096
diff --git a/fs_mgr/include/fs_mgr.h b/fs_mgr/include/fs_mgr.h
index 46d8f97..7565965 100644
--- a/fs_mgr/include/fs_mgr.h
+++ b/fs_mgr/include/fs_mgr.h
@@ -40,6 +40,13 @@
VERITY_MODE_DEFAULT = VERITY_MODE_RESTART
};
+// Mount modes
+enum mount_mode {
+ MOUNT_MODE_DEFAULT = 0,
+ MOUNT_MODE_EARLY = 1,
+ MOUNT_MODE_LATE = 2
+};
+
/*
* The entries must be kept in the same order as they were seen in the fstab.
* Unless explicitly requested, a lookup on mount point should always
@@ -82,7 +89,7 @@
#define FS_MGR_MNTALL_DEV_NOT_ENCRYPTED 1
#define FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE 0
#define FS_MGR_MNTALL_FAIL -1
-int fs_mgr_mount_all(struct fstab *fstab);
+int fs_mgr_mount_all(struct fstab *fstab, int mount_mode);
#define FS_MGR_DOMNT_FAILED -1
#define FS_MGR_DOMNT_BUSY -2
@@ -110,6 +117,7 @@
int fs_mgr_is_notrim(struct fstab_rec *fstab);
int fs_mgr_is_formattable(struct fstab_rec *fstab);
int fs_mgr_is_nofail(struct fstab_rec *fstab);
+int fs_mgr_is_latemount(struct fstab_rec *fstab);
int fs_mgr_swapon_all(struct fstab *fstab);
int fs_mgr_do_format(struct fstab_rec *fstab);
diff --git a/healthd/Android.mk b/healthd/Android.mk
index a4469fc..fe65e19 100644
--- a/healthd/Android.mk
+++ b/healthd/Android.mk
@@ -19,12 +19,39 @@
include $(BUILD_STATIC_LIBRARY)
include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+ healthd_mode_android.cpp \
+ healthd_mode_charger.cpp \
+ AnimationParser.cpp \
+ BatteryPropertiesRegistrar.cpp \
+
+LOCAL_MODULE := libhealthd_internal
+LOCAL_C_INCLUDES := bootable/recovery
+LOCAL_EXPORT_C_INCLUDE_DIRS := \
+ $(LOCAL_PATH) \
+ $(LOCAL_PATH)/include \
+
+LOCAL_STATIC_LIBRARIES := \
+ libbatterymonitor \
+ libbatteryservice \
+ libbinder \
+ libminui \
+ libpng \
+ libz \
+ libutils \
+ libbase \
+ libcutils \
+ liblog \
+ libm \
+ libc \
+
+include $(BUILD_STATIC_LIBRARY)
+
+
+include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
- healthd.cpp \
- healthd_mode_android.cpp \
- healthd_mode_charger.cpp \
- BatteryPropertiesRegistrar.cpp
+ healthd.cpp \
LOCAL_MODULE := healthd
LOCAL_MODULE_TAGS := optional
@@ -44,7 +71,20 @@
LOCAL_C_INCLUDES := bootable/recovery
-LOCAL_STATIC_LIBRARIES := libbatterymonitor libbatteryservice libbinder libminui libpng libz libutils libcutils liblog libm libc
+LOCAL_STATIC_LIBRARIES := \
+ libhealthd_internal \
+ libbatterymonitor \
+ libbatteryservice \
+ libbinder \
+ libminui \
+ libpng \
+ libz \
+ libutils \
+ libbase \
+ libcutils \
+ liblog \
+ libm \
+ libc
ifeq ($(strip $(BOARD_CHARGER_ENABLE_SUSPEND)),true)
LOCAL_STATIC_LIBRARIES += libsuspend
@@ -61,7 +101,7 @@
define _add-charger-image
include $$(CLEAR_VARS)
-LOCAL_MODULE := system_core_charger_$(notdir $(1))
+LOCAL_MODULE := system_core_charger_res_images_$(notdir $(1))
LOCAL_MODULE_STEM := $(notdir $(1))
_img_modules += $$(LOCAL_MODULE)
LOCAL_SRC_FILES := $1
diff --git a/healthd/AnimationParser.cpp b/healthd/AnimationParser.cpp
new file mode 100644
index 0000000..864038b
--- /dev/null
+++ b/healthd/AnimationParser.cpp
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ */
+
+#include "AnimationParser.h"
+
+#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
+
+#include <cutils/klog.h>
+
+#include "animation.h"
+
+#define LOGE(x...) do { KLOG_ERROR("charger", x); } while (0)
+#define LOGW(x...) do { KLOG_WARNING("charger", x); } while (0)
+#define LOGV(x...) do { KLOG_DEBUG("charger", x); } while (0)
+
+namespace android {
+
+// Lines consisting of only whitespace or whitespace followed by '#' can be ignored.
+bool can_ignore_line(const char* str) {
+ for (int i = 0; str[i] != '\0' && str[i] != '#'; i++) {
+ if (!isspace(str[i])) return false;
+ }
+ return true;
+}
+
+bool remove_prefix(const std::string& line, const char* prefix, const char** rest) {
+ const char* str = line.c_str();
+ int start;
+ char c;
+
+ std::string format = base::StringPrintf(" %s%%n%%c", prefix);
+ if (sscanf(str, format.c_str(), &start, &c) != 1) {
+ return false;
+ }
+
+ *rest = &str[start];
+ return true;
+}
+
+bool parse_text_field(const char* in, animation::text_field* field) {
+ int* x = &field->pos_x;
+ int* y = &field->pos_y;
+ int* r = &field->color_r;
+ int* g = &field->color_g;
+ int* b = &field->color_b;
+ int* a = &field->color_a;
+
+ int start = 0, end = 0;
+
+ if (sscanf(in, "c c %d %d %d %d %n%*s%n", r, g, b, a, &start, &end) == 4) {
+ *x = CENTER_VAL;
+ *y = CENTER_VAL;
+ } else if (sscanf(in, "c %d %d %d %d %d %n%*s%n", y, r, g, b, a, &start, &end) == 5) {
+ *x = CENTER_VAL;
+ } else if (sscanf(in, "%d c %d %d %d %d %n%*s%n", x, r, g, b, a, &start, &end) == 5) {
+ *y = CENTER_VAL;
+ } else if (sscanf(in, "%d %d %d %d %d %d %n%*s%n", x, y, r, g, b, a, &start, &end) != 6) {
+ return false;
+ }
+
+ if (end == 0) return false;
+
+ field->font_file.assign(&in[start], end - start);
+
+ return true;
+}
+
+bool parse_animation_desc(const std::string& content, animation* anim) {
+ static constexpr const char* animation_prefix = "animation: ";
+ static constexpr const char* fail_prefix = "fail: ";
+ static constexpr const char* clock_prefix = "clock_display: ";
+ static constexpr const char* percent_prefix = "percent_display: ";
+ static constexpr const char* frame_prefix = "frame: ";
+
+ std::vector<animation::frame> frames;
+
+ for (const auto& line : base::Split(content, "\n")) {
+ animation::frame frame;
+ const char* rest;
+
+ if (can_ignore_line(line.c_str())) {
+ continue;
+ } else if (remove_prefix(line, animation_prefix, &rest)) {
+ int start = 0, end = 0;
+ if (sscanf(rest, "%d %d %n%*s%n", &anim->num_cycles, &anim->first_frame_repeats,
+ &start, &end) != 2 ||
+ end == 0) {
+ LOGE("Bad animation format: %s\n", line.c_str());
+ return false;
+ } else {
+ anim->animation_file.assign(&rest[start], end - start);
+ }
+ } else if (remove_prefix(line, fail_prefix, &rest)) {
+ anim->fail_file.assign(rest);
+ } else if (remove_prefix(line, clock_prefix, &rest)) {
+ if (!parse_text_field(rest, &anim->text_clock)) {
+ LOGE("Bad clock_display format: %s\n", line.c_str());
+ return false;
+ }
+ } else if (remove_prefix(line, percent_prefix, &rest)) {
+ if (!parse_text_field(rest, &anim->text_percent)) {
+ LOGE("Bad percent_display format: %s\n", line.c_str());
+ return false;
+ }
+ } else if (sscanf(line.c_str(), " frame: %d %d %d",
+ &frame.disp_time, &frame.min_level, &frame.max_level) == 3) {
+ frames.push_back(std::move(frame));
+ } else {
+ LOGE("Malformed animation description line: %s\n", line.c_str());
+ return false;
+ }
+ }
+
+ if (anim->animation_file.empty() || frames.empty()) {
+ LOGE("Bad animation description. Provide the 'animation: ' line and at least one 'frame: ' "
+ "line.\n");
+ return false;
+ }
+
+ anim->num_frames = frames.size();
+ anim->frames = new animation::frame[frames.size()];
+ std::copy(frames.begin(), frames.end(), anim->frames);
+
+ return true;
+}
+
+} // namespace android
diff --git a/healthd/AnimationParser.h b/healthd/AnimationParser.h
new file mode 100644
index 0000000..bc00845
--- /dev/null
+++ b/healthd/AnimationParser.h
@@ -0,0 +1,31 @@
+/*
+ * 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 HEALTHD_ANIMATION_PARSER_H
+#define HEALTHD_ANIMATION_PARSER_H
+
+#include "animation.h"
+
+namespace android {
+
+bool parse_animation_desc(const std::string& content, animation* anim);
+
+bool can_ignore_line(const char* str);
+bool remove_prefix(const std::string& str, const char* prefix, const char** rest);
+bool parse_text_field(const char* in, animation::text_field* field);
+} // namespace android
+
+#endif // HEALTHD_ANIMATION_PARSER_H
diff --git a/healthd/animation.h b/healthd/animation.h
new file mode 100644
index 0000000..562b689
--- /dev/null
+++ b/healthd/animation.h
@@ -0,0 +1,73 @@
+/*
+ * 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 HEALTHD_ANIMATION_H
+#define HEALTHD_ANIMATION_H
+
+#include <inttypes.h>
+#include <string>
+
+struct GRSurface;
+struct GRFont;
+
+namespace android {
+
+#define CENTER_VAL INT_MAX
+
+struct animation {
+ struct frame {
+ int disp_time;
+ int min_level;
+ int max_level;
+
+ GRSurface* surface;
+ };
+
+ struct text_field {
+ std::string font_file;
+ int pos_x;
+ int pos_y;
+ int color_r;
+ int color_g;
+ int color_b;
+ int color_a;
+
+ GRFont* font;
+ };
+
+ std::string animation_file;
+ std::string fail_file;
+
+ text_field text_clock;
+ text_field text_percent;
+
+ bool run;
+
+ frame* frames;
+ int cur_frame;
+ int num_frames;
+ int first_frame_repeats; // Number of times to repeat the first frame in the current cycle
+
+ int cur_cycle;
+ int num_cycles; // Number of cycles to complete before blanking the screen
+
+ int cur_level; // current battery level being animated (0-100)
+ int cur_status; // current battery status - see BatteryService.h for BATTERY_STATUS_*
+};
+
+}
+
+#endif // HEALTHD_ANIMATION_H
diff --git a/healthd/healthd_mode_charger.cpp b/healthd/healthd_mode_charger.cpp
index 5846626..fb17f2d 100644
--- a/healthd/healthd_mode_charger.cpp
+++ b/healthd/healthd_mode_charger.cpp
@@ -30,6 +30,9 @@
#include <time.h>
#include <unistd.h>
+#include <android-base/file.h>
+#include <android-base/stringprintf.h>
+
#include <sys/socket.h>
#include <linux/netlink.h>
@@ -44,10 +47,14 @@
#include <suspend/autosuspend.h>
#endif
+#include "animation.h"
+#include "AnimationParser.h"
#include "minui/minui.h"
#include <healthd/healthd.h>
+using namespace android;
+
char *locale;
#ifndef max
@@ -67,8 +74,6 @@
#define POWER_ON_KEY_TIME (2 * MSEC_PER_SEC)
#define UNPLUGGED_SHUTDOWN_TIME (10 * MSEC_PER_SEC)
-#define BATTERY_FULL_THRESH 95
-
#define LAST_KMSG_PATH "/proc/last_kmsg"
#define LAST_KMSG_PSTORE_PATH "/sys/fs/pstore/console-ramoops"
#define LAST_KMSG_MAX_SZ (32 * 1024)
@@ -77,34 +82,14 @@
#define LOGW(x...) do { KLOG_WARNING("charger", x); } while (0)
#define LOGV(x...) do { KLOG_DEBUG("charger", x); } while (0)
+static constexpr const char* animation_desc_path = "/res/values/charger/animation.txt";
+
struct key_state {
bool pending;
bool down;
int64_t timestamp;
};
-struct frame {
- int disp_time;
- int min_capacity;
- bool level_only;
-
- GRSurface* surface;
-};
-
-struct animation {
- bool run;
-
- struct frame *frames;
- int cur_frame;
- int num_frames;
-
- int cur_cycle;
- int num_cycles;
-
- /* current capacity being animated */
- int capacity;
-};
-
struct charger {
bool have_battery_state;
bool charger_connected;
@@ -119,54 +104,83 @@
int boot_min_cap;
};
-static struct frame batt_anim_frames[] = {
+static const struct animation BASE_ANIMATION = {
+ .text_clock = {
+ .pos_x = 0,
+ .pos_y = 0,
+
+ .color_r = 255,
+ .color_g = 255,
+ .color_b = 255,
+ .color_a = 255,
+
+ .font = nullptr,
+ },
+ .text_percent = {
+ .pos_x = 0,
+ .pos_y = 0,
+
+ .color_r = 255,
+ .color_g = 255,
+ .color_b = 255,
+ .color_a = 255,
+ },
+
+ .run = false,
+
+ .frames = nullptr,
+ .cur_frame = 0,
+ .num_frames = 0,
+ .first_frame_repeats = 2,
+
+ .cur_cycle = 0,
+ .num_cycles = 3,
+
+ .cur_level = 0,
+ .cur_status = BATTERY_STATUS_UNKNOWN,
+};
+
+
+static struct animation::frame default_animation_frames[] = {
{
.disp_time = 750,
- .min_capacity = 0,
- .level_only = false,
+ .min_level = 0,
+ .max_level = 19,
.surface = NULL,
},
{
.disp_time = 750,
- .min_capacity = 20,
- .level_only = false,
+ .min_level = 0,
+ .max_level = 39,
.surface = NULL,
},
{
.disp_time = 750,
- .min_capacity = 40,
- .level_only = false,
+ .min_level = 0,
+ .max_level = 59,
.surface = NULL,
},
{
.disp_time = 750,
- .min_capacity = 60,
- .level_only = false,
+ .min_level = 0,
+ .max_level = 79,
.surface = NULL,
},
{
.disp_time = 750,
- .min_capacity = 80,
- .level_only = true,
+ .min_level = 80,
+ .max_level = 95,
.surface = NULL,
},
{
.disp_time = 750,
- .min_capacity = BATTERY_FULL_THRESH,
- .level_only = false,
+ .min_level = 0,
+ .max_level = 100,
.surface = NULL,
},
};
-static struct animation battery_animation = {
- .run = false,
- .frames = batt_anim_frames,
- .cur_frame = 0,
- .num_frames = ARRAY_SIZE(batt_anim_frames),
- .cur_cycle = 0,
- .num_cycles = 3,
- .capacity = 0,
-};
+static struct animation battery_animation = BASE_ANIMATION;
static struct charger charger_state;
static struct healthd_config *healthd_config;
@@ -257,13 +271,13 @@
static int draw_text(const char *str, int x, int y)
{
- int str_len_px = gr_measure(str);
+ int str_len_px = gr_measure(gr_sys_font(), str);
if (x < 0)
x = (gr_fb_width() - str_len_px) / 2;
if (y < 0)
y = (gr_fb_height() - char_height) / 2;
- gr_text(x, y, str, 0);
+ gr_text(gr_sys_font(), x, y, str, 0);
return y + char_height;
}
@@ -273,8 +287,79 @@
gr_color(0xa4, 0xc6, 0x39, 255);
}
+// Negative x or y coordinates position the text away from the opposite edge that positive ones do.
+void determine_xy(const animation::text_field& field, const int length, int* x, int* y)
+{
+ *x = field.pos_x;
+ *y = field.pos_y;
+
+ int str_len_px = length * field.font->char_width;
+ if (field.pos_x == CENTER_VAL) {
+ *x = (gr_fb_width() - str_len_px) / 2;
+ } else if (field.pos_x >= 0) {
+ *x = field.pos_x;
+ } else { // position from max edge
+ *x = gr_fb_width() + field.pos_x - str_len_px;
+ }
+
+ if (field.pos_y == CENTER_VAL) {
+ *y = (gr_fb_height() - field.font->char_height) / 2;
+ } else if (field.pos_y >= 0) {
+ *y = field.pos_y;
+ } else { // position from max edge
+ *y = gr_fb_height() + field.pos_y - field.font->char_height;
+ }
+}
+
+static void draw_clock(const animation& anim)
+{
+ static constexpr char CLOCK_FORMAT[] = "%H:%M";
+ static constexpr int CLOCK_LENGTH = 6;
+
+ const animation::text_field& field = anim.text_clock;
+
+ if (field.font == nullptr || field.font->char_width == 0 || field.font->char_height == 0) return;
+
+ time_t rawtime;
+ time(&rawtime);
+ struct tm* time_info = localtime(&rawtime);
+
+ char clock_str[CLOCK_LENGTH];
+ size_t length = strftime(clock_str, CLOCK_LENGTH, CLOCK_FORMAT, time_info);
+ if (length != CLOCK_LENGTH - 1) {
+ LOGE("Could not format time\n");
+ return;
+ }
+
+ int x, y;
+ determine_xy(field, length, &x, &y);
+
+ LOGV("drawing clock %s %d %d\n", clock_str, x, y);
+ gr_color(field.color_r, field.color_g, field.color_b, field.color_a);
+ gr_text(field.font, x, y, clock_str, false);
+}
+
+static void draw_percent(const animation& anim)
+{
+ if (anim.cur_level <= 0 || anim.cur_status != BATTERY_STATUS_CHARGING) return;
+
+ const animation::text_field& field = anim.text_percent;
+ if (field.font == nullptr || field.font->char_width == 0 || field.font->char_height == 0) {
+ return;
+ }
+
+ std::string str = base::StringPrintf("%d%%", anim.cur_level);
+
+ int x, y;
+ determine_xy(field, str.size(), &x, &y);
+
+ LOGV("drawing percent %s %d %d\n", str.c_str(), x, y);
+ gr_color(field.color_r, field.color_g, field.color_b, field.color_a);
+ gr_text(field.font, x, y, str.c_str(), false);
+}
+
/* returns the last y-offset of where the surface ends */
-static int draw_surface_centered(struct charger* /*charger*/, GRSurface* surface)
+static int draw_surface_centered(GRSurface* surface)
{
int w;
int h;
@@ -295,7 +380,7 @@
{
int y;
if (charger->surf_unknown) {
- draw_surface_centered(charger, charger->surf_unknown);
+ draw_surface_centered(charger->surf_unknown);
} else {
android_green();
y = draw_text("Charging!", -1, -1);
@@ -303,17 +388,19 @@
}
}
-static void draw_battery(struct charger *charger)
+static void draw_battery(const struct charger* charger)
{
- struct animation *batt_anim = charger->batt_anim;
- struct frame *frame = &batt_anim->frames[batt_anim->cur_frame];
+ const struct animation& anim = *charger->batt_anim;
+ const struct animation::frame& frame = anim.frames[anim.cur_frame];
- if (batt_anim->num_frames != 0) {
- draw_surface_centered(charger, frame->surface);
+ if (anim.num_frames != 0) {
+ draw_surface_centered(frame.surface);
LOGV("drawing frame #%d min_cap=%d time=%d\n",
- batt_anim->cur_frame, frame->min_capacity,
- frame->disp_time);
+ anim.cur_frame, frame.min_level,
+ frame.disp_time);
}
+ draw_clock(anim);
+ draw_percent(anim);
}
static void redraw_screen(struct charger *charger)
@@ -323,7 +410,7 @@
clear_screen();
/* try to display *something* */
- if (batt_anim->capacity < 0 || batt_anim->num_frames == 0)
+ if (batt_anim->cur_level < 0 || batt_anim->num_frames == 0)
draw_unknown(charger);
else
draw_battery(charger);
@@ -342,16 +429,33 @@
anim->run = false;
}
+static void init_status_display(struct animation* anim)
+{
+ int res;
+
+ if (!anim->text_clock.font_file.empty()) {
+ if ((res =
+ gr_init_font(anim->text_clock.font_file.c_str(), &anim->text_clock.font)) < 0) {
+ LOGE("Could not load time font (%d)\n", res);
+ }
+ }
+
+ if (!anim->text_percent.font_file.empty()) {
+ if ((res =
+ gr_init_font(anim->text_percent.font_file.c_str(), &anim->text_percent.font)) < 0) {
+ LOGE("Could not load percent font (%d)\n", res);
+ }
+ }
+}
+
static void update_screen_state(struct charger *charger, int64_t now)
{
struct animation *batt_anim = charger->batt_anim;
int disp_time;
- if (!batt_anim->run || now < charger->next_screen_transition)
- return;
+ if (!batt_anim->run || now < charger->next_screen_transition) return;
if (!minui_inited) {
-
if (healthd_config && healthd_config->screen_on) {
if (!healthd_config->screen_on(batt_prop)) {
LOGV("[%" PRId64 "] leave screen off\n", now);
@@ -364,7 +468,8 @@
}
gr_init();
- gr_font_size(&char_width, &char_height);
+ gr_font_size(gr_sys_font(), &char_width, &char_height);
+ init_status_display(batt_anim);
#ifndef CHARGER_DISABLE_INIT_BLANK
gr_fb_blank(true);
@@ -373,7 +478,7 @@
}
/* animation is over, blank screen and leave */
- if (batt_anim->cur_cycle == batt_anim->num_cycles) {
+ if (batt_anim->num_cycles > 0 && batt_anim->cur_cycle == batt_anim->num_cycles) {
reset_animation(batt_anim);
charger->next_screen_transition = -1;
gr_fb_blank(true);
@@ -389,21 +494,24 @@
if (batt_anim->cur_frame == 0) {
LOGV("[%" PRId64 "] animation starting\n", now);
- if (batt_prop && batt_prop->batteryLevel >= 0 && batt_anim->num_frames != 0) {
- int i;
+ if (batt_prop) {
+ batt_anim->cur_level = batt_prop->batteryLevel;
+ batt_anim->cur_status = batt_prop->batteryStatus;
+ if (batt_prop->batteryLevel >= 0 && batt_anim->num_frames != 0) {
+ /* find first frame given current battery level */
+ for (int i = 0; i < batt_anim->num_frames; i++) {
+ if (batt_anim->cur_level >= batt_anim->frames[i].min_level &&
+ batt_anim->cur_level <= batt_anim->frames[i].max_level) {
+ batt_anim->cur_frame = i;
+ break;
+ }
+ }
- /* find first frame given current capacity */
- for (i = 1; i < batt_anim->num_frames; i++) {
- if (batt_prop->batteryLevel < batt_anim->frames[i].min_capacity)
- break;
+ // repeat the first frame first_frame_repeats times
+ disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time *
+ batt_anim->first_frame_repeats;
}
- batt_anim->cur_frame = i - 1;
-
- /* show the first frame for twice as long */
- disp_time = batt_anim->frames[batt_anim->cur_frame].disp_time * 2;
}
- if (batt_prop)
- batt_anim->capacity = batt_prop->batteryLevel;
}
/* unblank the screen on first cycle */
@@ -416,8 +524,8 @@
/* if we don't have anim frames, we only have one image, so just bump
* the cycle counter and exit
*/
- if (batt_anim->num_frames == 0 || batt_anim->capacity < 0) {
- LOGV("[%" PRId64 "] animation missing or unknown battery status\n", now);
+ if (batt_anim->num_frames == 0 || batt_anim->cur_level < 0) {
+ LOGW("[%" PRId64 "] animation missing or unknown battery status\n", now);
charger->next_screen_transition = now + BATTERY_UNKNOWN_TIME;
batt_anim->cur_cycle++;
return;
@@ -432,12 +540,11 @@
if (charger->charger_connected) {
batt_anim->cur_frame++;
- /* if the frame is used for level-only, that is only show it when it's
- * the current level, skip it during the animation.
- */
while (batt_anim->cur_frame < batt_anim->num_frames &&
- batt_anim->frames[batt_anim->cur_frame].level_only)
+ (batt_anim->cur_level < batt_anim->frames[batt_anim->cur_frame].min_level ||
+ batt_anim->cur_level > batt_anim->frames[batt_anim->cur_frame].max_level)) {
batt_anim->cur_frame++;
+ }
if (batt_anim->cur_frame >= batt_anim->num_frames) {
batt_anim->cur_cycle++;
batt_anim->cur_frame = 0;
@@ -521,7 +628,7 @@
LOGW("[%" PRId64 "] booting from charger mode\n", now);
property_set("sys.boot_from_charger_mode", "1");
} else {
- if (charger->batt_anim->capacity >= charger->boot_min_cap) {
+ if (charger->batt_anim->cur_level >= charger->boot_min_cap) {
LOGW("[%" PRId64 "] rebooting\n", now);
android_reboot(ANDROID_RB_RESTART, 0, 0);
} else {
@@ -672,6 +779,52 @@
ev_dispatch();
}
+animation* init_animation()
+{
+ bool parse_success;
+
+ std::string content;
+ if (base::ReadFileToString(animation_desc_path, &content)) {
+ parse_success = parse_animation_desc(content, &battery_animation);
+ } else {
+ LOGW("Could not open animation description at %s\n", animation_desc_path);
+ parse_success = false;
+ }
+
+ if (!parse_success) {
+ LOGW("Could not parse animation description. Using default animation.\n");
+ battery_animation = BASE_ANIMATION;
+ battery_animation.animation_file.assign("charger/battery_scale");
+ battery_animation.frames = default_animation_frames;
+ battery_animation.num_frames = ARRAY_SIZE(default_animation_frames);
+ }
+ if (battery_animation.fail_file.empty()) {
+ battery_animation.fail_file.assign("charger/battery_fail");
+ }
+
+ LOGV("Animation Description:\n");
+ LOGV(" animation: %d %d '%s' (%d)\n",
+ battery_animation.num_cycles, battery_animation.first_frame_repeats,
+ battery_animation.animation_file.c_str(), battery_animation.num_frames);
+ LOGV(" fail_file: '%s'\n", battery_animation.fail_file.c_str());
+ LOGV(" clock: %d %d %d %d %d %d '%s'\n",
+ battery_animation.text_clock.pos_x, battery_animation.text_clock.pos_y,
+ battery_animation.text_clock.color_r, battery_animation.text_clock.color_g,
+ battery_animation.text_clock.color_b, battery_animation.text_clock.color_a,
+ battery_animation.text_clock.font_file.c_str());
+ LOGV(" percent: %d %d %d %d %d %d '%s'\n",
+ battery_animation.text_percent.pos_x, battery_animation.text_percent.pos_y,
+ battery_animation.text_percent.color_r, battery_animation.text_percent.color_g,
+ battery_animation.text_percent.color_b, battery_animation.text_percent.color_a,
+ battery_animation.text_percent.font_file.c_str());
+ for (int i = 0; i < battery_animation.num_frames; i++) {
+ LOGV(" frame %.2d: %d %d %d\n", i, battery_animation.frames[i].disp_time,
+ battery_animation.frames[i].min_level, battery_animation.frames[i].max_level);
+ }
+
+ return &battery_animation;
+}
+
void healthd_mode_charger_init(struct healthd_config* config)
{
int ret;
@@ -689,35 +842,39 @@
healthd_register_event(epollfd, charger_event_handler);
}
- ret = res_create_display_surface("charger/battery_fail", &charger->surf_unknown);
- if (ret < 0) {
- LOGE("Cannot load battery_fail image\n");
- charger->surf_unknown = NULL;
- }
+ struct animation* anim = init_animation();
+ charger->batt_anim = anim;
- charger->batt_anim = &battery_animation;
+ ret = res_create_display_surface(anim->fail_file.c_str(), &charger->surf_unknown);
+ if (ret < 0) {
+ LOGE("Cannot load custom battery_fail image. Reverting to built in.\n");
+ ret = res_create_display_surface("charger/battery_fail", &charger->surf_unknown);
+ if (ret < 0) {
+ LOGE("Cannot load built in battery_fail image\n");
+ charger->surf_unknown = NULL;
+ }
+ }
GRSurface** scale_frames;
int scale_count;
int scale_fps; // Not in use (charger/battery_scale doesn't have FPS text
// chunk). We are using hard-coded frame.disp_time instead.
- ret = res_create_multi_display_surface("charger/battery_scale", &scale_count, &scale_fps,
- &scale_frames);
+ ret = res_create_multi_display_surface(anim->animation_file.c_str(),
+ &scale_count, &scale_fps, &scale_frames);
if (ret < 0) {
LOGE("Cannot load battery_scale image\n");
- charger->batt_anim->num_frames = 0;
- charger->batt_anim->num_cycles = 1;
- } else if (scale_count != charger->batt_anim->num_frames) {
+ anim->num_frames = 0;
+ anim->num_cycles = 1;
+ } else if (scale_count != anim->num_frames) {
LOGE("battery_scale image has unexpected frame count (%d, expected %d)\n",
- scale_count, charger->batt_anim->num_frames);
- charger->batt_anim->num_frames = 0;
- charger->batt_anim->num_cycles = 1;
+ scale_count, anim->num_frames);
+ anim->num_frames = 0;
+ anim->num_cycles = 1;
} else {
- for (i = 0; i < charger->batt_anim->num_frames; i++) {
- charger->batt_anim->frames[i].surface = scale_frames[i];
+ for (i = 0; i < anim->num_frames; i++) {
+ anim->frames[i].surface = scale_frames[i];
}
}
-
ev_sync_key_state(set_key_callback, charger);
charger->next_screen_transition = -1;
diff --git a/healthd/tests/Android.mk b/healthd/tests/Android.mk
new file mode 100644
index 0000000..87e8862
--- /dev/null
+++ b/healthd/tests/Android.mk
@@ -0,0 +1,21 @@
+# Copyright 2016 The Android Open Source Project
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ AnimationParser_test.cpp \
+
+LOCAL_MODULE := healthd_test
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_STATIC_LIBRARIES := \
+ libhealthd_internal \
+
+LOCAL_SHARED_LIBRARIES := \
+ liblog \
+ libbase \
+ libcutils \
+
+include $(BUILD_NATIVE_TEST)
diff --git a/healthd/tests/AnimationParser_test.cpp b/healthd/tests/AnimationParser_test.cpp
new file mode 100644
index 0000000..2fc3185
--- /dev/null
+++ b/healthd/tests/AnimationParser_test.cpp
@@ -0,0 +1,192 @@
+/*
+ * 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.
+ */
+
+#include "AnimationParser.h"
+
+#include <gtest/gtest.h>
+
+using namespace android;
+
+TEST(AnimationParserTest, Test_can_ignore_line) {
+ EXPECT_TRUE(can_ignore_line(""));
+ EXPECT_TRUE(can_ignore_line(" "));
+ EXPECT_TRUE(can_ignore_line("#"));
+ EXPECT_TRUE(can_ignore_line(" # comment"));
+
+ EXPECT_FALSE(can_ignore_line("text"));
+ EXPECT_FALSE(can_ignore_line("text # comment"));
+ EXPECT_FALSE(can_ignore_line(" text"));
+ EXPECT_FALSE(can_ignore_line(" text # comment"));
+}
+
+TEST(AnimationParserTest, Test_remove_prefix) {
+ static const char TEST_STRING[] = "abcdef";
+ const char* rest = nullptr;
+ EXPECT_FALSE(remove_prefix(TEST_STRING, "def", &rest));
+ // Ignore strings that only consist of the prefix
+ EXPECT_FALSE(remove_prefix(TEST_STRING, TEST_STRING, &rest));
+
+ EXPECT_TRUE(remove_prefix(TEST_STRING, "abc", &rest));
+ EXPECT_STREQ("def", rest);
+
+ EXPECT_TRUE(remove_prefix(" abcdef", "abc", &rest));
+ EXPECT_STREQ("def", rest);
+}
+
+TEST(AnimationParserTest, Test_parse_text_field) {
+ static const char TEST_FILE_NAME[] = "font_file";
+ static const int TEST_X = 3;
+ static const int TEST_Y = 6;
+ static const int TEST_R = 1;
+ static const int TEST_G = 2;
+ static const int TEST_B = 4;
+ static const int TEST_A = 8;
+
+ static const char TEST_XCENT_YCENT[] = "c c 1 2 4 8 font_file ";
+ static const char TEST_XCENT_YVAL[] = "c 6 1 2 4 8 font_file ";
+ static const char TEST_XVAL_YCENT[] = "3 c 1 2 4 8 font_file ";
+ static const char TEST_XVAL_YVAL[] = "3 6 1 2 4 8 font_file ";
+ static const char TEST_BAD_MISSING[] = "c c 1 2 4 font_file";
+ static const char TEST_BAD_NO_FILE[] = "c c 1 2 4 8";
+
+ animation::text_field out;
+
+ EXPECT_TRUE(parse_text_field(TEST_XCENT_YCENT, &out));
+ EXPECT_EQ(CENTER_VAL, out.pos_x);
+ EXPECT_EQ(CENTER_VAL, out.pos_y);
+ EXPECT_EQ(TEST_R, out.color_r);
+ EXPECT_EQ(TEST_G, out.color_g);
+ EXPECT_EQ(TEST_B, out.color_b);
+ EXPECT_EQ(TEST_A, out.color_a);
+ EXPECT_STREQ(TEST_FILE_NAME, out.font_file.c_str());
+
+ EXPECT_TRUE(parse_text_field(TEST_XCENT_YVAL, &out));
+ EXPECT_EQ(CENTER_VAL, out.pos_x);
+ EXPECT_EQ(TEST_Y, out.pos_y);
+ EXPECT_EQ(TEST_R, out.color_r);
+ EXPECT_EQ(TEST_G, out.color_g);
+ EXPECT_EQ(TEST_B, out.color_b);
+ EXPECT_EQ(TEST_A, out.color_a);
+ EXPECT_STREQ(TEST_FILE_NAME, out.font_file.c_str());
+
+ EXPECT_TRUE(parse_text_field(TEST_XVAL_YCENT, &out));
+ EXPECT_EQ(TEST_X, out.pos_x);
+ EXPECT_EQ(CENTER_VAL, out.pos_y);
+ EXPECT_EQ(TEST_R, out.color_r);
+ EXPECT_EQ(TEST_G, out.color_g);
+ EXPECT_EQ(TEST_B, out.color_b);
+ EXPECT_EQ(TEST_A, out.color_a);
+ EXPECT_STREQ(TEST_FILE_NAME, out.font_file.c_str());
+
+ EXPECT_TRUE(parse_text_field(TEST_XVAL_YVAL, &out));
+ EXPECT_EQ(TEST_X, out.pos_x);
+ EXPECT_EQ(TEST_Y, out.pos_y);
+ EXPECT_EQ(TEST_R, out.color_r);
+ EXPECT_EQ(TEST_G, out.color_g);
+ EXPECT_EQ(TEST_B, out.color_b);
+ EXPECT_EQ(TEST_A, out.color_a);
+ EXPECT_STREQ(TEST_FILE_NAME, out.font_file.c_str());
+
+ EXPECT_FALSE(parse_text_field(TEST_BAD_MISSING, &out));
+ EXPECT_FALSE(parse_text_field(TEST_BAD_NO_FILE, &out));
+}
+
+TEST(AnimationParserTest, Test_parse_animation_desc_basic) {
+ static const char TEST_ANIMATION[] = R"desc(
+ # Basic animation
+ animation: 5 1 test/animation_file
+ frame: 1000 0 100
+ )desc";
+ animation anim;
+
+ EXPECT_TRUE(parse_animation_desc(TEST_ANIMATION, &anim));
+}
+
+TEST(AnimationParserTest, Test_parse_animation_desc_bad_no_animation_line) {
+ static const char TEST_ANIMATION[] = R"desc(
+ # Bad animation
+ frame: 1000 90 10
+ )desc";
+ animation anim;
+
+ EXPECT_FALSE(parse_animation_desc(TEST_ANIMATION, &anim));
+}
+
+TEST(AnimationParserTest, Test_parse_animation_desc_bad_no_frame) {
+ static const char TEST_ANIMATION[] = R"desc(
+ # Bad animation
+ animation: 5 1 test/animation_file
+ )desc";
+ animation anim;
+
+ EXPECT_FALSE(parse_animation_desc(TEST_ANIMATION, &anim));
+}
+
+TEST(AnimationParserTest, Test_parse_animation_desc_bad_animation_line_format) {
+ static const char TEST_ANIMATION[] = R"desc(
+ # Bad animation
+ animation: 5 1
+ frame: 1000 90 10
+ )desc";
+ animation anim;
+
+ EXPECT_FALSE(parse_animation_desc(TEST_ANIMATION, &anim));
+}
+
+TEST(AnimationParserTest, Test_parse_animation_desc_full) {
+ static const char TEST_ANIMATION[] = R"desc(
+ # Full animation
+ animation: 5 1 test/animation_file
+ clock_display: 11 12 13 14 15 16 test/time_font
+ percent_display: 21 22 23 24 25 26 test/percent_font
+
+ frame: 10 20 30
+ frame: 40 50 60
+ )desc";
+ animation anim;
+
+ EXPECT_TRUE(parse_animation_desc(TEST_ANIMATION, &anim));
+
+ EXPECT_EQ(5, anim.num_cycles);
+ EXPECT_EQ(1, anim.first_frame_repeats);
+ EXPECT_STREQ("test/animation_file", anim.animation_file.c_str());
+
+ EXPECT_EQ(11, anim.text_clock.pos_x);
+ EXPECT_EQ(12, anim.text_clock.pos_y);
+ EXPECT_EQ(13, anim.text_clock.color_r);
+ EXPECT_EQ(14, anim.text_clock.color_g);
+ EXPECT_EQ(15, anim.text_clock.color_b);
+ EXPECT_EQ(16, anim.text_clock.color_a);
+ EXPECT_STREQ("test/time_font", anim.text_clock.font_file.c_str());
+
+ EXPECT_EQ(21, anim.text_percent.pos_x);
+ EXPECT_EQ(22, anim.text_percent.pos_y);
+ EXPECT_EQ(23, anim.text_percent.color_r);
+ EXPECT_EQ(24, anim.text_percent.color_g);
+ EXPECT_EQ(25, anim.text_percent.color_b);
+ EXPECT_EQ(26, anim.text_percent.color_a);
+ EXPECT_STREQ("test/percent_font", anim.text_percent.font_file.c_str());
+
+ EXPECT_EQ(2, anim.num_frames);
+
+ EXPECT_EQ(10, anim.frames[0].disp_time);
+ EXPECT_EQ(20, anim.frames[0].min_level);
+ EXPECT_EQ(30, anim.frames[0].max_level);
+
+ EXPECT_EQ(40, anim.frames[1].disp_time);
+ EXPECT_EQ(50, anim.frames[1].min_level);
+ EXPECT_EQ(60, anim.frames[1].max_level);
+}
diff --git a/include/android/log.h b/include/android/log.h
index 1c171b7..391c826 100644
--- a/include/android/log.h
+++ b/include/android/log.h
@@ -89,6 +89,11 @@
} android_LogPriority;
/*
+ * Release any logger resources (a new log write will immediately re-acquire)
+ */
+void __android_log_close();
+
+/*
* Send a simple string to the log.
*/
int __android_log_write(int prio, const char *tag, const char *text);
diff --git a/include/backtrace/BacktraceMap.h b/include/backtrace/BacktraceMap.h
index 2373c45..b80045f 100644
--- a/include/backtrace/BacktraceMap.h
+++ b/include/backtrace/BacktraceMap.h
@@ -71,6 +71,12 @@
bool IsWritable(uintptr_t pc) { return GetFlags(pc) & PROT_WRITE; }
bool IsExecutable(uintptr_t pc) { return GetFlags(pc) & PROT_EXEC; }
+ // In order to use the iterators on this object, a caller must
+ // call the LockIterator and UnlockIterator function to guarantee
+ // that the data does not change while it's being used.
+ virtual void LockIterator() {}
+ virtual void UnlockIterator() {}
+
typedef std::deque<backtrace_map_t>::iterator iterator;
iterator begin() { return maps_.begin(); }
iterator end() { return maps_.end(); }
@@ -102,4 +108,18 @@
pid_t pid_;
};
+class ScopedBacktraceMapIteratorLock {
+public:
+ explicit ScopedBacktraceMapIteratorLock(BacktraceMap* map) : map_(map) {
+ map->LockIterator();
+ }
+
+ ~ScopedBacktraceMapIteratorLock() {
+ map_->UnlockIterator();
+ }
+
+private:
+ BacktraceMap* map_;
+};
+
#endif // _BACKTRACE_BACKTRACE_MAP_H
diff --git a/include/cutils/trace.h b/include/cutils/trace.h
index c9790ad..19313af 100644
--- a/include/cutils/trace.h
+++ b/include/cutils/trace.h
@@ -70,7 +70,8 @@
#define ATRACE_TAG_PACKAGE_MANAGER (1<<18)
#define ATRACE_TAG_SYSTEM_SERVER (1<<19)
#define ATRACE_TAG_DATABASE (1<<20)
-#define ATRACE_TAG_LAST ATRACE_TAG_DATABASE
+#define ATRACE_TAG_NETWORK (1<<21)
+#define ATRACE_TAG_LAST ATRACE_TAG_NETWORK
// Reserved for initialization.
#define ATRACE_TAG_NOT_READY (1ULL<<63)
diff --git a/include/sysutils/FrameworkListener.h b/include/sysutils/FrameworkListener.h
index 18049cd..2137069 100644
--- a/include/sysutils/FrameworkListener.h
+++ b/include/sysutils/FrameworkListener.h
@@ -32,6 +32,7 @@
int mCommandCount;
bool mWithSeq;
FrameworkCommandCollection *mCommands;
+ bool mSkipToNextNullByte;
public:
FrameworkListener(const char *socketName);
diff --git a/include/utils/Mutex.h b/include/utils/Mutex.h
index 9b0b734..848a600 100644
--- a/include/utils/Mutex.h
+++ b/include/utils/Mutex.h
@@ -64,13 +64,18 @@
status_t tryLock();
#if defined(__ANDROID__)
- // lock the mutex, but don't wait longer than timeoutMilliseconds.
+ // Lock the mutex, but don't wait longer than timeoutNs (relative time).
// Returns 0 on success, TIMED_OUT for failure due to timeout expiration.
//
// OSX doesn't have pthread_mutex_timedlock() or equivalent. To keep
// capabilities consistent across host OSes, this method is only available
// when building Android binaries.
- status_t timedLock(nsecs_t timeoutMilliseconds);
+ //
+ // FIXME?: pthread_mutex_timedlock is based on CLOCK_REALTIME,
+ // which is subject to NTP adjustments, and includes time during suspend,
+ // so a timeout may occur even though no processes could run.
+ // Not holding a partial wakelock may lead to a system suspend.
+ status_t timedLock(nsecs_t timeoutNs);
#endif
// Manages the mutex automatically. It'll be locked when Autolock is
@@ -134,6 +139,7 @@
}
#if defined(__ANDROID__)
inline status_t Mutex::timedLock(nsecs_t timeoutNs) {
+ timeoutNs += systemTime(SYSTEM_TIME_REALTIME);
const struct timespec ts = {
/* .tv_sec = */ static_cast<time_t>(timeoutNs / 1000000000),
/* .tv_nsec = */ static_cast<long>(timeoutNs % 1000000000),
diff --git a/init/builtins.cpp b/init/builtins.cpp
index 70f9194..44217f0 100644
--- a/init/builtins.cpp
+++ b/init/builtins.cpp
@@ -479,9 +479,9 @@
*
* start_index: index of the first path in the args list
*/
-static void import_late(const std::vector<std::string>& args, size_t start_index) {
+static void import_late(const std::vector<std::string>& args, size_t start_index, size_t end_index) {
Parser& parser = Parser::GetInstance();
- if (args.size() <= start_index) {
+ if (end_index <= start_index) {
// Use the default set if no path is given
static const std::vector<std::string> init_directories = {
"/system/etc/init",
@@ -493,25 +493,23 @@
parser.ParseConfig(dir);
}
} else {
- for (size_t i = start_index; i < args.size(); ++i) {
+ for (size_t i = start_index; i < end_index; ++i) {
parser.ParseConfig(args[i]);
}
}
}
-/* mount_all <fstab> [ <path> ]*
+/* mount_fstab
*
- * This function might request a reboot, in which case it will
- * not return.
+ * Call fs_mgr_mount_all() to mount the given fstab
*/
-static int do_mount_all(const std::vector<std::string>& args) {
+static int mount_fstab(const char* fstabfile, int mount_mode) {
pid_t pid;
int ret = -1;
int child_ret = -1;
int status;
struct fstab *fstab;
- const char* fstabfile = args[1].c_str();
/*
* Call fs_mgr_mount_all() to mount all filesystems. We fork(2) and
* do the call in the child to provide protection to the main init
@@ -536,7 +534,7 @@
/* child, call fs_mgr_mount_all() */
klog_set_level(6); /* So we can see what fs_mgr_mount_all() does */
fstab = fs_mgr_read_fstab(fstabfile);
- child_ret = fs_mgr_mount_all(fstab);
+ child_ret = fs_mgr_mount_all(fstab, mount_mode);
fs_mgr_free_fstab(fstab);
if (child_ret == -1) {
ERROR("fs_mgr_mount_all returned an error\n");
@@ -546,28 +544,38 @@
/* fork failed, return an error */
return -1;
}
+ return ret;
+}
- /* Paths of .rc files are specified at the 2nd argument and beyond */
- import_late(args, 2);
-
- if (ret == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
+/* Queue event based on fs_mgr return code.
+ *
+ * code: return code of fs_mgr_mount_all
+ *
+ * This function might request a reboot, in which case it will
+ * not return.
+ *
+ * return code is processed based on input code
+ */
+static int queue_fs_event(int code) {
+ int ret = code;
+ if (code == FS_MGR_MNTALL_DEV_NEEDS_ENCRYPTION) {
ActionManager::GetInstance().QueueEventTrigger("encrypt");
- } else if (ret == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
+ } else if (code == FS_MGR_MNTALL_DEV_MIGHT_BE_ENCRYPTED) {
property_set("ro.crypto.state", "encrypted");
property_set("ro.crypto.type", "block");
ActionManager::GetInstance().QueueEventTrigger("defaultcrypto");
- } else if (ret == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
+ } else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTED) {
property_set("ro.crypto.state", "unencrypted");
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
- } else if (ret == FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
+ } else if (code == FS_MGR_MNTALL_DEV_NOT_ENCRYPTABLE) {
property_set("ro.crypto.state", "unsupported");
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
- } else if (ret == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
+ } else if (code == FS_MGR_MNTALL_DEV_NEEDS_RECOVERY) {
/* Setup a wipe via recovery, and reboot into recovery */
ERROR("fs_mgr_mount_all suggested recovery, so wiping data via recovery.\n");
ret = wipe_data_via_recovery("wipe_data_via_recovery");
/* If reboot worked, there is no return. */
- } else if (ret == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
+ } else if (code == FS_MGR_MNTALL_DEV_FILE_ENCRYPTED) {
if (e4crypt_install_keyring()) {
return -1;
}
@@ -577,14 +585,55 @@
// Although encrypted, we have device key, so we do not need to
// do anything different from the nonencrypted case.
ActionManager::GetInstance().QueueEventTrigger("nonencrypted");
- } else if (ret > 0) {
- ERROR("fs_mgr_mount_all returned unexpected error %d\n", ret);
+ } else if (code > 0) {
+ ERROR("fs_mgr_mount_all returned unexpected error %d\n", code);
}
/* else ... < 0: error */
return ret;
}
+/* mount_all <fstab> [ <path> ]* [--<options>]*
+ *
+ * This function might request a reboot, in which case it will
+ * not return.
+ */
+static int do_mount_all(const std::vector<std::string>& args) {
+ std::size_t na = 0;
+ bool import_rc = true;
+ bool queue_event = true;
+ int mount_mode = MOUNT_MODE_DEFAULT;
+ const char* fstabfile = args[1].c_str();
+ std::size_t path_arg_end = args.size();
+
+ for (na = args.size() - 1; na > 1; --na) {
+ if (args[na] == "--early") {
+ path_arg_end = na;
+ queue_event = false;
+ mount_mode = MOUNT_MODE_EARLY;
+ } else if (args[na] == "--late") {
+ path_arg_end = na;
+ import_rc = false;
+ mount_mode = MOUNT_MODE_LATE;
+ }
+ }
+
+ int ret = mount_fstab(fstabfile, mount_mode);
+
+ if (import_rc) {
+ /* Paths of .rc files are specified at the 2nd argument and beyond */
+ import_late(args, 2, path_arg_end);
+ }
+
+ if (queue_event) {
+ /* queue_fs_event will queue event based on mount_fstab return code
+ * and return processed return code*/
+ ret = queue_fs_event(ret);
+ }
+
+ return ret;
+}
+
static int do_swapon_all(const std::vector<std::string>& args) {
struct fstab *fstab;
int ret;
diff --git a/init/readme.txt b/init/readme.txt
index 4481e24..dad7e06 100644
--- a/init/readme.txt
+++ b/init/readme.txt
@@ -1,4 +1,3 @@
-
Android Init Language
---------------------
@@ -78,6 +77,14 @@
conflict resolution when multiple services are added to the system, as
each one will go into a separate file.
+There are two options "early" and "late" in mount_all command
+which can be set after optional paths. With "--early" set, the
+init executable will skip mounting entries with "latemount" flag
+and triggering fs encryption state event. With "--late" set,
+init executable will only mount entries with "latemount" flag but skip
+importing rc files. By default, no option is set, and mount_all will
+mount_all will process all entries in the given fstab.
+
Actions
-------
Actions are named sequences of commands. Actions have a trigger which
@@ -291,10 +298,11 @@
owned by the root user and root group. If provided, the mode, owner and group
will be updated if the directory exists already.
-mount_all <fstab> [ <path> ]*
+mount_all <fstab> [ <path> ]* [--<option>]
Calls fs_mgr_mount_all on the given fs_mgr-format fstab and imports .rc files
- at the specified paths (e.g., on the partitions just mounted). Refer to the
- section of "Init .rc Files" for detail.
+ at the specified paths (e.g., on the partitions just mounted) with optional
+ options "early" and "late".
+ Refer to the section of "Init .rc Files" for detail.
mount <type> <device> <dir> [ <flag> ]* [<options>]
Attempt to mount the named device at the directory <dir>
diff --git a/libbacktrace/BacktraceMap.cpp b/libbacktrace/BacktraceMap.cpp
index ba86632..85f2436 100644
--- a/libbacktrace/BacktraceMap.cpp
+++ b/libbacktrace/BacktraceMap.cpp
@@ -35,8 +35,8 @@
}
void BacktraceMap::FillIn(uintptr_t addr, backtrace_map_t* map) {
- for (BacktraceMap::const_iterator it = begin();
- it != end(); ++it) {
+ ScopedBacktraceMapIteratorLock lock(this);
+ for (BacktraceMap::const_iterator it = begin(); it != end(); ++it) {
if (addr >= it->start && addr < it->end) {
*map = *it;
return;
diff --git a/libbacktrace/UnwindMap.cpp b/libbacktrace/UnwindMap.cpp
index 34d79f9..af79562 100644
--- a/libbacktrace/UnwindMap.cpp
+++ b/libbacktrace/UnwindMap.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <pthread.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
@@ -72,6 +73,7 @@
}
UnwindMapLocal::UnwindMapLocal() : UnwindMap(getpid()), map_created_(false) {
+ pthread_rwlock_init(&map_lock_, nullptr);
}
UnwindMapLocal::~UnwindMapLocal() {
@@ -82,9 +84,14 @@
}
bool UnwindMapLocal::GenerateMap() {
+ // Lock so that multiple threads cannot modify the maps data at the
+ // same time.
+ pthread_rwlock_wrlock(&map_lock_);
+
// It's possible for the map to be regenerated while this loop is occurring.
// If that happens, get the map again, but only try at most three times
// before giving up.
+ bool generated = false;
for (int i = 0; i < 3; i++) {
maps_.clear();
@@ -110,12 +117,17 @@
}
// Check to see if the map changed while getting the data.
if (ret != -UNW_EINVAL) {
- return true;
+ generated = true;
+ break;
}
}
- BACK_LOGW("Unable to generate the map.");
- return false;
+ pthread_rwlock_unlock(&map_lock_);
+
+ if (!generated) {
+ BACK_LOGW("Unable to generate the map.");
+ }
+ return generated;
}
bool UnwindMapLocal::Build() {
diff --git a/libbacktrace/UnwindMap.h b/libbacktrace/UnwindMap.h
index 111401f..f85b54a 100644
--- a/libbacktrace/UnwindMap.h
+++ b/libbacktrace/UnwindMap.h
@@ -17,6 +17,7 @@
#ifndef _LIBBACKTRACE_UNWIND_MAP_H
#define _LIBBACKTRACE_UNWIND_MAP_H
+#include <pthread.h>
#include <stdint.h>
#include <sys/types.h>
@@ -56,10 +57,15 @@
void FillIn(uintptr_t addr, backtrace_map_t* map) override;
+ void LockIterator() override { pthread_rwlock_rdlock(&map_lock_); }
+ void UnlockIterator() override { pthread_rwlock_unlock(&map_lock_); }
+
private:
bool GenerateMap();
bool map_created_;
+
+ pthread_rwlock_t map_lock_;
};
#endif // _LIBBACKTRACE_UNWIND_MAP_H
diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp
index f6b2591..913e12d 100644
--- a/libbacktrace/backtrace_test.cpp
+++ b/libbacktrace/backtrace_test.cpp
@@ -896,6 +896,7 @@
std::unique_ptr<BacktraceMap> map(BacktraceMap::Create(pid));
// Basic test that verifies that the map is in the expected order.
+ ScopedBacktraceMapIteratorLock lock(map.get());
std::vector<map_test_t>::const_iterator test_it = test_maps.begin();
for (BacktraceMap::const_iterator it = map->begin(); it != map->end(); ++it) {
ASSERT_TRUE(test_it != test_maps.end());
diff --git a/liblog/event_tag_map.c b/liblog/event_tag_map.c
index 3cb04cf..345f0d3 100644
--- a/liblog/event_tag_map.c
+++ b/liblog/event_tag_map.c
@@ -99,6 +99,9 @@
if (processFile(newTagMap) != 0)
goto fail;
+ if (fd >= 0)
+ close(fd);
+
return newTagMap;
fail:
diff --git a/liblog/logger_write.c b/liblog/logger_write.c
index b802ed7..c7b5a84 100644
--- a/liblog/logger_write.c
+++ b/liblog/logger_write.c
@@ -132,6 +132,41 @@
}
return kLogNotAvailable;
}
+/*
+ * Release any logger resources. A new log write will immediately re-acquire.
+ */
+LIBLOG_ABI_PUBLIC void __android_log_close()
+{
+ struct android_log_transport_write *transport;
+
+ __android_log_lock();
+
+ write_to_log = __write_to_log_init;
+
+ /*
+ * Threads that are actively writing at this point are not held back
+ * by a lock and are at risk of dropping the messages with a return code
+ * -EBADF. Prefer to return error code than add the overhead of a lock to
+ * each log writing call to guarantee delivery. In addition, anyone
+ * calling this is doing so to release the logging resources and shut down,
+ * for them to do so with outstanding log requests in other threads is a
+ * disengenuous use of this function.
+ */
+
+ write_transport_for_each(transport, &__android_log_persist_write) {
+ if (transport->close) {
+ (*transport->close)();
+ }
+ }
+
+ write_transport_for_each(transport, &__android_log_transport_write) {
+ if (transport->close) {
+ (*transport->close)();
+ }
+ }
+
+ __android_log_unlock();
+}
/* log_init_lock assumed */
static int __write_to_log_initialize()
diff --git a/liblog/tests/liblog_test.cpp b/liblog/tests/liblog_test.cpp
index 6aa4fb7..f0acdf8 100644
--- a/liblog/tests/liblog_test.cpp
+++ b/liblog/tests/liblog_test.cpp
@@ -132,12 +132,17 @@
ASSERT_TRUE(NULL != (logger_list = android_logger_list_open(
LOG_ID_EVENTS, ANDROID_LOG_RDONLY | ANDROID_LOG_NONBLOCK, 1000, pid)));
+ // Check that we can close and reopen the logger
log_time ts(CLOCK_MONOTONIC);
-
ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts, sizeof(ts)));
+ __android_log_close();
+
+ log_time ts1(CLOCK_MONOTONIC);
+ ASSERT_LT(0, __android_log_btwrite(0, EVENT_TYPE_LONG, &ts1, sizeof(ts1)));
usleep(1000000);
int count = 0;
+ int second_count = 0;
for (;;) {
log_msg log_msg;
@@ -161,10 +166,13 @@
log_time tx(eventData + 4 + 1);
if (ts == tx) {
++count;
+ } else if (ts1 == tx) {
+ ++second_count;
}
}
EXPECT_EQ(1, count);
+ EXPECT_EQ(1, second_count);
android_logger_list_close(logger_list);
}
diff --git a/libsysutils/src/FrameworkListener.cpp b/libsysutils/src/FrameworkListener.cpp
index e7b3dd6..579ead9 100644
--- a/libsysutils/src/FrameworkListener.cpp
+++ b/libsysutils/src/FrameworkListener.cpp
@@ -49,6 +49,7 @@
errorRate = 0;
mCommandCount = 0;
mWithSeq = withSeq;
+ mSkipToNextNullByte = false;
}
bool FrameworkListener::onDataAvailable(SocketClient *c) {
@@ -59,10 +60,15 @@
if (len < 0) {
SLOGE("read() failed (%s)", strerror(errno));
return false;
- } else if (!len)
+ } else if (!len) {
return false;
- if(buffer[len-1] != '\0')
+ } else if (buffer[len-1] != '\0') {
SLOGW("String is not zero-terminated");
+ android_errorWriteLog(0x534e4554, "29831647");
+ c->sendMsg(500, "Command too large for buffer", false);
+ mSkipToNextNullByte = true;
+ return false;
+ }
int offset = 0;
int i;
@@ -70,11 +76,16 @@
for (i = 0; i < len; i++) {
if (buffer[i] == '\0') {
/* IMPORTANT: dispatchCommand() expects a zero-terminated string */
- dispatchCommand(c, buffer + offset);
+ if (mSkipToNextNullByte) {
+ mSkipToNextNullByte = false;
+ } else {
+ dispatchCommand(c, buffer + offset);
+ }
offset = i + 1;
}
}
+ mSkipToNextNullByte = false;
return true;
}
diff --git a/libutils/RefBase.cpp b/libutils/RefBase.cpp
index f90e28b..4ead19c 100644
--- a/libutils/RefBase.cpp
+++ b/libutils/RefBase.cpp
@@ -575,15 +575,14 @@
// grab a strong-reference, which is always safe due to the
// extended life-time.
curCount = impl->mStrong.fetch_add(1, std::memory_order_relaxed);
- }
-
- // If the strong reference count has already been incremented by
- // someone else, the implementor of onIncStrongAttempted() is holding
- // an unneeded reference. So call onLastStrongRef() here to remove it.
- // (No, this is not pretty.) Note that we MUST NOT do this if we
- // are in fact acquiring the first reference.
- if (curCount > 0 && curCount < INITIAL_STRONG_VALUE) {
- impl->mBase->onLastStrongRef(id);
+ // If the strong reference count has already been incremented by
+ // someone else, the implementor of onIncStrongAttempted() is holding
+ // an unneeded reference. So call onLastStrongRef() here to remove it.
+ // (No, this is not pretty.) Note that we MUST NOT do this if we
+ // are in fact acquiring the first reference.
+ if (curCount != 0 && curCount != INITIAL_STRONG_VALUE) {
+ impl->mBase->onLastStrongRef(id);
+ }
}
}
@@ -593,7 +592,7 @@
ALOGD("attemptIncStrong of %p from %p: cnt=%d\n", this, id, curCount);
#endif
- // curCount is the value of mStrong before we increment ed it.
+ // curCount is the value of mStrong before we incremented it.
// Now we need to fix-up the count if it was INITIAL_STRONG_VALUE.
// This must be done safely, i.e.: handle the case where several threads
// were here in attemptIncStrong().
diff --git a/logd/LogBuffer.cpp b/logd/LogBuffer.cpp
index 8c30f79..79a7e8c 100644
--- a/logd/LogBuffer.cpp
+++ b/logd/LogBuffer.cpp
@@ -691,7 +691,7 @@
&& ((!gc && (element->getPid() == worstPid))
|| (mLastWorstPidOfSystem[id].find(element->getPid())
== mLastWorstPidOfSystem[id].end()))) {
- mLastWorstPidOfSystem[id][element->getUid()] = it;
+ mLastWorstPidOfSystem[id][element->getPid()] = it;
}
if ((!gc && !worstPid && (element->getUid() == worst))
|| (mLastWorstUid[id].find(element->getUid())
diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp
index a0b7499..c7506ef 100644
--- a/logd/LogKlog.cpp
+++ b/logd/LogKlog.cpp
@@ -26,6 +26,7 @@
#include <syslog.h>
#include <log/logger.h>
+#include <private/android_filesystem_config.h>
#include "LogBuffer.h"
#include "LogKlog.h"
@@ -614,7 +615,12 @@
// Parse pid, tid and uid
const pid_t pid = sniffPid(&p, len - (p - buf));
const pid_t tid = pid;
- const uid_t uid = pid ? logbuf->pidToUid(pid) : 0;
+ uid_t uid = AID_ROOT;
+ if (pid) {
+ logbuf->lock();
+ uid = logbuf->pidToUid(pid);
+ logbuf->unlock();
+ }
// Parse (rules at top) to pull out a tag from the incoming kernel message.
// Some may view the following as an ugly heuristic, the desire is to
@@ -630,126 +636,124 @@
const char *tag = "";
const char *etag = tag;
size_t taglen = len - (p - buf);
- if (!isspace(*p) && *p) {
- const char *bt, *et, *cp;
+ const char *bt = p;
- bt = p;
- if ((taglen >= 6) && !fast<strncmp>(p, "[INFO]", 6)) {
- // <PRI>[<TIME>] "[INFO]"<tag> ":" message
- bt = p + 6;
- taglen -= 6;
- }
- for(et = bt; taglen && *et && (*et != ':') && !isspace(*et); ++et, --taglen) {
- // skip ':' within [ ... ]
- if (*et == '[') {
- while (taglen && *et && *et != ']') {
- ++et;
- --taglen;
- }
- }
- }
- for(cp = et; taglen && isspace(*cp); ++cp, --taglen);
- size_t size;
+ static const char infoBrace[] = "[INFO]";
+ static const size_t infoBraceLen = strlen(infoBrace);
+ if ((taglen >= infoBraceLen) && !fast<strncmp>(p, infoBrace, infoBraceLen)) {
+ // <PRI>[<TIME>] "[INFO]"<tag> ":" message
+ bt = p + infoBraceLen;
+ taglen -= infoBraceLen;
+ }
+ const char *et;
+ for (et = bt; taglen && *et && (*et != ':') && !isspace(*et); ++et, --taglen) {
+ // skip ':' within [ ... ]
+ if (*et == '[') {
+ while (taglen && *et && *et != ']') {
+ ++et;
+ --taglen;
+ }
+ if (!taglen) {
+ break;
+ }
+ }
+ }
+ const char *cp;
+ for (cp = et; taglen && isspace(*cp); ++cp, --taglen);
+
+ // Validate tag
+ size_t size = et - bt;
+ if (taglen && size) {
if (*cp == ':') {
+ // ToDo: handle case insensitive colon separated logging stutter:
+ // <tag> : <tag>: ...
+
// One Word
tag = bt;
etag = et;
p = cp + 1;
- } else if (taglen) {
- size = et - bt;
- if ((taglen > size) && // enough space for match plus trailing :
- (*bt == *cp) && // ubber fast<strncmp> pair
- fast<strncmp>(bt + 1, cp + 1, size - 1)) {
- // <PRI>[<TIME>] <tag>_host '<tag>.<num>' : message
- if (!fast<strncmp>(bt + size - 5, "_host", 5)
- && !fast<strncmp>(bt + 1, cp + 1, size - 6)) {
+ } else if ((taglen > size) && (tolower(*bt) == tolower(*cp))) {
+ // clean up any tag stutter
+ if (!fast<strncasecmp>(bt + 1, cp + 1, size - 1)) { // no match
+ // <PRI>[<TIME>] <tag> <tag> : message
+ // <PRI>[<TIME>] <tag> <tag>: message
+ // <PRI>[<TIME>] <tag> '<tag>.<num>' : message
+ // <PRI>[<TIME>] <tag> '<tag><num>' : message
+ // <PRI>[<TIME>] <tag> '<tag><stuff>' : message
+ const char *b = cp;
+ cp += size;
+ taglen -= size;
+ while (--taglen && !isspace(*++cp) && (*cp != ':'));
+ const char *e;
+ for (e = cp; taglen && isspace(*cp); ++cp, --taglen);
+ if (taglen && (*cp == ':')) {
+ tag = b;
+ etag = e;
+ p = cp + 1;
+ }
+ } else {
+ // what about <PRI>[<TIME>] <tag>_host '<tag><stuff>' : message
+ static const char host[] = "_host";
+ static const size_t hostlen = strlen(host);
+ if ((size > hostlen) &&
+ !fast<strncmp>(bt + size - hostlen, host, hostlen) &&
+ !fast<strncmp>(bt + 1, cp + 1, size - hostlen - 1)) {
const char *b = cp;
- cp += size - 5;
- taglen -= size - 5;
+ cp += size - hostlen;
+ taglen -= size - hostlen;
if (*cp == '.') {
while (--taglen && !isspace(*++cp) && (*cp != ':'));
const char *e;
- for(e = cp; taglen && isspace(*cp); ++cp, --taglen);
- if (*cp == ':') {
+ for (e = cp; taglen && isspace(*cp); ++cp, --taglen);
+ if (taglen && (*cp == ':')) {
tag = b;
etag = e;
p = cp + 1;
}
}
} else {
- while (--taglen && !isspace(*++cp) && (*cp != ':'));
- const char *e;
- for(e = cp; taglen && isspace(*cp); ++cp, --taglen);
- // Two words
- if (*cp == ':') {
- tag = bt;
- etag = e;
- p = cp + 1;
- }
- }
- } else if (isspace(cp[size])) {
- cp += size;
- taglen -= size;
- while (--taglen && isspace(*++cp));
- // <PRI>[<TIME>] <tag> <tag> : message
- if (*cp == ':') {
- tag = bt;
- etag = et;
- p = cp + 1;
- }
- } else if (cp[size] == ':') {
- // <PRI>[<TIME>] <tag> <tag> : message
- tag = bt;
- etag = et;
- p = cp + size + 1;
- } else if ((cp[size] == '.') || isdigit(cp[size])) {
- // <PRI>[<TIME>] <tag> '<tag>.<num>' : message
- // <PRI>[<TIME>] <tag> '<tag><num>' : message
- const char *b = cp;
- cp += size;
- taglen -= size;
- while (--taglen && !isspace(*++cp) && (*cp != ':'));
- const char *e = cp;
- while (taglen && isspace(*cp)) {
- ++cp;
- --taglen;
- }
- if (*cp == ':') {
- tag = b;
- etag = e;
- p = cp + 1;
- }
- } else {
- while (--taglen && !isspace(*++cp) && (*cp != ':'));
- const char *e = cp;
- while (taglen && isspace(*cp)) {
- ++cp;
- --taglen;
- }
- // Two words
- if (*cp == ':') {
- tag = bt;
- etag = e;
- p = cp + 1;
+ goto twoWord;
}
}
- } /* else no tag */
- size = etag - tag;
- if ((size <= 1)
- // register names like x9
- || ((size == 2) && (isdigit(tag[0]) || isdigit(tag[1])))
- // register names like x18 but not driver names like en0
- || ((size == 3) && (isdigit(tag[1]) && isdigit(tag[2])))
- // blacklist
- || ((size == 3) && !fast<strncmp>(tag, "CPU", 3))
- || ((size == 7) && !fast<strncasecmp>(tag, "WARNING", 7))
- || ((size == 5) && !fast<strncasecmp>(tag, "ERROR", 5))
- || ((size == 4) && !fast<strncasecmp>(tag, "INFO", 4))) {
- p = start;
- etag = tag = "";
+ } else {
+ // <PRI>[<TIME>] <tag> <stuff>' : message
+twoWord: while (--taglen && !isspace(*++cp) && (*cp != ':'));
+ const char *e;
+ for (e = cp; taglen && isspace(*cp); ++cp, --taglen);
+ // Two words
+ if (taglen && (*cp == ':')) {
+ tag = bt;
+ etag = e;
+ p = cp + 1;
+ }
}
+ } // else no tag
+
+ static const char cpu[] = "CPU";
+ static const size_t cpuLen = strlen(cpu);
+ static const char warning[] = "WARNING";
+ static const size_t warningLen = strlen(warning);
+ static const char error[] = "ERROR";
+ static const size_t errorLen = strlen(error);
+ static const char info[] = "INFO";
+ static const size_t infoLen = strlen(info);
+
+ size = etag - tag;
+ if ((size <= 1)
+ // register names like x9
+ || ((size == 2) && (isdigit(tag[0]) || isdigit(tag[1])))
+ // register names like x18 but not driver names like en0
+ || ((size == 3) && (isdigit(tag[1]) && isdigit(tag[2])))
+ // blacklist
+ || ((size == cpuLen) && !fast<strncmp>(tag, cpu, cpuLen))
+ || ((size == warningLen) && !fast<strncasecmp>(tag, warning, warningLen))
+ || ((size == errorLen) && !fast<strncasecmp>(tag, error, errorLen))
+ || ((size == infoLen) && !fast<strncasecmp>(tag, info, infoLen))) {
+ p = start;
+ etag = tag = "";
}
+
// Suppress additional stutter in tag:
// eg: [143:healthd]healthd -> [143:healthd]
taglen = etag - tag;
diff --git a/rootdir/Android.mk b/rootdir/Android.mk
index 94f5cb7..aa5a520 100644
--- a/rootdir/Android.mk
+++ b/rootdir/Android.mk
@@ -125,7 +125,7 @@
ln -sf /system/etc $(TARGET_ROOT_OUT)/etc; \
ln -sf /sys/kernel/debug $(TARGET_ROOT_OUT)/d; \
ln -sf /storage/self/primary $(TARGET_ROOT_OUT)/sdcard
-ifdef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE
+ifdef BOARD_USES_VENDORIMAGE
LOCAL_POST_INSTALL_CMD += ; mkdir -p $(TARGET_ROOT_OUT)/vendor
else
LOCAL_POST_INSTALL_CMD += ; ln -sf /system/vendor $(TARGET_ROOT_OUT)/vendor
diff --git a/rootdir/asan.options b/rootdir/asan.options
index 70e0eca..d728f12 100644
--- a/rootdir/asan.options
+++ b/rootdir/asan.options
@@ -3,4 +3,5 @@
alloc_dealloc_mismatch=0
allocator_may_return_null=1
detect_container_overflow=0
+abort_on_error=1
include_if_exists=/system/asan.options.%b
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 56379db..a9b6af0 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -236,6 +236,9 @@
export DOWNLOAD_CACHE /data/cache
+ # set RLIMIT_NICE to allow priorities from 19 to -20
+ setrlimit 13 40 40
+
# Healthd can trigger a full boot from charger mode by signaling this
# property when the power button is held.
on property:sys.boot_from_charger_mode=1
@@ -258,6 +261,11 @@
# Mount filesystems and start core system services.
on late-init
trigger early-fs
+
+ # Mount fstab in init.{$device}.rc by mount_all command. Optional parameter
+ # '--early' can be specified to skip entries with 'latemount'.
+ # /system and /vendor must be mounted by the end of the fs stage,
+ # while /data is optional.
trigger fs
trigger post-fs
@@ -266,9 +274,18 @@
# issued fs triggers have completed.
trigger load_system_props_action
+ # Mount fstab in init.{$device}.rc by mount_all with '--late' parameter
+ # to only mount entries with 'latemount'. This is needed if '--early' is
+ # specified in the previous mount_all command on the fs stage.
+ # With /system mounted and properties form /system + /factory available,
+ # some services can be started.
+ trigger late-fs
+
# Now we can mount /data. File encryption requires keymaster to decrypt
- # /data, which in turn can only be loaded when system properties are present
+ # /data, which in turn can only be loaded when system properties are present.
trigger post-fs-data
+
+ # Load persist properties and override properties (if enabled) from /data.
trigger load_persist_props_action
# Remove a file to wake up anything waiting for firmware.
@@ -484,9 +501,6 @@
hostname localhost
domainname localdomain
- # set RLIMIT_NICE to allow priorities from 19 to -20
- setrlimit 13 40 40
-
# Memory management. Basic kernel parameters, and allow the high
# level system server to be able to adjust the kernel OOM driver
# parameters to match how it is managing things.
diff --git a/rootdir/init.usb.rc b/rootdir/init.usb.rc
index 1fd1e2a..915d159 100644
--- a/rootdir/init.usb.rc
+++ b/rootdir/init.usb.rc
@@ -103,7 +103,7 @@
# Used to set USB configuration at boot and to switch the configuration
# when changing the default configuration
-on property:persist.sys.usb.config=*
+on boot && property:persist.sys.usb.config=*
setprop sys.usb.config ${persist.sys.usb.config}
#