Merge "Revert "Add Winscope report to the bugreport when using continuous mode""
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 53b3a00..39b3aac 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -192,10 +192,12 @@
{ REQ, "events/cpufreq_interactive/enable" },
} },
{ "sync", "Synchronization", 0, {
- // before linux kernel 4.9
+ // linux kernel < 4.9
{ OPT, "events/sync/enable" },
- // starting in linux kernel 4.9
+ // linux kernel == 4.9.x
{ OPT, "events/fence/enable" },
+ // linux kernel > 4.9
+ { OPT, "events/dma_fence/enable" },
} },
{ "workq", "Kernel Workqueues", 0, {
{ REQ, "events/workqueue/enable" },
diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc
index a282424..66c9cc6 100644
--- a/cmds/atrace/atrace.rc
+++ b/cmds/atrace/atrace.rc
@@ -91,6 +91,8 @@
chmod 0666 /sys/kernel/tracing/events/sync/enable
chmod 0666 /sys/kernel/debug/tracing/events/fence/enable
chmod 0666 /sys/kernel/tracing/events/fence/enable
+ chmod 0666 /sys/kernel/debug/tracing/events/dma_fence/enable
+ chmod 0666 /sys/kernel/tracing/events/dma_fence/enable
chmod 0666 /sys/kernel/debug/tracing/events/kmem/rss_stat/enable
chmod 0666 /sys/kernel/tracing/events/kmem/rss_stat/enable
chmod 0666 /sys/kernel/debug/tracing/events/kmem/ion_heap_grow/enable
diff --git a/cmds/dumpstate/DumpstateService.cpp b/cmds/dumpstate/DumpstateService.cpp
index bb089e6..37ba4f9 100644
--- a/cmds/dumpstate/DumpstateService.cpp
+++ b/cmds/dumpstate/DumpstateService.cpp
@@ -44,17 +44,18 @@
return binder::Status::fromExceptionCode(code, String8(msg.c_str()));
}
-static binder::Status error(uint32_t code, const std::string& msg) {
- MYLOGE("%s (%d) ", msg.c_str(), code);
- return binder::Status::fromServiceSpecificError(code, String8(msg.c_str()));
-}
-
-// Takes ownership of data.
-static void* callAndNotify(void* data) {
+// Creates a bugreport and exits, thus preserving the oneshot nature of the service.
+// Note: takes ownership of data.
+[[noreturn]] static void* dumpstate_thread_main(void* data) {
std::unique_ptr<DumpstateInfo> ds_info(static_cast<DumpstateInfo*>(data));
ds_info->ds->Run(ds_info->calling_uid, ds_info->calling_package);
- MYLOGD("Finished Run()\n");
- return nullptr;
+ MYLOGD("Finished taking a bugreport. Exiting.\n");
+ exit(0);
+}
+
+[[noreturn]] static void signalErrorAndExit(sp<IDumpstateListener> listener, int error_code) {
+ listener->onError(error_code);
+ exit(0);
}
class DumpstateToken : public BnDumpstateToken {};
@@ -120,6 +121,25 @@
const sp<IDumpstateListener>& listener) {
MYLOGI("startBugreport() with mode: %d\n", bugreport_mode);
+ // This is the bugreporting API flow, so ensure there is only one bugreport in progress at a
+ // time.
+ std::lock_guard<std::mutex> lock(lock_);
+ if (ds_ != nullptr) {
+ MYLOGE("Error! There is already a bugreport in progress. Returning.");
+ if (listener != nullptr) {
+ listener->onError(IDumpstateListener::BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS);
+ }
+ return exception(binder::Status::EX_SERVICE_SPECIFIC,
+ "There is already a bugreport in progress");
+ }
+
+ // From here on, all conditions that indicate we are done with this incoming request should
+ // result in exiting the service to free it up for next invocation.
+ if (listener == nullptr) {
+ MYLOGE("Invalid input: no listener");
+ exit(0);
+ }
+
if (bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_FULL &&
bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE &&
bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_REMOTE &&
@@ -127,30 +147,23 @@
bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_TELEPHONY &&
bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_WIFI &&
bugreport_mode != Dumpstate::BugreportMode::BUGREPORT_DEFAULT) {
- return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
- StringPrintf("Invalid bugreport mode: %d", bugreport_mode));
- }
-
- if (bugreport_fd.get() == -1 || screenshot_fd.get() == -1) {
- return exception(binder::Status::EX_ILLEGAL_ARGUMENT, "Invalid file descriptor");
+ MYLOGE("Invalid input: bad bugreport mode: %d", bugreport_mode);
+ signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
}
std::unique_ptr<Dumpstate::DumpOptions> options = std::make_unique<Dumpstate::DumpOptions>();
options->Initialize(static_cast<Dumpstate::BugreportMode>(bugreport_mode), bugreport_fd,
screenshot_fd);
- // This is the bugreporting API flow, so ensure there is only one bugreport in progress at a
- // time.
- std::lock_guard<std::mutex> lock(lock_);
- if (ds_ != nullptr) {
- return exception(binder::Status::EX_SERVICE_SPECIFIC,
- "There is already a bugreport in progress");
+ if (bugreport_fd.get() == -1 || (options->do_fb && screenshot_fd.get() == -1)) {
+ MYLOGE("Invalid filedescriptor");
+ signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
}
+
+
ds_ = &(Dumpstate::GetInstance());
ds_->SetOptions(std::move(options));
- if (listener != nullptr) {
- ds_->listener_ = listener;
- }
+ ds_->listener_ = listener;
DumpstateInfo* ds_info = new DumpstateInfo();
ds_info->ds = ds_;
@@ -158,11 +171,12 @@
ds_info->calling_package = calling_package;
pthread_t thread;
- status_t err = pthread_create(&thread, nullptr, callAndNotify, ds_info);
+ status_t err = pthread_create(&thread, nullptr, dumpstate_thread_main, ds_info);
if (err != 0) {
delete ds_info;
ds_info = nullptr;
- return error(err, "Could not create a background thread.");
+ MYLOGE("Could not create a thread");
+ signalErrorAndExit(listener, IDumpstateListener::BUGREPORT_ERROR_RUNTIME_ERROR);
}
return binder::Status::ok();
}
diff --git a/cmds/dumpstate/binder/android/os/IDumpstate.aidl b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
index b1005d3..cb2d8b8 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstate.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstate.aidl
@@ -36,6 +36,9 @@
IDumpstateToken setListener(@utf8InCpp String name, IDumpstateListener listener,
boolean getSectionDetails);
+ // NOTE: If you add to or change these modes, please also change the corresponding enums
+ // in system server, in BugreportParams.java.
+
// These modes encapsulate a set of run time options for generating bugreports.
// Takes a bugreport without user interference.
const int BUGREPORT_MODE_FULL = 0;
@@ -58,7 +61,6 @@
// Default mode.
const int BUGREPORT_MODE_DEFAULT = 6;
- // TODO(b/111441001): Should the args be for the consuming application rather than triggering?
/*
* Starts a bugreport in the background.
*
@@ -71,7 +73,7 @@
* @param callingUid UID of the original application that requested the report.
* @param callingPackage package of the original application that requested the report.
* @param bugreportFd the file to which the zipped bugreport should be written
- * @param screenshotFd the file to which screenshot should be written; optional
+ * @param screenshotFd the file to which screenshot should be written
* @param bugreportMode the mode that specifies other run time options; must be one of above
* @param listener callback for updates; optional
*/
diff --git a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
index 907a67c..ea1e467 100644
--- a/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
+++ b/cmds/dumpstate/binder/android/os/IDumpstateListener.aidl
@@ -19,6 +19,11 @@
/**
* Listener for dumpstate events.
*
+ * <p>When bugreport creation is complete one of {@code onError} or {@code onFinished} is called.
+ *
+ * <p>These methods are synchronous by design in order to make dumpstate's lifecycle simpler
+ * to handle.
+ *
* {@hide}
*/
interface IDumpstateListener {
@@ -27,7 +32,10 @@
*
* @param progress the progress in [0, 100]
*/
- oneway void onProgress(int progress);
+ void onProgress(int progress);
+
+ // NOTE: If you add to or change these error codes, please also change the corresponding enums
+ // in system server, in BugreportManager.java.
/* Options specified are invalid or incompatible */
const int BUGREPORT_ERROR_INVALID_INPUT = 1;
@@ -41,15 +49,18 @@
/* The request to get user consent timed out */
const int BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT = 4;
+ /* There is currently a bugreport running. The caller should try again later. */
+ const int BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS = 5;
+
/**
* Called on an error condition with one of the error codes listed above.
*/
- oneway void onError(int errorCode);
+ void onError(int errorCode);
/**
* Called when taking bugreport finishes successfully.
*/
- oneway void onFinished();
+ void onFinished();
// TODO(b/111441001): Remove old methods when not used anymore.
void onProgressUpdated(int progress);
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index dc3faa0..898aa9e 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -34,6 +34,7 @@
#include <unistd.h>
#include <chrono>
+#include <fstream>
#include <functional>
#include <future>
#include <memory>
@@ -54,7 +55,9 @@
#include <android/os/IIncidentCompanion.h>
#include <cutils/native_handle.h>
#include <cutils/properties.h>
+#include <debuggerd/client.h>
#include <dumpsys.h>
+#include <dumputils/dump_utils.h>
#include <hidl/ServiceManagement.h>
#include <openssl/sha.h>
#include <private/android_filesystem_config.h>
@@ -129,6 +132,19 @@
// TODO: temporary variables and functions used during C++ refactoring
static Dumpstate& ds = Dumpstate::GetInstance();
+#define RETURN_IF_USER_DENIED_CONSENT() \
+ if (ds.IsUserConsentDenied()) { \
+ MYLOGE("Returning early as user denied consent to share bugreport with calling app."); \
+ return Dumpstate::RunStatus::USER_CONSENT_DENIED; \
+ }
+
+// Runs func_ptr, but checks user consent before and after running it. Returns USER_CONSENT_DENIED
+// if consent is found to be denied.
+#define RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(func_ptr, ...) \
+ RETURN_IF_USER_DENIED_CONSENT(); \
+ func_ptr(__VA_ARGS__); \
+ RETURN_IF_USER_DENIED_CONSENT();
+
namespace android {
namespace os {
namespace {
@@ -158,7 +174,7 @@
}
static bool CopyFileToFd(const std::string& input_file, int out_fd) {
- MYLOGD("Going to copy bugreport file (%s) to %d\n", ds.path_.c_str(), out_fd);
+ MYLOGD("Going to copy file (%s) to %d\n", input_file.c_str(), out_fd);
// Obtain a handle to the source file.
android::base::unique_fd in_fd(OpenForRead(input_file));
@@ -166,19 +182,28 @@
if (CopyFile(in_fd.get(), out_fd)) {
return true;
}
- MYLOGE("Failed to copy zip file: %s\n", strerror(errno));
+ MYLOGE("Failed to copy file: %s\n", strerror(errno));
}
return false;
}
static bool UnlinkAndLogOnError(const std::string& file) {
- if (unlink(file.c_str()) != -1) {
- MYLOGE("Failed to remove file (%s): %s\n", file.c_str(), strerror(errno));
+ if (unlink(file.c_str())) {
+ MYLOGE("Failed to unlink file (%s): %s\n", file.c_str(), strerror(errno));
return false;
}
return true;
}
+static bool IsFileEmpty(const std::string& file_path) {
+ std::ifstream file(file_path, std::ios::binary | std::ios::ate);
+ if(file.bad()) {
+ MYLOGE("Cannot open file: %s\n", file_path.c_str());
+ return true;
+ }
+ return file.tellg() <= 0;
+}
+
} // namespace
} // namespace os
} // namespace android
@@ -461,9 +486,7 @@
if (!ds.AddZipEntry("anrd_trace.txt", path)) {
MYLOGE("Unable to add anrd_trace file %s to zip file\n", path);
} else {
- if (remove(path)) {
- MYLOGE("Error removing anrd_trace file %s: %s", path, strerror(errno));
- }
+ android::os::UnlinkAndLogOnError(path);
return true;
}
} else {
@@ -768,6 +791,17 @@
ZipWriter::ErrorCodeString(err));
return UNKNOWN_ERROR;
}
+ bool finished_entry = false;
+ auto finish_entry = [this, &finished_entry] {
+ if (!finished_entry) {
+ // This should only be called when we're going to return an earlier error,
+ // which would've been logged. This may imply the file is already corrupt
+ // and any further logging from FinishEntry is more likely to mislead than
+ // not.
+ this->zip_writer_->FinishEntry();
+ }
+ };
+ auto scope_guard = android::base::make_scope_guard(finish_entry);
auto start = std::chrono::steady_clock::now();
auto end = start + timeout;
struct pollfd pfd = {fd, POLLIN};
@@ -784,11 +818,11 @@
int rc = TEMP_FAILURE_RETRY(poll(&pfd, 1, time_left_ms()));
if (rc < 0) {
- MYLOGE("Error in poll while adding from fd to zip entry %s:%s", entry_name.c_str(),
- strerror(errno));
+ MYLOGE("Error in poll while adding from fd to zip entry %s:%s\n",
+ entry_name.c_str(), strerror(errno));
return -errno;
} else if (rc == 0) {
- MYLOGE("Timed out adding from fd to zip entry %s:%s Timeout:%lldms",
+ MYLOGE("Timed out adding from fd to zip entry %s:%s Timeout:%lldms\n",
entry_name.c_str(), strerror(errno), timeout.count());
return TIMED_OUT;
}
@@ -809,6 +843,7 @@
}
err = zip_writer_->FinishEntry();
+ finished_entry = true;
if (err != 0) {
MYLOGE("zip_writer_->FinishEntry(): %s\n", ZipWriter::ErrorCodeString(err));
return UNKNOWN_ERROR;
@@ -1042,9 +1077,9 @@
RunCommand("IP RULES v6", {"ip", "-6", "rule", "show"});
}
-static void RunDumpsysTextByPriority(const std::string& title, int priority,
- std::chrono::milliseconds timeout,
- std::chrono::milliseconds service_timeout) {
+static Dumpstate::RunStatus RunDumpsysTextByPriority(const std::string& title, int priority,
+ std::chrono::milliseconds timeout,
+ std::chrono::milliseconds service_timeout) {
auto start = std::chrono::steady_clock::now();
sp<android::IServiceManager> sm = defaultServiceManager();
Dumpsys dumpsys(sm.get());
@@ -1052,6 +1087,7 @@
Dumpsys::setServiceArgs(args, /* asProto = */ false, priority);
Vector<String16> services = dumpsys.listServices(priority, /* supports_proto = */ false);
for (const String16& service : services) {
+ RETURN_IF_USER_DENIED_CONSENT();
std::string path(title);
path.append(" - ").append(String8(service).c_str());
DumpstateSectionReporter section_reporter(path, ds.listener_, ds.report_section_);
@@ -1077,6 +1113,7 @@
break;
}
}
+ return Dumpstate::RunStatus::OK;
}
static void RunDumpsysText(const std::string& title, int priority,
@@ -1089,24 +1126,27 @@
}
/* Dump all services registered with Normal or Default priority. */
-static void RunDumpsysTextNormalPriority(const std::string& title,
- std::chrono::milliseconds timeout,
- std::chrono::milliseconds service_timeout) {
+static Dumpstate::RunStatus RunDumpsysTextNormalPriority(const std::string& title,
+ std::chrono::milliseconds timeout,
+ std::chrono::milliseconds service_timeout) {
DurationReporter duration_reporter(title);
dprintf(STDOUT_FILENO, "------ %s (/system/bin/dumpsys) ------\n", title.c_str());
fsync(STDOUT_FILENO);
RunDumpsysTextByPriority(title, IServiceManager::DUMP_FLAG_PRIORITY_NORMAL, timeout,
service_timeout);
- RunDumpsysTextByPriority(title, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT, timeout,
- service_timeout);
+
+ RETURN_IF_USER_DENIED_CONSENT();
+
+ return RunDumpsysTextByPriority(title, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT, timeout,
+ service_timeout);
}
-static void RunDumpsysProto(const std::string& title, int priority,
- std::chrono::milliseconds timeout,
- std::chrono::milliseconds service_timeout) {
+static Dumpstate::RunStatus RunDumpsysProto(const std::string& title, int priority,
+ std::chrono::milliseconds timeout,
+ std::chrono::milliseconds service_timeout) {
if (!ds.IsZipping()) {
MYLOGD("Not dumping %s because it's not a zipped bugreport\n", title.c_str());
- return;
+ return Dumpstate::RunStatus::OK;
}
sp<android::IServiceManager> sm = defaultServiceManager();
Dumpsys dumpsys(sm.get());
@@ -1117,6 +1157,7 @@
auto start = std::chrono::steady_clock::now();
Vector<String16> services = dumpsys.listServices(priority, /* supports_proto = */ true);
for (const String16& service : services) {
+ RETURN_IF_USER_DENIED_CONSENT();
std::string path(kProtoPath);
path.append(String8(service).c_str());
if (priority == IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL) {
@@ -1145,35 +1186,54 @@
break;
}
}
+ return Dumpstate::RunStatus::OK;
}
// Runs dumpsys on services that must dump first and will take less than 100ms to dump.
-static void RunDumpsysCritical() {
+static Dumpstate::RunStatus RunDumpsysCritical() {
RunDumpsysText("DUMPSYS CRITICAL", IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL,
/* timeout= */ 5s, /* service_timeout= */ 500ms);
- RunDumpsysProto("DUMPSYS CRITICAL PROTO", IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL,
- /* timeout= */ 5s, /* service_timeout= */ 500ms);
+
+ RETURN_IF_USER_DENIED_CONSENT();
+
+ return RunDumpsysProto("DUMPSYS CRITICAL PROTO", IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL,
+ /* timeout= */ 5s, /* service_timeout= */ 500ms);
}
// Runs dumpsys on services that must dump first but can take up to 250ms to dump.
-static void RunDumpsysHigh() {
+static Dumpstate::RunStatus RunDumpsysHigh() {
// TODO meminfo takes ~10s, connectivity takes ~5sec to dump. They are both
// high priority. Reduce timeout once they are able to dump in a shorter time or
// moved to a parallel task.
RunDumpsysText("DUMPSYS HIGH", IServiceManager::DUMP_FLAG_PRIORITY_HIGH,
/* timeout= */ 90s, /* service_timeout= */ 30s);
- RunDumpsysProto("DUMPSYS HIGH PROTO", IServiceManager::DUMP_FLAG_PRIORITY_HIGH,
- /* timeout= */ 5s, /* service_timeout= */ 1s);
+
+ RETURN_IF_USER_DENIED_CONSENT();
+
+ return RunDumpsysProto("DUMPSYS HIGH PROTO", IServiceManager::DUMP_FLAG_PRIORITY_HIGH,
+ /* timeout= */ 5s, /* service_timeout= */ 1s);
}
// Runs dumpsys on services that must dump but can take up to 10s to dump.
-static void RunDumpsysNormal() {
+static Dumpstate::RunStatus RunDumpsysNormal() {
RunDumpsysTextNormalPriority("DUMPSYS", /* timeout= */ 90s, /* service_timeout= */ 10s);
- RunDumpsysProto("DUMPSYS PROTO", IServiceManager::DUMP_FLAG_PRIORITY_NORMAL,
- /* timeout= */ 90s, /* service_timeout= */ 10s);
+
+ RETURN_IF_USER_DENIED_CONSENT();
+
+ return RunDumpsysProto("DUMPSYS PROTO", IServiceManager::DUMP_FLAG_PRIORITY_NORMAL,
+ /* timeout= */ 90s, /* service_timeout= */ 10s);
}
static void DumpHals() {
+ if (!ds.IsZipping()) {
+ RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z", "--debug"},
+ CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
+ return;
+ }
+ DurationReporter duration_reporter("DUMP HALS");
+ RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z"},
+ CommandOptions::WithTimeout(2).AsRootIfAvailable().Build());
+
using android::hidl::manager::V1_0::IServiceManager;
using android::hardware::defaultServiceManager;
@@ -1223,9 +1283,16 @@
}
}
-static void dumpstate() {
+// Dumps various things. Returns early with status USER_CONSENT_DENIED if user denies consent
+// via the consent they are shown. Ignores other errors that occur while running various
+// commands. The consent checking is currently done around long running tasks, which happen to
+// be distributed fairly evenly throughout the function.
+static Dumpstate::RunStatus dumpstate() {
DurationReporter duration_reporter("DUMPSTATE");
+ // Dump various things. Note that anything that takes "long" (i.e. several seconds) should
+ // check intermittently (if it's intrerruptable like a foreach on pids) and/or should be wrapped
+ // in a consent check (via RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK).
dump_dev_files("TRUSTY VERSION", "/sys/bus/platform/drivers/trusty", "trusty_version");
RunCommand("UPTIME", {"uptime"});
DumpBlockStatFiles();
@@ -1233,7 +1300,9 @@
DumpFile("MEMORY INFO", "/proc/meminfo");
RunCommand("CPU INFO", {"top", "-b", "-n", "1", "-H", "-s", "6", "-o",
"pid,tid,user,pr,ni,%cpu,s,virt,res,pcy,cmd,name"});
- RunCommand("PROCRANK", {"procrank"}, AS_ROOT_20);
+
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunCommand, "PROCRANK", {"procrank"}, AS_ROOT_20);
+
DumpFile("VIRTUAL MEMORY STATS", "/proc/vmstat");
DumpFile("VMALLOC INFO", "/proc/vmallocinfo");
DumpFile("SLAB INFO", "/proc/slabinfo");
@@ -1248,16 +1317,11 @@
RunCommand("PROCESSES AND THREADS",
{"ps", "-A", "-T", "-Z", "-O", "pri,nice,rtprio,sched,pcy,time"});
- RunCommand("LIBRANK", {"librank"}, CommandOptions::AS_ROOT);
- if (ds.IsZipping()) {
- RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z"},
- CommandOptions::WithTimeout(2).AsRootIfAvailable().Build());
- DumpHals();
- } else {
- RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z", "--debug"},
- CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
- }
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunCommand, "LIBRANK", {"librank"},
+ CommandOptions::AS_ROOT);
+
+ DumpHals();
RunCommand("PRINTENV", {"printenv"});
RunCommand("NETSTAT", {"netstat", "-nW"});
@@ -1276,7 +1340,9 @@
}
RunCommand("LIST OF OPEN FILES", {"lsof"}, CommandOptions::AS_ROOT);
- for_each_pid(do_showmap, "SMAPS OF ALL PROCESSES");
+
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(for_each_pid, do_showmap, "SMAPS OF ALL PROCESSES");
+
for_each_tid(show_wchan, "BLOCKED PROCESS WAIT-CHANNELS");
for_each_pid(show_showtime, "PROCESS TIMES (pid cmd user system iowait+percentage)");
@@ -1314,7 +1380,7 @@
RunCommand("IPv6 ND CACHE", {"ip", "-6", "neigh", "show"});
RunCommand("MULTICAST ADDRESSES", {"ip", "maddr"});
- RunDumpsysHigh();
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysHigh);
RunCommand("SYSTEM PROPERTIES", {"getprop"});
@@ -1336,7 +1402,7 @@
ds.AddDir(WMTRACE_DATA_DIR, false);
}
- ds.DumpstateBoard();
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(ds.DumpstateBoard);
/* Migrate the ril_dumpstate to a device specific dumpstate? */
int rilDumpstateTimeout = android::base::GetIntProperty("ril.dumpstate.timeout", 0);
@@ -1356,14 +1422,16 @@
printf("== Android Framework Services\n");
printf("========================================================\n");
- RunDumpsysNormal();
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysNormal);
printf("========================================================\n");
printf("== Checkins\n");
printf("========================================================\n");
RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"});
- RunDumpsys("CHECKIN MEMINFO", {"meminfo", "--checkin"});
+
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsys, "CHECKIN MEMINFO", {"meminfo", "--checkin"});
+
RunDumpsys("CHECKIN NETSTATS", {"netstats", "--checkin"});
RunDumpsys("CHECKIN PROCSTATS", {"procstats", "-c"});
RunDumpsys("CHECKIN USAGESTATS", {"usagestats", "-c"});
@@ -1421,19 +1489,27 @@
printf("========================================================\n");
printf("== dumpstate: done (id %d)\n", ds.id_);
printf("========================================================\n");
+ return Dumpstate::RunStatus::OK;
}
-/* Dumps state for the default case. Returns true if everything went fine. */
-static bool DumpstateDefault() {
+/*
+ * Dumps state for the default case; drops root after it's no longer necessary.
+ *
+ * Returns RunStatus::OK if everything went fine.
+ * Returns RunStatus::ERROR if there was an error.
+ * Returns RunStatus::USER_DENIED_CONSENT if user explicitly denied consent to sharing the bugreport
+ * with the caller.
+ */
+static Dumpstate::RunStatus DumpstateDefault() {
// Try to dump anrd trace if the daemon is running.
dump_anrd_trace();
- // Invoking the following dumpsys calls before dump_traces() to try and
+ // Invoking the following dumpsys calls before DumpTraces() to try and
// keep the system stats as close to its initial state as possible.
- RunDumpsysCritical();
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(RunDumpsysCritical);
/* collect stack traces from Dalvik and native processes (needs root) */
- dump_traces_path = dump_traces();
+ RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(ds.DumpTraces, &dump_traces_path);
/* Run some operations that require root. */
ds.tombstone_data_ = GetDumpFds(TOMBSTONE_DIR, TOMBSTONE_FILE_PREFIX, !ds.IsZipping());
@@ -1469,11 +1545,11 @@
}
if (!DropRootUser()) {
- return false;
+ return Dumpstate::RunStatus::ERROR;
}
- dumpstate();
- return true;
+ RETURN_IF_USER_DENIED_CONSENT();
+ return dumpstate();
}
// This method collects common dumpsys for telephony and wifi
@@ -1510,6 +1586,9 @@
RunDumpsys("DUMPSYS", {"connectivity"}, CommandOptions::WithTimeout(90).Build(),
SEC_TO_MSEC(10));
+ RunDumpsys("DUMPSYS", {"connmetrics"}, CommandOptions::WithTimeout(90).Build(),
+ SEC_TO_MSEC(10));
+ RunDumpsys("DUMPSYS", {"netd"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10));
RunDumpsys("DUMPSYS", {"carrier_config"}, CommandOptions::WithTimeout(90).Build(),
SEC_TO_MSEC(10));
RunDumpsys("DUMPSYS", {"wifi"}, CommandOptions::WithTimeout(90).Build(),
@@ -1556,20 +1635,123 @@
RunDumpsys("DUMPSYS", {"wifi"}, CommandOptions::WithTimeout(90).Build(),
SEC_TO_MSEC(10));
- if (ds.IsZipping()) {
- RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z"},
- CommandOptions::WithTimeout(2).AsRootIfAvailable().Build());
- DumpHals();
- } else {
- RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z", "--debug"},
- CommandOptions::WithTimeout(10).AsRootIfAvailable().Build());
- }
+ DumpHals();
printf("========================================================\n");
printf("== dumpstate: done (id %d)\n", ds.id_);
printf("========================================================\n");
}
+Dumpstate::RunStatus Dumpstate::DumpTraces(const char** path) {
+ DurationReporter duration_reporter("DUMP TRACES");
+
+ const std::string temp_file_pattern = "/data/anr/dumptrace_XXXXXX";
+ const size_t buf_size = temp_file_pattern.length() + 1;
+ std::unique_ptr<char[]> file_name_buf(new char[buf_size]);
+ memcpy(file_name_buf.get(), temp_file_pattern.c_str(), buf_size);
+
+ // Create a new, empty file to receive all trace dumps.
+ //
+ // TODO: This can be simplified once we remove support for the old style
+ // dumps. We can have a file descriptor passed in to dump_traces instead
+ // of creating a file, closing it and then reopening it again.
+ android::base::unique_fd fd(mkostemp(file_name_buf.get(), O_APPEND | O_CLOEXEC));
+ if (fd < 0) {
+ MYLOGE("mkostemp on pattern %s: %s\n", file_name_buf.get(), strerror(errno));
+ return RunStatus::OK;
+ }
+
+ // Nobody should have access to this temporary file except dumpstate, but we
+ // temporarily grant 'read' to 'others' here because this file is created
+ // when tombstoned is still running as root, but dumped after dropping. This
+ // can go away once support for old style dumping has.
+ const int chmod_ret = fchmod(fd, 0666);
+ if (chmod_ret < 0) {
+ MYLOGE("fchmod on %s failed: %s\n", file_name_buf.get(), strerror(errno));
+ return RunStatus::OK;
+ }
+
+ std::unique_ptr<DIR, decltype(&closedir)> proc(opendir("/proc"), closedir);
+ if (proc.get() == nullptr) {
+ MYLOGE("opendir /proc failed: %s\n", strerror(errno));
+ return RunStatus::OK;
+ }
+
+ // Number of times process dumping has timed out. If we encounter too many
+ // failures, we'll give up.
+ int timeout_failures = 0;
+ bool dalvik_found = false;
+
+ const std::set<int> hal_pids = get_interesting_hal_pids();
+
+ struct dirent* d;
+ while ((d = readdir(proc.get()))) {
+ RETURN_IF_USER_DENIED_CONSENT();
+ int pid = atoi(d->d_name);
+ if (pid <= 0) {
+ continue;
+ }
+
+ const std::string link_name = android::base::StringPrintf("/proc/%d/exe", pid);
+ std::string exe;
+ if (!android::base::Readlink(link_name, &exe)) {
+ continue;
+ }
+
+ bool is_java_process;
+ if (exe == "/system/bin/app_process32" || exe == "/system/bin/app_process64") {
+ // Don't bother dumping backtraces for the zygote.
+ if (IsZygote(pid)) {
+ continue;
+ }
+
+ dalvik_found = true;
+ is_java_process = true;
+ } else if (should_dump_native_traces(exe.c_str()) || hal_pids.find(pid) != hal_pids.end()) {
+ is_java_process = false;
+ } else {
+ // Probably a native process we don't care about, continue.
+ continue;
+ }
+
+ // If 3 backtrace dumps fail in a row, consider debuggerd dead.
+ if (timeout_failures == 3) {
+ dprintf(fd, "ERROR: Too many stack dump failures, exiting.\n");
+ break;
+ }
+
+ const uint64_t start = Nanotime();
+ const int ret = dump_backtrace_to_file_timeout(
+ pid, is_java_process ? kDebuggerdJavaBacktrace : kDebuggerdNativeBacktrace,
+ is_java_process ? 5 : 20, fd);
+
+ if (ret == -1) {
+ // For consistency, the header and footer to this message match those
+ // dumped by debuggerd in the success case.
+ dprintf(fd, "\n---- pid %d at [unknown] ----\n", pid);
+ dprintf(fd, "Dump failed, likely due to a timeout.\n");
+ dprintf(fd, "---- end %d ----", pid);
+ timeout_failures++;
+ continue;
+ }
+
+ // We've successfully dumped stack traces, reset the failure count
+ // and write a summary of the elapsed time to the file and continue with the
+ // next process.
+ timeout_failures = 0;
+
+ dprintf(fd, "[dump %s stack %d: %.3fs elapsed]\n", is_java_process ? "dalvik" : "native",
+ pid, (float)(Nanotime() - start) / NANOS_PER_SEC);
+ }
+
+ if (!dalvik_found) {
+ MYLOGE("Warning: no Dalvik processes found to dump stacks\n");
+ }
+
+ *path = file_name_buf.release();
+ return RunStatus::OK;
+}
+
void Dumpstate::DumpstateBoard() {
DurationReporter duration_reporter("dumpstate_board()");
printf("========================================================\n");
@@ -1586,13 +1768,8 @@
for (int i = 0; i < NUM_OF_DUMPS; i++) {
paths.emplace_back(StringPrintf("%s/%s", ds.bugreport_internal_dir_.c_str(),
kDumpstateBoardFiles[i].c_str()));
- remover.emplace_back(android::base::make_scope_guard(std::bind(
- [](std::string path) {
- if (remove(path.c_str()) != 0 && errno != ENOENT) {
- MYLOGE("Could not remove(%s): %s\n", path.c_str(), strerror(errno));
- }
- },
- paths[i])));
+ remover.emplace_back(android::base::make_scope_guard(
+ std::bind([](std::string path) { android::os::UnlinkAndLogOnError(path); }, paths[i])));
}
sp<IDumpstateDevice> dumpstate_device(IDumpstateDevice::getService());
@@ -1613,6 +1790,7 @@
return;
}
+ // TODO(128270426): Check for consent in between?
for (size_t i = 0; i < paths.size(); i++) {
MYLOGI("Calling IDumpstateDevice implementation using path %s\n", paths[i].c_str());
@@ -1740,7 +1918,9 @@
}
// TODO: Should truncate the existing file.
// ... and re-open it for further logging.
- redirect_to_existing_file(stderr, const_cast<char*>(ds.log_path_.c_str()));
+ if (!redirect_to_existing_file(stderr, const_cast<char*>(ds.log_path_.c_str()))) {
+ return false;
+ }
fprintf(stderr, "\n");
int32_t err = zip_writer_->Finish();
@@ -1753,9 +1933,7 @@
ds.zip_file.reset(nullptr);
MYLOGD("Removing temporary file %s\n", tmp_path_.c_str())
- if (remove(tmp_path_.c_str()) != 0) {
- MYLOGE("Failed to remove temporary file (%s): %s\n", tmp_path_.c_str(), strerror(errno));
- }
+ android::os::UnlinkAndLogOnError(tmp_path_);
return true;
}
@@ -1974,7 +2152,7 @@
"--es", "android.intent.extra.DUMPSTATE_LOG", ds.log_path_
};
// clang-format on
- if (ds.options_->do_fb) {
+ if (ds.options_->do_fb && !android::os::IsFileEmpty(ds.screenshot_path_)) {
am_args.push_back("--es");
am_args.push_back("android.intent.extra.SCREENSHOT");
am_args.push_back(ds.screenshot_path_);
@@ -2050,13 +2228,13 @@
break;
case Dumpstate::BugreportMode::BUGREPORT_TELEPHONY:
options->telephony_only = true;
- options->do_fb = true;
+ options->do_fb = false;
options->do_broadcast = true;
break;
case Dumpstate::BugreportMode::BUGREPORT_WIFI:
options->wifi_only = true;
options->do_zip_file = true;
- options->do_fb = true;
+ options->do_fb = false;
options->do_broadcast = true;
break;
case Dumpstate::BugreportMode::BUGREPORT_DEFAULT:
@@ -2339,6 +2517,7 @@
register_sig_handler();
+ // TODO(b/111441001): maybe skip if already started?
if (options_->do_start_service) {
MYLOGI("Starting 'dumpstate' service\n");
android::status_t ret;
@@ -2361,12 +2540,17 @@
// If we are going to use a socket, do it as early as possible
// to avoid timeouts from bugreport.
if (options_->use_socket) {
- redirect_to_socket(stdout, "dumpstate");
+ if (!redirect_to_socket(stdout, "dumpstate")) {
+ return ERROR;
+ }
}
if (options_->use_control_socket) {
MYLOGD("Opening control socket\n");
control_socket_fd_ = open_socket("dumpstate");
+ if (control_socket_fd_ == -1) {
+ return ERROR;
+ }
options_->do_progress_updates = 1;
}
@@ -2425,7 +2609,9 @@
if (is_redirecting) {
// Redirect stderr to log_path_ for debugging.
TEMP_FAILURE_RETRY(dup_stderr_fd = dup(fileno(stderr)));
- redirect_to_file(stderr, const_cast<char*>(log_path_.c_str()));
+ if (!redirect_to_file(stderr, const_cast<char*>(log_path_.c_str()))) {
+ return ERROR;
+ }
if (chown(log_path_.c_str(), AID_SHELL, AID_SHELL)) {
MYLOGE("Unable to change ownership of dumpstate log file %s: %s\n", log_path_.c_str(),
strerror(errno));
@@ -2438,7 +2624,9 @@
/* TODO: rather than generating a text file now and zipping it later,
it would be more efficient to redirect stdout to the zip entry
directly, but the libziparchive doesn't support that option yet. */
- redirect_to_file(stdout, const_cast<char*>(tmp_path_.c_str()));
+ if (!redirect_to_file(stdout, const_cast<char*>(tmp_path_.c_str()))) {
+ return ERROR;
+ }
if (chown(tmp_path_.c_str(), AID_SHELL, AID_SHELL)) {
MYLOGE("Unable to change ownership of temporary bugreport file %s: %s\n",
tmp_path_.c_str(), strerror(errno));
@@ -2460,9 +2648,12 @@
DumpstateWifiOnly();
} else {
// Dump state for the default case. This also drops root.
- if (!DumpstateDefault()) {
- // Something went wrong.
- return RunStatus::ERROR;
+ RunStatus s = DumpstateDefault();
+ if (s != RunStatus::OK) {
+ if (s == RunStatus::USER_CONSENT_TIMED_OUT) {
+ HandleUserConsentDenied();
+ }
+ return s;
}
}
@@ -2485,15 +2676,22 @@
// Do an early return if there were errors. We make an exception for consent
// timing out because it's possible the user got distracted. In this case the
// bugreport is not shared but made available for manual retrieval.
+ MYLOGI("User denied consent. Returning\n");
return status;
}
- if (options_->screenshot_fd.get() != -1) {
+ if (options_->do_fb && options_->screenshot_fd.get() != -1) {
bool copy_succeeded = android::os::CopyFileToFd(screenshot_path_,
options_->screenshot_fd.get());
if (copy_succeeded) {
android::os::UnlinkAndLogOnError(screenshot_path_);
}
}
+ if (status == Dumpstate::RunStatus::USER_CONSENT_TIMED_OUT) {
+ MYLOGI(
+ "Did not receive user consent yet."
+ " Will not copy the bugreport artifacts to caller.\n");
+ // TODO(b/111441001): cancel outstanding requests
+ }
}
/* vibrate a few but shortly times to let user know it's finished */
@@ -2547,6 +2745,11 @@
}
}
+bool Dumpstate::IsUserConsentDenied() const {
+ return ds.consent_callback_ != nullptr &&
+ ds.consent_callback_->getResult() == UserConsentResult::DENIED;
+}
+
void Dumpstate::CleanupFiles() {
android::os::UnlinkAndLogOnError(tmp_path_);
android::os::UnlinkAndLogOnError(screenshot_path_);
diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h
index 9803f00..d02ec75 100644
--- a/cmds/dumpstate/dumpstate.h
+++ b/cmds/dumpstate/dumpstate.h
@@ -73,13 +73,13 @@
*/
class DurationReporter {
public:
- explicit DurationReporter(const std::string& title, bool log_only = false);
+ explicit DurationReporter(const std::string& title, bool logcat_only = false);
~DurationReporter();
private:
std::string title_;
- bool log_only_;
+ bool logcat_only_;
uint64_t started_;
DISALLOW_COPY_AND_ASSIGN(DurationReporter);
@@ -291,6 +291,12 @@
// TODO: temporary method until Dumpstate object is properly set
void SetProgress(std::unique_ptr<Progress> progress);
+ // Dumps Dalvik and native stack traces, sets the trace file location to path
+ // if it succeeded.
+ // Note that it returns early if user consent is denied with status USER_CONSENT_DENIED.
+ // Returns OK in all other cases.
+ RunStatus DumpTraces(const char** path);
+
void DumpstateBoard();
/*
@@ -328,6 +334,13 @@
void SetOptions(std::unique_ptr<DumpOptions> options);
/*
+ * Returns true if user consent is necessary and has been denied.
+ * Consent is only necessary if the caller has asked to copy over the bugreport to a file they
+ * provided.
+ */
+ bool IsUserConsentDenied() const;
+
+ /*
* Structure to hold options that determine the behavior of dumpstate.
*/
struct DumpOptions {
@@ -519,21 +532,30 @@
/** opens a socket and returns its file descriptor */
int open_socket(const char *service);
-/* redirect output to a service control socket */
-void redirect_to_socket(FILE *redirect, const char *service);
+/*
+ * Redirects 'redirect' to a service control socket.
+ *
+ * Returns true if redirect succeeds.
+ */
+bool redirect_to_socket(FILE* redirect, const char* service);
-/* redirect output to a new file */
-void redirect_to_file(FILE *redirect, char *path);
+/*
+ * Redirects 'redirect' to a file indicated by 'path', truncating it.
+ *
+ * Returns true if redirect succeeds.
+ */
+bool redirect_to_file(FILE* redirect, char* path);
-/* redirect output to an existing file */
-void redirect_to_existing_file(FILE *redirect, char *path);
+/*
+ * Redirects 'redirect' to an existing file indicated by 'path', appending it.
+ *
+ * Returns true if redirect succeeds.
+ */
+bool redirect_to_existing_file(FILE* redirect, char* path);
/* create leading directories, if necessary */
void create_parent_dirs(const char *path);
-/* dump Dalvik and native stack traces, return the trace file location (NULL if none) */
-const char *dump_traces();
-
/* for each process in the system, run the specified function */
void for_each_pid(for_each_pid_func func, const char *header);
diff --git a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
index 9f99114..fc3642c 100644
--- a/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_smoke_test.cpp
@@ -21,6 +21,10 @@
#include <libgen.h>
#include <android-base/file.h>
+#include <android/os/BnDumpstate.h>
+#include <android/os/BnDumpstateListener.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
#include <cutils/properties.h>
#include <ziparchive/zip_archive.h>
@@ -34,6 +38,24 @@
using ::testing::Test;
using ::std::literals::chrono_literals::operator""s;
+using android::base::unique_fd;
+
+class DumpstateListener;
+
+namespace {
+
+sp<IDumpstate> GetDumpstateService() {
+ return android::interface_cast<IDumpstate>(
+ android::defaultServiceManager()->getService(String16("dumpstate")));
+}
+
+int OpenForWrite(const std::string& filename) {
+ return TEMP_FAILURE_RETRY(open(filename.c_str(),
+ O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_NOFOLLOW,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
+}
+
+} // namespace
struct SectionInfo {
std::string name;
@@ -46,41 +68,71 @@
* Listens to bugreport progress and updates the user by writing the progress to STDOUT. All the
* section details generated by dumpstate are added to a vector to be used by Tests later.
*/
-class DumpstateListener : public IDumpstateListener {
+class DumpstateListener : public BnDumpstateListener {
public:
- int outFd_, max_progress_;
- std::shared_ptr<std::vector<SectionInfo>> sections_;
DumpstateListener(int fd, std::shared_ptr<std::vector<SectionInfo>> sections)
- : outFd_(fd), max_progress_(5000), sections_(sections) {
+ : out_fd_(fd), sections_(sections) {
}
+
+ DumpstateListener(int fd) : out_fd_(fd) {
+ }
+
binder::Status onProgress(int32_t progress) override {
- dprintf(outFd_, "\rIn progress %d", progress);
+ dprintf(out_fd_, "\rIn progress %d", progress);
return binder::Status::ok();
}
+
binder::Status onError(int32_t error_code) override {
- dprintf(outFd_, "\rError %d", error_code);
+ std::lock_guard<std::mutex> lock(lock_);
+ error_code_ = error_code;
+ dprintf(out_fd_, "\rError code %d", error_code);
return binder::Status::ok();
}
+
binder::Status onFinished() override {
- dprintf(outFd_, "\rFinished");
+ std::lock_guard<std::mutex> lock(lock_);
+ is_finished_ = true;
+ dprintf(out_fd_, "\rFinished");
return binder::Status::ok();
}
+
binder::Status onProgressUpdated(int32_t progress) override {
- dprintf(outFd_, "\rIn progress %d/%d", progress, max_progress_);
+ dprintf(out_fd_, "\rIn progress %d/%d", progress, max_progress_);
return binder::Status::ok();
}
+
binder::Status onMaxProgressUpdated(int32_t max_progress) override {
+ std::lock_guard<std::mutex> lock(lock_);
max_progress_ = max_progress;
return binder::Status::ok();
}
+
binder::Status onSectionComplete(const ::std::string& name, int32_t status, int32_t size_bytes,
int32_t duration_ms) override {
- sections_->push_back({name, status, size_bytes, duration_ms});
+ std::lock_guard<std::mutex> lock(lock_);
+ if (sections_.get() != nullptr) {
+ sections_->push_back({name, status, size_bytes, duration_ms});
+ }
return binder::Status::ok();
}
- IBinder* onAsBinder() override {
- return nullptr;
+
+ bool getIsFinished() {
+ std::lock_guard<std::mutex> lock(lock_);
+ return is_finished_;
}
+
+ int getErrorCode() {
+ std::lock_guard<std::mutex> lock(lock_);
+ return error_code_;
+ }
+
+ private:
+ int out_fd_;
+ int max_progress_ = 5000;
+ int error_code_ = -1;
+ bool is_finished_ = false;
+ std::shared_ptr<std::vector<SectionInfo>> sections_;
+ std::mutex lock_;
};
/**
@@ -293,6 +345,148 @@
SectionExists("DUMPSYS - wifi", /* bytes= */ 100000);
}
+class DumpstateBinderTest : public Test {
+ protected:
+ void SetUp() override {
+ // In case there is a stray service, stop it first.
+ property_set("ctl.stop", "bugreportd");
+ // dry_run results in a faster bugreport.
+ property_set("dumpstate.dry_run", "true");
+ // We need to receive some async calls later. Ensure we have binder threads.
+ ProcessState::self()->startThreadPool();
+ }
+
+ void TearDown() override {
+ property_set("ctl.stop", "bugreportd");
+ property_set("dumpstate.dry_run", "");
+
+ unlink("/data/local/tmp/tmp.zip");
+ unlink("/data/local/tmp/tmp.png");
+ }
+
+ // Waits until listener gets the callbacks.
+ void WaitTillExecutionComplete(DumpstateListener* listener) {
+ // Wait till one of finished, error or timeout.
+ static const int kBugreportTimeoutSeconds = 120;
+ int i = 0;
+ while (!listener->getIsFinished() && listener->getErrorCode() == -1 &&
+ i < kBugreportTimeoutSeconds) {
+ sleep(1);
+ i++;
+ }
+ }
+};
+
+TEST_F(DumpstateBinderTest, Baseline) {
+ // In the beginning dumpstate binder service is not running.
+ sp<android::os::IDumpstate> ds_binder(GetDumpstateService());
+ EXPECT_EQ(ds_binder, nullptr);
+
+ // Start bugreportd, which runs dumpstate binary with -w; which starts dumpstate service
+ // and makes it wait.
+ property_set("dumpstate.dry_run", "true");
+ property_set("ctl.start", "bugreportd");
+
+ // Now we are able to retrieve dumpstate binder service.
+ ds_binder = GetDumpstateService();
+ EXPECT_NE(ds_binder, nullptr);
+
+ // Prepare arguments
+ unique_fd bugreport_fd(OpenForWrite("/bugreports/tmp.zip"));
+ unique_fd screenshot_fd(OpenForWrite("/bugreports/tmp.png"));
+
+ EXPECT_NE(bugreport_fd.get(), -1);
+ EXPECT_NE(screenshot_fd.get(), -1);
+
+ sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout))));
+ android::binder::Status status =
+ ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd,
+ Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener);
+ // startBugreport is an async call. Verify binder call succeeded first, then wait till listener
+ // gets expected callbacks.
+ EXPECT_TRUE(status.isOk());
+ WaitTillExecutionComplete(listener.get());
+
+ // Bugreport generation requires user consent, which we cannot get in a test set up,
+ // so instead of getting is_finished_, we are more likely to get a consent error.
+ EXPECT_TRUE(
+ listener->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_DENIED_CONSENT ||
+ listener->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
+
+ // The service should have died on its own, freeing itself up for a new invocation.
+ sleep(2);
+ ds_binder = GetDumpstateService();
+ EXPECT_EQ(ds_binder, nullptr);
+}
+
+TEST_F(DumpstateBinderTest, ServiceDies_OnInvalidInput) {
+ // Start bugreportd, which runs dumpstate binary with -w; which starts dumpstate service
+ // and makes it wait.
+ property_set("ctl.start", "bugreportd");
+ sp<android::os::IDumpstate> ds_binder(GetDumpstateService());
+ EXPECT_NE(ds_binder, nullptr);
+
+ // Prepare arguments
+ unique_fd bugreport_fd(OpenForWrite("/data/local/tmp/tmp.zip"));
+ unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png"));
+
+ EXPECT_NE(bugreport_fd.get(), -1);
+ EXPECT_NE(screenshot_fd.get(), -1);
+
+ // Call startBugreport with bad arguments.
+ sp<DumpstateListener> listener(new DumpstateListener(dup(fileno(stdout))));
+ android::binder::Status status =
+ ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd,
+ 2000, // invalid bugreport mode
+ listener);
+ EXPECT_EQ(listener->getErrorCode(), IDumpstateListener::BUGREPORT_ERROR_INVALID_INPUT);
+
+ // The service should have died, freeing itself up for a new invocation.
+ sleep(2);
+ ds_binder = GetDumpstateService();
+ EXPECT_EQ(ds_binder, nullptr);
+}
+
+TEST_F(DumpstateBinderTest, SimultaneousBugreportsNotAllowed) {
+ // Start bugreportd, which runs dumpstate binary with -w; which starts dumpstate service
+ // and makes it wait.
+ property_set("dumpstate.dry_run", "true");
+ property_set("ctl.start", "bugreportd");
+ sp<android::os::IDumpstate> ds_binder(GetDumpstateService());
+ EXPECT_NE(ds_binder, nullptr);
+
+ // Prepare arguments
+ unique_fd bugreport_fd(OpenForWrite("/data/local/tmp/tmp.zip"));
+ unique_fd screenshot_fd(OpenForWrite("/data/local/tmp/tmp.png"));
+
+ EXPECT_NE(bugreport_fd.get(), -1);
+ EXPECT_NE(screenshot_fd.get(), -1);
+
+ sp<DumpstateListener> listener1(new DumpstateListener(dup(fileno(stdout))));
+ android::binder::Status status =
+ ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd,
+ Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener1);
+ EXPECT_TRUE(status.isOk());
+
+ // try to make another call to startBugreport. This should fail.
+ sp<DumpstateListener> listener2(new DumpstateListener(dup(fileno(stdout))));
+ status = ds_binder->startBugreport(123, "com.dummy.package", bugreport_fd, screenshot_fd,
+ Dumpstate::BugreportMode::BUGREPORT_INTERACTIVE, listener2);
+ EXPECT_FALSE(status.isOk());
+ WaitTillExecutionComplete(listener2.get());
+ EXPECT_EQ(listener2->getErrorCode(),
+ IDumpstateListener::BUGREPORT_ERROR_ANOTHER_REPORT_IN_PROGRESS);
+
+ // Meanwhile the first call works as expected. Service should not die in this case.
+ WaitTillExecutionComplete(listener1.get());
+
+ // Bugreport generation requires user consent, which we cannot get in a test set up,
+ // so instead of getting is_finished_, we are more likely to get a consent error.
+ EXPECT_TRUE(
+ listener1->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_DENIED_CONSENT ||
+ listener1->getErrorCode() == IDumpstateListener::BUGREPORT_ERROR_USER_CONSENT_TIMED_OUT);
+}
+
} // namespace dumpstate
} // namespace os
} // namespace android
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index eb73d41..71d15f4 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -373,7 +373,7 @@
EXPECT_EQ(status, Dumpstate::RunStatus::OK);
EXPECT_TRUE(options_.do_add_date);
- EXPECT_TRUE(options_.do_fb);
+ EXPECT_FALSE(options_.do_fb);
EXPECT_TRUE(options_.do_broadcast);
EXPECT_TRUE(options_.do_zip_file);
EXPECT_TRUE(options_.telephony_only);
@@ -404,7 +404,7 @@
EXPECT_EQ(status, Dumpstate::RunStatus::OK);
EXPECT_TRUE(options_.do_add_date);
- EXPECT_TRUE(options_.do_fb);
+ EXPECT_FALSE(options_.do_fb);
EXPECT_TRUE(options_.do_broadcast);
EXPECT_TRUE(options_.do_zip_file);
EXPECT_TRUE(options_.wifi_only);
@@ -666,8 +666,7 @@
EXPECT_THAT(err, StrEq("stderr\n"));
// We don't know the exact duration, so we check the prefix and suffix
EXPECT_THAT(out,
- StartsWith("------ I AM GROOT (" + kSimpleCommand + ") ------\nstdout\n------"));
- EXPECT_THAT(out, EndsWith("s was the duration of 'I AM GROOT' ------\n"));
+ StartsWith("------ I AM GROOT (" + kSimpleCommand + ") ------\nstdout\n"));
}
TEST_F(DumpstateTest, RunCommandWithLoggingMessage) {
@@ -702,8 +701,7 @@
EXPECT_EQ(0, RunCommand("I AM GROOT", {kSimpleCommand}));
// We don't know the exact duration, so we check the prefix and suffix
EXPECT_THAT(out, StartsWith("------ I AM GROOT (" + kSimpleCommand +
- ") ------\n\t(skipped on dry run)\n------"));
- EXPECT_THAT(out, EndsWith("s was the duration of 'I AM GROOT' ------\n"));
+ ") ------\n\t(skipped on dry run)\n"));
EXPECT_THAT(err, IsEmpty());
}
@@ -1042,7 +1040,6 @@
// We don't know the exact duration, so we check the prefix and suffix
EXPECT_THAT(out, StartsWith("*** Error dumping /I/cant/believe/I/exist (Y U NO EXIST?): No "
"such file or directory\n"));
- EXPECT_THAT(out, EndsWith("s was the duration of 'Y U NO EXIST?' ------\n"));
}
TEST_F(DumpstateTest, DumpFileSingleLine) {
@@ -1082,8 +1079,7 @@
EXPECT_THAT(err, IsEmpty());
EXPECT_THAT(
out, StartsWith("------ Might as well dump. Dump! (" + kTestDataPath + "single-line.txt:"));
- EXPECT_THAT(out, HasSubstr("\n\t(skipped on dry run)\n------"));
- EXPECT_THAT(out, EndsWith("s was the duration of 'Might as well dump. Dump!' ------\n"));
+ EXPECT_THAT(out, HasSubstr("\n\t(skipped on dry run)\n"));
}
TEST_F(DumpstateTest, DumpFileUpdateProgress) {
diff --git a/cmds/dumpstate/utils.cpp b/cmds/dumpstate/utils.cpp
index 528e43d..0bb80dc 100644
--- a/cmds/dumpstate/utils.cpp
+++ b/cmds/dumpstate/utils.cpp
@@ -50,8 +50,6 @@
#include <android-base/unique_fd.h>
#include <cutils/properties.h>
#include <cutils/sockets.h>
-#include <debuggerd/client.h>
-#include <dumputils/dump_utils.h>
#include <log/log.h>
#include <private/android_filesystem_config.h>
@@ -95,8 +93,8 @@
return singleton_;
}
-DurationReporter::DurationReporter(const std::string& title, bool log_only)
- : title_(title), log_only_(log_only) {
+DurationReporter::DurationReporter(const std::string& title, bool logcat_only)
+ : title_(title), logcat_only_(logcat_only) {
if (!title_.empty()) {
started_ = Nanotime();
}
@@ -104,14 +102,16 @@
DurationReporter::~DurationReporter() {
if (!title_.empty()) {
- uint64_t elapsed = Nanotime() - started_;
- if (log_only_) {
- MYLOGD("Duration of '%s': %.3fs\n", title_.c_str(), (float)elapsed / NANOS_PER_SEC);
- } else {
- // Use "Yoda grammar" to make it easier to grep|sort sections.
- printf("------ %.3fs was the duration of '%s' ------\n", (float)elapsed / NANOS_PER_SEC,
- title_.c_str());
+ float elapsed = (float)(Nanotime() - started_) / NANOS_PER_SEC;
+ if (elapsed < .5f) {
+ return;
}
+ MYLOGD("Duration of '%s': %.2fs\n", title_.c_str(), elapsed);
+ if (logcat_only_) {
+ return;
+ }
+ // Use "Yoda grammar" to make it easier to grep|sort sections.
+ printf("------ %.3fs was the duration of '%s' ------\n", elapsed, title_.c_str());
}
}
@@ -280,6 +280,12 @@
if (header) printf("\n------ %s ------\n", header);
while ((de = readdir(d))) {
+ if (ds.IsUserConsentDenied()) {
+ MYLOGE(
+ "Returning early because user denied consent to share bugreport with calling app.");
+ closedir(d);
+ return;
+ }
int pid;
int fd;
char cmdpath[255];
@@ -352,6 +358,12 @@
func(pid, pid, cmdline);
while ((de = readdir(d))) {
+ if (ds.IsUserConsentDenied()) {
+ MYLOGE(
+ "Returning early because user denied consent to share bugreport with calling app.");
+ closedir(d);
+ return;
+ }
int tid;
int fd;
char commpath[255];
@@ -710,12 +722,12 @@
int s = android_get_control_socket(service);
if (s < 0) {
MYLOGE("android_get_control_socket(%s): %s\n", service, strerror(errno));
- exit(1);
+ return -1;
}
fcntl(s, F_SETFD, FD_CLOEXEC);
if (listen(s, 4) < 0) {
MYLOGE("listen(control socket): %s\n", strerror(errno));
- exit(1);
+ return -1;
}
struct sockaddr addr;
@@ -723,18 +735,23 @@
int fd = accept(s, &addr, &alen);
if (fd < 0) {
MYLOGE("accept(control socket): %s\n", strerror(errno));
- exit(1);
+ return -1;
}
return fd;
}
/* redirect output to a service control socket */
-void redirect_to_socket(FILE *redirect, const char *service) {
+bool redirect_to_socket(FILE* redirect, const char* service) {
int fd = open_socket(service);
+ if (fd == -1) {
+ return false;
+ }
fflush(redirect);
- dup2(fd, fileno(redirect));
+ // TODO: handle dup2 failure
+ TEMP_FAILURE_RETRY(dup2(fd, fileno(redirect)));
close(fd);
+ return true;
}
// TODO: should call is_valid_output_file and/or be merged into it.
@@ -764,7 +781,7 @@
}
}
-void _redirect_to_file(FILE *redirect, char *path, int truncate_flag) {
+bool _redirect_to_file(FILE* redirect, char* path, int truncate_flag) {
create_parent_dirs(path);
int fd = TEMP_FAILURE_RETRY(open(path,
@@ -772,128 +789,20 @@
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
if (fd < 0) {
MYLOGE("%s: %s\n", path, strerror(errno));
- exit(1);
+ return false;
}
TEMP_FAILURE_RETRY(dup2(fd, fileno(redirect)));
close(fd);
+ return true;
}
-void redirect_to_file(FILE *redirect, char *path) {
- _redirect_to_file(redirect, path, O_TRUNC);
+bool redirect_to_file(FILE* redirect, char* path) {
+ return _redirect_to_file(redirect, path, O_TRUNC);
}
-void redirect_to_existing_file(FILE *redirect, char *path) {
- _redirect_to_file(redirect, path, O_APPEND);
-}
-
-// Dump Dalvik and native stack traces, return the trace file location (nullptr if none).
-const char* dump_traces() {
- DurationReporter duration_reporter("DUMP TRACES");
-
- const std::string temp_file_pattern = "/data/anr/dumptrace_XXXXXX";
- const size_t buf_size = temp_file_pattern.length() + 1;
- std::unique_ptr<char[]> file_name_buf(new char[buf_size]);
- memcpy(file_name_buf.get(), temp_file_pattern.c_str(), buf_size);
-
- // Create a new, empty file to receive all trace dumps.
- //
- // TODO: This can be simplified once we remove support for the old style
- // dumps. We can have a file descriptor passed in to dump_traces instead
- // of creating a file, closing it and then reopening it again.
- android::base::unique_fd fd(mkostemp(file_name_buf.get(), O_APPEND | O_CLOEXEC));
- if (fd < 0) {
- MYLOGE("mkostemp on pattern %s: %s\n", file_name_buf.get(), strerror(errno));
- return nullptr;
- }
-
- // Nobody should have access to this temporary file except dumpstate, but we
- // temporarily grant 'read' to 'others' here because this file is created
- // when tombstoned is still running as root, but dumped after dropping. This
- // can go away once support for old style dumping has.
- const int chmod_ret = fchmod(fd, 0666);
- if (chmod_ret < 0) {
- MYLOGE("fchmod on %s failed: %s\n", file_name_buf.get(), strerror(errno));
- return nullptr;
- }
-
- std::unique_ptr<DIR, decltype(&closedir)> proc(opendir("/proc"), closedir);
- if (proc.get() == nullptr) {
- MYLOGE("opendir /proc failed: %s\n", strerror(errno));
- return nullptr;
- }
-
- // Number of times process dumping has timed out. If we encounter too many
- // failures, we'll give up.
- int timeout_failures = 0;
- bool dalvik_found = false;
-
- const std::set<int> hal_pids = get_interesting_hal_pids();
-
- struct dirent* d;
- while ((d = readdir(proc.get()))) {
- int pid = atoi(d->d_name);
- if (pid <= 0) {
- continue;
- }
-
- const std::string link_name = android::base::StringPrintf("/proc/%d/exe", pid);
- std::string exe;
- if (!android::base::Readlink(link_name, &exe)) {
- continue;
- }
-
- bool is_java_process;
- if (exe == "/system/bin/app_process32" || exe == "/system/bin/app_process64") {
- // Don't bother dumping backtraces for the zygote.
- if (IsZygote(pid)) {
- continue;
- }
-
- dalvik_found = true;
- is_java_process = true;
- } else if (should_dump_native_traces(exe.c_str()) || hal_pids.find(pid) != hal_pids.end()) {
- is_java_process = false;
- } else {
- // Probably a native process we don't care about, continue.
- continue;
- }
-
- // If 3 backtrace dumps fail in a row, consider debuggerd dead.
- if (timeout_failures == 3) {
- dprintf(fd, "ERROR: Too many stack dump failures, exiting.\n");
- break;
- }
-
- const uint64_t start = Nanotime();
- const int ret = dump_backtrace_to_file_timeout(
- pid, is_java_process ? kDebuggerdJavaBacktrace : kDebuggerdNativeBacktrace,
- is_java_process ? 5 : 20, fd);
-
- if (ret == -1) {
- // For consistency, the header and footer to this message match those
- // dumped by debuggerd in the success case.
- dprintf(fd, "\n---- pid %d at [unknown] ----\n", pid);
- dprintf(fd, "Dump failed, likely due to a timeout.\n");
- dprintf(fd, "---- end %d ----", pid);
- timeout_failures++;
- continue;
- }
-
- // We've successfully dumped stack traces, reset the failure count
- // and write a summary of the elapsed time to the file and continue with the
- // next process.
- timeout_failures = 0;
-
- dprintf(fd, "[dump %s stack %d: %.3fs elapsed]\n", is_java_process ? "dalvik" : "native",
- pid, (float)(Nanotime() - start) / NANOS_PER_SEC);
- }
-
- if (!dalvik_found) {
- MYLOGE("Warning: no Dalvik processes found to dump stacks\n");
- }
-
- return file_name_buf.release();
+bool redirect_to_existing_file(FILE* redirect, char* path) {
+ return _redirect_to_file(redirect, path, O_APPEND);
}
void dump_route_tables() {
diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp
index 9d3a234..4619427 100644
--- a/cmds/installd/Android.bp
+++ b/cmds/installd/Android.bp
@@ -34,6 +34,7 @@
"libprocessgroup",
"libselinux",
"libutils",
+ "server_configurable_flags",
],
product_variables: {
@@ -144,9 +145,11 @@
],
shared_libs: [
"libbase",
+ "libbinder",
"liblog",
"libprotobuf-cpp-full",
"libselinux",
+ "libutils",
"libziparchive",
],
static_libs: [
@@ -155,6 +158,7 @@
"lib_apex_manifest_proto",
"libavb",
"libdm",
+ "libvold_binder",
],
}
@@ -225,6 +229,7 @@
"libprocessgroup",
"libselinux",
"libutils",
+ "server_configurable_flags",
],
}
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index ccffd29..ae4ea78 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -39,6 +39,7 @@
#include <sys/xattr.h>
#include <unistd.h>
+#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/scopeguard.h>
@@ -80,6 +81,8 @@
// An uuid used in unit tests.
static constexpr const char* kTestUuid = "TEST";
+static constexpr const mode_t kRollbackFolderMode = 0700;
+
static constexpr const char* kCpPath = "/system/bin/cp";
static constexpr const char* kXattrDefault = "user.default";
@@ -177,7 +180,7 @@
}
binder::Status checkArgumentPackageName(const std::string& packageName) {
- if (is_valid_package_name(packageName.c_str())) {
+ if (is_valid_package_name(packageName)) {
return ok();
} else {
return exception(binder::Status::EX_ILLEGAL_ARGUMENT,
@@ -784,8 +787,8 @@
binder::Status InstalldNativeService::snapshotAppData(
const std::unique_ptr<std::string>& volumeUuid,
- const std::string& packageName, int32_t user, int32_t storageFlags,
- int64_t* _aidl_return) {
+ const std::string& packageName, int32_t user, int32_t snapshotId,
+ int32_t storageFlags, int64_t* _aidl_return) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
@@ -802,16 +805,19 @@
bool clear_ce_on_exit = false;
bool clear_de_on_exit = false;
- auto deleter = [&clear_ce_on_exit, &clear_de_on_exit, &volume_uuid, &user, &package_name] {
+ auto deleter = [&clear_ce_on_exit, &clear_de_on_exit, &volume_uuid, &user, &package_name,
+ &snapshotId] {
if (clear_de_on_exit) {
- auto to = create_data_misc_de_rollback_package_path(volume_uuid, user, package_name);
+ auto to = create_data_misc_de_rollback_package_path(volume_uuid, user, snapshotId,
+ package_name);
if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) {
LOG(WARNING) << "Failed to delete app data snapshot: " << to;
}
}
if (clear_ce_on_exit) {
- auto to = create_data_misc_ce_rollback_package_path(volume_uuid, user, package_name);
+ auto to = create_data_misc_ce_rollback_package_path(volume_uuid, user, snapshotId,
+ package_name);
if (delete_dir_contents(to.c_str(), 1, nullptr) != 0) {
LOG(WARNING) << "Failed to delete app data snapshot: " << to;
}
@@ -847,15 +853,21 @@
if (storageFlags & FLAG_STORAGE_DE) {
auto from = create_data_user_de_package_path(volume_uuid, user, package_name);
- auto to = create_data_misc_de_rollback_path(volume_uuid, user);
+ auto to = create_data_misc_de_rollback_path(volume_uuid, user, snapshotId);
+ auto rollback_package_path = create_data_misc_de_rollback_package_path(volume_uuid, user,
+ snapshotId, package_name);
- int rd = delete_dir_contents(to, true /* ignore_if_missing */);
- if (rd != 0) {
- res = error(rd, "Failed clearing existing snapshot " + to);
- return res;
+ int rc = create_dir_if_needed(to.c_str(), kRollbackFolderMode);
+ if (rc != 0) {
+ return error(rc, "Failed to create folder " + to);
}
- int rc = copy_directory_recursive(from.c_str(), to.c_str());
+ rc = delete_dir_contents(rollback_package_path, true /* ignore_if_missing */);
+ if (rc != 0) {
+ return error(rc, "Failed clearing existing snapshot " + rollback_package_path);
+ }
+
+ rc = copy_directory_recursive(from.c_str(), to.c_str());
if (rc != 0) {
res = error(rc, "Failed copying " + from + " to " + to);
clear_de_on_exit = true;
@@ -865,15 +877,21 @@
if (storageFlags & FLAG_STORAGE_CE) {
auto from = create_data_user_ce_package_path(volume_uuid, user, package_name);
- auto to = create_data_misc_ce_rollback_path(volume_uuid, user);
+ auto to = create_data_misc_ce_rollback_path(volume_uuid, user, snapshotId);
+ auto rollback_package_path = create_data_misc_ce_rollback_package_path(volume_uuid, user,
+ snapshotId, package_name);
- int rd = delete_dir_contents(to, true /* ignore_if_missing */);
- if (rd != 0) {
- res = error(rd, "Failed clearing existing snapshot " + to);
- return res;
+ int rc = create_dir_if_needed(to.c_str(), kRollbackFolderMode);
+ if (rc != 0) {
+ return error(rc, "Failed to create folder " + to);
}
- int rc = copy_directory_recursive(from.c_str(), to.c_str());
+ rc = delete_dir_contents(rollback_package_path, true /* ignore_if_missing */);
+ if (rc != 0) {
+ return error(rc, "Failed clearing existing snapshot " + rollback_package_path);
+ }
+
+ rc = copy_directory_recursive(from.c_str(), to.c_str());
if (rc != 0) {
res = error(rc, "Failed copying " + from + " to " + to);
clear_ce_on_exit = true;
@@ -881,7 +899,7 @@
}
if (_aidl_return != nullptr) {
auto ce_snapshot_path = create_data_misc_ce_rollback_package_path(volume_uuid, user,
- package_name);
+ snapshotId, package_name);
rc = get_path_inode(ce_snapshot_path, reinterpret_cast<ino_t*>(_aidl_return));
if (rc != 0) {
res = error(rc, "Failed to get_path_inode for " + ce_snapshot_path);
@@ -896,8 +914,8 @@
binder::Status InstalldNativeService::restoreAppDataSnapshot(
const std::unique_ptr<std::string>& volumeUuid, const std::string& packageName,
- const int32_t appId, const int64_t ceDataInode, const std::string& seInfo,
- const int32_t user, int32_t storageFlags) {
+ const int32_t appId, const std::string& seInfo, const int32_t user,
+ const int32_t snapshotId, int32_t storageFlags) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
@@ -907,9 +925,9 @@
const char* package_name = packageName.c_str();
auto from_ce = create_data_misc_ce_rollback_package_path(volume_uuid,
- user, package_name);
+ user, snapshotId, package_name);
auto from_de = create_data_misc_de_rollback_package_path(volume_uuid,
- user, package_name);
+ user, snapshotId, package_name);
const bool needs_ce_rollback = (storageFlags & FLAG_STORAGE_CE) &&
(access(from_ce.c_str(), F_OK) == 0);
@@ -926,7 +944,11 @@
// app with no data in those cases is arguably better than leaving the app
// with mismatched / stale data.
LOG(INFO) << "Clearing app data for " << packageName << " to restore snapshot.";
- binder::Status res = clearAppData(volumeUuid, packageName, user, storageFlags, ceDataInode);
+ // It's fine to pass 0 as ceDataInode here, because restoreAppDataSnapshot
+ // can only be called when user unlocks the phone, meaning that CE user data
+ // is decrypted.
+ binder::Status res = clearAppData(volumeUuid, packageName, user, storageFlags,
+ 0 /* ceDataInode */);
if (!res.isOk()) {
return res;
}
@@ -962,7 +984,8 @@
binder::Status InstalldNativeService::destroyAppDataSnapshot(
const std::unique_ptr<std::string> &volumeUuid, const std::string& packageName,
- const int32_t user, const int64_t ceSnapshotInode, int32_t storageFlags) {
+ const int32_t user, const int64_t ceSnapshotInode, const int32_t snapshotId,
+ int32_t storageFlags) {
ENFORCE_UID(AID_SYSTEM);
CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid);
CHECK_ARGUMENT_PACKAGE_NAME(packageName);
@@ -973,7 +996,7 @@
if (storageFlags & FLAG_STORAGE_DE) {
auto de_snapshot_path = create_data_misc_de_rollback_package_path(volume_uuid,
- user, package_name);
+ user, snapshotId, package_name);
int res = delete_dir_contents_and_dir(de_snapshot_path, true /* ignore_if_missing */);
if (res != 0) {
@@ -983,7 +1006,7 @@
if (storageFlags & FLAG_STORAGE_CE) {
auto ce_snapshot_path = create_data_misc_ce_rollback_package_path(volume_uuid,
- user, package_name, ceSnapshotInode);
+ user, snapshotId, package_name, ceSnapshotInode);
int res = delete_dir_contents_and_dir(ce_snapshot_path, true /* ignore_if_missing */);
if (res != 0) {
return error(res, "Failed clearing snapshot " + ce_snapshot_path);
diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h
index 578132d..0e91cb2 100644
--- a/cmds/installd/InstalldNativeService.h
+++ b/cmds/installd/InstalldNativeService.h
@@ -61,14 +61,14 @@
binder::Status fixupAppData(const std::unique_ptr<std::string>& uuid, int32_t flags);
binder::Status snapshotAppData(const std::unique_ptr<std::string>& volumeUuid,
- const std::string& packageName, const int32_t user, int32_t storageFlags,
- int64_t* _aidl_return);
+ const std::string& packageName, const int32_t user, const int32_t snapshotId,
+ int32_t storageFlags, int64_t* _aidl_return);
binder::Status restoreAppDataSnapshot(const std::unique_ptr<std::string>& volumeUuid,
- const std::string& packageName, const int32_t appId, const int64_t ceDataInode,
- const std::string& seInfo, const int32_t user, int32_t storageFlags);
+ const std::string& packageName, const int32_t appId, const std::string& seInfo,
+ const int32_t user, const int32_t snapshotId, int32_t storageFlags);
binder::Status destroyAppDataSnapshot(const std::unique_ptr<std::string> &volumeUuid,
const std::string& packageName, const int32_t user, const int64_t ceSnapshotInode,
- int32_t storageFlags);
+ const int32_t snapshotId, int32_t storageFlags);
binder::Status getAppSize(const std::unique_ptr<std::string>& uuid,
const std::vector<std::string>& packageNames, int32_t userId, int32_t flags,
diff --git a/cmds/installd/OWNERS b/cmds/installd/OWNERS
index 5d4f176..5673918 100644
--- a/cmds/installd/OWNERS
+++ b/cmds/installd/OWNERS
@@ -3,6 +3,8 @@
agampe@google.com
calin@google.com
jsharkey@android.com
+maco@google.com
mathieuc@google.com
+narayan@google.com
ngeoffray@google.com
toddke@google.com
diff --git a/cmds/installd/TEST_MAPPING b/cmds/installd/TEST_MAPPING
index 3de5c79..287f2d9 100644
--- a/cmds/installd/TEST_MAPPING
+++ b/cmds/installd/TEST_MAPPING
@@ -14,6 +14,15 @@
},
{
"name": "installd_utils_test"
+ },
+ {
+ "name": "CtsUsesLibraryHostTestCases"
+ },
+ {
+ "name": "CtsClassloaderSplitsHostTestCases"
+ },
+ {
+ "name": "CtsCompilationTestCases"
}
]
}
diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl
index 377074a..51fe66b 100644
--- a/cmds/installd/binder/android/os/IInstalld.aidl
+++ b/cmds/installd/binder/android/os/IInstalld.aidl
@@ -106,11 +106,11 @@
@nullable @utf8InCpp String dexMetadata);
long snapshotAppData(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName,
- int userId, int storageFlags);
+ int userId, int snapshotId, int storageFlags);
void restoreAppDataSnapshot(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName,
- int appId, long ceDataInode, @utf8InCpp String seInfo, int user, int storageflags);
+ int appId, @utf8InCpp String seInfo, int user, int snapshotId, int storageflags);
void destroyAppDataSnapshot(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName,
- int userId, long ceSnapshotInode, int storageFlags);
+ int userId, long ceSnapshotInode, int snapshotId, int storageFlags);
const int FLAG_STORAGE_DE = 0x1;
const int FLAG_STORAGE_CE = 0x2;
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index 3d7feb7..7eee749 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -45,6 +45,7 @@
#include <private/android_filesystem_config.h>
#include <processgroup/sched_policy.h>
#include <selinux/android.h>
+#include <server_configurable_flags/get_flags.h>
#include <system/thread_defs.h>
#include "dexopt.h"
@@ -57,6 +58,7 @@
using android::base::EndsWith;
using android::base::GetBoolProperty;
using android::base::GetProperty;
+using android::base::ReadFdToString;
using android::base::ReadFully;
using android::base::StringPrintf;
using android::base::WriteFully;
@@ -260,6 +262,47 @@
return "";
}
+// Determines which binary we should use for execution (the debug or non-debug version).
+// e.g. dex2oatd vs dex2oat
+static const char* select_execution_binary(const char* binary, const char* debug_binary,
+ bool background_job_compile) {
+ return select_execution_binary(
+ binary,
+ debug_binary,
+ background_job_compile,
+ is_debug_runtime(),
+ (android::base::GetProperty("ro.build.version.codename", "") == "REL"),
+ is_debuggable_build());
+}
+
+// Determines which binary we should use for execution (the debug or non-debug version).
+// e.g. dex2oatd vs dex2oat
+// This is convenient method which is much easier to test because it doesn't read
+// system properties.
+const char* select_execution_binary(
+ const char* binary,
+ const char* debug_binary,
+ bool background_job_compile,
+ bool is_debug_runtime,
+ bool is_release,
+ bool is_debuggable_build) {
+ // Do not use debug binaries for release candidates (to give more soak time).
+ bool is_debug_bg_job = background_job_compile && is_debuggable_build && !is_release;
+
+ // If the runtime was requested to use libartd.so, we'll run the debug version - assuming
+ // the file is present (it may not be on images with very little space available).
+ bool useDebug = (is_debug_runtime || is_debug_bg_job) && (access(debug_binary, X_OK) == 0);
+
+ return useDebug ? debug_binary : binary;
+}
+
+// Namespace for Android Runtime flags applied during boot time.
+static const char* RUNTIME_NATIVE_BOOT_NAMESPACE = "runtime_native_boot";
+// Feature flag name for running the JIT in Zygote experiment, b/119800099.
+static const char* ENABLE_APEX_IMAGE = "enable_apex_image";
+// Location of the apex image.
+static const char* kApexImage = "/system/framework/apex.art";
+
class RunDex2Oat : public ExecVHelper {
public:
RunDex2Oat(int zip_fd,
@@ -277,6 +320,7 @@
bool background_job_compile,
int profile_fd,
const char* class_loader_context,
+ const std::string& class_loader_context_fds,
int target_sdk_version,
bool enable_hidden_api_checks,
bool generate_compact_dex,
@@ -293,6 +337,14 @@
: "dalvik.vm.boot-dex2oat-threads";
std::string dex2oat_threads_arg = MapPropertyToArg(threads_property, "-j%s");
+ std::string bootclasspath;
+ char* dex2oat_bootclasspath = getenv("DEX2OATBOOTCLASSPATH");
+ if (dex2oat_bootclasspath != nullptr) {
+ bootclasspath = StringPrintf("-Xbootclasspath:%s", dex2oat_bootclasspath);
+ }
+ // If DEX2OATBOOTCLASSPATH is not in the environment, dex2oat is going to query
+ // BOOTCLASSPATH.
+
const std::string dex2oat_isa_features_key =
StringPrintf("dalvik.vm.isa.%s.features", instruction_set);
std::string instruction_set_features_arg =
@@ -338,21 +390,23 @@
std::string dex2oat_large_app_threshold_arg =
MapPropertyToArg("dalvik.vm.dex2oat-very-large", "--very-large-app-threshold=%s");
- // If the runtime was requested to use libartd.so, we'll run dex2oatd, otherwise dex2oat.
- const char* dex2oat_bin = kDex2oatPath;
- // Do not use dex2oatd for release candidates (give dex2oat more soak time).
- bool is_release = android::base::GetProperty("ro.build.version.codename", "") == "REL";
- if (is_debug_runtime() ||
- (background_job_compile && is_debuggable_build() && !is_release)) {
- if (access(kDex2oatDebugPath, X_OK) == 0) {
- dex2oat_bin = kDex2oatDebugPath;
- }
- }
+
+ const char* dex2oat_bin = select_execution_binary(
+ kDex2oatPath, kDex2oatDebugPath, background_job_compile);
bool generate_minidebug_info = kEnableMinidebugInfo &&
GetBoolProperty(kMinidebugInfoSystemProperty, kMinidebugInfoSystemPropertyDefault);
- std::string boot_image = MapPropertyToArg("dalvik.vm.boot-image", "-Ximage:%s");
+ std::string boot_image;
+ std::string use_apex_image =
+ server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE,
+ ENABLE_APEX_IMAGE,
+ /*default_value=*/ "");
+ if (use_apex_image == "true") {
+ boot_image = StringPrintf("-Ximage:%s", kApexImage);
+ } else {
+ boot_image = MapPropertyToArg("dalvik.vm.boot-image", "-Ximage:%s");
+ }
// clang FORTIFY doesn't let us use strlen in constant array bounds, so we
// use arraysize instead.
@@ -368,12 +422,17 @@
std::string dex2oat_image_fd;
std::string target_sdk_version_arg;
if (target_sdk_version != 0) {
- StringPrintf("-Xtarget-sdk-version:%d", target_sdk_version);
+ target_sdk_version_arg = StringPrintf("-Xtarget-sdk-version:%d", target_sdk_version);
}
std::string class_loader_context_arg;
+ std::string class_loader_context_fds_arg;
if (class_loader_context != nullptr) {
class_loader_context_arg = StringPrintf("--class-loader-context=%s",
class_loader_context);
+ if (!class_loader_context_fds.empty()) {
+ class_loader_context_fds_arg = StringPrintf("--class-loader-context-fds=%s",
+ class_loader_context_fds.c_str());
+ }
}
if (swap_fd >= 0) {
@@ -440,6 +499,7 @@
AddArg(instruction_set_features_arg);
AddRuntimeArg(boot_image);
+ AddRuntimeArg(bootclasspath);
AddRuntimeArg(dex2oat_Xms_arg);
AddRuntimeArg(dex2oat_Xmx_arg);
@@ -465,15 +525,16 @@
AddArg(profile_arg);
AddArg(base_dir);
AddArg(class_loader_context_arg);
+ AddArg(class_loader_context_fds_arg);
if (generate_minidebug_info) {
AddArg(kMinidebugDex2oatFlag);
}
if (disable_cdex) {
AddArg(kDisableCompactDexFlag);
}
- AddArg(target_sdk_version_arg);
+ AddRuntimeArg(target_sdk_version_arg);
if (enable_hidden_api_checks) {
- AddRuntimeArg("-Xhidden-api-checks");
+ AddRuntimeArg("-Xhidden-api-policy:enabled");
}
if (dex_metadata_fd > -1) {
@@ -555,7 +616,7 @@
// the app uid. If we cannot do that, there's no point in returning the fd
// since dex2oat/profman will fail with SElinux denials.
if (fchown(fd.get(), uid, uid) < 0) {
- PLOG(ERROR) << "Could not chwon profile " << profile;
+ PLOG(ERROR) << "Could not chown profile " << profile;
return invalid_unique_fd();
}
return fd;
@@ -650,7 +711,12 @@
const std::vector<std::string>& dex_locations,
bool copy_and_update,
bool store_aggregation_counters) {
- const char* profman_bin = is_debug_runtime() ? kProfmanDebugPath: kProfmanPath;
+
+ // TODO(calin): Assume for now we run in the bg compile job (which is in
+ // most of the invocation). With the current data flow, is not very easy or
+ // clean to discover this in RunProfman (it will require quite a messy refactoring).
+ const char* profman_bin = select_execution_binary(
+ kProfmanPath, kProfmanDebugPath, /*background_job_compile=*/ true);
if (copy_and_update) {
CHECK_EQ(1u, profile_fds.size());
@@ -1456,17 +1522,20 @@
class RunDexoptAnalyzer : public ExecVHelper {
public:
RunDexoptAnalyzer(const std::string& dex_file,
- int vdex_fd,
- int oat_fd,
- int zip_fd,
- const std::string& instruction_set,
- const std::string& compiler_filter,
- bool profile_was_updated,
- bool downgrade,
- const char* class_loader_context) {
+ int vdex_fd,
+ int oat_fd,
+ int zip_fd,
+ const std::string& instruction_set,
+ const std::string& compiler_filter,
+ bool profile_was_updated,
+ bool downgrade,
+ const char* class_loader_context,
+ const std::string& class_loader_context_fds) {
CHECK_GE(zip_fd, 0);
- const char* dexoptanalyzer_bin =
- is_debug_runtime() ? kDexoptanalyzerDebugPath : kDexoptanalyzerPath;
+
+ // We always run the analyzer in the background job.
+ const char* dexoptanalyzer_bin = select_execution_binary(
+ kDexoptanalyzerPath, kDexoptanalyzerDebugPath, /*background_job_compile=*/ true);
std::string dex_file_arg = "--dex-file=" + dex_file;
std::string oat_fd_arg = "--oat-fd=" + std::to_string(oat_fd);
@@ -1480,6 +1549,10 @@
if (class_loader_context != nullptr) {
class_loader_context_arg += class_loader_context;
}
+ std::string class_loader_context_fds_arg = "--class-loader-context-fds=";
+ if (!class_loader_context_fds.empty()) {
+ class_loader_context_fds_arg += class_loader_context_fds;
+ }
// program name, dex file, isa, filter
AddArg(dex_file_arg);
@@ -1491,7 +1564,7 @@
if (vdex_fd >= 0) {
AddArg(vdex_fd_arg);
}
- AddArg(zip_fd_arg.c_str());
+ AddArg(zip_fd_arg);
if (profile_was_updated) {
AddArg(assume_profile_changed);
}
@@ -1499,11 +1572,28 @@
AddArg(downgrade_flag);
}
if (class_loader_context != nullptr) {
- AddArg(class_loader_context_arg.c_str());
+ AddArg(class_loader_context_arg);
+ if (!class_loader_context_fds.empty()) {
+ AddArg(class_loader_context_fds_arg);
+ }
}
PrepareArgs(dexoptanalyzer_bin);
}
+
+ // Dexoptanalyzer mode which flattens the given class loader context and
+ // prints a list of its dex files in that flattened order.
+ RunDexoptAnalyzer(const char* class_loader_context) {
+ CHECK(class_loader_context != nullptr);
+
+ // We always run the analyzer in the background job.
+ const char* dexoptanalyzer_bin = select_execution_binary(
+ kDexoptanalyzerPath, kDexoptanalyzerDebugPath, /*background_job_compile=*/ true);
+
+ AddArg("--flatten-class-loader-context");
+ AddArg(std::string("--class-loader-context=") + class_loader_context);
+ PrepareArgs(dexoptanalyzer_bin);
+ }
};
// Prepares the oat dir for the secondary dex files.
@@ -1683,6 +1773,95 @@
return true;
}
+static bool get_class_loader_context_dex_paths(const char* class_loader_context, int uid,
+ /* out */ std::vector<std::string>* context_dex_paths) {
+ if (class_loader_context == nullptr) {
+ return true;
+ }
+
+ LOG(DEBUG) << "Getting dex paths for context " << class_loader_context;
+
+ // Pipe to get the hash result back from our child process.
+ unique_fd pipe_read, pipe_write;
+ if (!Pipe(&pipe_read, &pipe_write)) {
+ PLOG(ERROR) << "Failed to create pipe";
+ return false;
+ }
+
+ pid_t pid = fork();
+ if (pid == 0) {
+ // child -- drop privileges before continuing.
+ drop_capabilities(uid);
+
+ // Route stdout to `pipe_write`
+ while ((dup2(pipe_write, STDOUT_FILENO) == -1) && (errno == EINTR)) {}
+ pipe_write.reset();
+ pipe_read.reset();
+
+ RunDexoptAnalyzer run_dexopt_analyzer(class_loader_context);
+ run_dexopt_analyzer.Exec(kSecondaryDexDexoptAnalyzerSkippedFailExec);
+ }
+
+ /* parent */
+ pipe_write.reset();
+
+ std::string str_dex_paths;
+ if (!ReadFdToString(pipe_read, &str_dex_paths)) {
+ PLOG(ERROR) << "Failed to read from pipe";
+ return false;
+ }
+ pipe_read.reset();
+
+ int return_code = wait_child(pid);
+ if (!WIFEXITED(return_code)) {
+ PLOG(ERROR) << "Error waiting for child dexoptanalyzer process";
+ return false;
+ }
+
+ constexpr int kFlattenClassLoaderContextSuccess = 50;
+ return_code = WEXITSTATUS(return_code);
+ if (return_code != kFlattenClassLoaderContextSuccess) {
+ LOG(ERROR) << "Dexoptanalyzer could not flatten class loader context, code=" << return_code;
+ return false;
+ }
+
+ if (!str_dex_paths.empty()) {
+ *context_dex_paths = android::base::Split(str_dex_paths, ":");
+ }
+ return true;
+}
+
+static int open_dex_paths(const std::vector<std::string>& dex_paths,
+ /* out */ std::vector<unique_fd>* zip_fds, /* out */ std::string* error_msg) {
+ for (const std::string& dex_path : dex_paths) {
+ zip_fds->emplace_back(open(dex_path.c_str(), O_RDONLY));
+ if (zip_fds->back().get() < 0) {
+ *error_msg = StringPrintf(
+ "installd cannot open '%s' for input during dexopt", dex_path.c_str());
+ if (errno == ENOENT) {
+ return kSecondaryDexDexoptAnalyzerSkippedNoFile;
+ } else {
+ return kSecondaryDexDexoptAnalyzerSkippedOpenZip;
+ }
+ }
+ }
+ return 0;
+}
+
+static std::string join_fds(const std::vector<unique_fd>& fds) {
+ std::stringstream ss;
+ bool is_first = true;
+ for (const unique_fd& fd : fds) {
+ if (is_first) {
+ is_first = false;
+ } else {
+ ss << ":";
+ }
+ ss << fd.get();
+ }
+ return ss.str();
+}
+
// Processes the dex_path as a secondary dex files and return true if the path dex file should
// be compiled. Returns false for errors (logged) or true if the secondary dex path was process
// successfully.
@@ -1694,7 +1873,7 @@
int dexopt_flags, const char* volume_uuid, int uid, const char* instruction_set,
const char* compiler_filter, bool* is_public_out, int* dexopt_needed_out,
std::string* oat_dir_out, bool downgrade, const char* class_loader_context,
- /* out */ std::string* error_msg) {
+ const std::vector<std::string>& context_dex_paths, /* out */ std::string* error_msg) {
LOG(DEBUG) << "Processing secondary dex path " << dex_path;
int storage_flag;
if (!validate_dexopt_storage_flags(dexopt_flags, &storage_flag, error_msg)) {
@@ -1734,6 +1913,13 @@
}
}
+ // Open class loader context dex files.
+ std::vector<unique_fd> context_zip_fds;
+ int open_dex_paths_rc = open_dex_paths(context_dex_paths, &context_zip_fds, error_msg);
+ if (open_dex_paths_rc != 0) {
+ _exit(open_dex_paths_rc);
+ }
+
// Prepare the oat directories.
if (!prepare_secondary_dex_oat_dir(dex_path, uid, instruction_set)) {
_exit(kSecondaryDexDexoptAnalyzerSkippedPrepareDir);
@@ -1765,7 +1951,8 @@
instruction_set,
compiler_filter, profile_was_updated,
downgrade,
- class_loader_context);
+ class_loader_context,
+ join_fds(context_zip_fds));
run_dexopt_analyzer.Exec(kSecondaryDexDexoptAnalyzerSkippedFailExec);
}
@@ -1853,10 +2040,16 @@
// Check if we're dealing with a secondary dex file and if we need to compile it.
std::string oat_dir_str;
+ std::vector<std::string> context_dex_paths;
if (is_secondary_dex) {
+ if (!get_class_loader_context_dex_paths(class_loader_context, uid, &context_dex_paths)) {
+ *error_msg = "Failed acquiring context dex paths";
+ return -1; // We had an error, logged in the process method.
+ }
+
if (process_secondary_dex_dexopt(dex_path, pkgname, dexopt_flags, volume_uuid, uid,
instruction_set, compiler_filter, &is_public, &dexopt_needed, &oat_dir_str,
- downgrade, class_loader_context, error_msg)) {
+ downgrade, class_loader_context, context_dex_paths, error_msg)) {
oat_dir = oat_dir_str.c_str();
if (dexopt_needed == NO_DEXOPT_NEEDED) {
return 0; // Nothing to do, report success.
@@ -1868,7 +2061,7 @@
return -1; // We had an error, logged in the process method.
}
} else {
- // Currently these flags are only use for secondary dex files.
+ // Currently these flags are only used for secondary dex files.
// Verify that they are not set for primary apks.
CHECK((dexopt_flags & DEXOPT_STORAGE_CE) == 0);
CHECK((dexopt_flags & DEXOPT_STORAGE_DE) == 0);
@@ -1882,6 +2075,13 @@
return -1;
}
+ // Open class loader context dex files.
+ std::vector<unique_fd> context_input_fds;
+ if (open_dex_paths(context_dex_paths, &context_input_fds, error_msg) != 0) {
+ LOG(ERROR) << *error_msg;
+ return -1;
+ }
+
// Create the output OAT file.
char out_oat_path[PKG_PATH_MAX];
Dex2oatFileWrapper out_oat_fd = open_oat_out_file(dex_path, oat_dir, is_public, uid,
@@ -1917,14 +2117,20 @@
// Create a swap file if necessary.
unique_fd swap_fd = maybe_open_dexopt_swap_file(out_oat_path);
- // Create the app image file if needed.
- Dex2oatFileWrapper image_fd = maybe_open_app_image(
- out_oat_path, generate_app_image, is_public, uid, is_secondary_dex);
-
// Open the reference profile if needed.
Dex2oatFileWrapper reference_profile_fd = maybe_open_reference_profile(
pkgname, dex_path, profile_name, profile_guided, is_public, uid, is_secondary_dex);
+ if (reference_profile_fd.get() == -1) {
+ // We don't create an app image without reference profile since there is no speedup from
+ // loading it in that case and instead will be a small overhead.
+ generate_app_image = false;
+ }
+
+ // Create the app image file if needed.
+ Dex2oatFileWrapper image_fd = maybe_open_app_image(
+ out_oat_path, generate_app_image, is_public, uid, is_secondary_dex);
+
unique_fd dex_metadata_fd;
if (dex_metadata_path != nullptr) {
dex_metadata_fd.reset(TEMP_FAILURE_RETRY(open(dex_metadata_path, O_RDONLY | O_NOFOLLOW)));
@@ -1950,6 +2156,7 @@
background_job_compile,
reference_profile_fd.get(),
class_loader_context,
+ join_fds(context_input_fds),
target_sdk_version,
enable_hidden_api_checks,
generate_compact_dex,
@@ -1961,11 +2168,6 @@
/* child -- drop privileges before continuing */
drop_capabilities(uid);
- // Clear BOOTCLASSPATH.
- // Let dex2oat use the BCP from boot image, excluding updatable BCP
- // modules for AOT to avoid app recompilation after their upgrades.
- unsetenv("BOOTCLASSPATH");
-
SetDex2OatScheduling(boot_complete);
if (flock(out_oat_fd.get(), LOCK_EX | LOCK_NB) != 0) {
PLOG(ERROR) << "flock(" << out_oat_path << ") failed";
@@ -2063,7 +2265,7 @@
drop_capabilities(uid);
const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str();
- if (!validate_secondary_dex_path(pkgname.c_str(), dex_path.c_str(), volume_uuid_cstr,
+ if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid_cstr,
uid, storage_flag)) {
LOG(ERROR) << "Could not validate secondary dex path " << dex_path;
_exit(kReconcileSecondaryDexValidationError);
diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h
index 5902659..a8c48c5 100644
--- a/cmds/installd/dexopt.h
+++ b/cmds/installd/dexopt.h
@@ -36,7 +36,7 @@
// Location of binaries in the Android Runtime APEX.
static constexpr const char* kDex2oatPath = ANDROID_RUNTIME_APEX_BIN "/dex2oat";
static constexpr const char* kDex2oatDebugPath = ANDROID_RUNTIME_APEX_BIN "/dex2oatd";
-static constexpr const char* kProfmanPath = ANDROID_RUNTIME_APEX_BIN "/profmand";
+static constexpr const char* kProfmanPath = ANDROID_RUNTIME_APEX_BIN "/profman";
static constexpr const char* kProfmanDebugPath = ANDROID_RUNTIME_APEX_BIN "/profmand";
static constexpr const char* kDexoptanalyzerPath = ANDROID_RUNTIME_APEX_BIN "/dexoptanalyzer";
static constexpr const char* kDexoptanalyzerDebugPath = ANDROID_RUNTIME_APEX_BIN "/dexoptanalyzerd";
@@ -128,6 +128,14 @@
bool move_ab(const char* apk_path, const char* instruction_set, const char* output_path);
+const char* select_execution_binary(
+ const char* binary,
+ const char* debug_binary,
+ bool background_job_compile,
+ bool is_debug_runtime,
+ bool is_release,
+ bool is_debuggable_build);
+
} // namespace installd
} // namespace android
diff --git a/cmds/installd/globals.cpp b/cmds/installd/globals.cpp
index b3a6daf..1394701 100644
--- a/cmds/installd/globals.cpp
+++ b/cmds/installd/globals.cpp
@@ -44,6 +44,8 @@
static constexpr const char* PRIVATE_APP_SUBDIR = "app-private/"; // sub-directory under
// ANDROID_DATA
+static constexpr const char* STAGING_SUBDIR = "app-staging/"; // sub-directory under ANDROID_DATA
+
std::string android_app_dir;
std::string android_app_ephemeral_dir;
std::string android_app_lib_dir;
@@ -54,6 +56,7 @@
std::string android_mnt_expand_dir;
std::string android_profiles_dir;
std::string android_root_dir;
+std::string android_staging_dir;
std::vector<std::string> android_system_dirs;
@@ -110,6 +113,9 @@
// Get the android profiles directory.
android_profiles_dir = android_data_dir + PROFILES_SUBDIR;
+ // Get the android session staging directory.
+ android_staging_dir = android_data_dir + STAGING_SUBDIR;
+
// Take note of the system and vendor directories.
android_system_dirs.clear();
android_system_dirs.push_back(android_root_dir + APP_SUBDIR);
diff --git a/cmds/installd/globals.h b/cmds/installd/globals.h
index 633e33b..a88a86e 100644
--- a/cmds/installd/globals.h
+++ b/cmds/installd/globals.h
@@ -38,6 +38,7 @@
extern std::string android_mnt_expand_dir;
extern std::string android_profiles_dir;
extern std::string android_root_dir;
+extern std::string android_staging_dir;
extern std::vector<std::string> android_system_dirs;
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index 670abea..2e2cc18 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -25,6 +25,7 @@
#include <android-base/logging.h>
#include <android-base/macros.h>
#include <android-base/stringprintf.h>
+#include <libdm/dm.h>
#include <selinux/android.h>
#include <apexd.h>
@@ -41,23 +42,6 @@
namespace android {
namespace installd {
-// Configuration for bind-mounted Bionic artifacts.
-
-static constexpr const char* kLinkerMountPoint = "/bionic/bin/linker";
-static constexpr const char* kRuntimeLinkerPath = "/apex/com.android.runtime/bin/linker";
-
-static constexpr const char* kBionicLibsMountPointDir = "/bionic/lib/";
-static constexpr const char* kRuntimeBionicLibsDir = "/apex/com.android.runtime/lib/bionic/";
-
-static constexpr const char* kLinkerMountPoint64 = "/bionic/bin/linker64";
-static constexpr const char* kRuntimeLinkerPath64 = "/apex/com.android.runtime/bin/linker64";
-
-static constexpr const char* kBionicLibsMountPointDir64 = "/bionic/lib64/";
-static constexpr const char* kRuntimeBionicLibsDir64 = "/apex/com.android.runtime/lib64/bionic/";
-
-static const std::vector<std::string> kBionicLibFileNames = {"libc.so", "libm.so", "libdl.so"};
-
-
static void CloseDescriptor(int fd) {
if (fd >= 0) {
int result = close(fd);
@@ -94,41 +78,35 @@
}
}
-// Copied from system/core/init/mount_namespace.cpp.
-static bool BindMount(const std::string& source, const std::string& mount_point,
- bool recursive = false) {
- unsigned long mountflags = MS_BIND;
- if (recursive) {
- mountflags |= MS_REC;
- }
- if (mount(source.c_str(), mount_point.c_str(), nullptr, mountflags, nullptr) == -1) {
- PLOG(ERROR) << "Could not bind-mount " << source << " to " << mount_point;
- return false;
- }
- return true;
-}
+static void TryExtraMount(const char* name, const char* slot, const char* target) {
+ std::string partition_name = StringPrintf("%s%s", name, slot);
-// Copied from system/core/init/mount_namespace.cpp and and adjusted (bind
-// mounts are not made private, as the /postinstall is already private (see
-// `android::installd::otapreopt_chroot`).
-static bool BindMountBionic(const std::string& linker_source, const std::string& lib_dir_source,
- const std::string& linker_mount_point,
- const std::string& lib_mount_dir) {
- if (access(linker_source.c_str(), F_OK) != 0) {
- PLOG(INFO) << linker_source << " does not exist. Skipping mounting Bionic there.";
- return true;
- }
- if (!BindMount(linker_source, linker_mount_point)) {
- return false;
- }
- for (const auto& libname : kBionicLibFileNames) {
- std::string mount_point = lib_mount_dir + libname;
- std::string source = lib_dir_source + libname;
- if (!BindMount(source, mount_point)) {
- return false;
+ // See whether update_engine mounted a logical partition.
+ {
+ auto& dm = dm::DeviceMapper::Instance();
+ if (dm.GetState(partition_name) != dm::DmDeviceState::INVALID) {
+ std::string path;
+ if (dm.GetDmDevicePathByName(partition_name, &path)) {
+ int mount_result = mount(path.c_str(),
+ target,
+ "ext4",
+ MS_RDONLY,
+ /* data */ nullptr);
+ if (mount_result == 0) {
+ return;
+ }
+ }
}
}
- return true;
+
+ // Fall back and attempt a direct mount.
+ std::string block_device = StringPrintf("/dev/block/by-name/%s", partition_name.c_str());
+ int mount_result = mount(block_device.c_str(),
+ target,
+ "ext4",
+ MS_RDONLY,
+ /* data */ nullptr);
+ UNUSED(mount_result);
}
// Entry for otapreopt_chroot. Expected parameters are:
@@ -191,29 +169,11 @@
LOG(ERROR) << "Target slot suffix not legal: " << arg[2];
exit(207);
}
- {
- std::string vendor_partition = StringPrintf("/dev/block/by-name/vendor%s",
- arg[2]);
- int vendor_result = mount(vendor_partition.c_str(),
- "/postinstall/vendor",
- "ext4",
- MS_RDONLY,
- /* data */ nullptr);
- UNUSED(vendor_result);
- }
+ TryExtraMount("vendor", arg[2], "/postinstall/vendor");
// Try to mount the product partition. update_engine doesn't do this for us, but we
// want it for product APKs. Same notes as vendor above.
- {
- std::string product_partition = StringPrintf("/dev/block/by-name/product%s",
- arg[2]);
- int product_result = mount(product_partition.c_str(),
- "/postinstall/product",
- "ext4",
- MS_RDONLY,
- /* data */ nullptr);
- UNUSED(product_result);
- }
+ TryExtraMount("product", arg[2], "/postinstall/product");
// Setup APEX mount point and its security context.
static constexpr const char* kPostinstallApexDir = "/postinstall/apex";
@@ -274,23 +234,6 @@
// the Android Runtime APEX, as it is required by otapreopt to run dex2oat.
std::vector<apex::ApexFile> active_packages = ActivateApexPackages();
- // Bind-mount Bionic artifacts from the Runtime APEX.
- // This logic is copied and adapted from system/core/init/mount_namespace.cpp.
- if (!BindMountBionic(kRuntimeLinkerPath, kRuntimeBionicLibsDir, kLinkerMountPoint,
- kBionicLibsMountPointDir)) {
- LOG(ERROR) << "Failed to mount 32-bit Bionic artifacts from the Runtime APEX.";
- // Clean up and exit.
- DeactivateApexPackages(active_packages);
- exit(215);
- }
- if (!BindMountBionic(kRuntimeLinkerPath64, kRuntimeBionicLibsDir64, kLinkerMountPoint64,
- kBionicLibsMountPointDir64)) {
- LOG(ERROR) << "Failed to mount 64-bit Bionic artifacts from the Runtime APEX.";
- // Clean up and exit.
- DeactivateApexPackages(active_packages);
- exit(216);
- }
-
// Now go on and run otapreopt.
// Incoming: cmd + status-fd + target-slot + cmd... | Incoming | = argc
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp
index 9c9db0f..bd45005 100644
--- a/cmds/installd/tests/Android.bp
+++ b/cmds/installd/tests/Android.bp
@@ -15,6 +15,7 @@
"libinstalld",
"liblog",
],
+ test_config: "installd_utils_test.xml",
}
cc_test {
@@ -31,6 +32,7 @@
"libprocessgroup",
"libselinux",
"libutils",
+ "server_configurable_flags",
],
static_libs: [
"libdiskusage",
@@ -38,6 +40,7 @@
"liblog",
"liblogwrap",
],
+ test_config: "installd_cache_test.xml",
}
cc_test {
@@ -54,6 +57,7 @@
"libprocessgroup",
"libselinux",
"libutils",
+ "server_configurable_flags",
],
static_libs: [
"libdiskusage",
@@ -61,6 +65,7 @@
"liblog",
"liblogwrap",
],
+ test_config: "installd_service_test.xml",
}
cc_test {
@@ -77,13 +82,17 @@
"libprocessgroup",
"libselinux",
"libutils",
+ "server_configurable_flags",
],
static_libs: [
"libdiskusage",
"libinstalld",
"liblog",
"liblogwrap",
+ "libziparchive",
+ "libz",
],
+ test_config: "installd_dexopt_test.xml",
}
cc_test {
@@ -96,6 +105,7 @@
"libbase",
"libcutils",
"libutils",
+ "server_configurable_flags",
],
static_libs: [
"liblog",
diff --git a/cmds/installd/tests/installd_cache_test.xml b/cmds/installd/tests/installd_cache_test.xml
new file mode 100644
index 0000000..97af514
--- /dev/null
+++ b/cmds/installd/tests/installd_cache_test.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+<!-- Note: this is derived from the autogenerated configuration. We require
+ root support. -->
+<configuration description="Runs installd_cache_test.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="installd_cache_test->/data/local/tmp/installd_cache_test" />
+ </target_preparer>
+
+ <!-- The test requires root for file access. -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="installd_cache_test" />
+ </test>
+</configuration>
diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp
index 78edce0..13fd067 100644
--- a/cmds/installd/tests/installd_dexopt_test.cpp
+++ b/cmds/installd/tests/installd_dexopt_test.cpp
@@ -41,6 +41,7 @@
#include "globals.h"
#include "tests/test_utils.h"
#include "utils.h"
+#include "ziparchive/zip_writer.h"
using android::base::ReadFully;
using android::base::unique_fd;
@@ -195,6 +196,7 @@
std::unique_ptr<std::string> volume_uuid_;
std::string package_name_;
std::string apk_path_;
+ std::string empty_dm_file_;
std::string app_apk_dir_;
std::string app_private_dir_ce_;
std::string app_private_dir_de_;
@@ -260,6 +262,26 @@
<< " : " << error_msg;
}
+ // Create an empty dm file.
+ empty_dm_file_ = apk_path_ + ".dm";
+ {
+ int fd = open(empty_dm_file_.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
+ if (fd < 0) {
+ return ::testing::AssertionFailure() << "Could not open " << empty_dm_file_;
+ }
+ FILE* file = fdopen(fd, "wb");
+ if (file == nullptr) {
+ return ::testing::AssertionFailure() << "Null file for " << empty_dm_file_
+ << " fd=" << fd;
+ }
+ ZipWriter writer(file);
+ // Add vdex to zip.
+ writer.StartEntry("primary.prof", ZipWriter::kCompress);
+ writer.FinishEntry();
+ writer.Finish();
+ close(fd);
+ }
+
// Create the app user data.
status = service_->createAppData(
volume_uuid_,
@@ -327,16 +349,21 @@
void CompileSecondaryDex(const std::string& path, int32_t dex_storage_flag,
bool should_binder_call_succeed, bool should_dex_be_compiled = true,
- /*out */ binder::Status* binder_result = nullptr, int32_t uid = -1) {
+ /*out */ binder::Status* binder_result = nullptr, int32_t uid = -1,
+ const char* class_loader_context = nullptr) {
if (uid == -1) {
uid = kTestAppUid;
}
+ if (class_loader_context == nullptr) {
+ class_loader_context = "&";
+ }
std::unique_ptr<std::string> package_name_ptr(new std::string(package_name_));
int32_t dexopt_needed = 0; // does not matter;
std::unique_ptr<std::string> out_path = nullptr; // does not matter
int32_t dex_flags = DEXOPT_SECONDARY_DEX | dex_storage_flag;
std::string compiler_filter = "speed-profile";
- std::unique_ptr<std::string> class_loader_context_ptr(new std::string("&"));
+ std::unique_ptr<std::string> class_loader_context_ptr(
+ new std::string(class_loader_context));
std::unique_ptr<std::string> se_info_ptr(new std::string(se_info_));
bool downgrade = false;
int32_t target_sdk_version = 0; // default
@@ -474,7 +501,7 @@
bool prof_result;
ASSERT_BINDER_SUCCESS(service_->prepareAppProfile(
package_name_, kTestUserId, kTestAppId, *profile_name_ptr, apk_path_,
- /*dex_metadata*/ nullptr, &prof_result));
+ dm_path_ptr, &prof_result));
ASSERT_TRUE(prof_result);
binder::Status result = service_->dexopt(apk_path_,
@@ -555,12 +582,26 @@
/*binder_ok*/ true, /*compile_ok*/ true);
}
+TEST_F(DexoptTest, DexoptSecondaryCeWithContext) {
+ LOG(INFO) << "DexoptSecondaryCeWithContext";
+ std::string class_loader_context = "PCL[" + secondary_dex_ce_ + "]";
+ CompileSecondaryDex(secondary_dex_ce_, DEXOPT_STORAGE_CE,
+ /*binder_ok*/ true, /*compile_ok*/ true, nullptr, -1, class_loader_context.c_str());
+}
+
TEST_F(DexoptTest, DexoptSecondaryDe) {
LOG(INFO) << "DexoptSecondaryDe";
CompileSecondaryDex(secondary_dex_de_, DEXOPT_STORAGE_DE,
/*binder_ok*/ true, /*compile_ok*/ true);
}
+TEST_F(DexoptTest, DexoptSecondaryDeWithContext) {
+ LOG(INFO) << "DexoptSecondaryDeWithContext";
+ std::string class_loader_context = "PCL[" + secondary_dex_de_ + "]";
+ CompileSecondaryDex(secondary_dex_de_, DEXOPT_STORAGE_DE,
+ /*binder_ok*/ true, /*compile_ok*/ true, nullptr, -1, class_loader_context.c_str());
+}
+
TEST_F(DexoptTest, DexoptSecondaryDoesNotExist) {
LOG(INFO) << "DexoptSecondaryDoesNotExist";
// If the file validates but does not exist we do not treat it as an error.
@@ -576,7 +617,7 @@
CompileSecondaryDex(secondary_dex_ce_, DEXOPT_STORAGE_DE,
/*binder_ok*/ false, /*compile_ok*/ false, &status);
EXPECT_STREQ(status.toString8().c_str(),
- "Status(-8): '-1: Dexoptanalyzer path validation failed'");
+ "Status(-8, EX_SERVICE_SPECIFIC): '-1: Dexoptanalyzer path validation failed'");
}
TEST_F(DexoptTest, DexoptSecondaryAppOwnershipValidationError) {
@@ -585,7 +626,7 @@
CompileSecondaryDex("/data/data/random.app/secondary.jar", DEXOPT_STORAGE_CE,
/*binder_ok*/ false, /*compile_ok*/ false, &status);
EXPECT_STREQ(status.toString8().c_str(),
- "Status(-8): '-1: Dexoptanalyzer path validation failed'");
+ "Status(-8, EX_SERVICE_SPECIFIC): '-1: Dexoptanalyzer path validation failed'");
}
TEST_F(DexoptTest, DexoptSecondaryAcessViaDifferentUidError) {
@@ -593,7 +634,8 @@
binder::Status status;
CompileSecondaryDex(secondary_dex_ce_, DEXOPT_STORAGE_CE,
/*binder_ok*/ false, /*compile_ok*/ false, &status, kSystemUid);
- EXPECT_STREQ(status.toString8().c_str(), "Status(-8): '-1: Dexoptanalyzer open zip failed'");
+ EXPECT_STREQ(status.toString8().c_str(),
+ "Status(-8, EX_SERVICE_SPECIFIC): '-1: Dexoptanalyzer open zip failed'");
}
TEST_F(DexoptTest, DexoptPrimaryPublic) {
@@ -615,7 +657,7 @@
DEX2OAT_FROM_SCRATCH,
&status);
EXPECT_STREQ(status.toString8().c_str(),
- "Status(-8): \'256: Dex2oat invocation for "
+ "Status(-8, EX_SERVICE_SPECIFIC): \'256: Dex2oat invocation for "
"/data/app/com.installd.test.dexopt/base.jar failed: unspecified dex2oat error'");
}
@@ -625,7 +667,9 @@
DEXOPT_BOOTCOMPLETE | DEXOPT_PROFILE_GUIDED | DEXOPT_GENERATE_APP_IMAGE,
app_oat_dir_.c_str(),
kTestAppGid,
- DEX2OAT_FROM_SCRATCH);
+ DEX2OAT_FROM_SCRATCH,
+ /*binder_result=*/nullptr,
+ empty_dm_file_.c_str());
}
TEST_F(DexoptTest, DexoptPrimaryProfilePublic) {
@@ -635,7 +679,9 @@
DEXOPT_GENERATE_APP_IMAGE,
app_oat_dir_.c_str(),
kTestAppGid,
- DEX2OAT_FROM_SCRATCH);
+ DEX2OAT_FROM_SCRATCH,
+ /*binder_result=*/nullptr,
+ empty_dm_file_.c_str());
}
TEST_F(DexoptTest, DexoptPrimaryBackgroundOk) {
@@ -645,7 +691,9 @@
DEXOPT_GENERATE_APP_IMAGE,
app_oat_dir_.c_str(),
kTestAppGid,
- DEX2OAT_FROM_SCRATCH);
+ DEX2OAT_FROM_SCRATCH,
+ /*binder_result=*/nullptr,
+ empty_dm_file_.c_str());
}
TEST_F(DexoptTest, ResolveStartupConstStrings) {
@@ -664,7 +712,9 @@
DEXOPT_GENERATE_APP_IMAGE,
app_oat_dir_.c_str(),
kTestAppGid,
- DEX2OAT_FROM_SCRATCH);
+ DEX2OAT_FROM_SCRATCH,
+ /*binder_result=*/nullptr,
+ empty_dm_file_.c_str());
run_cmd_and_process_output(
"oatdump --header-only --oat-file=" + odex,
[&](const std::string& line) {
@@ -681,7 +731,9 @@
DEXOPT_GENERATE_APP_IMAGE,
app_oat_dir_.c_str(),
kTestAppGid,
- DEX2OAT_FROM_SCRATCH);
+ DEX2OAT_FROM_SCRATCH,
+ /*binder_result=*/nullptr,
+ empty_dm_file_.c_str());
run_cmd_and_process_output(
"oatdump --header-only --oat-file=" + odex,
[&](const std::string& line) {
@@ -916,8 +968,7 @@
protected:
void TransitionToSystemServer() {
ASSERT_TRUE(DropCapabilities(kSystemUid, kSystemGid));
- int32_t res = selinux_android_setcontext(
- kSystemUid, true, se_info_.c_str(), "system_server");
+ int32_t res = selinux_android_setcon("u:r:system_server:s0");
ASSERT_EQ(0, res) << "Failed to setcon " << strerror(errno);
}
@@ -1175,5 +1226,64 @@
ASSERT_TRUE(std::find(profiles.begin(), profiles.end(), ref_prof) != profiles.end());
}
+TEST_F(DexoptTest, select_execution_binary) {
+ LOG(INFO) << "DexoptTestselect_execution_binary";
+
+ std::string release_str = app_private_dir_ce_ + "/release";
+ std::string debug_str = app_private_dir_ce_ + "/debug";
+
+ // Setup the binaries. Note that we only need executable files to actually
+ // test the execution binary selection
+ run_cmd("touch " + release_str);
+ run_cmd("touch " + debug_str);
+ run_cmd("chmod 777 " + release_str);
+ run_cmd("chmod 777 " + debug_str);
+
+ const char* release = release_str.c_str();
+ const char* debug = debug_str.c_str();
+
+ ASSERT_STREQ(release, select_execution_binary(
+ release,
+ debug,
+ /*background_job_compile=*/ false,
+ /*is_debug_runtime=*/ false,
+ /*is_release=*/ false,
+ /*is_debuggable_build=*/ false));
+
+ ASSERT_STREQ(release, select_execution_binary(
+ release,
+ debug,
+ /*background_job_compile=*/ true,
+ /*is_debug_runtime=*/ false,
+ /*is_release=*/ true,
+ /*is_debuggable_build=*/ true));
+
+ ASSERT_STREQ(debug, select_execution_binary(
+ release,
+ debug,
+ /*background_job_compile=*/ false,
+ /*is_debug_runtime=*/ true,
+ /*is_release=*/ false,
+ /*is_debuggable_build=*/ false));
+
+ ASSERT_STREQ(debug, select_execution_binary(
+ release,
+ debug,
+ /*background_job_compile=*/ true,
+ /*is_debug_runtime=*/ false,
+ /*is_release=*/ false,
+ /*is_debuggable_build=*/ true));
+
+
+ // Select the release when the debug file is not there.
+ ASSERT_STREQ(release, select_execution_binary(
+ release,
+ "does_not_exist",
+ /*background_job_compile=*/ false,
+ /*is_debug_runtime=*/ true,
+ /*is_release=*/ false,
+ /*is_debuggable_build=*/ false));
+}
+
} // namespace installd
} // namespace android
diff --git a/cmds/installd/tests/installd_dexopt_test.xml b/cmds/installd/tests/installd_dexopt_test.xml
new file mode 100644
index 0000000..24526cc
--- /dev/null
+++ b/cmds/installd/tests/installd_dexopt_test.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+<!-- Note: this is derived from the autogenerated configuration. We require
+ root support. -->
+<configuration description="Runs installd_dexopt_test.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="installd_dexopt_test->/data/local/tmp/installd_dexopt_test" />
+ </target_preparer>
+
+ <!-- The test runs as root to prepare the temporary directory, make selinux adjustments
+ and so on. -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="installd_dexopt_test" />
+ </test>
+</configuration>
diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp
index ed1a0f4..cd63931 100644
--- a/cmds/installd/tests/installd_service_test.cpp
+++ b/cmds/installd/tests/installd_service_test.cpp
@@ -259,32 +259,59 @@
return false;
}
- return (::mkdir(path.c_str(), mode) != -1);
+ if (::mkdir(path.c_str(), mode) != 0) {
+ PLOG(DEBUG) << "Failed to create folder " << path;
+ return false;
+ }
+ return true;
}
-TEST_F(ServiceTest, CreateAppDataSnapshot) {
- auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0);
- auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0);
+class AppDataSnapshotTest : public testing::Test {
+private:
+ std::string rollback_ce_base_dir;
+ std::string rollback_de_base_dir;
- ASSERT_TRUE(mkdirs(rollback_ce_dir, 700));
- ASSERT_TRUE(mkdirs(rollback_de_dir, 700));
+protected:
+ InstalldNativeService* service;
- auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo");
- auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo");
+ std::string fake_package_ce_path;
+ std::string fake_package_de_path;
- ASSERT_TRUE(mkdirs(fake_package_ce_path, 700));
- ASSERT_TRUE(mkdirs(fake_package_de_path, 700));
+ virtual void SetUp() {
+ setenv("ANDROID_LOG_TAGS", "*:v", 1);
+ android::base::InitLogging(nullptr);
- auto deleter = [&rollback_ce_dir, &rollback_de_dir,
- &fake_package_ce_path, &fake_package_de_path]() {
- delete_dir_contents(rollback_ce_dir, true);
- delete_dir_contents(rollback_de_dir, true);
- delete_dir_contents(fake_package_ce_path, true);
- delete_dir_contents(fake_package_de_path, true);
- rmdir(rollback_ce_dir.c_str());
- rmdir(rollback_de_dir.c_str());
- };
- auto scope_guard = android::base::make_scope_guard(deleter);
+ service = new InstalldNativeService();
+ ASSERT_TRUE(mkdirs("/data/local/tmp/user/0", 0700));
+
+ init_globals_from_data_and_root();
+
+ rollback_ce_base_dir = create_data_misc_ce_rollback_base_path("TEST", 0);
+ rollback_de_base_dir = create_data_misc_de_rollback_base_path("TEST", 0);
+
+ fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo");
+ fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo");
+
+ ASSERT_TRUE(mkdirs(rollback_ce_base_dir, 0700));
+ ASSERT_TRUE(mkdirs(rollback_de_base_dir, 0700));
+ ASSERT_TRUE(mkdirs(fake_package_ce_path, 0700));
+ ASSERT_TRUE(mkdirs(fake_package_de_path, 0700));
+ }
+
+ virtual void TearDown() {
+ ASSERT_EQ(0, delete_dir_contents_and_dir(rollback_ce_base_dir, true));
+ ASSERT_EQ(0, delete_dir_contents_and_dir(rollback_de_base_dir, true));
+ ASSERT_EQ(0, delete_dir_contents(fake_package_ce_path, true));
+ ASSERT_EQ(0, delete_dir_contents(fake_package_de_path, true));
+
+ delete service;
+ ASSERT_EQ(0, delete_dir_contents_and_dir("/data/local/tmp/user/0", true));
+ }
+};
+
+TEST_F(AppDataSnapshotTest, CreateAppDataSnapshot) {
+ auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 37);
+ auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 37);
ASSERT_TRUE(android::base::WriteStringToFile(
"TEST_CONTENT_CE", fake_package_ce_path + "/file1",
@@ -296,7 +323,7 @@
// Request a snapshot of the CE content but not the DE content.
int64_t ce_snapshot_inode;
ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
- "com.foo", 0, FLAG_STORAGE_CE, &ce_snapshot_inode));
+ "com.foo", 0, 37, FLAG_STORAGE_CE, &ce_snapshot_inode));
struct stat buf;
memset(&buf, 0, sizeof(buf));
ASSERT_EQ(0, stat((rollback_ce_dir + "/com.foo").c_str(), &buf));
@@ -318,7 +345,7 @@
// Request a snapshot of the DE content but not the CE content.
ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
- "com.foo", 0, FLAG_STORAGE_DE, &ce_snapshot_inode));
+ "com.foo", 0, 37, FLAG_STORAGE_DE, &ce_snapshot_inode));
// Only DE content snapshot was requested.
ASSERT_EQ(ce_snapshot_inode, 0);
@@ -339,7 +366,7 @@
// Request a snapshot of both the CE as well as the DE content.
ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
- "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr));
+ "com.foo", 0, 37, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr));
ASSERT_TRUE(android::base::ReadFileToString(
rollback_ce_dir + "/com.foo/file1", &ce_content, false /* follow_symlinks */));
@@ -349,27 +376,73 @@
ASSERT_EQ("TEST_CONTENT_DE_MODIFIED", de_content);
}
-TEST_F(ServiceTest, CreateAppDataSnapshot_AppDataAbsent) {
- auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0);
- auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0);
+TEST_F(AppDataSnapshotTest, CreateAppDataSnapshot_TwoSnapshotsWithTheSameId) {
+ auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 67);
+ auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 67);
- ASSERT_TRUE(mkdirs(rollback_ce_dir, 700));
- ASSERT_TRUE(mkdirs(rollback_de_dir, 700));
+ auto another_fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.bar");
+ auto another_fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.bar");
- auto deleter = [&rollback_ce_dir, &rollback_de_dir]() {
- delete_dir_contents(rollback_ce_dir, true);
- delete_dir_contents(rollback_de_dir, true);
- rmdir(rollback_ce_dir.c_str());
- rmdir(rollback_de_dir.c_str());
+ // Since this test sets up data for another package, some bookkeeping is required.
+ auto deleter = [&]() {
+ ASSERT_EQ(0, delete_dir_contents_and_dir(another_fake_package_ce_path, true));
+ ASSERT_EQ(0, delete_dir_contents_and_dir(another_fake_package_de_path, true));
};
-
auto scope_guard = android::base::make_scope_guard(deleter);
+ ASSERT_TRUE(mkdirs(another_fake_package_ce_path, 0700));
+ ASSERT_TRUE(mkdirs(another_fake_package_de_path, 0700));
+
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ "TEST_CONTENT_CE", fake_package_ce_path + "/file1",
+ 0700, 10000, 20000, false /* follow_symlinks */));
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ "TEST_CONTENT_DE", fake_package_de_path + "/file1",
+ 0700, 10000, 20000, false /* follow_symlinks */));
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ "ANOTHER_TEST_CONTENT_CE", another_fake_package_ce_path + "/file1",
+ 0700, 10000, 20000, false /* follow_symlinks */));
+ ASSERT_TRUE(android::base::WriteStringToFile(
+ "ANOTHER_TEST_CONTENT_DE", another_fake_package_de_path + "/file1",
+ 0700, 10000, 20000, false /* follow_symlinks */));
+
+ // Request snapshot for the package com.foo.
+ ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ "com.foo", 0, 67, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr));
+ // Now request snapshot with the same id for the package com.bar
+ ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
+ "com.bar", 0, 67, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr));
+
+ // Check that both snapshots have correct data in them.
+ std::string com_foo_ce_content, com_foo_de_content;
+ std::string com_bar_ce_content, com_bar_de_content;
+ ASSERT_TRUE(android::base::ReadFileToString(
+ rollback_ce_dir + "/com.foo/file1", &com_foo_ce_content, false /* follow_symlinks */));
+ ASSERT_TRUE(android::base::ReadFileToString(
+ rollback_de_dir + "/com.foo/file1", &com_foo_de_content, false /* follow_symlinks */));
+ ASSERT_TRUE(android::base::ReadFileToString(
+ rollback_ce_dir + "/com.bar/file1", &com_bar_ce_content, false /* follow_symlinks */));
+ ASSERT_TRUE(android::base::ReadFileToString(
+ rollback_de_dir + "/com.bar/file1", &com_bar_de_content, false /* follow_symlinks */));
+ ASSERT_EQ("TEST_CONTENT_CE", com_foo_ce_content);
+ ASSERT_EQ("TEST_CONTENT_DE", com_foo_de_content);
+ ASSERT_EQ("ANOTHER_TEST_CONTENT_CE", com_bar_ce_content);
+ ASSERT_EQ("ANOTHER_TEST_CONTENT_DE", com_bar_de_content);
+}
+
+TEST_F(AppDataSnapshotTest, CreateAppDataSnapshot_AppDataAbsent) {
+ auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 73);
+ auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 73);
+
+ // Similuating app data absence.
+ ASSERT_EQ(0, delete_dir_contents_and_dir(fake_package_ce_path, true));
+ ASSERT_EQ(0, delete_dir_contents_and_dir(fake_package_de_path, true));
+
int64_t ce_snapshot_inode;
ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
- "com.foo", 0, FLAG_STORAGE_CE, &ce_snapshot_inode));
+ "com.foo", 0, 73, FLAG_STORAGE_CE, &ce_snapshot_inode));
ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
- "com.foo", 0, FLAG_STORAGE_DE, nullptr));
+ "com.foo", 0, 73, FLAG_STORAGE_DE, nullptr));
// No CE content snapshot was performed.
ASSERT_EQ(ce_snapshot_inode, 0);
@@ -380,29 +453,12 @@
ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo").c_str(), &sb));
}
-TEST_F(ServiceTest, CreateAppDataSnapshot_ClearsExistingSnapshot) {
- auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0);
- auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0);
+TEST_F(AppDataSnapshotTest, CreateAppDataSnapshot_ClearsExistingSnapshot) {
+ auto rollback_ce_dir = create_data_misc_ce_rollback_package_path("TEST", 0, 13, "com.foo");
+ auto rollback_de_dir = create_data_misc_de_rollback_package_path("TEST", 0, 13, "com.foo");
- ASSERT_TRUE(mkdirs(rollback_ce_dir, 700));
- ASSERT_TRUE(mkdirs(rollback_de_dir, 700));
-
- auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo");
- auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo");
-
- ASSERT_TRUE(mkdirs(fake_package_ce_path, 700));
- ASSERT_TRUE(mkdirs(fake_package_de_path, 700));
-
- auto deleter = [&rollback_ce_dir, &rollback_de_dir,
- &fake_package_ce_path, &fake_package_de_path]() {
- delete_dir_contents(rollback_ce_dir, true);
- delete_dir_contents(rollback_de_dir, true);
- delete_dir_contents(fake_package_ce_path, true);
- delete_dir_contents(fake_package_de_path, true);
- rmdir(rollback_ce_dir.c_str());
- rmdir(rollback_de_dir.c_str());
- };
- auto scope_guard = android::base::make_scope_guard(deleter);
+ ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700));
+ ASSERT_TRUE(mkdirs(rollback_de_dir, 0700));
// Simulate presence of an existing snapshot
ASSERT_TRUE(android::base::WriteStringToFile(
@@ -421,62 +477,40 @@
0700, 10000, 20000, false /* follow_symlinks */));
ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
- "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr));
+ "com.foo", 0, 13, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr));
// Previous snapshot (with data for file1) must be cleared.
struct stat sb;
- ASSERT_EQ(-1, stat((rollback_ce_dir + "/com.foo/file1").c_str(), &sb));
- ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo/file1").c_str(), &sb));
+ ASSERT_EQ(-1, stat((rollback_ce_dir + "/file1").c_str(), &sb));
+ ASSERT_EQ(-1, stat((rollback_de_dir + "/file1").c_str(), &sb));
+ // New snapshot (with data for file2) must be present.
+ ASSERT_NE(-1, stat((rollback_ce_dir + "/file2").c_str(), &sb));
+ ASSERT_NE(-1, stat((rollback_de_dir + "/file2").c_str(), &sb));
}
-TEST_F(ServiceTest, SnapshotAppData_WrongVolumeUuid) {
- // Setup app data to make sure that fails due to wrong volumeUuid being
+TEST_F(AppDataSnapshotTest, SnapshotAppData_WrongVolumeUuid) {
+ // Setup rollback folders to make sure that fails due to wrong volumeUuid being
// passed, not because of some other reason.
- auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0);
- auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0);
+ auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 17);
+ auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 17);
- ASSERT_TRUE(mkdirs(rollback_ce_dir, 700));
- ASSERT_TRUE(mkdirs(rollback_de_dir, 700));
-
- auto deleter = [&rollback_ce_dir, &rollback_de_dir]() {
- delete_dir_contents(rollback_ce_dir, true);
- delete_dir_contents(rollback_de_dir, true);
- rmdir(rollback_ce_dir.c_str());
- rmdir(rollback_de_dir.c_str());
- };
- auto scope_guard = android::base::make_scope_guard(deleter);
+ ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700));
+ ASSERT_TRUE(mkdirs(rollback_de_dir, 0700));
EXPECT_BINDER_FAIL(service->snapshotAppData(std::make_unique<std::string>("FOO"),
- "com.foo", 0, FLAG_STORAGE_DE, nullptr));
+ "com.foo", 0, 17, FLAG_STORAGE_DE, nullptr));
}
-TEST_F(ServiceTest, CreateAppDataSnapshot_ClearsCache) {
- auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo");
- auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo");
+TEST_F(AppDataSnapshotTest, CreateAppDataSnapshot_ClearsCache) {
auto fake_package_ce_cache_path = fake_package_ce_path + "/cache";
auto fake_package_ce_code_cache_path = fake_package_ce_path + "/code_cache";
auto fake_package_de_cache_path = fake_package_de_path + "/cache";
auto fake_package_de_code_cache_path = fake_package_de_path + "/code_cache";
- auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0);
- auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0);
- ASSERT_TRUE(mkdirs(fake_package_ce_path, 700));
- ASSERT_TRUE(mkdirs(fake_package_de_path, 700));
- ASSERT_TRUE(mkdirs(fake_package_ce_cache_path, 700));
- ASSERT_TRUE(mkdirs(fake_package_ce_code_cache_path, 700));
- ASSERT_TRUE(mkdirs(fake_package_de_cache_path, 700));
- ASSERT_TRUE(mkdirs(fake_package_de_code_cache_path, 700));
- ASSERT_TRUE(mkdirs(rollback_ce_dir, 700));
- ASSERT_TRUE(mkdirs(rollback_de_dir, 700));
-
- auto deleter = [&fake_package_ce_path, &fake_package_de_path,
- &rollback_ce_dir, &rollback_de_dir]() {
- delete_dir_contents(fake_package_ce_path, true);
- delete_dir_contents(fake_package_de_path, true);
- delete_dir_contents_and_dir(rollback_ce_dir, true);
- delete_dir_contents_and_dir(rollback_de_dir, true);
- };
- auto scope_guard = android::base::make_scope_guard(deleter);
+ ASSERT_TRUE(mkdirs(fake_package_ce_cache_path, 0700));
+ ASSERT_TRUE(mkdirs(fake_package_ce_code_cache_path, 0700));
+ ASSERT_TRUE(mkdirs(fake_package_de_cache_path, 0700));
+ ASSERT_TRUE(mkdirs(fake_package_de_code_cache_path, 0700));
ASSERT_TRUE(android::base::WriteStringToFile(
"TEST_CONTENT_CE", fake_package_ce_cache_path + "/file1",
@@ -491,7 +525,7 @@
"TEST_CONTENT_DE", fake_package_de_code_cache_path + "/file1",
0700, 10000, 20000, false /* follow_symlinks */));
ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"),
- "com.foo", 0, FLAG_STORAGE_CE | FLAG_STORAGE_DE, nullptr));
+ "com.foo", 0, 23, FLAG_STORAGE_CE | FLAG_STORAGE_DE, nullptr));
// The snapshot call must clear cache.
struct stat sb;
ASSERT_EQ(-1, stat((fake_package_ce_cache_path + "/file1").c_str(), &sb));
@@ -500,34 +534,17 @@
ASSERT_EQ(-1, stat((fake_package_de_code_cache_path + "/file1").c_str(), &sb));
}
-TEST_F(ServiceTest, RestoreAppDataSnapshot) {
- auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0);
- auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0);
+TEST_F(AppDataSnapshotTest, RestoreAppDataSnapshot) {
+ auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 239);
+ auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 239);
- ASSERT_TRUE(mkdirs(rollback_ce_dir, 700));
- ASSERT_TRUE(mkdirs(rollback_de_dir, 700));
-
- auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo");
- auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo");
-
- ASSERT_TRUE(mkdirs(fake_package_ce_path, 700));
- ASSERT_TRUE(mkdirs(fake_package_de_path, 700));
-
- auto deleter = [&rollback_ce_dir, &rollback_de_dir,
- &fake_package_ce_path, &fake_package_de_path]() {
- delete_dir_contents(rollback_ce_dir, true);
- delete_dir_contents(rollback_de_dir, true);
- delete_dir_contents(fake_package_ce_path, true);
- delete_dir_contents(fake_package_de_path, true);
- rmdir(rollback_ce_dir.c_str());
- rmdir(rollback_de_dir.c_str());
- };
- auto scope_guard = android::base::make_scope_guard(deleter);
+ ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700));
+ ASSERT_TRUE(mkdirs(rollback_de_dir, 0700));
// Write contents to the rollback location. We'll write the same files to the
// app data location and make sure the restore has overwritten them.
- ASSERT_TRUE(mkdirs(rollback_ce_dir + "/com.foo/", 700));
- ASSERT_TRUE(mkdirs(rollback_de_dir + "/com.foo/", 700));
+ ASSERT_TRUE(mkdirs(rollback_ce_dir + "/com.foo/", 0700));
+ ASSERT_TRUE(mkdirs(rollback_de_dir + "/com.foo/", 0700));
ASSERT_TRUE(android::base::WriteStringToFile(
"CE_RESTORE_CONTENT", rollback_ce_dir + "/com.foo/file1",
0700, 10000, 20000, false /* follow_symlinks */));
@@ -542,7 +559,7 @@
0700, 10000, 20000, false /* follow_symlinks */));
ASSERT_BINDER_SUCCESS(service->restoreAppDataSnapshot(std::make_unique<std::string>("TEST"),
- "com.foo", 10000, -1, "", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE));
+ "com.foo", 10000, "", 0, 239, FLAG_STORAGE_DE | FLAG_STORAGE_CE));
std::string ce_content, de_content;
ASSERT_TRUE(android::base::ReadFileToString(
@@ -553,29 +570,9 @@
ASSERT_EQ("DE_RESTORE_CONTENT", de_content);
}
-TEST_F(ServiceTest, CreateSnapshotThenDestroyIt) {
- auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0);
- auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0);
-
- ASSERT_TRUE(mkdirs(rollback_ce_dir, 700));
- ASSERT_TRUE(mkdirs(rollback_de_dir, 700));
-
- auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo");
- auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo");
-
- ASSERT_TRUE(mkdirs(fake_package_ce_path, 700));
- ASSERT_TRUE(mkdirs(fake_package_de_path, 700));
-
- auto deleter = [&rollback_ce_dir, &rollback_de_dir,
- &fake_package_ce_path, &fake_package_de_path]() {
- delete_dir_contents(rollback_ce_dir, true);
- delete_dir_contents(rollback_de_dir, true);
- delete_dir_contents(fake_package_ce_path, true);
- delete_dir_contents(fake_package_de_path, true);
- rmdir(rollback_ce_dir.c_str());
- rmdir(rollback_de_dir.c_str());
- };
- auto scope_guard = android::base::make_scope_guard(deleter);
+TEST_F(AppDataSnapshotTest, CreateSnapshotThenDestroyIt) {
+ auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 57);
+ auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 57);
// Prepare data for snapshot.
ASSERT_TRUE(android::base::WriteStringToFile(
@@ -588,7 +585,7 @@
int64_t ce_snapshot_inode;
// Request a snapshot of both the CE as well as the DE content.
ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"),
- "com.foo", 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE, &ce_snapshot_inode).isOk());
+ "com.foo", 0, 57, FLAG_STORAGE_DE | FLAG_STORAGE_CE, &ce_snapshot_inode).isOk());
// Because CE data snapshot was requested, ce_snapshot_inode can't be null.
ASSERT_NE(0, ce_snapshot_inode);
// Check snapshot is there.
@@ -598,39 +595,19 @@
ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"),
- "com.foo", 0, ce_snapshot_inode, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
+ "com.foo", 0, ce_snapshot_inode, 57, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
// Check snapshot is deleted.
ASSERT_EQ(-1, stat((rollback_ce_dir + "/com.foo").c_str(), &sb));
ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo").c_str(), &sb));
}
-TEST_F(ServiceTest, DestroyAppDataSnapshot_CeSnapshotInodeIsZero) {
- auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0);
- auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0);
-
- ASSERT_TRUE(mkdirs(rollback_ce_dir, 700));
- ASSERT_TRUE(mkdirs(rollback_de_dir, 700));
-
- auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo");
- auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo");
-
- ASSERT_TRUE(mkdirs(fake_package_ce_path, 700));
- ASSERT_TRUE(mkdirs(fake_package_de_path, 700));
-
- auto deleter = [&rollback_ce_dir, &rollback_de_dir,
- &fake_package_ce_path, &fake_package_de_path]() {
- delete_dir_contents(rollback_ce_dir, true);
- delete_dir_contents(rollback_de_dir, true);
- delete_dir_contents(fake_package_ce_path, true);
- delete_dir_contents(fake_package_de_path, true);
- rmdir(rollback_ce_dir.c_str());
- rmdir(rollback_de_dir.c_str());
- };
- auto scope_guard = android::base::make_scope_guard(deleter);
+TEST_F(AppDataSnapshotTest, DestroyAppDataSnapshot_CeSnapshotInodeIsZero) {
+ auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 1543);
+ auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 1543);
// Create a snapshot
- ASSERT_TRUE(mkdirs(rollback_ce_dir + "/com.foo/", 700));
- ASSERT_TRUE(mkdirs(rollback_de_dir + "/com.foo/", 700));
+ ASSERT_TRUE(mkdirs(rollback_ce_dir + "/com.foo/", 0700));
+ ASSERT_TRUE(mkdirs(rollback_de_dir + "/com.foo/", 0700));
ASSERT_TRUE(android::base::WriteStringToFile(
"CE_RESTORE_CONTENT", rollback_ce_dir + "/com.foo/file1",
0700, 10000, 20000, false /* follow_symlinks */));
@@ -639,7 +616,7 @@
0700, 10000, 20000, false /* follow_symlinks */));
ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"),
- "com.foo", 0, 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
+ "com.foo", 0, 0, 1543, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
// Check snapshot is deleted.
struct stat sb;
@@ -648,67 +625,33 @@
// Check that deleting already deleted snapshot is no-op.
ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"),
- "com.foo", 0, 0, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
+ "com.foo", 0, 0, 1543, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk());
}
-TEST_F(ServiceTest, DestroyAppDataSnapshot_WrongVolumeUuid) {
+TEST_F(AppDataSnapshotTest, DestroyAppDataSnapshot_WrongVolumeUuid) {
// Setup rollback data to make sure that test fails due to wrong volumeUuid
// being passed, not because of some other reason.
- auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0);
- auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0);
+ auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 43);
+ auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 43);
- ASSERT_TRUE(mkdirs(rollback_ce_dir, 700));
- ASSERT_TRUE(mkdirs(rollback_de_dir, 700));
-
- auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo");
- auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo");
-
- ASSERT_TRUE(mkdirs(fake_package_ce_path, 700));
- ASSERT_TRUE(mkdirs(fake_package_de_path, 700));
-
- auto deleter = [&rollback_ce_dir, &rollback_de_dir,
- &fake_package_ce_path, &fake_package_de_path]() {
- delete_dir_contents(rollback_ce_dir, true);
- delete_dir_contents(rollback_de_dir, true);
- delete_dir_contents(fake_package_ce_path, true);
- delete_dir_contents(fake_package_de_path, true);
- rmdir(rollback_ce_dir.c_str());
- rmdir(rollback_de_dir.c_str());
- };
- auto scope_guard = android::base::make_scope_guard(deleter);
+ ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700));
+ ASSERT_TRUE(mkdirs(rollback_de_dir, 0700));
ASSERT_FALSE(service->destroyAppDataSnapshot(std::make_unique<std::string>("BAR"),
- "com.foo", 0, 0, FLAG_STORAGE_DE).isOk());
+ "com.foo", 0, 0, 43, FLAG_STORAGE_DE).isOk());
}
-TEST_F(ServiceTest, RestoreAppDataSnapshot_WrongVolumeUuid) {
+TEST_F(AppDataSnapshotTest, RestoreAppDataSnapshot_WrongVolumeUuid) {
// Setup rollback data to make sure that fails due to wrong volumeUuid being
// passed, not because of some other reason.
- auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0);
- auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0);
+ auto rollback_ce_dir = create_data_misc_ce_rollback_path("TEST", 0, 41);
+ auto rollback_de_dir = create_data_misc_de_rollback_path("TEST", 0, 41);
- ASSERT_TRUE(mkdirs(rollback_ce_dir, 700));
- ASSERT_TRUE(mkdirs(rollback_de_dir, 700));
-
- auto fake_package_ce_path = create_data_user_ce_package_path("TEST", 0, "com.foo");
- auto fake_package_de_path = create_data_user_de_package_path("TEST", 0, "com.foo");
-
- ASSERT_TRUE(mkdirs(fake_package_ce_path, 700));
- ASSERT_TRUE(mkdirs(fake_package_de_path, 700));
-
- auto deleter = [&rollback_ce_dir, &rollback_de_dir,
- &fake_package_ce_path, &fake_package_de_path]() {
- delete_dir_contents(rollback_ce_dir, true);
- delete_dir_contents(rollback_de_dir, true);
- delete_dir_contents(fake_package_ce_path, true);
- delete_dir_contents(fake_package_de_path, true);
- rmdir(rollback_ce_dir.c_str());
- rmdir(rollback_de_dir.c_str());
- };
- auto scope_guard = android::base::make_scope_guard(deleter);
+ ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700));
+ ASSERT_TRUE(mkdirs(rollback_de_dir, 0700));
EXPECT_BINDER_FAIL(service->restoreAppDataSnapshot(std::make_unique<std::string>("BAR"),
- "com.foo", 10000, -1, "", 0, FLAG_STORAGE_DE));
+ "com.foo", 10000, "", 0, 41, FLAG_STORAGE_DE));
}
} // namespace installd
diff --git a/cmds/installd/tests/installd_service_test.xml b/cmds/installd/tests/installd_service_test.xml
new file mode 100644
index 0000000..b838f4f
--- /dev/null
+++ b/cmds/installd/tests/installd_service_test.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+<!-- Note: this is derived from the autogenerated configuration. We require
+ root support. -->
+<configuration description="Runs installd_service_test.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="installd_service_test->/data/local/tmp/installd_service_test" />
+ </target_preparer>
+
+ <!-- The test requires root for file access. -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="installd_service_test" />
+ </test>
+</configuration>
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index 1782aa2..e61eb6e 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -546,56 +546,86 @@
}
TEST_F(UtilsTest, TestRollbackPaths) {
- EXPECT_EQ("/data/misc_ce/0/rollback/com.foo",
- create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo"));
- EXPECT_EQ("/data/misc_ce/10/rollback/com.foo",
- create_data_misc_ce_rollback_package_path(nullptr, 10, "com.foo"));
+ EXPECT_EQ("/data/misc_ce/0/rollback/239/com.foo",
+ create_data_misc_ce_rollback_package_path(nullptr, 0, 239, "com.foo"));
+ EXPECT_EQ("/data/misc_ce/10/rollback/37/com.foo",
+ create_data_misc_ce_rollback_package_path(nullptr, 10, 37, "com.foo"));
- EXPECT_EQ("/data/misc_de/0/rollback/com.foo",
- create_data_misc_de_rollback_package_path(nullptr, 0, "com.foo"));
- EXPECT_EQ("/data/misc_de/10/rollback/com.foo",
- create_data_misc_de_rollback_package_path(nullptr, 10, "com.foo"));
+ EXPECT_EQ("/data/misc_de/0/rollback/73/com.foo",
+ create_data_misc_de_rollback_package_path(nullptr, 0, 73, "com.foo"));
+ EXPECT_EQ("/data/misc_de/10/rollback/13/com.foo",
+ create_data_misc_de_rollback_package_path(nullptr, 10, 13, "com.foo"));
- EXPECT_EQ("/data/misc_ce/0/rollback",
- create_data_misc_ce_rollback_path(nullptr, 0));
- EXPECT_EQ("/data/misc_ce/10/rollback",
- create_data_misc_ce_rollback_path(nullptr, 10));
+ EXPECT_EQ("/data/misc_ce/0/rollback/57",
+ create_data_misc_ce_rollback_path(nullptr, 0, 57));
+ EXPECT_EQ("/data/misc_ce/10/rollback/1543",
+ create_data_misc_ce_rollback_path(nullptr, 10, 1543));
- EXPECT_EQ("/data/misc_de/0/rollback",
- create_data_misc_de_rollback_path(nullptr, 0));
- EXPECT_EQ("/data/misc_de/10/rollback",
- create_data_misc_de_rollback_path(nullptr, 10));
+ EXPECT_EQ("/data/misc_de/0/rollback/43",
+ create_data_misc_de_rollback_path(nullptr, 0, 43));
+ EXPECT_EQ("/data/misc_de/10/rollback/41",
+ create_data_misc_de_rollback_path(nullptr, 10, 41));
- EXPECT_EQ("/data/misc_ce/0/rollback/com.foo",
- create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo", 0));
- EXPECT_EQ("/data/misc_ce/0/rollback/com.foo",
- create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo", 239));
+ EXPECT_EQ("/data/misc_ce/0/rollback/17/com.foo",
+ create_data_misc_ce_rollback_package_path(nullptr, 0, 17, "com.foo", 0));
+ EXPECT_EQ("/data/misc_ce/0/rollback/19/com.foo",
+ create_data_misc_ce_rollback_package_path(nullptr, 0, 19, "com.foo", 239));
- auto rollback_ce_package_path = create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo");
- auto deleter = [&rollback_ce_package_path]() {
- delete_dir_contents_and_dir(rollback_ce_package_path, true /* ignore_if_missing */);
+ auto rollback_ce_path = create_data_misc_ce_rollback_path(nullptr, 0, 53);
+ auto rollback_ce_package_path = create_data_misc_ce_rollback_package_path(nullptr, 0, 53,
+ "com.foo");
+ auto deleter = [&rollback_ce_path]() {
+ delete_dir_contents_and_dir(rollback_ce_path, true /* ignore_if_missing */);
};
auto scope_guard = android::base::make_scope_guard(deleter);
- ASSERT_NE(-1, mkdir(rollback_ce_package_path.c_str(), 700));
+ EXPECT_NE(-1, mkdir(rollback_ce_path.c_str(), 700));
+ EXPECT_NE(-1, mkdir(rollback_ce_package_path.c_str(), 700));
ino_t ce_data_inode;
- ASSERT_EQ(0, get_path_inode(rollback_ce_package_path, &ce_data_inode));
+ EXPECT_EQ(0, get_path_inode(rollback_ce_package_path, &ce_data_inode));
- EXPECT_EQ("/data/misc_ce/0/rollback/com.foo",
- create_data_misc_ce_rollback_package_path(nullptr, 0, "com.foo", ce_data_inode));
+ EXPECT_EQ("/data/misc_ce/0/rollback/53/com.foo",
+ create_data_misc_ce_rollback_package_path(nullptr, 0, 53, "com.foo", ce_data_inode));
// Check that path defined by inode is picked even if it's not the same as
// the fallback one.
- EXPECT_EQ("/data/misc_ce/0/rollback/com.foo",
- create_data_misc_ce_rollback_package_path(nullptr, 0, "com.bar", ce_data_inode));
+ EXPECT_EQ("/data/misc_ce/0/rollback/53/com.foo",
+ create_data_misc_ce_rollback_package_path(nullptr, 0, 53, "com.bar", ce_data_inode));
// These last couple of cases are never exercised in production because we
// only snapshot apps in the primary data partition. Exercise them here for
// the sake of completeness.
- EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/misc_ce/0/rollback/com.example",
- create_data_misc_ce_rollback_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0, "com.example"));
- EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/misc_de/0/rollback/com.example",
- create_data_misc_de_rollback_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0, "com.example"));
+ EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/misc_ce/0/rollback/7/com.example",
+ create_data_misc_ce_rollback_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0, 7,
+ "com.example"));
+ EXPECT_EQ("/mnt/expand/57f8f4bc-abf4-655f-bf67-946fc0f9f25b/misc_de/0/rollback/11/com.example",
+ create_data_misc_de_rollback_package_path("57f8f4bc-abf4-655f-bf67-946fc0f9f25b", 0, 11,
+ "com.example"));
+}
+
+TEST_F(UtilsTest, TestCreateDirIfNeeded) {
+ system("mkdir -p /data/local/tmp/user/0");
+
+ auto deleter = [&]() {
+ delete_dir_contents_and_dir("/data/local/tmp/user/0", true /* ignore_if_missing */);
+ };
+ auto scope_guard = android::base::make_scope_guard(deleter);
+
+ // Create folder and check it's permissions.
+ ASSERT_EQ(0, create_dir_if_needed("/data/local/tmp/user/0/foo", 0700));
+ struct stat st;
+ ASSERT_EQ(0, stat("/data/local/tmp/user/0/foo", &st));
+ ASSERT_EQ(0700, st.st_mode & ALLPERMS);
+
+ // Check that create_dir_if_needed is no-op if folder already exists with
+ // correct permissions.
+ ASSERT_EQ(0, create_dir_if_needed("/data/local/tmp/user/0/foo", 0700));
+
+ // Check -1 is returned if folder exists but with different permissions.
+ ASSERT_EQ(-1, create_dir_if_needed("/data/local/tmp/user/0/foo", 0750));
+
+ // Check that call fails if parent doesn't exist.
+ ASSERT_NE(0, create_dir_if_needed("/data/local/tmp/user/0/bar/baz", 0700));
}
} // namespace installd
diff --git a/cmds/installd/tests/installd_utils_test.xml b/cmds/installd/tests/installd_utils_test.xml
new file mode 100644
index 0000000..92aba50
--- /dev/null
+++ b/cmds/installd/tests/installd_utils_test.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+<!-- Note: this is derived from the autogenerated configuration. We require
+ root support. -->
+<configuration description="Runs installd_utils_test.">
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-suite-tag" value="apct-native" />
+
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="installd_utils_test->/data/local/tmp/installd_utils_test" />
+ </target_preparer>
+
+ <!-- The test requires root for file access (rollback paths). -->
+ <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="installd_utils_test" />
+ </test>
+</configuration>
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 5b487bb..da097db 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -197,32 +197,44 @@
return StringPrintf("%s/user_de/%u", data.c_str(), userid);
}
-
-std::string create_data_misc_ce_rollback_path(const char* volume_uuid, userid_t user) {
+std::string create_data_misc_ce_rollback_base_path(const char* volume_uuid, userid_t user) {
return StringPrintf("%s/misc_ce/%u/rollback", create_data_path(volume_uuid).c_str(), user);
}
-std::string create_data_misc_de_rollback_path(const char* volume_uuid, userid_t user) {
+std::string create_data_misc_de_rollback_base_path(const char* volume_uuid, userid_t user) {
return StringPrintf("%s/misc_de/%u/rollback", create_data_path(volume_uuid).c_str(), user);
}
-std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid,
- userid_t user, const char* package_name) {
- return StringPrintf("%s/%s",
- create_data_misc_ce_rollback_path(volume_uuid, user).c_str(), package_name);
+std::string create_data_misc_ce_rollback_path(const char* volume_uuid, userid_t user,
+ int32_t snapshot_id) {
+ return StringPrintf("%s/%d", create_data_misc_ce_rollback_base_path(volume_uuid, user).c_str(),
+ snapshot_id);
+}
+
+std::string create_data_misc_de_rollback_path(const char* volume_uuid, userid_t user,
+ int32_t snapshot_id) {
+ return StringPrintf("%s/%d", create_data_misc_de_rollback_base_path(volume_uuid, user).c_str(),
+ snapshot_id);
}
std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid,
- userid_t user, const char* package_name, ino_t ce_rollback_inode) {
- auto fallback = create_data_misc_ce_rollback_package_path(volume_uuid, user, package_name);
- auto user_path = create_data_misc_ce_rollback_path(volume_uuid, user);
+ userid_t user, int32_t snapshot_id, const char* package_name) {
+ return StringPrintf("%s/%s",
+ create_data_misc_ce_rollback_path(volume_uuid, user, snapshot_id).c_str(), package_name);
+}
+
+std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid,
+ userid_t user, int32_t snapshot_id, const char* package_name, ino_t ce_rollback_inode) {
+ auto fallback = create_data_misc_ce_rollback_package_path(volume_uuid, user, snapshot_id,
+ package_name);
+ auto user_path = create_data_misc_ce_rollback_path(volume_uuid, user, snapshot_id);
return resolve_ce_path_by_inode_or_fallback(user_path, ce_rollback_inode, fallback);
}
std::string create_data_misc_de_rollback_package_path(const char* volume_uuid,
- userid_t user, const char* package_name) {
+ userid_t user, int32_t snapshot_id, const char* package_name) {
return StringPrintf("%s/%s",
- create_data_misc_de_rollback_path(volume_uuid, user).c_str(), package_name);
+ create_data_misc_de_rollback_path(volume_uuid, user, snapshot_id).c_str(), package_name);
}
/**
@@ -528,6 +540,30 @@
return result;
}
+int create_dir_if_needed(const std::string& pathname, mode_t perms) {
+ struct stat st;
+
+ int rc;
+ if ((rc = stat(pathname.c_str(), &st)) != 0) {
+ if (errno == ENOENT) {
+ return mkdir(pathname.c_str(), perms);
+ } else {
+ return rc;
+ }
+ } else if (!S_ISDIR(st.st_mode)) {
+ LOG(DEBUG) << pathname << " is not a folder";
+ return -1;
+ }
+
+ mode_t actual_perms = st.st_mode & ALLPERMS;
+ if (actual_perms != perms) {
+ LOG(WARNING) << pathname << " permissions " << actual_perms << " expected " << perms;
+ return -1;
+ }
+
+ return 0;
+}
+
int delete_dir_contents(const std::string& pathname, bool ignore_if_missing) {
return delete_dir_contents(pathname.c_str(), 0, nullptr, ignore_if_missing);
}
@@ -892,6 +928,8 @@
static int validate_apk_path_internal(const std::string& path, int maxSubdirs) {
if (validate_path(android_app_dir, path, maxSubdirs) == 0) {
return 0;
+ } else if (validate_path(android_staging_dir, path, maxSubdirs) == 0) {
+ return 0;
} else if (validate_path(android_app_private_dir, path, maxSubdirs) == 0) {
return 0;
} else if (validate_path(android_app_ephemeral_dir, path, maxSubdirs) == 0) {
diff --git a/cmds/installd/utils.h b/cmds/installd/utils.h
index 5d3a97d..91d9e13 100644
--- a/cmds/installd/utils.h
+++ b/cmds/installd/utils.h
@@ -61,14 +61,18 @@
std::string create_data_user_ce_package_path_as_user_link(
const char* volume_uuid, userid_t userid, const char* package_name);
-std::string create_data_misc_ce_rollback_path(const char* volume_uuid, userid_t user);
-std::string create_data_misc_de_rollback_path(const char* volume_uuid, userid_t user);
+std::string create_data_misc_ce_rollback_base_path(const char* volume_uuid, userid_t user);
+std::string create_data_misc_de_rollback_base_path(const char* volume_uuid, userid_t user);
+std::string create_data_misc_ce_rollback_path(const char* volume_uuid, userid_t user,
+ int32_t snapshot_id);
+std::string create_data_misc_de_rollback_path(const char* volume_uuid, userid_t user,
+ int32_t snapshot_id);
std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid,
- userid_t user, const char* package_name);
+ userid_t user, int32_t snapshot_id, const char* package_name);
std::string create_data_misc_ce_rollback_package_path(const char* volume_uuid,
- userid_t user, const char* package_name, ino_t ce_rollback_inode);
+ userid_t user, int32_t snapshot_id, const char* package_name, ino_t ce_rollback_inode);
std::string create_data_misc_de_rollback_package_path(const char* volume_uuid,
- userid_t user, const char* package_name);
+ userid_t user, int32_t snapshot_id, const char* package_name);
std::string create_data_media_path(const char* volume_uuid, userid_t userid);
std::string create_data_media_obb_path(const char* volume_uuid, const char* package_name);
@@ -109,6 +113,8 @@
bool is_valid_filename(const std::string& name);
bool is_valid_package_name(const std::string& packageName);
+int create_dir_if_needed(const std::string& pathname, mode_t mode);
+
int delete_dir_contents(const std::string& pathname, bool ignore_if_missing = false);
int delete_dir_contents_and_dir(const std::string& pathname, bool ignore_if_missing = false);
diff --git a/cmds/installd/view_compiler.cpp b/cmds/installd/view_compiler.cpp
index f1ac717..60d6492 100644
--- a/cmds/installd/view_compiler.cpp
+++ b/cmds/installd/view_compiler.cpp
@@ -45,7 +45,7 @@
// and pass file descriptors.
// Open input file
- unique_fd infd{open(apk_path, 0)};
+ unique_fd infd{open(apk_path, O_RDONLY)}; // NOLINT(android-cloexec-open)
if (infd.get() < 0) {
PLOG(ERROR) << "Could not open input file: " << apk_path;
return false;
@@ -53,7 +53,7 @@
// Set up output file. viewcompiler can't open outputs by fd, but it can write to stdout, so
// we close stdout and open it towards the right output.
- unique_fd outfd{open(out_dex_file, O_CREAT | O_TRUNC | O_WRONLY, 0644)};
+ unique_fd outfd{open(out_dex_file, O_CREAT | O_TRUNC | O_WRONLY | O_CLOEXEC, 0644)};
if (outfd.get() < 0) {
PLOG(ERROR) << "Could not open output file: " << out_dex_file;
return false;
@@ -62,10 +62,6 @@
PLOG(ERROR) << "Could not change output file permissions";
return false;
}
- if (close(STDOUT_FILENO) != 0) {
- PLOG(ERROR) << "Could not close stdout";
- return false;
- }
if (dup2(outfd, STDOUT_FILENO) < 0) {
PLOG(ERROR) << "Could not duplicate output file descriptor";
return false;
@@ -96,4 +92,4 @@
}
} // namespace installd
-} // namespace android
\ No newline at end of file
+} // namespace android
diff --git a/cmds/installd/view_compiler.h b/cmds/installd/view_compiler.h
index f7c6e57..aa141ca 100644
--- a/cmds/installd/view_compiler.h
+++ b/cmds/installd/view_compiler.h
@@ -26,4 +26,4 @@
} // namespace installd
} // namespace android
-#endif // VIEW_COMPILER_H_
\ No newline at end of file
+#endif // VIEW_COMPILER_H_
diff --git a/cmds/ip-up-vpn/ip-up-vpn.c b/cmds/ip-up-vpn/ip-up-vpn.c
index 3b8955b..71f0837 100644
--- a/cmds/ip-up-vpn/ip-up-vpn.c
+++ b/cmds/ip-up-vpn/ip-up-vpn.c
@@ -95,6 +95,7 @@
strncpy(ifr.ifr_name, interface, IFNAMSIZ);
if (ioctl(s, SIOCSIFFLAGS, &ifr)) {
ALOGE("Cannot bring up %s: %s", interface, strerror(errno));
+ fclose(state);
return 1;
}
@@ -102,6 +103,7 @@
if (!set_address(&ifr.ifr_addr, address) ||
ioctl(s, SIOCSIFADDR, &ifr)) {
ALOGE("Cannot set address: %s", strerror(errno));
+ fclose(state);
return 1;
}
@@ -109,6 +111,7 @@
if (set_address(&ifr.ifr_netmask, env("INTERNAL_NETMASK4"))) {
if (ioctl(s, SIOCSIFNETMASK, &ifr)) {
ALOGE("Cannot set netmask: %s", strerror(errno));
+ fclose(state);
return 1;
}
}
@@ -123,6 +126,7 @@
fprintf(state, "%s\n", env("REMOTE_ADDR"));
} else {
ALOGE("Cannot parse parameters");
+ fclose(state);
return 1;
}
diff --git a/data/etc/android.hardware.nfc.ese.xml b/data/etc/android.hardware.nfc.ese.xml
new file mode 100644
index 0000000..6642bb2
--- /dev/null
+++ b/data/etc/android.hardware.nfc.ese.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<!-- This feature indicates that the device supports eSE-based NFC card
+ emulation -->
+<permissions>
+ <feature name="android.hardware.nfc.ese" />
+</permissions>
diff --git a/data/etc/android.hardware.nfc.uicc.xml b/data/etc/android.hardware.nfc.uicc.xml
new file mode 100644
index 0000000..4f12de4
--- /dev/null
+++ b/data/etc/android.hardware.nfc.uicc.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+
+<!-- This feature indicates that the device supports uicc-based NFC card
+ emulation -->
+<permissions>
+ <feature name="android.hardware.nfc.uicc" />
+</permissions>
diff --git a/include/OWNERS b/include/OWNERS
index 22be776..db52850 100644
--- a/include/OWNERS
+++ b/include/OWNERS
@@ -1,8 +1,10 @@
alexeykuzmin@google.com
dangittik@google.com
+jreck@google.com
lajos@google.com
mathias@google.com
michaelwr@google.com
+nona@google.com
racarr@google.com
romainguy@android.com
santoscordon@google.com
diff --git a/include/input/InputTransport.h b/include/input/InputTransport.h
index 1ea2c2c..ecdc075 100644
--- a/include/input/InputTransport.h
+++ b/include/input/InputTransport.h
@@ -41,6 +41,13 @@
*
* Note that this structure is used for IPCs so its layout must be identical
* on 64 and 32 bit processes. This is tested in StructLayout_test.cpp.
+ *
+ * Since the struct must be aligned to an 8-byte boundary, there could be uninitialized bytes
+ * in-between the defined fields. This padding data should be explicitly accounted for by adding
+ * "empty" fields into the struct. This data is memset to zero before sending the struct across
+ * the socket. Adding the explicit fields ensures that the memset is not optimized away by the
+ * compiler. When a new field is added to the struct, the corresponding change
+ * in StructLayout_test should be made.
*/
struct InputMessage {
enum {
@@ -61,6 +68,7 @@
union Body {
struct Key {
uint32_t seq;
+ uint32_t empty1;
nsecs_t eventTime __attribute__((aligned(8)));
int32_t deviceId;
int32_t source;
@@ -71,6 +79,7 @@
int32_t scanCode;
int32_t metaState;
int32_t repeatCount;
+ uint32_t empty2;
nsecs_t downTime __attribute__((aligned(8)));
inline size_t size() const {
@@ -80,6 +89,7 @@
struct Motion {
uint32_t seq;
+ uint32_t empty1;
nsecs_t eventTime __attribute__((aligned(8)));
int32_t deviceId;
int32_t source;
@@ -90,12 +100,14 @@
int32_t metaState;
int32_t buttonState;
int32_t edgeFlags;
+ uint32_t empty2;
nsecs_t downTime __attribute__((aligned(8)));
float xOffset;
float yOffset;
float xPrecision;
float yPrecision;
uint32_t pointerCount;
+ uint32_t empty3;
// Note that PointerCoords requires 8 byte alignment.
struct Pointer {
PointerProperties properties;
@@ -126,6 +138,7 @@
bool isValid(size_t actualSize) const;
size_t size() const;
+ void getSanitizedCopy(InputMessage* msg) const;
};
/*
diff --git a/libs/android_runtime_lazy/Android.bp b/libs/android_runtime_lazy/Android.bp
new file mode 100644
index 0000000..9284acb
--- /dev/null
+++ b/libs/android_runtime_lazy/Android.bp
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+// libandroid_runtime_lazy is a shim library.
+// This provides very limited small set of APIs from libandroid_runtime.
+//
+// By depending on this instead of libandroid_runtime,
+// a library can be loaded without paying the cost of libandroid_runtime
+// which is quite huge. The cost will be paid when libandroid_runtime is actually used.
+//
+// For Partial-source PDK build, there is a constraint that
+// frameworks/native modules should not depend on frameworks/base.
+// This library can be used to cut down the dependency between them.
+// (e.g. libbinder_ndk)
+//
+// Some libraries which serve as LL-NDK and NDK as well may depend on this
+// instead of libandroid_runtime. When they are used by a vendor process,
+// depending on libandroid_runtime is meaningless. In this case,
+// they can depend on libandroid_runtime_lazy.
+cc_library {
+ name: "libandroid_runtime_lazy",
+ vendor_available: true,
+ double_loadable: true,
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ "-Wunused",
+ "-Wunreachable-code",
+ ],
+
+ srcs: [
+ "android_runtime_lazy.cpp",
+ ],
+
+ shared_libs: [
+ "liblog",
+ "libutils",
+ ],
+
+ required: [
+ "libandroid_runtime",
+ ],
+
+ export_include_dirs: [
+ "include",
+ ],
+
+ header_libs: [
+ "jni_headers",
+ "libbinder_headers",
+ ],
+}
diff --git a/libs/android_runtime_lazy/android_runtime_lazy.cpp b/libs/android_runtime_lazy/android_runtime_lazy.cpp
new file mode 100644
index 0000000..98d8e8a
--- /dev/null
+++ b/libs/android_runtime_lazy/android_runtime_lazy.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define LOG_TAG "ANDROID_RUNTIME_LAZY"
+#include "android_runtime/AndroidRuntime.h"
+#include "android_util_Binder.h"
+
+#include <dlfcn.h>
+#include <mutex>
+
+#include <log/log.h>
+
+namespace android {
+namespace {
+
+std::once_flag loadFlag;
+
+typedef JNIEnv* (*getJNIEnv_t)();
+typedef sp<IBinder> (*ibinderForJavaObject_t)(JNIEnv* env, jobject obj);
+typedef jobject (*javaObjectForIBinder_t)(JNIEnv* env, const sp<IBinder>& val);
+
+getJNIEnv_t _getJNIEnv;
+ibinderForJavaObject_t _ibinderForJavaObject;
+javaObjectForIBinder_t _javaObjectForIBinder;
+
+void load() {
+ std::call_once(loadFlag, []() {
+ void* handle = dlopen("libandroid_runtime.so", RTLD_LAZY);
+ if (handle == nullptr) {
+ ALOGE("Could not open libandroid_runtime.");
+ return;
+ }
+
+ _getJNIEnv = reinterpret_cast<getJNIEnv_t>(
+ dlsym(handle, "_ZN7android14AndroidRuntime9getJNIEnvEv"));
+ if (_getJNIEnv == nullptr) {
+ ALOGW("Could not find getJNIEnv.");
+ // no return
+ }
+
+ _ibinderForJavaObject = reinterpret_cast<ibinderForJavaObject_t>(
+ dlsym(handle, "_ZN7android20ibinderForJavaObjectEP7_JNIEnvP8_jobject"));
+ if (_ibinderForJavaObject == nullptr) {
+ ALOGW("Could not find ibinderForJavaObject.");
+ // no return
+ }
+
+ _javaObjectForIBinder = reinterpret_cast<javaObjectForIBinder_t>(
+ dlsym(handle,
+ "_ZN7android20javaObjectForIBinderEP7_JNIEnvRKNS_2spINS_7IBinderEEE"));
+ if (_javaObjectForIBinder == nullptr) {
+ ALOGW("Could not find javaObjectForIBinder.");
+ // no return
+ }
+ });
+}
+
+} // namespace
+
+// exports delegate functions
+
+JNIEnv* AndroidRuntime::getJNIEnv() {
+ load();
+ if (_getJNIEnv == nullptr) {
+ return nullptr;
+ }
+ return _getJNIEnv();
+}
+
+sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj) {
+ load();
+ if (_ibinderForJavaObject == nullptr) {
+ return nullptr;
+ }
+ return _ibinderForJavaObject(env, obj);
+}
+
+jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val) {
+ load();
+ if (_javaObjectForIBinder == nullptr) {
+ return nullptr;
+ }
+ return _javaObjectForIBinder(env, val);
+}
+
+} // namespace android
diff --git a/libs/android_runtime_lazy/include/android_runtime/AndroidRuntime.h b/libs/android_runtime_lazy/include/android_runtime/AndroidRuntime.h
new file mode 100644
index 0000000..85231fa
--- /dev/null
+++ b/libs/android_runtime_lazy/include/android_runtime/AndroidRuntime.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include "jni.h"
+
+namespace android {
+
+// Intentionally use the same name with AndroidRuntime in frameworks/base/core/jni/
+// to make the client use this in the same way with the original class.
+class AndroidRuntime {
+public:
+ static JNIEnv* getJNIEnv();
+};
+
+} // namespace android
diff --git a/libs/android_runtime_lazy/include/android_util_Binder.h b/libs/android_runtime_lazy/include/android_util_Binder.h
new file mode 100644
index 0000000..e47390e
--- /dev/null
+++ b/libs/android_runtime_lazy/include/android_util_Binder.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <binder/IBinder.h>
+#include "jni.h"
+
+namespace android {
+
+// The name of this file is same with the file in frameworks/base/core/jni/
+// This is intentional to make the client use these exported functions
+// in the same way with the original.
+
+jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val);
+sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj);
+
+} // namespace android
diff --git a/libs/binder/Binder.cpp b/libs/binder/Binder.cpp
index f6cc3af..96ee295 100644
--- a/libs/binder/Binder.cpp
+++ b/libs/binder/Binder.cpp
@@ -86,6 +86,10 @@
class BBinder::Extras
{
public:
+ // unlocked objects
+ bool mRequestingSid = false;
+
+ // for below objects
Mutex mLock;
BpBinder::ObjectManager mObjects;
};
@@ -163,19 +167,8 @@
const void* objectID, void* object, void* cleanupCookie,
object_cleanup_func func)
{
- Extras* e = mExtras.load(std::memory_order_acquire);
-
- if (!e) {
- e = new Extras;
- Extras* expected = nullptr;
- if (!mExtras.compare_exchange_strong(expected, e,
- std::memory_order_release,
- std::memory_order_acquire)) {
- delete e;
- e = expected; // Filled in by CAS
- }
- if (e == nullptr) return; // out of memory
- }
+ Extras* e = getOrCreateExtras();
+ if (!e) return; // out of memory
AutoMutex _l(e->mLock);
e->mObjects.attach(objectID, object, cleanupCookie, func);
@@ -204,6 +197,30 @@
return this;
}
+bool BBinder::isRequestingSid()
+{
+ Extras* e = mExtras.load(std::memory_order_acquire);
+
+ return e && e->mRequestingSid;
+}
+
+void BBinder::setRequestingSid(bool requestingSid)
+{
+ Extras* e = mExtras.load(std::memory_order_acquire);
+
+ if (!e) {
+ // default is false. Most things don't need sids, so avoiding allocations when possible.
+ if (!requestingSid) {
+ return;
+ }
+
+ e = getOrCreateExtras();
+ if (!e) return; // out of memory
+ }
+
+ e->mRequestingSid = true;
+}
+
BBinder::~BBinder()
{
Extras* e = mExtras.load(std::memory_order_relaxed);
@@ -267,6 +284,25 @@
}
}
+BBinder::Extras* BBinder::getOrCreateExtras()
+{
+ Extras* e = mExtras.load(std::memory_order_acquire);
+
+ if (!e) {
+ e = new Extras;
+ Extras* expected = nullptr;
+ if (!mExtras.compare_exchange_strong(expected, e,
+ std::memory_order_release,
+ std::memory_order_acquire)) {
+ delete e;
+ e = expected; // Filled in by CAS
+ }
+ if (e == nullptr) return nullptr; // out of memory
+ }
+
+ return e;
+}
+
// ---------------------------------------------------------------------------
enum {
diff --git a/libs/binder/Debug.cpp b/libs/binder/Debug.cpp
index f38bbb2..a1c2a8b 100644
--- a/libs/binder/Debug.cpp
+++ b/libs/binder/Debug.cpp
@@ -221,7 +221,11 @@
for (word = 0; word < bytesPerLine; ) {
- const size_t startIndex = word+(alignment-(alignment?1:0));
+ size_t align_offset = alignment-(alignment?1:0);
+ if (remain > 0 && (size_t)remain <= align_offset) {
+ align_offset = remain - 1;
+ }
+ const size_t startIndex = word+align_offset;
for (index = 0; index < alignment || (alignment == 0 && index < bytesPerLine); index++) {
diff --git a/libs/binder/IInterface.cpp b/libs/binder/IInterface.cpp
index 6b77291..59d51ed 100644
--- a/libs/binder/IInterface.cpp
+++ b/libs/binder/IInterface.cpp
@@ -47,21 +47,3 @@
// ---------------------------------------------------------------------------
}; // namespace android
-
-extern "C" {
-
-void _ZN7android10IInterface8asBinderEv(void *retval, void* self) {
- ALOGW("deprecated asBinder call, please update your code");
- //ALOGI("self: %p, retval: %p", self, retval);
- android::sp<android::IBinder> *ret = new(retval) android::sp<android::IBinder>;
- *ret = android::IInterface::asBinder((android::IInterface*)self);
-}
-
-void _ZNK7android10IInterface8asBinderEv(void *retval, void *self) {
- ALOGW("deprecated asBinder call, please update your code");
- //ALOGI("self: %p, retval: %p", self, retval);
- android::sp<android::IBinder> *ret = new(retval) android::sp<android::IBinder>;
- *ret = android::IInterface::asBinder((android::IInterface*)self);
-}
-
-} // extern "C"
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 22f6f54..4b70e2e 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -88,7 +88,8 @@
"BR_FINISHED",
"BR_DEAD_BINDER",
"BR_CLEAR_DEATH_NOTIFICATION_DONE",
- "BR_FAILED_REPLY"
+ "BR_FAILED_REPLY",
+ "BR_TRANSACTION_SEC_CTX",
};
static const char *kCommandStrings[] = {
@@ -113,7 +114,7 @@
static const char* getReturnString(uint32_t cmd)
{
- size_t idx = cmd & 0xff;
+ size_t idx = cmd & _IOC_NRMASK;
if (idx < sizeof(kReturnStrings) / sizeof(kReturnStrings[0]))
return kReturnStrings[idx];
else
@@ -363,6 +364,11 @@
return mCallingPid;
}
+const char* IPCThreadState::getCallingSid() const
+{
+ return mCallingSid;
+}
+
uid_t IPCThreadState::getCallingUid() const
{
return mCallingUid;
@@ -370,6 +376,7 @@
int64_t IPCThreadState::clearCallingIdentity()
{
+ // ignore mCallingSid for legacy reasons
int64_t token = ((int64_t)mCallingUid<<32) | mCallingPid;
clearCaller();
return token;
@@ -398,12 +405,14 @@
void IPCThreadState::restoreCallingIdentity(int64_t token)
{
mCallingUid = (int)(token>>32);
+ mCallingSid = nullptr; // not enough data to restore
mCallingPid = (int)token;
}
void IPCThreadState::clearCaller()
{
mCallingPid = getpid();
+ mCallingSid = nullptr; // expensive to lookup
mCallingUid = getuid();
}
@@ -1089,10 +1098,19 @@
}
break;
+ case BR_TRANSACTION_SEC_CTX:
case BR_TRANSACTION:
{
- binder_transaction_data tr;
- result = mIn.read(&tr, sizeof(tr));
+ binder_transaction_data_secctx tr_secctx;
+ binder_transaction_data& tr = tr_secctx.transaction_data;
+
+ if (cmd == (int) BR_TRANSACTION_SEC_CTX) {
+ result = mIn.read(&tr_secctx, sizeof(tr_secctx));
+ } else {
+ result = mIn.read(&tr, sizeof(tr));
+ tr_secctx.secctx = 0;
+ }
+
ALOG_ASSERT(result == NO_ERROR,
"Not enough command data for brTRANSACTION");
if (result != NO_ERROR) break;
@@ -1108,15 +1126,18 @@
tr.offsets_size/sizeof(binder_size_t), freeBuffer, this);
const pid_t origPid = mCallingPid;
+ const char* origSid = mCallingSid;
const uid_t origUid = mCallingUid;
const int32_t origStrictModePolicy = mStrictModePolicy;
const int32_t origTransactionBinderFlags = mLastTransactionBinderFlags;
mCallingPid = tr.sender_pid;
+ mCallingSid = reinterpret_cast<const char*>(tr_secctx.secctx);
mCallingUid = tr.sender_euid;
mLastTransactionBinderFlags = tr.flags;
- //ALOGI(">>>> TRANSACT from pid %d uid %d\n", mCallingPid, mCallingUid);
+ // ALOGI(">>>> TRANSACT from pid %d sid %s uid %d\n", mCallingPid,
+ // (mCallingSid ? mCallingSid : "<N/A>"), mCallingUid);
Parcel reply;
status_t error;
@@ -1148,8 +1169,8 @@
}
mIPCThreadStateBase->popCurrentState();
- //ALOGI("<<<< TRANSACT from pid %d restore pid %d uid %d\n",
- // mCallingPid, origPid, origUid);
+ //ALOGI("<<<< TRANSACT from pid %d restore pid %d sid %s uid %d\n",
+ // mCallingPid, origPid, (origSid ? origSid : "<N/A>"), origUid);
if ((tr.flags & TF_ONE_WAY) == 0) {
LOG_ONEWAY("Sending reply to %d!", mCallingPid);
@@ -1160,6 +1181,7 @@
}
mCallingPid = origPid;
+ mCallingSid = origSid;
mCallingUid = origUid;
mStrictModePolicy = origStrictModePolicy;
mLastTransactionBinderFlags = origTransactionBinderFlags;
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 4ba6c2a..0203d41 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -36,6 +36,9 @@
sp<IServiceManager> defaultServiceManager()
{
+ static Mutex gDefaultServiceManagerLock;
+ static sp<IServiceManager> gDefaultServiceManager;
+
if (gDefaultServiceManager != nullptr) return gDefaultServiceManager;
{
@@ -74,10 +77,13 @@
bool checkPermission(const String16& permission, pid_t pid, uid_t uid)
{
+ static Mutex gPermissionControllerLock;
+ static sp<IPermissionController> gPermissionController;
+
sp<IPermissionController> pc;
- gDefaultServiceManagerLock.lock();
+ gPermissionControllerLock.lock();
pc = gPermissionController;
- gDefaultServiceManagerLock.unlock();
+ gPermissionControllerLock.unlock();
int64_t startTime = 0;
@@ -101,11 +107,11 @@
}
// Object is dead!
- gDefaultServiceManagerLock.lock();
+ gPermissionControllerLock.lock();
if (gPermissionController == pc) {
gPermissionController = nullptr;
}
- gDefaultServiceManagerLock.unlock();
+ gPermissionControllerLock.unlock();
}
// Need to retrieve the permission controller.
@@ -121,9 +127,9 @@
} else {
pc = interface_cast<IPermissionController>(binder);
// Install the new permission controller, and try again.
- gDefaultServiceManagerLock.lock();
+ gPermissionControllerLock.lock();
gPermissionController = pc;
- gDefaultServiceManagerLock.unlock();
+ gPermissionControllerLock.unlock();
}
}
}
@@ -142,6 +148,8 @@
virtual sp<IBinder> getService(const String16& name) const
{
+ static bool gSystemBootCompleted = false;
+
sp<IBinder> svc = checkService(name);
if (svc != nullptr) return svc;
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 0423264..9f8c408 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -76,14 +76,6 @@
// Note: must be kept in sync with android/os/StrictMode.java's PENALTY_GATHER
#define STRICT_MODE_PENALTY_GATHER (0x40 << 16)
-// XXX This can be made public if we want to provide
-// support for typed data.
-struct small_flat_data
-{
- uint32_t type;
- uint32_t data;
-};
-
namespace android {
static pthread_mutex_t gParcelGlobalAllocSizeLock = PTHREAD_MUTEX_INITIALIZER;
@@ -223,7 +215,7 @@
}
if (binder != nullptr) {
- IBinder *local = binder->localBinder();
+ BBinder *local = binder->localBinder();
if (!local) {
BpBinder *proxy = binder->remoteBinder();
if (proxy == nullptr) {
@@ -235,6 +227,9 @@
obj.handle = handle;
obj.cookie = 0;
} else {
+ if (local->isRequestingSid()) {
+ obj.flags |= FLAT_BINDER_FLAG_TXN_SECURITY_CTX;
+ }
obj.hdr.type = BINDER_TYPE_BINDER;
obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
obj.cookie = reinterpret_cast<uintptr_t>(local);
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index 3798b61..2d156df 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -40,7 +40,7 @@
#include <sys/stat.h>
#include <sys/types.h>
-#define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
+#define DEFAULT_BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
#define DEFAULT_MAX_BINDER_THREADS 15
#ifdef __ANDROID_VNDK__
@@ -77,7 +77,13 @@
if (gProcess != nullptr) {
return gProcess;
}
- gProcess = new ProcessState(kDefaultDriver);
+ gProcess = new ProcessState(kDefaultDriver, DEFAULT_BINDER_VM_SIZE);
+ return gProcess;
+}
+
+sp<ProcessState> ProcessState::selfOrNull()
+{
+ Mutex::Autolock _l(gProcessMutex);
return gProcess;
}
@@ -98,13 +104,19 @@
driver = "/dev/binder";
}
- gProcess = new ProcessState(driver);
+ gProcess = new ProcessState(driver, DEFAULT_BINDER_VM_SIZE);
return gProcess;
}
-sp<ProcessState> ProcessState::selfOrNull()
-{
+sp<ProcessState> ProcessState::initWithMmapSize(size_t mmap_size) {
Mutex::Autolock _l(gProcessMutex);
+ if (gProcess != nullptr) {
+ LOG_ALWAYS_FATAL_IF(mmap_size != gProcess->getMmapSize(),
+ "ProcessState already initialized with a different mmap size.");
+ return gProcess;
+ }
+
+ gProcess = new ProcessState(kDefaultDriver, mmap_size);
return gProcess;
}
@@ -181,8 +193,20 @@
mBinderContextCheckFunc = checkFunc;
mBinderContextUserData = userData;
- int dummy = 0;
- status_t result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy);
+ flat_binder_object obj {
+ .flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX,
+ };
+
+ status_t result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR_EXT, &obj);
+
+ // fallback to original method
+ if (result != 0) {
+ android_errorWriteLog(0x534e4554, "121035042");
+
+ int dummy = 0;
+ result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy);
+ }
+
if (result == 0) {
mManagesContexts = true;
} else if (result == -1) {
@@ -202,15 +226,6 @@
// already be invalid.
ssize_t ProcessState::getKernelReferences(size_t buf_count, uintptr_t* buf)
{
- // TODO: remove these when they are defined by bionic's binder.h
- struct binder_node_debug_info {
- binder_uintptr_t ptr;
- binder_uintptr_t cookie;
- __u32 has_strong_ref;
- __u32 has_weak_ref;
- };
-#define BINDER_GET_NODE_DEBUG_INFO _IOWR('b', 11, struct binder_node_debug_info)
-
binder_node_debug_info info = {};
uintptr_t* end = buf ? buf + buf_count : nullptr;
@@ -234,6 +249,10 @@
return count;
}
+size_t ProcessState::getMmapSize() {
+ return mMmapSize;
+}
+
void ProcessState::setCallRestriction(CallRestriction restriction) {
LOG_ALWAYS_FATAL_IF(IPCThreadState::selfOrNull(), "Call restrictions must be set before the threadpool is started.");
@@ -418,7 +437,7 @@
return fd;
}
-ProcessState::ProcessState(const char *driver)
+ProcessState::ProcessState(const char *driver, size_t mmap_size)
: mDriverName(String8(driver))
, mDriverFD(open_driver(driver))
, mVMStart(MAP_FAILED)
@@ -432,11 +451,12 @@
, mBinderContextUserData(nullptr)
, mThreadPoolStarted(false)
, mThreadPoolSeq(1)
+ , mMmapSize(mmap_size)
, mCallRestriction(CallRestriction::NONE)
{
if (mDriverFD >= 0) {
// mmap the binder, providing a chunk of virtual address space to receive transactions.
- mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
+ mVMStart = mmap(nullptr, mMmapSize, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
if (mVMStart == MAP_FAILED) {
// *sigh*
ALOGE("Using %s failed: unable to mmap transaction memory.\n", mDriverName.c_str());
@@ -453,7 +473,7 @@
{
if (mDriverFD >= 0) {
if (mVMStart != MAP_FAILED) {
- munmap(mVMStart, BINDER_VM_SIZE);
+ munmap(mVMStart, mMmapSize);
}
close(mDriverFD);
}
diff --git a/libs/binder/Static.cpp b/libs/binder/Static.cpp
index bd0e6f9..8625c6f 100644
--- a/libs/binder/Static.cpp
+++ b/libs/binder/Static.cpp
@@ -75,13 +75,4 @@
Mutex& gProcessMutex = *new Mutex;
sp<ProcessState> gProcess;
-// ------------ IServiceManager.cpp
-
-Mutex gDefaultServiceManagerLock;
-sp<IServiceManager> gDefaultServiceManager;
-#ifndef __ANDROID_VNDK__
-sp<IPermissionController> gPermissionController;
-#endif
-bool gSystemBootCompleted = false;
-
} // namespace android
diff --git a/libs/binder/Status.cpp b/libs/binder/Status.cpp
index a3f8755..8b33a56 100644
--- a/libs/binder/Status.cpp
+++ b/libs/binder/Status.cpp
@@ -63,6 +63,26 @@
return ret;
}
+std::string Status::exceptionToString(int32_t exceptionCode) {
+ switch (exceptionCode) {
+ #define EXCEPTION_TO_CASE(EXCEPTION) case EXCEPTION: return #EXCEPTION;
+ EXCEPTION_TO_CASE(EX_NONE)
+ EXCEPTION_TO_CASE(EX_SECURITY)
+ EXCEPTION_TO_CASE(EX_BAD_PARCELABLE)
+ EXCEPTION_TO_CASE(EX_ILLEGAL_ARGUMENT)
+ EXCEPTION_TO_CASE(EX_NULL_POINTER)
+ EXCEPTION_TO_CASE(EX_ILLEGAL_STATE)
+ EXCEPTION_TO_CASE(EX_NETWORK_MAIN_THREAD)
+ EXCEPTION_TO_CASE(EX_UNSUPPORTED_OPERATION)
+ EXCEPTION_TO_CASE(EX_SERVICE_SPECIFIC)
+ EXCEPTION_TO_CASE(EX_PARCELABLE)
+ EXCEPTION_TO_CASE(EX_HAS_REPLY_HEADER)
+ EXCEPTION_TO_CASE(EX_TRANSACTION_FAILED)
+ #undef EXCEPTION_TO_CASE
+ default: return std::to_string(exceptionCode);
+ }
+}
+
Status::Status(int32_t exceptionCode, int32_t errorCode)
: mException(exceptionCode),
mErrorCode(errorCode) {}
@@ -184,7 +204,7 @@
if (mException == EX_NONE) {
ret.append("No error");
} else {
- ret.appendFormat("Status(%d): '", mException);
+ ret.appendFormat("Status(%d, %s): '", mException, exceptionToString(mException).c_str());
if (mException == EX_SERVICE_SPECIFIC ||
mException == EX_TRANSACTION_FAILED) {
ret.appendFormat("%d: ", mErrorCode);
diff --git a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
index 5b66b92..c92c81f 100644
--- a/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
+++ b/libs/binder/aidl/android/content/pm/IPackageManagerNative.aidl
@@ -54,4 +54,18 @@
long getVersionCodeForPackage(in String packageName);
+ /* ApplicationInfo.isSystemApp() == true */
+ const int LOCATION_SYSTEM = 0x1;
+ /* ApplicationInfo.isVendor() == true */
+ const int LOCATION_VENDOR = 0x2;
+ /* ApplicationInfo.isProduct() == true */
+ const int LOCATION_PRODUCT = 0x4;
+
+ /**
+ * Returns a set of bitflags about package location.
+ * LOCATION_SYSTEM: getApplicationInfo(packageName).isSystemApp()
+ * LOCATION_VENDOR: getApplicationInfo(packageName).isVendor()
+ * LOCATION_PRODUCT: getApplicationInfo(packageName).isProduct()
+ */
+ int getLocationFlags(in @utf8InCpp String packageName);
}
diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h
index c251468..cf3ef84 100644
--- a/libs/binder/include/binder/Binder.h
+++ b/libs/binder/include/binder/Binder.h
@@ -60,6 +60,10 @@
virtual BBinder* localBinder();
+ bool isRequestingSid();
+ // This must be called before the object is sent to another process. Not thread safe.
+ void setRequestingSid(bool requestSid);
+
protected:
virtual ~BBinder();
@@ -75,6 +79,8 @@
class Extras;
+ Extras* getOrCreateExtras();
+
std::atomic<Extras*> mExtras;
void* mReserved0;
};
diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h
index 1d4f881..78f2e1d 100644
--- a/libs/binder/include/binder/BpBinder.h
+++ b/libs/binder/include/binder/BpBinder.h
@@ -67,7 +67,6 @@
virtual BpBinder* remoteBinder();
- status_t setConstantData(const void* data, size_t size);
void sendObituary();
static uint32_t getBinderProxyCount(uint32_t uid);
diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h
index 14edcbe..0630963 100644
--- a/libs/binder/include/binder/IBinder.h
+++ b/libs/binder/include/binder/IBinder.h
@@ -143,6 +143,9 @@
* dies. The @a cookie is optional. If non-NULL, you can
* supply a NULL @a recipient, and the recipient previously
* added with that cookie will be unlinked.
+ *
+ * If the binder is dead, this will return DEAD_OBJECT. Deleting
+ * the object will also unlink all death recipients.
*/
// NOLINTNEXTLINE(google-default-arguments)
virtual status_t unlinkToDeath( const wp<DeathRecipient>& recipient,
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index 745f618..a20ef7c 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -42,6 +42,11 @@
status_t clearLastError();
pid_t getCallingPid() const;
+ // nullptr if unavailable
+ //
+ // this can't be restored once it's cleared, and it does not return the
+ // context of the current process when not in a binder call.
+ const char* getCallingSid() const;
uid_t getCallingUid() const;
void setStrictModePolicy(int32_t policy);
@@ -51,6 +56,7 @@
int32_t getLastTransactionBinderFlags() const;
int64_t clearCallingIdentity();
+ // Restores PID/UID (not SID)
void restoreCallingIdentity(int64_t token);
int setupPolling(int* fd);
@@ -154,6 +160,7 @@
Parcel mOut;
status_t mLastError;
pid_t mCallingPid;
+ const char* mCallingSid;
uid_t mCallingUid;
int32_t mStrictModePolicy;
int32_t mLastTransactionBinderFlags;
diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h
index c9c273a..f6560a7 100644
--- a/libs/binder/include/binder/Parcel.h
+++ b/libs/binder/include/binder/Parcel.h
@@ -20,6 +20,8 @@
#include <string>
#include <vector>
+#include <linux/android/binder.h>
+
#include <android-base/unique_fd.h>
#include <cutils/native_handle.h>
#include <utils/Errors.h>
@@ -27,7 +29,6 @@
#include <utils/String16.h>
#include <utils/Vector.h>
#include <utils/Flattenable.h>
-#include <linux/android/binder.h>
#include <binder/IInterface.h>
#include <binder/Parcelable.h>
diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h
index 224cb36..8a1f7e2 100644
--- a/libs/binder/include/binder/ProcessState.h
+++ b/libs/binder/include/binder/ProcessState.h
@@ -36,6 +36,8 @@
public:
static sp<ProcessState> self();
static sp<ProcessState> selfOrNull();
+ // Note: don't call self() or selfOrNull() before initWithMmapSize()
+ static sp<ProcessState> initWithMmapSize(size_t mmapSize); // size in bytes
/* initWithDriver() can be used to configure libbinder to use
* a different binder driver dev node. It must be called *before*
@@ -76,6 +78,7 @@
String8 getDriverName();
ssize_t getKernelReferences(size_t count, uintptr_t* buf);
+ size_t getMmapSize();
enum class CallRestriction {
// all calls okay
@@ -92,7 +95,7 @@
private:
friend class IPCThreadState;
- explicit ProcessState(const char* driver);
+ explicit ProcessState(const char* driver, size_t mmap_size);
~ProcessState();
ProcessState(const ProcessState& o);
@@ -135,6 +138,7 @@
String8 mRootDir;
bool mThreadPoolStarted;
volatile int32_t mThreadPoolSeq;
+ const size_t mMmapSize;
CallRestriction mCallRestriction;
};
diff --git a/libs/binder/include/binder/Status.h b/libs/binder/include/binder/Status.h
index c3738f8..7d889b6 100644
--- a/libs/binder/include/binder/Status.h
+++ b/libs/binder/include/binder/Status.h
@@ -22,6 +22,7 @@
#include <binder/Parcel.h>
#include <utils/String8.h>
+#include <string>
namespace android {
namespace binder {
@@ -97,6 +98,8 @@
static Status fromStatusT(status_t status);
+ static std::string exceptionToString(status_t exceptionCode);
+
Status() = default;
~Status() = default;
diff --git a/libs/binder/include/private/binder/Static.h b/libs/binder/include/private/binder/Static.h
index 171be77..f8e0ee5 100644
--- a/libs/binder/include/private/binder/Static.h
+++ b/libs/binder/include/private/binder/Static.h
@@ -21,10 +21,6 @@
#include <binder/IBinder.h>
#include <binder/ProcessState.h>
-#ifndef __ANDROID_VNDK__
-#include <binder/IPermissionController.h>
-#endif
-#include <binder/IServiceManager.h>
namespace android {
@@ -35,12 +31,4 @@
extern Mutex& gProcessMutex;
extern sp<ProcessState> gProcess;
-// For IServiceManager.cpp
-extern Mutex gDefaultServiceManagerLock;
-extern sp<IServiceManager> gDefaultServiceManager;
-#ifndef __ANDROID_VNDK__
-extern sp<IPermissionController> gPermissionController;
-#endif
-extern bool gSystemBootCompleted;
-
} // namespace android
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index a96c9a0..21bef2e 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -39,17 +39,12 @@
],
shared_libs: [
+ "libandroid_runtime_lazy",
"libbase",
"libbinder",
"libutils",
],
- required: [
- // libbinder_ndk may be used by Java and non-Java things. When lower-level things use it,
- // they shouldn't have to take on the cost of loading libandroid_runtime.
- "libandroid_runtime",
- ],
-
header_libs: [
"jni_headers",
],
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index f9c8c8a..bd6886d 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -31,6 +31,7 @@
using ::android::sp;
using ::android::status_t;
using ::android::String16;
+using ::android::String8;
using ::android::wp;
namespace ABBinderTag {
@@ -67,8 +68,6 @@
AIBinder::~AIBinder() {}
bool AIBinder::associateClass(const AIBinder_Class* clazz) {
- using ::android::String8;
-
if (clazz == nullptr) return false;
if (mClazz == clazz) return true;
@@ -119,6 +118,33 @@
return getClass()->getInterfaceDescriptor();
}
+status_t ABBinder::dump(int fd, const ::android::Vector<String16>& args) {
+ AIBinder_onDump onDump = getClass()->onDump;
+
+ if (onDump == nullptr) {
+ return STATUS_OK;
+ }
+
+ // technically UINT32_MAX would be okay here, but INT32_MAX is expected since this may be
+ // null in Java
+ if (args.size() > INT32_MAX) {
+ LOG(ERROR) << "ABBinder::dump received too many arguments: " << args.size();
+ return STATUS_BAD_VALUE;
+ }
+
+ std::vector<String8> utf8Args; // owns memory of utf8s
+ utf8Args.reserve(args.size());
+ std::vector<const char*> utf8Pointers; // what can be passed over NDK API
+ utf8Pointers.reserve(args.size());
+
+ for (size_t i = 0; i < args.size(); i++) {
+ utf8Args.push_back(String8(args[i]));
+ utf8Pointers.push_back(utf8Args[i].c_str());
+ }
+
+ return onDump(this, fd, utf8Pointers.data(), utf8Pointers.size());
+}
+
status_t ABBinder::onTransact(transaction_code_t code, const Parcel& data, Parcel* reply,
binder_flags_t flags) {
if (isUserCommand(code)) {
@@ -232,10 +258,29 @@
return new AIBinder_Class(interfaceDescriptor, onCreate, onDestroy, onTransact);
}
+void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) {
+ CHECK(clazz != nullptr) << "setOnDump requires non-null clazz";
+
+ // this is required to be called before instances are instantiated
+ clazz->onDump = onDump;
+}
+
void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wp<IBinder>& who) {
CHECK(who == mWho);
mOnDied(mCookie);
+
+ sp<AIBinder_DeathRecipient> recipient = mParentRecipient.promote();
+ sp<IBinder> strongWho = who.promote();
+
+ // otherwise this will be cleaned up later with pruneDeadTransferEntriesLocked
+ if (recipient != nullptr && strongWho != nullptr) {
+ status_t result = recipient->unlinkToDeath(strongWho, mCookie);
+ if (result != ::android::DEAD_OBJECT) {
+ LOG(WARNING) << "Unlinking to dead binder resulted in: " << result;
+ }
+ }
+
mWho = nullptr;
}
@@ -244,24 +289,34 @@
CHECK(onDied != nullptr);
}
-binder_status_t AIBinder_DeathRecipient::linkToDeath(AIBinder* binder, void* cookie) {
+void AIBinder_DeathRecipient::pruneDeadTransferEntriesLocked() {
+ mDeathRecipients.erase(std::remove_if(mDeathRecipients.begin(), mDeathRecipients.end(),
+ [](const sp<TransferDeathRecipient>& tdr) {
+ return tdr->getWho() == nullptr;
+ }),
+ mDeathRecipients.end());
+}
+
+binder_status_t AIBinder_DeathRecipient::linkToDeath(sp<IBinder> binder, void* cookie) {
CHECK(binder != nullptr);
std::lock_guard<std::mutex> l(mDeathRecipientsMutex);
sp<TransferDeathRecipient> recipient =
- new TransferDeathRecipient(binder->getBinder(), cookie, mOnDied);
+ new TransferDeathRecipient(binder, cookie, this, mOnDied);
- status_t status = binder->getBinder()->linkToDeath(recipient, cookie, 0 /*flags*/);
+ status_t status = binder->linkToDeath(recipient, cookie, 0 /*flags*/);
if (status != STATUS_OK) {
return PruneStatusT(status);
}
mDeathRecipients.push_back(recipient);
+
+ pruneDeadTransferEntriesLocked();
return STATUS_OK;
}
-binder_status_t AIBinder_DeathRecipient::unlinkToDeath(AIBinder* binder, void* cookie) {
+binder_status_t AIBinder_DeathRecipient::unlinkToDeath(sp<IBinder> binder, void* cookie) {
CHECK(binder != nullptr);
std::lock_guard<std::mutex> l(mDeathRecipientsMutex);
@@ -269,10 +324,10 @@
for (auto it = mDeathRecipients.rbegin(); it != mDeathRecipients.rend(); ++it) {
sp<TransferDeathRecipient> recipient = *it;
- if (recipient->getCookie() == cookie && recipient->getWho() == binder->getBinder()) {
+ if (recipient->getCookie() == cookie && recipient->getWho() == binder) {
mDeathRecipients.erase(it.base() - 1);
- status_t status = binder->getBinder()->unlinkToDeath(recipient, cookie, 0 /*flags*/);
+ status_t status = binder->unlinkToDeath(recipient, cookie, 0 /*flags*/);
if (status != ::android::OK) {
LOG(ERROR) << __func__
<< ": removed reference to death recipient but unlink failed.";
@@ -325,6 +380,30 @@
return PruneStatusT(binder->getBinder()->pingBinder());
}
+binder_status_t AIBinder_dump(AIBinder* binder, int fd, const char** args, uint32_t numArgs) {
+ if (binder == nullptr) {
+ return STATUS_UNEXPECTED_NULL;
+ }
+
+ ABBinder* bBinder = binder->asABBinder();
+ if (bBinder != nullptr) {
+ AIBinder_onDump onDump = binder->getClass()->onDump;
+ if (onDump == nullptr) {
+ return STATUS_OK;
+ }
+ return PruneStatusT(onDump(bBinder, fd, args, numArgs));
+ }
+
+ ::android::Vector<String16> utf16Args;
+ utf16Args.setCapacity(numArgs);
+ for (uint32_t i = 0; i < numArgs; i++) {
+ utf16Args.push(String16(String8(args[i])));
+ }
+
+ status_t status = binder->getBinder()->dump(fd, utf16Args);
+ return PruneStatusT(status);
+}
+
binder_status_t AIBinder_linkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient,
void* cookie) {
if (binder == nullptr || recipient == nullptr) {
@@ -333,7 +412,7 @@
}
// returns binder_status_t
- return recipient->linkToDeath(binder, cookie);
+ return recipient->linkToDeath(binder->getBinder(), cookie);
}
binder_status_t AIBinder_unlinkToDeath(AIBinder* binder, AIBinder_DeathRecipient* recipient,
@@ -344,7 +423,7 @@
}
// returns binder_status_t
- return recipient->unlinkToDeath(binder, cookie);
+ return recipient->unlinkToDeath(binder->getBinder(), cookie);
}
uid_t AIBinder_getCallingUid() {
@@ -498,9 +577,15 @@
LOG(ERROR) << __func__ << ": requires non-null onBinderDied parameter.";
return nullptr;
}
- return new AIBinder_DeathRecipient(onBinderDied);
+ auto ret = new AIBinder_DeathRecipient(onBinderDied);
+ ret->incStrong(nullptr);
+ return ret;
}
void AIBinder_DeathRecipient_delete(AIBinder_DeathRecipient* recipient) {
- delete recipient;
+ if (recipient == nullptr) {
+ return;
+ }
+
+ recipient->decStrong(nullptr);
}
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index 7852298..5cb68c2 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -25,6 +25,7 @@
#include <binder/Binder.h>
#include <binder/IBinder.h>
+#include <utils/Vector.h>
inline bool isUserCommand(transaction_code_t code) {
return code >= FIRST_CALL_TRANSACTION && code <= LAST_CALL_TRANSACTION;
@@ -66,6 +67,7 @@
ABBinder* asABBinder() override { return this; }
const ::android::String16& getInterfaceDescriptor() const override;
+ ::android::status_t dump(int fd, const ::android::Vector<::android::String16>& args) override;
::android::status_t onTransact(uint32_t code, const ::android::Parcel& data,
::android::Parcel* reply, binder_flags_t flags) override;
@@ -106,10 +108,14 @@
const ::android::String16& getInterfaceDescriptor() const { return mInterfaceDescriptor; }
+ // required to be non-null, implemented for every class
const AIBinder_Class_onCreate onCreate;
const AIBinder_Class_onDestroy onDestroy;
const AIBinder_Class_onTransact onTransact;
+ // optional methods for a class
+ AIBinder_onDump onDump;
+
private:
// This must be a String16 since BBinder virtual getInterfaceDescriptor returns a reference to
// one.
@@ -122,13 +128,14 @@
//
// When the AIBinder_DeathRecipient is dropped, so are the actual underlying death recipients. When
// the IBinder dies, only a wp to it is kept.
-struct AIBinder_DeathRecipient {
+struct AIBinder_DeathRecipient : ::android::RefBase {
// One of these is created for every linkToDeath. This is to be able to recover data when a
// binderDied receipt only gives us information about the IBinder.
struct TransferDeathRecipient : ::android::IBinder::DeathRecipient {
TransferDeathRecipient(const ::android::wp<::android::IBinder>& who, void* cookie,
- const AIBinder_DeathRecipient_onBinderDied& onDied)
- : mWho(who), mCookie(cookie), mOnDied(onDied) {}
+ const ::android::wp<AIBinder_DeathRecipient>& parentRecipient,
+ const AIBinder_DeathRecipient_onBinderDied onDied)
+ : mWho(who), mCookie(cookie), mParentRecipient(parentRecipient), mOnDied(onDied) {}
void binderDied(const ::android::wp<::android::IBinder>& who) override;
@@ -138,14 +145,24 @@
private:
::android::wp<::android::IBinder> mWho;
void* mCookie;
- const AIBinder_DeathRecipient_onBinderDied& mOnDied;
+
+ ::android::wp<AIBinder_DeathRecipient> mParentRecipient;
+
+ // This is kept separately from AIBinder_DeathRecipient in case the death recipient is
+ // deleted while the death notification is fired
+ const AIBinder_DeathRecipient_onBinderDied mOnDied;
};
explicit AIBinder_DeathRecipient(AIBinder_DeathRecipient_onBinderDied onDied);
- binder_status_t linkToDeath(AIBinder* binder, void* cookie);
- binder_status_t unlinkToDeath(AIBinder* binder, void* cookie);
+ binder_status_t linkToDeath(::android::sp<::android::IBinder>, void* cookie);
+ binder_status_t unlinkToDeath(::android::sp<::android::IBinder> binder, void* cookie);
private:
+ // When the user of this API deletes a Bp object but not the death recipient, the
+ // TransferDeathRecipient object can't be cleaned up. This is called whenever a new
+ // TransferDeathRecipient is linked, and it ensures that mDeathRecipients can't grow unbounded.
+ void pruneDeadTransferEntriesLocked();
+
std::mutex mDeathRecipientsMutex;
std::vector<::android::sp<TransferDeathRecipient>> mDeathRecipients;
AIBinder_DeathRecipient_onBinderDied mOnDied;
diff --git a/libs/binder/ndk/ibinder_jni.cpp b/libs/binder/ndk/ibinder_jni.cpp
index 4a31080..d931785 100644
--- a/libs/binder/ndk/ibinder_jni.cpp
+++ b/libs/binder/ndk/ibinder_jni.cpp
@@ -17,69 +17,19 @@
#include <android/binder_ibinder_jni.h>
#include "ibinder_internal.h"
-#include <android-base/logging.h>
-#include <binder/IBinder.h>
-
-#include <mutex>
-
-#include <dlfcn.h>
+#include <android_util_Binder.h>
using ::android::IBinder;
+using ::android::ibinderForJavaObject;
+using ::android::javaObjectForIBinder;
using ::android::sp;
-struct LazyAndroidRuntime {
- typedef sp<IBinder> (*FromJava)(JNIEnv* env, jobject obj);
- typedef jobject (*ToJava)(JNIEnv* env, const sp<IBinder>& val);
-
- static FromJava ibinderForJavaObject;
- static ToJava javaObjectForIBinder;
-
- static void load() {
- std::call_once(mLoadFlag, []() {
- void* handle = dlopen("libandroid_runtime.so", RTLD_LAZY);
- if (handle == nullptr) {
- LOG(WARNING) << "Could not open libandroid_runtime.";
- return;
- }
-
- ibinderForJavaObject = reinterpret_cast<FromJava>(
- dlsym(handle, "_ZN7android20ibinderForJavaObjectEP7_JNIEnvP8_jobject"));
- if (ibinderForJavaObject == nullptr) {
- LOG(WARNING) << "Could not find ibinderForJavaObject.";
- // no return
- }
-
- javaObjectForIBinder = reinterpret_cast<ToJava>(dlsym(
- handle, "_ZN7android20javaObjectForIBinderEP7_JNIEnvRKNS_2spINS_7IBinderEEE"));
- if (javaObjectForIBinder == nullptr) {
- LOG(WARNING) << "Could not find javaObjectForIBinder.";
- // no return
- }
- });
- }
-
- private:
- static std::once_flag mLoadFlag;
-
- LazyAndroidRuntime(){};
-};
-
-LazyAndroidRuntime::FromJava LazyAndroidRuntime::ibinderForJavaObject = nullptr;
-LazyAndroidRuntime::ToJava LazyAndroidRuntime::javaObjectForIBinder = nullptr;
-std::once_flag LazyAndroidRuntime::mLoadFlag;
-
AIBinder* AIBinder_fromJavaBinder(JNIEnv* env, jobject binder) {
if (binder == nullptr) {
return nullptr;
}
- LazyAndroidRuntime::load();
- if (LazyAndroidRuntime::ibinderForJavaObject == nullptr) {
- return nullptr;
- }
-
- sp<IBinder> ibinder = (LazyAndroidRuntime::ibinderForJavaObject)(env, binder);
-
+ sp<IBinder> ibinder = ibinderForJavaObject(env, binder);
sp<AIBinder> cbinder = ABpBinder::lookupOrCreateFromBinder(ibinder);
AIBinder_incStrong(cbinder.get());
@@ -91,10 +41,5 @@
return nullptr;
}
- LazyAndroidRuntime::load();
- if (LazyAndroidRuntime::javaObjectForIBinder == nullptr) {
- return nullptr;
- }
-
- return (LazyAndroidRuntime::javaObjectForIBinder)(env, binder->getBinder());
+ return javaObjectForIBinder(env, binder->getBinder());
}
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
index 9c6c55e..80d1254 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h
@@ -179,6 +179,31 @@
__INTRODUCED_IN(29);
/**
+ * Dump information about an AIBinder (usually for debugging).
+ *
+ * When no arguments are provided, a brief overview of the interview should be given.
+ *
+ * \param binder interface being dumped
+ * \param fd file descriptor to be dumped to, should be flushed, ownership is not passed.
+ * \param args array of null-terminated strings for dump (may be null if numArgs is 0)
+ * \param numArgs number of args to be sent
+ *
+ * \return binder_status_t result of transaction (if remote, for instance)
+ */
+typedef binder_status_t (*AIBinder_onDump)(AIBinder* binder, int fd, const char** args,
+ uint32_t numArgs);
+
+/**
+ * This sets the implementation of the dump method for a class.
+ *
+ * If this isn't set, nothing will be dumped when dump is called (for instance with
+ * android.os.Binder#dump). Must be called before any instance of the class is created.
+ *
+ * \param dump function to call when an instance of this binder class is being dumped.
+ */
+void AIBinder_Class_setOnDump(AIBinder_Class* clazz, AIBinder_onDump onDump) __INTRODUCED_IN(29);
+
+/**
* Creates a new binder object of the appropriate class.
*
* Ownership of args is passed to this object. The lifecycle is implemented with AIBinder_incStrong
@@ -237,6 +262,21 @@
binder_status_t AIBinder_ping(AIBinder* binder) __INTRODUCED_IN(29);
/**
+ * Built-in transaction for all binder objects. This dumps information about a given binder.
+ *
+ * See also AIBinder_Class_setOnDump, AIBinder_onDump
+ *
+ * \param binder the binder to dump information about
+ * \param fd where information should be dumped to
+ * \param args null-terminated arguments to pass (may be null if numArgs is 0)
+ * \param numArgs number of args to send
+ *
+ * \return STATUS_OK if dump succeeds (or if there is nothing to dump)
+ */
+binder_status_t AIBinder_dump(AIBinder* binder, int fd, const char** args, uint32_t numArgs)
+ __INTRODUCED_IN(29);
+
+/**
* Registers for notifications that the associated binder is dead. The same death recipient may be
* associated with multiple different binders. If the binder is local, then no death recipient will
* be given (since if the local process dies, then no recipient will exist to recieve a
@@ -261,6 +301,11 @@
* may return a binder transaction failure and in case the death recipient cannot be found, it
* returns STATUS_NAME_NOT_FOUND.
*
+ * This only ever needs to be called when the AIBinder_DeathRecipient remains for use with other
+ * AIBinder objects. If the death recipient is deleted, all binders will automatically be unlinked.
+ * If the binder dies, it will automatically unlink. If the binder is deleted, it will be
+ * automatically unlinked.
+ *
* \param binder the binder object to remove a previously linked death recipient from.
* \param recipient the callback to remove.
* \param cookie the cookie used to link to death.
diff --git a/libs/binder/ndk/include_ndk/android/binder_interface_utils.h b/libs/binder/ndk/include_ndk/android/binder_interface_utils.h
index a42c60b..83a1048 100644
--- a/libs/binder/ndk/include_ndk/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_ndk/android/binder_interface_utils.h
@@ -104,6 +104,39 @@
* this will be checked using AIBinder_isRemote.
*/
virtual bool isRemote() = 0;
+
+ /**
+ * Dumps information about the interface. By default, dumps nothing.
+ */
+ virtual inline binder_status_t dump(int /*fd*/, const char** /*args*/, uint32_t /*numArgs*/);
+
+ /**
+ * Interprets this binder as this underlying interface if this has stored an ICInterface in the
+ * binder's user data.
+ *
+ * This does not do type checking and should only be used when the binder is known to originate
+ * from ICInterface. Most likely, you want to use I*::fromBinder.
+ */
+ static inline std::shared_ptr<ICInterface> asInterface(AIBinder* binder);
+
+ /**
+ * Helper method to create a class
+ */
+ static inline AIBinder_Class* defineClass(const char* interfaceDescriptor,
+ AIBinder_Class_onTransact onTransact);
+
+ private:
+ class ICInterfaceData {
+ public:
+ std::shared_ptr<ICInterface> interface;
+
+ static inline std::shared_ptr<ICInterface> getInterface(AIBinder* binder);
+
+ static inline void* onCreate(void* args);
+ static inline void onDestroy(void* userData);
+ static inline binder_status_t onDump(AIBinder* binder, int fd, const char** args,
+ uint32_t numArgs);
+ };
};
/**
@@ -117,7 +150,7 @@
SpAIBinder asBinder() override;
- bool isRemote() override { return true; }
+ bool isRemote() override { return false; }
protected:
/**
@@ -144,10 +177,63 @@
bool isRemote() override { return AIBinder_isRemote(mBinder.get()); }
+ binder_status_t dump(int fd, const char** args, uint32_t numArgs) override {
+ return AIBinder_dump(asBinder().get(), fd, args, numArgs);
+ }
+
private:
SpAIBinder mBinder;
};
+// END OF CLASS DECLARATIONS
+
+binder_status_t ICInterface::dump(int /*fd*/, const char** /*args*/, uint32_t /*numArgs*/) {
+ return STATUS_OK;
+}
+
+std::shared_ptr<ICInterface> ICInterface::asInterface(AIBinder* binder) {
+ return ICInterfaceData::getInterface(binder);
+}
+
+AIBinder_Class* ICInterface::defineClass(const char* interfaceDescriptor,
+ AIBinder_Class_onTransact onTransact) {
+ AIBinder_Class* clazz = AIBinder_Class_define(interfaceDescriptor, ICInterfaceData::onCreate,
+ ICInterfaceData::onDestroy, onTransact);
+ if (clazz == nullptr) {
+ return nullptr;
+ }
+
+ // We can't know if this method is overriden by a subclass interface, so we must register
+ // ourselves. The default (nothing to dump) is harmless.
+ AIBinder_Class_setOnDump(clazz, ICInterfaceData::onDump);
+ return clazz;
+}
+
+std::shared_ptr<ICInterface> ICInterface::ICInterfaceData::getInterface(AIBinder* binder) {
+ if (binder == nullptr) return nullptr;
+
+ void* userData = AIBinder_getUserData(binder);
+ if (userData == nullptr) return nullptr;
+
+ return static_cast<ICInterfaceData*>(userData)->interface;
+}
+
+void* ICInterface::ICInterfaceData::onCreate(void* args) {
+ std::shared_ptr<ICInterface> interface = static_cast<ICInterface*>(args)->ref<ICInterface>();
+ ICInterfaceData* data = new ICInterfaceData{interface};
+ return static_cast<void*>(data);
+}
+
+void ICInterface::ICInterfaceData::onDestroy(void* userData) {
+ delete static_cast<ICInterfaceData*>(userData);
+}
+
+binder_status_t ICInterface::ICInterfaceData::onDump(AIBinder* binder, int fd, const char** args,
+ uint32_t numArgs) {
+ std::shared_ptr<ICInterface> interface = getInterface(binder);
+ return interface->dump(fd, args, numArgs);
+}
+
template <typename INTERFACE>
SpAIBinder BnCInterface<INTERFACE>::asBinder() {
std::lock_guard<std::mutex> l(mMutex);
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 655f4d5..7e65817 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -2,10 +2,12 @@
global:
AIBinder_associateClass;
AIBinder_Class_define;
+ AIBinder_Class_setOnDump;
AIBinder_DeathRecipient_delete;
AIBinder_DeathRecipient_new;
AIBinder_debugGetRefCount;
AIBinder_decStrong;
+ AIBinder_dump;
AIBinder_fromJavaBinder;
AIBinder_getCallingPid;
AIBinder_getCallingUid;
diff --git a/libs/binder/ndk/test/Android.bp b/libs/binder/ndk/test/Android.bp
index b29b6e7..8cd4e03 100644
--- a/libs/binder/ndk/test/Android.bp
+++ b/libs/binder/ndk/test/Android.bp
@@ -41,7 +41,7 @@
name: "test_libbinder_ndk_test_defaults",
defaults: ["test_libbinder_ndk_defaults"],
shared_libs: [
- "libandroid_runtime",
+ "libandroid_runtime_lazy",
"libbase",
"libbinder",
"libutils",
diff --git a/libs/binder/ndk/test/main_client.cpp b/libs/binder/ndk/test/main_client.cpp
index bff601e..8467734 100644
--- a/libs/binder/ndk/test/main_client.cpp
+++ b/libs/binder/ndk/test/main_client.cpp
@@ -86,12 +86,15 @@
// the binder driver should return this if the service dies during the transaction
EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die());
+ foo = nullptr;
+ AIBinder_decStrong(binder);
+ binder = nullptr;
+
std::unique_lock<std::mutex> lock(deathMutex);
EXPECT_TRUE(deathCv.wait_for(lock, 1s, [&] { return deathRecieved; }));
EXPECT_TRUE(deathRecieved);
AIBinder_DeathRecipient_delete(recipient);
- AIBinder_decStrong(binder);
}
TEST(NdkBinder, RetrieveNonNdkService) {
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 35296a9..d0cea0b 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -31,6 +31,7 @@
"/system/bin/mediaextractor", // media.extractor
"/system/bin/mediametrics", // media.metrics
"/system/bin/mediaserver",
+ "/system/bin/netd",
"/system/bin/sdcard",
"/system/bin/statsd",
"/system/bin/surfaceflinger",
diff --git a/libs/graphicsenv/Android.bp b/libs/graphicsenv/Android.bp
index 4da30e9..52a41ff 100644
--- a/libs/graphicsenv/Android.bp
+++ b/libs/graphicsenv/Android.bp
@@ -22,6 +22,7 @@
cflags: ["-Wall", "-Werror"],
shared_libs: [
+ "libdl_android",
"liblog",
],
diff --git a/libs/gui/IGraphicBufferProducer.cpp b/libs/gui/IGraphicBufferProducer.cpp
index 74ab5ac..60af8b5 100644
--- a/libs/gui/IGraphicBufferProducer.cpp
+++ b/libs/gui/IGraphicBufferProducer.cpp
@@ -355,7 +355,7 @@
data.writeUint32(height);
data.writeInt32(static_cast<int32_t>(format));
data.writeUint64(usage);
- status_t result = remote()->transact(ALLOCATE_BUFFERS, data, &reply, TF_ONE_WAY);
+ status_t result = remote()->transact(ALLOCATE_BUFFERS, data, &reply, IBinder::FLAG_ONEWAY);
if (result != NO_ERROR) {
ALOGE("allocateBuffers failed to transact: %d", result);
}
diff --git a/libs/gui/ISurfaceComposer.cpp b/libs/gui/ISurfaceComposer.cpp
index d2d27e8..76d242d 100644
--- a/libs/gui/ISurfaceComposer.cpp
+++ b/libs/gui/ISurfaceComposer.cpp
@@ -504,8 +504,8 @@
ALOGE("enableVSyncInjections failed to writeBool: %d", result);
return result;
}
- result = remote()->transact(BnSurfaceComposer::ENABLE_VSYNC_INJECTIONS,
- data, &reply, TF_ONE_WAY);
+ result = remote()->transact(BnSurfaceComposer::ENABLE_VSYNC_INJECTIONS, data, &reply,
+ IBinder::FLAG_ONEWAY);
if (result != NO_ERROR) {
ALOGE("enableVSyncInjections failed to transact: %d", result);
return result;
@@ -525,7 +525,8 @@
ALOGE("injectVSync failed to writeInt64: %d", result);
return result;
}
- result = remote()->transact(BnSurfaceComposer::INJECT_VSYNC, data, &reply, TF_ONE_WAY);
+ result = remote()->transact(BnSurfaceComposer::INJECT_VSYNC, data, &reply,
+ IBinder::FLAG_ONEWAY);
if (result != NO_ERROR) {
ALOGE("injectVSync failed to transact: %d", result);
return result;
diff --git a/libs/gui/include/gui/ISurfaceComposer.h b/libs/gui/include/gui/ISurfaceComposer.h
index 99a3a75..5f79804 100644
--- a/libs/gui/include/gui/ISurfaceComposer.h
+++ b/libs/gui/include/gui/ISurfaceComposer.h
@@ -174,8 +174,25 @@
virtual status_t setActiveColorMode(const sp<IBinder>& display,
ui::ColorMode colorMode) = 0;
- /* Capture the specified screen. requires READ_FRAME_BUFFER permission
- * This function will fail if there is a secure window on screen.
+ /**
+ * Capture the specified screen. This requires READ_FRAME_BUFFER
+ * permission. This function will fail if there is a secure window on
+ * screen.
+ *
+ * This function can capture a subregion (the source crop) of the screen.
+ * The subregion can be optionally rotated. It will also be scaled to
+ * match the size of the output buffer.
+ *
+ * At the moment, sourceCrop is ignored and is always set to the visible
+ * region (projected display viewport) of the screen.
+ *
+ * reqWidth and reqHeight specifies the size of the buffer. When either
+ * of them is 0, they are set to the size of the logical display viewport.
+ *
+ * When useIdentityTransform is true, layer transformations are disabled.
+ *
+ * rotation specifies the rotation of the source crop (and the pixels in
+ * it) around its center.
*/
virtual status_t captureScreen(const sp<IBinder>& display, sp<GraphicBuffer>* outBuffer,
Rect sourceCrop, uint32_t reqWidth, uint32_t reqHeight,
diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h
index 788962e..38de701 100644
--- a/libs/gui/include/gui/LayerState.h
+++ b/libs/gui/include/gui/LayerState.h
@@ -152,10 +152,24 @@
sp<IBinder> token;
sp<IGraphicBufferProducer> surface;
uint32_t layerStack;
+
+ // These states define how layers are projected onto the physical display.
+ //
+ // Layers are first clipped to `viewport'. They are then translated and
+ // scaled from `viewport' to `frame'. Finally, they are rotated according
+ // to `orientation', `width', and `height'.
+ //
+ // For example, assume viewport is Rect(0, 0, 200, 100), frame is Rect(20,
+ // 10, 420, 210), and the size of the display is WxH. When orientation is
+ // 0, layers will be scaled by a factor of 2 and translated by (20, 10).
+ // When orientation is 1, layers will be additionally rotated by 90
+ // degrees around the origin clockwise and translated by (W, 0).
uint32_t orientation;
Rect viewport;
Rect frame;
+
uint32_t width, height;
+
status_t write(Parcel& output) const;
status_t read(const Parcel& input);
};
diff --git a/libs/input/InputTransport.cpp b/libs/input/InputTransport.cpp
index aa0bf17..03f593f 100644
--- a/libs/input/InputTransport.cpp
+++ b/libs/input/InputTransport.cpp
@@ -96,6 +96,106 @@
return sizeof(Header);
}
+/**
+ * There could be non-zero bytes in-between InputMessage fields. Force-initialize the entire
+ * memory to zero, then only copy the valid bytes on a per-field basis.
+ */
+void InputMessage::getSanitizedCopy(InputMessage* msg) const {
+ memset(msg, 0, sizeof(*msg));
+
+ // Write the header
+ msg->header.type = header.type;
+
+ // Write the body
+ switch(header.type) {
+ case InputMessage::TYPE_KEY: {
+ // uint32_t seq
+ msg->body.key.seq = body.key.seq;
+ // nsecs_t eventTime
+ msg->body.key.eventTime = body.key.eventTime;
+ // int32_t deviceId
+ msg->body.key.deviceId = body.key.deviceId;
+ // int32_t source
+ msg->body.key.source = body.key.source;
+ // int32_t displayId
+ msg->body.key.displayId = body.key.displayId;
+ // int32_t action
+ msg->body.key.action = body.key.action;
+ // int32_t flags
+ msg->body.key.flags = body.key.flags;
+ // int32_t keyCode
+ msg->body.key.keyCode = body.key.keyCode;
+ // int32_t scanCode
+ msg->body.key.scanCode = body.key.scanCode;
+ // int32_t metaState
+ msg->body.key.metaState = body.key.metaState;
+ // int32_t repeatCount
+ msg->body.key.repeatCount = body.key.repeatCount;
+ // nsecs_t downTime
+ msg->body.key.downTime = body.key.downTime;
+ break;
+ }
+ case InputMessage::TYPE_MOTION: {
+ // uint32_t seq
+ msg->body.motion.seq = body.motion.seq;
+ // nsecs_t eventTime
+ msg->body.motion.eventTime = body.motion.eventTime;
+ // int32_t deviceId
+ msg->body.motion.deviceId = body.motion.deviceId;
+ // int32_t source
+ msg->body.motion.source = body.motion.source;
+ // int32_t displayId
+ msg->body.motion.displayId = body.motion.displayId;
+ // int32_t action
+ msg->body.motion.action = body.motion.action;
+ // int32_t actionButton
+ msg->body.motion.actionButton = body.motion.actionButton;
+ // int32_t flags
+ msg->body.motion.flags = body.motion.flags;
+ // int32_t metaState
+ msg->body.motion.metaState = body.motion.metaState;
+ // int32_t buttonState
+ msg->body.motion.buttonState = body.motion.buttonState;
+ // int32_t edgeFlags
+ msg->body.motion.edgeFlags = body.motion.edgeFlags;
+ // nsecs_t downTime
+ msg->body.motion.downTime = body.motion.downTime;
+ // float xOffset
+ msg->body.motion.xOffset = body.motion.xOffset;
+ // float yOffset
+ msg->body.motion.yOffset = body.motion.yOffset;
+ // float xPrecision
+ msg->body.motion.xPrecision = body.motion.xPrecision;
+ // float yPrecision
+ msg->body.motion.yPrecision = body.motion.yPrecision;
+ // uint32_t pointerCount
+ msg->body.motion.pointerCount = body.motion.pointerCount;
+ //struct Pointer pointers[MAX_POINTERS]
+ for (size_t i = 0; i < body.motion.pointerCount; i++) {
+ // PointerProperties properties
+ msg->body.motion.pointers[i].properties.id = body.motion.pointers[i].properties.id;
+ msg->body.motion.pointers[i].properties.toolType =
+ body.motion.pointers[i].properties.toolType,
+ // PointerCoords coords
+ msg->body.motion.pointers[i].coords.bits = body.motion.pointers[i].coords.bits;
+ const uint32_t count = BitSet64::count(body.motion.pointers[i].coords.bits);
+ memcpy(&msg->body.motion.pointers[i].coords.values[0],
+ &body.motion.pointers[i].coords.values[0],
+ count * (sizeof(body.motion.pointers[i].coords.values[0])));
+ }
+ break;
+ }
+ case InputMessage::TYPE_FINISHED: {
+ msg->body.finished.seq = body.finished.seq;
+ msg->body.finished.handled = body.finished.handled;
+ break;
+ }
+ default: {
+ LOG_FATAL("Unexpected message type %i", header.type);
+ break;
+ }
+ }
+}
// --- InputChannel ---
@@ -149,10 +249,12 @@
}
status_t InputChannel::sendMessage(const InputMessage* msg) {
- size_t msgLength = msg->size();
+ const size_t msgLength = msg->size();
+ InputMessage cleanMsg;
+ msg->getSanitizedCopy(&cleanMsg);
ssize_t nWrite;
do {
- nWrite = ::send(mFd, msg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
+ nWrite = ::send(mFd, &cleanMsg, msgLength, MSG_DONTWAIT | MSG_NOSIGNAL);
} while (nWrite == -1 && errno == EINTR);
if (nWrite < 0) {
diff --git a/libs/input/tests/StructLayout_test.cpp b/libs/input/tests/StructLayout_test.cpp
index d19f3b8..12a6782 100644
--- a/libs/input/tests/StructLayout_test.cpp
+++ b/libs/input/tests/StructLayout_test.cpp
@@ -65,6 +65,9 @@
CHECK_OFFSET(InputMessage::Body::Motion, yPrecision, 76);
CHECK_OFFSET(InputMessage::Body::Motion, pointerCount, 80);
CHECK_OFFSET(InputMessage::Body::Motion, pointers, 88);
+
+ CHECK_OFFSET(InputMessage::Body::Finished, seq, 0);
+ CHECK_OFFSET(InputMessage::Body::Finished, handled, 4);
}
} // namespace android
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index d25ad1a..ec7f927 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -45,6 +45,8 @@
// Don't warn about struct padding
"-Wno-padded",
+
+ "-Wno-switch-enum",
],
sanitize: {
@@ -63,6 +65,7 @@
"GraphicBufferMapper.cpp",
"HdrCapabilities.cpp",
"PixelFormat.cpp",
+ "PublicFormat.cpp",
"Rect.cpp",
"Region.cpp",
"UiConfig.cpp",
diff --git a/libs/ui/PublicFormat.cpp b/libs/ui/PublicFormat.cpp
new file mode 100644
index 0000000..26eb771
--- /dev/null
+++ b/libs/ui/PublicFormat.cpp
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2019 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 <ui/PublicFormat.h>
+
+// ----------------------------------------------------------------------------
+namespace android {
+// ----------------------------------------------------------------------------
+
+int mapPublicFormatToHalFormat(PublicFormat f) {
+ switch (f) {
+ case PublicFormat::JPEG:
+ case PublicFormat::DEPTH_POINT_CLOUD:
+ return HAL_PIXEL_FORMAT_BLOB;
+ case PublicFormat::DEPTH16:
+ return HAL_PIXEL_FORMAT_Y16;
+ case PublicFormat::RAW_SENSOR:
+ case PublicFormat::RAW_DEPTH:
+ return HAL_PIXEL_FORMAT_RAW16;
+ default:
+ // Most formats map 1:1
+ return static_cast<int>(f);
+ }
+}
+
+android_dataspace mapPublicFormatToHalDataspace(PublicFormat f) {
+ switch (f) {
+ case PublicFormat::JPEG:
+ return HAL_DATASPACE_V0_JFIF;
+ case PublicFormat::DEPTH_POINT_CLOUD:
+ case PublicFormat::DEPTH16:
+ case PublicFormat::RAW_DEPTH:
+ return HAL_DATASPACE_DEPTH;
+ case PublicFormat::RAW_SENSOR:
+ case PublicFormat::RAW_PRIVATE:
+ case PublicFormat::RAW10:
+ case PublicFormat::RAW12:
+ return HAL_DATASPACE_ARBITRARY;
+ case PublicFormat::YUV_420_888:
+ case PublicFormat::NV21:
+ case PublicFormat::YV12:
+ return HAL_DATASPACE_V0_JFIF;
+ default:
+ // Most formats map to UNKNOWN
+ return HAL_DATASPACE_UNKNOWN;
+ }
+}
+
+PublicFormat mapHalFormatDataspaceToPublicFormat(int format, android_dataspace dataSpace) {
+ switch (format) {
+ case HAL_PIXEL_FORMAT_RGBA_8888:
+ case HAL_PIXEL_FORMAT_RGBX_8888:
+ case HAL_PIXEL_FORMAT_RGBA_FP16:
+ case HAL_PIXEL_FORMAT_RGBA_1010102:
+ case HAL_PIXEL_FORMAT_RGB_888:
+ case HAL_PIXEL_FORMAT_RGB_565:
+ case HAL_PIXEL_FORMAT_Y8:
+ case HAL_PIXEL_FORMAT_RAW10:
+ case HAL_PIXEL_FORMAT_RAW12:
+ case HAL_PIXEL_FORMAT_YCbCr_420_888:
+ case HAL_PIXEL_FORMAT_YV12:
+ // Enums overlap in both name and value
+ return static_cast<PublicFormat>(format);
+ case HAL_PIXEL_FORMAT_RAW16:
+ switch (dataSpace) {
+ case HAL_DATASPACE_DEPTH:
+ return PublicFormat::RAW_DEPTH;
+ default:
+ return PublicFormat::RAW_SENSOR;
+ }
+ case HAL_PIXEL_FORMAT_RAW_OPAQUE:
+ // Name differs, though value is the same
+ return PublicFormat::RAW_PRIVATE;
+ case HAL_PIXEL_FORMAT_YCbCr_422_SP:
+ // Name differs, though the value is the same
+ return PublicFormat::NV16;
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ // Name differs, though the value is the same
+ return PublicFormat::NV21;
+ case HAL_PIXEL_FORMAT_YCbCr_422_I:
+ // Name differs, though the value is the same
+ return PublicFormat::YUY2;
+ case HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED:
+ // Name differs, though the value is the same
+ return PublicFormat::PRIVATE;
+ case HAL_PIXEL_FORMAT_Y16:
+ // Dataspace-dependent
+ switch (dataSpace) {
+ case HAL_DATASPACE_DEPTH:
+ return PublicFormat::DEPTH16;
+ default:
+ // Assume non-depth Y16 is just Y16.
+ return PublicFormat::Y16;
+ }
+ case HAL_PIXEL_FORMAT_BLOB:
+ // Dataspace-dependent
+ switch (dataSpace) {
+ case HAL_DATASPACE_DEPTH:
+ return PublicFormat::DEPTH_POINT_CLOUD;
+ case HAL_DATASPACE_V0_JFIF:
+ return PublicFormat::JPEG;
+ default:
+ // Assume otherwise-marked blobs are also JPEG
+ return PublicFormat::JPEG;
+ }
+ case HAL_PIXEL_FORMAT_BGRA_8888:
+ // Not defined in public API
+ return PublicFormat::UNKNOWN;
+
+ default:
+ return PublicFormat::UNKNOWN;
+ }
+}
+
+// ----------------------------------------------------------------------------
+}; // namespace android
+// ----------------------------------------------------------------------------
diff --git a/libs/ui/include/ui/PublicFormat.h b/libs/ui/include/ui/PublicFormat.h
new file mode 100644
index 0000000..75b6705
--- /dev/null
+++ b/libs/ui/include/ui/PublicFormat.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2019 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 UI_PUBLICFORMAT_H
+#define UI_PUBLICFORMAT_H
+
+#include <system/graphics.h>
+
+namespace android {
+
+/**
+ * Enum mirroring the public API definitions for image and pixel formats.
+ * Some of these are hidden in the public API
+ *
+ * Keep up to date with android.graphics.ImageFormat and
+ * android.graphics.PixelFormat
+ *
+ * TODO: PublicFormat is going to be deprecated(b/126594675)
+ */
+enum class PublicFormat {
+ UNKNOWN = 0x0,
+ RGBA_8888 = 0x1,
+ RGBX_8888 = 0x2,
+ RGB_888 = 0x3,
+ RGB_565 = 0x4,
+ NV16 = 0x10,
+ NV21 = 0x11,
+ YUY2 = 0x14,
+ RGBA_FP16 = 0x16,
+ RAW_SENSOR = 0x20,
+ PRIVATE = 0x22,
+ YUV_420_888 = 0x23,
+ RAW_PRIVATE = 0x24,
+ RAW10 = 0x25,
+ RAW12 = 0x26,
+ RGBA_1010102 = 0x2b,
+ JPEG = 0x100,
+ DEPTH_POINT_CLOUD = 0x101,
+ RAW_DEPTH = 0x1002, // @hide
+ YV12 = 0x32315659,
+ Y8 = 0x20203859, // @hide
+ Y16 = 0x20363159, // @hide
+ DEPTH16 = 0x44363159
+};
+
+/* Convert from android.graphics.ImageFormat/PixelFormat enums to graphics.h HAL
+ * format */
+extern int mapPublicFormatToHalFormat(PublicFormat f);
+
+/* Convert from android.graphics.ImageFormat/PixelFormat enums to graphics.h HAL
+ * dataspace */
+extern android_dataspace mapPublicFormatToHalDataspace(PublicFormat f);
+
+/* Convert from HAL format, dataspace pair to
+ * android.graphics.ImageFormat/PixelFormat.
+ * For unknown/unspecified pairs, returns PublicFormat::UNKNOWN */
+extern PublicFormat mapHalFormatDataspaceToPublicFormat(int format, android_dataspace dataSpace);
+
+}; // namespace android
+
+#endif // UI_PUBLICFORMAT_H
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index e824238..36deedc 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -552,6 +552,15 @@
break;
}
}
+
+ // If the driver doesn't understand it, we should map sRGB-encoded P3 to
+ // sRGB rather than just dropping the colorspace on the floor.
+ // For this format, the driver is expected to apply the sRGB
+ // transfer function during framebuffer operations.
+ if (!copyAttribute && attr[1] == EGL_GL_COLORSPACE_DISPLAY_P3_EXT) {
+ strippedAttribList->push_back(attr[0]);
+ strippedAttribList->push_back(EGL_GL_COLORSPACE_SRGB_KHR);
+ }
}
if (copyAttribute) {
strippedAttribList->push_back(attr[0]);
diff --git a/services/surfaceflinger/BufferLayer.cpp b/services/surfaceflinger/BufferLayer.cpp
index f5b5eda..707cb42 100644
--- a/services/surfaceflinger/BufferLayer.cpp
+++ b/services/surfaceflinger/BufferLayer.cpp
@@ -204,7 +204,7 @@
if (!blackOutLayer) {
// TODO: we could be more subtle with isFixedSize()
- const bool useFiltering = getFiltering() || needsFiltering(renderArea) || isFixedSize();
+ const bool useFiltering = needsFiltering(renderArea) || isFixedSize();
// Query the texture matrix given our current filtering mode.
float textureMatrix[16];
diff --git a/services/surfaceflinger/BufferLayerConsumer.cpp b/services/surfaceflinger/BufferLayerConsumer.cpp
index 87333d0..ae8ebf0 100644
--- a/services/surfaceflinger/BufferLayerConsumer.cpp
+++ b/services/surfaceflinger/BufferLayerConsumer.cpp
@@ -435,7 +435,9 @@
BLC_LOGD("computeCurrentTransformMatrixLocked: "
"mCurrentTextureImage is nullptr");
}
- const Rect& cropRect = canUseImageCrop(mCurrentCrop) ? Rect::EMPTY_RECT : mCurrentCrop;
+
+ const Rect& currentCrop = getCurrentCropLocked();
+ const Rect& cropRect = canUseImageCrop(currentCrop) ? Rect::EMPTY_RECT : currentCrop;
GLConsumer::computeTransformMatrix(mCurrentTransformMatrix, buf, cropRect, mCurrentTransform,
mFilteringEnabled);
}
@@ -490,6 +492,10 @@
Rect BufferLayerConsumer::getCurrentCrop() const {
Mutex::Autolock lock(mMutex);
+ return getCurrentCropLocked();
+}
+
+Rect BufferLayerConsumer::getCurrentCropLocked() const {
return (mCurrentScalingMode == NATIVE_WINDOW_SCALING_MODE_SCALE_CROP)
? GLConsumer::scaleDownCrop(mCurrentCrop, mDefaultWidth, mDefaultHeight)
: mCurrentCrop;
diff --git a/services/surfaceflinger/BufferLayerConsumer.h b/services/surfaceflinger/BufferLayerConsumer.h
index f81cdb1..84404c7 100644
--- a/services/surfaceflinger/BufferLayerConsumer.h
+++ b/services/surfaceflinger/BufferLayerConsumer.h
@@ -274,6 +274,9 @@
// mCurrentTextureImage must not be nullptr.
void computeCurrentTransformMatrixLocked();
+ // See getCurrentCrop, but with mMutex already held.
+ Rect getCurrentCropLocked() const;
+
// doFenceWaitLocked inserts a wait command into the RenderEngine command
// stream to ensure that it is safe for future RenderEngine commands to
// access the current texture buffer.
diff --git a/services/surfaceflinger/ContainerLayer.cpp b/services/surfaceflinger/ContainerLayer.cpp
index 6f5bd0e..f259d93 100644
--- a/services/surfaceflinger/ContainerLayer.cpp
+++ b/services/surfaceflinger/ContainerLayer.cpp
@@ -30,6 +30,10 @@
void ContainerLayer::onDraw(const RenderArea&, const Region& /* clip */, bool) const {}
+bool ContainerLayer::isVisible() const {
+ return !isHiddenByPolicy();
+}
+
void ContainerLayer::setPerFrameData(const sp<const DisplayDevice>&) {}
} // namespace android
diff --git a/services/surfaceflinger/ContainerLayer.h b/services/surfaceflinger/ContainerLayer.h
index 38b1882..b352b96 100644
--- a/services/surfaceflinger/ContainerLayer.h
+++ b/services/surfaceflinger/ContainerLayer.h
@@ -32,7 +32,7 @@
const char* getTypeId() const override { return "ContainerLayer"; }
void onDraw(const RenderArea& renderArea, const Region& clip,
bool useIdentityTransform) const override;
- bool isVisible() const override { return false; }
+ bool isVisible() const override;
void setPerFrameData(const sp<const DisplayDevice>& displayDevice) override;
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 309fd0a..65c3839 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -224,6 +224,7 @@
std::unique_ptr<RE::Surface> renderSurface,
int displayWidth,
int displayHeight,
+ int displayInstallOrientation,
bool hasWideColorGamut,
const HdrCapabilities& hdrCapabilities,
const int32_t supportedPerFrameMetadata,
@@ -239,6 +240,7 @@
mSurface{std::move(renderSurface)},
mDisplayWidth(displayWidth),
mDisplayHeight(displayHeight),
+ mDisplayInstallOrientation(displayInstallOrientation),
mPageFlipCount(0),
mIsSecure(isSecure),
mLayerStack(NO_LAYER_STACK),
@@ -610,9 +612,8 @@
// need to take care of primary display rotation for mGlobalTransform
// for case if the panel is not installed aligned with device orientation
if (mType == DisplayType::DISPLAY_PRIMARY) {
- int primaryDisplayOrientation = mFlinger->getPrimaryDisplayOrientation();
DisplayDevice::orientationToTransfrom(
- (orientation + primaryDisplayOrientation) % (DisplayState::eOrientation270 + 1),
+ (orientation + mDisplayInstallOrientation) % (DisplayState::eOrientation270 + 1),
w, h, &R);
}
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index 6c3bd91..d779ca4 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -25,7 +25,7 @@
#include <math/mat4.h>
#include <binder/IBinder.h>
-#include <gui/ISurfaceComposer.h>
+#include <gui/LayerState.h>
#include <hardware/hwcomposer_defs.h>
#include <ui/GraphicTypes.h>
#include <ui/HdrCapabilities.h>
@@ -88,6 +88,7 @@
std::unique_ptr<RE::Surface> renderSurface,
int displayWidth,
int displayHeight,
+ int displayInstallOrientation,
bool hasWideColorGamut,
const HdrCapabilities& hdrCapabilities,
const int32_t supportedPerFrameMetadata,
@@ -111,6 +112,7 @@
int getWidth() const;
int getHeight() const;
+ int getInstallOrientation() const { return mDisplayInstallOrientation; }
void setVisibleLayersSortedByZ(const Vector< sp<Layer> >& layers);
const Vector< sp<Layer> >& getVisibleLayersSortedByZ() const;
@@ -234,6 +236,7 @@
std::unique_ptr<RE::Surface> mSurface;
int mDisplayWidth;
int mDisplayHeight;
+ const int mDisplayInstallOrientation;
mutable uint32_t mPageFlipCount;
String8 mDisplayName;
bool mIsSecure;
@@ -337,23 +340,120 @@
class DisplayRenderArea : public RenderArea {
public:
DisplayRenderArea(const sp<const DisplayDevice> device,
- ISurfaceComposer::Rotation rotation = ISurfaceComposer::eRotateNone)
- : DisplayRenderArea(device, device->getBounds(), device->getHeight(), device->getWidth(),
+ Transform::orientation_flags rotation = Transform::ROT_0)
+ : DisplayRenderArea(device, device->getBounds(), device->getWidth(), device->getHeight(),
rotation) {}
- DisplayRenderArea(const sp<const DisplayDevice> device, Rect sourceCrop, uint32_t reqHeight,
- uint32_t reqWidth, ISurfaceComposer::Rotation rotation)
- : RenderArea(reqHeight, reqWidth, CaptureFill::OPAQUE, rotation), mDevice(device),
- mSourceCrop(sourceCrop) {}
+ DisplayRenderArea(const sp<const DisplayDevice> device, Rect sourceCrop, uint32_t reqWidth,
+ uint32_t reqHeight, Transform::orientation_flags rotation)
+ : RenderArea(reqWidth, reqHeight, CaptureFill::OPAQUE,
+ getDisplayRotation(rotation, device->getInstallOrientation())),
+ mDevice(device),
+ mSourceCrop(sourceCrop) {}
const Transform& getTransform() const override { return mDevice->getTransform(); }
Rect getBounds() const override { return mDevice->getBounds(); }
int getHeight() const override { return mDevice->getHeight(); }
int getWidth() const override { return mDevice->getWidth(); }
bool isSecure() const override { return mDevice->isSecure(); }
- bool needsFiltering() const override { return mDevice->needsFiltering(); }
- Rect getSourceCrop() const override { return mSourceCrop; }
+
+ bool needsFiltering() const override {
+ // check if the projection from the logical display to the physical
+ // display needs filtering
+ if (mDevice->needsFiltering()) {
+ return true;
+ }
+
+ // check if the projection from the logical render area (i.e., the
+ // physical display) to the physical render area requires filtering
+ const Rect sourceCrop = getSourceCrop();
+ int width = sourceCrop.width();
+ int height = sourceCrop.height();
+ if (getRotationFlags() & Transform::ROT_90) {
+ std::swap(width, height);
+ }
+ return width != getReqWidth() || height != getReqHeight();
+ }
+
+ Rect getSourceCrop() const override {
+ // use the projected display viewport by default.
+ if (mSourceCrop.isEmpty()) {
+ return mDevice->getScissor();
+ }
+
+ // Recompute the device transformation for the source crop.
+ Transform rotation;
+ Transform translatePhysical;
+ Transform translateLogical;
+ Transform scale;
+ const Rect& viewport = mDevice->getViewport();
+ const Rect& scissor = mDevice->getScissor();
+ const Rect& frame = mDevice->getFrame();
+
+ const int orientation = mDevice->getInstallOrientation();
+ // Install orientation is transparent to the callers. Apply it now.
+ uint32_t flags = 0x00;
+ switch (orientation) {
+ case DisplayState::eOrientation90:
+ flags = Transform::ROT_90;
+ break;
+ case DisplayState::eOrientation180:
+ flags = Transform::ROT_180;
+ break;
+ case DisplayState::eOrientation270:
+ flags = Transform::ROT_270;
+ break;
+ default:
+ break;
+ }
+ rotation.set(flags, getWidth(), getHeight());
+ translateLogical.set(-viewport.left, -viewport.top);
+ translatePhysical.set(scissor.left, scissor.top);
+ scale.set(frame.getWidth() / float(viewport.getWidth()), 0, 0,
+ frame.getHeight() / float(viewport.getHeight()));
+ const Transform finalTransform =
+ rotation * translatePhysical * scale * translateLogical;
+ return finalTransform.transform(mSourceCrop);
+ }
private:
+ // Install orientation is transparent to the callers. We need to cancel
+ // it out by modifying rotation flags.
+ static Transform::orientation_flags getDisplayRotation(
+ Transform::orientation_flags rotation, int orientation) {
+ if (orientation == DisplayState::eOrientationDefault) {
+ return rotation;
+ }
+
+ // convert hw orientation into flag presentation
+ // here inverse transform needed
+ uint8_t hw_rot_90 = 0x00;
+ uint8_t hw_flip_hv = 0x00;
+ switch (orientation) {
+ case DisplayState::eOrientation90:
+ hw_rot_90 = Transform::ROT_90;
+ hw_flip_hv = Transform::ROT_180;
+ break;
+ case DisplayState::eOrientation180:
+ hw_flip_hv = Transform::ROT_180;
+ break;
+ case DisplayState::eOrientation270:
+ hw_rot_90 = Transform::ROT_90;
+ break;
+ }
+
+ // transform flags operation
+ // 1) flip H V if both have ROT_90 flag
+ // 2) XOR these flags
+ uint8_t rotation_rot_90 = rotation & Transform::ROT_90;
+ uint8_t rotation_flip_hv = rotation & Transform::ROT_180;
+ if (rotation_rot_90 & hw_rot_90) {
+ rotation_flip_hv = (~rotation_flip_hv) & Transform::ROT_180;
+ }
+
+ return static_cast<Transform::orientation_flags>(
+ (rotation_rot_90 ^ hw_rot_90) | (rotation_flip_hv ^ hw_flip_hv));
+ }
+
const sp<const DisplayDevice> mDevice;
const Rect mSourceCrop;
};
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 0caac9b..72f1fc4 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -88,7 +88,6 @@
mCurrentOpacity(true),
mCurrentFrameNumber(0),
mFrameLatencyNeeded(false),
- mFiltering(false),
mNeedsFiltering(false),
mProtectedByApp(false),
mClientRef(client),
@@ -793,14 +792,6 @@
return true;
}
-void Layer::setFiltering(bool filtering) {
- mFiltering = filtering;
-}
-
-bool Layer::getFiltering() const {
- return mFiltering;
-}
-
// ----------------------------------------------------------------------------
// local state
// ----------------------------------------------------------------------------
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 301f190..239f397 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -530,9 +530,6 @@
// -----------------------------------------------------------------------
void clearWithOpenGL(const RenderArea& renderArea) const;
- void setFiltering(bool filtering);
- bool getFiltering() const;
-
inline const State& getDrawingState() const { return mDrawingState; }
inline const State& getCurrentState() const { return mCurrentState; }
@@ -755,8 +752,6 @@
bool mCurrentOpacity;
std::atomic<uint64_t> mCurrentFrameNumber;
bool mFrameLatencyNeeded;
- // Whether filtering is forced on or not
- bool mFiltering;
// Whether filtering is needed b/c of the drawingstate
bool mNeedsFiltering;
diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS
index ce0611c..69d8c89 100644
--- a/services/surfaceflinger/OWNERS
+++ b/services/surfaceflinger/OWNERS
@@ -1,4 +1,6 @@
+adyabr@google.com
akrulec@google.com
+alecmouri@google.com
chaviw@google.com
lpy@google.com
marissaw@google.com
diff --git a/services/surfaceflinger/RenderArea.cpp b/services/surfaceflinger/RenderArea.cpp
index 1a8edf3..93759e8 100644
--- a/services/surfaceflinger/RenderArea.cpp
+++ b/services/surfaceflinger/RenderArea.cpp
@@ -1,7 +1,5 @@
#include "RenderArea.h"
-#include <gui/LayerState.h>
-
namespace android {
float RenderArea::getCaptureFillValue(CaptureFill captureFill) {
@@ -13,37 +11,5 @@
return 1.0f;
}
}
-/*
- * Checks that the requested width and height are valid and updates them to the render area
- * dimensions if they are set to 0
- */
-status_t RenderArea::updateDimensions(int displayRotation) {
- // get screen geometry
-
- uint32_t width = getWidth();
- uint32_t height = getHeight();
-
- if (mRotationFlags & Transform::ROT_90) {
- std::swap(width, height);
- }
-
- if (displayRotation & DisplayState::eOrientationSwapMask) {
- std::swap(width, height);
- }
-
- if ((mReqWidth > width) || (mReqHeight > height)) {
- ALOGE("size mismatch (%d, %d) > (%d, %d)", mReqWidth, mReqHeight, width, height);
- return BAD_VALUE;
- }
-
- if (mReqWidth == 0) {
- mReqWidth = width;
- }
- if (mReqHeight == 0) {
- mReqHeight = height;
- }
-
- return NO_ERROR;
-}
} // namespace android
diff --git a/services/surfaceflinger/RenderArea.h b/services/surfaceflinger/RenderArea.h
index 96e4b5f..d980bd5 100644
--- a/services/surfaceflinger/RenderArea.h
+++ b/services/surfaceflinger/RenderArea.h
@@ -1,50 +1,80 @@
#pragma once
-#include <ui/GraphicTypes.h>
-
#include "Transform.h"
#include <functional>
namespace android {
+// RenderArea describes a rectangular area that layers can be rendered to.
+//
+// There is a logical render area and a physical render area. When a layer is
+// rendered to the render area, it is first transformed and clipped to the logical
+// render area. The transformed and clipped layer is then projected onto the
+// physical render area.
class RenderArea {
-
public:
enum class CaptureFill {CLEAR, OPAQUE};
static float getCaptureFillValue(CaptureFill captureFill);
- RenderArea(uint32_t reqHeight, uint32_t reqWidth, CaptureFill captureFill,
- ISurfaceComposer::Rotation rotation = ISurfaceComposer::eRotateNone)
- : mReqHeight(reqHeight), mReqWidth(reqWidth), mCaptureFill(captureFill) {
- mRotationFlags = Transform::fromRotation(rotation);
- }
+ RenderArea(uint32_t reqWidth, uint32_t reqHeight, CaptureFill captureFill,
+ Transform::orientation_flags rotation = Transform::ROT_0)
+ : mReqWidth(reqWidth),
+ mReqHeight(reqHeight),
+ mCaptureFill(captureFill),
+ mRotationFlags(rotation) {}
virtual ~RenderArea() = default;
- virtual const Transform& getTransform() const = 0;
- virtual Rect getBounds() const = 0;
- virtual int getHeight() const = 0;
- virtual int getWidth() const = 0;
- virtual bool isSecure() const = 0;
- virtual bool needsFiltering() const = 0;
- virtual Rect getSourceCrop() const = 0;
-
+ // Invoke drawLayers to render layers into the render area.
virtual void render(std::function<void()> drawLayers) { drawLayers(); }
- int getReqHeight() const { return mReqHeight; };
- int getReqWidth() const { return mReqWidth; };
- Transform::orientation_flags getRotationFlags() const { return mRotationFlags; };
- status_t updateDimensions(int displayRotation);
+ // Returns true if the render area is secure. A secure layer should be
+ // blacked out / skipped when rendered to an insecure render area.
+ virtual bool isSecure() const = 0;
+ // Returns true if the otherwise disabled layer filtering should be
+ // enabled when rendering to this render area.
+ virtual bool needsFiltering() const = 0;
+
+ // Returns the transform to be applied on layers to transform them into
+ // the logical render area.
+ virtual const Transform& getTransform() const = 0;
+
+ // Returns the size of the logical render area. Layers are clipped to the
+ // logical render area.
+ virtual int getWidth() const = 0;
+ virtual int getHeight() const = 0;
+ virtual Rect getBounds() const = 0;
+
+ // Returns the source crop of the render area. The source crop defines
+ // how layers are projected from the logical render area onto the physical
+ // render area. It can be larger than the logical render area. It can
+ // also be optionally rotated.
+ //
+ // Layers are first clipped to the source crop (in addition to being
+ // clipped to the logical render area already). The source crop and the
+ // layers are then rotated around the center of the source crop, and
+ // scaled to the physical render area linearly.
+ virtual Rect getSourceCrop() const = 0;
+
+ // Returns the rotation of the source crop and the layers.
+ Transform::orientation_flags getRotationFlags() const { return mRotationFlags; };
+
+ // Returns the size of the physical render area.
+ int getReqWidth() const { return mReqWidth; };
+ int getReqHeight() const { return mReqHeight; };
+
+ // Returns the fill color of the physical render area. Regions not
+ // covered by any rendered layer should be filled with this color.
CaptureFill getCaptureFill() const { return mCaptureFill; };
private:
- uint32_t mReqHeight;
- uint32_t mReqWidth;
- Transform::orientation_flags mRotationFlags;
- CaptureFill mCaptureFill;
+ const uint32_t mReqWidth;
+ const uint32_t mReqHeight;
+ const CaptureFill mCaptureFill;
+ const Transform::orientation_flags mRotationFlags;
};
} // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 286a133..78d751a 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -75,6 +75,7 @@
#include "LayerVector.h"
#include "MonitoredProducer.h"
#include "SurfaceFlinger.h"
+#include "Transform.h"
#include "clz.h"
#include "DisplayHardware/ComposerHal.h"
@@ -112,6 +113,27 @@
using ui::RenderIntent;
namespace {
+
+#pragma clang diagnostic push
+#pragma clang diagnostic error "-Wswitch-enum"
+
+Transform::orientation_flags fromSurfaceComposerRotation(ISurfaceComposer::Rotation rotation) {
+ switch (rotation) {
+ case ISurfaceComposer::eRotateNone:
+ return Transform::ROT_0;
+ case ISurfaceComposer::eRotate90:
+ return Transform::ROT_90;
+ case ISurfaceComposer::eRotate180:
+ return Transform::ROT_180;
+ case ISurfaceComposer::eRotate270:
+ return Transform::ROT_270;
+ }
+ ALOGE("Invalid rotation passed to captureScreen(): %d\n", rotation);
+ return Transform::ROT_0;
+}
+
+#pragma clang diagnostic pop
+
class ConditionalLock {
public:
ConditionalLock(Mutex& mutex, bool lock) : mMutex(mutex), mLocked(lock) {
@@ -144,7 +166,7 @@
int64_t SurfaceFlinger::maxFrameBufferAcquiredBuffers;
// TODO(courtneygo): Rename hasWideColorDisplay to clarify its actual meaning.
bool SurfaceFlinger::hasWideColorDisplay;
-
+int SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientationDefault;
std::string getHwcServiceName() {
char value[PROPERTY_VALUE_MAX] = {};
@@ -278,19 +300,19 @@
switch (primaryDisplayOrientation) {
case V1_1::DisplayOrientation::ORIENTATION_90:
- mPrimaryDisplayOrientation = DisplayState::eOrientation90;
+ SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientation90;
break;
case V1_1::DisplayOrientation::ORIENTATION_180:
- mPrimaryDisplayOrientation = DisplayState::eOrientation180;
+ SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientation180;
break;
case V1_1::DisplayOrientation::ORIENTATION_270:
- mPrimaryDisplayOrientation = DisplayState::eOrientation270;
+ SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientation270;
break;
default:
- mPrimaryDisplayOrientation = DisplayState::eOrientationDefault;
+ SurfaceFlinger::primaryDisplayOrientation = DisplayState::eOrientationDefault;
break;
}
- ALOGV("Primary Display Orientation is set to %2d.", mPrimaryDisplayOrientation);
+ ALOGV("Primary Display Orientation is set to %2d.", SurfaceFlinger::primaryDisplayOrientation);
mPrimaryDispSync.init(SurfaceFlinger::hasSyncFramework, SurfaceFlinger::dispSyncPresentTimeOffset);
@@ -952,7 +974,7 @@
info.secure = true;
if (type == DisplayDevice::DISPLAY_PRIMARY &&
- mPrimaryDisplayOrientation & DisplayState::eOrientationSwapMask) {
+ primaryDisplayOrientation & DisplayState::eOrientationSwapMask) {
std::swap(info.w, info.h);
}
@@ -2395,6 +2417,9 @@
nativeWindow->setSwapInterval(nativeWindow.get(), 0);
}
+ const int displayInstallOrientation = state.type == DisplayDevice::DISPLAY_PRIMARY ?
+ primaryDisplayOrientation : DisplayState::eOrientationDefault;
+
// virtual displays are always considered enabled
auto initialPowerMode = (state.type >= DisplayDevice::DISPLAY_VIRTUAL) ? HWC_POWER_MODE_NORMAL
: HWC_POWER_MODE_OFF;
@@ -2402,7 +2427,7 @@
sp<DisplayDevice> hw =
new DisplayDevice(this, state.type, hwcId, state.isSecure, display, nativeWindow,
dispSurface, std::move(renderSurface), displayWidth, displayHeight,
- hasWideColorGamut, hdrCapabilities,
+ displayInstallOrientation, hasWideColorGamut, hdrCapabilities,
supportedPerFrameMetadata, hwcColorModes, initialPowerMode);
if (maxFrameBufferAcquiredBuffers >= 3) {
@@ -4427,6 +4452,12 @@
result.append("\n");
/*
+ * Tracing state
+ */
+ mTracing.dump(result);
+ result.append("\n");
+
+ /*
* HWC layer minidump
*/
for (size_t d = 0; d < mDisplays.size(); d++) {
@@ -4755,12 +4786,12 @@
case 1025: { // Set layer tracing
n = data.readInt32();
if (n) {
- ALOGV("LayerTracing enabled");
+ ALOGD("LayerTracing enabled");
mTracing.enable();
doTracing("tracing.enable");
reply->writeInt32(NO_ERROR);
} else {
- ALOGV("LayerTracing disabled");
+ ALOGD("LayerTracing disabled");
status_t err = mTracing.disable();
reply->writeInt32(err);
}
@@ -4826,20 +4857,24 @@
if (CC_UNLIKELY(display == 0)) return BAD_VALUE;
- const sp<const DisplayDevice> device(getDisplayDeviceLocked(display));
- if (CC_UNLIKELY(device == 0)) return BAD_VALUE;
+ auto renderAreaRotation = fromSurfaceComposerRotation(rotation);
- const Rect& dispScissor = device->getScissor();
- if (!dispScissor.isEmpty()) {
- sourceCrop.set(dispScissor);
- // adb shell screencap will default reqWidth and reqHeight to zeros.
+ sp<DisplayDevice> device;
+ {
+ Mutex::Autolock _l(mStateLock);
+
+ device = getDisplayDeviceLocked(display);
+ if (!device) return BAD_VALUE;
+
+ // set the requested width/height to the logical display viewport size
+ // by default
if (reqWidth == 0 || reqHeight == 0) {
reqWidth = uint32_t(device->getViewport().width());
reqHeight = uint32_t(device->getViewport().height());
}
}
- DisplayRenderArea renderArea(device, sourceCrop, reqHeight, reqWidth, rotation);
+ DisplayRenderArea renderArea(device, sourceCrop, reqWidth, reqHeight, renderAreaRotation);
auto traverseLayers = std::bind(std::mem_fn(&SurfaceFlinger::traverseLayersInDisplay), this,
device, minLayerZ, maxLayerZ, std::placeholders::_1);
@@ -4855,9 +4890,10 @@
public:
LayerRenderArea(SurfaceFlinger* flinger, const sp<Layer>& layer, const Rect crop,
int32_t reqWidth, int32_t reqHeight, bool childrenOnly)
- : RenderArea(reqHeight, reqWidth, CaptureFill::CLEAR),
+ : RenderArea(reqWidth, reqHeight, CaptureFill::CLEAR),
mLayer(layer),
mCrop(crop),
+ mNeedsFiltering(false),
mFlinger(flinger),
mChildrenOnly(childrenOnly) {}
const Transform& getTransform() const override { return mTransform; }
@@ -4868,7 +4904,7 @@
int getHeight() const override { return mLayer->getDrawingState().active.h; }
int getWidth() const override { return mLayer->getDrawingState().active.w; }
bool isSecure() const override { return false; }
- bool needsFiltering() const override { return false; }
+ bool needsFiltering() const override { return mNeedsFiltering; }
Rect getSourceCrop() const override {
if (mCrop.isEmpty()) {
return getBounds();
@@ -4889,6 +4925,11 @@
};
void render(std::function<void()> drawLayers) override {
+ const Rect sourceCrop = getSourceCrop();
+ // no need to check rotation because there is none
+ mNeedsFiltering = sourceCrop.width() != getReqWidth() ||
+ sourceCrop.height() != getReqHeight();
+
if (!mChildrenOnly) {
mTransform = mLayer->getTransform().inverse();
drawLayers();
@@ -4911,6 +4952,7 @@
// layer which has no properties set and which does not draw.
sp<ContainerLayer> screenshotParentLayer;
Transform mTransform;
+ bool mNeedsFiltering;
SurfaceFlinger* mFlinger;
const bool mChildrenOnly;
@@ -4945,6 +4987,14 @@
int32_t reqWidth = crop.width() * frameScale;
int32_t reqHeight = crop.height() * frameScale;
+ // really small crop or frameScale
+ if (reqWidth <= 0) {
+ reqWidth = 1;
+ }
+ if (reqHeight <= 0) {
+ reqHeight = 1;
+ }
+
LayerRenderArea renderArea(this, parent, crop, reqWidth, reqHeight, childrenOnly);
auto traverseLayers = [parent, childrenOnly](const LayerVector::Visitor& visitor) {
@@ -4966,8 +5016,6 @@
bool useIdentityTransform) {
ATRACE_CALL();
- renderArea.updateDimensions(mPrimaryDisplayOrientation);
-
const uint32_t usage = GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN |
GRALLOC_USAGE_HW_RENDER | GRALLOC_USAGE_HW_TEXTURE;
*outBuffer = new GraphicBuffer(renderArea.getReqWidth(), renderArea.getReqHeight(),
@@ -5043,57 +5091,12 @@
auto& engine(getRenderEngine());
// get screen geometry
- const auto raWidth = renderArea.getWidth();
const auto raHeight = renderArea.getHeight();
const auto reqWidth = renderArea.getReqWidth();
const auto reqHeight = renderArea.getReqHeight();
- Rect sourceCrop = renderArea.getSourceCrop();
-
- bool filtering = false;
- if (mPrimaryDisplayOrientation & DisplayState::eOrientationSwapMask) {
- filtering = static_cast<int32_t>(reqWidth) != raHeight ||
- static_cast<int32_t>(reqHeight) != raWidth;
- } else {
- filtering = static_cast<int32_t>(reqWidth) != raWidth ||
- static_cast<int32_t>(reqHeight) != raHeight;
- }
-
- // if a default or invalid sourceCrop is passed in, set reasonable values
- if (sourceCrop.width() == 0 || sourceCrop.height() == 0 || !sourceCrop.isValid()) {
- sourceCrop.setLeftTop(Point(0, 0));
- sourceCrop.setRightBottom(Point(raWidth, raHeight));
- } else if (mPrimaryDisplayOrientation != DisplayState::eOrientationDefault) {
- Transform tr;
- uint32_t flags = 0x00;
- switch (mPrimaryDisplayOrientation) {
- case DisplayState::eOrientation90:
- flags = Transform::ROT_90;
- break;
- case DisplayState::eOrientation180:
- flags = Transform::ROT_180;
- break;
- case DisplayState::eOrientation270:
- flags = Transform::ROT_270;
- break;
- }
- tr.set(flags, raWidth, raHeight);
- sourceCrop = tr.transform(sourceCrop);
- }
-
- // ensure that sourceCrop is inside screen
- if (sourceCrop.left < 0) {
- ALOGE("Invalid crop rect: l = %d (< 0)", sourceCrop.left);
- }
- if (sourceCrop.right > raWidth) {
- ALOGE("Invalid crop rect: r = %d (> %d)", sourceCrop.right, raWidth);
- }
- if (sourceCrop.top < 0) {
- ALOGE("Invalid crop rect: t = %d (< 0)", sourceCrop.top);
- }
- if (sourceCrop.bottom > raHeight) {
- ALOGE("Invalid crop rect: b = %d (> %d)", sourceCrop.bottom, raHeight);
- }
+ const auto sourceCrop = renderArea.getSourceCrop();
+ const auto rotation = renderArea.getRotationFlags();
// assume ColorMode::SRGB / RenderIntent::COLORIMETRIC
engine.setOutputDataSpace(Dataspace::SRGB);
@@ -5102,37 +5105,6 @@
// make sure to clear all GL error flags
engine.checkErrors();
- Transform::orientation_flags rotation = renderArea.getRotationFlags();
- if (mPrimaryDisplayOrientation != DisplayState::eOrientationDefault) {
- // convert hw orientation into flag presentation
- // here inverse transform needed
- uint8_t hw_rot_90 = 0x00;
- uint8_t hw_flip_hv = 0x00;
- switch (mPrimaryDisplayOrientation) {
- case DisplayState::eOrientation90:
- hw_rot_90 = Transform::ROT_90;
- hw_flip_hv = Transform::ROT_180;
- break;
- case DisplayState::eOrientation180:
- hw_flip_hv = Transform::ROT_180;
- break;
- case DisplayState::eOrientation270:
- hw_rot_90 = Transform::ROT_90;
- break;
- }
-
- // transform flags operation
- // 1) flip H V if both have ROT_90 flag
- // 2) XOR these flags
- uint8_t rotation_rot_90 = rotation & Transform::ROT_90;
- uint8_t rotation_flip_hv = rotation & Transform::ROT_180;
- if (rotation_rot_90 & hw_rot_90) {
- rotation_flip_hv = (~rotation_flip_hv) & Transform::ROT_180;
- }
- rotation = static_cast<Transform::orientation_flags>
- ((rotation_rot_90 ^ hw_rot_90) | (rotation_flip_hv ^ hw_flip_hv));
- }
-
// set-up our viewport
engine.setViewportAndProjection(reqWidth, reqHeight, sourceCrop, raHeight, yswap,
rotation);
@@ -5143,9 +5115,7 @@
engine.clearWithColor(0, 0, 0, alpha);
traverseLayers([&](Layer* layer) {
- if (filtering) layer->setFiltering(true);
layer->draw(renderArea, useIdentityTransform);
- if (filtering) layer->setFiltering(false);
});
}
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index d2b1233..0914a09 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -286,6 +286,8 @@
// want to support color management to disable color management.
static bool hasWideColorDisplay;
+ static int primaryDisplayOrientation;
+
static char const* getServiceName() ANDROID_API {
return "SurfaceFlinger";
}
@@ -345,8 +347,6 @@
bool authenticateSurfaceTextureLocked(
const sp<IGraphicBufferProducer>& bufferProducer) const;
- int getPrimaryDisplayOrientation() const { return mPrimaryDisplayOrientation; }
-
private:
friend class Client;
friend class DisplayEventConnection;
@@ -858,7 +858,6 @@
mutable std::unique_ptr<MessageQueue> mEventQueue{std::make_unique<impl::MessageQueue>()};
FrameTracker mAnimFrameTracker;
DispSync mPrimaryDispSync;
- int mPrimaryDisplayOrientation = DisplayState::eOrientationDefault;
// protected by mDestroyedLayerLock;
mutable Mutex mDestroyedLayerLock;
diff --git a/services/surfaceflinger/SurfaceTracing.cpp b/services/surfaceflinger/SurfaceTracing.cpp
index f8c466e..67dcd06 100644
--- a/services/surfaceflinger/SurfaceTracing.cpp
+++ b/services/surfaceflinger/SurfaceTracing.cpp
@@ -27,52 +27,67 @@
namespace android {
void SurfaceTracing::enable() {
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+
if (mEnabled) {
return;
}
- ATRACE_CALL();
mEnabled = true;
- std::lock_guard<std::mutex> protoGuard(mTraceMutex);
- mTrace.set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 |
- LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L);
+ mTrace = std::make_unique<LayersTraceFileProto>();
+ mTrace->set_magic_number(uint64_t(LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_H) << 32 |
+ LayersTraceFileProto_MagicNumber_MAGIC_NUMBER_L);
}
status_t SurfaceTracing::disable() {
+ ATRACE_CALL();
+ std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+
if (!mEnabled) {
return NO_ERROR;
}
- ATRACE_CALL();
- std::lock_guard<std::mutex> protoGuard(mTraceMutex);
mEnabled = false;
status_t err(writeProtoFileLocked());
ALOGE_IF(err == PERMISSION_DENIED, "Could not save the proto file! Permission denied");
ALOGE_IF(err == NOT_ENOUGH_DATA, "Could not save the proto file! There are missing fields");
- mTrace.Clear();
+ mTrace.reset();
return err;
}
-bool SurfaceTracing::isEnabled() {
+bool SurfaceTracing::isEnabled() const {
+ std::lock_guard<std::mutex> protoGuard(mTraceMutex);
return mEnabled;
}
void SurfaceTracing::traceLayers(const char* where, LayersProto layers) {
std::lock_guard<std::mutex> protoGuard(mTraceMutex);
-
- LayersTraceProto* entry = mTrace.add_entry();
+ if (!mEnabled) {
+ return;
+ }
+ LayersTraceProto* entry = mTrace->add_entry();
entry->set_elapsed_realtime_nanos(elapsedRealtimeNano());
entry->set_where(where);
entry->mutable_layers()->Swap(&layers);
+
+ constexpr int maxBufferedEntryCount = 3600;
+ if (mTrace->entry_size() >= maxBufferedEntryCount) {
+ // TODO: flush buffered entries without disabling tracing
+ ALOGE("too many buffered frames; force disable tracing");
+ mEnabled = false;
+ writeProtoFileLocked();
+ mTrace.reset();
+ }
}
status_t SurfaceTracing::writeProtoFileLocked() {
ATRACE_CALL();
- if (!mTrace.IsInitialized()) {
+ if (!mTrace->IsInitialized()) {
return NOT_ENOUGH_DATA;
}
std::string output;
- if (!mTrace.SerializeToString(&output)) {
+ if (!mTrace->SerializeToString(&output)) {
return PERMISSION_DENIED;
}
if (!android::base::WriteStringToFile(output, mOutputFileName, true)) {
@@ -82,4 +97,11 @@
return NO_ERROR;
}
+void SurfaceTracing::dump(String8& result) const {
+ std::lock_guard<std::mutex> protoGuard(mTraceMutex);
+
+ result.appendFormat("Tracing state: %s\n", mEnabled ? "enabled" : "disabled");
+ result.appendFormat(" number of entries: %d\n", mTrace ? mTrace->entry_size() : 0);
+}
+
} // namespace android
diff --git a/services/surfaceflinger/SurfaceTracing.h b/services/surfaceflinger/SurfaceTracing.h
index 590ab96..fd8cb82 100644
--- a/services/surfaceflinger/SurfaceTracing.h
+++ b/services/surfaceflinger/SurfaceTracing.h
@@ -18,7 +18,9 @@
#include <layerproto/LayerProtoHeader.h>
#include <utils/Errors.h>
+#include <utils/String8.h>
+#include <memory>
#include <mutex>
using namespace android::surfaceflinger;
@@ -32,9 +34,10 @@
public:
void enable();
status_t disable();
- bool isEnabled();
+ bool isEnabled() const;
void traceLayers(const char* where, LayersProto);
+ void dump(String8& result) const;
private:
static constexpr auto DEFAULT_FILENAME = "/data/misc/wmtrace/layers_trace.pb";
@@ -43,8 +46,8 @@
bool mEnabled = false;
std::string mOutputFileName = DEFAULT_FILENAME;
- std::mutex mTraceMutex;
- LayersTraceFileProto mTrace;
+ mutable std::mutex mTraceMutex;
+ std::unique_ptr<LayersTraceFileProto> mTrace;
};
} // namespace android
diff --git a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
index 558845f..4c5fa99 100644
--- a/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
+++ b/services/surfaceflinger/tests/unittests/TestableSurfaceFlinger.h
@@ -110,7 +110,7 @@
*/
auto& mutableHasWideColorDisplay() { return SurfaceFlinger::hasWideColorDisplay; }
-
+ auto& mutablePrimaryDisplayOrientation() { return SurfaceFlinger::primaryDisplayOrientation; }
auto& mutableBuiltinDisplays() { return mFlinger->mBuiltinDisplays; }
auto& mutableCurrentState() { return mFlinger->mCurrentState; }
auto& mutableDisplays() { return mFlinger->mDisplays; }
@@ -320,10 +320,11 @@
sp<DisplayDevice> inject() {
std::unordered_map<ui::ColorMode, std::vector<ui::RenderIntent>> hdrAndRenderIntents;
sp<DisplayDevice> device =
- new DisplayDevice(mFlinger.mFlinger.get(), mType, mHwcId, mSecure, mDisplayToken,
- mNativeWindow, mDisplaySurface, std::move(mRenderSurface), 0,
- 0, false, HdrCapabilities(), 0, hdrAndRenderIntents,
- HWC_POWER_MODE_NORMAL);
+ new DisplayDevice(mFlinger.mFlinger.get(), mType, mHwcId, mSecure,
+ mDisplayToken, mNativeWindow, mDisplaySurface,
+ std::move(mRenderSurface), 0, 0,
+ DisplayState::eOrientationDefault, false, HdrCapabilities(),
+ 0, hdrAndRenderIntents, HWC_POWER_MODE_NORMAL);
mFlinger.mutableDisplays().add(mDisplayToken, device);
DisplayDeviceState state(mType, mSecure);
diff --git a/services/vr/virtual_touchpad/Android.bp b/services/vr/virtual_touchpad/Android.bp
index 0263481..131a306 100644
--- a/services/vr/virtual_touchpad/Android.bp
+++ b/services/vr/virtual_touchpad/Android.bp
@@ -62,7 +62,7 @@
service_src = [
"main.cpp",
"VirtualTouchpadService.cpp",
- "aidl/android/dvr/VirtualTouchpadService.aidl",
+ "aidl/android/dvr/IVirtualTouchpadService.aidl",
]
service_static_libs = [
@@ -99,7 +99,7 @@
client_src = [
"VirtualTouchpadClient.cpp",
"DvrVirtualTouchpadClient.cpp",
- "aidl/android/dvr/VirtualTouchpadService.aidl",
+ "aidl/android/dvr/IVirtualTouchpadService.aidl",
]
client_shared_libs = [
diff --git a/services/vr/virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl b/services/vr/virtual_touchpad/aidl/android/dvr/IVirtualTouchpadService.aidl
similarity index 97%
rename from services/vr/virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl
rename to services/vr/virtual_touchpad/aidl/android/dvr/IVirtualTouchpadService.aidl
index 256203c..89aa44a 100644
--- a/services/vr/virtual_touchpad/aidl/android/dvr/VirtualTouchpadService.aidl
+++ b/services/vr/virtual_touchpad/aidl/android/dvr/IVirtualTouchpadService.aidl
@@ -1,7 +1,7 @@
package android.dvr;
/** @hide */
-interface VirtualTouchpadService
+interface IVirtualTouchpadService
{
const String SERVICE_NAME = "virtual_touchpad";
diff --git a/vulkan/libvulkan/Android.bp b/vulkan/libvulkan/Android.bp
index 206c8eb..71a120a 100644
--- a/vulkan/libvulkan/Android.bp
+++ b/vulkan/libvulkan/Android.bp
@@ -76,6 +76,7 @@
"libhardware",
"libsync",
"libbase",
+ "libdl_android",
"libhidlbase",
"libhidltransport",
"liblog",