Merge "Support dumping mainline relevant service pre-boot into bugreport"
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 36a53bb..e048a19 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -7,16 +7,19 @@
cmds/idlcli/
include/input/
libs/binder/ndk/
+ libs/binderthreadstate/
libs/graphicsenv/
libs/gui/
libs/input/
libs/renderengine/
libs/ui/
libs/vr/
+ opengl/libs/
services/bufferhub/
services/inputflinger/
services/surfaceflinger/
services/vr/
+ vulkan/
[Hook Scripts]
owners_hook = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} "OWNERS$"
diff --git a/aidl/binder/android/os/PersistableBundle.aidl b/aidl/binder/android/os/PersistableBundle.aidl
index 94e8607..493ecb4 100644
--- a/aidl/binder/android/os/PersistableBundle.aidl
+++ b/aidl/binder/android/os/PersistableBundle.aidl
@@ -17,4 +17,4 @@
package android.os;
-parcelable PersistableBundle cpp_header "binder/PersistableBundle.h";
+@JavaOnlyStableParcelable parcelable PersistableBundle cpp_header "binder/PersistableBundle.h";
diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp
index 5186ad3..cf75bba 100644
--- a/cmds/atrace/atrace.cpp
+++ b/cmds/atrace/atrace.cpp
@@ -854,7 +854,6 @@
tags |= c.tags;
}
}
- ok &= setTagsProperty(tags);
bool coreServicesTagEnabled = false;
for (size_t i = 0; i < arraysize(k_categories); i++) {
@@ -876,9 +875,11 @@
packageList += android::base::GetProperty(k_coreServicesProp, "");
}
ok &= setAppCmdlineProperty(&packageList[0]);
+ ok &= setTagsProperty(tags);
+#if !ATRACE_SHMEM
ok &= pokeBinderServices();
pokeHalServices();
-
+#endif
if (g_tracePdx) {
ok &= ServiceUtility::PokeServices();
}
diff --git a/cmds/cmd/cmd.cpp b/cmds/cmd/cmd.cpp
index 7b4aeb2..8dad475 100644
--- a/cmds/cmd/cmd.cpp
+++ b/cmds/cmd/cmd.cpp
@@ -223,7 +223,8 @@
sp<MyResultReceiver> result = new MyResultReceiver();
#if DEBUG
- ALOGD("cmd: Invoking %s in=%d, out=%d, err=%d", cmd, in, out, err);
+ ALOGD("cmd: Invoking %.*s in=%d, out=%d, err=%d",
+ static_cast<int>(cmd.size()), cmd.data(), in, out, err);
#endif
// TODO: block until a result is returned to MyResultReceiver.
diff --git a/cmds/dumpstate/DumpstateInternal.h b/cmds/dumpstate/DumpstateInternal.h
index 10db5d6..c1ec55e 100644
--- a/cmds/dumpstate/DumpstateInternal.h
+++ b/cmds/dumpstate/DumpstateInternal.h
@@ -49,6 +49,7 @@
// TODO: use functions from <chrono> instead
const uint64_t NANOS_PER_SEC = 1000000000;
+const uint64_t NANOS_PER_MILLI = 1000000;
uint64_t Nanotime();
// Switches to non-root user and group.
diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp
index 498f727..310b5ce 100644
--- a/cmds/dumpstate/dumpstate.cpp
+++ b/cmds/dumpstate/dumpstate.cpp
@@ -135,6 +135,11 @@
static char cmdline_buf[16384] = "(unknown)";
static const char *dump_traces_path = nullptr;
static const uint64_t USER_CONSENT_TIMEOUT_MS = 30 * 1000;
+// Because telephony reports are significantly faster to collect (< 10 seconds vs. > 2 minutes),
+// it's often the case that they time out far too quickly for consent with such a hefty dialog for
+// the user to read. For telephony reports only, we increase the default timeout to 2 minutes to
+// roughly match full reports' durations.
+static const uint64_t TELEPHONY_REPORT_USER_CONSENT_TIMEOUT_MS = 2 * 60 * 1000;
// TODO: variables and functions below should be part of dumpstate object
@@ -156,6 +161,8 @@
#define WLUTIL "/vendor/xbin/wlutil"
#define WMTRACE_DATA_DIR "/data/misc/wmtrace"
#define OTA_METADATA_DIR "/metadata/ota"
+#define SNAPSHOTCTL_LOG_DIR "/data/misc/snapshotctl_log"
+#define LINKERCONFIG_DIR "/linkerconfig"
// TODO(narayan): Since this information has to be kept in sync
// with tombstoned, we should just put it in a common header.
@@ -661,7 +668,7 @@
}
uint64_t Dumpstate::ConsentCallback::getElapsedTimeMs() const {
- return Nanotime() - start_time_;
+ return (Nanotime() - start_time_) / NANOS_PER_MILLI;
}
void Dumpstate::PrintHeader() const {
@@ -889,6 +896,14 @@
CommandOptions::WithTimeoutInMs(timeout_ms).Build());
}
+static void DoRadioLogcat() {
+ unsigned long timeout_ms = logcat_timeout({"radio"});
+ RunCommand(
+ "RADIO LOG",
+ {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"},
+ CommandOptions::WithTimeoutInMs(timeout_ms).Build(), true /* verbose_duration */);
+}
+
static void DoLogcat() {
unsigned long timeout_ms;
// DumpFile("EVENT LOG TAGS", "/etc/event-log-tags");
@@ -907,11 +922,7 @@
"STATS LOG",
{"logcat", "-b", "stats", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"},
CommandOptions::WithTimeoutInMs(timeout_ms).Build(), true /* verbose_duration */);
- timeout_ms = logcat_timeout({"radio"});
- RunCommand(
- "RADIO LOG",
- {"logcat", "-b", "radio", "-v", "threadtime", "-v", "printable", "-v", "uid", "-d", "*:v"},
- CommandOptions::WithTimeoutInMs(timeout_ms).Build(), true /* verbose_duration */);
+ DoRadioLogcat();
RunCommand("LOG STATISTICS", {"logcat", "-b", "all", "-S"});
@@ -1412,11 +1423,14 @@
RunCommand("FILESYSTEMS & FREE SPACE", {"df"});
/* Binder state is expensive to look at as it uses a lot of memory. */
- DumpFile("BINDER FAILED TRANSACTION LOG", "/sys/kernel/debug/binder/failed_transaction_log");
- DumpFile("BINDER TRANSACTION LOG", "/sys/kernel/debug/binder/transaction_log");
- DumpFile("BINDER TRANSACTIONS", "/sys/kernel/debug/binder/transactions");
- DumpFile("BINDER STATS", "/sys/kernel/debug/binder/stats");
- DumpFile("BINDER STATE", "/sys/kernel/debug/binder/state");
+ std::string binder_logs_dir = access("/dev/binderfs/binder_logs", R_OK) ?
+ "/sys/kernel/debug/binder" : "/dev/binderfs/binder_logs";
+
+ DumpFile("BINDER FAILED TRANSACTION LOG", binder_logs_dir + "/failed_transaction_log");
+ DumpFile("BINDER TRANSACTION LOG", binder_logs_dir + "/transaction_log");
+ DumpFile("BINDER TRANSACTIONS", binder_logs_dir + "/transactions");
+ DumpFile("BINDER STATS", binder_logs_dir + "/stats");
+ DumpFile("BINDER STATE", binder_logs_dir + "/state");
/* Add window and surface trace files. */
if (!PropertiesHelper::IsUserBuild()) {
@@ -1517,6 +1531,9 @@
// This differs from the usual dumpsys stats, which is the stats report data.
RunDumpsys("STATSDSTATS", {"stats", "--metadata"});
+ // Add linker configuration directory
+ ds.AddDir(LINKERCONFIG_DIR, true);
+
RUN_SLOW_FUNCTION_WITH_CONSENT_CHECK(DumpIncidentReport);
return Dumpstate::RunStatus::OK;
@@ -1552,6 +1569,7 @@
ds.AddDir(RECOVERY_DATA_DIR, true);
ds.AddDir(UPDATE_ENGINE_LOG_DIR, true);
ds.AddDir(LOGPERSIST_DATA_DIR, false);
+ ds.AddDir(SNAPSHOTCTL_LOG_DIR, false);
if (!PropertiesHelper::IsUserBuild()) {
ds.AddDir(PROFILE_DATA_DIR_CUR, true);
ds.AddDir(PROFILE_DATA_DIR_REF, true);
@@ -1595,8 +1613,10 @@
return status;
}
-// This method collects common dumpsys for telephony and wifi
-static void DumpstateRadioCommon() {
+// This method collects common dumpsys for telephony and wifi. Typically, wifi
+// reports are fine to include all information, but telephony reports on user
+// builds need to strip some content (see DumpstateTelephonyOnly).
+static void DumpstateRadioCommon(bool include_sensitive_info = true) {
DumpIpTablesAsRoot();
ds.AddDir(LOGPERSIST_DATA_DIR, false);
@@ -1605,26 +1625,51 @@
return;
}
- do_dmesg();
- DoLogcat();
+ // We need to be picky about some stuff for telephony reports on user builds.
+ if (!include_sensitive_info) {
+ // Only dump the radio log buffer (other buffers and dumps contain too much unrelated info).
+ DoRadioLogcat();
+ } else {
+ // Contains various system properties and process startup info.
+ do_dmesg();
+ // Logs other than the radio buffer may contain package/component names and potential PII.
+ DoLogcat();
+ // Too broad for connectivity problems.
+ DoKmsg();
+ // Contains unrelated hardware info (camera, NFC, biometrics, ...).
+ DumpHals();
+ }
+
DumpPacketStats();
- DoKmsg();
DumpIpAddrAndRules();
dump_route_tables();
- DumpHals();
-
RunDumpsys("NETWORK DIAGNOSTICS", {"connectivity", "--diag"},
CommandOptions::WithTimeout(10).Build());
}
-// This method collects dumpsys for telephony debugging only
+// We use "telephony" here for legacy reasons, though this now really means "connectivity" (cellular
+// + wifi + networking). This method collects dumpsys for connectivity debugging only. General rules
+// for what can be included on user builds: all reported information MUST directly relate to
+// connectivity debugging or customer support and MUST NOT contain unrelated personally identifiable
+// information. This information MUST NOT identify user-installed packages (UIDs are OK, package
+// names are not), and MUST NOT contain logs of user application traffic.
+// TODO(b/148168577) rename this and other related fields/methods to "connectivity" instead.
static void DumpstateTelephonyOnly() {
DurationReporter duration_reporter("DUMPSTATE");
+
const CommandOptions DUMPSYS_COMPONENTS_OPTIONS = CommandOptions::WithTimeout(60).Build();
- DumpstateRadioCommon();
+ const bool include_sensitive_info = !PropertiesHelper::IsUserBuild();
- RunCommand("SYSTEM PROPERTIES", {"getprop"});
+ DumpstateRadioCommon(include_sensitive_info);
+
+ if (include_sensitive_info) {
+ // Contains too much unrelated PII, and given the unstructured nature of sysprops, we can't
+ // really cherrypick all of the connectivity-related ones. Apps generally have no business
+ // reading these anyway, and there should be APIs to supply the info in a more app-friendly
+ // way.
+ RunCommand("SYSTEM PROPERTIES", {"getprop"});
+ }
printf("========================================================\n");
printf("== Android Framework Services\n");
@@ -1632,15 +1677,28 @@
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));
+ // TODO(b/146521742) build out an argument to include bound services here for user builds
RunDumpsys("DUMPSYS", {"carrier_config"}, CommandOptions::WithTimeout(90).Build(),
SEC_TO_MSEC(10));
RunDumpsys("DUMPSYS", {"wifi"}, CommandOptions::WithTimeout(90).Build(),
SEC_TO_MSEC(10));
- RunDumpsys("BATTERYSTATS", {"batterystats"}, CommandOptions::WithTimeout(90).Build(),
+ RunDumpsys("DUMPSYS", {"netpolicy"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10));
+ RunDumpsys("DUMPSYS", {"network_management"}, CommandOptions::WithTimeout(90).Build(),
SEC_TO_MSEC(10));
+ if (include_sensitive_info) {
+ // Contains raw IP addresses, omit from reports on user builds.
+ RunDumpsys("DUMPSYS", {"netd"}, CommandOptions::WithTimeout(90).Build(), SEC_TO_MSEC(10));
+ // Contains raw destination IP/MAC addresses, omit from reports on user builds.
+ RunDumpsys("DUMPSYS", {"connmetrics"}, CommandOptions::WithTimeout(90).Build(),
+ SEC_TO_MSEC(10));
+ // Contains package/component names, omit from reports on user builds.
+ RunDumpsys("BATTERYSTATS", {"batterystats"}, CommandOptions::WithTimeout(90).Build(),
+ SEC_TO_MSEC(10));
+ // Contains package names, but should be relatively simple to remove them (also contains
+ // UIDs already), omit from reports on user builds.
+ RunDumpsys("BATTERYSTATS", {"deviceidle"}, CommandOptions::WithTimeout(90).Build(),
+ SEC_TO_MSEC(10));
+ }
printf("========================================================\n");
printf("== Running Application Services\n");
@@ -1648,18 +1706,24 @@
RunDumpsys("TELEPHONY SERVICES", {"activity", "service", "TelephonyDebugService"});
- printf("========================================================\n");
- printf("== Running Application Services (non-platform)\n");
- printf("========================================================\n");
+ if (include_sensitive_info) {
+ printf("========================================================\n");
+ printf("== Running Application Services (non-platform)\n");
+ printf("========================================================\n");
- RunDumpsys("APP SERVICES NON-PLATFORM", {"activity", "service", "all-non-platform"},
- DUMPSYS_COMPONENTS_OPTIONS);
+ // Contains package/component names and potential PII, omit from reports on user builds.
+ // To get dumps of the active CarrierService(s) on user builds, we supply an argument to the
+ // carrier_config dumpsys instead.
+ RunDumpsys("APP SERVICES NON-PLATFORM", {"activity", "service", "all-non-platform"},
+ DUMPSYS_COMPONENTS_OPTIONS);
- printf("========================================================\n");
- printf("== Checkins\n");
- printf("========================================================\n");
+ printf("========================================================\n");
+ printf("== Checkins\n");
+ printf("========================================================\n");
- RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"});
+ // Contains package/component names, omit from reports on user builds.
+ RunDumpsys("CHECKIN BATTERYSTATS", {"batterystats", "-c"});
+ }
printf("========================================================\n");
printf("== dumpstate: done (id %d)\n", ds.id_);
@@ -2271,6 +2335,7 @@
break;
case Dumpstate::BugreportMode::BUGREPORT_TELEPHONY:
options->telephony_only = true;
+ options->do_progress_updates = true;
options->do_fb = false;
options->do_broadcast = true;
break;
@@ -2821,8 +2886,13 @@
if (consent_result == UserConsentResult::UNAVAILABLE) {
// User has not responded yet.
uint64_t elapsed_ms = consent_callback_->getElapsedTimeMs();
- if (elapsed_ms < USER_CONSENT_TIMEOUT_MS) {
- uint delay_seconds = (USER_CONSENT_TIMEOUT_MS - elapsed_ms) / 1000;
+ // Telephony is a fast report type, particularly on user builds where information may be
+ // more aggressively limited. To give the user time to read the consent dialog, increase the
+ // timeout.
+ uint64_t timeout_ms = options_->telephony_only ? TELEPHONY_REPORT_USER_CONSENT_TIMEOUT_MS
+ : USER_CONSENT_TIMEOUT_MS;
+ if (elapsed_ms < timeout_ms) {
+ uint delay_seconds = (timeout_ms - elapsed_ms) / 1000;
MYLOGD("Did not receive user consent yet; going to wait for %d seconds", delay_seconds);
sleep(delay_seconds);
}
diff --git a/cmds/dumpstate/tests/dumpstate_test.cpp b/cmds/dumpstate/tests/dumpstate_test.cpp
index cff1d43..99d482f 100644
--- a/cmds/dumpstate/tests/dumpstate_test.cpp
+++ b/cmds/dumpstate/tests/dumpstate_test.cpp
@@ -372,12 +372,12 @@
EXPECT_TRUE(options_.do_broadcast);
EXPECT_TRUE(options_.do_zip_file);
EXPECT_TRUE(options_.telephony_only);
+ EXPECT_TRUE(options_.do_progress_updates);
// Other options retain default values
EXPECT_TRUE(options_.do_vibrate);
EXPECT_FALSE(options_.use_control_socket);
EXPECT_FALSE(options_.show_header_only);
- EXPECT_FALSE(options_.do_progress_updates);
EXPECT_FALSE(options_.is_remote_mode);
EXPECT_FALSE(options_.use_socket);
}
diff --git a/cmds/idlcli/Android.bp b/cmds/idlcli/Android.bp
index 08a31c1..402767a 100644
--- a/cmds/idlcli/Android.bp
+++ b/cmds/idlcli/Android.bp
@@ -15,6 +15,7 @@
cc_defaults {
name: "idlcli-defaults",
shared_libs: [
+ "android.hardware.vibrator-ndk_platform",
"android.hardware.vibrator@1.0",
"android.hardware.vibrator@1.1",
"android.hardware.vibrator@1.2",
@@ -24,7 +25,6 @@
"libhidlbase",
"liblog",
"libutils",
- "vintf-vibrator-ndk_platform",
],
cflags: [
"-DLOG_TAG=\"idlcli\"",
diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp
index 737c6c9..0fde31a 100644
--- a/cmds/installd/InstalldNativeService.cpp
+++ b/cmds/installd/InstalldNativeService.cpp
@@ -103,6 +103,9 @@
static constexpr const char* kPropApkVerityMode = "ro.apk_verity.mode";
static constexpr const char* kFuseProp = "persist.sys.fuse";
+static constexpr const char* kMntSdcardfs = "/mnt/runtime/default/";
+static constexpr const char* kMntFuse = "/mnt/pass_through/0/";
+
namespace {
constexpr const char* kDump = "android.permission.DUMP";
@@ -626,10 +629,8 @@
if (delete_dir_contents(path, true) != 0) {
res = error("Failed to delete contents of " + path);
}
- path = StringPrintf("%s/Android/obb/%s", extPath.c_str(), pkgname);
- if (delete_dir_contents(path, true) != 0) {
- res = error("Failed to delete contents of " + path);
- }
+ // Note that we explicitly don't delete OBBs - those are only removed on
+ // app uninstall.
}
}
}
@@ -2799,15 +2800,13 @@
std::getline(in, ignored);
if (android::base::GetBoolProperty(kFuseProp, false)) {
- // TODO(b/146139106): Use sdcardfs mounts on devices running sdcardfs so we don't bypass
- // it's VFS cache
- if (target.compare(0, 17, "/mnt/pass_through") == 0) {
+ if (target.find(kMntFuse) == 0) {
LOG(DEBUG) << "Found storage mount " << source << " at " << target;
mStorageMounts[source] = target;
}
} else {
#if !BYPASS_SDCARDFS
- if (target.compare(0, 21, "/mnt/runtime/default/") == 0) {
+ if (target.find(kMntSdcardfs) == 0) {
LOG(DEBUG) << "Found storage mount " << source << " at " << target;
mStorageMounts[source] = target;
}
@@ -2822,17 +2821,6 @@
std::lock_guard<std::recursive_mutex> lock(mMountsLock);
const char* uuid_ = uuid ? uuid->c_str() : nullptr;
auto path = StringPrintf("%s/media", create_data_path(uuid_).c_str());
- if (android::base::GetBoolProperty(kFuseProp, false)) {
- // TODO(b/146139106): This is only safe on devices not running sdcardfs where there is no
- // risk of bypassing the sdcardfs VFS cache
-
- // Always use the lower filesystem path on FUSE enabled devices not running sdcardfs
- // The upper filesystem path, /mnt/pass_through/<userid>/<vol>/ which was a bind mount
- // to the lower filesytem may have been unmounted already when a user is
- // removed and the path will now be pointing to a tmpfs without content
- return StringPrintf("%s/%u", path.c_str(), userid);
- }
-
auto resolved = mStorageMounts[path];
if (resolved.empty()) {
LOG(WARNING) << "Failed to find storage mount for " << path;
diff --git a/cmds/installd/QuotaUtils.cpp b/cmds/installd/QuotaUtils.cpp
index b238dd3..f2abf3a 100644
--- a/cmds/installd/QuotaUtils.cpp
+++ b/cmds/installd/QuotaUtils.cpp
@@ -97,6 +97,26 @@
}
}
+int64_t GetOccupiedSpaceForProjectId(const std::string& uuid, int projectId) {
+ const std::string device = FindQuotaDeviceForUuid(uuid);
+ if (device == "") {
+ return -1;
+ }
+ struct dqblk dq;
+ if (quotactl(QCMD(Q_GETQUOTA, PRJQUOTA), device.c_str(), projectId,
+ reinterpret_cast<char*>(&dq)) != 0) {
+ if (errno != ESRCH) {
+ PLOG(ERROR) << "Failed to quotactl " << device << " for Project ID " << projectId;
+ }
+ return -1;
+ } else {
+#if MEASURE_DEBUG
+ LOG(DEBUG) << "quotactl() for Project ID " << projectId << " " << dq.dqb_curspace;
+#endif
+ return dq.dqb_curspace;
+ }
+}
+
int64_t GetOccupiedSpaceForGid(const std::string& uuid, gid_t gid) {
const std::string device = FindQuotaDeviceForUuid(uuid);
if (device == "") {
diff --git a/cmds/installd/QuotaUtils.h b/cmds/installd/QuotaUtils.h
index 9ad170f..96aca04 100644
--- a/cmds/installd/QuotaUtils.h
+++ b/cmds/installd/QuotaUtils.h
@@ -35,6 +35,8 @@
/* Get the current occupied space in bytes for a gid or -1 if fails */
int64_t GetOccupiedSpaceForGid(const std::string& uuid, gid_t gid);
+/* Get the current occupied space in bytes for a project id or -1 if fails */
+int64_t GetOccupiedSpaceForProjectId(const std::string& uuid, int projectId);
} // namespace installd
} // namespace android
diff --git a/cmds/installd/TEST_MAPPING b/cmds/installd/TEST_MAPPING
index 287f2d9..c6583a1 100644
--- a/cmds/installd/TEST_MAPPING
+++ b/cmds/installd/TEST_MAPPING
@@ -15,6 +15,10 @@
{
"name": "installd_utils_test"
},
+ // AdoptableHostTest moves packages, part of which is handled by installd
+ {
+ "name": "AdoptableHostTest"
+ },
{
"name": "CtsUsesLibraryHostTestCases"
},
diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp
index f95e445..70bbc33 100644
--- a/cmds/installd/dexopt.cpp
+++ b/cmds/installd/dexopt.cpp
@@ -299,9 +299,10 @@
// 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";
+static const char* ENABLE_JITZYGOTE_IMAGE = "enable_apex_image";
+// Location of the JIT Zygote image.
+static const char* kJitZygoteImage =
+ "boot.art:/nonx/boot-framework.art!/system/etc/boot-image.prof";
// Phenotype property name for enabling profiling the boot class path.
static const char* PROFILE_BOOT_CLASS_PATH = "profilebootclasspath";
@@ -405,9 +406,9 @@
GetBoolProperty(kMinidebugInfoSystemProperty, kMinidebugInfoSystemPropertyDefault);
std::string boot_image;
- std::string use_apex_image =
+ std::string use_jitzygote_image =
server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE,
- ENABLE_APEX_IMAGE,
+ ENABLE_JITZYGOTE_IMAGE,
/*default_value=*/ "");
std::string profile_boot_class_path = GetProperty("dalvik.vm.profilebootclasspath", "");
@@ -417,10 +418,10 @@
PROFILE_BOOT_CLASS_PATH,
/*default_value=*/ profile_boot_class_path);
- if (use_apex_image == "true" || profile_boot_class_path == "true") {
- boot_image = StringPrintf("-Ximage:%s", kApexImage);
+ if (use_jitzygote_image == "true" || profile_boot_class_path == "true") {
+ boot_image = StringPrintf("--boot-image=%s", kJitZygoteImage);
} else {
- boot_image = MapPropertyToArg("dalvik.vm.boot-image", "-Ximage:%s");
+ boot_image = MapPropertyToArg("dalvik.vm.boot-image", "--boot-image=%s");
}
// clang FORTIFY doesn't let us use strlen in constant array bounds, so we
@@ -513,7 +514,8 @@
AddArg(instruction_set_variant_arg);
AddArg(instruction_set_features_arg);
- AddRuntimeArg(boot_image);
+ AddArg(boot_image);
+
AddRuntimeArg(bootclasspath);
AddRuntimeArg(dex2oat_Xms_arg);
AddRuntimeArg(dex2oat_Xmx_arg);
diff --git a/cmds/installd/otapreopt_chroot.cpp b/cmds/installd/otapreopt_chroot.cpp
index 3ff9d11..7c989f6 100644
--- a/cmds/installd/otapreopt_chroot.cpp
+++ b/cmds/installd/otapreopt_chroot.cpp
@@ -73,7 +73,7 @@
for (const apex::ApexFile& apex_file : active_packages) {
const std::string& package_path = apex_file.GetPath();
base::Result<void> status = apex::deactivatePackage(package_path);
- if (!status) {
+ if (!status.ok()) {
LOG(ERROR) << "Failed to deactivate " << package_path << ": "
<< status.error();
}
diff --git a/cmds/installd/tests/installd_utils_test.cpp b/cmds/installd/tests/installd_utils_test.cpp
index e61eb6e..d236f76 100644
--- a/cmds/installd/tests/installd_utils_test.cpp
+++ b/cmds/installd/tests/installd_utils_test.cpp
@@ -104,12 +104,12 @@
EXPECT_EQ(-1, validate_apk_path(badint2))
<< badint2 << " should be rejected as a invalid path";
- // Only one subdir should be allowed.
- const char *bad_path3 = TEST_APP_DIR "example.com/subdir/pkg.apk";
+ // Should not have more than two sub directories
+ const char *bad_path3 = TEST_APP_DIR "random/example.com/subdir/pkg.apk";
EXPECT_EQ(-1, validate_apk_path(bad_path3))
<< bad_path3 << " should be rejected as a invalid path";
- const char *bad_path4 = TEST_APP_DIR "example.com/subdir/../pkg.apk";
+ const char *bad_path4 = TEST_APP_DIR "random/example.com/subdir/pkg.apk";
EXPECT_EQ(-1, validate_apk_path(bad_path4))
<< bad_path4 << " should be rejected as a invalid path";
@@ -120,6 +120,7 @@
TEST_F(UtilsTest, IsValidApkPath_TopDir) {
EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/com.example"));
+ EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/random/com.example"));
EXPECT_EQ(0, validate_apk_path(TEST_EXPAND_DIR "app/com.example"));
EXPECT_EQ(-1, validate_apk_path(TEST_DATA_DIR "data/com.example"));
EXPECT_EQ(-1, validate_apk_path(TEST_EXPAND_DIR "data/com.example"));
@@ -127,6 +128,7 @@
TEST_F(UtilsTest, IsValidApkPath_TopFile) {
EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/com.example/base.apk"));
+ EXPECT_EQ(0, validate_apk_path(TEST_DATA_DIR "app/random/com.example/base.apk"));
EXPECT_EQ(0, validate_apk_path(TEST_EXPAND_DIR "app/com.example/base.apk"));
EXPECT_EQ(-1, validate_apk_path(TEST_DATA_DIR "data/com.example/base.apk"));
EXPECT_EQ(-1, validate_apk_path(TEST_EXPAND_DIR "data/com.example/base.apk"));
@@ -134,6 +136,7 @@
TEST_F(UtilsTest, IsValidApkPath_OatDir) {
EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat"));
+ EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/random/com.example/oat"));
EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat"));
EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat"));
EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat"));
@@ -141,6 +144,7 @@
TEST_F(UtilsTest, IsValidApkPath_OatDirDir) {
EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat/arm64"));
+ EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/random/com.example/oat/arm64"));
EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat/arm64"));
EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat/arm64"));
EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat/arm64"));
@@ -148,6 +152,7 @@
TEST_F(UtilsTest, IsValidApkPath_OatDirDirFile) {
EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/com.example/oat/arm64/base.odex"));
+ EXPECT_EQ(0, validate_apk_path_subdirs(TEST_DATA_DIR "app/random/com.example/oat/arm64/base.odex"));
EXPECT_EQ(0, validate_apk_path_subdirs(TEST_EXPAND_DIR "app/com.example/oat/arm64/base.odex"));
EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_DATA_DIR "data/com.example/oat/arm64/base.odex"));
EXPECT_EQ(-1, validate_apk_path_subdirs(TEST_EXPAND_DIR "data/com.example/oat/arm64/base.odex"));
@@ -164,6 +169,10 @@
EXPECT_EQ(0, validate_apk_path(path2))
<< path2 << " should be allowed as a valid path";
+ const char *path3 = TEST_APP_DIR "random/example.com/example.apk";
+ EXPECT_EQ(0, validate_apk_path(path3))
+ << path3 << " should be allowed as a valid path";
+
const char *badpriv1 = TEST_APP_PRIVATE_DIR "../example.apk";
EXPECT_EQ(-1, validate_apk_path(badpriv1))
<< badpriv1 << " should be rejected as a invalid path";
@@ -172,16 +181,16 @@
EXPECT_EQ(-1, validate_apk_path(badpriv2))
<< badpriv2 << " should be rejected as a invalid path";
- // Only one subdir should be allowed.
- const char *bad_path3 = TEST_APP_PRIVATE_DIR "example.com/subdir/pkg.apk";
+ // Only one or two subdir should be allowed.
+ const char *bad_path3 = TEST_APP_PRIVATE_DIR "random/example.com/subdir/pkg.apk";
EXPECT_EQ(-1, validate_apk_path(bad_path3))
<< bad_path3 << " should be rejected as a invalid path";
- const char *bad_path4 = TEST_APP_PRIVATE_DIR "example.com/subdir/../pkg.apk";
+ const char *bad_path4 = TEST_APP_PRIVATE_DIR "random/example.com/subdir/../pkg.apk";
EXPECT_EQ(-1, validate_apk_path(bad_path4))
<< bad_path4 << " should be rejected as a invalid path";
- const char *bad_path5 = TEST_APP_PRIVATE_DIR "example.com1/../example.com2/pkg.apk";
+ const char *bad_path5 = TEST_APP_PRIVATE_DIR "random/example.com1/../example.com2/pkg.apk";
EXPECT_EQ(-1, validate_apk_path(bad_path5))
<< bad_path5 << " should be rejected as a invalid path";
}
@@ -229,10 +238,16 @@
<< badasec6 << " should be rejected as a invalid path";
}
-TEST_F(UtilsTest, IsValidApkPath_TwoSubdirFail) {
- const char *badasec7 = TEST_ASEC_DIR "com.example.asec/subdir1/pkg.apk";
- EXPECT_EQ(-1, validate_apk_path(badasec7))
- << badasec7 << " should be rejected as a invalid path";
+TEST_F(UtilsTest, IsValidApkPath_TwoSubdir) {
+ const char *badasec7 = TEST_ASEC_DIR "random/com.example.asec/pkg.apk";
+ EXPECT_EQ(0, validate_apk_path(badasec7))
+ << badasec7 << " should be allowed as a valid path";
+}
+
+TEST_F(UtilsTest, IsValidApkPath_ThreeSubdirFail) {
+ const char *badasec8 = TEST_ASEC_DIR "random/com.example.asec/subdir/pkg.apk";
+ EXPECT_EQ(-1, validate_apk_path(badasec8))
+ << badasec8 << " should be rejcted as an invalid path";
}
TEST_F(UtilsTest, CheckSystemApp_Dir1) {
@@ -511,8 +526,8 @@
EXPECT_EQ(0, validate_apk_path("/data/app/com.example"));
EXPECT_EQ(0, validate_apk_path("/data/app/com.example/file"));
EXPECT_EQ(0, validate_apk_path("/data/app/com.example//file"));
- EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/"));
- EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/file"));
+ EXPECT_EQ(0, validate_apk_path("/data/app/random/com.example/"));
+ EXPECT_EQ(0, validate_apk_path("/data/app/random/com.example/file"));
EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/dir/file"));
EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/dir//file"));
EXPECT_NE(0, validate_apk_path("/data/app/com.example/dir/dir/dir/file"));
@@ -527,8 +542,10 @@
EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/file"));
EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/file"));
EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir//file"));
- EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/file"));
- EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir//file"));
+ EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/file"));
+ EXPECT_EQ(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir//file"));
+ EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/dir/file"));
+ EXPECT_NE(0, validate_apk_path_subdirs("/data/app/com.example/dir/dir/dir/dir//file"));
}
TEST_F(UtilsTest, MatchExtension_Valid) {
diff --git a/cmds/installd/utils.cpp b/cmds/installd/utils.cpp
index 4eb1df0..2f79552 100644
--- a/cmds/installd/utils.cpp
+++ b/cmds/installd/utils.cpp
@@ -945,11 +945,11 @@
}
int validate_apk_path(const char* path) {
- return validate_apk_path_internal(path, 1 /* maxSubdirs */);
+ return validate_apk_path_internal(path, 2 /* maxSubdirs */);
}
int validate_apk_path_subdirs(const char* path) {
- return validate_apk_path_internal(path, 3 /* maxSubdirs */);
+ return validate_apk_path_internal(path, 4 /* maxSubdirs */);
}
int ensure_config_user_dirs(userid_t userid) {
diff --git a/cmds/servicemanager/ServiceManager.cpp b/cmds/servicemanager/ServiceManager.cpp
index ae74ac3..abe6436 100644
--- a/cmds/servicemanager/ServiceManager.cpp
+++ b/cmds/servicemanager/ServiceManager.cpp
@@ -423,11 +423,12 @@
void ServiceManager::handleClientCallbacks() {
for (const auto& [name, service] : mNameToService) {
- handleServiceClientCallback(name);
+ handleServiceClientCallback(name, true);
}
}
-ssize_t ServiceManager::handleServiceClientCallback(const std::string& serviceName) {
+ssize_t ServiceManager::handleServiceClientCallback(const std::string& serviceName,
+ bool isCalledOnInterval) {
auto serviceIt = mNameToService.find(serviceName);
if (serviceIt == mNameToService.end() || mNameToClientCallback.count(serviceName) < 1) {
return -1;
@@ -451,14 +452,17 @@
service.guaranteeClient = false;
}
- if (hasClients && !service.hasClients) {
- // client was retrieved in some other way
- sendClientCallbackNotifications(serviceName, true);
- }
+ // only send notifications if this was called via the interval checking workflow
+ if (isCalledOnInterval) {
+ if (hasClients && !service.hasClients) {
+ // client was retrieved in some other way
+ sendClientCallbackNotifications(serviceName, true);
+ }
- // there are no more clients, but the callback has not been called yet
- if (!hasClients && service.hasClients) {
- sendClientCallbackNotifications(serviceName, false);
+ // there are no more clients, but the callback has not been called yet
+ if (!hasClients && service.hasClients) {
+ sendClientCallbackNotifications(serviceName, false);
+ }
}
return count;
@@ -518,7 +522,7 @@
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
- int clients = handleServiceClientCallback(name);
+ int clients = handleServiceClientCallback(name, false);
// clients < 0: feature not implemented or other error. Assume clients.
// Otherwise:
@@ -527,7 +531,7 @@
// So, if clients > 2, then at least one other service on the system must hold a refcount.
if (clients < 0 || clients > 2) {
// client callbacks are either disabled or there are other clients
- LOG(INFO) << "Tried to unregister " << name << " but there are clients: " << clients;
+ LOG(INFO) << "Tried to unregister " << name << ", but there are clients: " << clients;
return Status::fromExceptionCode(Status::EX_ILLEGAL_STATE);
}
diff --git a/cmds/servicemanager/ServiceManager.h b/cmds/servicemanager/ServiceManager.h
index 77f5250..a2fc5a8 100644
--- a/cmds/servicemanager/ServiceManager.h
+++ b/cmds/servicemanager/ServiceManager.h
@@ -75,7 +75,7 @@
void removeRegistrationCallback(const wp<IBinder>& who,
ServiceCallbackMap::iterator* it,
bool* found);
- ssize_t handleServiceClientCallback(const std::string& serviceName);
+ ssize_t handleServiceClientCallback(const std::string& serviceName, bool isCalledOnInterval);
// Also updates mHasClients (of what the last callback was)
void sendClientCallbackNotifications(const std::string& serviceName, bool hasClients);
// removes a callback from mNameToClientCallback, deleting the entry if the vector is empty
diff --git a/libs/adbd_auth/Android.bp b/libs/adbd_auth/Android.bp
index 9cf0143..8ac044c 100644
--- a/libs/adbd_auth/Android.bp
+++ b/libs/adbd_auth/Android.bp
@@ -20,11 +20,14 @@
"-Wthread-safety",
"-Werror",
],
+ stl: "libc++_static",
+
srcs: ["adbd_auth.cpp"],
export_include_dirs: ["include"],
version_script: "libadbd_auth.map.txt",
stubs: {
+ versions: ["1"],
symbol_file: "libadbd_auth.map.txt",
},
@@ -36,7 +39,7 @@
}
},
- shared_libs: [
+ static_libs: [
"libbase",
"libcutils",
"liblog",
diff --git a/libs/adbd_auth/adbd_auth.cpp b/libs/adbd_auth/adbd_auth.cpp
index 6479109..a9c2311 100644
--- a/libs/adbd_auth/adbd_auth.cpp
+++ b/libs/adbd_auth/adbd_auth.cpp
@@ -178,6 +178,10 @@
this->callbacks_.key_authorized(arg, id);
this->dispatched_prompt_ = std::nullopt;
+
+ // We need to dispatch pending prompts here upon success as well,
+ // since we might have multiple queued prompts.
+ DispatchPendingPrompt();
} else if (packet[0] == 'N' && packet[1] == 'O') {
CHECK_EQ(2UL, packet.length());
// TODO: Do we want a callback if the key is denied?
diff --git a/libs/android_runtime_lazy/android_runtime_lazy.cpp b/libs/android_runtime_lazy/android_runtime_lazy.cpp
index 98d8e8a..8062be6 100644
--- a/libs/android_runtime_lazy/android_runtime_lazy.cpp
+++ b/libs/android_runtime_lazy/android_runtime_lazy.cpp
@@ -15,6 +15,7 @@
*/
#define LOG_TAG "ANDROID_RUNTIME_LAZY"
#include "android_runtime/AndroidRuntime.h"
+#include "android_os_Parcel.h"
#include "android_util_Binder.h"
#include <dlfcn.h>
@@ -28,12 +29,18 @@
std::once_flag loadFlag;
typedef JNIEnv* (*getJNIEnv_t)();
+
+// android_util_Binder.h
typedef sp<IBinder> (*ibinderForJavaObject_t)(JNIEnv* env, jobject obj);
typedef jobject (*javaObjectForIBinder_t)(JNIEnv* env, const sp<IBinder>& val);
+// android_os_Parcel.h
+typedef Parcel* (*parcelForJavaObject_t)(JNIEnv* env, jobject obj);
+
getJNIEnv_t _getJNIEnv;
ibinderForJavaObject_t _ibinderForJavaObject;
javaObjectForIBinder_t _javaObjectForIBinder;
+parcelForJavaObject_t _parcelForJavaObject;
void load() {
std::call_once(loadFlag, []() {
@@ -64,6 +71,13 @@
ALOGW("Could not find javaObjectForIBinder.");
// no return
}
+
+ _parcelForJavaObject = reinterpret_cast<parcelForJavaObject_t>(
+ dlsym(handle, "_ZN7android19parcelForJavaObjectEP7_JNIEnvP8_jobject"));
+ if (_parcelForJavaObject == nullptr) {
+ ALOGW("Could not find parcelForJavaObject.");
+ // no return
+ }
});
}
@@ -95,4 +109,12 @@
return _javaObjectForIBinder(env, val);
}
+Parcel* parcelForJavaObject(JNIEnv* env, jobject obj) {
+ load();
+ if (_parcelForJavaObject == nullptr) {
+ return nullptr;
+ }
+ return _parcelForJavaObject(env, obj);
+}
+
} // namespace android
diff --git a/libs/android_runtime_lazy/include/android_os_Parcel.h b/libs/android_runtime_lazy/include/android_os_Parcel.h
new file mode 100644
index 0000000..19b094d
--- /dev/null
+++ b/libs/android_runtime_lazy/include/android_os_Parcel.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2020 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/Parcel.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.
+
+Parcel* parcelForJavaObject(JNIEnv* env, jobject obj);
+
+} // namespace android
diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp
index 079dd82..bc541f4 100644
--- a/libs/binder/Android.bp
+++ b/libs/binder/Android.bp
@@ -73,7 +73,6 @@
// or dessert updates. Instead, apex users should use libbinder_ndk.
apex_available: [
"//apex_available:platform",
- "com.android.vndk.current",
// TODO(b/139016109) remove these three
"com.android.media.swcodec",
"test_com.android.media.swcodec",
@@ -139,7 +138,6 @@
"liblog",
"libcutils",
"libutils",
- "libbinderthreadstate",
],
header_libs: [
@@ -172,7 +170,6 @@
name: "libbinder_aidl_test_stub",
local_include_dir: "aidl",
srcs: [":libbinder_aidl"],
- visibility: [":__subpackages__"],
vendor_available: true,
backend: {
java: {
diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp
index 238c9dc..f16c39c 100644
--- a/libs/binder/BpBinder.cpp
+++ b/libs/binder/BpBinder.cpp
@@ -435,7 +435,8 @@
Vector<Obituary>* obits = mObituaries;
if(obits != nullptr) {
if (!obits->isEmpty()) {
- ALOGI("onLastStrongRef automatically unlinking death recipients");
+ ALOGI("onLastStrongRef automatically unlinking death recipients: %s",
+ mDescriptorCache.size() ? String8(mDescriptorCache).c_str() : "<uncached descriptor>");
}
if (ipc) ipc->clearDeathNotification(mHandle, this);
diff --git a/libs/binder/BufferedTextOutput.cpp b/libs/binder/BufferedTextOutput.cpp
index fb424fd..856a178 100644
--- a/libs/binder/BufferedTextOutput.cpp
+++ b/libs/binder/BufferedTextOutput.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include <binder/BufferedTextOutput.h>
+#include "BufferedTextOutput.h"
#include <binder/Debug.h>
#include <cutils/atomic.h>
diff --git a/libs/binder/include/binder/BufferedTextOutput.h b/libs/binder/BufferedTextOutput.h
similarity index 100%
rename from libs/binder/include/binder/BufferedTextOutput.h
rename to libs/binder/BufferedTextOutput.h
diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp
index 4981d7a..4dcd07a 100644
--- a/libs/binder/IPCThreadState.cpp
+++ b/libs/binder/IPCThreadState.cpp
@@ -17,7 +17,6 @@
#define LOG_TAG "IPCThreadState"
#include <binder/IPCThreadState.h>
-#include <binderthreadstate/IPCThreadStateBase.h>
#include <binder/Binder.h>
#include <binder/BpBinder.h>
@@ -803,6 +802,7 @@
IPCThreadState::IPCThreadState()
: mProcess(ProcessState::self()),
+ mServingStackPointer(nullptr),
mWorkSource(kUnsetWorkSource),
mPropagateWorkSource(false),
mStrictModePolicy(0),
@@ -813,7 +813,6 @@
clearCaller();
mIn.setDataCapacity(256);
mOut.setDataCapacity(256);
- mIPCThreadStateBase = IPCThreadStateBase::self();
}
IPCThreadState::~IPCThreadState()
@@ -1163,9 +1162,6 @@
"Not enough command data for brTRANSACTION");
if (result != NO_ERROR) break;
- //Record the fact that we're in a binder call.
- mIPCThreadStateBase->pushCurrentState(
- IPCThreadStateBase::CallState::BINDER);
Parcel buffer;
buffer.ipcSetDataReference(
reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
@@ -1173,6 +1169,9 @@
reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
tr.offsets_size/sizeof(binder_size_t), freeBuffer, this);
+ const void* origServingStackPointer = mServingStackPointer;
+ mServingStackPointer = &origServingStackPointer; // anything on the stack
+
const pid_t origPid = mCallingPid;
const char* origSid = mCallingSid;
const uid_t origUid = mCallingUid;
@@ -1223,7 +1222,6 @@
error = the_context_object->transact(tr.code, buffer, &reply, tr.flags);
}
- mIPCThreadStateBase->popCurrentState();
//ALOGI("<<<< TRANSACT from pid %d restore pid %d sid %s uid %d\n",
// mCallingPid, origPid, (origSid ? origSid : "<N/A>"), origUid);
@@ -1235,6 +1233,7 @@
LOG_ONEWAY("NOT sending reply to %d!", mCallingPid);
}
+ mServingStackPointer = origServingStackPointer;
mCallingPid = origPid;
mCallingSid = origSid;
mCallingUid = origUid;
@@ -1290,8 +1289,8 @@
return result;
}
-bool IPCThreadState::isServingCall() const {
- return mIPCThreadStateBase->getCurrentBinderCallState() == IPCThreadStateBase::CallState::BINDER;
+const void* IPCThreadState::getServingStackPointer() const {
+ return mServingStackPointer;
}
void IPCThreadState::threadDestructor(void *st)
diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp
index 5ca9156..9888b59 100644
--- a/libs/binder/IServiceManager.cpp
+++ b/libs/binder/IServiceManager.cpp
@@ -85,27 +85,38 @@
sp<AidlServiceManager> mTheRealServiceManager;
};
+static std::once_flag gSmOnce;
+static sp<IServiceManager> gDefaultServiceManager;
+
sp<IServiceManager> defaultServiceManager()
{
- static Mutex gDefaultServiceManagerLock;
- static sp<IServiceManager> gDefaultServiceManager;
-
- if (gDefaultServiceManager != nullptr) return gDefaultServiceManager;
-
- {
- AutoMutex _l(gDefaultServiceManagerLock);
- while (gDefaultServiceManager == nullptr) {
- gDefaultServiceManager = new ServiceManagerShim(
- interface_cast<AidlServiceManager>(
- ProcessState::self()->getContextObject(nullptr)));
- if (gDefaultServiceManager == nullptr)
+ std::call_once(gSmOnce, []() {
+ sp<AidlServiceManager> sm = nullptr;
+ while (sm == nullptr) {
+ sm = interface_cast<AidlServiceManager>(ProcessState::self()->getContextObject(nullptr));
+ if (sm == nullptr) {
sleep(1);
+ }
}
- }
+
+ gDefaultServiceManager = new ServiceManagerShim(sm);
+ });
return gDefaultServiceManager;
}
+void setDefaultServiceManager(const sp<IServiceManager>& sm) {
+ bool called = false;
+ std::call_once(gSmOnce, [&]() {
+ gDefaultServiceManager = sm;
+ called = true;
+ });
+
+ if (!called) {
+ LOG_ALWAYS_FATAL("setDefaultServiceManager() called after defaultServiceManager().");
+ }
+}
+
#if !defined(__ANDROID_VNDK__) && defined(__ANDROID__)
// IPermissionController is not accessible to vendors
diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp
index dc9482c..f064bd7 100644
--- a/libs/binder/LazyServiceRegistrar.cpp
+++ b/libs/binder/LazyServiceRegistrar.cpp
@@ -53,14 +53,13 @@
struct Service {
sp<IBinder> service;
- std::string name;
bool allowIsolated;
int dumpFlags;
};
/**
- * Number of services that have been registered.
+ * Map of registered names and services
*/
- std::vector<Service> mRegisteredServices;
+ std::map<std::string, Service> mRegisteredServices;
};
bool ClientCounterCallback::registerService(const sp<IBinder>& service, const std::string& name,
@@ -68,20 +67,24 @@
auto manager = interface_cast<AidlServiceManager>(
ProcessState::self()->getContextObject(nullptr));
- ALOGI("Registering service %s", name.c_str());
+ bool reRegister = mRegisteredServices.count(name) > 0;
+ std::string regStr = (reRegister) ? "Re-registering" : "Registering";
+ ALOGI("%s service %s", regStr.c_str(), name.c_str());
if (!manager->addService(name.c_str(), service, allowIsolated, dumpFlags).isOk()) {
ALOGE("Failed to register service %s", name.c_str());
return false;
}
- if (!manager->registerClientCallback(name, service, this).isOk())
- {
- ALOGE("Failed to add client callback for service %s", name.c_str());
- return false;
+ if (!manager->registerClientCallback(name, service, this).isOk()) {
+ ALOGE("Failed to add client callback for service %s", name.c_str());
+ return false;
}
- mRegisteredServices.push_back({service, name, allowIsolated, dumpFlags});
+ if (!reRegister) {
+ // Only add this when a service is added for the first time, as it is not removed
+ mRegisteredServices[name] = {service, allowIsolated, dumpFlags};
+ }
return true;
}
@@ -119,10 +122,11 @@
for (; unRegisterIt != mRegisteredServices.end(); ++unRegisterIt) {
auto& entry = (*unRegisterIt);
- bool success = manager->tryUnregisterService(entry.name, entry.service).isOk();
+ bool success = manager->tryUnregisterService(entry.first, entry.second.service).isOk();
+
if (!success) {
- ALOGI("Failed to unregister service %s", entry.name.c_str());
+ ALOGI("Failed to unregister service %s", entry.first.c_str());
break;
}
}
@@ -137,7 +141,8 @@
auto& entry = (*reRegisterIt);
// re-register entry
- if (!registerService(entry.service, entry.name, entry.allowIsolated, entry.dumpFlags)) {
+ if (!registerService(entry.second.service, entry.first, entry.second.allowIsolated,
+ entry.second.dumpFlags)) {
// Must restart. Otherwise, clients will never be able to get a hold of this service.
ALOGE("Bad state: could not re-register services");
}
diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp
index 9be06cd..822247f 100644
--- a/libs/binder/Parcel.cpp
+++ b/libs/binder/Parcel.cpp
@@ -505,7 +505,7 @@
}
}
-#if defined(__ANDROID_APEX_COM_ANDROID_VNDK_CURRENT__) || (defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__))
+#if defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
constexpr int32_t kHeader = B_PACK_CHARS('V', 'N', 'D', 'R');
#else
constexpr int32_t kHeader = B_PACK_CHARS('S', 'Y', 'S', 'T');
@@ -2543,11 +2543,13 @@
if (objectsSize == 0) {
free(mObjects);
mObjects = nullptr;
+ mObjectsCapacity = 0;
} else {
binder_size_t* objects =
(binder_size_t*)realloc(mObjects, objectsSize*sizeof(binder_size_t));
if (objects) {
mObjects = objects;
+ mObjectsCapacity = objectsSize;
}
}
mObjectsSize = objectsSize;
diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp
index ce2cd99..c0f5e31 100644
--- a/libs/binder/ProcessState.cpp
+++ b/libs/binder/ProcessState.cpp
@@ -60,14 +60,14 @@
: mIsMain(isMain)
{
}
-
+
protected:
virtual bool threadLoop()
{
IPCThreadState::self()->joinThreadPool(mIsMain);
return false;
}
-
+
const bool mIsMain;
};
@@ -296,7 +296,7 @@
void ProcessState::expungeHandle(int32_t handle, IBinder* binder)
{
AutoMutex _l(mLock);
-
+
handle_entry* e = lookupHandleLocked(handle);
// This handle may have already been replaced with a new BpBinder
@@ -387,7 +387,7 @@
{
// TODO(b/139016109): enforce in build system
-#if defined(__ANDROID_APEX__) && !defined(__ANDROID_APEX_COM_ANDROID_VNDK_CURRENT__)
+#if defined(__ANDROID_APEX__)
LOG_ALWAYS_FATAL("Cannot use libbinder in APEX (only system.img libbinder) since it is not stable.");
#endif
@@ -416,5 +416,5 @@
}
mDriverFD = -1;
}
-
+
} // namespace android
diff --git a/libs/binder/Static.cpp b/libs/binder/Static.cpp
index bd40536..7a77f6d 100644
--- a/libs/binder/Static.cpp
+++ b/libs/binder/Static.cpp
@@ -19,7 +19,7 @@
#include "Static.h"
-#include <binder/BufferedTextOutput.h>
+#include "BufferedTextOutput.h"
#include <binder/IPCThreadState.h>
#include <utils/Log.h>
diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING
index b3afd81..9aa7651 100644
--- a/libs/binder/TEST_MAPPING
+++ b/libs/binder/TEST_MAPPING
@@ -19,7 +19,16 @@
"name": "binderStabilityTest"
},
{
+ "name": "libbinder_ndk_unit_test"
+ },
+ {
"name": "CtsNdkBinderTestCases"
+ },
+ {
+ "name": "aidl_lazy_test"
+ },
+ {
+ "name": "libbinderthreadstateutils_test"
}
]
}
diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h
index ff9244e..4818889 100644
--- a/libs/binder/include/binder/IPCThreadState.h
+++ b/libs/binder/include/binder/IPCThreadState.h
@@ -29,8 +29,6 @@
// ---------------------------------------------------------------------------
namespace android {
-class IPCThreadStateBase;
-
class IPCThreadState
{
public:
@@ -113,31 +111,12 @@
// Service manager registration
void setTheContextObject(sp<BBinder> obj);
- // Is this thread currently serving a binder call. This method
- // returns true if while traversing backwards from the function call
- // stack for this thread, we encounter a function serving a binder
- // call before encountering a hwbinder call / hitting the end of the
- // call stack.
- // Eg: If thread T1 went through the following call pattern
- // 1) T1 receives and executes hwbinder call H1.
- // 2) While handling H1, T1 makes binder call B1.
- // 3) The handler of B1, calls into T1 with a callback B2.
- // If isServingCall() is called during H1 before 3), this method
- // will return false, else true.
+ // WARNING: DO NOT USE THIS API
//
- // ----
- // | B2 | ---> While callback B2 is being handled, during 3).
- // ----
- // | H1 | ---> While H1 is being handled.
- // ----
- // Fig: Thread Call stack while handling B2
- //
- // This is since after 3), while traversing the thread call stack,
- // we hit a binder call before a hwbinder call / end of stack. This
- // method may be typically used to determine whether to use
- // hardware::IPCThreadState methods or IPCThreadState methods to
- // infer information about thread state.
- bool isServingCall() const;
+ // Returns a pointer to the stack from the last time a transaction
+ // was initiated by the kernel. Used to compare when making nested
+ // calls between multiple different transports.
+ const void* getServingStackPointer() const;
// The work source represents the UID of the process we should attribute the transaction
// to. We use -1 to specify that the work source was not set using #setWorkSource.
@@ -181,6 +160,7 @@
Parcel mIn;
Parcel mOut;
status_t mLastError;
+ const void* mServingStackPointer;
pid_t mCallingPid;
const char* mCallingSid;
uid_t mCallingUid;
@@ -191,7 +171,6 @@
bool mPropagateWorkSource;
int32_t mStrictModePolicy;
int32_t mLastTransactionBinderFlags;
- IPCThreadStateBase *mIPCThreadStateBase;
ProcessState::CallRestriction mCallRestriction;
};
diff --git a/libs/binder/include/binder/IServiceManager.h b/libs/binder/include/binder/IServiceManager.h
index 2c43263..1d520c1 100644
--- a/libs/binder/include/binder/IServiceManager.h
+++ b/libs/binder/include/binder/IServiceManager.h
@@ -100,6 +100,14 @@
sp<IServiceManager> defaultServiceManager();
+/**
+ * Directly set the default service manager. Only used for testing.
+ * Note that the caller is responsible for caling this method
+ * *before* any call to defaultServiceManager(); if the latter is
+ * called first, setDefaultServiceManager() will abort.
+ */
+void setDefaultServiceManager(const sp<IServiceManager>& sm);
+
template<typename INTERFACE>
sp<INTERFACE> waitForService(const String16& name) {
const sp<IServiceManager> sm = defaultServiceManager();
diff --git a/libs/binder/include/binder/Stability.h b/libs/binder/include/binder/Stability.h
index b2f51d3..2894482 100644
--- a/libs/binder/include/binder/Stability.h
+++ b/libs/binder/include/binder/Stability.h
@@ -81,7 +81,7 @@
VINTF = 0b111111,
};
-#if defined(__ANDROID_APEX_COM_ANDROID_VNDK_CURRENT__) || (defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__))
+#if defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
static constexpr Level kLocalStability = Level::VENDOR;
#else
static constexpr Level kLocalStability = Level::SYSTEM;
diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp
index c0ea6d7..e66e425 100644
--- a/libs/binder/ndk/Android.bp
+++ b/libs/binder/ndk/Android.bp
@@ -50,6 +50,7 @@
"ibinder.cpp",
"ibinder_jni.cpp",
"parcel.cpp",
+ "parcel_jni.cpp",
"process.cpp",
"stability.cpp",
"status.cpp",
diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp
index e752c45..75dcdc8 100644
--- a/libs/binder/ndk/ibinder.cpp
+++ b/libs/binder/ndk/ibinder.cpp
@@ -24,10 +24,13 @@
#include <android-base/logging.h>
#include <binder/IPCThreadState.h>
+#include <binder/IResultReceiver.h>
+#include <private/android_filesystem_config.h>
using DeathRecipient = ::android::IBinder::DeathRecipient;
using ::android::IBinder;
+using ::android::IResultReceiver;
using ::android::Parcel;
using ::android::sp;
using ::android::status_t;
@@ -158,6 +161,45 @@
binder_status_t status = getClass()->onTransact(this, code, &in, &out);
return PruneStatusT(status);
+ } else if (code == SHELL_COMMAND_TRANSACTION) {
+ int in = data.readFileDescriptor();
+ int out = data.readFileDescriptor();
+ int err = data.readFileDescriptor();
+
+ int argc = data.readInt32();
+ std::vector<String8> utf8Args; // owns memory of utf8s
+ std::vector<const char*> utf8Pointers; // what can be passed over NDK API
+ for (int i = 0; i < argc && data.dataAvail() > 0; i++) {
+ utf8Args.push_back(String8(data.readString16()));
+ utf8Pointers.push_back(utf8Args[i].c_str());
+ }
+
+ data.readStrongBinder(); // skip over the IShellCallback
+ sp<IResultReceiver> resultReceiver = IResultReceiver::asInterface(data.readStrongBinder());
+
+ // Shell commands should only be callable by ADB.
+ uid_t uid = AIBinder_getCallingUid();
+ if (uid != AID_ROOT && uid != AID_SHELL) {
+ if (resultReceiver != nullptr) {
+ resultReceiver->send(-1);
+ }
+ return STATUS_PERMISSION_DENIED;
+ }
+
+ // Check that the file descriptors are valid.
+ if (in == STATUS_BAD_TYPE || out == STATUS_BAD_TYPE || err == STATUS_BAD_TYPE) {
+ if (resultReceiver != nullptr) {
+ resultReceiver->send(-1);
+ }
+ return STATUS_BAD_VALUE;
+ }
+
+ binder_status_t status = getClass()->handleShellCommand(
+ this, in, out, err, utf8Pointers.data(), utf8Pointers.size());
+ if (resultReceiver != nullptr) {
+ resultReceiver->send(status);
+ }
+ return status;
} else {
return BBinder::onTransact(code, data, reply, flags);
}
@@ -266,6 +308,13 @@
clazz->onDump = onDump;
}
+void AIBinder_Class_setHandleShellCommand(AIBinder_Class* clazz,
+ AIBinder_handleShellCommand handleShellCommand) {
+ CHECK(clazz != nullptr) << "setHandleShellCommand requires non-null clazz";
+
+ clazz->handleShellCommand = handleShellCommand;
+}
+
void AIBinder_DeathRecipient::TransferDeathRecipient::binderDied(const wp<IBinder>& who) {
CHECK(who == mWho);
diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h
index 5cb68c2..5779427 100644
--- a/libs/binder/ndk/ibinder_internal.h
+++ b/libs/binder/ndk/ibinder_internal.h
@@ -17,6 +17,7 @@
#pragma once
#include <android/binder_ibinder.h>
+#include <android/binder_shell.h>
#include "ibinder_internal.h"
#include <atomic>
@@ -115,6 +116,7 @@
// optional methods for a class
AIBinder_onDump onDump;
+ AIBinder_handleShellCommand handleShellCommand;
private:
// This must be a String16 since BBinder virtual getInterfaceDescriptor returns a reference to
diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h b/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h
index be3029c..cd1ff1f 100644
--- a/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h
+++ b/libs/binder/ndk/include_ndk/android/binder_ibinder_jni.h
@@ -36,13 +36,13 @@
/**
* Converts an android.os.IBinder object into an AIBinder* object.
*
- * If either env or the binder is null, null is returned. If this binder object was originally an
+ * If the binder is null, null is returned. If this binder object was originally an
* AIBinder object, the original object is returned. The returned object has one refcount
* associated with it, and so this should be accompanied with an AIBinder_decStrong call.
*
* Available since API level 29.
*
- * \param env Java environment.
+ * \param env Java environment. Must not be null.
* \param binder android.os.IBinder java object.
*
* \return an AIBinder object representing the Java binder object. If either parameter is null, or
@@ -54,12 +54,12 @@
/**
* Converts an AIBinder* object into an android.os.IBinder object.
*
- * If either env or the binder is null, null is returned. If this binder object was originally an
- * IBinder object, the original java object will be returned.
+ * If the binder is null, null is returned. If this binder object was originally an IBinder object,
+ * the original java object will be returned.
*
* Available since API level 29.
*
- * \param env Java environment.
+ * \param env Java environment. Must not be null.
* \param binder the object to convert.
*
* \return an android.os.IBinder object or null if the parameters were null.
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 83a1048..7331ba2 100644
--- a/libs/binder/ndk/include_ndk/android/binder_interface_utils.h
+++ b/libs/binder/ndk/include_ndk/android/binder_interface_utils.h
@@ -30,6 +30,11 @@
#include <android/binder_auto_utils.h>
#include <android/binder_ibinder.h>
+#if __has_include(<android/binder_shell.h>)
+#include <android/binder_shell.h>
+#define HAS_BINDER_SHELL_COMMAND
+#endif //_has_include
+
#include <assert.h>
#include <memory>
@@ -108,7 +113,15 @@
/**
* Dumps information about the interface. By default, dumps nothing.
*/
- virtual inline binder_status_t dump(int /*fd*/, const char** /*args*/, uint32_t /*numArgs*/);
+ virtual inline binder_status_t dump(int fd, const char** args, uint32_t numArgs);
+
+#ifdef HAS_BINDER_SHELL_COMMAND
+ /**
+ * Process shell commands. By default, does nothing.
+ */
+ virtual inline binder_status_t handleShellCommand(int in, int out, int err, const char** argv,
+ uint32_t argc);
+#endif
/**
* Interprets this binder as this underlying interface if this has stored an ICInterface in the
@@ -136,6 +149,11 @@
static inline void onDestroy(void* userData);
static inline binder_status_t onDump(AIBinder* binder, int fd, const char** args,
uint32_t numArgs);
+
+#ifdef HAS_BINDER_SHELL_COMMAND
+ static inline binder_status_t handleShellCommand(AIBinder* binder, int in, int out, int err,
+ const char** argv, uint32_t argc);
+#endif
};
};
@@ -191,6 +209,13 @@
return STATUS_OK;
}
+#ifdef HAS_BINDER_SHELL_COMMAND
+binder_status_t ICInterface::handleShellCommand(int /*in*/, int /*out*/, int /*err*/,
+ const char** /*argv*/, uint32_t /*argc*/) {
+ return STATUS_OK;
+}
+#endif
+
std::shared_ptr<ICInterface> ICInterface::asInterface(AIBinder* binder) {
return ICInterfaceData::getInterface(binder);
}
@@ -203,9 +228,12 @@
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.
+ // We can't know if these methods are overridden by a subclass interface, so we must register
+ // ourselves. The defaults are harmless.
AIBinder_Class_setOnDump(clazz, ICInterfaceData::onDump);
+#ifdef HAS_BINDER_SHELL_COMMAND
+ AIBinder_Class_setHandleShellCommand(clazz, ICInterfaceData::handleShellCommand);
+#endif
return clazz;
}
@@ -234,6 +262,15 @@
return interface->dump(fd, args, numArgs);
}
+#ifdef HAS_BINDER_SHELL_COMMAND
+binder_status_t ICInterface::ICInterfaceData::handleShellCommand(AIBinder* binder, int in, int out,
+ int err, const char** argv,
+ uint32_t argc) {
+ std::shared_ptr<ICInterface> interface = getInterface(binder);
+ return interface->handleShellCommand(in, out, err, argv, argc);
+}
+#endif
+
template <typename INTERFACE>
SpAIBinder BnCInterface<INTERFACE>::asBinder() {
std::lock_guard<std::mutex> l(mMutex);
diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel_jni.h b/libs/binder/ndk/include_ndk/android/binder_parcel_jni.h
new file mode 100644
index 0000000..65e1704
--- /dev/null
+++ b/libs/binder/ndk/include_ndk/android/binder_parcel_jni.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+/**
+ * @addtogroup NdkBinder
+ * @{
+ */
+
+/**
+ * @file binder_parcel_jni.h
+ * @brief Conversions between AParcel and android.os.Parcel
+ */
+
+#pragma once
+
+#include <android/binder_parcel.h>
+
+#include <jni.h>
+
+__BEGIN_DECLS
+#if __ANDROID_API__ >= 30
+
+/**
+ * Converts an android.os.Parcel object into an AParcel* object.
+ *
+ * If the parcel is null, null is returned.
+ *
+ * Available since API level 30.
+ *
+ * \param env Java environment. Must not be null.
+ * \param parcel android.os.Parcel java object.
+ *
+ * \return an AParcel object representing the Java parcel object. If either parameter is null, this
+ * will return null. This must be deleted with AParcel_delete. This does not take ownership of the
+ * jobject and is only good for as long as the jobject is alive.
+ */
+__attribute__((warn_unused_result)) AParcel* AParcel_fromJavaParcel(JNIEnv* env, jobject parcel)
+ __INTRODUCED_IN(30);
+
+#endif //__ANDROID_API__ >= 30
+__END_DECLS
+
+/** @} */
diff --git a/libs/binder/ndk/include_platform/android/binder_shell.h b/libs/binder/ndk/include_platform/android/binder_shell.h
new file mode 100644
index 0000000..17b38b0
--- /dev/null
+++ b/libs/binder/ndk/include_platform/android/binder_shell.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2020 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 <android/binder_ibinder.h>
+
+__BEGIN_DECLS
+
+/**
+ * Function to execute a shell command.
+ *
+ * Available since API level 30.
+ *
+ * \param binder the binder executing the command
+ * \param in input file descriptor, should be flushed, ownership is not passed
+ * \param out output file descriptor, should be flushed, ownership is not passed
+ * \param err error file descriptor, should be flushed, ownership is not passed
+ * \param argv array of null-terminated strings for command (may be null if argc
+ * is 0)
+ * \param argc length of argv array
+ *
+ * \return binder_status_t result of transaction
+ */
+typedef binder_status_t (*AIBinder_handleShellCommand)(AIBinder* binder, int in, int out, int err,
+ const char** argv, uint32_t argc);
+
+/**
+ * This sets the implementation of handleShellCommand for a class.
+ *
+ * If this isn't set, nothing will be executed when handleShellCommand is called.
+ *
+ * Available since API level 30.
+ *
+ * \param handleShellCommand function to call when a shell transaction is
+ * received
+ */
+void AIBinder_Class_setHandleShellCommand(AIBinder_Class* clazz,
+ AIBinder_handleShellCommand handleShellCommand)
+ __INTRODUCED_IN(30);
+
+__END_DECLS
diff --git a/libs/binder/ndk/include_platform/android/binder_stability.h b/libs/binder/ndk/include_platform/android/binder_stability.h
index 56d95a7..f5e8bf6 100644
--- a/libs/binder/ndk/include_platform/android/binder_stability.h
+++ b/libs/binder/ndk/include_platform/android/binder_stability.h
@@ -30,8 +30,7 @@
FLAG_PRIVATE_VENDOR = 0x10000000,
};
-#if defined(__ANDROID_APEX_COM_ANDROID_VNDK_CURRENT__) || \
- (defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__))
+#if defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
enum {
FLAG_PRIVATE_LOCAL = FLAG_PRIVATE_VENDOR,
@@ -46,8 +45,7 @@
AIBinder_markVendorStability(binder);
}
-#else // defined(__ANDROID_APEX_COM_ANDROID_VNDK_CURRENT__) || (defined(__ANDROID_VNDK__) &&
- // !defined(__ANDROID_APEX__))
+#else // defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
enum {
FLAG_PRIVATE_LOCAL = 0,
@@ -64,8 +62,7 @@
AIBinder_markSystemStability(binder);
}
-#endif // defined(__ANDROID_APEX_COM_ANDROID_VNDK_CURRENT__) || (defined(__ANDROID_VNDK__) &&
- // !defined(__ANDROID_APEX__))
+#endif // defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__)
/**
* This interface has system<->vendor stability
diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt
index 71d8103..7e72f22 100644
--- a/libs/binder/ndk/libbinder_ndk.map.txt
+++ b/libs/binder/ndk/libbinder_ndk.map.txt
@@ -105,10 +105,12 @@
AIBinder_setExtension;
AStatus_getDescription;
AStatus_deleteDescription;
+ AParcel_fromJavaParcel;
AIBinder_markSystemStability; # apex
AIBinder_markVendorStability; # llndk
AIBinder_markVintfStability; # apex llndk
+ AIBinder_Class_setHandleShellCommand; # apex llndk
local:
*;
};
diff --git a/libs/binder/ndk/parcel_jni.cpp b/libs/binder/ndk/parcel_jni.cpp
new file mode 100644
index 0000000..53b2d7c
--- /dev/null
+++ b/libs/binder/ndk/parcel_jni.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 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 <android/binder_parcel_jni.h>
+#include "parcel_internal.h"
+
+#include <android_os_Parcel.h>
+
+using ::android::Parcel;
+using ::android::parcelForJavaObject;
+
+AParcel* AParcel_fromJavaParcel(JNIEnv* env, jobject jbinder) {
+ if (jbinder == nullptr) {
+ return nullptr;
+ }
+
+ Parcel* parcel = parcelForJavaObject(env, jbinder);
+
+ if (parcel == nullptr) {
+ return nullptr;
+ }
+
+ return new AParcel(nullptr /*binder*/, parcel, false /*shouldOwn*/);
+}
diff --git a/libs/binder/ndk/runtests.sh b/libs/binder/ndk/runtests.sh
deleted file mode 100755
index a0c49fb..0000000
--- a/libs/binder/ndk/runtests.sh
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/usr/bin/env bash
-
-# Copyright (C) 2018 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.
-
-if [ -z $ANDROID_BUILD_TOP ]; then
- echo "You need to source and lunch before you can use this script"
- exit 1
-fi
-
-set -ex
-
-function run_libbinder_ndk_test() {
- adb shell /data/nativetest64/libbinder_ndk_test_server/libbinder_ndk_test_server &
-
- # avoid getService 1s delay for most runs, non-critical
- sleep 0.1
-
- adb shell /data/nativetest64/libbinder_ndk_test_client/libbinder_ndk_test_client; \
- adb shell killall libbinder_ndk_test_server
-}
-
-[ "$1" != "--skip-build" ] && $ANDROID_BUILD_TOP/build/soong/soong_ui.bash --make-mode \
- MODULES-IN-frameworks-native-libs-binder-ndk
-
-adb root
-adb wait-for-device
-adb sync data
-
-# very simple unit tests, tests things outside of the NDK as well
-run_libbinder_ndk_test
-
-# CTS tests (much more comprehensive, new tests should ideally go here)
-atest android.binder.cts
diff --git a/libs/binder/ndk/test/Android.bp b/libs/binder/ndk/test/Android.bp
index ebd08b2..cb4b20f 100644
--- a/libs/binder/ndk/test/Android.bp
+++ b/libs/binder/ndk/test/Android.bp
@@ -56,16 +56,18 @@
// specifically the parts which are outside of the NDK. Actual users should
// also instead use AIDL to generate these stubs. See android.binder.cts.
cc_test {
- name: "libbinder_ndk_test_client",
+ name: "libbinder_ndk_unit_test",
defaults: ["test_libbinder_ndk_test_defaults"],
- srcs: ["main_client.cpp"],
-}
+ srcs: ["libbinder_ndk_unit_test.cpp"],
+ static_libs: [
+ "IBinderNdkUnitTest-cpp",
+ "IBinderNdkUnitTest-ndk_platform",
+ ],
+ test_suites: ["general-tests"],
+ require_root: true,
-cc_test {
- name: "libbinder_ndk_test_server",
- defaults: ["test_libbinder_ndk_test_defaults"],
- srcs: ["main_server.cpp"],
- gtest: false,
+ // force since binderVendorDoubleLoadTest has its own
+ auto_gen_config: true,
}
cc_test {
@@ -85,14 +87,21 @@
"libbinder_ndk",
"libutils",
],
- test_suites: ["device-tests"],
+ test_suites: ["general-tests"],
}
aidl_interface {
name: "IBinderVendorDoubleLoadTest",
- // TODO(b/119771576): only vendor is needed
- vendor_available: true,
+ vendor: true,
srcs: [
"IBinderVendorDoubleLoadTest.aidl",
],
}
+
+aidl_interface {
+ name: "IBinderNdkUnitTest",
+ srcs: [
+ "IBinderNdkUnitTest.aidl",
+ "IEmpty.aidl",
+ ],
+}
diff --git a/libs/binder/ndk/test/IBinderNdkUnitTest.aidl b/libs/binder/ndk/test/IBinderNdkUnitTest.aidl
new file mode 100644
index 0000000..6e8e463
--- /dev/null
+++ b/libs/binder/ndk/test/IBinderNdkUnitTest.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2020 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 AIDL is to test things that can't be tested in CtsNdkBinderTestCases
+// because it requires libbinder_ndk implementation details or APIs not
+// available to apps. Please prefer adding tests to CtsNdkBinderTestCases
+// over here.
+
+import IEmpty;
+
+interface IBinderNdkUnitTest {
+ void takeInterface(IEmpty test);
+ void forceFlushCommands();
+}
diff --git a/libs/binder/ndk/test/IEmpty.aidl b/libs/binder/ndk/test/IEmpty.aidl
new file mode 100644
index 0000000..95e4341
--- /dev/null
+++ b/libs/binder/ndk/test/IEmpty.aidl
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+interface IEmpty { }
diff --git a/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp
new file mode 100644
index 0000000..fd30d87
--- /dev/null
+++ b/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2018 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 <IBinderNdkUnitTest.h>
+#include <aidl/BnBinderNdkUnitTest.h>
+#include <aidl/BnEmpty.h>
+#include <android-base/logging.h>
+#include <android/binder_ibinder_jni.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <gtest/gtest.h>
+#include <iface/iface.h>
+
+// warning: this is assuming that libbinder_ndk is using the same copy
+// of libbinder that we are.
+#include <binder/IPCThreadState.h>
+#include <binder/IResultReceiver.h>
+#include <binder/IServiceManager.h>
+#include <binder/IShellCallback.h>
+
+#include <sys/prctl.h>
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+
+using namespace android;
+
+constexpr char kExistingNonNdkService[] = "SurfaceFlinger";
+constexpr char kBinderNdkUnitTestService[] = "BinderNdkUnitTest";
+
+class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest {
+ ndk::ScopedAStatus takeInterface(const std::shared_ptr<aidl::IEmpty>& empty) {
+ (void)empty;
+ return ndk::ScopedAStatus::ok();
+ }
+ ndk::ScopedAStatus forceFlushCommands() {
+ // warning: this is assuming that libbinder_ndk is using the same copy
+ // of libbinder that we are.
+ android::IPCThreadState::self()->flushCommands();
+ return ndk::ScopedAStatus::ok();
+ }
+ binder_status_t handleShellCommand(int /*in*/, int out, int /*err*/, const char** args,
+ uint32_t numArgs) override {
+ for (uint32_t i = 0; i < numArgs; i++) {
+ dprintf(out, "%s", args[i]);
+ }
+ fsync(out);
+ return STATUS_OK;
+ }
+};
+
+int generatedService() {
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+
+ auto service = ndk::SharedRefBase::make<MyBinderNdkUnitTest>();
+ binder_status_t status =
+ AServiceManager_addService(service->asBinder().get(), kBinderNdkUnitTestService);
+
+ if (status != STATUS_OK) {
+ LOG(FATAL) << "Could not register: " << status << " " << kBinderNdkUnitTestService;
+ }
+
+ ABinderProcess_joinThreadPool();
+
+ return 1; // should not return
+}
+
+// manually-written parceling class considered bad practice
+class MyFoo : public IFoo {
+ binder_status_t doubleNumber(int32_t in, int32_t* out) override {
+ *out = 2 * in;
+ LOG(INFO) << "doubleNumber (" << in << ") => " << *out;
+ return STATUS_OK;
+ }
+
+ binder_status_t die() override {
+ LOG(FATAL) << "IFoo::die called!";
+ return STATUS_UNKNOWN_ERROR;
+ }
+};
+
+int manualService(const char* instance) {
+ ABinderProcess_setThreadPoolMaxThreadCount(0);
+
+ // Strong reference to MyFoo kept by service manager.
+ binder_status_t status = (new MyFoo)->addService(instance);
+
+ if (status != STATUS_OK) {
+ LOG(FATAL) << "Could not register: " << status << " " << instance;
+ }
+
+ ABinderProcess_joinThreadPool();
+
+ return 1; // should not return
+}
+
+// This is too slow
+// TEST(NdkBinder, GetServiceThatDoesntExist) {
+// sp<IFoo> foo = IFoo::getService("asdfghkl;");
+// EXPECT_EQ(nullptr, foo.get());
+// }
+
+TEST(NdkBinder, CheckServiceThatDoesntExist) {
+ AIBinder* binder = AServiceManager_checkService("asdfghkl;");
+ ASSERT_EQ(nullptr, binder);
+}
+
+TEST(NdkBinder, CheckServiceThatDoesExist) {
+ AIBinder* binder = AServiceManager_checkService(kExistingNonNdkService);
+ EXPECT_NE(nullptr, binder);
+ EXPECT_EQ(STATUS_OK, AIBinder_ping(binder));
+
+ AIBinder_decStrong(binder);
+}
+
+TEST(NdkBinder, DoubleNumber) {
+ sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName);
+ ASSERT_NE(foo, nullptr);
+
+ int32_t out;
+ EXPECT_EQ(STATUS_OK, foo->doubleNumber(1, &out));
+ EXPECT_EQ(2, out);
+}
+
+void LambdaOnDeath(void* cookie) {
+ auto onDeath = static_cast<std::function<void(void)>*>(cookie);
+ (*onDeath)();
+};
+TEST(NdkBinder, DeathRecipient) {
+ using namespace std::chrono_literals;
+
+ AIBinder* binder;
+ sp<IFoo> foo = IFoo::getService(IFoo::kInstanceNameToDieFor, &binder);
+ ASSERT_NE(nullptr, foo.get());
+ ASSERT_NE(nullptr, binder);
+
+ std::mutex deathMutex;
+ std::condition_variable deathCv;
+ bool deathRecieved = false;
+
+ std::function<void(void)> onDeath = [&] {
+ std::cerr << "Binder died (as requested)." << std::endl;
+ deathRecieved = true;
+ deathCv.notify_one();
+ };
+
+ AIBinder_DeathRecipient* recipient = AIBinder_DeathRecipient_new(LambdaOnDeath);
+
+ EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, static_cast<void*>(&onDeath)));
+
+ // the binder driver should return this if the service dies during the transaction
+ EXPECT_EQ(STATUS_DEAD_OBJECT, foo->die());
+
+ foo = 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);
+ binder = nullptr;
+}
+
+TEST(NdkBinder, RetrieveNonNdkService) {
+ AIBinder* binder = AServiceManager_getService(kExistingNonNdkService);
+ ASSERT_NE(nullptr, binder);
+ EXPECT_TRUE(AIBinder_isRemote(binder));
+ EXPECT_TRUE(AIBinder_isAlive(binder));
+ EXPECT_EQ(STATUS_OK, AIBinder_ping(binder));
+
+ AIBinder_decStrong(binder);
+}
+
+void OnBinderDeath(void* cookie) {
+ LOG(ERROR) << "BINDER DIED. COOKIE: " << cookie;
+}
+
+TEST(NdkBinder, LinkToDeath) {
+ AIBinder* binder = AServiceManager_getService(kExistingNonNdkService);
+ ASSERT_NE(nullptr, binder);
+
+ AIBinder_DeathRecipient* recipient = AIBinder_DeathRecipient_new(OnBinderDeath);
+ ASSERT_NE(nullptr, recipient);
+
+ EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, nullptr));
+ EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, nullptr));
+ EXPECT_EQ(STATUS_OK, AIBinder_unlinkToDeath(binder, recipient, nullptr));
+ EXPECT_EQ(STATUS_OK, AIBinder_unlinkToDeath(binder, recipient, nullptr));
+ EXPECT_EQ(STATUS_NAME_NOT_FOUND, AIBinder_unlinkToDeath(binder, recipient, nullptr));
+
+ AIBinder_DeathRecipient_delete(recipient);
+ AIBinder_decStrong(binder);
+}
+
+class MyTestFoo : public IFoo {
+ binder_status_t doubleNumber(int32_t in, int32_t* out) override {
+ *out = 2 * in;
+ LOG(INFO) << "doubleNumber (" << in << ") => " << *out;
+ return STATUS_OK;
+ }
+ binder_status_t die() override {
+ ADD_FAILURE() << "die called on local instance";
+ return STATUS_OK;
+ }
+};
+
+TEST(NdkBinder, GetServiceInProcess) {
+ static const char* kInstanceName = "test-get-service-in-process";
+
+ sp<IFoo> foo = new MyTestFoo;
+ EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName));
+
+ sp<IFoo> getFoo = IFoo::getService(kInstanceName);
+ EXPECT_EQ(foo.get(), getFoo.get());
+
+ int32_t out;
+ EXPECT_EQ(STATUS_OK, getFoo->doubleNumber(1, &out));
+ EXPECT_EQ(2, out);
+}
+
+TEST(NdkBinder, EqualityOfRemoteBinderPointer) {
+ AIBinder* binderA = AServiceManager_getService(kExistingNonNdkService);
+ ASSERT_NE(nullptr, binderA);
+
+ AIBinder* binderB = AServiceManager_getService(kExistingNonNdkService);
+ ASSERT_NE(nullptr, binderB);
+
+ EXPECT_EQ(binderA, binderB);
+
+ AIBinder_decStrong(binderA);
+ AIBinder_decStrong(binderB);
+}
+
+TEST(NdkBinder, ToFromJavaNullptr) {
+ EXPECT_EQ(nullptr, AIBinder_toJavaBinder(nullptr, nullptr));
+ EXPECT_EQ(nullptr, AIBinder_fromJavaBinder(nullptr, nullptr));
+}
+
+TEST(NdkBinder, ABpBinderRefCount) {
+ AIBinder* binder = AServiceManager_getService(kExistingNonNdkService);
+ AIBinder_Weak* wBinder = AIBinder_Weak_new(binder);
+
+ ASSERT_NE(nullptr, binder);
+ EXPECT_EQ(1, AIBinder_debugGetRefCount(binder));
+
+ AIBinder_decStrong(binder);
+
+ // assert because would need to decStrong if non-null and we shouldn't need to add a no-op here
+ ASSERT_NE(nullptr, AIBinder_Weak_promote(wBinder));
+
+ AIBinder_Weak_delete(wBinder);
+}
+
+TEST(NdkBinder, AddServiceMultipleTimes) {
+ static const char* kInstanceName1 = "test-multi-1";
+ static const char* kInstanceName2 = "test-multi-2";
+ sp<IFoo> foo = new MyTestFoo;
+ EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName1));
+ EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName2));
+ EXPECT_EQ(IFoo::getService(kInstanceName1), IFoo::getService(kInstanceName2));
+}
+
+TEST(NdkBinder, SentAidlBinderCanBeDestroyed) {
+ static volatile bool destroyed = false;
+ static std::mutex dMutex;
+ static std::condition_variable cv;
+
+ class MyEmpty : public aidl::BnEmpty {
+ virtual ~MyEmpty() {
+ destroyed = true;
+ cv.notify_one();
+ }
+ };
+
+ std::shared_ptr<MyEmpty> empty = ndk::SharedRefBase::make<MyEmpty>();
+
+ ndk::SpAIBinder binder(AServiceManager_getService(kBinderNdkUnitTestService));
+ std::shared_ptr<aidl::IBinderNdkUnitTest> service =
+ aidl::IBinderNdkUnitTest::fromBinder(binder);
+
+ EXPECT_FALSE(destroyed);
+
+ service->takeInterface(empty);
+ service->forceFlushCommands();
+ empty = nullptr;
+
+ // give other binder thread time to process commands
+ {
+ using namespace std::chrono_literals;
+ std::unique_lock<std::mutex> lk(dMutex);
+ cv.wait_for(lk, 1s, [] { return destroyed; });
+ }
+
+ EXPECT_TRUE(destroyed);
+}
+
+class MyResultReceiver : public BnResultReceiver {
+ public:
+ Mutex mMutex;
+ Condition mCondition;
+ bool mHaveResult = false;
+ int32_t mResult = 0;
+
+ virtual void send(int32_t resultCode) {
+ AutoMutex _l(mMutex);
+ mResult = resultCode;
+ mHaveResult = true;
+ mCondition.signal();
+ }
+
+ int32_t waitForResult() {
+ AutoMutex _l(mMutex);
+ while (!mHaveResult) {
+ mCondition.wait(mMutex);
+ }
+ return mResult;
+ }
+};
+
+class MyShellCallback : public BnShellCallback {
+ public:
+ virtual int openFile(const String16& /*path*/, const String16& /*seLinuxContext*/,
+ const String16& /*mode*/) {
+ // Empty implementation.
+ return 0;
+ }
+};
+
+bool ReadFdToString(int fd, std::string* content) {
+ char buf[64];
+ ssize_t n;
+ while ((n = TEMP_FAILURE_RETRY(read(fd, &buf[0], sizeof(buf)))) > 0) {
+ content->append(buf, n);
+ }
+ return (n == 0) ? true : false;
+}
+
+std::string shellCmdToString(sp<IBinder> unitTestService, const std::vector<const char*>& args) {
+ int inFd[2] = {-1, -1};
+ int outFd[2] = {-1, -1};
+ int errFd[2] = {-1, -1};
+
+ EXPECT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, inFd));
+ EXPECT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, outFd));
+ EXPECT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, errFd));
+
+ sp<MyShellCallback> cb = new MyShellCallback();
+ sp<MyResultReceiver> resultReceiver = new MyResultReceiver();
+
+ Vector<String16> argsVec;
+ for (int i = 0; i < args.size(); i++) {
+ argsVec.add(String16(args[i]));
+ }
+ status_t error = IBinder::shellCommand(unitTestService, inFd[0], outFd[0], errFd[0], argsVec,
+ cb, resultReceiver);
+ EXPECT_EQ(error, android::OK);
+
+ status_t res = resultReceiver->waitForResult();
+ EXPECT_EQ(res, android::OK);
+
+ close(inFd[0]);
+ close(inFd[1]);
+ close(outFd[0]);
+ close(errFd[0]);
+ close(errFd[1]);
+
+ std::string ret;
+ EXPECT_TRUE(ReadFdToString(outFd[1], &ret));
+ close(outFd[1]);
+ return ret;
+}
+
+TEST(NdkBinder, UseHandleShellCommand) {
+ static const sp<android::IServiceManager> sm(android::defaultServiceManager());
+ sp<IBinder> testService = sm->getService(String16(kBinderNdkUnitTestService));
+
+ EXPECT_EQ("", shellCmdToString(testService, {}));
+ EXPECT_EQ("", shellCmdToString(testService, {"", ""}));
+ EXPECT_EQ("Hello world!", shellCmdToString(testService, {"Hello ", "world!"}));
+ EXPECT_EQ("CMD", shellCmdToString(testService, {"C", "M", "D"}));
+}
+
+int main(int argc, char* argv[]) {
+ ::testing::InitGoogleTest(&argc, argv);
+
+ if (fork() == 0) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
+ return manualService(IFoo::kInstanceNameToDieFor);
+ }
+ if (fork() == 0) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
+ return manualService(IFoo::kSomeInstanceName);
+ }
+ if (fork() == 0) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
+ return generatedService();
+ }
+
+ ABinderProcess_setThreadPoolMaxThreadCount(1); // to recieve death notifications/callbacks
+ ABinderProcess_startThreadPool();
+
+ return RUN_ALL_TESTS();
+}
+
+#include <android/binder_auto_utils.h>
+#include <android/binder_interface_utils.h>
+#include <android/binder_parcel_utils.h>
diff --git a/libs/binder/ndk/test/main_client.cpp b/libs/binder/ndk/test/main_client.cpp
deleted file mode 100644
index 8467734..0000000
--- a/libs/binder/ndk/test/main_client.cpp
+++ /dev/null
@@ -1,210 +0,0 @@
-/*
- * Copyright (C) 2018 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 <android-base/logging.h>
-#include <android/binder_ibinder_jni.h>
-#include <android/binder_manager.h>
-#include <android/binder_process.h>
-#include <gtest/gtest.h>
-#include <iface/iface.h>
-
-#include <chrono>
-#include <condition_variable>
-#include <mutex>
-
-using ::android::sp;
-
-constexpr char kExistingNonNdkService[] = "SurfaceFlinger";
-
-// This is too slow
-// TEST(NdkBinder, GetServiceThatDoesntExist) {
-// sp<IFoo> foo = IFoo::getService("asdfghkl;");
-// EXPECT_EQ(nullptr, foo.get());
-// }
-
-TEST(NdkBinder, CheckServiceThatDoesntExist) {
- AIBinder* binder = AServiceManager_checkService("asdfghkl;");
- ASSERT_EQ(nullptr, binder);
-}
-
-TEST(NdkBinder, CheckServiceThatDoesExist) {
- AIBinder* binder = AServiceManager_checkService(kExistingNonNdkService);
- EXPECT_NE(nullptr, binder);
- EXPECT_EQ(STATUS_OK, AIBinder_ping(binder));
-
- AIBinder_decStrong(binder);
-}
-
-TEST(NdkBinder, DoubleNumber) {
- sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName);
- ASSERT_NE(foo, nullptr);
-
- int32_t out;
- EXPECT_EQ(STATUS_OK, foo->doubleNumber(1, &out));
- EXPECT_EQ(2, out);
-}
-
-void LambdaOnDeath(void* cookie) {
- auto onDeath = static_cast<std::function<void(void)>*>(cookie);
- (*onDeath)();
-};
-TEST(NdkBinder, DeathRecipient) {
- using namespace std::chrono_literals;
-
- AIBinder* binder;
- sp<IFoo> foo = IFoo::getService(IFoo::kInstanceNameToDieFor, &binder);
- ASSERT_NE(nullptr, foo.get());
- ASSERT_NE(nullptr, binder);
-
- std::mutex deathMutex;
- std::condition_variable deathCv;
- bool deathRecieved = false;
-
- std::function<void(void)> onDeath = [&] {
- std::cerr << "Binder died (as requested)." << std::endl;
- deathRecieved = true;
- deathCv.notify_one();
- };
-
- AIBinder_DeathRecipient* recipient = AIBinder_DeathRecipient_new(LambdaOnDeath);
-
- EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, static_cast<void*>(&onDeath)));
-
- // 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);
-}
-
-TEST(NdkBinder, RetrieveNonNdkService) {
- AIBinder* binder = AServiceManager_getService(kExistingNonNdkService);
- ASSERT_NE(nullptr, binder);
- EXPECT_TRUE(AIBinder_isRemote(binder));
- EXPECT_TRUE(AIBinder_isAlive(binder));
- EXPECT_EQ(STATUS_OK, AIBinder_ping(binder));
-
- AIBinder_decStrong(binder);
-}
-
-void OnBinderDeath(void* cookie) {
- LOG(ERROR) << "BINDER DIED. COOKIE: " << cookie;
-}
-
-TEST(NdkBinder, LinkToDeath) {
- AIBinder* binder = AServiceManager_getService(kExistingNonNdkService);
- ASSERT_NE(nullptr, binder);
-
- AIBinder_DeathRecipient* recipient = AIBinder_DeathRecipient_new(OnBinderDeath);
- ASSERT_NE(nullptr, recipient);
-
- EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, nullptr));
- EXPECT_EQ(STATUS_OK, AIBinder_linkToDeath(binder, recipient, nullptr));
- EXPECT_EQ(STATUS_OK, AIBinder_unlinkToDeath(binder, recipient, nullptr));
- EXPECT_EQ(STATUS_OK, AIBinder_unlinkToDeath(binder, recipient, nullptr));
- EXPECT_EQ(STATUS_NAME_NOT_FOUND, AIBinder_unlinkToDeath(binder, recipient, nullptr));
-
- AIBinder_DeathRecipient_delete(recipient);
- AIBinder_decStrong(binder);
-}
-
-class MyTestFoo : public IFoo {
- binder_status_t doubleNumber(int32_t in, int32_t* out) override {
- *out = 2 * in;
- LOG(INFO) << "doubleNumber (" << in << ") => " << *out;
- return STATUS_OK;
- }
- binder_status_t die() override {
- ADD_FAILURE() << "die called on local instance";
- return STATUS_OK;
- }
-};
-
-TEST(NdkBinder, GetServiceInProcess) {
- static const char* kInstanceName = "test-get-service-in-process";
-
- sp<IFoo> foo = new MyTestFoo;
- EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName));
-
- sp<IFoo> getFoo = IFoo::getService(kInstanceName);
- EXPECT_EQ(foo.get(), getFoo.get());
-
- int32_t out;
- EXPECT_EQ(STATUS_OK, getFoo->doubleNumber(1, &out));
- EXPECT_EQ(2, out);
-}
-
-TEST(NdkBinder, EqualityOfRemoteBinderPointer) {
- AIBinder* binderA = AServiceManager_getService(kExistingNonNdkService);
- ASSERT_NE(nullptr, binderA);
-
- AIBinder* binderB = AServiceManager_getService(kExistingNonNdkService);
- ASSERT_NE(nullptr, binderB);
-
- EXPECT_EQ(binderA, binderB);
-
- AIBinder_decStrong(binderA);
- AIBinder_decStrong(binderB);
-}
-
-TEST(NdkBinder, ToFromJavaNullptr) {
- EXPECT_EQ(nullptr, AIBinder_toJavaBinder(nullptr, nullptr));
- EXPECT_EQ(nullptr, AIBinder_fromJavaBinder(nullptr, nullptr));
-}
-
-TEST(NdkBinder, ABpBinderRefCount) {
- AIBinder* binder = AServiceManager_getService(kExistingNonNdkService);
- AIBinder_Weak* wBinder = AIBinder_Weak_new(binder);
-
- ASSERT_NE(nullptr, binder);
- EXPECT_EQ(1, AIBinder_debugGetRefCount(binder));
-
- AIBinder_decStrong(binder);
-
- // assert because would need to decStrong if non-null and we shouldn't need to add a no-op here
- ASSERT_NE(nullptr, AIBinder_Weak_promote(wBinder));
-
- AIBinder_Weak_delete(wBinder);
-}
-
-TEST(NdkBinder, AddServiceMultipleTimes) {
- static const char* kInstanceName1 = "test-multi-1";
- static const char* kInstanceName2 = "test-multi-2";
- sp<IFoo> foo = new MyTestFoo;
- EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName1));
- EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName2));
- EXPECT_EQ(IFoo::getService(kInstanceName1), IFoo::getService(kInstanceName2));
-}
-
-int main(int argc, char* argv[]) {
- ::testing::InitGoogleTest(&argc, argv);
-
- ABinderProcess_setThreadPoolMaxThreadCount(1); // to recieve death notifications/callbacks
- ABinderProcess_startThreadPool();
-
- return RUN_ALL_TESTS();
-}
-
-#include <android/binder_auto_utils.h>
-#include <android/binder_interface_utils.h>
-#include <android/binder_parcel_utils.h>
diff --git a/libs/binder/ndk/test/main_server.cpp b/libs/binder/ndk/test/main_server.cpp
deleted file mode 100644
index a6e17e8..0000000
--- a/libs/binder/ndk/test/main_server.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2018 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 <android-base/logging.h>
-#include <android/binder_process.h>
-#include <iface/iface.h>
-
-using ::android::sp;
-
-class MyFoo : public IFoo {
- binder_status_t doubleNumber(int32_t in, int32_t* out) override {
- *out = 2 * in;
- LOG(INFO) << "doubleNumber (" << in << ") => " << *out;
- return STATUS_OK;
- }
-
- binder_status_t die() override {
- LOG(FATAL) << "IFoo::die called!";
- return STATUS_UNKNOWN_ERROR;
- }
-};
-
-int service(const char* instance) {
- ABinderProcess_setThreadPoolMaxThreadCount(0);
-
- // Strong reference to MyFoo kept by service manager.
- binder_status_t status = (new MyFoo)->addService(instance);
-
- if (status != STATUS_OK) {
- LOG(FATAL) << "Could not register: " << status << " " << instance;
- }
-
- ABinderProcess_joinThreadPool();
-
- return 1; // should not return
-}
-
-int main() {
- if (fork() == 0) {
- return service(IFoo::kInstanceNameToDieFor);
- }
-
- return service(IFoo::kSomeInstanceName);
-}
diff --git a/libs/binderthreadstate/1.0/Android.bp b/libs/binderthreadstate/1.0/Android.bp
new file mode 100644
index 0000000..ebdc932
--- /dev/null
+++ b/libs/binderthreadstate/1.0/Android.bp
@@ -0,0 +1,14 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+hidl_interface {
+ name: "binderthreadstateutilstest@1.0",
+ root: "binderthreadstateutilstest",
+ system_ext_specific: true,
+ srcs: [
+ "IHidlStuff.hal",
+ ],
+ interfaces: [
+ "android.hidl.base@1.0",
+ ],
+ gen_java: true,
+}
diff --git a/libs/binderthreadstate/1.0/IHidlStuff.hal b/libs/binderthreadstate/1.0/IHidlStuff.hal
new file mode 100644
index 0000000..ffb6499
--- /dev/null
+++ b/libs/binderthreadstate/1.0/IHidlStuff.hal
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package binderthreadstateutilstest@1.0;
+
+interface IHidlStuff {
+ callLocal();
+ call(int32_t idx);
+};
diff --git a/libs/binderthreadstate/Android.bp b/libs/binderthreadstate/Android.bp
index ee1a6a4..4655e1d 100644
--- a/libs/binderthreadstate/Android.bp
+++ b/libs/binderthreadstate/Android.bp
@@ -12,6 +12,59 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+// DO NOT ADD NEW USAGES OF THIS
+// See comments in header file.
+cc_library_static {
+ name: "libbinderthreadstateutils",
+ double_loadable: true,
+ vendor_available: true,
+ host_supported: true,
+
+ shared_libs: [
+ "libbinder",
+ "libhidlbase", // libhwbinder is in here
+ ],
+
+ export_include_dirs: ["include"],
+
+ cflags: [
+ "-Wall",
+ "-Werror",
+ ],
+}
+
+hidl_package_root {
+ name: "binderthreadstateutilstest",
+}
+
+aidl_interface {
+ name: "binderthreadstateutilstest.aidl",
+ srcs: ["IAidlStuff.aidl"],
+}
+
+cc_test {
+ name: "libbinderthreadstateutils_test",
+ srcs: ["test.cpp"],
+ static_libs: [
+ "binderthreadstateutilstest@1.0",
+ "binderthreadstateutilstest.aidl-cpp",
+ "libbinderthreadstateutils",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libcutils",
+ "libhidlbase",
+ "libutils",
+ "liblog",
+ ],
+ test_suites: [
+ "general-tests",
+ ],
+ require_root: true,
+}
+
+// TODO(b/148692216): remove empty lib
cc_library {
name: "libbinderthreadstate",
recovery_available: true,
@@ -21,28 +74,4 @@
support_system_process: true,
},
host_supported: true,
-
- srcs: [
- "IPCThreadStateBase.cpp",
- ],
-
- header_libs: [
- "libbase_headers",
- "libutils_headers",
- ],
-
- shared_libs: [
- "liblog",
- ],
-
- export_include_dirs: ["include"],
-
- sanitize: {
- misc_undefined: ["integer"],
- },
-
- cflags: [
- "-Wall",
- "-Werror",
- ],
}
diff --git a/libs/binderthreadstate/IAidlStuff.aidl b/libs/binderthreadstate/IAidlStuff.aidl
new file mode 100644
index 0000000..0c81c42
--- /dev/null
+++ b/libs/binderthreadstate/IAidlStuff.aidl
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+
+interface IAidlStuff {
+ void callLocal();
+ void call(int idx);
+}
diff --git a/libs/binderthreadstate/IPCThreadStateBase.cpp b/libs/binderthreadstate/IPCThreadStateBase.cpp
deleted file mode 100644
index fede151..0000000
--- a/libs/binderthreadstate/IPCThreadStateBase.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (C) 2018 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 "IPCThreadStateBase"
-
-#include <binderthreadstate/IPCThreadStateBase.h>
-#include <android-base/macros.h>
-
-#include <utils/Log.h>
-
-#include <errno.h>
-#include <inttypes.h>
-#include <pthread.h>
-
-namespace android {
-
-static pthread_mutex_t gTLSMutex = PTHREAD_MUTEX_INITIALIZER;
-static bool gHaveTLS = false;
-static pthread_key_t gTLS = 0;
-
-IPCThreadStateBase::IPCThreadStateBase() {
- pthread_setspecific(gTLS, this);
-}
-
-IPCThreadStateBase* IPCThreadStateBase::self()
-{
- if (gHaveTLS) {
-restart:
- const pthread_key_t k = gTLS;
- IPCThreadStateBase* st = (IPCThreadStateBase*)pthread_getspecific(k);
- if (st) return st;
- return new IPCThreadStateBase;
- }
-
- pthread_mutex_lock(&gTLSMutex);
- if (!gHaveTLS) {
- int key_create_value = pthread_key_create(&gTLS, threadDestructor);
- if (key_create_value != 0) {
- pthread_mutex_unlock(&gTLSMutex);
- ALOGW("IPCThreadStateBase::self() unable to create TLS key, expect a crash: %s\n",
- strerror(key_create_value));
- return nullptr;
- }
- gHaveTLS = true;
- }
- pthread_mutex_unlock(&gTLSMutex);
- goto restart;
-}
-
-void IPCThreadStateBase::pushCurrentState(CallState callState) {
- mCallStateStack.emplace(callState);
-}
-
-IPCThreadStateBase::CallState IPCThreadStateBase::popCurrentState() {
- ALOG_ASSERT(mCallStateStack.size > 0);
- CallState val = mCallStateStack.top();
- mCallStateStack.pop();
- return val;
-}
-
-IPCThreadStateBase::CallState IPCThreadStateBase::getCurrentBinderCallState() {
- if (mCallStateStack.size() > 0) {
- return mCallStateStack.top();
- }
- return CallState::NONE;
-}
-
-void IPCThreadStateBase::threadDestructor(void *st)
-{
- IPCThreadStateBase* const self = static_cast<IPCThreadStateBase*>(st);
- if (self) {
- delete self;
- }
-}
-
-}; // namespace android
diff --git a/libs/binderthreadstate/TEST_MAPPING b/libs/binderthreadstate/TEST_MAPPING
new file mode 100644
index 0000000..2bd0463
--- /dev/null
+++ b/libs/binderthreadstate/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libbinderthreadstateutils_test"
+ }
+ ]
+}
diff --git a/libs/binderthreadstate/include/binderthreadstate/CallerUtils.h b/libs/binderthreadstate/include/binderthreadstate/CallerUtils.h
new file mode 100644
index 0000000..a3e5026
--- /dev/null
+++ b/libs/binderthreadstate/include/binderthreadstate/CallerUtils.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2020 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
+
+// WARNING: DO NOT USE THIS
+// You should:
+// - have code know how it is handling things. Pass in caller information rather
+// than assuming that code is running in a specific global context
+// - use AIDL exclusively in your stack (HIDL is no longer required anywhere)
+
+#include <binder/IPCThreadState.h>
+#include <hwbinder/IPCThreadState.h>
+
+namespace android {
+
+enum class BinderCallType {
+ NONE,
+ BINDER,
+ HWBINDER,
+};
+
+// Based on where we are in recursion of nested binder/hwbinder calls, determine
+// which one we are closer to.
+inline static BinderCallType getCurrentServingCall() {
+ const void* hwbinderSp = android::hardware::IPCThreadState::self()->getServingStackPointer();
+ const void* binderSp = android::IPCThreadState::self()->getServingStackPointer();
+
+ if (hwbinderSp == nullptr && binderSp == nullptr) return BinderCallType::NONE;
+ if (hwbinderSp == nullptr) return BinderCallType::BINDER;
+ if (binderSp == nullptr) return BinderCallType::HWBINDER;
+
+ if (hwbinderSp < binderSp) return BinderCallType::HWBINDER;
+ return BinderCallType::BINDER;
+}
+
+} // namespace android
diff --git a/libs/binderthreadstate/include/binderthreadstate/IPCThreadStateBase.h b/libs/binderthreadstate/include/binderthreadstate/IPCThreadStateBase.h
deleted file mode 100644
index 6fdcc84..0000000
--- a/libs/binderthreadstate/include/binderthreadstate/IPCThreadStateBase.h
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2018 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 BINDER_THREADSTATE_IPC_THREADSTATE_BASE_H
-#define BINDER_THREADSTATE_IPC_THREADSTATE_BASE_H
-
-#include <stack>
-namespace android {
-
-class IPCThreadStateBase {
-public:
- enum CallState {
- HWBINDER,
- BINDER,
- NONE,
- };
- static IPCThreadStateBase* self();
- void pushCurrentState(CallState callState);
- CallState popCurrentState();
- CallState getCurrentBinderCallState();
-
-private:
- IPCThreadStateBase();
- static void threadDestructor(void *st);
-
- std::stack<CallState> mCallStateStack;
-};
-
-}; // namespace android
-
-#endif // BINDER_THREADSTATE_IPC_THREADSTATE_BASE_H
diff --git a/libs/binderthreadstate/test.cpp b/libs/binderthreadstate/test.cpp
new file mode 100644
index 0000000..68cc225
--- /dev/null
+++ b/libs/binderthreadstate/test.cpp
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2020 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 <BnAidlStuff.h>
+#include <android-base/logging.h>
+#include <binder/IServiceManager.h>
+#include <binderthreadstate/CallerUtils.h>
+#include <binderthreadstateutilstest/1.0/IHidlStuff.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlTransportSupport.h>
+#include <linux/prctl.h>
+#include <sys/prctl.h>
+
+using android::BinderCallType;
+using android::defaultServiceManager;
+using android::getCurrentServingCall;
+using android::getService;
+using android::OK;
+using android::sp;
+using android::String16;
+using android::binder::Status;
+using android::hardware::Return;
+using binderthreadstateutilstest::V1_0::IHidlStuff;
+
+constexpr size_t kP1Id = 1;
+constexpr size_t kP2Id = 2;
+
+// AIDL and HIDL are in separate namespaces so using same service names
+std::string id2name(size_t id) {
+ return "libbinderthreadstateutils-" + std::to_string(id);
+}
+
+// There are two servers calling each other recursively like this.
+//
+// P1 P2
+// | --HIDL--> |
+// | <--HIDL-- |
+// | --AIDL--> |
+// | <--AIDL-- |
+// | --HIDL--> |
+// | <--HIDL-- |
+// | --AIDL--> |
+// | <--AIDL-- |
+// ..........
+//
+// Calls always come in pairs (AIDL returns AIDL, HIDL returns HIDL) because
+// this means that P1 always has a 'waitForResponse' call which can service the
+// returning call and continue the recursion. Of course, with more threads, more
+// complicated calls are possible, but this should do here.
+
+static void callHidl(size_t id, int32_t idx) {
+ auto stuff = IHidlStuff::getService(id2name(id));
+ CHECK(stuff->call(idx).isOk());
+}
+
+static void callAidl(size_t id, int32_t idx) {
+ sp<IAidlStuff> stuff;
+ CHECK(OK == android::getService<IAidlStuff>(String16(id2name(id).c_str()), &stuff));
+ CHECK(stuff->call(idx).isOk());
+}
+
+class HidlServer : public IHidlStuff {
+public:
+ HidlServer(size_t thisId, size_t otherId) : thisId(thisId), otherId(otherId) {}
+ size_t thisId;
+ size_t otherId;
+
+ Return<void> callLocal() {
+ CHECK(BinderCallType::NONE == getCurrentServingCall());
+ return android::hardware::Status::ok();
+ }
+ Return<void> call(int32_t idx) {
+ LOG(INFO) << "HidlServer CALL " << thisId << " to " << otherId << " at idx: " << idx
+ << " with tid: " << gettid();
+ CHECK(BinderCallType::HWBINDER == getCurrentServingCall());
+ if (idx > 0) {
+ if (thisId == kP1Id && idx % 4 < 2) {
+ callHidl(otherId, idx - 1);
+ } else {
+ callAidl(otherId, idx - 1);
+ }
+ }
+ CHECK(BinderCallType::HWBINDER == getCurrentServingCall());
+ return android::hardware::Status::ok();
+ }
+};
+class AidlServer : public BnAidlStuff {
+public:
+ AidlServer(size_t thisId, size_t otherId) : thisId(thisId), otherId(otherId) {}
+ size_t thisId;
+ size_t otherId;
+
+ Status callLocal() {
+ CHECK(BinderCallType::NONE == getCurrentServingCall());
+ return Status::ok();
+ }
+ Status call(int32_t idx) {
+ LOG(INFO) << "AidlServer CALL " << thisId << " to " << otherId << " at idx: " << idx
+ << " with tid: " << gettid();
+ CHECK(BinderCallType::BINDER == getCurrentServingCall());
+ if (idx > 0) {
+ if (thisId == kP2Id && idx % 4 < 2) {
+ callHidl(otherId, idx - 1);
+ } else {
+ callAidl(otherId, idx - 1);
+ }
+ }
+ CHECK(BinderCallType::BINDER == getCurrentServingCall());
+ return Status::ok();
+ }
+};
+
+TEST(BinderThreadState, LocalHidlCall) {
+ sp<IHidlStuff> server = new HidlServer(0, 0);
+ EXPECT_TRUE(server->callLocal().isOk());
+}
+
+TEST(BinderThreadState, LocalAidlCall) {
+ sp<IAidlStuff> server = new AidlServer(0, 0);
+ EXPECT_TRUE(server->callLocal().isOk());
+}
+
+TEST(BindThreadState, RemoteHidlCall) {
+ auto stuff = IHidlStuff::getService(id2name(kP1Id));
+ ASSERT_NE(nullptr, stuff);
+ ASSERT_TRUE(stuff->call(0).isOk());
+}
+TEST(BindThreadState, RemoteAidlCall) {
+ sp<IAidlStuff> stuff;
+ ASSERT_EQ(OK, android::getService<IAidlStuff>(String16(id2name(kP1Id).c_str()), &stuff));
+ ASSERT_NE(nullptr, stuff);
+ ASSERT_TRUE(stuff->call(0).isOk());
+}
+
+TEST(BindThreadState, RemoteNestedStartHidlCall) {
+ auto stuff = IHidlStuff::getService(id2name(kP1Id));
+ ASSERT_NE(nullptr, stuff);
+ ASSERT_TRUE(stuff->call(100).isOk());
+}
+TEST(BindThreadState, RemoteNestedStartAidlCall) {
+ sp<IAidlStuff> stuff;
+ ASSERT_EQ(OK, android::getService<IAidlStuff>(String16(id2name(kP1Id).c_str()), &stuff));
+ ASSERT_NE(nullptr, stuff);
+ EXPECT_TRUE(stuff->call(100).isOk());
+}
+
+int server(size_t thisId, size_t otherId) {
+ // AIDL
+ android::ProcessState::self()->setThreadPoolMaxThreadCount(1);
+ sp<AidlServer> aidlServer = new AidlServer(thisId, otherId);
+ CHECK(OK == defaultServiceManager()->addService(String16(id2name(thisId).c_str()), aidlServer));
+ android::ProcessState::self()->startThreadPool();
+
+ // HIDL
+ setenv("TREBLE_TESTING_OVERRIDE", "true", true);
+ android::hardware::configureRpcThreadpool(1, true /*callerWillJoin*/);
+ sp<IHidlStuff> hidlServer = new HidlServer(thisId, otherId);
+ CHECK(OK == hidlServer->registerAsService(id2name(thisId).c_str()));
+ android::hardware::joinRpcThreadpool();
+
+ return EXIT_FAILURE;
+}
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ setenv("TREBLE_TESTING_OVERRIDE", "true", true);
+ if (fork() == 0) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
+ return server(kP1Id, kP2Id);
+ }
+ if (fork() == 0) {
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
+ return server(kP2Id, kP1Id);
+ }
+
+ android::waitForService<IAidlStuff>(String16(id2name(kP1Id).c_str()));
+ android::hardware::details::waitForHwService(IHidlStuff::descriptor, id2name(kP1Id).c_str());
+ android::waitForService<IAidlStuff>(String16(id2name(kP2Id).c_str()));
+ android::hardware::details::waitForHwService(IHidlStuff::descriptor, id2name(kP2Id).c_str());
+
+ return RUN_ALL_TESTS();
+}
diff --git a/libs/cputimeinstate/Android.bp b/libs/cputimeinstate/Android.bp
index 28cb138..b1943a4 100644
--- a/libs/cputimeinstate/Android.bp
+++ b/libs/cputimeinstate/Android.bp
@@ -8,19 +8,26 @@
"liblog",
"libnetdutils"
],
+ header_libs: ["bpf_prog_headers"],
cflags: [
"-Werror",
"-Wall",
"-Wextra",
],
+ export_include_dirs: ["."],
}
cc_test {
name: "libtimeinstate_test",
srcs: ["testtimeinstate.cpp"],
shared_libs: [
+ "libbase",
+ "libbpf",
+ "libbpf_android",
"libtimeinstate",
+ "libnetdutils",
],
+ header_libs: ["bpf_prog_headers"],
cflags: [
"-Werror",
"-Wall",
diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp
index 5fd4a95..1465296 100644
--- a/libs/cputimeinstate/cputimeinstate.cpp
+++ b/libs/cputimeinstate/cputimeinstate.cpp
@@ -17,12 +17,16 @@
#define LOG_TAG "libtimeinstate"
#include "cputimeinstate.h"
+#include <bpf_timeinstate.h>
#include <dirent.h>
#include <errno.h>
#include <inttypes.h>
+#include <sys/sysinfo.h>
#include <mutex>
+#include <numeric>
+#include <optional>
#include <set>
#include <string>
#include <unordered_map>
@@ -37,44 +41,38 @@
#include <libbpf.h>
#include <log/log.h>
-#define BPF_FS_PATH "/sys/fs/bpf/"
-
using android::base::StringPrintf;
using android::base::unique_fd;
namespace android {
namespace bpf {
-struct time_key_t {
- uint32_t uid;
- uint32_t freq;
-};
-
-struct val_t {
- uint64_t ar[100];
-};
-
static std::mutex gInitializedMutex;
static bool gInitialized = false;
+static std::mutex gTrackingMutex;
+static bool gTracking = false;
static uint32_t gNPolicies = 0;
+static uint32_t gNCpus = 0;
static std::vector<std::vector<uint32_t>> gPolicyFreqs;
static std::vector<std::vector<uint32_t>> gPolicyCpus;
static std::set<uint32_t> gAllFreqs;
-static unique_fd gMapFd;
+static unique_fd gTisMapFd;
+static unique_fd gConcurrentMapFd;
-static bool readNumbersFromFile(const std::string &path, std::vector<uint32_t> *out) {
+static std::optional<std::vector<uint32_t>> readNumbersFromFile(const std::string &path) {
std::string data;
- if (!android::base::ReadFileToString(path, &data)) return false;
+ if (!android::base::ReadFileToString(path, &data)) return {};
auto strings = android::base::Split(data, " \n");
+ std::vector<uint32_t> ret;
for (const auto &s : strings) {
if (s.empty()) continue;
uint32_t n;
- if (!android::base::ParseUint(s, &n)) return false;
- out->emplace_back(n);
+ if (!android::base::ParseUint(s, &n)) return {};
+ ret.emplace_back(n);
}
- return true;
+ return ret;
}
static int isPolicyFile(const struct dirent *d) {
@@ -89,10 +87,22 @@
return policyN1 - policyN2;
}
+static int bpf_obj_get_wronly(const char *pathname) {
+ union bpf_attr attr;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.pathname = ptr_to_u64((void *)pathname);
+ attr.file_flags = BPF_F_WRONLY;
+
+ return syscall(__NR_bpf, BPF_OBJ_GET, &attr, sizeof(attr));
+}
+
static bool initGlobals() {
std::lock_guard<std::mutex> guard(gInitializedMutex);
if (gInitialized) return true;
+ gNCpus = get_nprocs_conf();
+
struct dirent **dirlist;
const char basepath[] = "/sys/devices/system/cpu/cpufreq";
int ret = scandir(basepath, &dirlist, isPolicyFile, comparePolicyFiles);
@@ -111,21 +121,28 @@
for (const auto &name : {"available", "boost"}) {
std::string path =
StringPrintf("%s/%s/scaling_%s_frequencies", basepath, policy.c_str(), name);
- if (!readNumbersFromFile(path, &freqs)) return false;
+ auto nums = readNumbersFromFile(path);
+ if (!nums) continue;
+ freqs.insert(freqs.end(), nums->begin(), nums->end());
}
+ if (freqs.empty()) return false;
std::sort(freqs.begin(), freqs.end());
gPolicyFreqs.emplace_back(freqs);
for (auto freq : freqs) gAllFreqs.insert(freq);
- std::vector<uint32_t> cpus;
std::string path = StringPrintf("%s/%s/%s", basepath, policy.c_str(), "related_cpus");
- if (!readNumbersFromFile(path, &cpus)) return false;
- gPolicyCpus.emplace_back(cpus);
+ auto cpus = readNumbersFromFile(path);
+ if (!cpus) return false;
+ gPolicyCpus.emplace_back(*cpus);
}
- gMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_times")};
- if (gMapFd < 0) return false;
+ gTisMapFd = unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_time_in_state_map")};
+ if (gTisMapFd < 0) return false;
+
+ gConcurrentMapFd =
+ unique_fd{bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")};
+ if (gConcurrentMapFd < 0) return false;
gInitialized = true;
return true;
@@ -145,97 +162,289 @@
// process dies then it must be called again to resume tracking.
// This function should *not* be called while tracking is already active; doing so is unnecessary
// and can lead to accounting errors.
-bool startTrackingUidCpuFreqTimes() {
- return attachTracepointProgram("sched", "sched_switch") &&
+bool startTrackingUidTimes() {
+ std::lock_guard<std::mutex> guard(gTrackingMutex);
+ if (!initGlobals()) return false;
+ if (gTracking) return true;
+
+ unique_fd cpuPolicyFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_cpu_policy_map"));
+ if (cpuPolicyFd < 0) return false;
+
+ for (uint32_t i = 0; i < gPolicyCpus.size(); ++i) {
+ for (auto &cpu : gPolicyCpus[i]) {
+ if (writeToMapEntry(cpuPolicyFd, &cpu, &i, BPF_ANY)) return false;
+ }
+ }
+
+ unique_fd freqToIdxFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_freq_to_idx_map"));
+ if (freqToIdxFd < 0) return false;
+ freq_idx_key_t key;
+ for (uint32_t i = 0; i < gNPolicies; ++i) {
+ key.policy = i;
+ for (uint32_t j = 0; j < gPolicyFreqs[i].size(); ++j) {
+ key.freq = gPolicyFreqs[i][j];
+ // Start indexes at 1 so that uninitialized state is distinguishable from lowest freq.
+ // The uid_times map still uses 0-based indexes, and the sched_switch program handles
+ // conversion between them, so this does not affect our map reading code.
+ uint32_t idx = j + 1;
+ if (writeToMapEntry(freqToIdxFd, &key, &idx, BPF_ANY)) return false;
+ }
+ }
+
+ unique_fd cpuLastUpdateFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_cpu_last_update_map"));
+ if (cpuLastUpdateFd < 0) return false;
+ std::vector<uint64_t> zeros(get_nprocs_conf(), 0);
+ uint32_t zero = 0;
+ if (writeToMapEntry(cpuLastUpdateFd, &zero, zeros.data(), BPF_ANY)) return false;
+
+ unique_fd nrActiveFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_nr_active_map"));
+ if (nrActiveFd < 0) return false;
+ if (writeToMapEntry(nrActiveFd, &zero, &zero, BPF_ANY)) return false;
+
+ unique_fd policyNrActiveFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_policy_nr_active_map"));
+ if (policyNrActiveFd < 0) return false;
+ for (uint32_t i = 0; i < gNPolicies; ++i) {
+ if (writeToMapEntry(policyNrActiveFd, &i, &zero, BPF_ANY)) return false;
+ }
+
+ unique_fd policyFreqIdxFd(bpf_obj_get_wronly(BPF_FS_PATH "map_time_in_state_policy_freq_idx_map"));
+ if (policyFreqIdxFd < 0) return false;
+ for (uint32_t i = 0; i < gNPolicies; ++i) {
+ if (writeToMapEntry(policyFreqIdxFd, &i, &zero, BPF_ANY)) return false;
+ }
+
+ gTracking = attachTracepointProgram("sched", "sched_switch") &&
attachTracepointProgram("power", "cpu_frequency");
+ return gTracking;
}
-// Retrieve the times in ns that uid spent running at each CPU frequency and store in freqTimes.
-// Returns false on error. Otherwise, returns true and populates freqTimes with a vector of vectors
-// using the format:
+std::optional<std::vector<std::vector<uint32_t>>> getCpuFreqs() {
+ if (!gInitialized && !initGlobals()) return {};
+ return gPolicyFreqs;
+}
+
+// Retrieve the times in ns that uid spent running at each CPU frequency.
+// Return contains no value on error, otherwise it contains a vector of vectors using the format:
// [[t0_0, t0_1, ...],
// [t1_0, t1_1, ...], ...]
// where ti_j is the ns that uid spent running on the ith cluster at that cluster's jth lowest freq.
-bool getUidCpuFreqTimes(uint32_t uid, std::vector<std::vector<uint64_t>> *freqTimes) {
- if (!gInitialized && !initGlobals()) return false;
- time_key_t key = {.uid = uid, .freq = 0};
+std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t uid) {
+ if (!gInitialized && !initGlobals()) return {};
- freqTimes->clear();
- freqTimes->resize(gNPolicies);
- std::vector<uint32_t> idxs(gNPolicies, 0);
+ std::vector<std::vector<uint64_t>> out;
+ uint32_t maxFreqCount = 0;
+ for (const auto &freqList : gPolicyFreqs) {
+ if (freqList.size() > maxFreqCount) maxFreqCount = freqList.size();
+ out.emplace_back(freqList.size(), 0);
+ }
- val_t value;
- for (uint32_t freq : gAllFreqs) {
- key.freq = freq;
- int ret = findMapEntry(gMapFd, &key, &value);
- if (ret) {
- if (errno == ENOENT)
- memset(&value.ar, 0, sizeof(value.ar));
- else
- return false;
+ std::vector<tis_val_t> vals(gNCpus);
+ time_key_t key = {.uid = uid};
+ for (uint32_t i = 0; i <= (maxFreqCount - 1) / FREQS_PER_ENTRY; ++i) {
+ key.bucket = i;
+ if (findMapEntry(gTisMapFd, &key, vals.data())) {
+ if (errno != ENOENT) return {};
+ continue;
}
- for (uint32_t i = 0; i < gNPolicies; ++i) {
- if (idxs[i] == gPolicyFreqs[i].size() || freq != gPolicyFreqs[i][idxs[i]]) continue;
- uint64_t time = 0;
- for (uint32_t cpu : gPolicyCpus[i]) time += value.ar[cpu];
- idxs[i] += 1;
- (*freqTimes)[i].emplace_back(time);
+
+ auto offset = i * FREQS_PER_ENTRY;
+ auto nextOffset = (i + 1) * FREQS_PER_ENTRY;
+ for (uint32_t j = 0; j < gNPolicies; ++j) {
+ if (offset >= gPolicyFreqs[j].size()) continue;
+ auto begin = out[j].begin() + offset;
+ auto end = nextOffset < gPolicyFreqs[j].size() ? begin + FREQS_PER_ENTRY : out[j].end();
+
+ for (const auto &cpu : gPolicyCpus[j]) {
+ std::transform(begin, end, std::begin(vals[cpu].ar), begin, std::plus<uint64_t>());
+ }
}
}
- return true;
+ return out;
}
-// Retrieve the times in ns that each uid spent running at each CPU freq and store in freqTimeMap.
-// Returns false on error. Otherwise, returns true and populates freqTimeMap with a map from uids to
-// vectors of vectors using the format:
+// Retrieve the times in ns that each uid spent running at each CPU freq.
+// Return contains no value on error, otherwise it contains a map from uids to vectors of vectors
+// using the format:
// { uid0 -> [[t0_0_0, t0_0_1, ...], [t0_1_0, t0_1_1, ...], ...],
// uid1 -> [[t1_0_0, t1_0_1, ...], [t1_1_0, t1_1_1, ...], ...], ... }
// where ti_j_k is the ns uid i spent running on the jth cluster at the cluster's kth lowest freq.
-bool getUidsCpuFreqTimes(
- std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> *freqTimeMap) {
- if (!gInitialized && !initGlobals()) return false;
-
- int fd = bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_times");
- if (fd < 0) return false;
- BpfMap<time_key_t, val_t> m(fd);
-
- std::vector<std::unordered_map<uint32_t, uint32_t>> policyFreqIdxs;
- for (uint32_t i = 0; i < gNPolicies; ++i) {
- std::unordered_map<uint32_t, uint32_t> freqIdxs;
- for (size_t j = 0; j < gPolicyFreqs[i].size(); ++j) freqIdxs[gPolicyFreqs[i][j]] = j;
- policyFreqIdxs.emplace_back(freqIdxs);
+std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>>
+getUidsCpuFreqTimes() {
+ if (!gInitialized && !initGlobals()) return {};
+ time_key_t key, prevKey;
+ std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> map;
+ if (getFirstMapKey(gTisMapFd, &key)) {
+ if (errno == ENOENT) return map;
+ return std::nullopt;
}
- auto fn = [freqTimeMap, &policyFreqIdxs](const time_key_t &key, const val_t &val,
- const BpfMap<time_key_t, val_t> &) {
- if (freqTimeMap->find(key.uid) == freqTimeMap->end()) {
- (*freqTimeMap)[key.uid].resize(gNPolicies);
- for (uint32_t i = 0; i < gNPolicies; ++i) {
- (*freqTimeMap)[key.uid][i].resize(gPolicyFreqs[i].size(), 0);
+ std::vector<std::vector<uint64_t>> mapFormat;
+ for (const auto &freqList : gPolicyFreqs) mapFormat.emplace_back(freqList.size(), 0);
+
+ std::vector<tis_val_t> vals(gNCpus);
+ do {
+ if (findMapEntry(gTisMapFd, &key, vals.data())) return {};
+ if (map.find(key.uid) == map.end()) map.emplace(key.uid, mapFormat);
+
+ auto offset = key.bucket * FREQS_PER_ENTRY;
+ auto nextOffset = (key.bucket + 1) * FREQS_PER_ENTRY;
+ for (uint32_t i = 0; i < gNPolicies; ++i) {
+ if (offset >= gPolicyFreqs[i].size()) continue;
+ auto begin = map[key.uid][i].begin() + offset;
+ auto end = nextOffset < gPolicyFreqs[i].size() ? begin + FREQS_PER_ENTRY :
+ map[key.uid][i].end();
+ for (const auto &cpu : gPolicyCpus[i]) {
+ std::transform(begin, end, std::begin(vals[cpu].ar), begin, std::plus<uint64_t>());
}
}
+ prevKey = key;
+ } while (!getNextMapKey(gTisMapFd, &prevKey, &key));
+ if (errno != ENOENT) return {};
+ return map;
+}
+
+static bool verifyConcurrentTimes(const concurrent_time_t &ct) {
+ uint64_t activeSum = std::accumulate(ct.active.begin(), ct.active.end(), (uint64_t)0);
+ uint64_t policySum = 0;
+ for (const auto &vec : ct.policy) {
+ policySum += std::accumulate(vec.begin(), vec.end(), (uint64_t)0);
+ }
+ return activeSum == policySum;
+}
+
+// Retrieve the times in ns that uid spent running concurrently with each possible number of other
+// tasks on each cluster (policy times) and overall (active times).
+// Return contains no value on error, otherwise it contains a concurrent_time_t with the format:
+// {.active = [a0, a1, ...], .policy = [[p0_0, p0_1, ...], [p1_0, p1_1, ...], ...]}
+// where ai is the ns spent running concurrently with tasks on i other cpus and pi_j is the ns spent
+// running on the ith cluster, concurrently with tasks on j other cpus in the same cluster
+std::optional<concurrent_time_t> getUidConcurrentTimes(uint32_t uid, bool retry) {
+ if (!gInitialized && !initGlobals()) return {};
+ concurrent_time_t ret = {.active = std::vector<uint64_t>(gNCpus, 0)};
+ for (const auto &cpuList : gPolicyCpus) ret.policy.emplace_back(cpuList.size(), 0);
+ std::vector<concurrent_val_t> vals(gNCpus);
+ time_key_t key = {.uid = uid};
+ for (key.bucket = 0; key.bucket <= (gNCpus - 1) / CPUS_PER_ENTRY; ++key.bucket) {
+ if (findMapEntry(gConcurrentMapFd, &key, vals.data())) {
+ if (errno != ENOENT) return {};
+ continue;
+ }
+ auto offset = key.bucket * CPUS_PER_ENTRY;
+ auto nextOffset = (key.bucket + 1) * CPUS_PER_ENTRY;
+
+ auto activeBegin = ret.active.begin() + offset;
+ auto activeEnd = nextOffset < gNCpus ? activeBegin + CPUS_PER_ENTRY : ret.active.end();
+
+ for (uint32_t cpu = 0; cpu < gNCpus; ++cpu) {
+ std::transform(activeBegin, activeEnd, std::begin(vals[cpu].active), activeBegin,
+ std::plus<uint64_t>());
+ }
- for (size_t policy = 0; policy < gNPolicies; ++policy) {
+ for (uint32_t policy = 0; policy < gNPolicies; ++policy) {
+ if (offset >= gPolicyCpus[policy].size()) continue;
+ auto policyBegin = ret.policy[policy].begin() + offset;
+ auto policyEnd = nextOffset < gPolicyCpus[policy].size() ? policyBegin + CPUS_PER_ENTRY
+ : ret.policy[policy].end();
+
for (const auto &cpu : gPolicyCpus[policy]) {
- auto freqIdx = policyFreqIdxs[policy][key.freq];
- (*freqTimeMap)[key.uid][policy][freqIdx] += val.ar[cpu];
+ std::transform(policyBegin, policyEnd, std::begin(vals[cpu].policy), policyBegin,
+ std::plus<uint64_t>());
}
}
- return android::netdutils::status::ok;
- };
- return isOk(m.iterateWithValue(fn));
+ }
+ if (!verifyConcurrentTimes(ret) && retry) return getUidConcurrentTimes(uid, false);
+ return ret;
+}
+
+// Retrieve the times in ns that each uid spent running concurrently with each possible number of
+// other tasks on each cluster (policy times) and overall (active times).
+// Return contains no value on error, otherwise it contains a map from uids to concurrent_time_t's
+// using the format:
+// { uid0 -> {.active = [a0, a1, ...], .policy = [[p0_0, p0_1, ...], [p1_0, p1_1, ...], ...] }, ...}
+// where ai is the ns spent running concurrently with tasks on i other cpus and pi_j is the ns spent
+// running on the ith cluster, concurrently with tasks on j other cpus in the same cluster.
+std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsConcurrentTimes() {
+ if (!gInitialized && !initGlobals()) return {};
+ time_key_t key, prevKey;
+ std::unordered_map<uint32_t, concurrent_time_t> ret;
+ if (getFirstMapKey(gConcurrentMapFd, &key)) {
+ if (errno == ENOENT) return ret;
+ return {};
+ }
+
+ concurrent_time_t retFormat = {.active = std::vector<uint64_t>(gNCpus, 0)};
+ for (const auto &cpuList : gPolicyCpus) retFormat.policy.emplace_back(cpuList.size(), 0);
+
+ std::vector<concurrent_val_t> vals(gNCpus);
+ std::vector<uint64_t>::iterator activeBegin, activeEnd, policyBegin, policyEnd;
+
+ do {
+ if (findMapEntry(gConcurrentMapFd, &key, vals.data())) return {};
+ if (ret.find(key.uid) == ret.end()) ret.emplace(key.uid, retFormat);
+
+ auto offset = key.bucket * CPUS_PER_ENTRY;
+ auto nextOffset = (key.bucket + 1) * CPUS_PER_ENTRY;
+
+ activeBegin = ret[key.uid].active.begin();
+ activeEnd = nextOffset < gNCpus ? activeBegin + CPUS_PER_ENTRY : ret[key.uid].active.end();
+
+ for (uint32_t cpu = 0; cpu < gNCpus; ++cpu) {
+ std::transform(activeBegin, activeEnd, std::begin(vals[cpu].active), activeBegin,
+ std::plus<uint64_t>());
+ }
+
+ for (uint32_t policy = 0; policy < gNPolicies; ++policy) {
+ if (offset >= gPolicyCpus[policy].size()) continue;
+ policyBegin = ret[key.uid].policy[policy].begin() + offset;
+ policyEnd = nextOffset < gPolicyCpus[policy].size() ? policyBegin + CPUS_PER_ENTRY
+ : ret[key.uid].policy[policy].end();
+
+ for (const auto &cpu : gPolicyCpus[policy]) {
+ std::transform(policyBegin, policyEnd, std::begin(vals[cpu].policy), policyBegin,
+ std::plus<uint64_t>());
+ }
+ }
+ prevKey = key;
+ } while (!getNextMapKey(gConcurrentMapFd, &prevKey, &key));
+ if (errno != ENOENT) return {};
+ for (const auto &[key, value] : ret) {
+ if (!verifyConcurrentTimes(value)) {
+ auto val = getUidConcurrentTimes(key, false);
+ if (val.has_value()) ret[key] = val.value();
+ }
+ }
+ return ret;
}
// Clear all time in state data for a given uid. Returns false on error, true otherwise.
-bool clearUidCpuFreqTimes(uint32_t uid) {
+// This is only suitable for clearing data when an app is uninstalled; if called on a UID with
+// running tasks it will cause time in state vs. concurrent time totals to be inconsistent for that
+// UID.
+bool clearUidTimes(uint32_t uid) {
if (!gInitialized && !initGlobals()) return false;
- time_key_t key = {.uid = uid, .freq = 0};
- std::vector<uint32_t> idxs(gNPolicies, 0);
- for (auto freq : gAllFreqs) {
- key.freq = freq;
- if (deleteMapEntry(gMapFd, &key) && errno != ENOENT) return false;
+ time_key_t key = {.uid = uid};
+
+ uint32_t maxFreqCount = 0;
+ for (const auto &freqList : gPolicyFreqs) {
+ if (freqList.size() > maxFreqCount) maxFreqCount = freqList.size();
+ }
+
+ tis_val_t zeros = {0};
+ std::vector<tis_val_t> vals(gNCpus, zeros);
+ for (key.bucket = 0; key.bucket <= (maxFreqCount - 1) / FREQS_PER_ENTRY; ++key.bucket) {
+ if (writeToMapEntry(gTisMapFd, &key, vals.data(), BPF_EXIST) && errno != ENOENT)
+ return false;
+ if (deleteMapEntry(gTisMapFd, &key) && errno != ENOENT) return false;
+ }
+
+ concurrent_val_t czeros = {.policy = {0}, .active = {0}};
+ std::vector<concurrent_val_t> cvals(gNCpus, czeros);
+ for (key.bucket = 0; key.bucket <= (gNCpus - 1) / CPUS_PER_ENTRY; ++key.bucket) {
+ if (writeToMapEntry(gConcurrentMapFd, &key, cvals.data(), BPF_EXIST) && errno != ENOENT)
+ return false;
+ if (deleteMapEntry(gConcurrentMapFd, &key) && errno != ENOENT) return false;
}
return true;
}
diff --git a/libs/cputimeinstate/cputimeinstate.h b/libs/cputimeinstate/cputimeinstate.h
index 9f6103e..49469d8 100644
--- a/libs/cputimeinstate/cputimeinstate.h
+++ b/libs/cputimeinstate/cputimeinstate.h
@@ -22,10 +22,20 @@
namespace android {
namespace bpf {
-bool startTrackingUidCpuFreqTimes();
-bool getUidCpuFreqTimes(unsigned int uid, std::vector<std::vector<uint64_t>> *freqTimes);
-bool getUidsCpuFreqTimes(std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>> *tisMap);
-bool clearUidCpuFreqTimes(unsigned int uid);
+bool startTrackingUidTimes();
+std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t uid);
+std::optional<std::unordered_map<uint32_t, std::vector<std::vector<uint64_t>>>>
+ getUidsCpuFreqTimes();
+std::optional<std::vector<std::vector<uint32_t>>> getCpuFreqs();
+
+struct concurrent_time_t {
+ std::vector<uint64_t> active;
+ std::vector<std::vector<uint64_t>> policy;
+};
+
+std::optional<concurrent_time_t> getUidConcurrentTimes(uint32_t uid, bool retry = true);
+std::optional<std::unordered_map<uint32_t, concurrent_time_t>> getUidsConcurrentTimes();
+bool clearUidTimes(unsigned int uid);
} // namespace bpf
} // namespace android
diff --git a/libs/cputimeinstate/testtimeinstate.cpp b/libs/cputimeinstate/testtimeinstate.cpp
index 9837865..23d87fd 100644
--- a/libs/cputimeinstate/testtimeinstate.cpp
+++ b/libs/cputimeinstate/testtimeinstate.cpp
@@ -1,57 +1,381 @@
+/*
+ * Copyright (C) 2018 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 <bpf_timeinstate.h>
+
+#include <sys/sysinfo.h>
+
+#include <numeric>
#include <unordered_map>
#include <vector>
#include <gtest/gtest.h>
+#include <android-base/unique_fd.h>
+#include <bpf/BpfMap.h>
#include <cputimeinstate.h>
+#include <libbpf.h>
namespace android {
namespace bpf {
+static constexpr uint64_t NSEC_PER_SEC = 1000000000;
+static constexpr uint64_t NSEC_PER_YEAR = NSEC_PER_SEC * 60 * 60 * 24 * 365;
+
using std::vector;
-TEST(TimeInStateTest, SingleUid) {
- vector<vector<uint64_t>> times;
- ASSERT_TRUE(getUidCpuFreqTimes(0, ×));
- EXPECT_FALSE(times.empty());
+TEST(TimeInStateTest, SingleUidTimeInState) {
+ auto times = getUidCpuFreqTimes(0);
+ ASSERT_TRUE(times.has_value());
+ EXPECT_FALSE(times->empty());
}
-TEST(TimeInStateTest, AllUid) {
+TEST(TimeInStateTest, SingleUidConcurrentTimes) {
+ auto concurrentTimes = getUidConcurrentTimes(0);
+ ASSERT_TRUE(concurrentTimes.has_value());
+ ASSERT_FALSE(concurrentTimes->active.empty());
+ ASSERT_FALSE(concurrentTimes->policy.empty());
+
+ uint64_t policyEntries = 0;
+ for (const auto &policyTimeVec : concurrentTimes->policy) policyEntries += policyTimeVec.size();
+ ASSERT_EQ(concurrentTimes->active.size(), policyEntries);
+}
+
+static void TestConcurrentTimesConsistent(const struct concurrent_time_t &concurrentTime) {
+ size_t maxPolicyCpus = 0;
+ for (const auto &vec : concurrentTime.policy) {
+ maxPolicyCpus = std::max(maxPolicyCpus, vec.size());
+ }
+ uint64_t policySum = 0;
+ for (size_t i = 0; i < maxPolicyCpus; ++i) {
+ for (const auto &vec : concurrentTime.policy) {
+ if (i < vec.size()) policySum += vec[i];
+ }
+ ASSERT_LE(concurrentTime.active[i], policySum);
+ policySum -= concurrentTime.active[i];
+ }
+ policySum = 0;
+ for (size_t i = 0; i < concurrentTime.active.size(); ++i) {
+ for (const auto &vec : concurrentTime.policy) {
+ if (i < vec.size()) policySum += vec[vec.size() - 1 - i];
+ }
+ auto activeSum = concurrentTime.active[concurrentTime.active.size() - 1 - i];
+ // This check is slightly flaky because we may read a map entry in the middle of an update
+ // when active times have been updated but policy times have not. This happens infrequently
+ // and can be distinguished from more serious bugs by re-running the test: if the underlying
+ // data itself is inconsistent, the test will fail every time.
+ ASSERT_LE(activeSum, policySum);
+ policySum -= activeSum;
+ }
+}
+
+static void TestUidTimesConsistent(const std::vector<std::vector<uint64_t>> &timeInState,
+ const struct concurrent_time_t &concurrentTime) {
+ ASSERT_NO_FATAL_FAILURE(TestConcurrentTimesConsistent(concurrentTime));
+ ASSERT_EQ(timeInState.size(), concurrentTime.policy.size());
+ uint64_t policySum = 0;
+ for (uint32_t i = 0; i < timeInState.size(); ++i) {
+ uint64_t tisSum =
+ std::accumulate(timeInState[i].begin(), timeInState[i].end(), (uint64_t)0);
+ uint64_t concurrentSum = std::accumulate(concurrentTime.policy[i].begin(),
+ concurrentTime.policy[i].end(), (uint64_t)0);
+ if (tisSum < concurrentSum)
+ ASSERT_LE(concurrentSum - tisSum, NSEC_PER_SEC);
+ else
+ ASSERT_LE(tisSum - concurrentSum, NSEC_PER_SEC);
+ policySum += concurrentSum;
+ }
+ uint64_t activeSum = std::accumulate(concurrentTime.active.begin(), concurrentTime.active.end(),
+ (uint64_t)0);
+ EXPECT_EQ(activeSum, policySum);
+}
+
+TEST(TimeInStateTest, SingleUidTimesConsistent) {
+ auto times = getUidCpuFreqTimes(0);
+ ASSERT_TRUE(times.has_value());
+
+ auto concurrentTimes = getUidConcurrentTimes(0);
+ ASSERT_TRUE(concurrentTimes.has_value());
+
+ ASSERT_NO_FATAL_FAILURE(TestUidTimesConsistent(*times, *concurrentTimes));
+}
+
+TEST(TimeInStateTest, AllUidTimeInState) {
vector<size_t> sizes;
- std::unordered_map<uint32_t, vector<vector<uint64_t>>> map;
- ASSERT_TRUE(getUidsCpuFreqTimes(&map));
+ auto map = getUidsCpuFreqTimes();
+ ASSERT_TRUE(map.has_value());
- ASSERT_FALSE(map.empty());
+ ASSERT_FALSE(map->empty());
- auto firstEntry = map.begin()->second;
+ auto firstEntry = map->begin()->second;
for (const auto &subEntry : firstEntry) sizes.emplace_back(subEntry.size());
- for (const auto &vec : map) {
+ for (const auto &vec : *map) {
ASSERT_EQ(vec.second.size(), sizes.size());
for (size_t i = 0; i < vec.second.size(); ++i) ASSERT_EQ(vec.second[i].size(), sizes[i]);
}
}
+TEST(TimeInStateTest, SingleAndAllUidTimeInStateConsistent) {
+ auto map = getUidsCpuFreqTimes();
+ ASSERT_TRUE(map.has_value());
+ ASSERT_FALSE(map->empty());
+
+ for (const auto &kv : *map) {
+ uint32_t uid = kv.first;
+ auto times1 = kv.second;
+ auto times2 = getUidCpuFreqTimes(uid);
+ ASSERT_TRUE(times2.has_value());
+
+ ASSERT_EQ(times1.size(), times2->size());
+ for (uint32_t i = 0; i < times1.size(); ++i) {
+ ASSERT_EQ(times1[i].size(), (*times2)[i].size());
+ for (uint32_t j = 0; j < times1[i].size(); ++j) {
+ ASSERT_LE((*times2)[i][j] - times1[i][j], NSEC_PER_SEC);
+ }
+ }
+ }
+}
+
+TEST(TimeInStateTest, AllUidConcurrentTimes) {
+ auto map = getUidsConcurrentTimes();
+ ASSERT_TRUE(map.has_value());
+ ASSERT_FALSE(map->empty());
+
+ auto firstEntry = map->begin()->second;
+ for (const auto &kv : *map) {
+ ASSERT_EQ(kv.second.active.size(), firstEntry.active.size());
+ ASSERT_EQ(kv.second.policy.size(), firstEntry.policy.size());
+ for (size_t i = 0; i < kv.second.policy.size(); ++i) {
+ ASSERT_EQ(kv.second.policy[i].size(), firstEntry.policy[i].size());
+ }
+ }
+}
+
+TEST(TimeInStateTest, SingleAndAllUidConcurrentTimesConsistent) {
+ auto map = getUidsConcurrentTimes();
+ ASSERT_TRUE(map.has_value());
+ for (const auto &kv : *map) {
+ uint32_t uid = kv.first;
+ auto times1 = kv.second;
+ auto times2 = getUidConcurrentTimes(uid);
+ ASSERT_TRUE(times2.has_value());
+ for (uint32_t i = 0; i < times1.active.size(); ++i) {
+ ASSERT_LE(times2->active[i] - times1.active[i], NSEC_PER_SEC);
+ }
+ for (uint32_t i = 0; i < times1.policy.size(); ++i) {
+ for (uint32_t j = 0; j < times1.policy[i].size(); ++j) {
+ ASSERT_LE(times2->policy[i][j] - times1.policy[i][j], NSEC_PER_SEC);
+ }
+ }
+ }
+}
+
+void TestCheckDelta(uint64_t before, uint64_t after) {
+ // Times should never decrease
+ ASSERT_LE(before, after);
+ // UID can't have run for more than ~1s on each CPU
+ ASSERT_LE(after - before, NSEC_PER_SEC * 2 * get_nprocs_conf());
+}
+
+TEST(TimeInStateTest, AllUidTimeInStateMonotonic) {
+ auto map1 = getUidsCpuFreqTimes();
+ ASSERT_TRUE(map1.has_value());
+ sleep(1);
+ auto map2 = getUidsCpuFreqTimes();
+ ASSERT_TRUE(map2.has_value());
+
+ for (const auto &kv : *map1) {
+ uint32_t uid = kv.first;
+ auto times = kv.second;
+ ASSERT_NE(map2->find(uid), map2->end());
+ for (uint32_t policy = 0; policy < times.size(); ++policy) {
+ for (uint32_t freqIdx = 0; freqIdx < times[policy].size(); ++freqIdx) {
+ auto before = times[policy][freqIdx];
+ auto after = (*map2)[uid][policy][freqIdx];
+ ASSERT_NO_FATAL_FAILURE(TestCheckDelta(before, after));
+ }
+ }
+ }
+}
+
+TEST(TimeInStateTest, AllUidConcurrentTimesMonotonic) {
+ auto map1 = getUidsConcurrentTimes();
+ ASSERT_TRUE(map1.has_value());
+ ASSERT_FALSE(map1->empty());
+ sleep(1);
+ auto map2 = getUidsConcurrentTimes();
+ ASSERT_TRUE(map2.has_value());
+ ASSERT_FALSE(map2->empty());
+
+ for (const auto &kv : *map1) {
+ uint32_t uid = kv.first;
+ auto times = kv.second;
+ ASSERT_NE(map2->find(uid), map2->end());
+ for (uint32_t i = 0; i < times.active.size(); ++i) {
+ auto before = times.active[i];
+ auto after = (*map2)[uid].active[i];
+ ASSERT_NO_FATAL_FAILURE(TestCheckDelta(before, after));
+ }
+ for (uint32_t policy = 0; policy < times.policy.size(); ++policy) {
+ for (uint32_t idx = 0; idx < times.policy[policy].size(); ++idx) {
+ auto before = times.policy[policy][idx];
+ auto after = (*map2)[uid].policy[policy][idx];
+ ASSERT_NO_FATAL_FAILURE(TestCheckDelta(before, after));
+ }
+ }
+ }
+}
+
+TEST(TimeInStateTest, AllUidTimeInStateSanityCheck) {
+ auto map = getUidsCpuFreqTimes();
+ ASSERT_TRUE(map.has_value());
+
+ bool foundLargeValue = false;
+ for (const auto &kv : *map) {
+ for (const auto &timeVec : kv.second) {
+ for (const auto &time : timeVec) {
+ ASSERT_LE(time, NSEC_PER_YEAR);
+ if (time > UINT32_MAX) foundLargeValue = true;
+ }
+ }
+ }
+ // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using
+ // uint64_t as expected, we should have some times higher than that.
+ ASSERT_TRUE(foundLargeValue);
+}
+
+TEST(TimeInStateTest, AllUidConcurrentTimesSanityCheck) {
+ auto concurrentMap = getUidsConcurrentTimes();
+ ASSERT_TRUE(concurrentMap);
+
+ bool activeFoundLargeValue = false;
+ bool policyFoundLargeValue = false;
+ for (const auto &kv : *concurrentMap) {
+ for (const auto &time : kv.second.active) {
+ ASSERT_LE(time, NSEC_PER_YEAR);
+ if (time > UINT32_MAX) activeFoundLargeValue = true;
+ }
+ for (const auto &policyTimeVec : kv.second.policy) {
+ for (const auto &time : policyTimeVec) {
+ ASSERT_LE(time, NSEC_PER_YEAR);
+ if (time > UINT32_MAX) policyFoundLargeValue = true;
+ }
+ }
+ }
+ // UINT32_MAX nanoseconds is less than 5 seconds, so if every part of our pipeline is using
+ // uint64_t as expected, we should have some times higher than that.
+ ASSERT_TRUE(activeFoundLargeValue);
+ ASSERT_TRUE(policyFoundLargeValue);
+}
+
+TEST(TimeInStateTest, AllUidTimesConsistent) {
+ auto tisMap = getUidsCpuFreqTimes();
+ ASSERT_TRUE(tisMap.has_value());
+
+ auto concurrentMap = getUidsConcurrentTimes();
+ ASSERT_TRUE(concurrentMap.has_value());
+
+ ASSERT_EQ(tisMap->size(), concurrentMap->size());
+ for (const auto &kv : *tisMap) {
+ uint32_t uid = kv.first;
+ auto times = kv.second;
+ ASSERT_NE(concurrentMap->find(uid), concurrentMap->end());
+
+ auto concurrentTimes = (*concurrentMap)[uid];
+ ASSERT_NO_FATAL_FAILURE(TestUidTimesConsistent(times, concurrentTimes));
+ }
+}
+
TEST(TimeInStateTest, RemoveUid) {
- vector<vector<uint64_t>> times, times2;
- ASSERT_TRUE(getUidCpuFreqTimes(0, ×));
- ASSERT_FALSE(times.empty());
+ uint32_t uid = 0;
+ {
+ // Find an unused UID
+ auto times = getUidsCpuFreqTimes();
+ ASSERT_TRUE(times.has_value());
+ ASSERT_FALSE(times->empty());
+ for (const auto &kv : *times) uid = std::max(uid, kv.first);
+ ++uid;
+ }
+ {
+ // Add a map entry for our fake UID by copying a real map entry
+ android::base::unique_fd fd{
+ bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_time_in_state_map")};
+ ASSERT_GE(fd, 0);
+ time_key_t k;
+ ASSERT_FALSE(getFirstMapKey(fd, &k));
+ std::vector<tis_val_t> vals(get_nprocs_conf());
+ ASSERT_FALSE(findMapEntry(fd, &k, vals.data()));
+ uint32_t copiedUid = k.uid;
+ k.uid = uid;
+ ASSERT_FALSE(writeToMapEntry(fd, &k, vals.data(), BPF_NOEXIST));
+
+ android::base::unique_fd fd2{
+ bpf_obj_get(BPF_FS_PATH "map_time_in_state_uid_concurrent_times_map")};
+ k.uid = copiedUid;
+ k.bucket = 0;
+ std::vector<concurrent_val_t> cvals(get_nprocs_conf());
+ ASSERT_FALSE(findMapEntry(fd2, &k, cvals.data()));
+ k.uid = uid;
+ ASSERT_FALSE(writeToMapEntry(fd2, &k, cvals.data(), BPF_NOEXIST));
+ }
+ auto times = getUidCpuFreqTimes(uid);
+ ASSERT_TRUE(times.has_value());
+ ASSERT_FALSE(times->empty());
+
+ auto concurrentTimes = getUidConcurrentTimes(0);
+ ASSERT_TRUE(concurrentTimes.has_value());
+ ASSERT_FALSE(concurrentTimes->active.empty());
+ ASSERT_FALSE(concurrentTimes->policy.empty());
uint64_t sum = 0;
- for (size_t i = 0; i < times.size(); ++i) {
- for (auto x : times[i]) sum += x;
+ for (size_t i = 0; i < times->size(); ++i) {
+ for (auto x : (*times)[i]) sum += x;
}
ASSERT_GT(sum, (uint64_t)0);
- ASSERT_TRUE(clearUidCpuFreqTimes(0));
-
- ASSERT_TRUE(getUidCpuFreqTimes(0, ×2));
- ASSERT_EQ(times2.size(), times.size());
- for (size_t i = 0; i < times.size(); ++i) {
- ASSERT_EQ(times2[i].size(), times[i].size());
- for (size_t j = 0; j < times[i].size(); ++j) ASSERT_LE(times2[i][j], times[i][j]);
+ uint64_t activeSum = 0;
+ for (size_t i = 0; i < concurrentTimes->active.size(); ++i) {
+ activeSum += concurrentTimes->active[i];
}
+ ASSERT_GT(activeSum, (uint64_t)0);
+
+ ASSERT_TRUE(clearUidTimes(uid));
+
+ auto allTimes = getUidsCpuFreqTimes();
+ ASSERT_TRUE(allTimes.has_value());
+ ASSERT_FALSE(allTimes->empty());
+ ASSERT_EQ(allTimes->find(uid), allTimes->end());
+
+ auto allConcurrentTimes = getUidsConcurrentTimes();
+ ASSERT_TRUE(allConcurrentTimes.has_value());
+ ASSERT_FALSE(allConcurrentTimes->empty());
+ ASSERT_EQ(allConcurrentTimes->find(uid), allConcurrentTimes->end());
+}
+
+TEST(TimeInStateTest, GetCpuFreqs) {
+ auto freqs = getCpuFreqs();
+ ASSERT_TRUE(freqs.has_value());
+
+ auto times = getUidCpuFreqTimes(0);
+ ASSERT_TRUE(times.has_value());
+
+ ASSERT_EQ(freqs->size(), times->size());
+ for (size_t i = 0; i < freqs->size(); ++i) EXPECT_EQ((*freqs)[i].size(), (*times)[i].size());
}
} // namespace bpf
diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp
index 250f902..0ede8b1 100644
--- a/libs/dumputils/dump_utils.cpp
+++ b/libs/dumputils/dump_utils.cpp
@@ -16,7 +16,9 @@
#include <set>
#include <android-base/file.h>
+#include <android-base/properties.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <android/hidl/manager/1.0/IServiceManager.h>
#include <dumputils/dump_utils.h>
#include <log/log.h>
@@ -32,7 +34,6 @@
"/system/bin/mediametrics", // media.metrics
"/system/bin/mediaserver",
"/system/bin/netd",
- "/system/bin/vold",
"/system/bin/sdcard",
"/system/bin/statsd",
"/system/bin/surfaceflinger",
@@ -42,11 +43,19 @@
NULL,
};
+
+// Native processes to dump on debuggable builds.
+static const char* debuggable_native_processes_to_dump[] = {
+ "/system/bin/vold",
+ NULL,
+};
+
/* list of hal interface to dump containing process during native dumps */
static const char* hal_interfaces_to_dump[] {
"android.hardware.audio@2.0::IDevicesFactory",
"android.hardware.audio@4.0::IDevicesFactory",
"android.hardware.audio@5.0::IDevicesFactory",
+ "android.hardware.audio@6.0::IDevicesFactory",
"android.hardware.biometrics.face@1.0::IBiometricsFace",
"android.hardware.bluetooth@1.0::IBluetoothHci",
"android.hardware.camera.provider@2.4::ICameraProvider",
@@ -65,13 +74,34 @@
NULL,
};
-bool should_dump_hal_interface(const char* interface) {
+/* list of extra hal interfaces to dump containing process during native dumps */
+// This is filled when dumpstate is called.
+static std::set<const std::string> extra_hal_interfaces_to_dump;
+
+static void read_extra_hals_to_dump_from_property() {
+ // extra hals to dump are already filled
+ if (extra_hal_interfaces_to_dump.size() > 0) {
+ return;
+ }
+ std::string value = android::base::GetProperty("ro.dump.hals.extra", "");
+ std::vector<std::string> tokens = android::base::Split(value, ",");
+ for (const auto &token : tokens) {
+ std::string trimmed_token = android::base::Trim(token);
+ if (trimmed_token.length() == 0) {
+ continue;
+ }
+ extra_hal_interfaces_to_dump.insert(trimmed_token);
+ }
+}
+
+// check if interface is included in either default hal list or extra hal list
+bool should_dump_hal_interface(const std::string& interface) {
for (const char** i = hal_interfaces_to_dump; *i; i++) {
- if (!strcmp(*i, interface)) {
+ if (interface == *i) {
return true;
}
}
- return false;
+ return extra_hal_interfaces_to_dump.find(interface) != extra_hal_interfaces_to_dump.end();
}
bool should_dump_native_traces(const char* path) {
@@ -80,6 +110,15 @@
return true;
}
}
+
+ if (android::base::GetBoolProperty("ro.debuggable", false)) {
+ for (const char** p = debuggable_native_processes_to_dump; *p; p++) {
+ if (!strcmp(*p, path)) {
+ return true;
+ }
+ }
+ }
+
return false;
}
@@ -91,13 +130,15 @@
sp<IServiceManager> manager = IServiceManager::getService();
std::set<int> pids;
+ read_extra_hals_to_dump_from_property();
+
Return<void> ret = manager->debugDump([&](auto& hals) {
for (const auto &info : hals) {
if (info.pid == static_cast<int>(IServiceManager::PidConstant::NO_PID)) {
continue;
}
- if (!should_dump_hal_interface(info.interfaceName.c_str())) {
+ if (!should_dump_hal_interface(info.interfaceName)) {
continue;
}
diff --git a/libs/fakeservicemanager/Android.bp b/libs/fakeservicemanager/Android.bp
new file mode 100644
index 0000000..de32ff4
--- /dev/null
+++ b/libs/fakeservicemanager/Android.bp
@@ -0,0 +1,25 @@
+cc_defaults {
+ name: "fakeservicemanager_defaults",
+ srcs: [
+ "ServiceManager.cpp",
+ ],
+
+ shared_libs: [
+ "libbinder",
+ "libutils",
+ ],
+}
+
+cc_library {
+ name: "libfakeservicemanager",
+ defaults: ["fakeservicemanager_defaults"],
+}
+
+cc_test_host {
+ name: "fakeservicemanager_test",
+ defaults: ["fakeservicemanager_defaults"],
+ srcs: [
+ "test_sm.cpp",
+ ],
+ static_libs: ["libgmock"],
+}
diff --git a/libs/fakeservicemanager/ServiceManager.cpp b/libs/fakeservicemanager/ServiceManager.cpp
new file mode 100644
index 0000000..6964324
--- /dev/null
+++ b/libs/fakeservicemanager/ServiceManager.cpp
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2020 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 "ServiceManager.h"
+
+namespace android {
+
+ServiceManager::ServiceManager() {}
+
+sp<IBinder> ServiceManager::getService( const String16& name) const {
+ // Servicemanager is single-threaded and cannot block. This method exists for legacy reasons.
+ return checkService(name);
+}
+
+sp<IBinder> ServiceManager::checkService( const String16& name) const {
+ auto it = mNameToService.find(name);
+ if (it == mNameToService.end()) {
+ return nullptr;
+ }
+ return it->second;
+}
+
+status_t ServiceManager::addService(const String16& name, const sp<IBinder>& service,
+ bool /*allowIsolated*/,
+ int /*dumpsysFlags*/) {
+ mNameToService[name] = service;
+ return NO_ERROR;
+}
+
+Vector<String16> ServiceManager::listServices(int /*dumpsysFlags*/) {
+ Vector<String16> services;
+ for (auto const& [name, service] : mNameToService) {
+ (void) service;
+ services.push_back(name);
+ }
+ return services;
+}
+
+IBinder* ServiceManager::onAsBinder() {
+ return nullptr;
+}
+
+sp<IBinder> ServiceManager::waitForService(const String16& name) {
+ return checkService(name);
+}
+
+bool ServiceManager::isDeclared(const String16& name) {
+ return mNameToService.find(name) != mNameToService.end();
+}
+
+} // namespace android
diff --git a/libs/fakeservicemanager/ServiceManager.h b/libs/fakeservicemanager/ServiceManager.h
new file mode 100644
index 0000000..62311d4
--- /dev/null
+++ b/libs/fakeservicemanager/ServiceManager.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2020 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/IServiceManager.h>
+
+#include <map>
+
+namespace android {
+
+/**
+ * A local host simple implementation of IServiceManager, that does not
+ * communicate over binder.
+*/
+class ServiceManager : public IServiceManager {
+public:
+ ServiceManager();
+
+ /**
+ * Equivalent of checkService.
+ */
+ sp<IBinder> getService( const String16& name) const override;
+
+ /**
+ * Retrieve an existing service, non-blocking.
+ */
+ sp<IBinder> checkService( const String16& name) const override;
+
+ /**
+ * Register a service.
+ */
+ status_t addService(const String16& name, const sp<IBinder>& service,
+ bool allowIsolated = false,
+ int dumpsysFlags = DUMP_FLAG_PRIORITY_DEFAULT) override;
+
+ /**
+ * Return list of all existing services.
+ */
+ Vector<String16> listServices(int dumpsysFlags = 0) override;
+
+ IBinder* onAsBinder() override;
+
+ /**
+ * Effectively no-oped in this implementation - equivalent to checkService.
+ */
+ sp<IBinder> waitForService(const String16& name) override;
+
+ /**
+ * Check if a service is declared (e.g. VINTF manifest).
+ *
+ * If this returns true, waitForService should always be able to return the
+ * service.
+ */
+ bool isDeclared(const String16& name) override;
+
+private:
+ std::map<String16, sp<IBinder>> mNameToService;
+};
+
+} // namespace android
diff --git a/libs/fakeservicemanager/test_sm.cpp b/libs/fakeservicemanager/test_sm.cpp
new file mode 100644
index 0000000..71e5abe
--- /dev/null
+++ b/libs/fakeservicemanager/test_sm.cpp
@@ -0,0 +1,126 @@
+/*
+ * 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 <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+#include <binder/Binder.h>
+#include <binder/ProcessState.h>
+#include <binder/IServiceManager.h>
+
+#include "ServiceManager.h"
+
+using android::sp;
+using android::BBinder;
+using android::IBinder;
+using android::OK;
+using android::status_t;
+using android::ServiceManager;
+using android::String16;
+using android::IServiceManager;
+using testing::ElementsAre;
+
+static sp<IBinder> getBinder() {
+ class LinkableBinder : public BBinder {
+ status_t linkToDeath(const sp<DeathRecipient>&, void*, uint32_t) override {
+ // let SM linkToDeath
+ return OK;
+ }
+ };
+
+ return new LinkableBinder;
+}
+
+TEST(AddService, HappyHappy) {
+ auto sm = new ServiceManager();
+ EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
+}
+
+TEST(AddService, HappyOverExistingService) {
+ auto sm = new ServiceManager();
+ EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
+ EXPECT_EQ(sm->addService(String16("foo"), getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
+}
+
+TEST(GetService, HappyHappy) {
+ auto sm = new ServiceManager();
+ sp<IBinder> service = getBinder();
+
+ EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
+
+ EXPECT_EQ(sm->getService(String16("foo")), service);
+}
+
+TEST(GetService, NonExistant) {
+ auto sm = new ServiceManager();
+
+ EXPECT_EQ(sm->getService(String16("foo")), nullptr);
+}
+
+TEST(ListServices, AllServices) {
+ auto sm = new ServiceManager();
+
+ EXPECT_EQ(sm->addService(String16("sd"), getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
+ EXPECT_EQ(sm->addService(String16("sc"), getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_NORMAL), OK);
+ EXPECT_EQ(sm->addService(String16("sb"), getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_HIGH), OK);
+ EXPECT_EQ(sm->addService(String16("sa"), getBinder(), false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL), OK);
+
+ android::Vector<String16> out = sm->listServices(IServiceManager::DUMP_FLAG_PRIORITY_ALL);
+
+ // all there and in the right order
+ EXPECT_THAT(out, ElementsAre(String16("sa"), String16("sb"), String16("sc"),
+ String16("sd")));
+}
+
+TEST(WaitForService, NonExistant) {
+ auto sm = new ServiceManager();
+
+ EXPECT_EQ(sm->waitForService(String16("foo")), nullptr);
+}
+
+TEST(WaitForService, HappyHappy) {
+ auto sm = new ServiceManager();
+ sp<IBinder> service = getBinder();
+
+ EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
+
+ EXPECT_EQ(sm->waitForService(String16("foo")), service);
+}
+
+TEST(IsDeclared, NonExistant) {
+ auto sm = new ServiceManager();
+
+ EXPECT_FALSE(sm->isDeclared(String16("foo")));
+}
+
+TEST(IsDeclared, HappyHappy) {
+ auto sm = new ServiceManager();
+ sp<IBinder> service = getBinder();
+
+ EXPECT_EQ(sm->addService(String16("foo"), service, false /*allowIsolated*/,
+ IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT), OK);
+
+ EXPECT_TRUE(sm->isDeclared(String16("foo")));
+}
diff --git a/libs/gui/Android.bp b/libs/gui/Android.bp
index 166775b..ba3195a 100644
--- a/libs/gui/Android.bp
+++ b/libs/gui/Android.bp
@@ -186,6 +186,10 @@
"libvndksupport",
],
+ static_libs: [
+ "libbinderthreadstateutils",
+ ],
+
header_libs: [
"libgui_headers",
"libnativebase_headers",
diff --git a/libs/gui/BufferQueueThreadState.cpp b/libs/gui/BufferQueueThreadState.cpp
index 3b531ec..c13030b 100644
--- a/libs/gui/BufferQueueThreadState.cpp
+++ b/libs/gui/BufferQueueThreadState.cpp
@@ -15,6 +15,7 @@
*/
#include <binder/IPCThreadState.h>
+#include <binderthreadstate/CallerUtils.h>
#include <hwbinder/IPCThreadState.h>
#include <private/gui/BufferQueueThreadState.h>
#include <unistd.h>
@@ -22,14 +23,14 @@
namespace android {
uid_t BufferQueueThreadState::getCallingUid() {
- if (hardware::IPCThreadState::self()->isServingCall()) {
+ if (getCurrentServingCall() == BinderCallType::HWBINDER) {
return hardware::IPCThreadState::self()->getCallingUid();
}
return IPCThreadState::self()->getCallingUid();
}
pid_t BufferQueueThreadState::getCallingPid() {
- if (hardware::IPCThreadState::self()->isServingCall()) {
+ if (getCurrentServingCall() == BinderCallType::HWBINDER) {
return hardware::IPCThreadState::self()->getCallingPid();
}
return IPCThreadState::self()->getCallingPid();
diff --git a/libs/gui/OWNERS b/libs/gui/OWNERS
index 73150dc..c13401d 100644
--- a/libs/gui/OWNERS
+++ b/libs/gui/OWNERS
@@ -1,7 +1,12 @@
+adyabr@google.com
+akrulec@google.com
+alecmouri@google.com
jessehall@google.com
jwcai@google.com
lpy@google.com
marissaw@google.com
mathias@google.com
racarr@google.com
+steventhomas@google.com
stoza@google.com
+vhau@google.com
diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp
index 139987e..abc9103 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -577,7 +577,8 @@
uint32_t len = static_cast<uint32_t>(string8.length());
FlattenableUtils::write(buffer, size, len);
memcpy(static_cast<char*>(buffer), string8.string(), len);
- FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(len));
+ FlattenableUtils::advance(buffer, size, len);
+ size -= FlattenableUtils::align<4>(buffer);
}
bool Sensor::unflattenString8(void const*& buffer, size_t& size, String8& outputString8) {
diff --git a/libs/ui/Android.bp b/libs/ui/Android.bp
index 42b578c..080336b 100644
--- a/libs/ui/Android.bp
+++ b/libs/ui/Android.bp
@@ -32,12 +32,6 @@
sanitize: {
integer_overflow: true,
misc_undefined: ["bounds"],
- diag: {
- misc_undefined: ["bounds"],
- no_recover: [
- "bounds",
- ],
- },
},
srcs: [
diff --git a/opengl/libs/Android.bp b/opengl/libs/Android.bp
index eb90c8b..3c741ab 100644
--- a/opengl/libs/Android.bp
+++ b/opengl/libs/Android.bp
@@ -161,6 +161,10 @@
],
ldflags: ["-Wl,--exclude-libs=ALL"],
export_include_dirs: ["EGL/include"],
+ stubs: {
+ symbol_file: "libEGL.map.txt",
+ versions: ["29"],
+ },
}
cc_test {
diff --git a/services/stats/Android.bp b/services/stats/Android.bp
new file mode 100644
index 0000000..1ce0524
--- /dev/null
+++ b/services/stats/Android.bp
@@ -0,0 +1,22 @@
+cc_library_shared {
+ name: "libstatshidl",
+ srcs: [
+ "StatsHal.cpp",
+ ],
+ cflags: ["-Wall", "-Werror"],
+ shared_libs: [
+ "android.frameworks.stats@1.0",
+ "libhidlbase",
+ "liblog",
+ "libstatslog",
+ "libstatssocket",
+ "libutils",
+ ],
+ export_include_dirs: [
+ "include/",
+ ],
+ local_include_dirs: [
+ "include/stats",
+ ],
+ vintf_fragments: ["android.frameworks.stats@1.0-service.xml"]
+}
diff --git a/services/stats/OWNERS b/services/stats/OWNERS
new file mode 100644
index 0000000..a61babf
--- /dev/null
+++ b/services/stats/OWNERS
@@ -0,0 +1,9 @@
+jeffreyhuang@google.com
+joeo@google.com
+jtnguyen@google.com
+muhammadq@google.com
+ruchirr@google.com
+singhtejinder@google.com
+tsaichristine@google.com
+yaochen@google.com
+yro@google.com
diff --git a/services/stats/StatsHal.cpp b/services/stats/StatsHal.cpp
new file mode 100644
index 0000000..b775431
--- /dev/null
+++ b/services/stats/StatsHal.cpp
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2020 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 DEBUG false // STOPSHIP if true
+#define LOG_TAG "StatsHal"
+
+#include <log/log.h>
+#include <statslog.h>
+
+#include "StatsHal.h"
+
+namespace android {
+namespace frameworks {
+namespace stats {
+namespace V1_0 {
+namespace implementation {
+
+StatsHal::StatsHal() {}
+
+hardware::Return<void> StatsHal::reportSpeakerImpedance(
+ const SpeakerImpedance& speakerImpedance) {
+ android::util::stats_write(android::util::SPEAKER_IMPEDANCE_REPORTED,
+ speakerImpedance.speakerLocation, speakerImpedance.milliOhms);
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsHal::reportHardwareFailed(const HardwareFailed& hardwareFailed) {
+ android::util::stats_write(android::util::HARDWARE_FAILED, int32_t(hardwareFailed.hardwareType),
+ hardwareFailed.hardwareLocation, int32_t(hardwareFailed.errorCode));
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsHal::reportPhysicalDropDetected(
+ const PhysicalDropDetected& physicalDropDetected) {
+ android::util::stats_write(android::util::PHYSICAL_DROP_DETECTED,
+ int32_t(physicalDropDetected.confidencePctg), physicalDropDetected.accelPeak,
+ physicalDropDetected.freefallDuration);
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsHal::reportChargeCycles(const ChargeCycles& chargeCycles) {
+ std::vector<int32_t> buckets = chargeCycles.cycleBucket;
+ int initialSize = buckets.size();
+ for (int i = 0; i < 10 - initialSize; i++) {
+ buckets.push_back(-1); // Push -1 for buckets that do not exist.
+ }
+ android::util::stats_write(android::util::CHARGE_CYCLES_REPORTED, buckets[0], buckets[1],
+ buckets[2], buckets[3], buckets[4], buckets[5], buckets[6], buckets[7], buckets[8],
+ buckets[9]);
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsHal::reportBatteryHealthSnapshot(
+ const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) {
+ android::util::stats_write(android::util::BATTERY_HEALTH_SNAPSHOT,
+ int32_t(batteryHealthSnapshotArgs.type), batteryHealthSnapshotArgs.temperatureDeciC,
+ batteryHealthSnapshotArgs.voltageMicroV, batteryHealthSnapshotArgs.currentMicroA,
+ batteryHealthSnapshotArgs.openCircuitVoltageMicroV,
+ batteryHealthSnapshotArgs.resistanceMicroOhm, batteryHealthSnapshotArgs.levelPercent);
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsHal::reportSlowIo(const SlowIo& slowIo) {
+ android::util::stats_write(android::util::SLOW_IO, int32_t(slowIo.operation), slowIo.count);
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsHal::reportBatteryCausedShutdown(
+ const BatteryCausedShutdown& batteryCausedShutdown) {
+ android::util::stats_write(android::util::BATTERY_CAUSED_SHUTDOWN,
+ batteryCausedShutdown.voltageMicroV);
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsHal::reportUsbPortOverheatEvent(
+ const UsbPortOverheatEvent& usbPortOverheatEvent) {
+ android::util::stats_write(android::util::USB_PORT_OVERHEAT_EVENT_REPORTED,
+ usbPortOverheatEvent.plugTemperatureDeciC, usbPortOverheatEvent.maxTemperatureDeciC,
+ usbPortOverheatEvent.timeToOverheat, usbPortOverheatEvent.timeToHysteresis,
+ usbPortOverheatEvent.timeToInactive);
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsHal::reportSpeechDspStat(
+ const SpeechDspStat& speechDspStat) {
+ android::util::stats_write(android::util::SPEECH_DSP_STAT_REPORTED,
+ speechDspStat.totalUptimeMillis, speechDspStat.totalDowntimeMillis,
+ speechDspStat.totalCrashCount, speechDspStat.totalRecoverCount);
+
+ return hardware::Void();
+}
+
+hardware::Return<void> StatsHal::reportVendorAtom(const VendorAtom& vendorAtom) {
+ ALOGW("reportVendorAtom unsupported");
+ std::string reverDomainName = vendorAtom.reverseDomainName;
+ return hardware::Void();
+}
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace stats
+} // namespace frameworks
+} // namespace android
diff --git a/services/stats/android.frameworks.stats@1.0-service.xml b/services/stats/android.frameworks.stats@1.0-service.xml
new file mode 100644
index 0000000..bb02f66
--- /dev/null
+++ b/services/stats/android.frameworks.stats@1.0-service.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="framework">
+ <hal>
+ <name>android.frameworks.stats</name>
+ <transport>hwbinder</transport>
+ <version>1.0</version>
+ <interface>
+ <name>IStats</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+</manifest>
diff --git a/services/stats/include/stats/StatsHal.h b/services/stats/include/stats/StatsHal.h
new file mode 100644
index 0000000..ad14263
--- /dev/null
+++ b/services/stats/include/stats/StatsHal.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2020 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 <android/frameworks/stats/1.0/IStats.h>
+#include <android/frameworks/stats/1.0/types.h>
+
+using namespace android::frameworks::stats::V1_0;
+
+namespace android {
+namespace frameworks {
+namespace stats {
+namespace V1_0 {
+namespace implementation {
+
+using android::hardware::Return;
+
+/**
+* Implements the Stats HAL
+*/
+class StatsHal : public IStats {
+public:
+ StatsHal();
+
+ /**
+ * Binder call to get SpeakerImpedance atom.
+ */
+ virtual Return<void> reportSpeakerImpedance(const SpeakerImpedance& speakerImpedance) override;
+
+ /**
+ * Binder call to get HardwareFailed atom.
+ */
+ virtual Return<void> reportHardwareFailed(const HardwareFailed& hardwareFailed) override;
+
+ /**
+ * Binder call to get PhysicalDropDetected atom.
+ */
+ virtual Return<void> reportPhysicalDropDetected(
+ const PhysicalDropDetected& physicalDropDetected) override;
+
+ /**
+ * Binder call to get ChargeCyclesReported atom.
+ */
+ virtual Return<void> reportChargeCycles(const ChargeCycles& chargeCycles) override;
+
+ /**
+ * Binder call to get BatteryHealthSnapshot atom.
+ */
+ virtual Return<void> reportBatteryHealthSnapshot(
+ const BatteryHealthSnapshotArgs& batteryHealthSnapshotArgs) override;
+
+ /**
+ * Binder call to get SlowIo atom.
+ */
+ virtual Return<void> reportSlowIo(const SlowIo& slowIo) override;
+
+ /**
+ * Binder call to get BatteryCausedShutdown atom.
+ */
+ virtual Return<void> reportBatteryCausedShutdown(
+ const BatteryCausedShutdown& batteryCausedShutdown) override;
+
+ /**
+ * Binder call to get UsbPortOverheatEvent atom.
+ */
+ virtual Return<void> reportUsbPortOverheatEvent(
+ const UsbPortOverheatEvent& usbPortOverheatEvent) override;
+
+ /**
+ * Binder call to get Speech DSP state atom.
+ */
+ virtual Return<void> reportSpeechDspStat(
+ const SpeechDspStat& speechDspStat) override;
+
+ /**
+ * Binder call to get vendor atom.
+ */
+ virtual Return<void> reportVendorAtom(const VendorAtom& vendorAtom) override;
+};
+
+} // namespace implementation
+} // namespace V1_0
+} // namespace stats
+} // namespace frameworks
+} // namespace android
diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS
index 69d8c89..f2bc65d 100644
--- a/services/surfaceflinger/OWNERS
+++ b/services/surfaceflinger/OWNERS
@@ -5,4 +5,6 @@
lpy@google.com
marissaw@google.com
racarr@google.com
-stoza@google.com
\ No newline at end of file
+steventhomas@google.com
+stoza@google.com
+vhau@google.com
diff --git a/services/surfaceflinger/surfaceflinger.rc b/services/surfaceflinger/surfaceflinger.rc
index aea602b..d3942e8 100644
--- a/services/surfaceflinger/surfaceflinger.rc
+++ b/services/surfaceflinger/surfaceflinger.rc
@@ -2,6 +2,7 @@
class core animation
user system
group graphics drmrpc readproc
+ capabilities SYS_NICE
onrestart restart zygote
writepid /dev/stune/foreground/tasks
socket pdx/system/vr/display/client stream 0666 system graphics u:object_r:pdx_display_client_endpoint_socket:s0