diff options
244 files changed, 9782 insertions, 1904 deletions
diff --git a/METADATA b/METADATA new file mode 100644 index 0000000000..d97975ca3b --- /dev/null +++ b/METADATA @@ -0,0 +1,3 @@ +third_party { + license_type: NOTICE +} diff --git a/build/phone-hdpi-512-dalvik-heap.mk b/build/phone-hdpi-512-dalvik-heap.mk index 102c3f1f66..f9a12ef377 100644 --- a/build/phone-hdpi-512-dalvik-heap.mk +++ b/build/phone-hdpi-512-dalvik-heap.mk @@ -17,10 +17,10 @@ # Provides overrides to configure the Dalvik heap for a standard high density # phone with around 512MB total RAM. -PRODUCT_PROPERTY_OVERRIDES += \ - dalvik.vm.heapstartsize=5m \ - dalvik.vm.heapgrowthlimit=48m \ - dalvik.vm.heapsize=128m \ - dalvik.vm.heaptargetutilization=0.75 \ - dalvik.vm.heapminfree=512k \ - dalvik.vm.heapmaxfree=2m +PRODUCT_VENDOR_PROPERTIES += \ + dalvik.vm.heapstartsize?=5m \ + dalvik.vm.heapgrowthlimit?=48m \ + dalvik.vm.heapsize?=128m \ + dalvik.vm.heaptargetutilization?=0.75 \ + dalvik.vm.heapminfree?=512k \ + dalvik.vm.heapmaxfree?=2m diff --git a/build/phone-hdpi-dalvik-heap.mk b/build/phone-hdpi-dalvik-heap.mk index cc0ac90298..71cce7492a 100644 --- a/build/phone-hdpi-dalvik-heap.mk +++ b/build/phone-hdpi-dalvik-heap.mk @@ -16,9 +16,9 @@ # Provides overrides to configure the Dalvik heap for a standard high density phone. -PRODUCT_PROPERTY_OVERRIDES += \ - dalvik.vm.heapstartsize=5m \ - dalvik.vm.heapsize=32m \ - dalvik.vm.heaptargetutilization=0.75 \ - dalvik.vm.heapminfree=512k \ - dalvik.vm.heapmaxfree=2m +PRODUCT_VENDOR_PROPERTIES += \ + dalvik.vm.heapstartsize?=5m \ + dalvik.vm.heapsize?=32m \ + dalvik.vm.heaptargetutilization?=0.75 \ + dalvik.vm.heapminfree?=512k \ + dalvik.vm.heapmaxfree?=2m diff --git a/build/phone-xhdpi-1024-dalvik-heap.mk b/build/phone-xhdpi-1024-dalvik-heap.mk index 221227d4d8..a522a7dc7c 100644 --- a/build/phone-xhdpi-1024-dalvik-heap.mk +++ b/build/phone-xhdpi-1024-dalvik-heap.mk @@ -16,10 +16,10 @@ # Provides overrides to configure the Dalvik heap for a xhdpi phone -PRODUCT_PROPERTY_OVERRIDES += \ - dalvik.vm.heapstartsize=8m \ - dalvik.vm.heapgrowthlimit=96m \ - dalvik.vm.heapsize=256m \ - dalvik.vm.heaptargetutilization=0.75 \ - dalvik.vm.heapminfree=512k \ - dalvik.vm.heapmaxfree=8m +PRODUCT_VENDOR_PROPERTIES += \ + dalvik.vm.heapstartsize?=8m \ + dalvik.vm.heapgrowthlimit?=96m \ + dalvik.vm.heapsize?=256m \ + dalvik.vm.heaptargetutilization?=0.75 \ + dalvik.vm.heapminfree?=512k \ + dalvik.vm.heapmaxfree?=8m diff --git a/build/phone-xhdpi-2048-dalvik-heap.mk b/build/phone-xhdpi-2048-dalvik-heap.mk index 7ccfc13702..f38d2f22c3 100644 --- a/build/phone-xhdpi-2048-dalvik-heap.mk +++ b/build/phone-xhdpi-2048-dalvik-heap.mk @@ -17,10 +17,10 @@ # Provides overrides to configure the Dalvik heap for a 2GB phone # 192m of RAM gives enough space for 5 8 megapixel camera bitmaps in RAM. -PRODUCT_PROPERTY_OVERRIDES += \ - dalvik.vm.heapstartsize=8m \ - dalvik.vm.heapgrowthlimit=192m \ - dalvik.vm.heapsize=512m \ - dalvik.vm.heaptargetutilization=0.75 \ - dalvik.vm.heapminfree=512k \ - dalvik.vm.heapmaxfree=8m +PRODUCT_VENDOR_PROPERTIES += \ + dalvik.vm.heapstartsize?=8m \ + dalvik.vm.heapgrowthlimit?=192m \ + dalvik.vm.heapsize?=512m \ + dalvik.vm.heaptargetutilization?=0.75 \ + dalvik.vm.heapminfree?=512k \ + dalvik.vm.heapmaxfree?=8m diff --git a/build/phone-xhdpi-4096-dalvik-heap.mk b/build/phone-xhdpi-4096-dalvik-heap.mk index 2b848413fb..a6ff5a88c8 100644 --- a/build/phone-xhdpi-4096-dalvik-heap.mk +++ b/build/phone-xhdpi-4096-dalvik-heap.mk @@ -16,10 +16,10 @@ # Provides overrides to configure the Dalvik heap for a 4GB phone -PRODUCT_PROPERTY_OVERRIDES += \ - dalvik.vm.heapstartsize=8m \ - dalvik.vm.heapgrowthlimit=192m \ - dalvik.vm.heapsize=512m \ - dalvik.vm.heaptargetutilization=0.6 \ - dalvik.vm.heapminfree=8m \ - dalvik.vm.heapmaxfree=16m +PRODUCT_VENDOR_PROPERTIES += \ + dalvik.vm.heapstartsize?=8m \ + dalvik.vm.heapgrowthlimit?=192m \ + dalvik.vm.heapsize?=512m \ + dalvik.vm.heaptargetutilization?=0.6 \ + dalvik.vm.heapminfree?=8m \ + dalvik.vm.heapmaxfree?=16m diff --git a/build/phone-xhdpi-6144-dalvik-heap.mk b/build/phone-xhdpi-6144-dalvik-heap.mk index 2bacc4a9ab..f08830b2cf 100644 --- a/build/phone-xhdpi-6144-dalvik-heap.mk +++ b/build/phone-xhdpi-6144-dalvik-heap.mk @@ -16,10 +16,10 @@ # Provides overrides to configure the Dalvik heap for a 6GB phone -PRODUCT_PROPERTY_OVERRIDES += \ - dalvik.vm.heapstartsize=16m \ - dalvik.vm.heapgrowthlimit=256m \ - dalvik.vm.heapsize=512m \ - dalvik.vm.heaptargetutilization=0.5 \ - dalvik.vm.heapminfree=8m \ - dalvik.vm.heapmaxfree=32m +PRODUCT_VENDOR_PROPERTIES += \ + dalvik.vm.heapstartsize?=16m \ + dalvik.vm.heapgrowthlimit?=256m \ + dalvik.vm.heapsize?=512m \ + dalvik.vm.heaptargetutilization?=0.5 \ + dalvik.vm.heapminfree?=8m \ + dalvik.vm.heapmaxfree?=32m diff --git a/build/tablet-10in-xhdpi-2048-dalvik-heap.mk b/build/tablet-10in-xhdpi-2048-dalvik-heap.mk index 1721fcc2ca..48c6ea651e 100644 --- a/build/tablet-10in-xhdpi-2048-dalvik-heap.mk +++ b/build/tablet-10in-xhdpi-2048-dalvik-heap.mk @@ -16,10 +16,10 @@ # Provides overrides to configure the Dalvik heap for a standard tablet device. -PRODUCT_PROPERTY_OVERRIDES += \ - dalvik.vm.heapstartsize=16m \ - dalvik.vm.heapgrowthlimit=192m \ - dalvik.vm.heapsize=512m \ - dalvik.vm.heaptargetutilization=0.75 \ - dalvik.vm.heapminfree=512k \ - dalvik.vm.heapmaxfree=8m +PRODUCT_VENDOR_PROPERTIES += \ + dalvik.vm.heapstartsize?=16m \ + dalvik.vm.heapgrowthlimit?=192m \ + dalvik.vm.heapsize?=512m \ + dalvik.vm.heaptargetutilization?=0.75 \ + dalvik.vm.heapminfree?=512k \ + dalvik.vm.heapmaxfree?=8m diff --git a/build/tablet-7in-hdpi-1024-dalvik-heap.mk b/build/tablet-7in-hdpi-1024-dalvik-heap.mk index 7fd34b5f15..d0027dc24a 100644 --- a/build/tablet-7in-hdpi-1024-dalvik-heap.mk +++ b/build/tablet-7in-hdpi-1024-dalvik-heap.mk @@ -16,10 +16,10 @@ # Provides overrides to configure the Dalvik heap for a standard tablet device. -PRODUCT_PROPERTY_OVERRIDES += \ - dalvik.vm.heapstartsize=8m \ - dalvik.vm.heapgrowthlimit=80m \ - dalvik.vm.heapsize=384m \ - dalvik.vm.heaptargetutilization=0.75 \ - dalvik.vm.heapminfree=512k \ - dalvik.vm.heapmaxfree=8m +PRODUCT_VENDOR_PROPERTIES += \ + dalvik.vm.heapstartsize?=8m \ + dalvik.vm.heapgrowthlimit?=80m \ + dalvik.vm.heapsize?=384m \ + dalvik.vm.heaptargetutilization?=0.75 \ + dalvik.vm.heapminfree?=512k \ + dalvik.vm.heapmaxfree?=8m diff --git a/build/tablet-7in-xhdpi-2048-dalvik-heap.mk b/build/tablet-7in-xhdpi-2048-dalvik-heap.mk index e0f20c145b..7c06b4b5ab 100644 --- a/build/tablet-7in-xhdpi-2048-dalvik-heap.mk +++ b/build/tablet-7in-xhdpi-2048-dalvik-heap.mk @@ -16,10 +16,10 @@ # Provides overrides to configure the Dalvik heap for a 320dpi 7" tablet device. -PRODUCT_PROPERTY_OVERRIDES += \ - dalvik.vm.heapstartsize=16m \ - dalvik.vm.heapgrowthlimit=192m \ - dalvik.vm.heapsize=512m \ - dalvik.vm.heaptargetutilization=0.75 \ - dalvik.vm.heapminfree=512k \ - dalvik.vm.heapmaxfree=8m +PRODUCT_VENDOR_PROPERTIES += \ + dalvik.vm.heapstartsize?=16m \ + dalvik.vm.heapgrowthlimit?=192m \ + dalvik.vm.heapsize?=512m \ + dalvik.vm.heaptargetutilization?=0.75 \ + dalvik.vm.heapminfree?=512k \ + dalvik.vm.heapmaxfree?=8m diff --git a/build/tablet-dalvik-heap.mk b/build/tablet-dalvik-heap.mk index f577fb86f5..16886651f1 100644 --- a/build/tablet-dalvik-heap.mk +++ b/build/tablet-dalvik-heap.mk @@ -16,10 +16,10 @@ # Provides overrides to configure the Dalvik heap for a standard tablet device. -PRODUCT_PROPERTY_OVERRIDES += \ - dalvik.vm.heapstartsize=5m \ - dalvik.vm.heapgrowthlimit=48m \ - dalvik.vm.heapsize=256m \ - dalvik.vm.heaptargetutilization=0.75 \ - dalvik.vm.heapminfree=512k \ - dalvik.vm.heapmaxfree=2m +PRODUCT_VENDOR_PROPERTIES += \ + dalvik.vm.heapstartsize?=5m \ + dalvik.vm.heapgrowthlimit?=48m \ + dalvik.vm.heapsize?=256m \ + dalvik.vm.heaptargetutilization?=0.75 \ + dalvik.vm.heapminfree?=512k \ + dalvik.vm.heapmaxfree?=2m diff --git a/cmds/atrace/atrace.cpp b/cmds/atrace/atrace.cpp index 28fdaa4306..2519ffa45d 100644 --- a/cmds/atrace/atrace.cpp +++ b/cmds/atrace/atrace.cpp @@ -124,6 +124,7 @@ static const TracingCategory k_categories[] = { { "aidl", "AIDL calls", ATRACE_TAG_AIDL, { } }, { "nnapi", "NNAPI", ATRACE_TAG_NNAPI, { } }, { "rro", "Runtime Resource Overlay", ATRACE_TAG_RRO, { } }, + { "sysprop", "System Property", ATRACE_TAG_SYSPROP, { } }, { k_coreServiceCategory, "Core services", 0, { } }, { k_pdxServiceCategory, "PDX services", 0, { } }, { "sched", "CPU Scheduling", 0, { @@ -172,6 +173,8 @@ static const TracingCategory k_categories[] = { { OPT, "events/clk/clk_enable/enable" }, { OPT, "events/power/cpu_frequency_limits/enable" }, { OPT, "events/power/suspend_resume/enable" }, + { OPT, "events/cpuhp/cpuhp_enter/enable" }, + { OPT, "events/cpuhp/cpuhp_exit/enable" }, } }, { "membus", "Memory Bus Utilization", 0, { { REQ, "events/memory_bus/enable" }, @@ -239,6 +242,10 @@ static const TracingCategory k_categories[] = { { OPT, "events/kmem/ion_heap_shrink/enable" }, { OPT, "events/ion/ion_stat/enable" }, } }, + { "thermal", "Thermal event", 0, { + { REQ, "events/thermal/thermal_temperature/enable" }, + { OPT, "events/thermal/cdev_update/enable" }, + } }, }; struct TracingVendorCategory { diff --git a/cmds/atrace/atrace.rc b/cmds/atrace/atrace.rc index f442dae66f..994375b30e 100644 --- a/cmds/atrace/atrace.rc +++ b/cmds/atrace/atrace.rc @@ -47,6 +47,10 @@ on late-init chmod 0666 /sys/kernel/tracing/events/power/clock_set_rate/enable chmod 0666 /sys/kernel/debug/tracing/events/power/cpu_frequency_limits/enable chmod 0666 /sys/kernel/tracing/events/power/cpu_frequency_limits/enable + chmod 0666 /sys/kernel/debug/tracing/events/cpuhp/cpuhp_enter/enable + chmod 0666 /sys/kernel/tracing/events/cpuhp/cpuhp_enter/enable + chmod 0666 /sys/kernel/debug/tracing/events/cpuhp/cpuhp_exit/enable + chmod 0666 /sys/kernel/tracing/events/cpuhp/cpuhp_exit/enable chmod 0666 /sys/kernel/debug/tracing/events/power/gpu_frequency/enable chmod 0666 /sys/kernel/tracing/events/power/gpu_frequency/enable chmod 0666 /sys/kernel/debug/tracing/events/power/suspend_resume/enable @@ -166,6 +170,12 @@ on late-init chmod 0666 /sys/kernel/tracing/events/filemap/mm_filemap_delete_from_page_cache/enable chmod 0666 /sys/kernel/debug/tracing/events/filemap/mm_filemap_delete_from_page_cache/enable + # thermal + chmod 0666 /sys/kernel/debug/tracing/events/thermal/thermal_temperature/enable + chmod 0666 /sys/kernel/tracing/events/thermal/thermal_temperature/enable + chmod 0666 /sys/kernel/debug/tracing/events/thermal/cdev_update/enable + chmod 0666 /sys/kernel/tracing/events/thermal/cdev_update/enable + # Tracing disabled by default write /sys/kernel/debug/tracing/tracing_on 0 write /sys/kernel/tracing/tracing_on 0 diff --git a/cmds/bugreport/bugreport.cpp b/cmds/bugreport/bugreport.cpp index 840ae473bc..e5c52d8dff 100644 --- a/cmds/bugreport/bugreport.cpp +++ b/cmds/bugreport/bugreport.cpp @@ -27,12 +27,20 @@ // dumpstate, then connect to the dumpstate local client to read the // output. All of the dumpstate output is written to stdout, including // any errors encountered while reading/writing the output. -int main() { - +int main(int argc, char* /*argv*/[]) { fprintf(stderr, "=============================================================================\n"); - fprintf(stderr, "WARNING: flat bugreports are deprecated, use adb bugreport <zip_file> instead\n"); + fprintf(stderr, "WARNING: Flat (text file, non-zipped) bugreports are deprecated.\n"); + fprintf(stderr, "WARNING: Please generate zipped bugreports instead.\n"); + fprintf(stderr, "WARNING: On the host use: adb bugreport filename.zip\n"); + fprintf(stderr, "WARNING: On the device use: bugreportz\n"); + fprintf(stderr, "WARNING: bugreportz will output the filename to use with adb pull.\n"); fprintf(stderr, "=============================================================================\n\n\n"); + if (argc != 1) { + fprintf(stderr, "usage: bugreport\n"); + exit(1); + } + // Start the dumpstate service. property_set("ctl.start", "dumpstate"); diff --git a/cmds/bugreportz/main.cpp b/cmds/bugreportz/main.cpp index 40346bee1f..1d48e08f3e 100644 --- a/cmds/bugreportz/main.cpp +++ b/cmds/bugreportz/main.cpp @@ -30,7 +30,7 @@ static constexpr char VERSION[] = "1.1"; static void show_usage() { fprintf(stderr, - "usage: bugreportz [-h | -v]\n" + "usage: bugreportz [-hpv]\n" " -h: to display this help message\n" " -p: display progress\n" " -v: to display the version\n" @@ -64,6 +64,12 @@ int main(int argc, char* argv[]) { } } + // We don't support any non-option arguments. + if (optind != argc) { + show_usage(); + return EXIT_FAILURE; + } + // TODO: code below was copy-and-pasted from bugreport.cpp (except by the // timeout value); // should be reused instead. diff --git a/cmds/dumpstate/Android.bp b/cmds/dumpstate/Android.bp index acca11acf7..ead491e4dd 100644 --- a/cmds/dumpstate/Android.bp +++ b/cmds/dumpstate/Android.bp @@ -110,29 +110,20 @@ cc_binary { ], required: [ "atrace", - "df", - "getprop", "ip", "iptables", - "ip6tables", - "kill", "librank", "logcat", "lpdump", "lpdumpd", - "lsmod", - "lsof", - "netstat", - "printenv", "procrank", "screencap", "showmap", "ss", "storaged", - "top", - "uptime", + "toolbox", + "toybox", "vdc", - "vril-dump", ], init_rc: ["dumpstate.rc"], } diff --git a/cmds/dumpstate/dumpstate.cpp b/cmds/dumpstate/dumpstate.cpp index 581d3ded89..af41643d5d 100644 --- a/cmds/dumpstate/dumpstate.cpp +++ b/cmds/dumpstate/dumpstate.cpp @@ -78,6 +78,7 @@ #include <hardware_legacy/power.h> #include <hidl/ServiceManagement.h> #include <log/log.h> +#include <log/log_read.h> #include <openssl/sha.h> #include <private/android_filesystem_config.h> #include <private/android_logger.h> @@ -661,14 +662,24 @@ static int dump_stat_from_fd(const char *title __unused, const char *path, int f static const long MINIMUM_LOGCAT_TIMEOUT_MS = 50000; -/* timeout in ms to read a list of buffers */ +// Returns the actual readable size of the given buffer or -1 on error. +static long logcat_buffer_readable_size(const std::string& buffer) { + std::unique_ptr<logger_list, decltype(&android_logger_list_free)> logger_list{ + android_logger_list_alloc(0, 0, 0), &android_logger_list_free}; + auto logger = android_logger_open(logger_list.get(), android_name_to_log_id(buffer.c_str())); + + return android_logger_get_log_readable_size(logger); +} + +// Returns timeout in ms to read a list of buffers. static unsigned long logcat_timeout(const std::vector<std::string>& buffers) { unsigned long timeout_ms = 0; for (const auto& buffer : buffers) { - log_id_t id = android_name_to_log_id(buffer.c_str()); - unsigned long property_size = __android_logger_get_buffer_size(id); - /* Engineering margin is ten-fold our guess */ - timeout_ms += 10 * (property_size + worst_write_perf) / worst_write_perf; + long readable_size = logcat_buffer_readable_size(buffer); + if (readable_size > 0) { + // Engineering margin is ten-fold our guess. + timeout_ms += 10 * (readable_size + worst_write_perf) / worst_write_perf; + } } return timeout_ms > MINIMUM_LOGCAT_TIMEOUT_MS ? timeout_ms : MINIMUM_LOGCAT_TIMEOUT_MS; } @@ -1296,12 +1307,12 @@ static Dumpstate::RunStatus RunDumpsysNormal() { static void DumpHals() { if (!ds.IsZipping()) { - RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z", "--debug"}, + RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all", "--debug"}, CommandOptions::WithTimeout(10).AsRootIfAvailable().Build()); return; } DurationReporter duration_reporter("DUMP HALS"); - RunCommand("HARDWARE HALS", {"lshal", "-lVSietrpc", "--types=b,c,l,z"}, + RunCommand("HARDWARE HALS", {"lshal", "--all", "--types=all"}, CommandOptions::WithTimeout(10).AsRootIfAvailable().Build()); using android::hidl::manager::V1_0::IServiceManager; @@ -1481,8 +1492,7 @@ static Dumpstate::RunStatus dumpstate() { RunCommand("LSMOD", {"lsmod"}); } - if (__android_logger_property_get_bool( - "ro.logd.kernel", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE)) { + if (android::base::GetBoolProperty("ro.logd.kernel", false)) { DoKernelLogcat(); } else { do_dmesg(); diff --git a/cmds/dumpsys/dumpsys.cpp b/cmds/dumpsys/dumpsys.cpp index a427c8dd68..1327cfd155 100644 --- a/cmds/dumpsys/dumpsys.cpp +++ b/cmds/dumpsys/dumpsys.cpp @@ -230,7 +230,7 @@ int Dumpsys::main(int argc, char* const argv[]) { } const size_t N = services.size(); - if (N > 1) { + if (N > 1 || showListOnly) { // first print a list of the current services std::cout << "Currently running services:" << std::endl; diff --git a/cmds/dumpsys/tests/dumpsys_test.cpp b/cmds/dumpsys/tests/dumpsys_test.cpp index b9395ba4e2..3467898f3e 100644 --- a/cmds/dumpsys/tests/dumpsys_test.cpp +++ b/cmds/dumpsys/tests/dumpsys_test.cpp @@ -206,10 +206,7 @@ class DumpsysTest : public Test { } void AssertRunningServices(const std::vector<std::string>& services) { - std::string expected; - if (services.size() > 1) { - expected.append("Currently running services:\n"); - } + std::string expected = "Currently running services:\n"; for (const std::string& service : services) { expected.append(" ").append(service).append("\n"); } @@ -263,6 +260,21 @@ TEST_F(DumpsysTest, ListAllServices) { AssertRunningServices({"Locksmith", "Valet"}); } +TEST_F(DumpsysTest, ListServicesOneRegistered) { + ExpectListServices({"Locksmith"}); + ExpectCheckService("Locksmith"); + + CallMain({"-l"}); + + AssertRunningServices({"Locksmith"}); +} + +TEST_F(DumpsysTest, ListServicesEmpty) { + CallMain({"-l"}); + + AssertRunningServices({}); +} + // Tests 'dumpsys -l' when a service is not running TEST_F(DumpsysTest, ListRunningServices) { ExpectListServices({"Locksmith", "Valet"}); diff --git a/cmds/installd/Android.bp b/cmds/installd/Android.bp index 8ff4dd862d..523115f476 100644 --- a/cmds/installd/Android.bp +++ b/cmds/installd/Android.bp @@ -17,15 +17,15 @@ cc_defaults { "InstalldNativeService.cpp", "QuotaUtils.cpp", "dexopt.cpp", + "execv_helper.cpp", "globals.cpp", + "run_dex2oat.cpp", + "unique_file.cpp", "utils.cpp", "utils_default.cpp", "view_compiler.cpp", ":installd_aidl", ], - header_libs: [ - "dex2oat_headers", - ], shared_libs: [ "libbase", "libbinder", @@ -103,6 +103,28 @@ cc_library_headers { } // +// Unit tests +// + +cc_test_host { + name: "run_dex2oat_test", + test_suites: ["general-tests"], + clang: true, + srcs: [ + "run_dex2oat_test.cpp", + "run_dex2oat.cpp", + "unique_file.cpp", + "execv_helper.cpp", + ], + cflags: ["-Wall", "-Werror"], + shared_libs: [ + "libbase", + "server_configurable_flags", + ], + test_config: "run_dex2oat_test.xml", +} + +// // Executable // @@ -205,18 +227,18 @@ cc_binary { srcs: [ "dexopt.cpp", + "execv_helper.cpp", "globals.cpp", "otapreopt.cpp", "otapreopt_utils.cpp", + "run_dex2oat.cpp", + "unique_file.cpp", "utils.cpp", "utils_default.cpp", "view_compiler.cpp", ], - header_libs: ["dex2oat_headers"], - static_libs: [ - "libartimagevalues", "libdiskusage", "libotapreoptparameters", ], diff --git a/cmds/installd/CrateManager.cpp b/cmds/installd/CrateManager.cpp index 6e079ebbf3..b17cba15d5 100644 --- a/cmds/installd/CrateManager.cpp +++ b/cmds/installd/CrateManager.cpp @@ -86,7 +86,7 @@ void CrateManager::traverseChildDir(const std::string& targetDir, } void CrateManager::traverseAllPackagesForUser( - const std::unique_ptr<std::string>& uuid, userid_t userId, + const std::optional<std::string>& uuid, userid_t userId, std::function<void(FTSENT*)>& onHandlingPackage) { const char* uuid_ = uuid ? uuid->c_str() : nullptr; @@ -96,21 +96,21 @@ void CrateManager::traverseAllPackagesForUser( void CrateManager::createCrate( CratedFolder cratedFolder, - std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate) { + std::function<void(CratedFolder, CrateMetadata&&)>& onCreateCrate) { const char* path = cratedFolder->fts_path; if (path == nullptr || *path == '\0') { return; } - std::unique_ptr<CrateMetadata> crateMetadata = std::make_unique<CrateMetadata>(); - crateMetadata->uid = cratedFolder->fts_statp->st_uid; - crateMetadata->packageName = mPackageName; - crateMetadata->id = getValidatedCratedPath(path); + CrateMetadata crateMetadata; + crateMetadata.uid = cratedFolder->fts_statp->st_uid; + crateMetadata.packageName = mPackageName; + crateMetadata.id = getValidatedCratedPath(path); - onCreateCrate(cratedFolder, crateMetadata); + onCreateCrate(cratedFolder, std::move(crateMetadata)); } -void CrateManager::traverseAllCrates(std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate) { +void CrateManager::traverseAllCrates(std::function<void(CratedFolder, CrateMetadata&&)>& onCreateCrate) { std::function<void(FTSENT*)> onVisitCrateDir = [&](FTSENT* cratedFolder) -> void { createCrate(cratedFolder, onCreateCrate); }; @@ -118,11 +118,11 @@ void CrateManager::traverseAllCrates(std::function<void(CratedFolder, std::uniqu } #if CRATE_DEBUG -void CrateManager::dump(std::unique_ptr<CrateMetadata>& CrateMetadata) { +void CrateManager::dump(const CrateMetadata& CrateMetadata) { LOG(DEBUG) << "CrateMetadata = {" - << "uid : \"" << CrateMetadata->uid - << "\", packageName : \"" << CrateMetadata->packageName - << "\", id : \"" << CrateMetadata->id + << "uid : \"" << CrateMetadata.uid + << "\", packageName : \"" << CrateMetadata.packageName + << "\", id : \"" << CrateMetadata.id << "\"}"; } #endif diff --git a/cmds/installd/CrateManager.h b/cmds/installd/CrateManager.h index 4332d4cbc9..1f30b5dc79 100644 --- a/cmds/installd/CrateManager.h +++ b/cmds/installd/CrateManager.h @@ -25,7 +25,7 @@ #include <sys/stat.h> #include <sys/types.h> -#include <memory> +#include <optional> #include <string> #include <vector> @@ -55,18 +55,18 @@ public: CrateManager(const char* uuid, userid_t userId, const std::string& packageName); ~CrateManager(); - void traverseAllCrates(std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate); + void traverseAllCrates(std::function<void(CratedFolder, CrateMetadata&&)>& onCreateCrate); static void traverseChildDir(const std::string& targetDir, std::function<void(FTSENT*)>& onVisitChildDir); static void traverseAllPackagesForUser( - const std::unique_ptr<std::string>& uuid, + const std::optional<std::string>& uuid, userid_t userId, std::function<void(FTSENT*)>& onHandlingPackage); #if CRATE_DEBUG - static void dump(std::unique_ptr<CrateMetadata>& CrateMetadata); + static void dump(const CrateMetadata& CrateMetadata); #endif private: std::string mRoot; @@ -75,7 +75,7 @@ private: void createCrate( CratedFolder cratedFolder, - std::function<void(CratedFolder, std::unique_ptr<CrateMetadata>&)>& onCreateCrate); + std::function<void(CratedFolder, CrateMetadata&&)>& onCreateCrate); }; } // namespace installd diff --git a/cmds/installd/InstalldNativeService.cpp b/cmds/installd/InstalldNativeService.cpp index b9c1addf89..0782b430a3 100644 --- a/cmds/installd/InstalldNativeService.cpp +++ b/cmds/installd/InstalldNativeService.cpp @@ -166,7 +166,7 @@ binder::Status checkUid(uid_t expectedUid) { } } -binder::Status checkArgumentUuid(const std::unique_ptr<std::string>& uuid) { +binder::Status checkArgumentUuid(const std::optional<std::string>& uuid) { if (!uuid || is_valid_filename(*uuid)) { return ok(); } else { @@ -175,7 +175,7 @@ binder::Status checkArgumentUuid(const std::unique_ptr<std::string>& uuid) { } } -binder::Status checkArgumentUuidTestOrNull(const std::unique_ptr<std::string>& uuid) { +binder::Status checkArgumentUuidTestOrNull(const std::optional<std::string>& uuid) { if (!uuid || strcmp(uuid->c_str(), kTestUuid) == 0) { return ok(); } else { @@ -214,7 +214,7 @@ binder::Status checkArgumentPath(const std::string& path) { return ok(); } -binder::Status checkArgumentPath(const std::unique_ptr<std::string>& path) { +binder::Status checkArgumentPath(const std::optional<std::string>& path) { if (path) { return checkArgumentPath(*path); } else { @@ -421,8 +421,8 @@ static bool prepare_app_profile_dir(const std::string& packageName, int32_t appI } binder::Status InstalldNativeService::createAppDataBatched( - const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& uuids, - const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& packageNames, + const std::optional<std::vector<std::optional<std::string>>>& uuids, + const std::optional<std::vector<std::optional<std::string>>>& packageNames, int32_t userId, int32_t flags, const std::vector<int32_t>& appIds, const std::vector<std::string>& seInfos, const std::vector<int32_t>& targetSdkVersions, int64_t* _aidl_return) { @@ -432,10 +432,11 @@ binder::Status InstalldNativeService::createAppDataBatched( ATRACE_BEGIN("createAppDataBatched"); binder::Status ret; for (size_t i = 0; i < uuids->size(); i++) { - if (!packageNames->at(i)) { + std::optional<std::string> packageName = packageNames->at(i); + if (!packageName) { continue; } - ret = createAppData(uuids->at(i), *packageNames->at(i), userId, flags, appIds[i], + ret = createAppData(uuids->at(i), *packageName, userId, flags, appIds[i], seInfos[i], targetSdkVersions[i], _aidl_return); if (!ret.isOk()) { ATRACE_END(); @@ -446,7 +447,7 @@ binder::Status InstalldNativeService::createAppDataBatched( return ok(); } -binder::Status InstalldNativeService::createAppData(const std::unique_ptr<std::string>& uuid, +binder::Status InstalldNativeService::createAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return) { ENFORCE_UID(AID_SYSTEM); @@ -527,7 +528,7 @@ binder::Status InstalldNativeService::createAppData(const std::unique_ptr<std::s return ok(); } -binder::Status InstalldNativeService::migrateAppData(const std::unique_ptr<std::string>& uuid, +binder::Status InstalldNativeService::migrateAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); @@ -588,7 +589,7 @@ binder::Status InstalldNativeService::clearAppProfiles(const std::string& packag return res; } -binder::Status InstalldNativeService::clearAppData(const std::unique_ptr<std::string>& uuid, +binder::Status InstalldNativeService::clearAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); @@ -708,7 +709,7 @@ binder::Status InstalldNativeService::destroyAppProfiles(const std::string& pack return res; } -binder::Status InstalldNativeService::destroyAppData(const std::unique_ptr<std::string>& uuid, +binder::Status InstalldNativeService::destroyAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); @@ -780,7 +781,7 @@ static gid_t get_cache_gid(uid_t uid) { return (gid != -1) ? gid : uid; } -binder::Status InstalldNativeService::fixupAppData(const std::unique_ptr<std::string>& uuid, +binder::Status InstalldNativeService::fixupAppData(const std::optional<std::string>& uuid, int32_t flags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); @@ -900,7 +901,7 @@ static int32_t copy_directory_recursive(const char* from, const char* to) { } binder::Status InstalldNativeService::snapshotAppData( - const std::unique_ptr<std::string>& volumeUuid, + const std::optional<std::string>& volumeUuid, const std::string& packageName, int32_t user, int32_t snapshotId, int32_t storageFlags, int64_t* _aidl_return) { ENFORCE_UID(AID_SYSTEM); @@ -1027,7 +1028,7 @@ binder::Status InstalldNativeService::snapshotAppData( } binder::Status InstalldNativeService::restoreAppDataSnapshot( - const std::unique_ptr<std::string>& volumeUuid, const std::string& packageName, + const std::optional<std::string>& volumeUuid, const std::string& packageName, const int32_t appId, const std::string& seInfo, const int32_t user, const int32_t snapshotId, int32_t storageFlags) { ENFORCE_UID(AID_SYSTEM); @@ -1099,7 +1100,7 @@ binder::Status InstalldNativeService::restoreAppDataSnapshot( } binder::Status InstalldNativeService::destroyAppDataSnapshot( - const std::unique_ptr<std::string> &volumeUuid, const std::string& packageName, + const std::optional<std::string> &volumeUuid, const std::string& packageName, const int32_t user, const int64_t ceSnapshotInode, const int32_t snapshotId, int32_t storageFlags) { ENFORCE_UID(AID_SYSTEM); @@ -1132,7 +1133,7 @@ binder::Status InstalldNativeService::destroyAppDataSnapshot( } binder::Status InstalldNativeService::destroyCeSnapshotsNotSpecified( - const std::unique_ptr<std::string> &volumeUuid, const int32_t userId, + const std::optional<std::string> &volumeUuid, const int32_t user, const std::vector<int32_t>& retainSnapshotIds) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID_IS_TEST_OR_NULL(volumeUuid); @@ -1140,7 +1141,7 @@ binder::Status InstalldNativeService::destroyCeSnapshotsNotSpecified( const char* volume_uuid = volumeUuid ? volumeUuid->c_str() : nullptr; - auto base_path = create_data_misc_ce_rollback_base_path(volume_uuid, userId); + auto base_path = create_data_misc_ce_rollback_base_path(volume_uuid, user); std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(base_path.c_str()), closedir); if (!dir) { @@ -1159,7 +1160,7 @@ binder::Status InstalldNativeService::destroyCeSnapshotsNotSpecified( std::find(retainSnapshotIds.begin(), retainSnapshotIds.end(), snapshot_id) == retainSnapshotIds.end()) { auto rollback_path = create_data_misc_ce_rollback_path( - volume_uuid, userId, snapshot_id); + volume_uuid, user, snapshot_id); int res = delete_dir_contents_and_dir(rollback_path, true /* ignore_if_missing */); if (res != 0) { return error(res, "Failed clearing snapshot " + rollback_path); @@ -1169,8 +1170,8 @@ binder::Status InstalldNativeService::destroyCeSnapshotsNotSpecified( return ok(); } -binder::Status InstalldNativeService::moveCompleteApp(const std::unique_ptr<std::string>& fromUuid, - const std::unique_ptr<std::string>& toUuid, const std::string& packageName, +binder::Status InstalldNativeService::moveCompleteApp(const std::optional<std::string>& fromUuid, + const std::optional<std::string>& toUuid, const std::string& packageName, int32_t appId, const std::string& seInfo, int32_t targetSdkVersion, const std::string& fromCodePath) { ENFORCE_UID(AID_SYSTEM); @@ -1277,7 +1278,7 @@ fail: return res; } -binder::Status InstalldNativeService::createUserData(const std::unique_ptr<std::string>& uuid, +binder::Status InstalldNativeService::createUserData(const std::optional<std::string>& uuid, int32_t userId, int32_t userSerial ATTRIBUTE_UNUSED, int32_t flags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); @@ -1295,7 +1296,7 @@ binder::Status InstalldNativeService::createUserData(const std::unique_ptr<std:: return ok(); } -binder::Status InstalldNativeService::destroyUserData(const std::unique_ptr<std::string>& uuid, +binder::Status InstalldNativeService::destroyUserData(const std::optional<std::string>& uuid, int32_t userId, int32_t flags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); @@ -1332,13 +1333,13 @@ binder::Status InstalldNativeService::destroyUserData(const std::unique_ptr<std: return res; } -binder::Status InstalldNativeService::freeCache(const std::unique_ptr<std::string>& uuid, +binder::Status InstalldNativeService::freeCache(const std::optional<std::string>& uuid, int64_t targetFreeBytes, int64_t cacheReservedBytes, int32_t flags) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); std::lock_guard<std::recursive_mutex> lock(mLock); - auto uuidString = uuid ? *uuid : ""; + auto uuidString = uuid.value_or(""); const char* uuid_ = uuid ? uuid->c_str() : nullptr; auto data_path = create_data_path(uuid_); auto noop = (flags & FLAG_FREE_CACHE_NOOP); @@ -1736,7 +1737,7 @@ static void collectManualExternalStatsForUser(const std::string& path, struct st fts_close(fts); } -binder::Status InstalldNativeService::getAppSize(const std::unique_ptr<std::string>& uuid, +binder::Status InstalldNativeService::getAppSize(const std::optional<std::string>& uuid, const std::vector<std::string>& packageNames, int32_t userId, int32_t flags, int32_t appId, const std::vector<int64_t>& ceDataInodes, const std::vector<std::string>& codePaths, std::vector<int64_t>* _aidl_return) { @@ -1776,7 +1777,7 @@ binder::Status InstalldNativeService::getAppSize(const std::unique_ptr<std::stri memset(&stats, 0, sizeof(stats)); memset(&extStats, 0, sizeof(extStats)); - auto uuidString = uuid ? *uuid : ""; + auto uuidString = uuid.value_or(""); const char* uuid_ = uuid ? uuid->c_str() : nullptr; if (!IsQuotaSupported(uuidString)) { @@ -1963,7 +1964,7 @@ static external_sizes getExternalSizesForUserWithQuota(const std::string& uuid, return sizes; } -binder::Status InstalldNativeService::getUserSize(const std::unique_ptr<std::string>& uuid, +binder::Status InstalldNativeService::getUserSize(const std::optional<std::string>& uuid, int32_t userId, int32_t flags, const std::vector<int32_t>& appIds, std::vector<int64_t>* _aidl_return) { ENFORCE_UID(AID_SYSTEM); @@ -1983,7 +1984,7 @@ binder::Status InstalldNativeService::getUserSize(const std::unique_ptr<std::str memset(&stats, 0, sizeof(stats)); memset(&extStats, 0, sizeof(extStats)); - auto uuidString = uuid ? *uuid : ""; + auto uuidString = uuid.value_or(""); const char* uuid_ = uuid ? uuid->c_str() : nullptr; if (!IsQuotaSupported(uuidString)) { @@ -2095,7 +2096,7 @@ binder::Status InstalldNativeService::getUserSize(const std::unique_ptr<std::str return ok(); } -binder::Status InstalldNativeService::getExternalSize(const std::unique_ptr<std::string>& uuid, +binder::Status InstalldNativeService::getExternalSize(const std::optional<std::string>& uuid, int32_t userId, int32_t flags, const std::vector<int32_t>& appIds, std::vector<int64_t>* _aidl_return) { ENFORCE_UID(AID_SYSTEM); @@ -2110,7 +2111,7 @@ binder::Status InstalldNativeService::getExternalSize(const std::unique_ptr<std: LOG(INFO) << "Measuring external " << userId; #endif - auto uuidString = uuid ? *uuid : ""; + auto uuidString = uuid.value_or(""); const char* uuid_ = uuid ? uuid->c_str() : nullptr; int64_t totalSize = 0; @@ -2212,9 +2213,9 @@ binder::Status InstalldNativeService::getExternalSize(const std::unique_ptr<std: } binder::Status InstalldNativeService::getAppCrates( - const std::unique_ptr<std::string>& uuid, + const std::optional<std::string>& uuid, const std::vector<std::string>& packageNames, int32_t userId, - std::unique_ptr<std::vector<std::unique_ptr<CrateMetadata>>>* _aidl_return) { + std::optional<std::vector<std::optional<CrateMetadata>>>* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); for (const auto& packageName : packageNames) { @@ -2223,15 +2224,15 @@ binder::Status InstalldNativeService::getAppCrates( #ifdef ENABLE_STORAGE_CRATES std::lock_guard<std::recursive_mutex> lock(mLock); - auto retVector = std::make_unique<std::vector<std::unique_ptr<CrateMetadata>>>(); + auto retVector = std::vector<std::optional<CrateMetadata>>(); const char* uuid_ = uuid ? uuid->c_str() : nullptr; - std::function<void(CratedFolder, std::unique_ptr<CrateMetadata> &)> onCreateCrate = - [&](CratedFolder cratedFolder, std::unique_ptr<CrateMetadata> &crateMetadata) -> void { + std::function<void(CratedFolder, CrateMetadata&&)> onCreateCrate = + [&](CratedFolder cratedFolder, CrateMetadata&& crateMetadata) -> void { if (cratedFolder == nullptr) { return; } - retVector->push_back(std::move(crateMetadata)); + retVector.push_back(std::move(crateMetadata)); }; for (const auto& packageName : packageNames) { @@ -2243,15 +2244,15 @@ binder::Status InstalldNativeService::getAppCrates( } #if CRATE_DEBUG - LOG(WARNING) << "retVector->size() =" << retVector->size(); - for (auto iter = retVector->begin(); iter != retVector->end(); ++iter) { - CrateManager::dump(*iter); + LOG(WARNING) << "retVector.size() =" << retVector.size(); + for (auto& item : retVector) { + CrateManager::dump(item); } #endif *_aidl_return = std::move(retVector); #else // ENABLE_STORAGE_CRATES - *_aidl_return = nullptr; + _aidl_return->reset(); /* prevent compile warning fail */ if (userId < 0) { @@ -2262,18 +2263,18 @@ binder::Status InstalldNativeService::getAppCrates( } binder::Status InstalldNativeService::getUserCrates( - const std::unique_ptr<std::string>& uuid, int32_t userId, - std::unique_ptr<std::vector<std::unique_ptr<CrateMetadata>>>* _aidl_return) { + const std::optional<std::string>& uuid, int32_t userId, + std::optional<std::vector<std::optional<CrateMetadata>>>* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); #ifdef ENABLE_STORAGE_CRATES std::lock_guard<std::recursive_mutex> lock(mLock); const char* uuid_ = uuid ? uuid->c_str() : nullptr; - auto retVector = std::make_unique<std::vector<std::unique_ptr<CrateMetadata>>>(); + auto retVector = std::vector<std::optional<CrateMetadata>>(); - std::function<void(CratedFolder, std::unique_ptr<CrateMetadata> &)> onCreateCrate = - [&](CratedFolder cratedFolder, std::unique_ptr<CrateMetadata> &crateMetadata) -> void { + std::function<void(CratedFolder, CrateMetadata&&)> onCreateCrate = + [&](CratedFolder cratedFolder, CrateMetadata&& crateMetadata) -> void { if (cratedFolder == nullptr) { return; } @@ -2287,15 +2288,15 @@ binder::Status InstalldNativeService::getUserCrates( CrateManager::traverseAllPackagesForUser(uuid, userId, onHandingPackage); #if CRATE_DEBUG - LOG(DEBUG) << "retVector->size() =" << retVector->size(); - for (auto iter = retVector->begin(); iter != retVector->end(); ++iter) { - CrateManager::dump(*iter); + LOG(DEBUG) << "retVector.size() =" << retVector.size(); + for (auto& item : retVector) { + CrateManager::dump(item); } #endif *_aidl_return = std::move(retVector); #else // ENABLE_STORAGE_CRATES - *_aidl_return = nullptr; + _aidl_return->reset(); /* prevent compile warning fail */ if (userId < 0) { @@ -2305,7 +2306,7 @@ binder::Status InstalldNativeService::getUserCrates( return ok(); } -binder::Status InstalldNativeService::setAppQuota(const std::unique_ptr<std::string>& uuid, +binder::Status InstalldNativeService::setAppQuota(const std::optional<std::string>& uuid, int32_t userId, int32_t appId, int64_t cacheQuota) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); @@ -2376,19 +2377,19 @@ binder::Status InstalldNativeService::destroyProfileSnapshot(const std::string& return ok(); } -static const char* getCStr(const std::unique_ptr<std::string>& data, +static const char* getCStr(const std::optional<std::string>& data, const char* default_value = nullptr) { - return data == nullptr ? default_value : data->c_str(); + return data ? data->c_str() : default_value; } binder::Status InstalldNativeService::dexopt(const std::string& apkPath, int32_t uid, - const std::unique_ptr<std::string>& packageName, const std::string& instructionSet, - int32_t dexoptNeeded, const std::unique_ptr<std::string>& outputPath, int32_t dexFlags, - const std::string& compilerFilter, const std::unique_ptr<std::string>& uuid, - const std::unique_ptr<std::string>& classLoaderContext, - const std::unique_ptr<std::string>& seInfo, bool downgrade, int32_t targetSdkVersion, - const std::unique_ptr<std::string>& profileName, - const std::unique_ptr<std::string>& dexMetadataPath, - const std::unique_ptr<std::string>& compilationReason) { + const std::optional<std::string>& packageName, const std::string& instructionSet, + int32_t dexoptNeeded, const std::optional<std::string>& outputPath, int32_t dexFlags, + const std::string& compilerFilter, const std::optional<std::string>& uuid, + const std::optional<std::string>& classLoaderContext, + const std::optional<std::string>& seInfo, bool downgrade, int32_t targetSdkVersion, + const std::optional<std::string>& profileName, + const std::optional<std::string>& dexMetadataPath, + const std::optional<std::string>& compilationReason) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); CHECK_ARGUMENT_PATH(apkPath); @@ -2434,7 +2435,7 @@ binder::Status InstalldNativeService::compileLayouts(const std::string& apkPath, } binder::Status InstalldNativeService::linkNativeLibraryDirectory( - const std::unique_ptr<std::string>& uuid, const std::string& packageName, + const std::optional<std::string>& uuid, const std::string& packageName, const std::string& nativeLibPath32, int32_t userId) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); @@ -2525,7 +2526,7 @@ out: return res; } -binder::Status InstalldNativeService::restoreconAppData(const std::unique_ptr<std::string>& uuid, +binder::Status InstalldNativeService::restoreconAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, const std::string& seInfo) { ENFORCE_UID(AID_SYSTEM); @@ -2643,7 +2644,7 @@ binder::Status InstalldNativeService::moveAb(const std::string& apkPath, } binder::Status InstalldNativeService::deleteOdex(const std::string& apkPath, - const std::string& instructionSet, const std::unique_ptr<std::string>& outputPath) { + const std::string& instructionSet, const std::optional<std::string>& outputPath) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PATH(apkPath); CHECK_ARGUMENT_PATH(outputPath); @@ -2795,7 +2796,7 @@ binder::Status InstalldNativeService::assertFsverityRootHashMatches(const std::s binder::Status InstalldNativeService::reconcileSecondaryDexFile( const std::string& dexPath, const std::string& packageName, int32_t uid, - const std::vector<std::string>& isas, const std::unique_ptr<std::string>& volumeUuid, + const std::vector<std::string>& isas, const std::optional<std::string>& volumeUuid, int32_t storage_flag, bool* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(volumeUuid); @@ -2810,7 +2811,7 @@ binder::Status InstalldNativeService::reconcileSecondaryDexFile( binder::Status InstalldNativeService::hashSecondaryDexFile( const std::string& dexPath, const std::string& packageName, int32_t uid, - const std::unique_ptr<std::string>& volumeUuid, int32_t storageFlag, + const std::optional<std::string>& volumeUuid, int32_t storageFlag, std::vector<uint8_t>* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(volumeUuid); @@ -2869,7 +2870,7 @@ binder::Status InstalldNativeService::invalidateMounts() { // Mount volume's CE and DE storage to mirror binder::Status InstalldNativeService::tryMountDataMirror( - const std::unique_ptr<std::string>& uuid) { + const std::optional<std::string>& uuid) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); if (!sAppDataIsolationEnabled) { @@ -2933,7 +2934,7 @@ binder::Status InstalldNativeService::tryMountDataMirror( // Unmount volume's CE and DE storage from mirror binder::Status InstalldNativeService::onPrivateVolumeRemoved( - const std::unique_ptr<std::string>& uuid) { + const std::optional<std::string>& uuid) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_UUID(uuid); if (!sAppDataIsolationEnabled) { @@ -2977,7 +2978,7 @@ binder::Status InstalldNativeService::onPrivateVolumeRemoved( } std::string InstalldNativeService::findDataMediaPath( - const std::unique_ptr<std::string>& uuid, userid_t userid) { + const std::optional<std::string>& uuid, userid_t userid) { 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()); @@ -2990,15 +2991,14 @@ std::string InstalldNativeService::findDataMediaPath( } binder::Status InstalldNativeService::isQuotaSupported( - const std::unique_ptr<std::string>& uuid, bool* _aidl_return) { - auto uuidString = uuid ? *uuid : ""; - *_aidl_return = IsQuotaSupported(uuidString); + const std::optional<std::string>& uuid, bool* _aidl_return) { + *_aidl_return = IsQuotaSupported(uuid.value_or("")); return ok(); } binder::Status InstalldNativeService::prepareAppProfile(const std::string& packageName, int32_t userId, int32_t appId, const std::string& profileName, const std::string& codePath, - const std::unique_ptr<std::string>& dexMetadata, bool* _aidl_return) { + const std::optional<std::string>& dexMetadata, bool* _aidl_return) { ENFORCE_UID(AID_SYSTEM); CHECK_ARGUMENT_PACKAGE_NAME(packageName); CHECK_ARGUMENT_PATH(codePath); diff --git a/cmds/installd/InstalldNativeService.h b/cmds/installd/InstalldNativeService.h index 8e7d98b9a1..9819327840 100644 --- a/cmds/installd/InstalldNativeService.h +++ b/cmds/installd/InstalldNativeService.h @@ -40,81 +40,81 @@ public: static char const* getServiceName() { return "installd"; } virtual status_t dump(int fd, const Vector<String16> &args) override; - binder::Status createUserData(const std::unique_ptr<std::string>& uuid, int32_t userId, + binder::Status createUserData(const std::optional<std::string>& uuid, int32_t userId, int32_t userSerial, int32_t flags); - binder::Status destroyUserData(const std::unique_ptr<std::string>& uuid, int32_t userId, + binder::Status destroyUserData(const std::optional<std::string>& uuid, int32_t userId, int32_t flags); binder::Status createAppDataBatched( - const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& uuids, - const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& packageNames, + const std::optional<std::vector<std::optional<std::string>>>& uuids, + const std::optional<std::vector<std::optional<std::string>>>& packageNames, int32_t userId, int32_t flags, const std::vector<int32_t>& appIds, const std::vector<std::string>& seInfos, const std::vector<int32_t>& targetSdkVersions, int64_t* _aidl_return); - binder::Status createAppData(const std::unique_ptr<std::string>& uuid, + binder::Status createAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, const std::string& seInfo, int32_t targetSdkVersion, int64_t* _aidl_return); - binder::Status restoreconAppData(const std::unique_ptr<std::string>& uuid, + binder::Status restoreconAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags, int32_t appId, const std::string& seInfo); - binder::Status migrateAppData(const std::unique_ptr<std::string>& uuid, + binder::Status migrateAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags); - binder::Status clearAppData(const std::unique_ptr<std::string>& uuid, + binder::Status clearAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode); - binder::Status destroyAppData(const std::unique_ptr<std::string>& uuid, + binder::Status destroyAppData(const std::optional<std::string>& uuid, const std::string& packageName, int32_t userId, int32_t flags, int64_t ceDataInode); - binder::Status fixupAppData(const std::unique_ptr<std::string>& uuid, int32_t flags); + binder::Status fixupAppData(const std::optional<std::string>& uuid, int32_t flags); - binder::Status snapshotAppData(const std::unique_ptr<std::string>& volumeUuid, + binder::Status snapshotAppData(const std::optional<std::string>& volumeUuid, const std::string& packageName, const int32_t user, const int32_t snapshotId, int32_t storageFlags, int64_t* _aidl_return); - binder::Status restoreAppDataSnapshot(const std::unique_ptr<std::string>& volumeUuid, + binder::Status restoreAppDataSnapshot(const std::optional<std::string>& volumeUuid, const std::string& packageName, const int32_t appId, const std::string& seInfo, const int32_t user, const int32_t snapshotId, int32_t storageFlags); - binder::Status destroyAppDataSnapshot(const std::unique_ptr<std::string> &volumeUuid, + binder::Status destroyAppDataSnapshot(const std::optional<std::string> &volumeUuid, const std::string& packageName, const int32_t user, const int64_t ceSnapshotInode, const int32_t snapshotId, int32_t storageFlags); - binder::Status destroyCeSnapshotsNotSpecified(const std::unique_ptr<std::string> &volumeUuid, - const int32_t userId, const std::vector<int32_t>& retainSnapshotIds); + binder::Status destroyCeSnapshotsNotSpecified(const std::optional<std::string> &volumeUuid, + const int32_t user, const std::vector<int32_t>& retainSnapshotIds); - binder::Status getAppSize(const std::unique_ptr<std::string>& uuid, + binder::Status getAppSize(const std::optional<std::string>& uuid, const std::vector<std::string>& packageNames, int32_t userId, int32_t flags, int32_t appId, const std::vector<int64_t>& ceDataInodes, const std::vector<std::string>& codePaths, std::vector<int64_t>* _aidl_return); - binder::Status getUserSize(const std::unique_ptr<std::string>& uuid, + binder::Status getUserSize(const std::optional<std::string>& uuid, int32_t userId, int32_t flags, const std::vector<int32_t>& appIds, std::vector<int64_t>* _aidl_return); - binder::Status getExternalSize(const std::unique_ptr<std::string>& uuid, + binder::Status getExternalSize(const std::optional<std::string>& uuid, int32_t userId, int32_t flags, const std::vector<int32_t>& appIds, std::vector<int64_t>* _aidl_return); - binder::Status getAppCrates(const std::unique_ptr<std::string>& uuid, + binder::Status getAppCrates(const std::optional<std::string>& uuid, const std::vector<std::string>& packageNames, int32_t userId, - std::unique_ptr<std::vector<std::unique_ptr<android::os::storage::CrateMetadata>>>* + std::optional<std::vector<std::optional<android::os::storage::CrateMetadata>>>* _aidl_return); binder::Status getUserCrates( - const std::unique_ptr<std::string>& uuid, int32_t userId, - std::unique_ptr<std::vector<std::unique_ptr<android::os::storage::CrateMetadata>>>* + const std::optional<std::string>& uuid, int32_t userId, + std::optional<std::vector<std::optional<android::os::storage::CrateMetadata>>>* _aidl_return); - binder::Status setAppQuota(const std::unique_ptr<std::string>& uuid, + binder::Status setAppQuota(const std::optional<std::string>& uuid, int32_t userId, int32_t appId, int64_t cacheQuota); - binder::Status moveCompleteApp(const std::unique_ptr<std::string>& fromUuid, - const std::unique_ptr<std::string>& toUuid, const std::string& packageName, + binder::Status moveCompleteApp(const std::optional<std::string>& fromUuid, + const std::optional<std::string>& toUuid, const std::string& packageName, int32_t appId, const std::string& seInfo, int32_t targetSdkVersion, const std::string& fromCodePath); binder::Status dexopt(const std::string& apkPath, int32_t uid, - const std::unique_ptr<std::string>& packageName, const std::string& instructionSet, - int32_t dexoptNeeded, const std::unique_ptr<std::string>& outputPath, int32_t dexFlags, - const std::string& compilerFilter, const std::unique_ptr<std::string>& uuid, - const std::unique_ptr<std::string>& classLoaderContext, - const std::unique_ptr<std::string>& seInfo, bool downgrade, - int32_t targetSdkVersion, const std::unique_ptr<std::string>& profileName, - const std::unique_ptr<std::string>& dexMetadataPath, - const std::unique_ptr<std::string>& compilationReason); + const std::optional<std::string>& packageName, const std::string& instructionSet, + int32_t dexoptNeeded, const std::optional<std::string>& outputPath, int32_t dexFlags, + const std::string& compilerFilter, const std::optional<std::string>& uuid, + const std::optional<std::string>& classLoaderContext, + const std::optional<std::string>& seInfo, bool downgrade, + int32_t targetSdkVersion, const std::optional<std::string>& profileName, + const std::optional<std::string>& dexMetadataPath, + const std::optional<std::string>& compilationReason); binder::Status compileLayouts(const std::string& apkPath, const std::string& packageName, const std::string& outDexFile, int uid, bool* _aidl_return); @@ -137,9 +137,9 @@ public: const std::string& profileName); binder::Status rmPackageDir(const std::string& packageDir); - binder::Status freeCache(const std::unique_ptr<std::string>& uuid, int64_t targetFreeBytes, + binder::Status freeCache(const std::optional<std::string>& uuid, int64_t targetFreeBytes, int64_t cacheReservedBytes, int32_t flags); - binder::Status linkNativeLibraryDirectory(const std::unique_ptr<std::string>& uuid, + binder::Status linkNativeLibraryDirectory(const std::optional<std::string>& uuid, const std::string& packageName, const std::string& nativeLibPath32, int32_t userId); binder::Status createOatDir(const std::string& oatDir, const std::string& instructionSet); binder::Status linkFile(const std::string& relativePath, const std::string& fromBase, @@ -147,27 +147,27 @@ public: binder::Status moveAb(const std::string& apkPath, const std::string& instructionSet, const std::string& outputPath); binder::Status deleteOdex(const std::string& apkPath, const std::string& instructionSet, - const std::unique_ptr<std::string>& outputPath); + const std::optional<std::string>& outputPath); binder::Status installApkVerity(const std::string& filePath, android::base::unique_fd verityInput, int32_t contentSize); binder::Status assertFsverityRootHashMatches(const std::string& filePath, const std::vector<uint8_t>& expectedHash); binder::Status reconcileSecondaryDexFile(const std::string& dexPath, const std::string& packageName, int32_t uid, const std::vector<std::string>& isa, - const std::unique_ptr<std::string>& volumeUuid, int32_t storage_flag, bool* _aidl_return); + const std::optional<std::string>& volumeUuid, int32_t storage_flag, bool* _aidl_return); binder::Status hashSecondaryDexFile(const std::string& dexPath, - const std::string& packageName, int32_t uid, const std::unique_ptr<std::string>& volumeUuid, + const std::string& packageName, int32_t uid, const std::optional<std::string>& volumeUuid, int32_t storageFlag, std::vector<uint8_t>* _aidl_return); binder::Status invalidateMounts(); - binder::Status isQuotaSupported(const std::unique_ptr<std::string>& volumeUuid, + binder::Status isQuotaSupported(const std::optional<std::string>& volumeUuid, bool* _aidl_return); - binder::Status tryMountDataMirror(const std::unique_ptr<std::string>& volumeUuid); - binder::Status onPrivateVolumeRemoved(const std::unique_ptr<std::string>& volumeUuid); + binder::Status tryMountDataMirror(const std::optional<std::string>& volumeUuid); + binder::Status onPrivateVolumeRemoved(const std::optional<std::string>& volumeUuid); binder::Status prepareAppProfile(const std::string& packageName, int32_t userId, int32_t appId, const std::string& profileName, - const std::string& codePath, const std::unique_ptr<std::string>& dexMetadata, + const std::string& codePath, const std::optional<std::string>& dexMetadata, bool* _aidl_return); binder::Status migrateLegacyObbData(); @@ -184,7 +184,7 @@ private: /* Map from UID to cache quota size */ std::unordered_map<uid_t, int64_t> mCacheQuotas; - std::string findDataMediaPath(const std::unique_ptr<std::string>& uuid, userid_t userid); + std::string findDataMediaPath(const std::optional<std::string>& uuid, userid_t userid); }; } // namespace installd diff --git a/cmds/installd/OWNERS b/cmds/installd/OWNERS index 9a21104131..fc745d0cf7 100644 --- a/cmds/installd/OWNERS +++ b/cmds/installd/OWNERS @@ -1,6 +1,5 @@ set noparent -agampe@google.com calin@google.com jsharkey@android.com maco@google.com diff --git a/cmds/installd/TEST_MAPPING b/cmds/installd/TEST_MAPPING index c6583a1bf4..3f0fb6d2ba 100644 --- a/cmds/installd/TEST_MAPPING +++ b/cmds/installd/TEST_MAPPING @@ -15,6 +15,9 @@ { "name": "installd_utils_test" }, + { + "name": "run_dex2oat_test" + }, // AdoptableHostTest moves packages, part of which is handled by installd { "name": "AdoptableHostTest" diff --git a/cmds/installd/binder/android/os/IInstalld.aidl b/cmds/installd/binder/android/os/IInstalld.aidl index eeda6c5855..4ac70a4857 100644 --- a/cmds/installd/binder/android/os/IInstalld.aidl +++ b/cmds/installd/binder/android/os/IInstalld.aidl @@ -117,11 +117,10 @@ interface IInstalld { int userId, int snapshotId, int storageFlags); void restoreAppDataSnapshot(@nullable @utf8InCpp String uuid, in @utf8InCpp String packageName, int appId, @utf8InCpp String seInfo, int user, int snapshotId, int storageflags); - void destroyAppDataSnapshot(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, - int userId, long ceSnapshotInode, int snapshotId, int storageFlags); void destroyCeSnapshotsNotSpecified(@nullable @utf8InCpp String uuid, int userId, in int[] retainSnapshotIds); - + void destroyAppDataSnapshot(@nullable @utf8InCpp String uuid, @utf8InCpp String packageName, + int userId, long ceSnapshotInode, int snapshotId, int storageFlags); void tryMountDataMirror(@nullable @utf8InCpp String volumeUuid); void onPrivateVolumeRemoved(@nullable @utf8InCpp String volumeUuid); diff --git a/cmds/installd/dexopt.cpp b/cmds/installd/dexopt.cpp index ffa87249e2..5076ae6e56 100644 --- a/cmds/installd/dexopt.cpp +++ b/cmds/installd/dexopt.cpp @@ -39,7 +39,6 @@ #include <cutils/fs.h> #include <cutils/properties.h> #include <cutils/sched_policy.h> -#include <dex2oat_return_codes.h> #include <log/log.h> // TODO: Move everything to base/logging. #include <openssl/sha.h> #include <private/android_filesystem_config.h> @@ -50,11 +49,15 @@ #include "dexopt.h" #include "dexopt_return_codes.h" +#include "execv_helper.h" #include "globals.h" #include "installd_deps.h" #include "otapreopt_utils.h" +#include "run_dex2oat.h" +#include "unique_file.h" #include "utils.h" +using android::base::Basename; using android::base::EndsWith; using android::base::GetBoolProperty; using android::base::GetProperty; @@ -67,16 +70,6 @@ using android::base::unique_fd; namespace android { namespace installd { -// Should minidebug info be included in compiled artifacts? Even if this value is -// "true," usage might still be conditional to other constraints, e.g., system -// property overrides. -static constexpr bool kEnableMinidebugInfo = true; - -static constexpr const char* kMinidebugInfoSystemProperty = "dalvik.vm.dex2oat-minidebuginfo"; -static constexpr bool kMinidebugInfoSystemPropertyDefault = false; -static constexpr const char* kMinidebugDex2oatFlag = "--generate-mini-debug-info"; -static constexpr const char* kDisableCompactDexFlag = "--compact-dex-level=none"; - // Deleter using free() for use with std::unique_ptr<>. See also UniqueCPtr<> below. struct FreeDelete { @@ -186,93 +179,6 @@ bool clear_primary_current_profile(const std::string& package_name, const std::s return clear_current_profile(package_name, location, user, /*is_secondary_dex*/false); } -static std::vector<std::string> SplitBySpaces(const std::string& str) { - if (str.empty()) { - return {}; - } - return android::base::Split(str, " "); -} - -static const char* get_location_from_path(const char* path) { - static constexpr char kLocationSeparator = '/'; - const char *location = strrchr(path, kLocationSeparator); - if (location == nullptr) { - return path; - } else { - // Skip the separator character. - return location + 1; - } -} - -// ExecVHelper prepares and holds pointers to parsed command line arguments so that no allocations -// need to be performed between the fork and exec. -class ExecVHelper { - public: - // Store a placeholder for the binary name. - ExecVHelper() : args_(1u, std::string()) {} - - void PrepareArgs(const std::string& bin) { - CHECK(!args_.empty()); - CHECK(args_[0].empty()); - args_[0] = bin; - // Write char* into array. - for (const std::string& arg : args_) { - argv_.push_back(arg.c_str()); - } - argv_.push_back(nullptr); // Add null terminator. - } - - [[ noreturn ]] - void Exec(int exit_code) { - execv(argv_[0], (char * const *)&argv_[0]); - PLOG(ERROR) << "execv(" << argv_[0] << ") failed"; - exit(exit_code); - } - - // Add an arg if it's not empty. - void AddArg(const std::string& arg) { - if (!arg.empty()) { - args_.push_back(arg); - } - } - - // Add a runtime arg if it's not empty. - void AddRuntimeArg(const std::string& arg) { - if (!arg.empty()) { - args_.push_back("--runtime-arg"); - args_.push_back(arg); - } - } - - protected: - // Holder arrays for backing arg storage. - std::vector<std::string> args_; - - // Argument poiners. - std::vector<const char*> argv_; -}; - -static std::string MapPropertyToArg(const std::string& property, - const std::string& format, - const std::string& default_value = "") { - std::string prop = GetProperty(property, default_value); - if (!prop.empty()) { - return StringPrintf(format.c_str(), prop.c_str()); - } - return ""; -} - -static std::string MapPropertyToArgWithBackup(const std::string& property, - const std::string& backupProperty, - const std::string& format, - const std::string& default_value = "") { - std::string value = GetProperty(property, default_value); - if (!value.empty()) { - return StringPrintf(format.c_str(), value.c_str()); - } - return MapPropertyToArg(backupProperty, format, default_value); -} - // Determines which binary we should use for execution (the debug or non-debug version). // e.g. dex2oatd vs dex2oat static const char* select_execution_binary(const char* binary, const char* debug_binary, @@ -311,9 +217,6 @@ const char* select_execution_binary( 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_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"; @@ -328,288 +231,11 @@ static bool IsBootClassPathProfilingEnable() { return profile_boot_class_path == "true"; } -class RunDex2Oat : public ExecVHelper { - public: - RunDex2Oat(int zip_fd, - int oat_fd, - int input_vdex_fd, - int output_vdex_fd, - int image_fd, - const char* input_file_name, - const char* output_file_name, - int swap_fd, - const char* instruction_set, - const char* compiler_filter, - bool debuggable, - bool post_bootcomplete, - bool for_restore, - bool background_job_compile, - int profile_fd, - const char* class_loader_context, - const std::string& class_loader_context_fds, - int target_sdk_version, - bool enable_hidden_api_checks, - bool generate_compact_dex, - int dex_metadata_fd, - const char* compilation_reason) { - // Get the relative path to the input file. - const char* relative_input_file_name = get_location_from_path(input_file_name); - - std::string dex2oat_Xms_arg = MapPropertyToArg("dalvik.vm.dex2oat-Xms", "-Xms%s"); - std::string dex2oat_Xmx_arg = MapPropertyToArg("dalvik.vm.dex2oat-Xmx", "-Xmx%s"); - - std::string threads_format = "-j%s"; - std::string dex2oat_threads_arg = post_bootcomplete - ? (for_restore - ? MapPropertyToArgWithBackup( - "dalvik.vm.restore-dex2oat-threads", - "dalvik.vm.dex2oat-threads", - threads_format) - : MapPropertyToArg("dalvik.vm.dex2oat-threads", threads_format)) - : MapPropertyToArg("dalvik.vm.boot-dex2oat-threads", threads_format); - std::string cpu_set_format = "--cpu-set=%s"; - std::string dex2oat_cpu_set_arg = post_bootcomplete - ? (for_restore - ? MapPropertyToArgWithBackup( - "dalvik.vm.restore-dex2oat-cpu-set", - "dalvik.vm.dex2oat-cpu-set", - cpu_set_format) - : MapPropertyToArg("dalvik.vm.dex2oat-cpu-set", cpu_set_format)) - : MapPropertyToArg("dalvik.vm.boot-dex2oat-cpu-set", cpu_set_format); - - std::string bootclasspath; - char* dex2oat_bootclasspath = getenv("DEX2OATBOOTCLASSPATH"); - if (dex2oat_bootclasspath != nullptr) { - bootclasspath = StringPrintf("-Xbootclasspath:%s", dex2oat_bootclasspath); - } - // If DEX2OATBOOTCLASSPATH is not in the environment, dex2oat is going to query - // BOOTCLASSPATH. - - const std::string dex2oat_isa_features_key = - StringPrintf("dalvik.vm.isa.%s.features", instruction_set); - std::string instruction_set_features_arg = - MapPropertyToArg(dex2oat_isa_features_key, "--instruction-set-features=%s"); - - const std::string dex2oat_isa_variant_key = - StringPrintf("dalvik.vm.isa.%s.variant", instruction_set); - std::string instruction_set_variant_arg = - MapPropertyToArg(dex2oat_isa_variant_key, "--instruction-set-variant=%s"); - - const char* dex2oat_norelocation = "-Xnorelocate"; - - const std::string dex2oat_flags = GetProperty("dalvik.vm.dex2oat-flags", ""); - std::vector<std::string> dex2oat_flags_args = SplitBySpaces(dex2oat_flags); - ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags.c_str()); - - // If we are booting without the real /data, don't spend time compiling. - std::string vold_decrypt = GetProperty("vold.decrypt", ""); - bool skip_compilation = vold_decrypt == "trigger_restart_min_framework" || - vold_decrypt == "1"; - - std::string updatable_bcp_packages = - MapPropertyToArg("dalvik.vm.dex2oat-updatable-bcp-packages-file", - "--updatable-bcp-packages-file=%s"); - if (updatable_bcp_packages.empty()) { - // Make dex2oat fail by providing non-existent file name. - updatable_bcp_packages = "--updatable-bcp-packages-file=/nonx/updatable-bcp-packages.txt"; - } - - std::string resolve_startup_string_arg = - MapPropertyToArg("persist.device_config.runtime.dex2oat_resolve_startup_strings", - "--resolve-startup-const-strings=%s"); - if (resolve_startup_string_arg.empty()) { - // If empty, fall back to system property. - resolve_startup_string_arg = - MapPropertyToArg("dalvik.vm.dex2oat-resolve-startup-strings", - "--resolve-startup-const-strings=%s"); - } - - const std::string image_block_size_arg = - MapPropertyToArg("dalvik.vm.dex2oat-max-image-block-size", - "--max-image-block-size=%s"); - - const bool generate_debug_info = GetBoolProperty("debug.generate-debug-info", false); - - std::string image_format_arg; - if (image_fd >= 0) { - image_format_arg = MapPropertyToArg("dalvik.vm.appimageformat", "--image-format=%s"); - } - - std::string dex2oat_large_app_threshold_arg = - MapPropertyToArg("dalvik.vm.dex2oat-very-large", "--very-large-app-threshold=%s"); - - - - // Decide whether to use dex2oat64. - bool use_dex2oat64 = false; - // Check whether the device even supports 64-bit ABIs. - if (!GetProperty("ro.product.cpu.abilist64", "").empty()) { - use_dex2oat64 = GetBoolProperty("dalvik.vm.dex2oat64.enabled", false); - } - const char* dex2oat_bin = select_execution_binary( - (use_dex2oat64 ? kDex2oat64Path : kDex2oat32Path), - (use_dex2oat64 ? kDex2oatDebug64Path : kDex2oatDebug32Path), - background_job_compile); - - bool generate_minidebug_info = kEnableMinidebugInfo && - GetBoolProperty(kMinidebugInfoSystemProperty, kMinidebugInfoSystemPropertyDefault); - - std::string boot_image; - std::string use_jitzygote_image = - server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE, - ENABLE_JITZYGOTE_IMAGE, - /*default_value=*/ ""); - - if (use_jitzygote_image == "true" || IsBootClassPathProfilingEnable()) { - boot_image = StringPrintf("--boot-image=%s", kJitZygoteImage); - } else { - boot_image = MapPropertyToArg("dalvik.vm.boot-image", "--boot-image=%s"); - } - - // clang FORTIFY doesn't let us use strlen in constant array bounds, so we - // use arraysize instead. - std::string zip_fd_arg = StringPrintf("--zip-fd=%d", zip_fd); - std::string zip_location_arg = StringPrintf("--zip-location=%s", relative_input_file_name); - std::string input_vdex_fd_arg = StringPrintf("--input-vdex-fd=%d", input_vdex_fd); - std::string output_vdex_fd_arg = StringPrintf("--output-vdex-fd=%d", output_vdex_fd); - std::string oat_fd_arg = StringPrintf("--oat-fd=%d", oat_fd); - std::string oat_location_arg = StringPrintf("--oat-location=%s", output_file_name); - std::string instruction_set_arg = StringPrintf("--instruction-set=%s", instruction_set); - std::string dex2oat_compiler_filter_arg; - std::string dex2oat_swap_fd; - std::string dex2oat_image_fd; - std::string target_sdk_version_arg; - if (target_sdk_version != 0) { - target_sdk_version_arg = StringPrintf("-Xtarget-sdk-version:%d", target_sdk_version); - } - std::string class_loader_context_arg; - std::string class_loader_context_fds_arg; - if (class_loader_context != nullptr) { - class_loader_context_arg = StringPrintf("--class-loader-context=%s", - class_loader_context); - if (!class_loader_context_fds.empty()) { - class_loader_context_fds_arg = StringPrintf("--class-loader-context-fds=%s", - class_loader_context_fds.c_str()); - } - } - - if (swap_fd >= 0) { - dex2oat_swap_fd = StringPrintf("--swap-fd=%d", swap_fd); - } - if (image_fd >= 0) { - dex2oat_image_fd = StringPrintf("--app-image-fd=%d", image_fd); - } - - // Compute compiler filter. - bool have_dex2oat_relocation_skip_flag = false; - if (skip_compilation) { - dex2oat_compiler_filter_arg = "--compiler-filter=extract"; - have_dex2oat_relocation_skip_flag = true; - } else if (compiler_filter != nullptr) { - dex2oat_compiler_filter_arg = StringPrintf("--compiler-filter=%s", compiler_filter); - } - - if (dex2oat_compiler_filter_arg.empty()) { - dex2oat_compiler_filter_arg = MapPropertyToArg("dalvik.vm.dex2oat-filter", - "--compiler-filter=%s"); - } - - // Check whether all apps should be compiled debuggable. - if (!debuggable) { - debuggable = GetProperty("dalvik.vm.always_debuggable", "") == "1"; - } - std::string profile_arg; - if (profile_fd != -1) { - profile_arg = StringPrintf("--profile-file-fd=%d", profile_fd); - } - - // Get the directory of the apk to pass as a base classpath directory. - std::string base_dir; - std::string apk_dir(input_file_name); - unsigned long dir_index = apk_dir.rfind('/'); - bool has_base_dir = dir_index != std::string::npos; - if (has_base_dir) { - apk_dir = apk_dir.substr(0, dir_index); - base_dir = StringPrintf("--classpath-dir=%s", apk_dir.c_str()); - } - - std::string dex_metadata_fd_arg = "--dm-fd=" + std::to_string(dex_metadata_fd); - - std::string compilation_reason_arg = compilation_reason == nullptr - ? "" - : std::string("--compilation-reason=") + compilation_reason; - - ALOGV("Running %s in=%s out=%s\n", dex2oat_bin, relative_input_file_name, output_file_name); - - // Disable cdex if update input vdex is true since this combination of options is not - // supported. - const bool disable_cdex = !generate_compact_dex || (input_vdex_fd == output_vdex_fd); - - AddArg(zip_fd_arg); - AddArg(zip_location_arg); - AddArg(input_vdex_fd_arg); - AddArg(output_vdex_fd_arg); - AddArg(oat_fd_arg); - AddArg(oat_location_arg); - AddArg(instruction_set_arg); - - AddArg(instruction_set_variant_arg); - AddArg(instruction_set_features_arg); - - AddArg(boot_image); - - AddRuntimeArg(bootclasspath); - AddRuntimeArg(dex2oat_Xms_arg); - AddRuntimeArg(dex2oat_Xmx_arg); - - AddArg(updatable_bcp_packages); - AddArg(resolve_startup_string_arg); - AddArg(image_block_size_arg); - AddArg(dex2oat_compiler_filter_arg); - AddArg(dex2oat_threads_arg); - AddArg(dex2oat_cpu_set_arg); - AddArg(dex2oat_swap_fd); - AddArg(dex2oat_image_fd); - - if (generate_debug_info) { - AddArg("--generate-debug-info"); - } - if (debuggable) { - AddArg("--debuggable"); - } - AddArg(image_format_arg); - AddArg(dex2oat_large_app_threshold_arg); - - if (have_dex2oat_relocation_skip_flag) { - AddRuntimeArg(dex2oat_norelocation); - } - AddArg(profile_arg); - AddArg(base_dir); - AddArg(class_loader_context_arg); - AddArg(class_loader_context_fds_arg); - if (generate_minidebug_info) { - AddArg(kMinidebugDex2oatFlag); - } - if (disable_cdex) { - AddArg(kDisableCompactDexFlag); - } - AddRuntimeArg(target_sdk_version_arg); - if (enable_hidden_api_checks) { - AddRuntimeArg("-Xhidden-api-policy:enabled"); - } - - if (dex_metadata_fd > -1) { - AddArg(dex_metadata_fd_arg); - } - - AddArg(compilation_reason_arg); - - // Do not add args after dex2oat_flags, they should override others for debugging. - args_.insert(args_.end(), dex2oat_flags_args.begin(), dex2oat_flags_args.end()); - - PrepareArgs(dex2oat_bin); +static void UnlinkIgnoreResult(const std::string& path) { + if (unlink(path.c_str()) < 0) { + PLOG(ERROR) << "Failed to unlink " << path; } -}; +} /* * Whether dexopt should use a swap file when compiling an APK. @@ -727,6 +353,16 @@ static unique_fd open_reference_profile(uid_t uid, const std::string& package_na return open_profile(uid, profile, read_write ? (O_CREAT | O_RDWR) : O_RDONLY); } +static UniqueFile open_reference_profile_as_unique_file(uid_t uid, const std::string& package_name, + const std::string& location, bool read_write, bool is_secondary_dex) { + std::string profile_path = create_reference_profile_path(package_name, location, + is_secondary_dex); + unique_fd ufd = open_profile(uid, profile_path, read_write ? (O_CREAT | O_RDWR) : O_RDONLY); + return UniqueFile(ufd.release(), profile_path, [](const std::string& path) { + clear_profile(path); + }); +} + static unique_fd open_spnashot_profile(uid_t uid, const std::string& package_name, const std::string& location) { std::string profile = create_snapshot_profile_path(package_name, location); @@ -868,6 +504,7 @@ class RunProfman : public ExecVHelper { /*for_boot_image*/false); } + using ExecVHelper::Exec; // To suppress -Wno-overloaded-virtual void Exec() { ExecVHelper::Exec(DexoptReturnCodes::kProfmanExec); } @@ -1022,7 +659,7 @@ bool dump_profiles(int32_t uid, const std::string& pkgname, const std::string& p PLOG(ERROR) << "installd cannot open " << code_path.c_str(); return false; } - dex_locations.push_back(get_location_from_path(code_path.c_str())); + dex_locations.push_back(Basename(code_path)); apk_fds.push_back(std::move(apk_fd)); @@ -1216,118 +853,14 @@ static bool create_oat_out_path(const char* apk_path, const char* instruction_se return true; } -// Helper for fd management. This is similar to a unique_fd in that it closes the file descriptor -// on destruction. It will also run the given cleanup (unless told not to) after closing. -// -// Usage example: -// -// Dex2oatFileWrapper file(open(...), -// [name]() { -// unlink(name.c_str()); -// }); -// // Note: care needs to be taken about name, as it needs to have a lifetime longer than the -// wrapper if captured as a reference. -// -// if (file.get() == -1) { -// // Error opening... -// } -// -// ... -// if (error) { -// // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will run -// // and delete the file (after the fd is closed). -// return -1; -// } -// -// (Success case) -// file.SetCleanup(false); -// // At this point, when the Dex2oatFileWrapper is destructed, the cleanup function will not run -// // (leaving the file around; after the fd is closed). -// -class Dex2oatFileWrapper { - public: - Dex2oatFileWrapper() : value_(-1), cleanup_(), do_cleanup_(true), auto_close_(true) { - } - - Dex2oatFileWrapper(int value, std::function<void ()> cleanup) - : value_(value), cleanup_(cleanup), do_cleanup_(true), auto_close_(true) {} - - Dex2oatFileWrapper(Dex2oatFileWrapper&& other) { - value_ = other.value_; - cleanup_ = other.cleanup_; - do_cleanup_ = other.do_cleanup_; - auto_close_ = other.auto_close_; - other.release(); - } - - Dex2oatFileWrapper& operator=(Dex2oatFileWrapper&& other) { - value_ = other.value_; - cleanup_ = other.cleanup_; - do_cleanup_ = other.do_cleanup_; - auto_close_ = other.auto_close_; - other.release(); - return *this; - } - - ~Dex2oatFileWrapper() { - reset(-1); - } - - int get() { - return value_; - } - - void SetCleanup(bool cleanup) { - do_cleanup_ = cleanup; - } - - void reset(int new_value) { - if (auto_close_ && value_ >= 0) { - close(value_); - } - if (do_cleanup_ && cleanup_ != nullptr) { - cleanup_(); - } - - value_ = new_value; - } - - void reset(int new_value, std::function<void ()> new_cleanup) { - if (auto_close_ && value_ >= 0) { - close(value_); - } - if (do_cleanup_ && cleanup_ != nullptr) { - cleanup_(); - } - - value_ = new_value; - cleanup_ = new_cleanup; - } - - void DisableAutoClose() { - auto_close_ = false; - } - - private: - void release() { - value_ = -1; - do_cleanup_ = false; - cleanup_ = nullptr; - } - int value_; - std::function<void ()> cleanup_; - bool do_cleanup_; - bool auto_close_; -}; - // (re)Creates the app image if needed. -Dex2oatFileWrapper maybe_open_app_image(const char* out_oat_path, +UniqueFile maybe_open_app_image(const std::string& out_oat_path, bool generate_app_image, bool is_public, int uid, bool is_secondary_dex) { const std::string image_path = create_image_filename(out_oat_path); if (image_path.empty()) { // Happens when the out_oat_path has an unknown extension. - return Dex2oatFileWrapper(); + return UniqueFile(); } // In case there is a stale image, remove it now. Ignore any error. @@ -1335,18 +868,19 @@ Dex2oatFileWrapper maybe_open_app_image(const char* out_oat_path, // Not enabled, exit. if (!generate_app_image) { - return Dex2oatFileWrapper(); + return UniqueFile(); } std::string app_image_format = GetProperty("dalvik.vm.appimageformat", ""); if (app_image_format.empty()) { - return Dex2oatFileWrapper(); + return UniqueFile(); } // Recreate is true since we do not want to modify a mapped image. If the app is // already running and we modify the image file, it can cause crashes (b/27493510). - Dex2oatFileWrapper wrapper_fd( + UniqueFile image_file( open_output_file(image_path.c_str(), true /*recreate*/, 0600 /*permissions*/), - [image_path]() { unlink(image_path.c_str()); }); - if (wrapper_fd.get() < 0) { + image_path, + UnlinkIgnoreResult); + if (image_file.fd() < 0) { // Could not create application image file. Go on since we can compile without it. LOG(ERROR) << "installd could not create '" << image_path << "' for image file during dexopt"; @@ -1357,21 +891,21 @@ Dex2oatFileWrapper maybe_open_app_image(const char* out_oat_path, } } } else if (!set_permissions_and_ownership( - wrapper_fd.get(), is_public, uid, image_path.c_str(), is_secondary_dex)) { + image_file.fd(), is_public, uid, image_path.c_str(), is_secondary_dex)) { ALOGE("installd cannot set owner '%s' for image during dexopt\n", image_path.c_str()); - wrapper_fd.reset(-1); + image_file.reset(); } - return wrapper_fd; + return image_file; } // Creates the dexopt swap file if necessary and return its fd. // Returns -1 if there's no need for a swap or in case of errors. -unique_fd maybe_open_dexopt_swap_file(const char* out_oat_path) { +unique_fd maybe_open_dexopt_swap_file(const std::string& out_oat_path) { if (!ShouldUseSwapFileForDexopt()) { return invalid_unique_fd(); } - auto swap_file_name = std::string(out_oat_path) + ".swap"; + auto swap_file_name = out_oat_path + ".swap"; unique_fd swap_fd(open_output_file( swap_file_name.c_str(), /*recreate*/true, /*permissions*/0600)); if (swap_fd.get() < 0) { @@ -1389,13 +923,13 @@ unique_fd maybe_open_dexopt_swap_file(const char* out_oat_path) { // Opens the reference profiles if needed. // Note that the reference profile might not exist so it's OK if the fd will be -1. -Dex2oatFileWrapper maybe_open_reference_profile(const std::string& pkgname, +UniqueFile maybe_open_reference_profile(const std::string& pkgname, const std::string& dex_path, const char* profile_name, bool profile_guided, bool is_public, int uid, bool is_secondary_dex) { // If we are not profile guided compilation, or we are compiling system server // do not bother to open the profiles; we won't be using them. if (!profile_guided || (pkgname[0] == '*')) { - return Dex2oatFileWrapper(); + return UniqueFile(); } // If this is a secondary dex path which is public do not open the profile. @@ -1407,7 +941,7 @@ Dex2oatFileWrapper maybe_open_reference_profile(const std::string& pkgname, // compiling with a public profile from the .dm file the PackageManager will // set is_public toghether with the profile guided compilation. if (is_secondary_dex && is_public) { - return Dex2oatFileWrapper(); + return UniqueFile(); } // Open reference profile in read only mode as dex2oat does not get write permissions. @@ -1417,33 +951,28 @@ Dex2oatFileWrapper maybe_open_reference_profile(const std::string& pkgname, } else { if (profile_name == nullptr) { // This path is taken for system server re-compilation lunched from ZygoteInit. - return Dex2oatFileWrapper(); + return UniqueFile(); } else { location = profile_name; } } - unique_fd ufd = open_reference_profile(uid, pkgname, location, /*read_write*/false, - is_secondary_dex); - const auto& cleanup = [pkgname, location, is_secondary_dex]() { - clear_reference_profile(pkgname, location, is_secondary_dex); - }; - return Dex2oatFileWrapper(ufd.release(), cleanup); + return open_reference_profile_as_unique_file(uid, pkgname, location, /*read_write*/false, + is_secondary_dex); } -// Opens the vdex files and assigns the input fd to in_vdex_wrapper_fd and the output fd to -// out_vdex_wrapper_fd. Returns true for success or false in case of errors. +// Opens the vdex files and assigns the input fd to in_vdex_wrapper and the output fd to +// out_vdex_wrapper. Returns true for success or false in case of errors. bool open_vdex_files_for_dex2oat(const char* apk_path, const char* out_oat_path, int dexopt_needed, const char* instruction_set, bool is_public, int uid, bool is_secondary_dex, - bool profile_guided, Dex2oatFileWrapper* in_vdex_wrapper_fd, - Dex2oatFileWrapper* out_vdex_wrapper_fd) { - CHECK(in_vdex_wrapper_fd != nullptr); - CHECK(out_vdex_wrapper_fd != nullptr); + bool profile_guided, UniqueFile* in_vdex_wrapper, + UniqueFile* out_vdex_wrapper) { + CHECK(in_vdex_wrapper != nullptr); + CHECK(out_vdex_wrapper != nullptr); // Open the existing VDEX. We do this before creating the new output VDEX, which will // unlink the old one. char in_odex_path[PKG_PATH_MAX]; int dexopt_action = abs(dexopt_needed); bool is_odex_location = dexopt_needed < 0; - std::string in_vdex_path_str; // Infer the name of the output VDEX. const std::string out_vdex_path_str = create_vdex_filename(out_oat_path); @@ -1465,7 +994,7 @@ bool open_vdex_files_for_dex2oat(const char* apk_path, const char* out_oat_path, } else { path = out_oat_path; } - in_vdex_path_str = create_vdex_filename(path); + std::string in_vdex_path_str = create_vdex_filename(path); if (in_vdex_path_str.empty()) { ALOGE("installd cannot compute input vdex location for '%s'\n", path); return false; @@ -1483,13 +1012,15 @@ bool open_vdex_files_for_dex2oat(const char* apk_path, const char* out_oat_path, !profile_guided; if (update_vdex_in_place) { // Open the file read-write to be able to update it. - in_vdex_wrapper_fd->reset(open(in_vdex_path_str.c_str(), O_RDWR, 0)); - if (in_vdex_wrapper_fd->get() == -1) { + in_vdex_wrapper->reset(open(in_vdex_path_str.c_str(), O_RDWR, 0), + in_vdex_path_str); + if (in_vdex_wrapper->fd() == -1) { // If we failed to open the file, we cannot update it in place. update_vdex_in_place = false; } } else { - in_vdex_wrapper_fd->reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0)); + in_vdex_wrapper->reset(open(in_vdex_path_str.c_str(), O_RDONLY, 0), + in_vdex_path_str); } } @@ -1498,22 +1029,24 @@ bool open_vdex_files_for_dex2oat(const char* apk_path, const char* out_oat_path, if (update_vdex_in_place) { // We unlink the file in case the invocation of dex2oat fails, to ensure we don't // have bogus stale vdex files. - out_vdex_wrapper_fd->reset( - in_vdex_wrapper_fd->get(), - [out_vdex_path_str]() { unlink(out_vdex_path_str.c_str()); }); + out_vdex_wrapper->reset( + in_vdex_wrapper->fd(), + out_vdex_path_str, + UnlinkIgnoreResult); // Disable auto close for the in wrapper fd (it will be done when destructing the out // wrapper). - in_vdex_wrapper_fd->DisableAutoClose(); + in_vdex_wrapper->DisableAutoClose(); } else { - out_vdex_wrapper_fd->reset( + out_vdex_wrapper->reset( open_output_file(out_vdex_path_str.c_str(), /*recreate*/true, /*permissions*/0644), - [out_vdex_path_str]() { unlink(out_vdex_path_str.c_str()); }); - if (out_vdex_wrapper_fd->get() < 0) { + out_vdex_path_str, + UnlinkIgnoreResult); + if (out_vdex_wrapper->fd() < 0) { ALOGE("installd cannot open vdex'%s' during dexopt\n", out_vdex_path_str.c_str()); return false; } } - if (!set_permissions_and_ownership(out_vdex_wrapper_fd->get(), is_public, uid, + if (!set_permissions_and_ownership(out_vdex_wrapper->fd(), is_public, uid, out_vdex_path_str.c_str(), is_secondary_dex)) { ALOGE("installd cannot set owner '%s' for vdex during dexopt\n", out_vdex_path_str.c_str()); return false; @@ -1524,25 +1057,24 @@ bool open_vdex_files_for_dex2oat(const char* apk_path, const char* out_oat_path, } // Opens the output oat file for the given apk. -// If successful it stores the output path into out_oat_path and returns true. -Dex2oatFileWrapper open_oat_out_file(const char* apk_path, const char* oat_dir, - bool is_public, int uid, const char* instruction_set, bool is_secondary_dex, - char* out_oat_path) { +UniqueFile open_oat_out_file(const char* apk_path, const char* oat_dir, + bool is_public, int uid, const char* instruction_set, bool is_secondary_dex) { + char out_oat_path[PKG_PATH_MAX]; if (!create_oat_out_path(apk_path, instruction_set, oat_dir, is_secondary_dex, out_oat_path)) { - return Dex2oatFileWrapper(); + return UniqueFile(); } - const std::string out_oat_path_str(out_oat_path); - Dex2oatFileWrapper wrapper_fd( + UniqueFile oat( open_output_file(out_oat_path, /*recreate*/true, /*permissions*/0644), - [out_oat_path_str]() { unlink(out_oat_path_str.c_str()); }); - if (wrapper_fd.get() < 0) { + out_oat_path, + UnlinkIgnoreResult); + if (oat.fd() < 0) { PLOG(ERROR) << "installd cannot open output during dexopt" << out_oat_path; } else if (!set_permissions_and_ownership( - wrapper_fd.get(), is_public, uid, out_oat_path, is_secondary_dex)) { + oat.fd(), is_public, uid, out_oat_path, is_secondary_dex)) { ALOGE("installd cannot set owner '%s' for output during dexopt\n", out_oat_path); - wrapper_fd.reset(-1); + oat.reset(); } - return wrapper_fd; + return oat; } // Creates RDONLY fds for oat and vdex files, if exist. @@ -2149,8 +1681,8 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins } // Open the input file. - unique_fd input_fd(open(dex_path, O_RDONLY, 0)); - if (input_fd.get() < 0) { + UniqueFile in_dex(open(dex_path, O_RDONLY, 0), dex_path); + if (in_dex.fd() < 0) { *error_msg = StringPrintf("installd cannot open '%s' for input during dexopt", dex_path); LOG(ERROR) << *error_msg; return -1; @@ -2164,19 +1696,19 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins } // Create the output OAT file. - char out_oat_path[PKG_PATH_MAX]; - Dex2oatFileWrapper out_oat_fd = open_oat_out_file(dex_path, oat_dir, is_public, uid, - instruction_set, is_secondary_dex, out_oat_path); - if (out_oat_fd.get() < 0) { + UniqueFile out_oat = open_oat_out_file(dex_path, oat_dir, is_public, uid, + instruction_set, is_secondary_dex); + if (out_oat.fd() < 0) { *error_msg = "Could not open out oat file."; return -1; } // Open vdex files. - Dex2oatFileWrapper in_vdex_fd; - Dex2oatFileWrapper out_vdex_fd; - if (!open_vdex_files_for_dex2oat(dex_path, out_oat_path, dexopt_needed, instruction_set, - is_public, uid, is_secondary_dex, profile_guided, &in_vdex_fd, &out_vdex_fd)) { + UniqueFile in_vdex; + UniqueFile out_vdex; + if (!open_vdex_files_for_dex2oat(dex_path, out_oat.path().c_str(), dexopt_needed, + instruction_set, is_public, uid, is_secondary_dex, profile_guided, &in_vdex, + &out_vdex)) { *error_msg = "Could not open vdex files."; return -1; } @@ -2196,53 +1728,72 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins } // Create a swap file if necessary. - unique_fd swap_fd = maybe_open_dexopt_swap_file(out_oat_path); + unique_fd swap_fd = maybe_open_dexopt_swap_file(out_oat.path()); // Open the reference profile if needed. - Dex2oatFileWrapper reference_profile_fd = maybe_open_reference_profile( + UniqueFile reference_profile = maybe_open_reference_profile( pkgname, dex_path, profile_name, profile_guided, is_public, uid, is_secondary_dex); - if (reference_profile_fd.get() == -1) { + if (reference_profile.fd() == -1) { // We don't create an app image without reference profile since there is no speedup from // loading it in that case and instead will be a small overhead. generate_app_image = false; } // Create the app image file if needed. - Dex2oatFileWrapper image_fd = maybe_open_app_image( - out_oat_path, generate_app_image, is_public, uid, is_secondary_dex); + UniqueFile out_image = maybe_open_app_image( + out_oat.path(), generate_app_image, is_public, uid, is_secondary_dex); - unique_fd dex_metadata_fd; + UniqueFile dex_metadata; if (dex_metadata_path != nullptr) { - dex_metadata_fd.reset(TEMP_FAILURE_RETRY(open(dex_metadata_path, O_RDONLY | O_NOFOLLOW))); - if (dex_metadata_fd.get() < 0) { + dex_metadata.reset(TEMP_FAILURE_RETRY(open(dex_metadata_path, O_RDONLY | O_NOFOLLOW)), + dex_metadata_path); + if (dex_metadata.fd() < 0) { PLOG(ERROR) << "Failed to open dex metadata file " << dex_metadata_path; } } + std::string jitzygote_flag = server_configurable_flags::GetServerConfigurableFlag( + RUNTIME_NATIVE_BOOT_NAMESPACE, + ENABLE_JITZYGOTE_IMAGE, + /*default_value=*/ ""); + bool use_jitzygote_image = jitzygote_flag == "true" || IsBootClassPathProfilingEnable(); + + // Decide whether to use dex2oat64. + bool use_dex2oat64 = false; + // Check whether the device even supports 64-bit ABIs. + if (!GetProperty("ro.product.cpu.abilist64", "").empty()) { + use_dex2oat64 = GetBoolProperty("dalvik.vm.dex2oat64.enabled", false); + } + const char* dex2oat_bin = select_execution_binary( + (use_dex2oat64 ? kDex2oat64Path : kDex2oat32Path), + (use_dex2oat64 ? kDex2oatDebug64Path : kDex2oatDebug32Path), + background_job_compile); + + auto execv_helper = std::make_unique<ExecVHelper>(); + LOG(VERBOSE) << "DexInv: --- BEGIN '" << dex_path << "' ---"; - RunDex2Oat runner(input_fd.get(), - out_oat_fd.get(), - in_vdex_fd.get(), - out_vdex_fd.get(), - image_fd.get(), - dex_path, - out_oat_path, + RunDex2Oat runner(dex2oat_bin, execv_helper.get()); + runner.Initialize(out_oat, + out_vdex, + out_image, + in_dex, + in_vdex, + dex_metadata, + reference_profile, + class_loader_context, + join_fds(context_input_fds), swap_fd.get(), instruction_set, compiler_filter, debuggable, boot_complete, for_restore, - background_job_compile, - reference_profile_fd.get(), - class_loader_context, - join_fds(context_input_fds), target_sdk_version, enable_hidden_api_checks, generate_compact_dex, - dex_metadata_fd.get(), + use_jitzygote_image, compilation_reason); pid_t pid = fork(); @@ -2251,8 +1802,8 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins drop_capabilities(uid); SetDex2OatScheduling(boot_complete); - if (flock(out_oat_fd.get(), LOCK_EX | LOCK_NB) != 0) { - PLOG(ERROR) << "flock(" << out_oat_path << ") failed"; + if (flock(out_oat.fd(), LOCK_EX | LOCK_NB) != 0) { + PLOG(ERROR) << "flock(" << out_oat.path() << ") failed"; _exit(DexoptReturnCodes::kFlock); } @@ -2269,13 +1820,13 @@ int dexopt(const char* dex_path, uid_t uid, const char* pkgname, const char* ins } } - update_out_oat_access_times(dex_path, out_oat_path); + update_out_oat_access_times(dex_path, out_oat.path().c_str()); // We've been successful, don't delete output. - out_oat_fd.SetCleanup(false); - out_vdex_fd.SetCleanup(false); - image_fd.SetCleanup(false); - reference_profile_fd.SetCleanup(false); + out_oat.DisableCleanup(); + out_vdex.DisableCleanup(); + out_image.DisableCleanup(); + reference_profile.DisableCleanup(); return 0; } @@ -2325,7 +1876,7 @@ enum ReconcileSecondaryDexResult { // out_secondary_dex_exists will be set to false. bool reconcile_secondary_dex_file(const std::string& dex_path, const std::string& pkgname, int uid, const std::vector<std::string>& isas, - const std::unique_ptr<std::string>& volume_uuid, int storage_flag, + const std::optional<std::string>& volume_uuid, int storage_flag, /*out*/bool* out_secondary_dex_exists) { *out_secondary_dex_exists = false; // start by assuming the file does not exist. if (isas.size() == 0) { @@ -2346,7 +1897,7 @@ bool reconcile_secondary_dex_file(const std::string& dex_path, /* child -- drop privileges before continuing */ drop_capabilities(uid); - const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str(); + const char* volume_uuid_cstr = volume_uuid ? volume_uuid->c_str() : nullptr; if (!validate_secondary_dex_path(pkgname, dex_path, volume_uuid_cstr, uid, storage_flag)) { LOG(ERROR) << "Could not validate secondary dex path " << dex_path; @@ -2447,11 +1998,11 @@ bool reconcile_secondary_dex_file(const std::string& dex_path, // the app. // For any other errors (e.g. if any of the parameters are invalid) returns false. bool hash_secondary_dex_file(const std::string& dex_path, const std::string& pkgname, int uid, - const std::unique_ptr<std::string>& volume_uuid, int storage_flag, + const std::optional<std::string>& volume_uuid, int storage_flag, std::vector<uint8_t>* out_secondary_dex_hash) { out_secondary_dex_hash->clear(); - const char* volume_uuid_cstr = volume_uuid == nullptr ? nullptr : volume_uuid->c_str(); + const char* volume_uuid_cstr = volume_uuid ? volume_uuid->c_str() : nullptr; if (storage_flag != FLAG_STORAGE_CE && storage_flag != FLAG_STORAGE_DE) { LOG(ERROR) << "hash_secondary_dex_file called with invalid storage_flag: " @@ -2978,7 +2529,7 @@ bool prepare_app_profile(const std::string& package_name, appid_t app_id, const std::string& profile_name, const std::string& code_path, - const std::unique_ptr<std::string>& dex_metadata) { + const std::optional<std::string>& dex_metadata) { // Prepare the current profile. std::string cur_profile = create_current_profile_path(user_id, package_name, profile_name, /*is_secondary_dex*/ false); @@ -2989,7 +2540,7 @@ bool prepare_app_profile(const std::string& package_name, } // Check if we need to install the profile from the dex metadata. - if (dex_metadata == nullptr) { + if (!dex_metadata) { return true; } diff --git a/cmds/installd/dexopt.h b/cmds/installd/dexopt.h index cc448736ea..d35953c058 100644 --- a/cmds/installd/dexopt.h +++ b/cmds/installd/dexopt.h @@ -21,6 +21,8 @@ #include <sys/types.h> +#include <optional> + #include <cutils/multiuser.h> namespace android { @@ -100,17 +102,17 @@ bool prepare_app_profile(const std::string& package_name, appid_t app_id, const std::string& profile_name, const std::string& code_path, - const std::unique_ptr<std::string>& dex_metadata); + const std::optional<std::string>& dex_metadata); bool delete_odex(const char* apk_path, const char* instruction_set, const char* output_path); bool reconcile_secondary_dex_file(const std::string& dex_path, const std::string& pkgname, int uid, const std::vector<std::string>& isas, - const std::unique_ptr<std::string>& volumeUuid, int storage_flag, + const std::optional<std::string>& volumeUuid, int storage_flag, /*out*/bool* out_secondary_dex_exists); bool hash_secondary_dex_file(const std::string& dex_path, - const std::string& pkgname, int uid, const std::unique_ptr<std::string>& volume_uuid, + const std::string& pkgname, int uid, const std::optional<std::string>& volume_uuid, int storage_flag, std::vector<uint8_t>* out_secondary_dex_hash); int dexopt(const char *apk_path, uid_t uid, const char *pkgName, const char *instruction_set, diff --git a/cmds/installd/dexopt_return_codes.h b/cmds/installd/dexopt_return_codes.h index bbecfa4223..e5198ad70f 100644 --- a/cmds/installd/dexopt_return_codes.h +++ b/cmds/installd/dexopt_return_codes.h @@ -14,8 +14,6 @@ * limitations under the License. */ -#include <dex2oat_return_codes.h> - namespace android { namespace installd { @@ -70,48 +68,21 @@ inline const char* get_installd_return_code_name(DexoptReturnCodes code) { return nullptr; } -inline const char* get_dex2oat_return_code_name(art::dex2oat::ReturnCode code) { - switch (code) { - case art::dex2oat::ReturnCode::kNoFailure: - return "dex2oat success"; - case art::dex2oat::ReturnCode::kOther: - return "unspecified dex2oat error"; - case art::dex2oat::ReturnCode::kCreateRuntime: - return "dex2oat failed to create a runtime"; +inline const char* get_dex2oat_return_code_name(int code) { + if (code == 0) { + return "dex2oat success"; + } else { + return "dex2oat error"; } - return nullptr; } -// Get some slightly descriptive string for the return code. Handles both DexoptReturnCodes (local -// exit codes) as well as art::dex2oat::ReturnCode. +// Get some slightly descriptive string for the return code. inline const char* get_return_code_name(int code) { - // Try to enforce non-overlap (see comment on DexoptReturnCodes) - // TODO: How could switch-case checks be used to enforce completeness? - switch (code) { - case kSetGid: - case kSetUid: - case kCapSet: - case kFlock: - case kProfmanExec: - case kSetSchedPolicy: - case kSetPriority: - case kDex2oatExec: - case kInstructionSetLength: - case kHashValidatePath: - case kHashOpenPath: - case kHashReadDex: - case kHashWrite: - break; - case static_cast<int>(art::dex2oat::ReturnCode::kNoFailure): - case static_cast<int>(art::dex2oat::ReturnCode::kOther): - case static_cast<int>(art::dex2oat::ReturnCode::kCreateRuntime): - break; - } const char* value = get_installd_return_code_name(static_cast<DexoptReturnCodes>(code)); if (value != nullptr) { return value; } - value = get_dex2oat_return_code_name(static_cast<art::dex2oat::ReturnCode>(code)); + value = get_dex2oat_return_code_name(code); return value; } diff --git a/cmds/installd/execv_helper.cpp b/cmds/installd/execv_helper.cpp new file mode 100644 index 0000000000..a2d240abb9 --- /dev/null +++ b/cmds/installd/execv_helper.cpp @@ -0,0 +1,67 @@ +/* + * 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 LOG_TAG "installd" + +#include "execv_helper.h" + +#include <stdlib.h> +#include <unistd.h> + +#include <string> + +#include <android-base/logging.h> +#include <android-base/properties.h> + +namespace android { +namespace installd { + +// Store a placeholder for the binary name. +ExecVHelper::ExecVHelper() : args_(1u, std::string()) {} + +ExecVHelper::~ExecVHelper() {} + +void ExecVHelper::PrepareArgs(const std::string& bin) { + CHECK(!args_.empty()); + CHECK(args_[0].empty()); + args_[0] = bin; + // Write char* into array. + for (const std::string& arg : args_) { + argv_.push_back(arg.c_str()); + } + argv_.push_back(nullptr); // Add null terminator. +} + +void ExecVHelper::Exec(int exit_code) { + execv(argv_[0], (char * const *)&argv_[0]); + PLOG(ERROR) << "execv(" << argv_[0] << ") failed"; + exit(exit_code); +} + +void ExecVHelper::AddArg(const std::string& arg) { + if (!arg.empty()) { + args_.push_back(arg); + } +} + +void ExecVHelper::AddRuntimeArg(const std::string& arg) { + if (!arg.empty()) { + args_.push_back("--runtime-arg"); + args_.push_back(arg); + } +} + +} // namespace installd +} // namespace android diff --git a/cmds/installd/execv_helper.h b/cmds/installd/execv_helper.h new file mode 100644 index 0000000000..9adfc0e812 --- /dev/null +++ b/cmds/installd/execv_helper.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. + */ + +#ifndef ANDROID_INSTALLD_EXECV_HELPER_H +#define ANDROID_INSTALLD_EXECV_HELPER_H + +#include <string> +#include <vector> + +namespace android { +namespace installd { + +// ExecVHelper prepares and holds pointers to parsed command line arguments so that no allocations +// need to be performed between the fork and exec. +class ExecVHelper { + public: + ExecVHelper(); + virtual ~ExecVHelper(); + + [[ noreturn ]] + virtual void Exec(int exit_code); + + void PrepareArgs(const std::string& bin); + + // Add an arg if it's not empty. + void AddArg(const std::string& arg); + + // Add a runtime arg if it's not empty. + void AddRuntimeArg(const std::string& arg); + + protected: + // Holder arrays for backing arg storage. + std::vector<std::string> args_; + + // Argument poiners. + std::vector<const char*> argv_; +}; + +} // namespace installd +} // namespace android + +#endif // ANDROID_INSTALLD_EXECV_HELPER_H diff --git a/cmds/installd/otapreopt.cpp b/cmds/installd/otapreopt.cpp index 18f8268bf3..443821c3b9 100644 --- a/cmds/installd/otapreopt.cpp +++ b/cmds/installd/otapreopt.cpp @@ -31,10 +31,8 @@ #include <android-base/macros.h> #include <android-base/stringprintf.h> #include <android-base/strings.h> -#include <art_image_values.h> #include <cutils/fs.h> #include <cutils/properties.h> -#include <dex2oat_return_codes.h> #include <log/log.h> #include <private/android_filesystem_config.h> @@ -96,7 +94,7 @@ static constexpr bool IsPowerOfTwo(T x) { template<typename T> static constexpr T RoundDown(T x, typename std::decay<T>::type n) { - return DCHECK_CONSTEXPR(IsPowerOfTwo(n), , T(0))(x & -n); + return (x & -n); } template<typename T> @@ -523,6 +521,7 @@ private: // Choose a random relocation offset. Taken from art/runtime/gc/image_space.cc. static int32_t ChooseRelocationOffsetDelta(int32_t min_delta, int32_t max_delta) { constexpr size_t kPageSize = PAGE_SIZE; + static_assert(IsPowerOfTwo(kPageSize), "page size must be power of two"); CHECK_EQ(min_delta % kPageSize, 0u); CHECK_EQ(max_delta % kPageSize, 0u); CHECK_LT(min_delta, max_delta); diff --git a/cmds/installd/run_dex2oat.cpp b/cmds/installd/run_dex2oat.cpp new file mode 100644 index 0000000000..17ea90359c --- /dev/null +++ b/cmds/installd/run_dex2oat.cpp @@ -0,0 +1,390 @@ +/* + * 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 LOG_TAG "installd" + +#include "run_dex2oat.h" + +#include <memory> +#include <string> +#include <vector> + +#include <android-base/file.h> +#include <android-base/logging.h> +#include <android-base/properties.h> +#include <android-base/scopeguard.h> +#include <android-base/stringprintf.h> +#include <android-base/strings.h> +#include <log/log.h> +#include <server_configurable_flags/get_flags.h> + +#include "unique_file.h" + +using android::base::Basename; +using android::base::StringPrintf; + +namespace android { +namespace installd { + +namespace { + +// Should minidebug info be included in compiled artifacts? Even if this value is +// "true," usage might still be conditional to other constraints, e.g., system +// property overrides. +static constexpr bool kEnableMinidebugInfo = true; + +static constexpr const char* kMinidebugInfoSystemProperty = "dalvik.vm.dex2oat-minidebuginfo"; +static constexpr bool kMinidebugInfoSystemPropertyDefault = false; +static constexpr const char* kMinidebugDex2oatFlag = "--generate-mini-debug-info"; +static constexpr const char* kDisableCompactDexFlag = "--compact-dex-level=none"; + +// Location of the JIT Zygote image. +static const char* kJitZygoteImage = + "boot.art:/nonx/boot-framework.art!/system/etc/boot-image.prof"; + +std::vector<std::string> SplitBySpaces(const std::string& str) { + if (str.empty()) { + return {}; + } + return android::base::Split(str, " "); +} + +} // namespace + +RunDex2Oat::RunDex2Oat(const char* dex2oat_bin, ExecVHelper* execv_helper) + : dex2oat_bin_(dex2oat_bin), execv_helper_(execv_helper) {} + +void RunDex2Oat::Initialize(const UniqueFile& output_oat, + const UniqueFile& output_vdex, + const UniqueFile& output_image, + const UniqueFile& input_dex, + const UniqueFile& input_vdex, + const UniqueFile& dex_metadata, + const UniqueFile& profile, + const char* class_loader_context, + const std::string& class_loader_context_fds, + int swap_fd, + const char* instruction_set, + const char* compiler_filter, + bool debuggable, + bool post_bootcomplete, + bool for_restore, + int target_sdk_version, + bool enable_hidden_api_checks, + bool generate_compact_dex, + bool use_jitzygote_image, + const char* compilation_reason) { + PrepareBootImageAndBootClasspathFlags(use_jitzygote_image); + + PrepareInputFileFlags(output_oat, output_vdex, output_image, input_dex, input_vdex, + dex_metadata, profile, swap_fd, class_loader_context, + class_loader_context_fds); + + PrepareCompilerConfigFlags(input_vdex, output_vdex, instruction_set, compiler_filter, + debuggable, target_sdk_version, enable_hidden_api_checks, + generate_compact_dex, compilation_reason); + + PrepareCompilerRuntimeAndPerfConfigFlags(post_bootcomplete, for_restore); + + const std::string dex2oat_flags = GetProperty("dalvik.vm.dex2oat-flags", ""); + std::vector<std::string> dex2oat_flags_args = SplitBySpaces(dex2oat_flags); + ALOGV("dalvik.vm.dex2oat-flags=%s\n", dex2oat_flags.c_str()); + + // Do not add args after dex2oat_flags, they should override others for debugging. + for (auto it = dex2oat_flags_args.begin(); it != dex2oat_flags_args.end(); ++it) { + AddArg(*it); + } + + execv_helper_->PrepareArgs(dex2oat_bin_); +} + +RunDex2Oat::~RunDex2Oat() {} + +void RunDex2Oat::PrepareBootImageAndBootClasspathFlags(bool use_jitzygote_image) { + std::string boot_image; + if (use_jitzygote_image) { + boot_image = StringPrintf("--boot-image=%s", kJitZygoteImage); + } else { + boot_image = MapPropertyToArg("dalvik.vm.boot-image", "--boot-image=%s"); + } + AddArg(boot_image); + + // If DEX2OATBOOTCLASSPATH is not in the environment, dex2oat is going to query + // BOOTCLASSPATH. + char* dex2oat_bootclasspath = getenv("DEX2OATBOOTCLASSPATH"); + if (dex2oat_bootclasspath != nullptr) { + AddRuntimeArg(StringPrintf("-Xbootclasspath:%s", dex2oat_bootclasspath)); + } + + std::string updatable_bcp_packages = + MapPropertyToArg("dalvik.vm.dex2oat-updatable-bcp-packages-file", + "--updatable-bcp-packages-file=%s"); + if (updatable_bcp_packages.empty()) { + // Make dex2oat fail by providing non-existent file name. + updatable_bcp_packages = + "--updatable-bcp-packages-file=/nonx/updatable-bcp-packages.txt"; + } + AddArg(updatable_bcp_packages); +} + +void RunDex2Oat::PrepareInputFileFlags(const UniqueFile& output_oat, + const UniqueFile& output_vdex, + const UniqueFile& output_image, + const UniqueFile& input_dex, + const UniqueFile& input_vdex, + const UniqueFile& dex_metadata, + const UniqueFile& profile, + int swap_fd, + const char* class_loader_context, + const std::string& class_loader_context_fds) { + std::string input_basename = Basename(input_dex.path()); + LOG(VERBOSE) << "Running " << dex2oat_bin_ << " in=" << input_basename << " out=" + << output_oat.path(); + + AddArg(StringPrintf("--zip-fd=%d", input_dex.fd())); + AddArg(StringPrintf("--zip-location=%s", input_basename.c_str())); + AddArg(StringPrintf("--oat-fd=%d", output_oat.fd())); + AddArg(StringPrintf("--oat-location=%s", output_oat.path().c_str())); + AddArg(StringPrintf("--input-vdex-fd=%d", input_vdex.fd())); + AddArg(StringPrintf("--output-vdex-fd=%d", output_vdex.fd())); + + if (output_image.fd() >= 0) { + AddArg(StringPrintf("--app-image-fd=%d", output_image.fd())); + AddArg(MapPropertyToArg("dalvik.vm.appimageformat", "--image-format=%s")); + } + if (dex_metadata.fd() > -1) { + AddArg("--dm-fd=" + std::to_string(dex_metadata.fd())); + } + if (profile.fd() != -1) { + AddArg(StringPrintf("--profile-file-fd=%d", profile.fd())); + } + if (swap_fd >= 0) { + AddArg(StringPrintf("--swap-fd=%d", swap_fd)); + } + + // Get the directory of the apk to pass as a base classpath directory. + { + std::string apk_dir(input_dex.path()); + size_t dir_index = apk_dir.rfind('/'); + if (dir_index != std::string::npos) { + apk_dir = apk_dir.substr(0, dir_index); + AddArg(StringPrintf("--classpath-dir=%s", apk_dir.c_str())); + } + } + + if (class_loader_context != nullptr) { + AddArg(StringPrintf("--class-loader-context=%s", class_loader_context)); + if (!class_loader_context_fds.empty()) { + AddArg(StringPrintf("--class-loader-context-fds=%s", + class_loader_context_fds.c_str())); + } + } +} + +void RunDex2Oat::PrepareCompilerConfigFlags(const UniqueFile& input_vdex, + const UniqueFile& output_vdex, + const char* instruction_set, + const char* compiler_filter, + bool debuggable, + int target_sdk_version, + bool enable_hidden_api_checks, + bool generate_compact_dex, + const char* compilation_reason) { + // Disable cdex if update input vdex is true since this combination of options is not + // supported. + const bool disable_cdex = !generate_compact_dex || (input_vdex.fd() == output_vdex.fd()); + if (disable_cdex) { + AddArg(kDisableCompactDexFlag); + } + + // ISA related + { + AddArg(StringPrintf("--instruction-set=%s", instruction_set)); + + const std::string dex2oat_isa_features_key = + StringPrintf("dalvik.vm.isa.%s.features", instruction_set); + std::string instruction_set_features_arg = + MapPropertyToArg(dex2oat_isa_features_key, "--instruction-set-features=%s"); + AddArg(instruction_set_features_arg); + + const std::string dex2oat_isa_variant_key = + StringPrintf("dalvik.vm.isa.%s.variant", instruction_set); + std::string instruction_set_variant_arg = + MapPropertyToArg(dex2oat_isa_variant_key, "--instruction-set-variant=%s"); + AddArg(instruction_set_variant_arg); + } + + // Compute compiler filter. + { + std::string dex2oat_compiler_filter_arg; + { + // If we are booting without the real /data, don't spend time compiling. + std::string vold_decrypt = GetProperty("vold.decrypt", ""); + bool skip_compilation = vold_decrypt == "trigger_restart_min_framework" || + vold_decrypt == "1"; + + bool have_dex2oat_relocation_skip_flag = false; + if (skip_compilation) { + dex2oat_compiler_filter_arg = "--compiler-filter=extract"; + have_dex2oat_relocation_skip_flag = true; + } else if (compiler_filter != nullptr) { + dex2oat_compiler_filter_arg = StringPrintf("--compiler-filter=%s", + compiler_filter); + } + if (have_dex2oat_relocation_skip_flag) { + AddRuntimeArg("-Xnorelocate"); + } + } + + if (dex2oat_compiler_filter_arg.empty()) { + dex2oat_compiler_filter_arg = MapPropertyToArg("dalvik.vm.dex2oat-filter", + "--compiler-filter=%s"); + } + AddArg(dex2oat_compiler_filter_arg); + + if (compilation_reason != nullptr) { + AddArg(std::string("--compilation-reason=") + compilation_reason); + } + } + + AddArg(MapPropertyToArg("dalvik.vm.dex2oat-max-image-block-size", + "--max-image-block-size=%s")); + + AddArg(MapPropertyToArg("dalvik.vm.dex2oat-very-large", + "--very-large-app-threshold=%s")); + + std::string resolve_startup_string_arg = MapPropertyToArg( + "persist.device_config.runtime.dex2oat_resolve_startup_strings", + "--resolve-startup-const-strings=%s"); + if (resolve_startup_string_arg.empty()) { + // If empty, fall back to system property. + resolve_startup_string_arg = + MapPropertyToArg("dalvik.vm.dex2oat-resolve-startup-strings", + "--resolve-startup-const-strings=%s"); + } + AddArg(resolve_startup_string_arg); + + // Debug related + { + // Check whether all apps should be compiled debuggable. + if (!debuggable) { + debuggable = GetProperty("dalvik.vm.always_debuggable", "") == "1"; + } + if (debuggable) { + AddArg("--debuggable"); + } + + const bool generate_debug_info = GetBoolProperty("debug.generate-debug-info", false); + if (generate_debug_info) { + AddArg("--generate-debug-info"); + } + { + bool generate_minidebug_info = kEnableMinidebugInfo && + GetBoolProperty(kMinidebugInfoSystemProperty, + kMinidebugInfoSystemPropertyDefault); + if (generate_minidebug_info) { + AddArg(kMinidebugDex2oatFlag); + } + } + } + + if (target_sdk_version != 0) { + AddRuntimeArg(StringPrintf("-Xtarget-sdk-version:%d", target_sdk_version)); + } + + if (enable_hidden_api_checks) { + AddRuntimeArg("-Xhidden-api-policy:enabled"); + } +} + +void RunDex2Oat::PrepareCompilerRuntimeAndPerfConfigFlags(bool post_bootcomplete, + bool for_restore) { + // CPU set + { + std::string cpu_set_format = "--cpu-set=%s"; + std::string dex2oat_cpu_set_arg = post_bootcomplete + ? (for_restore + ? MapPropertyToArgWithBackup( + "dalvik.vm.restore-dex2oat-cpu-set", + "dalvik.vm.dex2oat-cpu-set", + cpu_set_format) + : MapPropertyToArg("dalvik.vm.dex2oat-cpu-set", cpu_set_format)) + : MapPropertyToArg("dalvik.vm.boot-dex2oat-cpu-set", cpu_set_format); + AddArg(dex2oat_cpu_set_arg); + } + + // Number of threads + { + std::string threads_format = "-j%s"; + std::string dex2oat_threads_arg = post_bootcomplete + ? (for_restore + ? MapPropertyToArgWithBackup( + "dalvik.vm.restore-dex2oat-threads", + "dalvik.vm.dex2oat-threads", + threads_format) + : MapPropertyToArg("dalvik.vm.dex2oat-threads", threads_format)) + : MapPropertyToArg("dalvik.vm.boot-dex2oat-threads", threads_format); + AddArg(dex2oat_threads_arg); + } + + AddRuntimeArg(MapPropertyToArg("dalvik.vm.dex2oat-Xms", "-Xms%s")); + AddRuntimeArg(MapPropertyToArg("dalvik.vm.dex2oat-Xmx", "-Xmx%s")); +} + +void RunDex2Oat::Exec(int exit_code) { + execv_helper_->Exec(exit_code); +} + +void RunDex2Oat::AddArg(const std::string& arg) { + execv_helper_->AddArg(arg); +} + +void RunDex2Oat::AddRuntimeArg(const std::string& arg) { + execv_helper_->AddRuntimeArg(arg); +} + +std::string RunDex2Oat::GetProperty(const std::string& key, + const std::string& default_value) { + return android::base::GetProperty(key, default_value); +} + +bool RunDex2Oat::GetBoolProperty(const std::string& key, bool default_value) { + return android::base::GetBoolProperty(key, default_value); +} + +std::string RunDex2Oat::MapPropertyToArg(const std::string& property, + const std::string& format, + const std::string& default_value) { + std::string prop = GetProperty(property, default_value); + if (!prop.empty()) { + return StringPrintf(format.c_str(), prop.c_str()); + } + return ""; +} + +std::string RunDex2Oat::MapPropertyToArgWithBackup( + const std::string& property, + const std::string& backupProperty, + const std::string& format, + const std::string& default_value) { + std::string value = GetProperty(property, default_value); + if (!value.empty()) { + return StringPrintf(format.c_str(), value.c_str()); + } + return MapPropertyToArg(backupProperty, format, default_value); +} + +} // namespace installd +} // namespace android diff --git a/cmds/installd/run_dex2oat.h b/cmds/installd/run_dex2oat.h new file mode 100644 index 0000000000..325a3a2599 --- /dev/null +++ b/cmds/installd/run_dex2oat.h @@ -0,0 +1,104 @@ +/* + * 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. + */ + +#ifndef ANDROID_INSTALLD_RUN_DEX2OAT_H +#define ANDROID_INSTALLD_RUN_DEX2OAT_H + +#include <memory> +#include <string> + +#include "execv_helper.h" + +namespace android { +namespace installd { + +class UniqueFile; + +class RunDex2Oat { + public: + explicit RunDex2Oat(const char* dex2oat_bin, ExecVHelper* execv_helper); + virtual ~RunDex2Oat(); + + void Initialize(const UniqueFile& output_oat, + const UniqueFile& output_vdex, + const UniqueFile& output_image, + const UniqueFile& input_dex, + const UniqueFile& input_vdex, + const UniqueFile& dex_metadata, + const UniqueFile& profile, + const char* class_loader_context, + const std::string& class_loader_context_fds, + int swap_fd, + const char* instruction_set, + const char* compiler_filter, + bool debuggable, + bool post_bootcomplete, + bool for_restore, + int target_sdk_version, + bool enable_hidden_api_checks, + bool generate_compact_dex, + bool use_jitzygote_image, + const char* compilation_reason); + + void Exec(int exit_code); + + protected: + void PrepareBootImageAndBootClasspathFlags(bool use_jitzygote_image); + void PrepareInputFileFlags(const UniqueFile& output_oat, + const UniqueFile& output_vdex, + const UniqueFile& output_image, + const UniqueFile& input_dex, + const UniqueFile& input_vdex, + const UniqueFile& dex_metadata, + const UniqueFile& profile, + int swap_fd, + const char* class_loader_context, + const std::string& class_loader_context_fds); + void PrepareCompilerConfigFlags(const UniqueFile& input_vdex, + const UniqueFile& output_vdex, + const char* instruction_set, + const char* compiler_filter, + bool debuggable, + int target_sdk_version, + bool enable_hidden_api_checks, + bool generate_compact_dex, + const char* compilation_reason); + void PrepareCompilerRuntimeAndPerfConfigFlags(bool post_bootcomplete, bool for_restore); + + virtual std::string GetProperty(const std::string& key, const std::string& default_value); + virtual bool GetBoolProperty(const std::string& key, bool default_value); + + private: + void AddArg(const std::string& arg); + void AddRuntimeArg(const std::string& arg); + + std::string MapPropertyToArg(const std::string& property, + const std::string& format, + const std::string& default_value = ""); + + std::string MapPropertyToArgWithBackup(const std::string& property, + const std::string& backupProperty, + const std::string& format, + const std::string& default_value = ""); + + const std::string dex2oat_bin_; + ExecVHelper* execv_helper_; // not owned +}; + +} // namespace installd +} // namespace android + +#endif // ANDROID_INSTALLD_RUN_DEX2OAT_H diff --git a/cmds/installd/run_dex2oat_test.cpp b/cmds/installd/run_dex2oat_test.cpp new file mode 100644 index 0000000000..3813cf7c09 --- /dev/null +++ b/cmds/installd/run_dex2oat_test.cpp @@ -0,0 +1,585 @@ +/* + * 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 <map> +#include <memory> +#include <string> + +#include <android-base/logging.h> + +#include <gtest/gtest.h> + +#include "execv_helper.h" +#include "run_dex2oat.h" +#include "unique_file.h" + +namespace android { +namespace installd { + +class RunDex2OatTest : public testing::Test { + public: + static constexpr const char* INPUT_PATH = "/dir/input/basename.apk"; + static constexpr const char* OUTPUT_PATH = "/dir/output/basename.oat"; + static constexpr const char* FLAG_UNUSED = "{{FLAG_UNUSED}}"; + + // UniqueFile closes FD. Avoid using standard I/O since the test is expected to print gtest + // results. Alternatively, mock out UniqueFile to avoid the side effect of close(2). + static constexpr int ZIP_FD = 3; + static constexpr int OAT_FD = 4; + static constexpr int INPUT_VDEX_FD = 5; + static constexpr int OUTPUT_VDEX_FD = 6; + static constexpr int IMAGE_FD = 7; + static constexpr int PROFILE_FD = 8; + static constexpr int DEX_METADATA_FD = 9; + static constexpr int SWAP_FD = 10; + + using FakeSystemProperties = std::map<std::string, std::string>; + + // A fake RunDex2Oat that allows to override (fake) system properties and starts with none. + class FakeRunDex2Oat : public RunDex2Oat { + private: + static constexpr const char* TRUE_STR = "true"; + static constexpr const char* FALSE_STR = "false"; + + public: + FakeRunDex2Oat(ExecVHelper* execv_helper, FakeSystemProperties* properties) + : RunDex2Oat("/dir/bin/dex2oat", execv_helper), properties_(properties) { } + + virtual ~FakeRunDex2Oat() {} + + virtual std::string GetProperty(const std::string& key, + const std::string& default_value) override { + if (!properties_) { + return default_value; + } + auto iter = properties_->find(key); + if (iter == properties_->end()) { + return default_value; + } + return iter->second; + } + + virtual bool GetBoolProperty(const std::string& key, bool default_value) override { + std::string value = GetProperty(key, ""); + if (value == "") { + return default_value; + } + return value == TRUE_STR; + } + + private: + FakeSystemProperties* properties_; + }; + + struct RunDex2OatArgs { + static std::unique_ptr<RunDex2OatArgs> MakeDefaultTestArgs() { + auto args = std::make_unique<RunDex2OatArgs>(); + args->input_dex.reset(ZIP_FD, INPUT_PATH); + args->output_oat.reset(OAT_FD, OUTPUT_PATH); + args->input_vdex.reset(INPUT_VDEX_FD, "UNUSED_PATH"); + args->output_vdex.reset(OUTPUT_VDEX_FD, "UNUSED_PATH"); + args->instruction_set = "arm64"; + args->compilation_reason = "rundex2oattest"; + return args; + } + + UniqueFile output_oat; + UniqueFile output_vdex; + UniqueFile output_image; + UniqueFile input_dex; + UniqueFile input_vdex; + UniqueFile dex_metadata; + UniqueFile profile; + int swap_fd = -1; + const char* instruction_set = nullptr; + const char* compiler_filter = "extract"; + bool debuggable = false; + bool post_bootcomplete = false; + bool for_restore = false; + const char* class_loader_context = nullptr; + std::string class_loader_context_fds; + int target_sdk_version = 0; + bool enable_hidden_api_checks = false; + bool generate_compact_dex = true; + bool use_jitzygote_image = false; + const char* compilation_reason = nullptr; + }; + + class FakeExecVHelper : public ExecVHelper { + public: + bool HasArg(const std::string& arg) const { + auto end = argv_.end() - 1; // To exclude the terminating nullptr + return find(argv_.begin(), end, arg) != end; + } + + bool FlagNotUsed(const std::string& flag) const { + auto has_prefix = [flag](const char* arg) { + return strncmp(arg, flag.c_str(), flag.size()) == 0; + }; + auto end = argv_.end() - 1; // To exclude the terminating nullptr + return find_if(argv_.begin(), end, has_prefix) == end; + } + + virtual void Exec(int exit_code) override { + std::string cmd; + for (auto arg : argv_) { + if (arg == nullptr) { + continue; + } + cmd += arg; + cmd += " "; + } + LOG(DEBUG) << "FakeExecVHelper exit_code: " << exit_code << " cmd: " << cmd << "\n"; + } + }; + + virtual void SetUp() override { + execv_helper_.reset(new FakeExecVHelper()); + system_properties_.clear(); + initializeDefaultExpectedFlags(); + } + + // Initializes the default flags expected to a run. It currently matches to the expected flags + // with RunDex2OatArgs::MakeDefaultTestArgs. + // + // default_expected_flags_ defines a mapping of <flag_name, expected_value>, where flag_name is + // something like "--flag-name", and expected_value can be "=value" or ":value" (depending on + // its delimiter), "" (if no value is needed), or a special value of FLAG_UNUSED to indicates + // that it should not be used. + void initializeDefaultExpectedFlags() { + default_expected_flags_.clear(); + + // Files + default_expected_flags_["--zip-fd"] = "=" + std::to_string(ZIP_FD); + default_expected_flags_["--zip-location"] = "=basename.apk"; + default_expected_flags_["--oat-fd"] = "=" + std::to_string(OAT_FD); + default_expected_flags_["--oat-location"] = "=" + std::string(OUTPUT_PATH); + default_expected_flags_["--input-vdex-fd"] = "=" + std::to_string(INPUT_VDEX_FD); + default_expected_flags_["--output-vdex-fd"] = "=" + std::to_string(OUTPUT_VDEX_FD); + default_expected_flags_["--classpath-dir"] = "=/dir/input"; + default_expected_flags_["--app-image-fd"] = FLAG_UNUSED; + default_expected_flags_["--profile-file-fd"] = FLAG_UNUSED; + default_expected_flags_["--swap-fd"] = FLAG_UNUSED; + default_expected_flags_["--class-loader-context"] = FLAG_UNUSED; + default_expected_flags_["--class-loader-context-fds"] = FLAG_UNUSED; + default_expected_flags_["--updatable-bcp-packages-file"] = + "=/nonx/updatable-bcp-packages.txt"; + + // Arch + default_expected_flags_["--instruction-set"] = "=arm64"; + default_expected_flags_["--instruction-set-features"] = FLAG_UNUSED; + default_expected_flags_["--instruction-set-variant"] = FLAG_UNUSED; + default_expected_flags_["--cpu-set"] = FLAG_UNUSED; + + // Misc + default_expected_flags_["--compiler-filter"] = "=extract"; + default_expected_flags_["--compilation-reason"] = "=rundex2oattest"; + default_expected_flags_["--compact-dex-level"] = FLAG_UNUSED; + default_expected_flags_["-j"] = FLAG_UNUSED; + default_expected_flags_["--max-image-block-size"] = FLAG_UNUSED; + default_expected_flags_["--very-large-app-threshold"] = FLAG_UNUSED; + default_expected_flags_["--resolve-startup-const-strings"] = FLAG_UNUSED; + + // Debug + default_expected_flags_["--debuggable"] = FLAG_UNUSED; + default_expected_flags_["--generate-debug-info"] = FLAG_UNUSED; + default_expected_flags_["--generate-mini-debug-info"] = FLAG_UNUSED; + + // Runtime + // TODO(victorhsieh): Check if the previous flag is actually --runtime-arg. + default_expected_flags_["-Xms"] = FLAG_UNUSED; + default_expected_flags_["-Xmx"] = FLAG_UNUSED; + default_expected_flags_["-Xbootclasspath"] = FLAG_UNUSED; + default_expected_flags_["-Xtarget-sdk-version"] = FLAG_UNUSED; + default_expected_flags_["-Xhidden-api-policy"] = FLAG_UNUSED; + default_expected_flags_["-Xnorelocate"] = FLAG_UNUSED; + + // Test only + default_expected_flags_["--foo"] = FLAG_UNUSED; + default_expected_flags_["--bar"] = FLAG_UNUSED; + default_expected_flags_["--baz"] = FLAG_UNUSED; + } + + void SetExpectedFlagUsed(const std::string& flag, const std::string& value) { + auto iter = default_expected_flags_.find(flag); + ASSERT_NE(iter, default_expected_flags_.end()) << "Must define the default value"; + iter->second = value; + } + + void VerifyExpectedFlags() { + for (auto const& [flag, value] : default_expected_flags_) { + if (value == FLAG_UNUSED) { + EXPECT_TRUE(execv_helper_->FlagNotUsed(flag)) + << "Flag " << flag << " should be unused, but got the value " << value; + } else if (value == "") { + EXPECT_TRUE(execv_helper_->HasArg(flag)) + << "Flag " << flag << " should be specified without value, but got " << value; + } else { + EXPECT_TRUE(execv_helper_->HasArg(flag + value)) + << "Flag " << flag << value << " is not specificed"; + } + } + } + + void setSystemProperty(const std::string& key, const std::string& value) { + system_properties_[key] = value; + } + + void CallRunDex2Oat(std::unique_ptr<RunDex2OatArgs> args) { + FakeRunDex2Oat runner(execv_helper_.get(), &system_properties_); + runner.Initialize(args->output_oat, + args->output_vdex, + args->output_image, + args->input_dex, + args->input_vdex, + args->dex_metadata, + args->profile, + args->class_loader_context, + args->class_loader_context_fds, + args->swap_fd, + args->instruction_set, + args->compiler_filter, + args->debuggable, + args->post_bootcomplete, + args->for_restore, + args->target_sdk_version, + args->enable_hidden_api_checks, + args->generate_compact_dex, + args->use_jitzygote_image, + args->compilation_reason); + runner.Exec(/*exit_code=*/ 0); + } + + private: + std::unique_ptr<FakeExecVHelper> execv_helper_; + std::map<std::string, std::string> default_expected_flags_; + FakeSystemProperties system_properties_; +}; + +TEST_F(RunDex2OatTest, BasicInputOutput) { + auto execv_helper = std::make_unique<FakeExecVHelper>(); + CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); + + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, WithAllOtherInputFds) { + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->output_image.reset(IMAGE_FD, "UNUSED_PATH"); + args->profile.reset(PROFILE_FD, "UNUSED_PATH"); + args->swap_fd = SWAP_FD; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--app-image-fd", "=" + std::to_string(IMAGE_FD)); + SetExpectedFlagUsed("--profile-file-fd", "=" + std::to_string(PROFILE_FD)); + SetExpectedFlagUsed("--swap-fd", "=" + std::to_string(SWAP_FD)); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, WithClassLoaderContext) { + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->class_loader_context = "CLASS_LOADER_CONTEXT"; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--class-loader-context", "=CLASS_LOADER_CONTEXT"); + SetExpectedFlagUsed("--class-loader-context-fds", FLAG_UNUSED); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, WithClassLoaderContextAndFds) { + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->class_loader_context = "CLASS_LOADER_CONTEXT"; + args->class_loader_context_fds = "CLASS_LOADER_CONTEXT_FDS"; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--class-loader-context", "=CLASS_LOADER_CONTEXT"); + SetExpectedFlagUsed("--class-loader-context-fds", "=CLASS_LOADER_CONTEXT_FDS"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, WithOnlyClassLoaderContextFds) { + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->class_loader_context_fds = "CLASS_LOADER_CONTEXT_FDS"; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--class-loader-context", FLAG_UNUSED); + SetExpectedFlagUsed("--class-loader-context-fds", FLAG_UNUSED); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, DEX2OATBOOTCLASSPATH) { + ASSERT_EQ(nullptr, getenv("DEX2OATBOOTCLASSPATH")); + ASSERT_EQ(0, setenv("DEX2OATBOOTCLASSPATH", "foobar", /*override=*/ false)) + << "Failed to setenv: " << strerror(errno); + + CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); + + SetExpectedFlagUsed("-Xbootclasspath", ":foobar"); + VerifyExpectedFlags(); + + ASSERT_EQ(0, unsetenv("DEX2OATBOOTCLASSPATH")) + << "Failed to setenv: " << strerror(errno); +} + +TEST_F(RunDex2OatTest, UpdatableBootClassPath) { + setSystemProperty("dalvik.vm.dex2oat-updatable-bcp-packages-file", "/path/to/file"); + CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); + + SetExpectedFlagUsed("--updatable-bcp-packages-file", "=/path/to/file"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, DoNotGenerateCompactDex) { + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->generate_compact_dex = false; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--compact-dex-level", "=none"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, DoNotGenerateCompactDexWithVdexInPlaceUpdate) { + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->generate_compact_dex = true; + args->input_vdex.reset(INPUT_VDEX_FD, "UNUSED_PATH"); + args->output_vdex.reset(INPUT_VDEX_FD, "UNUSED_PATH"); + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--compact-dex-level", "=none"); + SetExpectedFlagUsed("--output-vdex-fd", "=" + std::to_string(INPUT_VDEX_FD)); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, ISA) { + setSystemProperty("dalvik.vm.isa.x86.features", "a-x86-feature"); + setSystemProperty("dalvik.vm.isa.x86.variant", "a-x86-variant"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->instruction_set = "x86"; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--instruction-set", "=x86"); + SetExpectedFlagUsed("--instruction-set-features", "=a-x86-feature"); + SetExpectedFlagUsed("--instruction-set-variant", "=a-x86-variant"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, CpuSetPreBootComplete) { + setSystemProperty("dalvik.vm.boot-dex2oat-cpu-set", "1,2"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->post_bootcomplete = false; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--cpu-set", "=1,2"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, CpuSetPostBootCompleteNotForRestore) { + setSystemProperty("dalvik.vm.dex2oat-cpu-set", "1,2"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->post_bootcomplete = true; + args->for_restore = false; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--cpu-set", "=1,2"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, CpuSetPostBootCompleteForRestore) { + setSystemProperty("dalvik.vm.restore-dex2oat-cpu-set", "1,2"); + setSystemProperty("dalvik.vm.dex2oat-cpu-set", "2,3"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->post_bootcomplete = true; + args->for_restore = true; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--cpu-set", "=1,2"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, CpuSetPostBootCompleteForRestore_Backup) { + setSystemProperty("dalvik.vm.restore-dex2oat-cpu-set", ""); + setSystemProperty("dalvik.vm.dex2oat-cpu-set", "1,2"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->post_bootcomplete = true; + args->for_restore = true; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--cpu-set", "=1,2"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, Runtime) { + setSystemProperty("dalvik.vm.dex2oat-Xms", "1234m"); + setSystemProperty("dalvik.vm.dex2oat-Xmx", "5678m"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->target_sdk_version = 30; + args->enable_hidden_api_checks = true; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("-Xms", "1234m"); + SetExpectedFlagUsed("-Xmx", "5678m"); + SetExpectedFlagUsed("-Xtarget-sdk-version", ":30"); + SetExpectedFlagUsed("-Xhidden-api-policy", ":enabled"); + SetExpectedFlagUsed("-Xnorelocate", FLAG_UNUSED); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, SkipRelocationInMinFramework) { + setSystemProperty("vold.decrypt", "trigger_restart_min_framework"); + CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); + + SetExpectedFlagUsed("--compiler-filter", "=extract"); + SetExpectedFlagUsed("-Xnorelocate", ""); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, SkipRelocationIfDecryptedWithFullDiskEncryption) { + setSystemProperty("vold.decrypt", "1"); + CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); + + SetExpectedFlagUsed("--compiler-filter", "=extract"); + SetExpectedFlagUsed("-Xnorelocate", ""); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, DalvikVmDex2oatFilter) { + setSystemProperty("dalvik.vm.dex2oat-filter", "speed"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->compiler_filter = nullptr; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--compiler-filter", "=speed"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, ResolveStartupStartings) { + setSystemProperty("dalvik.vm.dex2oat-resolve-startup-strings", "false"); + CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); + + SetExpectedFlagUsed("--resolve-startup-const-strings", "=false"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, ResolveStartupStartingsOverride) { + setSystemProperty("dalvik.vm.dex2oat-resolve-startup-strings", "false"); + setSystemProperty("persist.device_config.runtime.dex2oat_resolve_startup_strings", "true"); + CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); + + SetExpectedFlagUsed("--resolve-startup-const-strings", "=true"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, ThreadsPreBootComplete) { + setSystemProperty("dalvik.vm.boot-dex2oat-threads", "2"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->post_bootcomplete = false; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("-j", "2"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, ThreadsPostBootCompleteNotForRestore) { + setSystemProperty("dalvik.vm.dex2oat-threads", "3"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->post_bootcomplete = true; + args->for_restore = false; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("-j", "3"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, ThreadsPostBootCompleteForRestore) { + setSystemProperty("dalvik.vm.restore-dex2oat-threads", "4"); + setSystemProperty("dalvik.vm.dex2oat-threads", "5"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->post_bootcomplete = true; + args->for_restore = true; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("-j", "4"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, ThreadsPostBootCompleteForRestore_Backup) { + setSystemProperty("dalvik.vm.restore-dex2oat-threads", ""); + setSystemProperty("dalvik.vm.dex2oat-threads", "5"); + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->post_bootcomplete = true; + args->for_restore = true; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("-j", "5"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, Debuggable) { + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->debuggable = true; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("--debuggable", ""); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, AlwaysDebuggable) { + setSystemProperty("dalvik.vm.always_debuggable", "1"); + CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); + + SetExpectedFlagUsed("--debuggable", ""); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, GenerateDebugInfo) { + setSystemProperty("debug.generate-debug-info", "true"); + CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); + + SetExpectedFlagUsed("--generate-debug-info", ""); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, HiddenApiCheck) { + auto args = RunDex2OatArgs::MakeDefaultTestArgs(); + args->enable_hidden_api_checks = true; + CallRunDex2Oat(std::move(args)); + + SetExpectedFlagUsed("-Xhidden-api-policy", ":enabled"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, Misc) { + setSystemProperty("dalvik.vm.dex2oat-max-image-block-size", "524288"); + setSystemProperty("dalvik.vm.dex2oat-very-large", "100000"); + CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); + + SetExpectedFlagUsed("--max-image-block-size", "=524288"); + SetExpectedFlagUsed("--very-large-app-threshold", "=100000"); + VerifyExpectedFlags(); +} + +TEST_F(RunDex2OatTest, ExtraFlags) { + setSystemProperty("dalvik.vm.dex2oat-flags", "--foo=123 --bar:456 --baz"); + CallRunDex2Oat(RunDex2OatArgs::MakeDefaultTestArgs()); + + SetExpectedFlagUsed("--foo", "=123"); + SetExpectedFlagUsed("--bar", ":456"); + SetExpectedFlagUsed("--baz", ""); + VerifyExpectedFlags(); +} + +} // namespace installd +} // namespace android diff --git a/cmds/installd/run_dex2oat_test.xml b/cmds/installd/run_dex2oat_test.xml new file mode 100644 index 0000000000..2467fe4e83 --- /dev/null +++ b/cmds/installd/run_dex2oat_test.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> +<configuration description="Unittest of run_dex2oat"> + <option name="test-suite-tag" value="apct" /> + <option name="test-suite-tag" value="apct-native" /> + + <option name="null-device" value="true" /> + <test class="com.android.tradefed.testtype.HostGTest" > + <option name="module-name" value="run_dex2oat_test" /> + </test> +</configuration> diff --git a/cmds/installd/tests/installd_cache_test.cpp b/cmds/installd/tests/installd_cache_test.cpp index 5a5cb53431..863cdfe55b 100644 --- a/cmds/installd/tests/installd_cache_test.cpp +++ b/cmds/installd/tests/installd_cache_test.cpp @@ -114,15 +114,14 @@ static void setxattr(const char* path, const char* key) { class CacheTest : public testing::Test { protected: InstalldNativeService* service; - std::unique_ptr<std::string> testUuid; + std::optional<std::string> testUuid; virtual void SetUp() { setenv("ANDROID_LOG_TAGS", "*:v", 1); android::base::InitLogging(nullptr); service = new InstalldNativeService(); - testUuid = std::make_unique<std::string>(); - *testUuid = std::string(kTestUuid); + testUuid = kTestUuid; system("mkdir -p /data/local/tmp/user/0"); } diff --git a/cmds/installd/tests/installd_dexopt_test.cpp b/cmds/installd/tests/installd_dexopt_test.cpp index 16e4055faa..fbf1e0c4b6 100644 --- a/cmds/installd/tests/installd_dexopt_test.cpp +++ b/cmds/installd/tests/installd_dexopt_test.cpp @@ -193,7 +193,7 @@ protected: const uid_t kTestAppGid = multiuser_get_shared_gid(kTestUserId, kTestAppId); InstalldNativeService* service_; - std::unique_ptr<std::string> volume_uuid_; + std::optional<std::string> volume_uuid_; std::string package_name_; std::string apk_path_; std::string empty_dm_file_; @@ -221,7 +221,7 @@ protected: ASSERT_TRUE(init_selinux()); service_ = new InstalldNativeService(); - volume_uuid_ = nullptr; + volume_uuid_ = std::nullopt; package_name_ = "com.installd.test.dexopt"; se_info_ = "default"; app_apk_dir_ = android_app_dir + package_name_; @@ -294,7 +294,7 @@ protected: } // Create a secondary dex file on CE storage - const char* volume_uuid_cstr = volume_uuid_ == nullptr ? nullptr : volume_uuid_->c_str(); + const char* volume_uuid_cstr = volume_uuid_ ? volume_uuid_->c_str() : nullptr; app_private_dir_ce_ = create_data_user_ce_package_path( volume_uuid_cstr, kTestUserId, package_name_.c_str()); secondary_dex_ce_ = app_private_dir_ce_ + "/secondary_ce.jar"; @@ -353,36 +353,32 @@ protected: if (class_loader_context == nullptr) { class_loader_context = "&"; } - std::unique_ptr<std::string> package_name_ptr(new std::string(package_name_)); int32_t dexopt_needed = 0; // does not matter; - std::unique_ptr<std::string> out_path = nullptr; // does not matter + std::optional<std::string> out_path; // does not matter int32_t dex_flags = DEXOPT_SECONDARY_DEX | dex_storage_flag; std::string compiler_filter = "speed-profile"; - std::unique_ptr<std::string> class_loader_context_ptr( - new std::string(class_loader_context)); - std::unique_ptr<std::string> se_info_ptr(new std::string(se_info_)); bool downgrade = false; int32_t target_sdk_version = 0; // default - std::unique_ptr<std::string> profile_name_ptr = nullptr; - std::unique_ptr<std::string> dm_path_ptr = nullptr; - std::unique_ptr<std::string> compilation_reason_ptr = nullptr; + std::optional<std::string> profile_name; + std::optional<std::string> dm_path; + std::optional<std::string> compilation_reason; binder::Status result = service_->dexopt(path, uid, - package_name_ptr, + package_name_, kRuntimeIsa, dexopt_needed, out_path, dex_flags, compiler_filter, volume_uuid_, - class_loader_context_ptr, - se_info_ptr, + class_loader_context, + se_info_, downgrade, target_sdk_version, - profile_name_ptr, - dm_path_ptr, - compilation_reason_ptr); + profile_name, + dm_path, + compilation_reason); ASSERT_EQ(should_binder_call_succeed, result.isOk()) << result.toString8().c_str(); int expected_access = should_dex_be_compiled ? 0 : -1; std::string odex = GetSecondaryDexArtifact(path, "odex"); @@ -481,41 +477,35 @@ protected: bool downgrade, bool should_binder_call_succeed, /*out */ binder::Status* binder_result) { - std::unique_ptr<std::string> package_name_ptr(new std::string(package_name_)); - std::unique_ptr<std::string> out_path( - oat_dir == nullptr ? nullptr : new std::string(oat_dir)); - std::unique_ptr<std::string> class_loader_context_ptr(new std::string("&")); - std::unique_ptr<std::string> se_info_ptr(new std::string(se_info_)); + std::optional<std::string> out_path = oat_dir ? std::make_optional<std::string>(oat_dir) : std::nullopt; + std::string class_loader_context = "&"; int32_t target_sdk_version = 0; // default - std::unique_ptr<std::string> profile_name_ptr(new std::string("primary.prof")); - std::unique_ptr<std::string> dm_path_ptr = nullptr; - if (dm_path != nullptr) { - dm_path_ptr.reset(new std::string(dm_path)); - } - std::unique_ptr<std::string> compilation_reason_ptr(new std::string("test-reason")); + std::string profile_name = "primary.prof"; + std::optional<std::string> dm_path_opt = dm_path ? std::make_optional<std::string>(dm_path) : std::nullopt; + std::string compilation_reason = "test-reason"; bool prof_result; ASSERT_BINDER_SUCCESS(service_->prepareAppProfile( - package_name_, kTestUserId, kTestAppId, *profile_name_ptr, apk_path_, - dm_path_ptr, &prof_result)); + package_name_, kTestUserId, kTestAppId, profile_name, apk_path_, + dm_path_opt, &prof_result)); ASSERT_TRUE(prof_result); binder::Status result = service_->dexopt(apk_path_, uid, - package_name_ptr, + package_name_, kRuntimeIsa, dexopt_needed, out_path, dex_flags, compiler_filter, volume_uuid_, - class_loader_context_ptr, - se_info_ptr, + class_loader_context, + se_info_, downgrade, target_sdk_version, - profile_name_ptr, - dm_path_ptr, - compilation_reason_ptr); + profile_name, + dm_path_opt, + compilation_reason); ASSERT_EQ(should_binder_call_succeed, result.isOk()) << result.toString8().c_str(); if (!should_binder_call_succeed) { @@ -673,7 +663,7 @@ TEST_F(DexoptTest, DexoptPrimaryFailedInvalidFilter) { &status); EXPECT_STREQ(status.toString8().c_str(), "Status(-8, EX_SERVICE_SPECIFIC): \'256: Dex2oat invocation for " - "/data/app/com.installd.test.dexopt/base.jar failed: unspecified dex2oat error'"); + "/data/app/com.installd.test.dexopt/base.jar failed: dex2oat error'"); } TEST_F(DexoptTest, DexoptPrimaryProfileNonPublic) { @@ -992,7 +982,7 @@ class ProfileTest : public DexoptTest { bool result; ASSERT_BINDER_SUCCESS(service_->prepareAppProfile( package_name, kTestUserId, kTestAppId, profile_name, apk_path_, - /*dex_metadata*/ nullptr, &result)); + /*dex_metadata*/ {}, &result)); ASSERT_EQ(expected_result, result); if (!expected_result) { diff --git a/cmds/installd/tests/installd_service_test.cpp b/cmds/installd/tests/installd_service_test.cpp index 0fb62ae9bf..1e7559d174 100644 --- a/cmds/installd/tests/installd_service_test.cpp +++ b/cmds/installd/tests/installd_service_test.cpp @@ -99,15 +99,14 @@ static int stat_mode(const char* path) { class ServiceTest : public testing::Test { protected: InstalldNativeService* service; - std::unique_ptr<std::string> testUuid; + std::optional<std::string> testUuid; virtual void SetUp() { setenv("ANDROID_LOG_TAGS", "*:v", 1); android::base::InitLogging(nullptr); service = new InstalldNativeService(); - testUuid = std::make_unique<std::string>(); - *testUuid = std::string(kTestUuid); + testUuid = kTestUuid; system("mkdir -p /data/local/tmp/user/0"); init_globals_from_data_and_root(); @@ -322,7 +321,7 @@ TEST_F(AppDataSnapshotTest, CreateAppDataSnapshot) { // Request a snapshot of the CE content but not the DE content. int64_t ce_snapshot_inode; - ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), + ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"), "com.foo", 0, 37, FLAG_STORAGE_CE, &ce_snapshot_inode)); struct stat buf; memset(&buf, 0, sizeof(buf)); @@ -344,7 +343,7 @@ TEST_F(AppDataSnapshotTest, CreateAppDataSnapshot) { 0700, 10000, 20000, false /* follow_symlinks */)); // Request a snapshot of the DE content but not the CE content. - ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), + ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"), "com.foo", 0, 37, FLAG_STORAGE_DE, &ce_snapshot_inode)); // Only DE content snapshot was requested. ASSERT_EQ(ce_snapshot_inode, 0); @@ -365,7 +364,7 @@ TEST_F(AppDataSnapshotTest, CreateAppDataSnapshot) { 0700, 10000, 20000, false /* follow_symlinks */)); // Request a snapshot of both the CE as well as the DE content. - ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), + ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"), "com.foo", 0, 37, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr)); ASSERT_TRUE(android::base::ReadFileToString( @@ -407,10 +406,10 @@ TEST_F(AppDataSnapshotTest, CreateAppDataSnapshot_TwoSnapshotsWithTheSameId) { 0700, 10000, 20000, false /* follow_symlinks */)); // Request snapshot for the package com.foo. - ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), + ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"), "com.foo", 0, 67, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr)); // Now request snapshot with the same id for the package com.bar - ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), + ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"), "com.bar", 0, 67, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr)); // Check that both snapshots have correct data in them. @@ -439,9 +438,9 @@ TEST_F(AppDataSnapshotTest, CreateAppDataSnapshot_AppDataAbsent) { ASSERT_EQ(0, delete_dir_contents_and_dir(fake_package_de_path, true)); int64_t ce_snapshot_inode; - ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), + ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"), "com.foo", 0, 73, FLAG_STORAGE_CE, &ce_snapshot_inode)); - ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), + ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"), "com.foo", 0, 73, FLAG_STORAGE_DE, nullptr)); // No CE content snapshot was performed. ASSERT_EQ(ce_snapshot_inode, 0); @@ -476,7 +475,7 @@ TEST_F(AppDataSnapshotTest, CreateAppDataSnapshot_ClearsExistingSnapshot) { "TEST_CONTENT_2_DE", fake_package_de_path + "/file2", 0700, 10000, 20000, false /* follow_symlinks */)); - ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), + ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"), "com.foo", 0, 13, FLAG_STORAGE_DE | FLAG_STORAGE_CE, nullptr)); // Previous snapshot (with data for file1) must be cleared. @@ -497,7 +496,7 @@ TEST_F(AppDataSnapshotTest, SnapshotAppData_WrongVolumeUuid) { ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700)); ASSERT_TRUE(mkdirs(rollback_de_dir, 0700)); - EXPECT_BINDER_FAIL(service->snapshotAppData(std::make_unique<std::string>("FOO"), + EXPECT_BINDER_FAIL(service->snapshotAppData(std::make_optional<std::string>("FOO"), "com.foo", 0, 17, FLAG_STORAGE_DE, nullptr)); } @@ -524,7 +523,7 @@ TEST_F(AppDataSnapshotTest, CreateAppDataSnapshot_ClearsCache) { ASSERT_TRUE(android::base::WriteStringToFile( "TEST_CONTENT_DE", fake_package_de_code_cache_path + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); - ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_unique<std::string>("TEST"), + ASSERT_BINDER_SUCCESS(service->snapshotAppData(std::make_optional<std::string>("TEST"), "com.foo", 0, 23, FLAG_STORAGE_CE | FLAG_STORAGE_DE, nullptr)); // The snapshot call must clear cache. struct stat sb; @@ -558,7 +557,7 @@ TEST_F(AppDataSnapshotTest, RestoreAppDataSnapshot) { "TEST_CONTENT_DE", fake_package_de_path + "/file1", 0700, 10000, 20000, false /* follow_symlinks */)); - ASSERT_BINDER_SUCCESS(service->restoreAppDataSnapshot(std::make_unique<std::string>("TEST"), + ASSERT_BINDER_SUCCESS(service->restoreAppDataSnapshot(std::make_optional<std::string>("TEST"), "com.foo", 10000, "", 0, 239, FLAG_STORAGE_DE | FLAG_STORAGE_CE)); std::string ce_content, de_content; @@ -584,7 +583,7 @@ TEST_F(AppDataSnapshotTest, CreateSnapshotThenDestroyIt) { int64_t ce_snapshot_inode; // Request a snapshot of both the CE as well as the DE content. - ASSERT_TRUE(service->snapshotAppData(std::make_unique<std::string>("TEST"), + ASSERT_TRUE(service->snapshotAppData(std::make_optional<std::string>("TEST"), "com.foo", 0, 57, FLAG_STORAGE_DE | FLAG_STORAGE_CE, &ce_snapshot_inode).isOk()); // Because CE data snapshot was requested, ce_snapshot_inode can't be null. ASSERT_NE(0, ce_snapshot_inode); @@ -594,7 +593,7 @@ TEST_F(AppDataSnapshotTest, CreateSnapshotThenDestroyIt) { ASSERT_EQ(0, stat((rollback_de_dir + "/com.foo").c_str(), &sb)); - ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"), + ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_optional<std::string>("TEST"), "com.foo", 0, ce_snapshot_inode, 57, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk()); // Check snapshot is deleted. ASSERT_EQ(-1, stat((rollback_ce_dir + "/com.foo").c_str(), &sb)); @@ -615,7 +614,7 @@ TEST_F(AppDataSnapshotTest, DestroyAppDataSnapshot_CeSnapshotInodeIsZero) { "DE_RESTORE_CONTENT", rollback_de_dir + "/com.foo/file1", 0700, 10000, 20000, false /* follow_symlinks */)); - ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"), + ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_optional<std::string>("TEST"), "com.foo", 0, 0, 1543, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk()); // Check snapshot is deleted. @@ -624,7 +623,7 @@ TEST_F(AppDataSnapshotTest, DestroyAppDataSnapshot_CeSnapshotInodeIsZero) { ASSERT_EQ(-1, stat((rollback_de_dir + "/com.foo").c_str(), &sb)); // Check that deleting already deleted snapshot is no-op. - ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_unique<std::string>("TEST"), + ASSERT_TRUE(service->destroyAppDataSnapshot(std::make_optional<std::string>("TEST"), "com.foo", 0, 0, 1543, FLAG_STORAGE_DE | FLAG_STORAGE_CE).isOk()); } @@ -637,7 +636,7 @@ TEST_F(AppDataSnapshotTest, DestroyAppDataSnapshot_WrongVolumeUuid) { ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700)); ASSERT_TRUE(mkdirs(rollback_de_dir, 0700)); - ASSERT_FALSE(service->destroyAppDataSnapshot(std::make_unique<std::string>("BAR"), + ASSERT_FALSE(service->destroyAppDataSnapshot(std::make_optional<std::string>("BAR"), "com.foo", 0, 0, 43, FLAG_STORAGE_DE).isOk()); } @@ -669,7 +668,7 @@ TEST_F(AppDataSnapshotTest, DestroyCeSnapshotsNotSpecified) { 0700, 10000, 20000, false /* follow_symlinks */)); ASSERT_TRUE(service->destroyCeSnapshotsNotSpecified( - std::make_unique<std::string>("TEST"), 0, { 1543, 77 }).isOk()); + std::make_optional<std::string>("TEST"), 0, { 1543, 77 }).isOk()); // Check only snapshots not specified are deleted. struct stat sb; @@ -690,7 +689,7 @@ TEST_F(AppDataSnapshotTest, RestoreAppDataSnapshot_WrongVolumeUuid) { ASSERT_TRUE(mkdirs(rollback_ce_dir, 0700)); ASSERT_TRUE(mkdirs(rollback_de_dir, 0700)); - EXPECT_BINDER_FAIL(service->restoreAppDataSnapshot(std::make_unique<std::string>("BAR"), + EXPECT_BINDER_FAIL(service->restoreAppDataSnapshot(std::make_optional<std::string>("BAR"), "com.foo", 10000, "", 0, 41, FLAG_STORAGE_DE)); } diff --git a/cmds/installd/unique_file.cpp b/cmds/installd/unique_file.cpp new file mode 100644 index 0000000000..e99ce1eeac --- /dev/null +++ b/cmds/installd/unique_file.cpp @@ -0,0 +1,80 @@ +/* + * 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 "unique_file.h" + +#include <string> + +#include <unistd.h> + +#include <android-base/logging.h> + +namespace android { +namespace installd { + +UniqueFile::UniqueFile() : UniqueFile(-1, "") {} + +UniqueFile::UniqueFile(int value, std::string path) : UniqueFile(value, path, nullptr) {} + +UniqueFile::UniqueFile(int value, std::string path, CleanUpFunction cleanup) + : value_(value), path_(path), cleanup_(cleanup), do_cleanup_(true), auto_close_(true) {} + +UniqueFile::UniqueFile(UniqueFile&& other) { + *this = std::move(other); +} + +UniqueFile::~UniqueFile() { + reset(); +} + +UniqueFile& UniqueFile::operator=(UniqueFile&& other) { + value_ = other.value_; + path_ = other.path_; + cleanup_ = other.cleanup_; + do_cleanup_ = other.do_cleanup_; + auto_close_ = other.auto_close_; + other.release(); + return *this; +} + +void UniqueFile::reset() { + reset(-1, ""); +} + +void UniqueFile::reset(int new_value, std::string path, CleanUpFunction new_cleanup) { + if (auto_close_ && value_ >= 0) { + if (close(value_) < 0) { + PLOG(ERROR) << "Failed to close fd " << value_ << ", with path " << path; + } + } + if (do_cleanup_ && cleanup_ != nullptr) { + cleanup_(path_); + } + + value_ = new_value; + path_ = path; + cleanup_ = new_cleanup; +} + +void UniqueFile::release() { + value_ = -1; + path_ = ""; + do_cleanup_ = false; + cleanup_ = nullptr; +} + +} // namespace installd +} // namespace android diff --git a/cmds/installd/unique_file.h b/cmds/installd/unique_file.h new file mode 100644 index 0000000000..e85e23b3a1 --- /dev/null +++ b/cmds/installd/unique_file.h @@ -0,0 +1,101 @@ +/* + * 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. + */ + +#ifndef ANDROID_INSTALLD_UNIQUE_FILE_H +#define ANDROID_INSTALLD_UNIQUE_FILE_H + +#include <functional> +#include <string> + +namespace android { +namespace installd { + +// A file management helper that serves two purposes: +// +// 1. Closes the file description on destruction, similar unique_fd. +// 2. Runs a cleanup function on after close, if not cancelled. +// +// The class does not assume the relationship between the given fd and file path. +// +// Example: +// +// UniqueFile file(open(...), +// filepath, +// [](const std::string& path) { +// unlink(path.c_str()); +// }); +// if (file.fd() == -1) { +// // Error opening... +// } +// +// ... +// if (error) { +// // At this point, when the UniqueFile is destructed, the cleanup function will run +// // (e.g. to delete the file) after the fd is closed. +// return -1; +// } +// +// (Success case) +// file.DisableCleanup(); +// // At this point, when the UniqueFile is destructed, the cleanup function will not run +// // (e.g. leaving the file around) after the fd is closed. +// +class UniqueFile { + private: + using CleanUpFunction = std::function<void (const std::string&)>; + + public: + UniqueFile(); + UniqueFile(int value, std::string path); + UniqueFile(int value, std::string path, CleanUpFunction cleanup); + UniqueFile(UniqueFile&& other); + ~UniqueFile(); + + UniqueFile& operator=(UniqueFile&& other); + + int fd() const { + return value_; + } + + const std::string& path() const { + return path_; + } + + void DisableAutoClose() { + auto_close_ = false; + } + + void DisableCleanup() { + do_cleanup_ = false; + } + + void reset(); + void reset(int new_value, std::string path, CleanUpFunction new_cleanup = nullptr); + + private: + void release(); + + int value_; + std::string path_; + CleanUpFunction cleanup_; + bool do_cleanup_; + bool auto_close_; +}; + +} // namespace installd +} // namespace android + +#endif // ANDROID_INSTALLD_UNIQUE_FILE_H diff --git a/cmds/lshal/Android.bp b/cmds/lshal/Android.bp index 5afae4b7d3..987adafc8e 100644 --- a/cmds/lshal/Android.bp +++ b/cmds/lshal/Android.bp @@ -75,7 +75,7 @@ cc_test { defaults: ["lshal_defaults"], gtest: true, static_libs: [ - "android.hardware.tests.baz@1.0", + "android.hardware.tests.inheritance@1.0", "libgmock", ], shared_libs: [ diff --git a/cmds/lshal/DebugCommand.cpp b/cmds/lshal/DebugCommand.cpp index af22ac9b3d..72958bd2a9 100644 --- a/cmds/lshal/DebugCommand.cpp +++ b/cmds/lshal/DebugCommand.cpp @@ -39,7 +39,7 @@ Status DebugCommand::parseArgs(const Arg &arg) { // Optargs cannnot be used because the flag should not be considered set // if it should really be contained in mOptions. if (std::string(arg.argv[optind]) == "-E") { - mExcludesParentInstances = true; + mParentDebugInfoLevel = ParentDebugInfoLevel::NOTHING; optind++; } @@ -67,7 +67,7 @@ Status DebugCommand::main(const Arg &arg) { return mLshal.emitDebugInfo( pair.first, pair.second.empty() ? "default" : pair.second, mOptions, - mExcludesParentInstances, + mParentDebugInfoLevel, mLshal.out().buf(), mLshal.err()); } diff --git a/cmds/lshal/DebugCommand.h b/cmds/lshal/DebugCommand.h index cd57e31bfc..317cc2821c 100644 --- a/cmds/lshal/DebugCommand.h +++ b/cmds/lshal/DebugCommand.h @@ -21,6 +21,7 @@ #include <android-base/macros.h> #include "Command.h" +#include "ParentDebugInfoLevel.h" #include "utils.h" namespace android { @@ -42,9 +43,8 @@ private: std::string mInterfaceName; std::vector<std::string> mOptions; - // Outputs the actual descriptor of a hal instead of the debug output - // if the arguments provided are a superclass of the actual hal impl. - bool mExcludesParentInstances; + // See comment on ParentDebugInfoLevel. + ParentDebugInfoLevel mParentDebugInfoLevel = ParentDebugInfoLevel::FULL; DISALLOW_COPY_AND_ASSIGN(DebugCommand); }; diff --git a/cmds/lshal/ListCommand.cpp b/cmds/lshal/ListCommand.cpp index fb11cee33e..92958d957b 100644 --- a/cmds/lshal/ListCommand.cpp +++ b/cmds/lshal/ListCommand.cpp @@ -555,7 +555,7 @@ void ListCommand::dumpTable(const NullableOStream<std::ostream>& out) const { std::stringstream ss; auto pair = splitFirst(iName, '/'); mLshal.emitDebugInfo(pair.first, pair.second, {}, - false /* excludesParentInstances */, ss, + ParentDebugInfoLevel::FQNAME_ONLY, ss, NullableOStream<std::ostream>(nullptr)); return ss.str(); }; @@ -916,6 +916,18 @@ void ListCommand::initFetchTypes() { } } +// Get all values of enum type T, assuming the first value is 0 and the last value is T::LAST. +// T::LAST is not included in the returned list. +template <typename T> +std::vector<T> GetAllValues() { + using BaseType = std::underlying_type_t<T>; + std::vector<T> ret; + for (BaseType i = 0; i < static_cast<BaseType>(T::LAST); ++i) { + ret.push_back(static_cast<T>(i)); + } + return ret; +} + void ListCommand::registerAllOptions() { int v = mOptions.size(); // A list of acceptable command line options @@ -989,6 +1001,15 @@ void ListCommand::registerAllOptions() { " - declared: only declared in VINTF manifest but is not registered to hwservicemanager;\n" " - N/A: no information for passthrough HALs."}); + mOptions.push_back({'A', "all", no_argument, v++, + [](ListCommand* thiz, const char*) { + auto allColumns = GetAllValues<TableColumnType>(); + thiz->mSelectedColumns.insert(thiz->mSelectedColumns.end(), + allColumns.begin(), allColumns.end()); + return OK; + }, + "print all columns"}); + // long options without short alternatives mOptions.push_back({'\0', "init-vintf", no_argument, v++, [](ListCommand* thiz, const char* arg) { thiz->mVintf = true; @@ -1019,46 +1040,55 @@ void ListCommand::registerAllOptions() { thiz->mNeat = true; return OK; }, "output is machine parsable (no explanatory text).\nCannot be used with --debug."}); - mOptions.push_back({'\0', "types", required_argument, v++, [](ListCommand* thiz, const char* arg) { - if (!arg) { return USAGE; } - - static const std::map<std::string, HalType> kHalTypeMap { - {"binderized", HalType::BINDERIZED_SERVICES}, - {"b", HalType::BINDERIZED_SERVICES}, - {"passthrough_clients", HalType::PASSTHROUGH_CLIENTS}, - {"c", HalType::PASSTHROUGH_CLIENTS}, - {"passthrough_libs", HalType::PASSTHROUGH_LIBRARIES}, - {"l", HalType::PASSTHROUGH_LIBRARIES}, - {"vintf", HalType::VINTF_MANIFEST}, - {"v", HalType::VINTF_MANIFEST}, - {"lazy", HalType::LAZY_HALS}, - {"z", HalType::LAZY_HALS}, - }; - - std::vector<std::string> halTypesArgs = split(std::string(arg), ','); - for (const auto& halTypeArg : halTypesArgs) { - if (halTypeArg.empty()) continue; - - const auto& halTypeIter = kHalTypeMap.find(halTypeArg); - if (halTypeIter == kHalTypeMap.end()) { - - thiz->err() << "Unrecognized HAL type: " << halTypeArg << std::endl; - return USAGE; - } - - // Append unique (non-repeated) HAL types to the reporting list - HalType halType = halTypeIter->second; - if (std::find(thiz->mListTypes.begin(), thiz->mListTypes.end(), halType) == - thiz->mListTypes.end()) { - thiz->mListTypes.push_back(halType); - } - } - - if (thiz->mListTypes.empty()) { return USAGE; } - return OK; - }, "comma-separated list of one or more sections.\nThe output is restricted to the selected " - "section(s). Valid options\nare: (b|binderized), (c|passthrough_clients), (l|" - "passthrough_libs), (v|vintf), and (z|lazy).\nDefault is `bcl`."}); + mOptions.push_back( + {'\0', "types", required_argument, v++, + [](ListCommand* thiz, const char* arg) { + if (!arg) { + return USAGE; + } + + static const std::map<std::string, std::vector<HalType>> kHalTypeMap{ + {"binderized", {HalType::BINDERIZED_SERVICES}}, + {"b", {HalType::BINDERIZED_SERVICES}}, + {"passthrough_clients", {HalType::PASSTHROUGH_CLIENTS}}, + {"c", {HalType::PASSTHROUGH_CLIENTS}}, + {"passthrough_libs", {HalType::PASSTHROUGH_LIBRARIES}}, + {"l", {HalType::PASSTHROUGH_LIBRARIES}}, + {"vintf", {HalType::VINTF_MANIFEST}}, + {"v", {HalType::VINTF_MANIFEST}}, + {"lazy", {HalType::LAZY_HALS}}, + {"z", {HalType::LAZY_HALS}}, + {"all", GetAllValues<HalType>()}, + {"a", GetAllValues<HalType>()}, + }; + + std::vector<std::string> halTypesArgs = split(std::string(arg), ','); + for (const auto& halTypeArg : halTypesArgs) { + if (halTypeArg.empty()) continue; + + const auto& halTypeIter = kHalTypeMap.find(halTypeArg); + if (halTypeIter == kHalTypeMap.end()) { + thiz->err() << "Unrecognized HAL type: " << halTypeArg << std::endl; + return USAGE; + } + + // Append unique (non-repeated) HAL types to the reporting list + for (auto halType : halTypeIter->second) { + if (std::find(thiz->mListTypes.begin(), thiz->mListTypes.end(), halType) == + thiz->mListTypes.end()) { + thiz->mListTypes.push_back(halType); + } + } + } + + if (thiz->mListTypes.empty()) { + return USAGE; + } + return OK; + }, + "comma-separated list of one or more sections.\nThe output is restricted to the " + "selected section(s). Valid options\nare: (b|binderized), (c|passthrough_clients), (l|" + "passthrough_libs), (v|vintf), (z|lazy), and (a|all).\nDefault is `b,c,l`."}); } // Create 'longopts' argument to getopt_long. Caller is responsible for maintaining diff --git a/cmds/lshal/ListCommand.h b/cmds/lshal/ListCommand.h index acc0dcfc2e..412aadd7e4 100644 --- a/cmds/lshal/ListCommand.h +++ b/cmds/lshal/ListCommand.h @@ -52,6 +52,9 @@ enum class HalType { PASSTHROUGH_LIBRARIES, VINTF_MANIFEST, LAZY_HALS, + + // Not a real HalType. Used to determine all HalTypes. + LAST, }; class ListCommand : public Command { diff --git a/cmds/lshal/Lshal.cpp b/cmds/lshal/Lshal.cpp index 132b31ebc3..2c3efe524e 100644 --- a/cmds/lshal/Lshal.cpp +++ b/cmds/lshal/Lshal.cpp @@ -101,7 +101,7 @@ Status Lshal::emitDebugInfo( const std::string &interfaceName, const std::string &instanceName, const std::vector<std::string> &options, - bool excludesParentInstances, + ParentDebugInfoLevel parentDebugInfoLevel, std::ostream &out, NullableOStream<std::ostream> err) const { using android::hidl::base::V1_0::IBase; @@ -126,7 +126,7 @@ Status Lshal::emitDebugInfo( return NO_INTERFACE; } - if (excludesParentInstances) { + if (parentDebugInfoLevel != ParentDebugInfoLevel::FULL) { const std::string descriptor = getDescriptor(base.get()); if (descriptor.empty()) { std::string msg = interfaceName + "/" + instanceName + " getDescriptor failed"; @@ -134,6 +134,9 @@ Status Lshal::emitDebugInfo( LOG(ERROR) << msg; } if (descriptor != interfaceName) { + if (parentDebugInfoLevel == ParentDebugInfoLevel::FQNAME_ONLY) { + out << "[See " << descriptor << "/" << instanceName << "]"; + } return OK; } } diff --git a/cmds/lshal/Lshal.h b/cmds/lshal/Lshal.h index 830bd872ff..50279d4d7a 100644 --- a/cmds/lshal/Lshal.h +++ b/cmds/lshal/Lshal.h @@ -25,6 +25,7 @@ #include "Command.h" #include "NullableOStream.h" +#include "ParentDebugInfoLevel.h" #include "utils.h" namespace android { @@ -49,7 +50,7 @@ public: const std::string &interfaceName, const std::string &instanceName, const std::vector<std::string> &options, - bool excludesParentInstances, + ParentDebugInfoLevel parentDebugInfoLevel, std::ostream &out, NullableOStream<std::ostream> err) const; diff --git a/libs/binder/include/binder/Nullable.h b/cmds/lshal/ParentDebugInfoLevel.h index b605bd3315..12ac9c8648 100644 --- a/libs/binder/include/binder/Nullable.h +++ b/cmds/lshal/ParentDebugInfoLevel.h @@ -15,30 +15,20 @@ */ #pragma once -#include <memory> -#include <utility> - namespace android { - -namespace aidl { - -// nullable/make_nullable provide source-level compatibility between std::opional and std::unique_ptr -// usage: -// nullable<Foo> a; -// nullable<Foo> b = make_nullable<Foo>(...); -// auto c = make_nullable<Foo>(...); -// c.reset(); -// c = make_nullable<Foo>(...); -// c = std::move(a); - -template <typename T> -using nullable = std::unique_ptr<T>; - -template <typename T, typename... Args> -inline nullable<T> make_nullable(Args&&... args) { - return std::make_unique<T>(std::forward<Args>(args)...); -} - -} // namespace aidl - -} // namespace android
\ No newline at end of file +namespace lshal { + +// Describe verbosity when dumping debug information on a HAL service by +// referring to a parent HAL interface FQName (for example, when dumping debug information +// on foo@1.0::IFoo but the HAL implementation is foo@1.1::IFoo). +enum class ParentDebugInfoLevel { + // Write nothing. + NOTHING, + // Write a short description that includes the FQName of the real implementation. + FQNAME_ONLY, + // Write full debug info. + FULL, +}; + +} // namespace lshal +} // namespace android diff --git a/cmds/lshal/TableEntry.h b/cmds/lshal/TableEntry.h index 0ff0c96d38..3c368132a5 100644 --- a/cmds/lshal/TableEntry.h +++ b/cmds/lshal/TableEntry.h @@ -35,19 +35,25 @@ using android::procpartition::Partition; using Pids = std::vector<int32_t>; enum class TableColumnType : unsigned int { - INTERFACE_NAME, + INTERFACE_NAME = 0, TRANSPORT, SERVER_PID, - SERVER_CMD, SERVER_ADDR, - CLIENT_PIDS, - CLIENT_CMDS, ARCH, THREADS, RELEASED, HASH, VINTF, SERVICE_STATUS, + CLIENT_PIDS, + + // Not a real TableColumnType. Used to determine all TableColumnTypes. + LAST, + + // Not included in all TableColumnTypes because they replace *PID(S) when the + // --cmdline option is set. + SERVER_CMD, + CLIENT_CMDS, }; enum : unsigned int { diff --git a/cmds/lshal/test.cpp b/cmds/lshal/test.cpp index 3d550babf4..9964888233 100644 --- a/cmds/lshal/test.cpp +++ b/cmds/lshal/test.cpp @@ -24,7 +24,7 @@ #include <gtest/gtest.h> #include <gmock/gmock.h> -#include <android/hardware/tests/baz/1.0/IQuux.h> +#include <android/hardware/tests/inheritance/1.0/IChild.h> #include <hidl/HidlTransportSupport.h> #include <vintf/parse_xml.h> @@ -44,6 +44,7 @@ using ::android::hardware::hidl_death_recipient; using ::android::hardware::hidl_handle; using ::android::hardware::hidl_string; using ::android::hardware::hidl_vec; +using ::android::hardware::Void; using android::vintf::Arch; using android::vintf::CompatibilityMatrix; using android::vintf::gCompatibilityMatrixConverter; @@ -59,10 +60,14 @@ using hidl_hash = hidl_array<uint8_t, 32>; namespace android { namespace hardware { namespace tests { -namespace baz { +namespace inheritance { namespace V1_0 { namespace implementation { -struct Quux : android::hardware::tests::baz::V1_0::IQuux { +struct Child : android::hardware::tests::inheritance::V1_0::IChild { + ::android::hardware::Return<void> doChild() override { return Void(); } + ::android::hardware::Return<void> doParent() override { return Void(); } + ::android::hardware::Return<void> doGrandparent() override { return Void(); } + ::android::hardware::Return<void> debug(const hidl_handle& hh, const hidl_vec<hidl_string>& options) override { const native_handle_t *handle = hh.getNativeHandle(); if (handle->numFds < 1) { @@ -76,7 +81,7 @@ struct Quux : android::hardware::tests::baz::V1_0::IQuux { } ssize_t written = write(fd, content.c_str(), content.size()); if (written != (ssize_t)content.size()) { - LOG(WARNING) << "SERVER(Quux) debug writes " << written << " bytes < " + LOG(WARNING) << "SERVER(Child) debug writes " << written << " bytes < " << content.size() << " bytes, errno = " << errno; } return Void(); @@ -85,7 +90,7 @@ struct Quux : android::hardware::tests::baz::V1_0::IQuux { } // namespace implementation } // namespace V1_0 -} // namespace baz +} // namespace inheritance } // namespace tests } // namespace hardware @@ -124,18 +129,24 @@ public: class DebugTest : public ::testing::Test { public: void SetUp() override { - using ::android::hardware::tests::baz::V1_0::IQuux; - using ::android::hardware::tests::baz::V1_0::implementation::Quux; + using ::android::hardware::tests::inheritance::V1_0::IChild; + using ::android::hardware::tests::inheritance::V1_0::IParent; + using ::android::hardware::tests::inheritance::V1_0::IGrandparent; + using ::android::hardware::tests::inheritance::V1_0::implementation::Child; err.str(""); out.str(""); serviceManager = new testing::NiceMock<MockServiceManager>(); - ON_CALL(*serviceManager, get(_, _)).WillByDefault(Invoke( - [](const auto &iface, const auto &inst) -> ::android::hardware::Return<sp<IBase>> { - if (iface == IQuux::descriptor && inst == "default") - return new Quux(); - return nullptr; - })); + ON_CALL(*serviceManager, get(_, _)) + .WillByDefault( + Invoke([](const auto& iface, + const auto& inst) -> ::android::hardware::Return<sp<IBase>> { + if (inst != "default") return nullptr; + if (iface == IChild::descriptor || iface == IParent::descriptor || + iface == IGrandparent::descriptor) + return new Child(); + return nullptr; + })); lshal = std::make_unique<Lshal>(out, err, serviceManager, serviceManager); } @@ -159,17 +170,17 @@ static Status callMain(const std::unique_ptr<T>& lshal, const std::vector<const TEST_F(DebugTest, Debug) { EXPECT_EQ(0u, callMain(lshal, { - "lshal", "debug", "android.hardware.tests.baz@1.0::IQuux/default", "foo", "bar" + "lshal", "debug", "android.hardware.tests.inheritance@1.0::IChild/default", "foo", "bar" })); - EXPECT_THAT(out.str(), StrEq("android.hardware.tests.baz@1.0::IQuux\nfoo\nbar")); + EXPECT_THAT(out.str(), StrEq("android.hardware.tests.inheritance@1.0::IChild\nfoo\nbar")); EXPECT_THAT(err.str(), IsEmpty()); } TEST_F(DebugTest, Debug2) { EXPECT_EQ(0u, callMain(lshal, { - "lshal", "debug", "android.hardware.tests.baz@1.0::IQuux", "baz", "quux" + "lshal", "debug", "android.hardware.tests.inheritance@1.0::IChild", "baz", "quux" })); - EXPECT_THAT(out.str(), StrEq("android.hardware.tests.baz@1.0::IQuux\nbaz\nquux")); + EXPECT_THAT(out.str(), StrEq("android.hardware.tests.inheritance@1.0::IChild\nbaz\nquux")); EXPECT_THAT(err.str(), IsEmpty()); } @@ -180,6 +191,22 @@ TEST_F(DebugTest, Debug3) { EXPECT_THAT(err.str(), HasSubstr("does not exist")); } +TEST_F(DebugTest, DebugParent) { + EXPECT_EQ(0u, callMain(lshal, { + "lshal", "debug", "android.hardware.tests.inheritance@1.0::IParent", "calling parent" + })); + EXPECT_THAT(out.str(), StrEq("android.hardware.tests.inheritance@1.0::IChild\ncalling parent")); + EXPECT_THAT(err.str(), IsEmpty()); +} + +TEST_F(DebugTest, DebugParentExclude) { + EXPECT_EQ(0u, callMain(lshal, { + "lshal", "debug", "-E", "android.hardware.tests.inheritance@1.0::IParent", "excluding" + })); + EXPECT_THAT(out.str(), IsEmpty()); + EXPECT_THAT(err.str(), IsEmpty()); +} + class MockLshal : public Lshal { public: MockLshal() {} @@ -681,8 +708,8 @@ TEST_F(ListTest, DumpEmptyAndDuplicateHalTypes) { TEST_F(ListTest, UnknownHalType) { optind = 1; // mimic Lshal::parseArg() - EXPECT_EQ(1u, mockList->main(createArg({"lshal", "-itrepac", "--types=c,a"}))); - EXPECT_THAT(err.str(), HasSubstr("Unrecognized HAL type: a")); + EXPECT_EQ(1u, mockList->main(createArg({"lshal", "-itrepac", "--types=c,r"}))); + EXPECT_THAT(err.str(), HasSubstr("Unrecognized HAL type: r")); } TEST_F(ListTest, Vintf) { @@ -766,6 +793,156 @@ TEST_F(ListTest, Vintf) { EXPECT_EQ("", err.str()); } +TEST_F(ListTest, AllColumns) { + // clang-format off + const std::string expected = + "[fake description 0]\n" + "Interface Transport Server PTR Arch Thread Use R Hash VINTF Status Clients\n" + "a.h.foo1@1.0::IFoo/1 hwbinder 1 0000000000002711 64 11/21 N 0000000000000000000000000000000000000000000000000000000000000000 X alive 2 4\n" + "a.h.foo2@2.0::IFoo/2 hwbinder 2 0000000000002712 64 12/22 Y 0202020202020202020202020202020202020202020202020202020202020202 X alive 3 5\n" + "\n" + "[fake description 1]\n" + "Interface Transport Server PTR Arch Thread Use R Hash VINTF Status Clients\n" + "a.h.foo3@3.0::IFoo/3 passthrough N/A N/A 32 N/A ? X N/A 4 6\n" + "a.h.foo4@4.0::IFoo/4 passthrough N/A N/A 32 N/A ? X N/A 5 7\n" + "\n" + "[fake description 2]\n" + "Interface Transport Server PTR Arch Thread Use R Hash VINTF Status Clients\n" + "a.h.foo5@5.0::IFoo/5 passthrough N/A N/A 32 N/A ? X N/A 6 8\n" + "a.h.foo6@6.0::IFoo/6 passthrough N/A N/A 32 N/A ? X N/A 7 9\n" + "\n"; + // clang-format on + + optind = 1; // mimic Lshal::parseArg() + EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--all"}))); + EXPECT_EQ(expected, out.str()); + EXPECT_EQ("", err.str()); +} + +TEST_F(ListTest, AllColumnsWithCmd) { + // clang-format off + const std::string expected = + "[fake description 0]\n" + "Interface Transport Server CMD PTR Arch Thread Use R Hash VINTF Status Clients CMD\n" + "a.h.foo1@1.0::IFoo/1 hwbinder command_line_1 0000000000002711 64 11/21 N 0000000000000000000000000000000000000000000000000000000000000000 X alive command_line_2;command_line_4\n" + "a.h.foo2@2.0::IFoo/2 hwbinder command_line_2 0000000000002712 64 12/22 Y 0202020202020202020202020202020202020202020202020202020202020202 X alive command_line_3;command_line_5\n" + "\n" + "[fake description 1]\n" + "Interface Transport Server CMD PTR Arch Thread Use R Hash VINTF Status Clients CMD\n" + "a.h.foo3@3.0::IFoo/3 passthrough N/A 32 N/A ? X N/A command_line_4;command_line_6\n" + "a.h.foo4@4.0::IFoo/4 passthrough N/A 32 N/A ? X N/A command_line_5;command_line_7\n" + "\n" + "[fake description 2]\n" + "Interface Transport Server CMD PTR Arch Thread Use R Hash VINTF Status Clients CMD\n" + "a.h.foo5@5.0::IFoo/5 passthrough N/A 32 N/A ? X N/A command_line_6;command_line_8\n" + "a.h.foo6@6.0::IFoo/6 passthrough N/A 32 N/A ? X N/A command_line_7;command_line_9\n" + "\n"; + // clang-format on + + optind = 1; // mimic Lshal::parseArg() + EXPECT_EQ(0u, mockList->main(createArg({"lshal", "-Am"}))); + EXPECT_EQ(expected, out.str()); + EXPECT_EQ("", err.str()); +} + +TEST_F(ListTest, AllSections) { + optind = 1; // mimic Lshal::parseArg() + EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--types=all"}))); + using HalTypeBase = std::underlying_type_t<HalType>; + for (HalTypeBase i = 0; i < static_cast<HalTypeBase>(HalType::LAST); ++i) { + EXPECT_THAT(out.str(), HasSubstr("[fake description " + std::to_string(i) + "]")); + } + EXPECT_THAT(out.str(), + Not(HasSubstr("[fake description " + + std::to_string(static_cast<HalTypeBase>(HalType::LAST)) + "]"))); + EXPECT_EQ("", err.str()); +} + +// Fake service returned by mocked IServiceManager::get for DumpDebug. +// The interfaceChain and getHashChain functions returns +// foo(id - 1) -> foo(id - 2) -> ... foo1 -> IBase. +class InheritingService : public IBase { +public: + explicit InheritingService(pid_t id) : mId(id) {} + android::hardware::Return<void> interfaceDescriptor(interfaceDescriptor_cb cb) override { + cb(getInterfaceName(mId)); + return hardware::Void(); + } + android::hardware::Return<void> interfaceChain(interfaceChain_cb cb) override { + std::vector<hidl_string> ret; + for (auto i = mId; i > 0; --i) { + ret.push_back(getInterfaceName(i)); + } + ret.push_back(IBase::descriptor); + cb(ret); + return hardware::Void(); + } + android::hardware::Return<void> getHashChain(getHashChain_cb cb) override { + std::vector<hidl_hash> ret; + for (auto i = mId; i > 0; --i) { + ret.push_back(getHashFromId(i)); + } + ret.push_back(getHashFromId(0xff)); + cb(ret); + return hardware::Void(); + } + android::hardware::Return<void> debug(const hidl_handle& hh, + const hidl_vec<hidl_string>&) override { + const native_handle_t* handle = hh.getNativeHandle(); + if (handle->numFds < 1) { + return Void(); + } + int fd = handle->data[0]; + std::string content = "debug info for "; + content += getInterfaceName(mId); + ssize_t written = write(fd, content.c_str(), content.size()); + if (written != (ssize_t)content.size()) { + LOG(WARNING) << "SERVER(" << descriptor << ") debug writes " << written << " bytes < " + << content.size() << " bytes, errno = " << errno; + } + return Void(); + } + +private: + pid_t mId; +}; + +TEST_F(ListTest, DumpDebug) { + size_t inheritanceLevel = 3; + sp<IBase> service = new InheritingService(inheritanceLevel); + + EXPECT_CALL(*serviceManager, list(_)).WillRepeatedly(Invoke([&](IServiceManager::list_cb cb) { + std::vector<hidl_string> ret; + for (auto i = 1; i <= inheritanceLevel; ++i) { + ret.push_back(getInterfaceName(i) + "/default"); + } + cb(ret); + return hardware::Void(); + })); + EXPECT_CALL(*serviceManager, get(_, _)) + .WillRepeatedly( + Invoke([&](const hidl_string&, const hidl_string& instance) -> sp<IBase> { + int id = getIdFromInstanceName(instance); + if (id > inheritanceLevel) return nullptr; + return sp<IBase>(service); + })); + + const std::string expected = "[fake description 0]\n" + "Interface\n" + "a.h.foo1@1.0::IFoo/default\n" + "[See a.h.foo3@3.0::IFoo/default]\n" + "a.h.foo2@2.0::IFoo/default\n" + "[See a.h.foo3@3.0::IFoo/default]\n" + "a.h.foo3@3.0::IFoo/default\n" + "debug info for a.h.foo3@3.0::IFoo\n" + "\n"; + + optind = 1; // mimic Lshal::parseArg() + EXPECT_EQ(0u, mockList->main(createArg({"lshal", "--types=b", "-id"}))); + EXPECT_EQ(expected, out.str()); + EXPECT_EQ("", err.str()); +} + class ListVintfTest : public ListTest { public: virtual void SetUp() override { diff --git a/cmds/servicemanager/Android.bp b/cmds/servicemanager/Android.bp index 7277e85d99..b1392515a2 100644 --- a/cmds/servicemanager/Android.bp +++ b/cmds/servicemanager/Android.bp @@ -44,6 +44,9 @@ cc_binary { cflags: [ "-DVENDORSERVICEMANAGER=1", ], + required: [ + "vndservice", + ], srcs: ["main.cpp"], } diff --git a/cmds/servicemanager/main.cpp b/cmds/servicemanager/main.cpp index 2618906261..b1bc6dc7d5 100644 --- a/cmds/servicemanager/main.cpp +++ b/cmds/servicemanager/main.cpp @@ -130,7 +130,7 @@ int main(int argc, char** argv) { } IPCThreadState::self()->setTheContextObject(manager); - ps->becomeContextManager(nullptr, nullptr); + ps->becomeContextManager(); sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/); diff --git a/cmds/servicemanager/servicemanager.rc b/cmds/servicemanager/servicemanager.rc index 152ac28ba4..6d5070fa04 100644 --- a/cmds/servicemanager/servicemanager.rc +++ b/cmds/servicemanager/servicemanager.rc @@ -3,16 +3,11 @@ service servicemanager /system/bin/servicemanager user system group system readproc critical - onrestart restart healthd - onrestart restart zygote + onrestart restart apexd onrestart restart audioserver - onrestart restart media - onrestart restart surfaceflinger - onrestart restart inputflinger - onrestart restart drm - onrestart restart cameraserver - onrestart restart keystore onrestart restart gatekeeperd - onrestart restart thermalservice + onrestart class_restart main + onrestart class_restart hal + onrestart class_restart early_hal writepid /dev/cpuset/system-background/tasks shutdown critical diff --git a/cmds/servicemanager/vndservicemanager.rc b/cmds/servicemanager/vndservicemanager.rc index 3fa4d7debd..756f6c3bc8 100644 --- a/cmds/servicemanager/vndservicemanager.rc +++ b/cmds/servicemanager/vndservicemanager.rc @@ -3,4 +3,7 @@ service vndservicemanager /vendor/bin/vndservicemanager /dev/vndbinder user system group system readproc writepid /dev/cpuset/system-background/tasks + onrestart class_restart main + onrestart class_restart hal + onrestart class_restart early_hal shutdown critical diff --git a/docs/Doxyfile b/docs/Doxyfile index efa639d2e7..a1bd960c5a 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -1621,7 +1621,23 @@ INCLUDE_FILE_PATTERNS = # undefined via #undef or recursively expanded use the := operator # instead of the = operator. -PREDEFINED = __attribute__(x)= +PREDEFINED = \ + "__ANDROID_API__=10000" \ + "__BEGIN_DECLS=" \ + "__END_DECLS=" \ + "__INTRODUCED_IN(x)=" \ + "__INTRODUCED_IN_32(x)=" \ + "__INTRODUCED_IN_64(x)=" \ + "__RENAME(x)=" \ + "__RENAME_LDBL(x,y,z)=" \ + "__printflike(x,y)=" \ + "__attribute__(x)=" \ + "__wur=" \ + "__mallocfunc=" \ + "__attribute_pure__=" \ + "__attribute__(x)=" \ + __ANDROID__ \ + __BIONIC__ \ # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then # this tag can be used to specify a list of macro names that should be expanded. diff --git a/headers/Android.bp b/headers/Android.bp index 53372357e6..8f41c2b75b 100644 --- a/headers/Android.bp +++ b/headers/Android.bp @@ -18,4 +18,11 @@ cc_library_headers { "libstagefright_foundation_headers", ], min_sdk_version: "29", + + host_supported: true, + target: { + darwin: { + enabled: false, + }, + }, } diff --git a/headers/media_plugin/media/openmax/OMX_VideoExt.h b/headers/media_plugin/media/openmax/OMX_VideoExt.h index dc37bbd1f7..e65b224a20 100644 --- a/headers/media_plugin/media/openmax/OMX_VideoExt.h +++ b/headers/media_plugin/media/openmax/OMX_VideoExt.h @@ -321,6 +321,46 @@ typedef enum OMX_VIDEO_DOLBYVISIONLEVELTYPE { OMX_VIDEO_DolbyVisionLevelmax = 0x7FFFFFFF } OMX_VIDEO_DOLBYVISIONLEVELTYPE; +/** AV1 Profile enum type */ +typedef enum OMX_VIDEO_AV1PROFILETYPE { + OMX_VIDEO_AV1ProfileMain8 = 0x00000001, + OMX_VIDEO_AV1ProfileMain10 = 0x00000002, + OMX_VIDEO_AV1ProfileMain10HDR10 = 0x00001000, + OMX_VIDEO_AV1ProfileMain10HDR10Plus = 0x00002000, + OMX_VIDEO_AV1ProfileUnknown = 0x6EFFFFFF, + OMX_VIDEO_AV1ProfileMax = 0x7FFFFFFF +} OMX_VIDEO_AV1PROFILETYPE; + +/** AV1 Level enum type */ +typedef enum OMX_VIDEO_AV1LEVELTYPE { + OMX_VIDEO_AV1Level2 = 0x1, + OMX_VIDEO_AV1Level21 = 0x2, + OMX_VIDEO_AV1Level22 = 0x4, + OMX_VIDEO_AV1Level23 = 0x8, + OMX_VIDEO_AV1Level3 = 0x10, + OMX_VIDEO_AV1Level31 = 0x20, + OMX_VIDEO_AV1Level32 = 0x40, + OMX_VIDEO_AV1Level33 = 0x80, + OMX_VIDEO_AV1Level4 = 0x100, + OMX_VIDEO_AV1Level41 = 0x200, + OMX_VIDEO_AV1Level42 = 0x400, + OMX_VIDEO_AV1Level43 = 0x800, + OMX_VIDEO_AV1Level5 = 0x1000, + OMX_VIDEO_AV1Level51 = 0x2000, + OMX_VIDEO_AV1Level52 = 0x4000, + OMX_VIDEO_AV1Level53 = 0x8000, + OMX_VIDEO_AV1Level6 = 0x10000, + OMX_VIDEO_AV1Level61 = 0x20000, + OMX_VIDEO_AV1Level62 = 0x40000, + OMX_VIDEO_AV1Level63 = 0x80000, + OMX_VIDEO_AV1Level7 = 0x100000, + OMX_VIDEO_AV1Level71 = 0x200000, + OMX_VIDEO_AV1Level72 = 0x400000, + OMX_VIDEO_AV1Level73 = 0x800000, + OMX_VIDEO_AV1LevelUnknown = 0x6EFFFFFF, + OMX_VIDEO_AV1LevelMax = 0x7FFFFFFF +} OMX_VIDEO_AV1LEVELTYPE; + /** * Structure for configuring video compression intra refresh period * diff --git a/include/android/bitmap.h b/include/android/bitmap.h index 2631b144af..f19539913e 100644 --- a/include/android/bitmap.h +++ b/include/android/bitmap.h @@ -125,6 +125,8 @@ int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap, * Note that {@link ADataSpace} only exposes a few values. This may return * {@link ADATASPACE_UNKNOWN}, even for Named ColorSpaces, if they have no * corresponding ADataSpace. + * + * Available since API level 30. */ int32_t AndroidBitmap_getDataSpace(JNIEnv* env, jobject jbitmap) __INTRODUCED_IN(30); @@ -189,6 +191,8 @@ enum AndroidBitmapCompressFormat { /** * User-defined function for writing the output of compression. * + * Available since API level 30. + * * @param userContext Pointer to user-defined data passed to * {@link AndroidBitmap_compress}. * @param data Compressed data of |size| bytes to write. @@ -202,6 +206,8 @@ typedef bool (*AndroidBitmap_CompressWriteFunc)(void* userContext, /** * Compress |pixels| as described by |info|. * + * Available since API level 30. + * * @param info Description of the pixels to compress. * @param dataspace {@link ADataSpace} describing the color space of the * pixels. @@ -234,6 +240,8 @@ typedef struct AHardwareBuffer AHardwareBuffer; * * Client must not modify it while a Bitmap is wrapping it. * + * Available since API level 30. + * * @param bitmap Handle to an android.graphics.Bitmap. * @param outBuffer On success, is set to a pointer to the * {@link AHardwareBuffer} associated with bitmap. This acquires diff --git a/include/android/choreographer.h b/include/android/choreographer.h index c1c4a72cd3..e9f559cd8e 100644 --- a/include/android/choreographer.h +++ b/include/android/choreographer.h @@ -129,9 +129,19 @@ void AChoreographer_postFrameCallbackDelayed64(AChoreographer* choreographer, * * This api is thread-safe. Any thread is allowed to register a new refresh * rate callback for the choreographer instance. + * + * Note that in API level 30, this api is not guaranteed to be atomic with + * DisplayManager. That is, calling Display#getRefreshRate very soon after + * a refresh rate callback is invoked may return a stale refresh rate. If any + * Display properties would be required by this callback, then it is recommended + * to listen directly to DisplayManager.DisplayListener#onDisplayChanged events + * instead. + * + * Available since API level 30. */ void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer, - AChoreographer_refreshRateCallback, void* data); + AChoreographer_refreshRateCallback, void* data) + __INTRODUCED_IN(30); /** * Unregisters a callback to be run when the display refresh rate changes, along @@ -144,9 +154,12 @@ void AChoreographer_registerRefreshRateCallback(AChoreographer* choreographer, * callback and associated data pointer are unregistered, then there is a * guarantee that when the unregistration completes that that callback will not * be run with the data pointer passed. + * + * Available since API level 30. */ void AChoreographer_unregisterRefreshRateCallback(AChoreographer* choreographer, - AChoreographer_refreshRateCallback, void* data); + AChoreographer_refreshRateCallback, void* data) + __INTRODUCED_IN(30); #endif /* __ANDROID_API__ >= 30 */ __END_DECLS diff --git a/include/android/configuration.h b/include/android/configuration.h index 05f43407fb..ccf3e59066 100644 --- a/include/android/configuration.h +++ b/include/android/configuration.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2010 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. @@ -58,13 +58,13 @@ enum { ACONFIGURATION_ORIENTATION_ANY = 0x0000, /** * Orientation: value corresponding to the - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#OrientationQualifier">port</a> + * <a href="/guide/topics/resources/providing-resources.html#OrientationQualifier">port</a> * resource qualifier. */ ACONFIGURATION_ORIENTATION_PORT = 0x0001, /** * Orientation: value corresponding to the - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#OrientationQualifier">land</a> + * <a href="/guide/topics/resources/providing-resources.html#OrientationQualifier">land</a> * resource qualifier. */ ACONFIGURATION_ORIENTATION_LAND = 0x0002, @@ -75,7 +75,7 @@ enum { ACONFIGURATION_TOUCHSCREEN_ANY = 0x0000, /** * Touchscreen: value corresponding to the - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#TouchscreenQualifier">notouch</a> + * <a href="/guide/topics/resources/providing-resources.html#TouchscreenQualifier">notouch</a> * resource qualifier. */ ACONFIGURATION_TOUCHSCREEN_NOTOUCH = 0x0001, @@ -83,7 +83,7 @@ enum { ACONFIGURATION_TOUCHSCREEN_STYLUS = 0x0002, /** * Touchscreen: value corresponding to the - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#TouchscreenQualifier">finger</a> + * <a href="/guide/topics/resources/providing-resources.html#TouchscreenQualifier">finger</a> * resource qualifier. */ ACONFIGURATION_TOUCHSCREEN_FINGER = 0x0003, @@ -92,43 +92,43 @@ enum { ACONFIGURATION_DENSITY_DEFAULT = 0, /** * Density: value corresponding to the - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">ldpi</a> + * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">ldpi</a> * resource qualifier. */ ACONFIGURATION_DENSITY_LOW = 120, /** * Density: value corresponding to the - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">mdpi</a> + * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">mdpi</a> * resource qualifier. */ ACONFIGURATION_DENSITY_MEDIUM = 160, /** * Density: value corresponding to the - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">tvdpi</a> + * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">tvdpi</a> * resource qualifier. */ ACONFIGURATION_DENSITY_TV = 213, /** * Density: value corresponding to the - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">hdpi</a> + * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">hdpi</a> * resource qualifier. */ ACONFIGURATION_DENSITY_HIGH = 240, /** * Density: value corresponding to the - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">xhdpi</a> + * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">xhdpi</a> * resource qualifier. */ ACONFIGURATION_DENSITY_XHIGH = 320, /** * Density: value corresponding to the - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">xxhdpi</a> + * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">xxhdpi</a> * resource qualifier. */ ACONFIGURATION_DENSITY_XXHIGH = 480, /** * Density: value corresponding to the - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">xxxhdpi</a> + * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">xxxhdpi</a> * resource qualifier. */ ACONFIGURATION_DENSITY_XXXHIGH = 640, @@ -141,19 +141,19 @@ enum { ACONFIGURATION_KEYBOARD_ANY = 0x0000, /** * Keyboard: value corresponding to the - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ImeQualifier">nokeys</a> + * <a href="/guide/topics/resources/providing-resources.html#ImeQualifier">nokeys</a> * resource qualifier. */ ACONFIGURATION_KEYBOARD_NOKEYS = 0x0001, /** * Keyboard: value corresponding to the - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ImeQualifier">qwerty</a> + * <a href="/guide/topics/resources/providing-resources.html#ImeQualifier">qwerty</a> * resource qualifier. */ ACONFIGURATION_KEYBOARD_QWERTY = 0x0002, /** * Keyboard: value corresponding to the - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ImeQualifier">12key</a> + * <a href="/guide/topics/resources/providing-resources.html#ImeQualifier">12key</a> * resource qualifier. */ ACONFIGURATION_KEYBOARD_12KEY = 0x0003, @@ -162,25 +162,25 @@ enum { ACONFIGURATION_NAVIGATION_ANY = 0x0000, /** * Navigation: value corresponding to the - * <a href="@@dacRoot/guide/topics/resources/providing-resources.html#NavigationQualifier">nonav</a> + * <a href="@/guide/topics/resources/providing-resources.html#NavigationQualifier">nonav</a> * resource qualifier. */ ACONFIGURATION_NAVIGATION_NONAV = 0x0001, /** * Navigation: value corresponding to the - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavigationQualifier">dpad</a> + * <a href="/guide/topics/resources/providing-resources.html#NavigationQualifier">dpad</a> * resource qualifier. */ ACONFIGURATION_NAVIGATION_DPAD = 0x0002, /** * Navigation: value corresponding to the - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavigationQualifier">trackball</a> + * <a href="/guide/topics/resources/providing-resources.html#NavigationQualifier">trackball</a> * resource qualifier. */ ACONFIGURATION_NAVIGATION_TRACKBALL = 0x0003, /** * Navigation: value corresponding to the - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavigationQualifier">wheel</a> + * <a href="/guide/topics/resources/providing-resources.html#NavigationQualifier">wheel</a> * resource qualifier. */ ACONFIGURATION_NAVIGATION_WHEEL = 0x0004, @@ -189,19 +189,19 @@ enum { ACONFIGURATION_KEYSHIDDEN_ANY = 0x0000, /** * Keyboard availability: value corresponding to the - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keysexposed</a> + * <a href="/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keysexposed</a> * resource qualifier. */ ACONFIGURATION_KEYSHIDDEN_NO = 0x0001, /** * Keyboard availability: value corresponding to the - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyshidden</a> + * <a href="/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyshidden</a> * resource qualifier. */ ACONFIGURATION_KEYSHIDDEN_YES = 0x0002, /** * Keyboard availability: value corresponding to the - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyssoft</a> + * <a href="/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyssoft</a> * resource qualifier. */ ACONFIGURATION_KEYSHIDDEN_SOFT = 0x0003, @@ -210,13 +210,13 @@ enum { ACONFIGURATION_NAVHIDDEN_ANY = 0x0000, /** * Navigation availability: value corresponding to the - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavAvailQualifier">navexposed</a> + * <a href="/guide/topics/resources/providing-resources.html#NavAvailQualifier">navexposed</a> * resource qualifier. */ ACONFIGURATION_NAVHIDDEN_NO = 0x0001, /** * Navigation availability: value corresponding to the - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavAvailQualifier">navhidden</a> + * <a href="/guide/topics/resources/providing-resources.html#NavAvailQualifier">navhidden</a> * resource qualifier. */ ACONFIGURATION_NAVHIDDEN_YES = 0x0002, @@ -226,28 +226,28 @@ enum { /** * Screen size: value indicating the screen is at least * approximately 320x426 dp units, corresponding to the - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">small</a> + * <a href="/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">small</a> * resource qualifier. */ ACONFIGURATION_SCREENSIZE_SMALL = 0x01, /** * Screen size: value indicating the screen is at least * approximately 320x470 dp units, corresponding to the - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">normal</a> + * <a href="/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">normal</a> * resource qualifier. */ ACONFIGURATION_SCREENSIZE_NORMAL = 0x02, /** * Screen size: value indicating the screen is at least * approximately 480x640 dp units, corresponding to the - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">large</a> + * <a href="/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">large</a> * resource qualifier. */ ACONFIGURATION_SCREENSIZE_LARGE = 0x03, /** * Screen size: value indicating the screen is at least * approximately 720x960 dp units, corresponding to the - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">xlarge</a> + * <a href="/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">xlarge</a> * resource qualifier. */ ACONFIGURATION_SCREENSIZE_XLARGE = 0x04, @@ -256,13 +256,13 @@ enum { ACONFIGURATION_SCREENLONG_ANY = 0x00, /** * Screen layout: value that corresponds to the - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenAspectQualifier">notlong</a> + * <a href="/guide/topics/resources/providing-resources.html#ScreenAspectQualifier">notlong</a> * resource qualifier. */ ACONFIGURATION_SCREENLONG_NO = 0x1, /** * Screen layout: value that corresponds to the - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenAspectQualifier">long</a> + * <a href="/guide/topics/resources/providing-resources.html#ScreenAspectQualifier">long</a> * resource qualifier. */ ACONFIGURATION_SCREENLONG_YES = 0x2, @@ -275,13 +275,13 @@ enum { ACONFIGURATION_WIDE_COLOR_GAMUT_ANY = 0x00, /** * Wide color gamut: value that corresponds to - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">no + * <a href="/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">no * nowidecg</a> resource qualifier specified. */ ACONFIGURATION_WIDE_COLOR_GAMUT_NO = 0x1, /** * Wide color gamut: value that corresponds to - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#WideColorGamutQualifier"> + * <a href="/guide/topics/resources/providing-resources.html#WideColorGamutQualifier"> * widecg</a> resource qualifier specified. */ ACONFIGURATION_WIDE_COLOR_GAMUT_YES = 0x2, @@ -290,13 +290,13 @@ enum { ACONFIGURATION_HDR_ANY = 0x00, /** * HDR: value that corresponds to - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#HDRQualifier"> + * <a href="/guide/topics/resources/providing-resources.html#HDRQualifier"> * lowdr</a> resource qualifier specified. */ ACONFIGURATION_HDR_NO = 0x1, /** * HDR: value that corresponds to - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#HDRQualifier"> + * <a href="/guide/topics/resources/providing-resources.html#HDRQualifier"> * highdr</a> resource qualifier specified. */ ACONFIGURATION_HDR_YES = 0x2, @@ -305,38 +305,38 @@ enum { ACONFIGURATION_UI_MODE_TYPE_ANY = 0x00, /** * UI mode: value that corresponds to - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">no + * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">no * UI mode type</a> resource qualifier specified. */ ACONFIGURATION_UI_MODE_TYPE_NORMAL = 0x01, /** * UI mode: value that corresponds to - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">desk</a> resource qualifier specified. + * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">desk</a> resource qualifier specified. */ ACONFIGURATION_UI_MODE_TYPE_DESK = 0x02, /** * UI mode: value that corresponds to - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">car</a> resource qualifier specified. + * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">car</a> resource qualifier specified. */ ACONFIGURATION_UI_MODE_TYPE_CAR = 0x03, /** * UI mode: value that corresponds to - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">television</a> resource qualifier specified. + * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">television</a> resource qualifier specified. */ ACONFIGURATION_UI_MODE_TYPE_TELEVISION = 0x04, /** * UI mode: value that corresponds to - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">appliance</a> resource qualifier specified. + * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">appliance</a> resource qualifier specified. */ ACONFIGURATION_UI_MODE_TYPE_APPLIANCE = 0x05, /** * UI mode: value that corresponds to - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">watch</a> resource qualifier specified. + * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">watch</a> resource qualifier specified. */ ACONFIGURATION_UI_MODE_TYPE_WATCH = 0x06, /** * UI mode: value that corresponds to - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">vr</a> resource qualifier specified. + * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">vr</a> resource qualifier specified. */ ACONFIGURATION_UI_MODE_TYPE_VR_HEADSET = 0x07, @@ -344,12 +344,12 @@ enum { ACONFIGURATION_UI_MODE_NIGHT_ANY = 0x00, /** * UI night mode: value that corresponds to - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NightQualifier">notnight</a> resource qualifier specified. + * <a href="/guide/topics/resources/providing-resources.html#NightQualifier">notnight</a> resource qualifier specified. */ ACONFIGURATION_UI_MODE_NIGHT_NO = 0x1, /** * UI night mode: value that corresponds to - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NightQualifier">night</a> resource qualifier specified. + * <a href="/guide/topics/resources/providing-resources.html#NightQualifier">night</a> resource qualifier specified. */ ACONFIGURATION_UI_MODE_NIGHT_YES = 0x2, @@ -366,78 +366,78 @@ enum { ACONFIGURATION_LAYOUTDIR_ANY = 0x00, /** * Layout direction: value that corresponds to - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">ldltr</a> resource qualifier specified. + * <a href="/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">ldltr</a> resource qualifier specified. */ ACONFIGURATION_LAYOUTDIR_LTR = 0x01, /** * Layout direction: value that corresponds to - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">ldrtl</a> resource qualifier specified. + * <a href="/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">ldrtl</a> resource qualifier specified. */ ACONFIGURATION_LAYOUTDIR_RTL = 0x02, /** * Bit mask for - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#MccQualifier">mcc</a> + * <a href="/guide/topics/resources/providing-resources.html#MccQualifier">mcc</a> * configuration. */ ACONFIGURATION_MCC = 0x0001, /** * Bit mask for - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#MccQualifier">mnc</a> + * <a href="/guide/topics/resources/providing-resources.html#MccQualifier">mnc</a> * configuration. */ ACONFIGURATION_MNC = 0x0002, /** * Bit mask for - * <a href="{@docRoot}guide/topics/resources/providing-resources.html#LocaleQualifier">locale</a> + * <a href="/guide/topics/resources/providing-resources.html#LocaleQualifier">locale</a> * configuration. */ ACONFIGURATION_LOCALE = 0x0004, /** * Bit mask for - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#TouchscreenQualifier">touchscreen</a> + * <a href="/guide/topics/resources/providing-resources.html#TouchscreenQualifier">touchscreen</a> * configuration. */ ACONFIGURATION_TOUCHSCREEN = 0x0008, /** * Bit mask for - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ImeQualifier">keyboard</a> + * <a href="/guide/topics/resources/providing-resources.html#ImeQualifier">keyboard</a> * configuration. */ ACONFIGURATION_KEYBOARD = 0x0010, /** * Bit mask for - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyboardHidden</a> + * <a href="/guide/topics/resources/providing-resources.html#KeyboardAvailQualifier">keyboardHidden</a> * configuration. */ ACONFIGURATION_KEYBOARD_HIDDEN = 0x0020, /** * Bit mask for - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#NavigationQualifier">navigation</a> + * <a href="/guide/topics/resources/providing-resources.html#NavigationQualifier">navigation</a> * configuration. */ ACONFIGURATION_NAVIGATION = 0x0040, /** * Bit mask for - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#OrientationQualifier">orientation</a> + * <a href="/guide/topics/resources/providing-resources.html#OrientationQualifier">orientation</a> * configuration. */ ACONFIGURATION_ORIENTATION = 0x0080, /** * Bit mask for - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#DensityQualifier">density</a> + * <a href="/guide/topics/resources/providing-resources.html#DensityQualifier">density</a> * configuration. */ ACONFIGURATION_DENSITY = 0x0100, /** * Bit mask for - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">screen size</a> + * <a href="/guide/topics/resources/providing-resources.html#ScreenSizeQualifier">screen size</a> * configuration. */ ACONFIGURATION_SCREEN_SIZE = 0x0200, /** * Bit mask for - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#VersionQualifier">platform version</a> + * <a href="/guide/topics/resources/providing-resources.html#VersionQualifier">platform version</a> * configuration. */ ACONFIGURATION_VERSION = 0x0400, @@ -447,27 +447,27 @@ enum { ACONFIGURATION_SCREEN_LAYOUT = 0x0800, /** * Bit mask for - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#UiModeQualifier">ui mode</a> + * <a href="/guide/topics/resources/providing-resources.html#UiModeQualifier">ui mode</a> * configuration. */ ACONFIGURATION_UI_MODE = 0x1000, /** * Bit mask for - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#SmallestScreenWidthQualifier">smallest screen width</a> + * <a href="/guide/topics/resources/providing-resources.html#SmallestScreenWidthQualifier">smallest screen width</a> * configuration. */ ACONFIGURATION_SMALLEST_SCREEN_SIZE = 0x2000, /** * Bit mask for - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">layout direction</a> + * <a href="/guide/topics/resources/providing-resources.html#LayoutDirectionQualifier">layout direction</a> * configuration. */ ACONFIGURATION_LAYOUTDIR = 0x4000, ACONFIGURATION_SCREEN_ROUND = 0x8000, /** * Bit mask for - * <a href="@dacRoot/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">wide color gamut</a> - * and <a href="@dacRoot/guide/topics/resources/providing-resources.html#HDRQualifier">HDR</a> configurations. + * <a href="/guide/topics/resources/providing-resources.html#WideColorGamutQualifier">wide color gamut</a> + * and <a href="/guide/topics/resources/providing-resources.html#HDRQualifier">HDR</a> configurations. */ ACONFIGURATION_COLOR_MODE = 0x10000, /** diff --git a/include/android/hardware_buffer_jni.h b/include/android/hardware_buffer_jni.h index 293e5ac469..ae208a6e75 100644 --- a/include/android/hardware_buffer_jni.h +++ b/include/android/hardware_buffer_jni.h @@ -39,9 +39,9 @@ __BEGIN_DECLS * Return the AHardwareBuffer wrapped by a Java HardwareBuffer object. * * This method does not acquire any additional reference to the AHardwareBuffer - * that is returned. To keep the AHardwareBuffer live after the Java - * HardwareBuffer object got garbage collected, be sure to use AHardwareBuffer_acquire() - * to acquire an additional reference. + * that is returned. To keep the AHardwareBuffer alive after the Java + * HardwareBuffer object is closed, explicitly or by the garbage collector, be + * sure to use AHardwareBuffer_acquire() to acquire an additional reference. * * Available since API level 26. */ @@ -50,7 +50,18 @@ AHardwareBuffer* AHardwareBuffer_fromHardwareBuffer(JNIEnv* env, /** * Return a new Java HardwareBuffer object that wraps the passed native - * AHardwareBuffer object. + * AHardwareBuffer object. The Java HardwareBuffer will acquire a reference to + * the internal buffer and manage its lifetime. For example: + * + * <pre><code> + * AHardwareBuffer* buffer; + * AHardwareBuffer_allocate(..., &buffer); // `buffer` has reference count 1 + * jobject java_result = AHardwareBuffer_toHardwareBuffer(buffer); // `buffer` has reference count 2. + * AHardwareBuffer_release(buffer); // `buffer` has reference count 1 + * return result; // The underlying buffer is kept alive by `java_result` and + * // will be set to 0 when it is closed on the Java side with + * // HardwareBuffer::close(). + * </code></pre> * * Available since API level 26. */ diff --git a/include/android/imagedecoder.h b/include/android/imagedecoder.h index 3a87da0fee..d7e6e4118f 100644 --- a/include/android/imagedecoder.h +++ b/include/android/imagedecoder.h @@ -133,6 +133,8 @@ typedef struct AImageDecoder AImageDecoder; /** * Create a new {@link AImageDecoder} from an {@link AAsset}. * + * Available since API level 30. + * * @param asset {@link AAsset} containing encoded image data. Client is still * responsible for calling {@link AAsset_close} on it, which may be * done after deleting the returned {@link AImageDecoder}. @@ -162,6 +164,8 @@ int AImageDecoder_createFromAAsset(struct AAsset* asset, AImageDecoder** outDeco /** * Create a new {@link AImageDecoder} from a file descriptor. * + * Available since API level 30. + * * @param fd Seekable, readable, open file descriptor for encoded data. * Client is still responsible for closing it, which may be done * after deleting the returned {@link AImageDecoder}. @@ -190,6 +194,8 @@ int AImageDecoder_createFromFd(int fd, AImageDecoder** outDecoder) __INTRODUCED_ /** * Create a new AImageDecoder from a buffer. * + * Available since API level 30. + * * @param buffer Pointer to encoded data. Must be valid for the entire time * the {@link AImageDecoder} is used. * @param length Byte length of buffer. @@ -217,12 +223,16 @@ int AImageDecoder_createFromBuffer(const void* buffer, size_t length, /** * Delete the AImageDecoder. + * + * Available since API level 30. */ void AImageDecoder_delete(AImageDecoder* decoder) __INTRODUCED_IN(30); /** * Choose the desired output format. * + * Available since API level 30. + * * @param format {@link AndroidBitmapFormat} to use for the output. * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value * indicating the reason for the failure. On failure, the @@ -247,6 +257,8 @@ int AImageDecoder_setAndroidBitmapFormat(AImageDecoder*, * Pass true to this method to leave them unpremultiplied. This has no effect on an * opaque image. * + * Available since API level 30. + * * @param unpremultipliedRequired Pass true to leave the pixels unpremultiplied. * @return {@link ANDROID_IMAGE_DECODER_SUCCESS} on success or a value * indicating the reason for the failure. @@ -267,6 +279,8 @@ int AImageDecoder_setUnpremultipliedRequired(AImageDecoder*, * Ignored by {@link ANDROID_BITMAP_FORMAT_A_8}, which does not support * an {@link ADataSpace}. * + * Available since API level 30. + * * @param dataspace The {@link ADataSpace} to decode into. An ADataSpace * specifies how to interpret the colors. By default, * AImageDecoder will decode into the ADataSpace specified by @@ -292,6 +306,8 @@ int AImageDecoder_setDataSpace(AImageDecoder*, int32_t dataspace) __INTRODUCED_I * specified by width and height, and the output image will be the size of the * crop rect. * + * Available since API level 30. + * * @param width Width of the output (prior to cropping). * This will affect future calls to * {@link AImageDecoder_getMinimumStride}, which will now return @@ -319,6 +335,8 @@ int AImageDecoder_setTargetSize(AImageDecoder*, int32_t width, int32_t height) _ * others. This computes the most efficient target size to use to reach a * particular sampleSize. * + * Available since API level 30. + * * @param sampleSize A subsampling rate of the original image. Must be greater * than or equal to 1. A sampleSize of 2 means to skip every * other pixel/line, resulting in a width and height that are @@ -344,6 +362,8 @@ int AImageDecoder_computeSampledSize(const AImageDecoder*, int sampleSize, * the specified {@link ARect}. Clients will only need to allocate enough memory * for the cropped ARect. * + * Available since API level 30. + * * @param crop Rectangle describing a crop of the decode. It must be contained inside of * the (possibly scaled, by {@link AImageDecoder_setTargetSize}) * image dimensions. This will affect future calls to @@ -376,6 +396,8 @@ typedef struct AImageDecoderHeaderInfo AImageDecoderHeaderInfo; * * This is owned by the {@link AImageDecoder} and will be destroyed when the * AImageDecoder is destroyed via {@link AImageDecoder_delete}. + * + * Available since API level 30. */ const AImageDecoderHeaderInfo* AImageDecoder_getHeaderInfo( const AImageDecoder*) __INTRODUCED_IN(30); @@ -385,6 +407,8 @@ const AImageDecoderHeaderInfo* AImageDecoder_getHeaderInfo( * pixel width of the output, unless {@link AImageDecoder_setTargetSize} is * used to choose a different size or {@link AImageDecoder_setCrop} is used to * set a crop rect. + * + * Available since API level 30. */ int32_t AImageDecoderHeaderInfo_getWidth(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); @@ -393,12 +417,16 @@ int32_t AImageDecoderHeaderInfo_getWidth(const AImageDecoderHeaderInfo*) __INTRO * pixel height of the output, unless {@link AImageDecoder_setTargetSize} is * used to choose a different size or {@link AImageDecoder_setCrop} is used to * set a crop rect. + * + * Available since API level 30. */ int32_t AImageDecoderHeaderInfo_getHeight(const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); /** * Report the mimeType of the encoded image. * + * Available since API level 30. + * * @return a string literal describing the mime type. */ const char* AImageDecoderHeaderInfo_getMimeType( @@ -409,6 +437,8 @@ const char* AImageDecoderHeaderInfo_getMimeType( * by default. {@link AImageDecoder} will try to choose one that is sensible * for the image and the system. Note that this does not indicate the * encoded format of the image. + * + * Available since API level 30. */ int32_t AImageDecoderHeaderInfo_getAndroidBitmapFormat( const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); @@ -419,6 +449,8 @@ int32_t AImageDecoderHeaderInfo_getAndroidBitmapFormat( * {@link ANDROID_BITMAP_FLAGS_ALPHA_OPAQUE}. If the image may contain alpha, * this returns {@link ANDROID_BITMAP_FLAGS_ALPHA_PREMUL}, because * {@link AImageDecoder_decodeImage} will premultiply pixels by default. + * + * Available since API level 30. */ int AImageDecoderHeaderInfo_getAlphaFlags( const AImageDecoderHeaderInfo*) __INTRODUCED_IN(30); @@ -429,6 +461,8 @@ int AImageDecoderHeaderInfo_getAlphaFlags( * By default, {@link AImageDecoder_decodeImage} will not do any color * conversion. * + * Available since API level 30. + * * @return The {@link ADataSpace} representing the way the colors * are encoded (or {@link ADATASPACE_UNKNOWN} if there is not a * corresponding ADataSpace). This specifies how to interpret the colors @@ -452,12 +486,16 @@ int32_t AImageDecoderHeaderInfo_getDataSpace( * * If the output is scaled (via {@link AImageDecoder_setTargetSize}) and/or * cropped (via {@link AImageDecoder_setCrop}), this takes those into account. + * + * Available since API level 30. */ size_t AImageDecoder_getMinimumStride(AImageDecoder*) __INTRODUCED_IN(30); /** * Decode the image into pixels, using the settings of the {@link AImageDecoder}. * + * Available since API level 30. + * * @param decoder Opaque object representing the decoder. * @param pixels On success, will be filled with the result * of the decode. Must be large enough to hold |size| bytes. diff --git a/include/android/sharedmem.h b/include/android/sharedmem.h index 6efa4f71cb..5f74682b40 100644 --- a/include/android/sharedmem.h +++ b/include/android/sharedmem.h @@ -65,6 +65,10 @@ extern "C" { * another process. File descriptors may also be sent to other processes over a Unix domain * socket with sendmsg and SCM_RIGHTS. See sendmsg(3) and cmsg(3) man pages for more information. * + * If you intend to share this file descriptor with a child process after + * calling exec(3), note that you will need to use fcntl(2) with FD_SETFD + * to clear the FD_CLOEXEC flag for this to work on all versions of Android. + * * Available since API level 26. * * \param name an optional name. diff --git a/include/android/thermal.h b/include/android/thermal.h index 3247fa167b..83582d6791 100644 --- a/include/android/thermal.h +++ b/include/android/thermal.h @@ -60,8 +60,6 @@ extern "C" { #endif -#if __ANDROID_API__ >= 30 - enum AThermalStatus { /** Error in thermal status. */ ATHERMAL_STATUS_ERROR = -1, @@ -111,36 +109,45 @@ typedef struct AThermalManager AThermalManager; */ typedef void (*AThermal_StatusCallback)(void *data, AThermalStatus status); +#if __ANDROID_API__ >= 30 + /** * Acquire an instance of the thermal manager. This must be freed using * {@link AThermal_releaseManager}. * + * Available since API level 30. + * * @return manager instance on success, nullptr on failure. - */ -AThermalManager* AThermal_acquireManager(); + */ +AThermalManager* AThermal_acquireManager() __INTRODUCED_IN(30); /** * Release the thermal manager pointer acquired via * {@link AThermal_acquireManager}. * - * @param manager The manager to be released. + * Available since API level 30. * + * @param manager The manager to be released. */ -void AThermal_releaseManager(AThermalManager *manager); +void AThermal_releaseManager(AThermalManager *manager) __INTRODUCED_IN(30); /** * Gets the current thermal status. * + * Available since API level 30. + * * @param manager The manager instance to use to query the thermal status. * Acquired via {@link AThermal_acquireManager}. * * @return current thermal status, ATHERMAL_STATUS_ERROR on failure. -*/ -AThermalStatus AThermal_getCurrentThermalStatus(AThermalManager *manager); + */ +AThermalStatus AThermal_getCurrentThermalStatus(AThermalManager *manager) __INTRODUCED_IN(30); /** * Register the thermal status listener for thermal status change. * + * Available since API level 30. + * * @param manager The manager instance to use to register. * Acquired via {@link AThermal_acquireManager}. * @param callback The callback function to be called when thermal status updated. @@ -152,11 +159,13 @@ AThermalStatus AThermal_getCurrentThermalStatus(AThermalManager *manager); * EPIPE if communication with the system service has failed. */ int AThermal_registerThermalStatusListener(AThermalManager *manager, - AThermal_StatusCallback callback, void *data); + AThermal_StatusCallback callback, void *data) __INTRODUCED_IN(30); /** * Unregister the thermal status listener previously resgistered. * + * Available since API level 30. + * * @param manager The manager instance to use to unregister. * Acquired via {@link AThermal_acquireManager}. * @param callback The callback function to be called when thermal status updated. @@ -168,8 +177,7 @@ int AThermal_registerThermalStatusListener(AThermalManager *manager, * EPIPE if communication with the system service has failed. */ int AThermal_unregisterThermalStatusListener(AThermalManager *manager, - AThermal_StatusCallback callback, void *data); - + AThermal_StatusCallback callback, void *data) __INTRODUCED_IN(30); #endif // __ANDROID_API__ >= 30 diff --git a/include/android/trace.h b/include/android/trace.h index d59690ab2e..dbad6f6f21 100644 --- a/include/android/trace.h +++ b/include/android/trace.h @@ -115,7 +115,7 @@ void ATrace_setCounter(const char* counterName, int64_t counterValue) __INTRODUC #endif /* __ANDROID_API__ >= 29 */ #ifdef __cplusplus -}; +} #endif #endif // ANDROID_NATIVE_TRACE_H diff --git a/include/input/Keyboard.h b/include/input/Keyboard.h index 92da10c056..0a00241f8c 100644 --- a/include/input/Keyboard.h +++ b/include/input/Keyboard.h @@ -20,8 +20,8 @@ #include <input/Input.h> #include <input/InputDevice.h> #include <input/InputEventLabels.h> +#include <input/PropertyMap.h> #include <utils/Errors.h> -#include <utils/PropertyMap.h> namespace android { diff --git a/include/input/PropertyMap.h b/include/input/PropertyMap.h new file mode 100644 index 0000000000..3d0433133c --- /dev/null +++ b/include/input/PropertyMap.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2010 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 _UTILS_PROPERTY_MAP_H +#define _UTILS_PROPERTY_MAP_H + +#include <utils/Errors.h> +#include <utils/KeyedVector.h> +#include <utils/String8.h> +#include <utils/Tokenizer.h> + +namespace android { + +/* + * Provides a mechanism for passing around string-based property key / value pairs + * and loading them from property files. + * + * The property files have the following simple structure: + * + * # Comment + * key = value + * + * Keys and values are any sequence of printable ASCII characters. + * The '=' separates the key from the value. + * The key and value may not contain whitespace. + * + * The '\' character is reserved for escape sequences and is not currently supported. + * The '"" character is reserved for quoting and is not currently supported. + * Files that contain the '\' or '"' character will fail to parse. + * + * The file must not contain duplicate keys. + * + * TODO Support escape sequences and quoted values when needed. + */ +class PropertyMap { +public: + /* Creates an empty property map. */ + PropertyMap(); + ~PropertyMap(); + + /* Clears the property map. */ + void clear(); + + /* Adds a property. + * Replaces the property with the same key if it is already present. + */ + void addProperty(const String8& key, const String8& value); + + /* Returns true if the property map contains the specified key. */ + bool hasProperty(const String8& key) const; + + /* Gets the value of a property and parses it. + * Returns true and sets outValue if the key was found and its value was parsed successfully. + * Otherwise returns false and does not modify outValue. (Also logs a warning.) + */ + bool tryGetProperty(const String8& key, String8& outValue) const; + bool tryGetProperty(const String8& key, bool& outValue) const; + bool tryGetProperty(const String8& key, int32_t& outValue) const; + bool tryGetProperty(const String8& key, float& outValue) const; + + /* Adds all values from the specified property map. */ + void addAll(const PropertyMap* map); + + /* Gets the underlying property map. */ + inline const KeyedVector<String8, String8>& getProperties() const { return mProperties; } + + /* Loads a property map from a file. */ + static status_t load(const String8& filename, PropertyMap** outMap); + +private: + class Parser { + PropertyMap* mMap; + Tokenizer* mTokenizer; + + public: + Parser(PropertyMap* map, Tokenizer* tokenizer); + ~Parser(); + status_t parse(); + + private: + status_t parseType(); + status_t parseKey(); + status_t parseKeyProperty(); + status_t parseModifier(const String8& token, int32_t* outMetaState); + status_t parseCharacterLiteral(char16_t* outCharacter); + }; + + KeyedVector<String8, String8> mProperties; +}; + +} // namespace android + +#endif // _UTILS_PROPERTY_MAP_H diff --git a/include/powermanager/IPowerManager.h b/include/powermanager/IPowerManager.h index 853f0c9fde..964e318584 100644 --- a/include/powermanager/IPowerManager.h +++ b/include/powermanager/IPowerManager.h @@ -46,10 +46,10 @@ public: IS_POWER_SAVE_MODE = IBinder::FIRST_CALL_TRANSACTION + 12, GET_POWER_SAVE_STATE = IBinder::FIRST_CALL_TRANSACTION + 13, SET_POWER_SAVE_MODE_ENABLED = IBinder::FIRST_CALL_TRANSACTION + 14, - REBOOT = IBinder::FIRST_CALL_TRANSACTION + 17, - REBOOT_SAFE_MODE = IBinder::FIRST_CALL_TRANSACTION + 18, - SHUTDOWN = IBinder::FIRST_CALL_TRANSACTION + 19, - CRASH = IBinder::FIRST_CALL_TRANSACTION + 20, + REBOOT = IBinder::FIRST_CALL_TRANSACTION + 21, + REBOOT_SAFE_MODE = IBinder::FIRST_CALL_TRANSACTION + 22, + SHUTDOWN = IBinder::FIRST_CALL_TRANSACTION + 23, + CRASH = IBinder::FIRST_CALL_TRANSACTION + 24, }; DECLARE_META_INTERFACE(PowerManager) diff --git a/libs/arect/Android.bp b/libs/arect/Android.bp index 258a4e3748..80aa8916da 100644 --- a/libs/arect/Android.bp +++ b/libs/arect/Android.bp @@ -22,6 +22,8 @@ ndk_headers { cc_library_headers { name: "libarect_headers", + // TODO(b/153609531): remove when no longer needed. + native_bridge_supported: true, export_include_dirs: ["include"], } diff --git a/libs/binder/Android.bp b/libs/binder/Android.bp index b24a577588..9675a5321e 100644 --- a/libs/binder/Android.bp +++ b/libs/binder/Android.bp @@ -78,7 +78,7 @@ cc_library { // or dessert updates. Instead, apex users should use libbinder_ndk. apex_available: [ "//apex_available:platform", - // TODO(b/139016109) remove these three + // TODO(b/166468760) remove these three "com.android.media.swcodec", "test_com.android.media.swcodec", ], @@ -127,6 +127,10 @@ cc_library { export_aidl_headers: true, }, + // TODO(b/142684679): for com.android.media which is compiled + // as vendor and used as system code. + use_apex_name_macro: true, + cflags: [ "-Wall", "-Wextra", diff --git a/libs/binder/AppOpsManager.cpp b/libs/binder/AppOpsManager.cpp index 2174ce2a88..1c6b49135d 100644 --- a/libs/binder/AppOpsManager.cpp +++ b/libs/binder/AppOpsManager.cpp @@ -91,12 +91,12 @@ int32_t AppOpsManager::checkAudioOpNoThrow(int32_t op, int32_t usage, int32_t ui } int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage) { - return noteOp(op, uid, callingPackage, std::unique_ptr<String16>(), + return noteOp(op, uid, callingPackage, {}, String16("Legacy AppOpsManager.noteOp call")); } int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPackage, - const std::unique_ptr<String16>& attributionTag, const String16& message) { + const std::optional<String16>& attributionTag, const String16& message) { sp<IAppOpsService> service = getService(); int32_t mode = service != nullptr ? service->noteOperation(op, uid, callingPackage, attributionTag, @@ -108,12 +108,12 @@ int32_t AppOpsManager::noteOp(int32_t op, int32_t uid, const String16& callingPa int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage, bool startIfModeDefault) { - return startOpNoThrow(op, uid, callingPackage, startIfModeDefault, std::unique_ptr<String16>(), + return startOpNoThrow(op, uid, callingPackage, startIfModeDefault, {}, String16("Legacy AppOpsManager.startOpNoThrow call")); } int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage, - bool startIfModeDefault, const std::unique_ptr<String16>& attributionTag, + bool startIfModeDefault, const std::optional<String16>& attributionTag, const String16& message) { sp<IAppOpsService> service = getService(); int32_t mode = service != nullptr @@ -125,11 +125,11 @@ int32_t AppOpsManager::startOpNoThrow(int32_t op, int32_t uid, const String16& c } void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage) { - finishOp(op, uid, callingPackage, std::unique_ptr<String16>()); + finishOp(op, uid, callingPackage, {}); } void AppOpsManager::finishOp(int32_t op, int32_t uid, const String16& callingPackage, - const std::unique_ptr<String16>& attributionTag) { + const std::optional<String16>& attributionTag) { sp<IAppOpsService> service = getService(); if (service != nullptr) { service->finishOperation(getClientId(), op, uid, callingPackage, attributionTag); diff --git a/libs/binder/BpBinder.cpp b/libs/binder/BpBinder.cpp index d2b9b8f018..c183d29773 100644 --- a/libs/binder/BpBinder.cpp +++ b/libs/binder/BpBinder.cpp @@ -224,7 +224,7 @@ status_t BpBinder::transact( using android::internal::Stability; auto stability = Stability::get(this); - auto required = privateVendor ? Stability::VENDOR : Stability::kLocalStability; + auto required = privateVendor ? Stability::VENDOR : Stability::getLocalStability(); if (CC_UNLIKELY(!Stability::check(stability, required))) { ALOGE("Cannot do a user transaction on a %s binder in a %s context.", diff --git a/libs/binder/IAppOpsService.cpp b/libs/binder/IAppOpsService.cpp index 0714723e02..cd78866624 100644 --- a/libs/binder/IAppOpsService.cpp +++ b/libs/binder/IAppOpsService.cpp @@ -22,6 +22,8 @@ #include <binder/Parcel.h> #include <utils/String8.h> +#include <optional> + namespace android { // ---------------------------------------------------------------------- @@ -47,7 +49,7 @@ public: } virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName, - const std::unique_ptr<String16>& attributionTag, bool shouldCollectAsyncNotedOp, + const std::optional<String16>& attributionTag, bool shouldCollectAsyncNotedOp, const String16& message) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); @@ -64,7 +66,7 @@ public: } virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid, - const String16& packageName, const std::unique_ptr<String16>& attributionTag, + const String16& packageName, const std::optional<String16>& attributionTag, bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); @@ -83,7 +85,7 @@ public: } virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid, - const String16& packageName, const std::unique_ptr<String16>& attributionTag) { + const String16& packageName, const std::optional<String16>& attributionTag) { Parcel data, reply; data.writeInterfaceToken(IAppOpsService::getInterfaceDescriptor()); data.writeStrongBinder(token); @@ -182,7 +184,7 @@ status_t BnAppOpsService::onTransact( int32_t code = data.readInt32(); int32_t uid = data.readInt32(); String16 packageName = data.readString16(); - std::unique_ptr<String16> attributionTag; + std::optional<String16> attributionTag; data.readString16(&attributionTag); bool shouldCollectAsyncNotedOp = data.readInt32() == 1; String16 message = data.readString16(); @@ -198,7 +200,7 @@ status_t BnAppOpsService::onTransact( int32_t code = data.readInt32(); int32_t uid = data.readInt32(); String16 packageName = data.readString16(); - std::unique_ptr<String16> attributionTag; + std::optional<String16> attributionTag; data.readString16(&attributionTag); bool startIfModeDefault = data.readInt32() == 1; bool shouldCollectAsyncNotedOp = data.readInt32() == 1; @@ -215,7 +217,7 @@ status_t BnAppOpsService::onTransact( int32_t code = data.readInt32(); int32_t uid = data.readInt32(); String16 packageName = data.readString16(); - std::unique_ptr<String16> attributionTag; + std::optional<String16> attributionTag; data.readString16(&attributionTag); finishOperation(token, code, uid, packageName, attributionTag); reply->writeNoException(); diff --git a/libs/binder/IMemory.cpp b/libs/binder/IMemory.cpp index c2bb811e9f..d8b44f957d 100644 --- a/libs/binder/IMemory.cpp +++ b/libs/binder/IMemory.cpp @@ -82,10 +82,10 @@ public: explicit BpMemoryHeap(const sp<IBinder>& impl); virtual ~BpMemoryHeap(); - virtual int getHeapID() const; - virtual void* getBase() const; - virtual size_t getSize() const; - virtual uint32_t getFlags() const; + int getHeapID() const override; + void* getBase() const override; + size_t getSize() const override; + uint32_t getFlags() const override; off_t getOffset() const override; private: diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index 7d008e25ad..0c71ed81a4 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -614,7 +614,7 @@ void IPCThreadState::joinThreadPool(bool isMain) talkWithDriver(false); } -int IPCThreadState::setupPolling(int* fd) +status_t IPCThreadState::setupPolling(int* fd) { if (mProcess->mDriverFD < 0) { return -EBADF; @@ -679,7 +679,7 @@ status_t IPCThreadState::transact(int32_t handle, CallStack::logStack("non-oneway call", CallStack::getCurrent(10).get(), ANDROID_LOG_ERROR); } else /* FATAL_IF_NOT_ONEWAY */ { - LOG_ALWAYS_FATAL("Process may not make oneway calls (code: %u).", code); + LOG_ALWAYS_FATAL("Process may not make non-oneway calls (code: %u).", code); } } diff --git a/libs/binder/IServiceManager.cpp b/libs/binder/IServiceManager.cpp index a9f2d73951..9aa82d908c 100644 --- a/libs/binder/IServiceManager.cpp +++ b/libs/binder/IServiceManager.cpp @@ -18,6 +18,9 @@ #include <binder/IServiceManager.h> +#include <inttypes.h> +#include <unistd.h> + #include <android/os/BnServiceCallback.h> #include <android/os/IServiceManager.h> #include <binder/IPCThreadState.h> @@ -36,8 +39,6 @@ #include "Static.h" -#include <unistd.h> - namespace android { using AidlServiceManager = android::os::IServiceManager; @@ -206,6 +207,10 @@ ServiceManagerShim::ServiceManagerShim(const sp<AidlServiceManager>& impl) : mTheRealServiceManager(impl) {} +// This implementation could be simplified and made more efficient by delegating +// to waitForService. However, this changes the threading structure in some +// cases and could potentially break prebuilts. Once we have higher logistical +// complexity, this could be attempted. sp<IBinder> ServiceManagerShim::getService(const String16& name) const { static bool gSystemBootCompleted = false; @@ -215,7 +220,8 @@ sp<IBinder> ServiceManagerShim::getService(const String16& name) const const bool isVendorService = strcmp(ProcessState::self()->getDriverName().c_str(), "/dev/vndbinder") == 0; - const long timeout = uptimeMillis() + 5000; + const long timeout = 5000; + int64_t startTime = uptimeMillis(); // Vendor code can't access system properties if (!gSystemBootCompleted && !isVendorService) { #ifdef __ANDROID__ @@ -229,15 +235,21 @@ sp<IBinder> ServiceManagerShim::getService(const String16& name) const // retry interval in millisecond; note that vendor services stay at 100ms const long sleepTime = gSystemBootCompleted ? 1000 : 100; + ALOGI("Waiting for service '%s' on '%s'...", String8(name).string(), + ProcessState::self()->getDriverName().c_str()); + int n = 0; - while (uptimeMillis() < timeout) { + while (uptimeMillis() - startTime < timeout) { n++; - ALOGI("Waiting for service '%s' on '%s'...", String8(name).string(), - ProcessState::self()->getDriverName().c_str()); usleep(1000*sleepTime); sp<IBinder> svc = checkService(name); - if (svc != nullptr) return svc; + if (svc != nullptr) { + ALOGI("Waiting for service '%s' on '%s' successful after waiting %" PRIi64 "ms", + String8(name).string(), ProcessState::self()->getDriverName().c_str(), + uptimeMillis() - startTime); + return svc; + } } ALOGW("Service %s didn't start. Returning NULL", String8(name).string()); return nullptr; @@ -322,6 +334,11 @@ sp<IBinder> ServiceManagerShim::waitForService(const String16& name16) while(true) { { + // It would be really nice if we could read binder commands on this + // thread instead of needing a threadpool to be started, but for + // instance, if we call getAndExecuteCommand, it might be the case + // that another thread serves the callback, and we never get a + // command, so we hang indefinitely. std::unique_lock<std::mutex> lock(waiter->mMutex); using std::literals::chrono_literals::operator""s; waiter->mCv.wait_for(lock, 1s, [&] { @@ -330,6 +347,8 @@ sp<IBinder> ServiceManagerShim::waitForService(const String16& name16) if (waiter->mBinder != nullptr) return waiter->mBinder; } + ALOGW("Waited one second for %s (is service started? are binder threads started and available?)", name.c_str()); + // Handle race condition for lazy services. Here is what can happen: // - the service dies (not processed by init yet). // - sm processes death notification. @@ -343,8 +362,6 @@ sp<IBinder> ServiceManagerShim::waitForService(const String16& name16) return nullptr; } if (out != nullptr) return out; - - ALOGW("Waited one second for %s", name.c_str()); } } diff --git a/libs/binder/LazyServiceRegistrar.cpp b/libs/binder/LazyServiceRegistrar.cpp index 6f49aa1607..325e204203 100644 --- a/libs/binder/LazyServiceRegistrar.cpp +++ b/libs/binder/LazyServiceRegistrar.cpp @@ -45,25 +45,31 @@ protected: Status onClients(const sp<IBinder>& service, bool clients) override; private: + struct Service { + sp<IBinder> service; + bool allowIsolated; + int dumpFlags; + + // whether, based on onClients calls, we know we have a client for this + // service or not + bool clients = false; + }; + + /** + * Looks up a service guaranteed to be registered (service from onClients). + */ + std::map<std::string, Service>::iterator assertRegisteredService(const sp<IBinder>& service); + /** * Unregisters all services that we can. If we can't unregister all, re-register other * services. */ void tryShutdown(); - /** - * Counter of the number of services that currently have at least one client. - */ + // count of services with clients size_t mNumConnectedServices; - struct Service { - sp<IBinder> service; - bool allowIsolated; - int dumpFlags; - }; - /** - * Map of registered names and services - */ + // map of registered names and services std::map<std::string, Service> mRegisteredServices; bool mForcePersist; @@ -89,12 +95,28 @@ bool ClientCounterCallback::registerService(const sp<IBinder>& service, const st } // Only add this when a service is added for the first time, as it is not removed - mRegisteredServices[name] = {service, allowIsolated, dumpFlags}; + mRegisteredServices[name] = { + .service = service, + .allowIsolated = allowIsolated, + .dumpFlags = dumpFlags + }; } return true; } +std::map<std::string, ClientCounterCallback::Service>::iterator ClientCounterCallback::assertRegisteredService(const sp<IBinder>& service) { + LOG_ALWAYS_FATAL_IF(service == nullptr, "Got onClients callback for null service"); + for (auto it = mRegisteredServices.begin(); it != mRegisteredServices.end(); ++it) { + auto const& [name, registered] = *it; + (void) name; + if (registered.service != service) continue; + return it; + } + LOG_ALWAYS_FATAL("Got callback on service which we did not register: %s", String8(service->getInterfaceDescriptor()).c_str()); + __builtin_unreachable(); +} + void ClientCounterCallback::forcePersist(bool persist) { mForcePersist = persist; if(!mForcePersist) { @@ -108,15 +130,25 @@ void ClientCounterCallback::forcePersist(bool persist) { * invocations could occur on different threads however. */ Status ClientCounterCallback::onClients(const sp<IBinder>& service, bool clients) { - if (clients) { - mNumConnectedServices++; - } else { - mNumConnectedServices--; + auto & [name, registered] = *assertRegisteredService(service); + if (registered.clients == clients) { + LOG_ALWAYS_FATAL("Process already thought %s had clients: %d but servicemanager has " + "notified has clients: %d", name.c_str(), registered.clients, clients); + } + registered.clients = clients; + + // update cache count of clients + { + size_t numWithClients = 0; + for (const auto& [name, registered] : mRegisteredServices) { + (void) name; + if (registered.clients) numWithClients++; + } + mNumConnectedServices = numWithClients; } ALOGI("Process has %zu (of %zu available) client(s) in use after notification %s has clients: %d", - mNumConnectedServices, mRegisteredServices.size(), - String8(service->getInterfaceDescriptor()).string(), clients); + mNumConnectedServices, mRegisteredServices.size(), name.c_str(), clients); tryShutdown(); return Status::ok(); @@ -192,4 +224,4 @@ void LazyServiceRegistrar::forcePersist(bool persist) { } } // namespace hardware -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/libs/binder/MemoryDealer.cpp b/libs/binder/MemoryDealer.cpp index ebf91f925e..b46b3e88fc 100644 --- a/libs/binder/MemoryDealer.cpp +++ b/libs/binder/MemoryDealer.cpp @@ -387,7 +387,7 @@ SimpleBestFitAllocator::chunk_t* SimpleBestFitAllocator::dealloc(size_t start) while (cur) { if (cur->start == start) { LOG_FATAL_IF(cur->free, - "block at offset 0x%08lX of size 0x%08lX already freed", + "block at offset 0x%08lX of size 0x%08X already freed", cur->start*kMemoryAlign, cur->size*kMemoryAlign); // merge freed blocks together @@ -411,7 +411,7 @@ SimpleBestFitAllocator::chunk_t* SimpleBestFitAllocator::dealloc(size_t start) } #endif LOG_FATAL_IF(!freed->free, - "freed block at offset 0x%08lX of size 0x%08lX is not free!", + "freed block at offset 0x%08lX of size 0x%08X is not free!", freed->start * kMemoryAlign, freed->size * kMemoryAlign); return freed; diff --git a/libs/binder/Parcel.cpp b/libs/binder/Parcel.cpp index a5549fc07d..14ab60ffc2 100644 --- a/libs/binder/Parcel.cpp +++ b/libs/binder/Parcel.cpp @@ -41,7 +41,6 @@ #include <binder/TextOutput.h> #include <cutils/ashmem.h> -#include <utils/Debug.h> #include <utils/Flattenable.h> #include <utils/Log.h> #include <utils/misc.h> @@ -763,6 +762,13 @@ status_t Parcel::writeUtf8AsUtf16(const std::string& str) { return NO_ERROR; } +status_t Parcel::writeUtf8AsUtf16(const std::optional<std::string>& str) { + if (!str) { + return writeInt32(-1); + } + return writeUtf8AsUtf16(*str); +} + status_t Parcel::writeUtf8AsUtf16(const std::unique_ptr<std::string>& str) { if (!str) { return writeInt32(-1); @@ -787,6 +793,12 @@ status_t Parcel::writeByteVector(const std::vector<int8_t>& val) { return writeByteVectorInternal(val.data(), val.size()); } +status_t Parcel::writeByteVector(const std::optional<std::vector<int8_t>>& val) +{ + if (!val) return writeInt32(-1); + return writeByteVectorInternal(val->data(), val->size()); +} + status_t Parcel::writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val) { if (!val) return writeInt32(-1); @@ -797,6 +809,12 @@ status_t Parcel::writeByteVector(const std::vector<uint8_t>& val) { return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val.data()), val.size()); } +status_t Parcel::writeByteVector(const std::optional<std::vector<uint8_t>>& val) +{ + if (!val) return writeInt32(-1); + return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val->data()), val->size()); +} + status_t Parcel::writeByteVector(const std::unique_ptr<std::vector<uint8_t>>& val) { if (!val) return writeInt32(-1); @@ -808,6 +826,11 @@ status_t Parcel::writeInt32Vector(const std::vector<int32_t>& val) return writeTypedVector(val, &Parcel::writeInt32); } +status_t Parcel::writeInt32Vector(const std::optional<std::vector<int32_t>>& val) +{ + return writeNullableTypedVector(val, &Parcel::writeInt32); +} + status_t Parcel::writeInt32Vector(const std::unique_ptr<std::vector<int32_t>>& val) { return writeNullableTypedVector(val, &Parcel::writeInt32); @@ -818,6 +841,11 @@ status_t Parcel::writeInt64Vector(const std::vector<int64_t>& val) return writeTypedVector(val, &Parcel::writeInt64); } +status_t Parcel::writeInt64Vector(const std::optional<std::vector<int64_t>>& val) +{ + return writeNullableTypedVector(val, &Parcel::writeInt64); +} + status_t Parcel::writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& val) { return writeNullableTypedVector(val, &Parcel::writeInt64); @@ -828,6 +856,11 @@ status_t Parcel::writeUint64Vector(const std::vector<uint64_t>& val) return writeTypedVector(val, &Parcel::writeUint64); } +status_t Parcel::writeUint64Vector(const std::optional<std::vector<uint64_t>>& val) +{ + return writeNullableTypedVector(val, &Parcel::writeUint64); +} + status_t Parcel::writeUint64Vector(const std::unique_ptr<std::vector<uint64_t>>& val) { return writeNullableTypedVector(val, &Parcel::writeUint64); @@ -838,6 +871,11 @@ status_t Parcel::writeFloatVector(const std::vector<float>& val) return writeTypedVector(val, &Parcel::writeFloat); } +status_t Parcel::writeFloatVector(const std::optional<std::vector<float>>& val) +{ + return writeNullableTypedVector(val, &Parcel::writeFloat); +} + status_t Parcel::writeFloatVector(const std::unique_ptr<std::vector<float>>& val) { return writeNullableTypedVector(val, &Parcel::writeFloat); @@ -848,6 +886,11 @@ status_t Parcel::writeDoubleVector(const std::vector<double>& val) return writeTypedVector(val, &Parcel::writeDouble); } +status_t Parcel::writeDoubleVector(const std::optional<std::vector<double>>& val) +{ + return writeNullableTypedVector(val, &Parcel::writeDouble); +} + status_t Parcel::writeDoubleVector(const std::unique_ptr<std::vector<double>>& val) { return writeNullableTypedVector(val, &Parcel::writeDouble); @@ -858,6 +901,11 @@ status_t Parcel::writeBoolVector(const std::vector<bool>& val) return writeTypedVector(val, &Parcel::writeBool); } +status_t Parcel::writeBoolVector(const std::optional<std::vector<bool>>& val) +{ + return writeNullableTypedVector(val, &Parcel::writeBool); +} + status_t Parcel::writeBoolVector(const std::unique_ptr<std::vector<bool>>& val) { return writeNullableTypedVector(val, &Parcel::writeBool); @@ -868,6 +916,11 @@ status_t Parcel::writeCharVector(const std::vector<char16_t>& val) return writeTypedVector(val, &Parcel::writeChar); } +status_t Parcel::writeCharVector(const std::optional<std::vector<char16_t>>& val) +{ + return writeNullableTypedVector(val, &Parcel::writeChar); +} + status_t Parcel::writeCharVector(const std::unique_ptr<std::vector<char16_t>>& val) { return writeNullableTypedVector(val, &Parcel::writeChar); @@ -879,12 +932,23 @@ status_t Parcel::writeString16Vector(const std::vector<String16>& val) } status_t Parcel::writeString16Vector( + const std::optional<std::vector<std::optional<String16>>>& val) +{ + return writeNullableTypedVector(val, &Parcel::writeString16); +} + +status_t Parcel::writeString16Vector( const std::unique_ptr<std::vector<std::unique_ptr<String16>>>& val) { return writeNullableTypedVector(val, &Parcel::writeString16); } status_t Parcel::writeUtf8VectorAsUtf16Vector( + const std::optional<std::vector<std::optional<std::string>>>& val) { + return writeNullableTypedVector(val, &Parcel::writeUtf8AsUtf16); +} + +status_t Parcel::writeUtf8VectorAsUtf16Vector( const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& val) { return writeNullableTypedVector(val, &Parcel::writeUtf8AsUtf16); } @@ -1019,6 +1083,15 @@ status_t Parcel::writeString8(const char* str, size_t len) return err; } +status_t Parcel::writeString16(const std::optional<String16>& str) +{ + if (!str) { + return writeInt32(-1); + } + + return writeString16(*str); +} + status_t Parcel::writeString16(const std::unique_ptr<String16>& str) { if (!str) { @@ -1061,11 +1134,20 @@ status_t Parcel::writeStrongBinderVector(const std::vector<sp<IBinder>>& val) return writeTypedVector(val, &Parcel::writeStrongBinder); } +status_t Parcel::writeStrongBinderVector(const std::optional<std::vector<sp<IBinder>>>& val) +{ + return writeNullableTypedVector(val, &Parcel::writeStrongBinder); +} + status_t Parcel::writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val) { return writeNullableTypedVector(val, &Parcel::writeStrongBinder); } +status_t Parcel::readStrongBinderVector(std::optional<std::vector<sp<IBinder>>>* val) const { + return readNullableTypedVector(val, &Parcel::readNullableStrongBinder); +} + status_t Parcel::readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const { return readNullableTypedVector(val, &Parcel::readNullableStrongBinder); } @@ -1164,6 +1246,10 @@ status_t Parcel::writeUniqueFileDescriptorVector(const std::vector<base::unique_ return writeTypedVector(val, &Parcel::writeUniqueFileDescriptor); } +status_t Parcel::writeUniqueFileDescriptorVector(const std::optional<std::vector<base::unique_fd>>& val) { + return writeNullableTypedVector(val, &Parcel::writeUniqueFileDescriptor); +} + status_t Parcel::writeUniqueFileDescriptorVector(const std::unique_ptr<std::vector<base::unique_fd>>& val) { return writeNullableTypedVector(val, &Parcel::writeUniqueFileDescriptor); } @@ -1439,7 +1525,7 @@ const void* Parcel::readInplace(size_t len) const template<class T> status_t Parcel::readAligned(T *pArg) const { - COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T)); + static_assert(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T)); if ((mDataPos+sizeof(T)) <= mDataSize) { if (mObjectsSize > 0) { @@ -1472,7 +1558,7 @@ T Parcel::readAligned() const { template<class T> status_t Parcel::writeAligned(T val) { - COMPILE_TIME_ASSERT_FUNCTION_SCOPE(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T)); + static_assert(PAD_SIZE_UNSAFE(sizeof(T)) == sizeof(T)); if ((mDataPos+sizeof(val)) <= mDataCapacity) { restart_write: @@ -1497,6 +1583,17 @@ status_t Parcel::readByteVector(std::vector<uint8_t>* val) const { return readByteVectorInternal(val, size); } +status_t Parcel::readByteVector(std::optional<std::vector<int8_t>>* val) const { + size_t size; + if (status_t status = reserveOutVector(val, &size); status != OK) return status; + if (!*val) { + // reserveOutVector does not create the out vector if size is < 0. + // This occurs when writing a null byte vector. + return OK; + } + return readByteVectorInternal(&**val, size); +} + status_t Parcel::readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const { size_t size; if (status_t status = reserveOutVector(val, &size); status != OK) return status; @@ -1508,6 +1605,17 @@ status_t Parcel::readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const return readByteVectorInternal(val->get(), size); } +status_t Parcel::readByteVector(std::optional<std::vector<uint8_t>>* val) const { + size_t size; + if (status_t status = reserveOutVector(val, &size); status != OK) return status; + if (!*val) { + // reserveOutVector does not create the out vector if size is < 0. + // This occurs when writing a null byte vector. + return OK; + } + return readByteVectorInternal(&**val, size); +} + status_t Parcel::readByteVector(std::unique_ptr<std::vector<uint8_t>>* val) const { size_t size; if (status_t status = reserveOutVector(val, &size); status != OK) return status; @@ -1519,6 +1627,10 @@ status_t Parcel::readByteVector(std::unique_ptr<std::vector<uint8_t>>* val) cons return readByteVectorInternal(val->get(), size); } +status_t Parcel::readInt32Vector(std::optional<std::vector<int32_t>>* val) const { + return readNullableTypedVector(val, &Parcel::readInt32); +} + status_t Parcel::readInt32Vector(std::unique_ptr<std::vector<int32_t>>* val) const { return readNullableTypedVector(val, &Parcel::readInt32); } @@ -1527,6 +1639,10 @@ status_t Parcel::readInt32Vector(std::vector<int32_t>* val) const { return readTypedVector(val, &Parcel::readInt32); } +status_t Parcel::readInt64Vector(std::optional<std::vector<int64_t>>* val) const { + return readNullableTypedVector(val, &Parcel::readInt64); +} + status_t Parcel::readInt64Vector(std::unique_ptr<std::vector<int64_t>>* val) const { return readNullableTypedVector(val, &Parcel::readInt64); } @@ -1535,6 +1651,10 @@ status_t Parcel::readInt64Vector(std::vector<int64_t>* val) const { return readTypedVector(val, &Parcel::readInt64); } +status_t Parcel::readUint64Vector(std::optional<std::vector<uint64_t>>* val) const { + return readNullableTypedVector(val, &Parcel::readUint64); +} + status_t Parcel::readUint64Vector(std::unique_ptr<std::vector<uint64_t>>* val) const { return readNullableTypedVector(val, &Parcel::readUint64); } @@ -1543,6 +1663,10 @@ status_t Parcel::readUint64Vector(std::vector<uint64_t>* val) const { return readTypedVector(val, &Parcel::readUint64); } +status_t Parcel::readFloatVector(std::optional<std::vector<float>>* val) const { + return readNullableTypedVector(val, &Parcel::readFloat); +} + status_t Parcel::readFloatVector(std::unique_ptr<std::vector<float>>* val) const { return readNullableTypedVector(val, &Parcel::readFloat); } @@ -1551,6 +1675,10 @@ status_t Parcel::readFloatVector(std::vector<float>* val) const { return readTypedVector(val, &Parcel::readFloat); } +status_t Parcel::readDoubleVector(std::optional<std::vector<double>>* val) const { + return readNullableTypedVector(val, &Parcel::readDouble); +} + status_t Parcel::readDoubleVector(std::unique_ptr<std::vector<double>>* val) const { return readNullableTypedVector(val, &Parcel::readDouble); } @@ -1559,6 +1687,28 @@ status_t Parcel::readDoubleVector(std::vector<double>* val) const { return readTypedVector(val, &Parcel::readDouble); } +status_t Parcel::readBoolVector(std::optional<std::vector<bool>>* val) const { + const int32_t start = dataPosition(); + int32_t size; + status_t status = readInt32(&size); + val->reset(); + + if (status != OK || size < 0) { + return status; + } + + setDataPosition(start); + val->emplace(); + + status = readBoolVector(&**val); + + if (status != OK) { + val->reset(); + } + + return status; +} + status_t Parcel::readBoolVector(std::unique_ptr<std::vector<bool>>* val) const { const int32_t start = dataPosition(); int32_t size; @@ -1611,6 +1761,10 @@ status_t Parcel::readBoolVector(std::vector<bool>* val) const { return OK; } +status_t Parcel::readCharVector(std::optional<std::vector<char16_t>>* val) const { + return readNullableTypedVector(val, &Parcel::readChar); +} + status_t Parcel::readCharVector(std::unique_ptr<std::vector<char16_t>>* val) const { return readNullableTypedVector(val, &Parcel::readChar); } @@ -1620,6 +1774,11 @@ status_t Parcel::readCharVector(std::vector<char16_t>* val) const { } status_t Parcel::readString16Vector( + std::optional<std::vector<std::optional<String16>>>* val) const { + return readNullableTypedVector(val, &Parcel::readString16); +} + +status_t Parcel::readString16Vector( std::unique_ptr<std::vector<std::unique_ptr<String16>>>* val) const { return readNullableTypedVector(val, &Parcel::readString16); } @@ -1629,6 +1788,11 @@ status_t Parcel::readString16Vector(std::vector<String16>* val) const { } status_t Parcel::readUtf8VectorFromUtf16Vector( + std::optional<std::vector<std::optional<std::string>>>* val) const { + return readNullableTypedVector(val, &Parcel::readUtf8FromUtf16); +} + +status_t Parcel::readUtf8VectorFromUtf16Vector( std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const { return readNullableTypedVector(val, &Parcel::readUtf8FromUtf16); } @@ -1820,6 +1984,21 @@ status_t Parcel::readUtf8FromUtf16(std::string* str) const { return NO_ERROR; } +status_t Parcel::readUtf8FromUtf16(std::optional<std::string>* str) const { + const int32_t start = dataPosition(); + int32_t size; + status_t status = readInt32(&size); + str->reset(); + + if (status != OK || size < 0) { + return status; + } + + setDataPosition(start); + str->emplace(); + return readUtf8FromUtf16(&**str); +} + status_t Parcel::readUtf8FromUtf16(std::unique_ptr<std::string>* str) const { const int32_t start = dataPosition(); int32_t size; @@ -1898,6 +2077,29 @@ String16 Parcel::readString16() const return String16(); } +status_t Parcel::readString16(std::optional<String16>* pArg) const +{ + const int32_t start = dataPosition(); + int32_t size; + status_t status = readInt32(&size); + pArg->reset(); + + if (status != OK || size < 0) { + return status; + } + + setDataPosition(start); + pArg->emplace(); + + status = readString16(&**pArg); + + if (status != OK) { + pArg->reset(); + } + + return status; +} + status_t Parcel::readString16(std::unique_ptr<String16>* pArg) const { const int32_t start = dataPosition(); @@ -2103,6 +2305,10 @@ status_t Parcel::readUniqueParcelFileDescriptor(base::unique_fd* val) const return OK; } +status_t Parcel::readUniqueFileDescriptorVector(std::optional<std::vector<base::unique_fd>>* val) const { + return readNullableTypedVector(val, &Parcel::readUniqueFileDescriptor); +} + status_t Parcel::readUniqueFileDescriptorVector(std::unique_ptr<std::vector<base::unique_fd>>* val) const { return readNullableTypedVector(val, &Parcel::readUniqueFileDescriptor); } @@ -2448,7 +2654,7 @@ status_t Parcel::growData(size_t len) size_t newSize = ((mDataSize+len)*3)/2; return (newSize <= mDataSize) ? (status_t) NO_MEMORY - : continueWrite(newSize); + : continueWrite(std::max(newSize, (size_t) 128)); } status_t Parcel::restartWrite(size_t desired) diff --git a/libs/binder/ProcessState.cpp b/libs/binder/ProcessState.cpp index 4b773e816f..a53056560e 100644 --- a/libs/binder/ProcessState.cpp +++ b/libs/binder/ProcessState.cpp @@ -32,6 +32,7 @@ #include <errno.h> #include <fcntl.h> +#include <mutex> #include <stdio.h> #include <stdlib.h> #include <unistd.h> @@ -73,38 +74,49 @@ protected: sp<ProcessState> ProcessState::self() { - Mutex::Autolock _l(gProcessMutex); - if (gProcess != nullptr) { - return gProcess; - } - gProcess = new ProcessState(kDefaultDriver); - return gProcess; + return init(kDefaultDriver, false /*requireDefault*/); } sp<ProcessState> ProcessState::initWithDriver(const char* driver) { - Mutex::Autolock _l(gProcessMutex); - if (gProcess != nullptr) { - // Allow for initWithDriver to be called repeatedly with the same - // driver. - if (!strcmp(gProcess->getDriverName().c_str(), driver)) { - return gProcess; - } - LOG_ALWAYS_FATAL("ProcessState was already initialized."); - } - - if (access(driver, R_OK) == -1) { - ALOGE("Binder driver %s is unavailable. Using /dev/binder instead.", driver); - driver = "/dev/binder"; - } - - gProcess = new ProcessState(driver); - return gProcess; + return init(driver, true /*requireDefault*/); } sp<ProcessState> ProcessState::selfOrNull() { - Mutex::Autolock _l(gProcessMutex); + return init(nullptr, false /*requireDefault*/); +} + +sp<ProcessState> ProcessState::init(const char *driver, bool requireDefault) +{ + [[clang::no_destroy]] static sp<ProcessState> gProcess; + [[clang::no_destroy]] static std::mutex gProcessMutex; + + if (driver == nullptr) { + std::lock_guard<std::mutex> l(gProcessMutex); + return gProcess; + } + + [[clang::no_destroy]] static std::once_flag gProcessOnce; + std::call_once(gProcessOnce, [&](){ + if (access(driver, R_OK) == -1) { + ALOGE("Binder driver %s is unavailable. Using /dev/binder instead.", driver); + driver = "/dev/binder"; + } + + std::lock_guard<std::mutex> l(gProcessMutex); + gProcess = new ProcessState(driver); + }); + + if (requireDefault) { + // Detect if we are trying to initialize with a different driver, and + // consider that an error. ProcessState will only be initialized once above. + LOG_ALWAYS_FATAL_IF(gProcess->getDriverName() != driver, + "ProcessState was already initialized with %s," + " can't initialize with %s.", + gProcess->getDriverName().c_str(), driver); + } + return gProcess; } @@ -132,11 +144,9 @@ void ProcessState::startThreadPool() } } -bool ProcessState::becomeContextManager(context_check_func checkFunc, void* userData) +bool ProcessState::becomeContextManager() { AutoMutex _l(mLock); - mBinderContextCheckFunc = checkFunc; - mBinderContextUserData = userData; flat_binder_object obj { .flags = FLAT_BINDER_FLAG_TXN_SECURITY_CTX, @@ -148,13 +158,11 @@ bool ProcessState::becomeContextManager(context_check_func checkFunc, void* user if (result != 0) { android_errorWriteLog(0x534e4554, "121035042"); - int dummy = 0; - result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &dummy); + int unused = 0; + result = ioctl(mDriverFD, BINDER_SET_CONTEXT_MGR, &unused); } if (result == -1) { - mBinderContextCheckFunc = nullptr; - mBinderContextUserData = nullptr; ALOGE("Binder ioctl to become context manager failed: %s\n", strerror(errno)); } @@ -328,6 +336,8 @@ void ProcessState::spawnPooledThread(bool isMain) } status_t ProcessState::setThreadPoolMaxThreadCount(size_t maxThreads) { + LOG_ALWAYS_FATAL_IF(mThreadPoolStarted && maxThreads < mMaxThreads, + "Binder threadpool cannot be shrunk after starting"); status_t result = NO_ERROR; if (ioctl(mDriverFD, BINDER_SET_MAX_THREADS, &maxThreads) != -1) { mMaxThreads = maxThreads; @@ -383,14 +393,12 @@ ProcessState::ProcessState(const char *driver) , mExecutingThreadsCount(0) , mMaxThreads(DEFAULT_MAX_BINDER_THREADS) , mStarvationStartTimeMs(0) - , mBinderContextCheckFunc(nullptr) - , mBinderContextUserData(nullptr) , mThreadPoolStarted(false) , mThreadPoolSeq(1) , mCallRestriction(CallRestriction::NONE) { -// TODO(b/139016109): enforce in build system +// TODO(b/166468760): enforce in build system #if defined(__ANDROID_APEX__) LOG_ALWAYS_FATAL("Cannot use libbinder in APEX (only system.img libbinder) since it is not stable."); #endif diff --git a/libs/binder/Stability.cpp b/libs/binder/Stability.cpp index e1565fac43..6115aec81d 100644 --- a/libs/binder/Stability.cpp +++ b/libs/binder/Stability.cpp @@ -22,7 +22,7 @@ namespace android { namespace internal { void Stability::markCompilationUnit(IBinder* binder) { - status_t result = set(binder, kLocalStability, true /*log*/); + status_t result = set(binder, getLocalStability(), true /*log*/); LOG_ALWAYS_FATAL_IF(result != OK, "Should only mark known object."); } @@ -45,7 +45,26 @@ bool Stability::requiresVintfDeclaration(const sp<IBinder>& binder) { } void Stability::tryMarkCompilationUnit(IBinder* binder) { - (void) set(binder, kLocalStability, false /*log*/); + (void) set(binder, getLocalStability(), false /*log*/); +} + +Stability::Level Stability::getLocalStability() { +#ifdef __ANDROID_VNDK__ + #ifdef __ANDROID_APEX__ + // TODO(b/142684679) avoid use_vendor on system APEXes + #if !defined(__ANDROID_APEX_COM_ANDROID_MEDIA_SWCODEC__) \ + && !defined(__ANDROID_APEX_TEST_COM_ANDROID_MEDIA_SWCODEC__) + #error VNDK + APEX only defined for com.android.media.swcodec + #endif + // TODO(b/142684679) avoid use_vendor on system APEXes + return Level::SYSTEM; + #else + return Level::VENDOR; + #endif +#else + // TODO(b/139325195): split up stability levels for system/APEX. + return Level::SYSTEM; +#endif } status_t Stability::set(IBinder* binder, int32_t stability, bool log) { diff --git a/libs/binder/Static.cpp b/libs/binder/Static.cpp index 779ed412ba..db0f1c7c3f 100644 --- a/libs/binder/Static.cpp +++ b/libs/binder/Static.cpp @@ -68,9 +68,4 @@ TextOutput& alog(*new LogTextOutput()); TextOutput& aout(*new FdTextOutput(STDOUT_FILENO)); TextOutput& aerr(*new FdTextOutput(STDERR_FILENO)); -// ------------ ProcessState.cpp - -Mutex& gProcessMutex = *new Mutex; -sp<ProcessState> gProcess; - } // namespace android diff --git a/libs/binder/Static.h b/libs/binder/Static.h index f8e0ee5f8d..83524e8575 100644 --- a/libs/binder/Static.h +++ b/libs/binder/Static.h @@ -27,8 +27,4 @@ namespace android { // For TextStream.cpp extern Vector<int32_t> gTextBuffers; -// For ProcessState.cpp -extern Mutex& gProcessMutex; -extern sp<ProcessState> gProcess; - } // namespace android diff --git a/libs/binder/Status.cpp b/libs/binder/Status.cpp index 674f0657a9..64ab7a9d14 100644 --- a/libs/binder/Status.cpp +++ b/libs/binder/Status.cpp @@ -193,13 +193,15 @@ status_t Status::writeToParcel(Parcel* parcel) const { } status_t status = parcel->writeInt32(mException); - if (status != OK) { return status; } + if (status != OK) return status; if (mException == EX_NONE) { // We have no more information to write. return status; } status = parcel->writeString16(String16(mMessage)); + if (status != OK) return status; status = parcel->writeInt32(0); // Empty remote stack trace header + if (status != OK) return status; if (mException == EX_SERVICE_SPECIFIC) { status = parcel->writeInt32(mErrorCode); } else if (mException == EX_PARCELABLE) { diff --git a/libs/binder/TEST_MAPPING b/libs/binder/TEST_MAPPING index 9aa76512c8..c23228399b 100644 --- a/libs/binder/TEST_MAPPING +++ b/libs/binder/TEST_MAPPING @@ -7,6 +7,9 @@ "name": "binderVendorDoubleLoadTest" }, { + "name": "binderAllocationLimits" + }, + { "name": "binderDriverInterfaceTest" }, { @@ -29,6 +32,17 @@ }, { "name": "libbinderthreadstateutils_test" + }, + { + "name": "CtsOsTestCases", + "options": [ + { + "exclude-filter": "android.os.cts.BuildTest#testSdkInt" + }, + { + "exclude-filter": "android.os.cts.StrictModeTest#testNonSdkApiUsage" + } + ] } ] } diff --git a/libs/binder/include/binder/AppOpsManager.h b/libs/binder/include/binder/AppOpsManager.h index d93935ae5d..233f12a58c 100644 --- a/libs/binder/include/binder/AppOpsManager.h +++ b/libs/binder/include/binder/AppOpsManager.h @@ -21,6 +21,8 @@ #include <utils/threads.h> +#include <optional> + #ifdef __ANDROID_VNDK__ #error "This header is not visible to vendors" #endif @@ -147,18 +149,18 @@ public: // const String16&) instead int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage); int32_t noteOp(int32_t op, int32_t uid, const String16& callingPackage, - const std::unique_ptr<String16>& attributionTag, const String16& message); + const std::optional<String16>& attributionTag, const String16& message); // @Deprecated, use startOpNoThrow(int32_t, int32_t, const String16&, bool, const String16&, // const String16&) instead int32_t startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage, bool startIfModeDefault); int32_t startOpNoThrow(int32_t op, int32_t uid, const String16& callingPackage, - bool startIfModeDefault, const std::unique_ptr<String16>& attributionTag, + bool startIfModeDefault, const std::optional<String16>& attributionTag, const String16& message); // @Deprecated, use finishOp(int32_t, int32_t, const String16&, bool, const String16&) instead void finishOp(int32_t op, int32_t uid, const String16& callingPackage); void finishOp(int32_t op, int32_t uid, const String16& callingPackage, - const std::unique_ptr<String16>& attributionTag); + const std::optional<String16>& attributionTag); void startWatchingMode(int32_t op, const String16& packageName, const sp<IAppOpsCallback>& callback); void stopWatchingMode(const sp<IAppOpsCallback>& callback); diff --git a/libs/binder/include/binder/Binder.h b/libs/binder/include/binder/Binder.h index 4cf051563a..f3fea163cd 100644 --- a/libs/binder/include/binder/Binder.h +++ b/libs/binder/include/binder/Binder.h @@ -84,11 +84,8 @@ public: // Appropriate values are: // SCHED_NORMAL: -20 <= priority <= 19 // SCHED_RR/SCHED_FIFO: 1 <= priority <= 99 - __attribute__((weak)) void setMinSchedulerPolicy(int policy, int priority); - __attribute__((weak)) int getMinSchedulerPolicy(); - __attribute__((weak)) int getMinSchedulerPriority(); pid_t getDebugPid(); diff --git a/libs/binder/include/binder/BpBinder.h b/libs/binder/include/binder/BpBinder.h index 8e871b8214..378a91115e 100644 --- a/libs/binder/include/binder/BpBinder.h +++ b/libs/binder/include/binder/BpBinder.h @@ -137,7 +137,6 @@ private: volatile int32_t mObitsSent; Vector<Obituary>* mObituaries; ObjectManager mObjects; - Parcel* mConstantData; mutable String16 mDescriptorCache; int32_t mTrackedUid; diff --git a/libs/binder/include/binder/IAppOpsService.h b/libs/binder/include/binder/IAppOpsService.h index 1ffb8deaba..a4a20c8b10 100644 --- a/libs/binder/include/binder/IAppOpsService.h +++ b/libs/binder/include/binder/IAppOpsService.h @@ -21,6 +21,8 @@ #include <binder/IAppOpsCallback.h> #include <binder/IInterface.h> +#include <optional> + #ifdef __ANDROID_VNDK__ #error "This header is not visible to vendors" #endif @@ -36,13 +38,13 @@ public: virtual int32_t checkOperation(int32_t code, int32_t uid, const String16& packageName) = 0; virtual int32_t noteOperation(int32_t code, int32_t uid, const String16& packageName, - const std::unique_ptr<String16>& attributionTag, bool shouldCollectAsyncNotedOp, + const std::optional<String16>& attributionTag, bool shouldCollectAsyncNotedOp, const String16& message) = 0; virtual int32_t startOperation(const sp<IBinder>& token, int32_t code, int32_t uid, - const String16& packageName, const std::unique_ptr<String16>& attributionTag, + const String16& packageName, const std::optional<String16>& attributionTag, bool startIfModeDefault, bool shouldCollectAsyncNotedOp, const String16& message) = 0; virtual void finishOperation(const sp<IBinder>& token, int32_t code, int32_t uid, - const String16& packageName, const std::unique_ptr<String16>& attributionTag) = 0; + const String16& packageName, const std::optional<String16>& attributionTag) = 0; virtual void startWatchingMode(int32_t op, const String16& packageName, const sp<IAppOpsCallback>& callback) = 0; virtual void stopWatchingMode(const sp<IAppOpsCallback>& callback) = 0; diff --git a/libs/binder/include/binder/IBinder.h b/libs/binder/include/binder/IBinder.h index 64604b74f0..eea0e89738 100644 --- a/libs/binder/include/binder/IBinder.h +++ b/libs/binder/include/binder/IBinder.h @@ -173,6 +173,10 @@ public: * The @a cookie is optional -- if non-NULL, it should be a * memory address that you own (that is, you know it is unique). * + * @note When all references to the binder being linked to are dropped, the + * recipient is automatically unlinked. So, you must hold onto a binder in + * order to receive death notifications about it. + * * @note You will only receive death notifications for remote binders, * as local binders by definition can't die without you dying as well. * Trying to use this function on a local binder will result in an diff --git a/libs/binder/include/binder/IPCThreadState.h b/libs/binder/include/binder/IPCThreadState.h index 7024f27cc7..cdecceae4f 100644 --- a/libs/binder/include/binder/IPCThreadState.h +++ b/libs/binder/include/binder/IPCThreadState.h @@ -47,19 +47,34 @@ public: // binder transactions to be processed. // // returns: 0 in case of success, a value < 0 in case of error - __attribute__((weak)) static status_t freeze(pid_t pid, bool enabled, uint32_t timeout_ms); sp<ProcessState> process(); status_t clearLastError(); + /** + * Returns the PID of the process which has made the current binder + * call. If not in a binder call, this will return getpid. If the + * call is oneway, this will return 0. + */ pid_t getCallingPid() const; - // nullptr if unavailable - // - // this can't be restored once it's cleared, and it does not return the - // context of the current process when not in a binder call. + + /** + * Returns the SELinux security identifier of the process which has + * made the current binder call. If not in a binder call this will + * return nullptr. If this isn't requested with + * Binder::setRequestingSid, it will also return nullptr. + * + * This can't be restored once it's cleared, and it does not return the + * context of the current process when not in a binder call. + */ const char* getCallingSid() const; + + /** + * Returns the UID of the process which has made the current binder + * call. If not in a binder call, this will return 0. + */ uid_t getCallingUid() const; void setStrictModePolicy(int32_t policy); @@ -84,8 +99,8 @@ public: int64_t clearCallingIdentity(); // Restores PID/UID (not SID) void restoreCallingIdentity(int64_t token); - - int setupPolling(int* fd); + + status_t setupPolling(int* fd); status_t handlePolledCommands(); void flushCommands(); diff --git a/libs/binder/include/binder/LazyServiceRegistrar.h b/libs/binder/include/binder/LazyServiceRegistrar.h index 6d711bc10f..d18c88ec0e 100644 --- a/libs/binder/include/binder/LazyServiceRegistrar.h +++ b/libs/binder/include/binder/LazyServiceRegistrar.h @@ -26,7 +26,19 @@ namespace internal { class ClientCounterCallback; } // namespace internal -/** Exits when all services registered through this object have 0 clients */ +/** + * Exits when all services registered through this object have 0 clients + * + * In order to use this class, it's expected that your service: + * - registers all services in the process with this API + * - configures services as oneshot in init .rc files + * - configures services as disabled in init.rc files, unless a client is + * guaranteed early in boot, in which case, forcePersist should also be used + * to avoid races. + * - uses 'interface' declarations in init .rc files + * + * For more information on init .rc configuration, see system/core/init/README.md + **/ class LazyServiceRegistrar { public: static LazyServiceRegistrar& getInstance(); @@ -47,4 +59,4 @@ class LazyServiceRegistrar { }; } // namespace binder -} // namespace android
\ No newline at end of file +} // namespace android diff --git a/libs/binder/include/binder/MemoryHeapBase.h b/libs/binder/include/binder/MemoryHeapBase.h index 3fccddcc59..52bd5decd4 100644 --- a/libs/binder/include/binder/MemoryHeapBase.h +++ b/libs/binder/include/binder/MemoryHeapBase.h @@ -57,28 +57,20 @@ public: virtual ~MemoryHeapBase(); /* implement IMemoryHeap interface */ - virtual int getHeapID() const; + int getHeapID() const override; /* virtual address of the heap. returns MAP_FAILED in case of error */ - virtual void* getBase() const; + void* getBase() const override; - virtual size_t getSize() const; - virtual uint32_t getFlags() const; - off_t getOffset() const override; + size_t getSize() const override; + uint32_t getFlags() const override; + off_t getOffset() const override; const char* getDevice() const; /* this closes this heap -- use carefully */ void dispose(); - /* this is only needed as a workaround, use only if you know - * what you are doing */ - status_t setDevice(const char* device) { - if (mDevice == nullptr) - mDevice = device; - return mDevice ? NO_ERROR : ALREADY_EXISTS; - } - protected: MemoryHeapBase(); // init() takes ownership of fd diff --git a/libs/binder/include/binder/Parcel.h b/libs/binder/include/binder/Parcel.h index c1f64fb541..b6cfb8ec0f 100644 --- a/libs/binder/include/binder/Parcel.h +++ b/libs/binder/include/binder/Parcel.h @@ -121,6 +121,7 @@ public: status_t writeString8(const String8& str); status_t writeString8(const char* str, size_t len); status_t writeString16(const String16& str); + status_t writeString16(const std::optional<String16>& str); status_t writeString16(const std::unique_ptr<String16>& str); status_t writeString16(const char16_t* str, size_t len); status_t writeStrongBinder(const sp<IBinder>& val); @@ -132,33 +133,48 @@ public: // Take a UTF8 encoded string, convert to UTF16, write it to the parcel. status_t writeUtf8AsUtf16(const std::string& str); + status_t writeUtf8AsUtf16(const std::optional<std::string>& str); status_t writeUtf8AsUtf16(const std::unique_ptr<std::string>& str); + status_t writeByteVector(const std::optional<std::vector<int8_t>>& val); status_t writeByteVector(const std::unique_ptr<std::vector<int8_t>>& val); status_t writeByteVector(const std::vector<int8_t>& val); + status_t writeByteVector(const std::optional<std::vector<uint8_t>>& val); status_t writeByteVector(const std::unique_ptr<std::vector<uint8_t>>& val); status_t writeByteVector(const std::vector<uint8_t>& val); + status_t writeInt32Vector(const std::optional<std::vector<int32_t>>& val); status_t writeInt32Vector(const std::unique_ptr<std::vector<int32_t>>& val); status_t writeInt32Vector(const std::vector<int32_t>& val); + status_t writeInt64Vector(const std::optional<std::vector<int64_t>>& val); status_t writeInt64Vector(const std::unique_ptr<std::vector<int64_t>>& val); status_t writeInt64Vector(const std::vector<int64_t>& val); + status_t writeUint64Vector(const std::optional<std::vector<uint64_t>>& val); status_t writeUint64Vector(const std::unique_ptr<std::vector<uint64_t>>& val); status_t writeUint64Vector(const std::vector<uint64_t>& val); + status_t writeFloatVector(const std::optional<std::vector<float>>& val); status_t writeFloatVector(const std::unique_ptr<std::vector<float>>& val); status_t writeFloatVector(const std::vector<float>& val); + status_t writeDoubleVector(const std::optional<std::vector<double>>& val); status_t writeDoubleVector(const std::unique_ptr<std::vector<double>>& val); status_t writeDoubleVector(const std::vector<double>& val); + status_t writeBoolVector(const std::optional<std::vector<bool>>& val); status_t writeBoolVector(const std::unique_ptr<std::vector<bool>>& val); status_t writeBoolVector(const std::vector<bool>& val); + status_t writeCharVector(const std::optional<std::vector<char16_t>>& val); status_t writeCharVector(const std::unique_ptr<std::vector<char16_t>>& val); status_t writeCharVector(const std::vector<char16_t>& val); status_t writeString16Vector( + const std::optional<std::vector<std::optional<String16>>>& val); + status_t writeString16Vector( const std::unique_ptr<std::vector<std::unique_ptr<String16>>>& val); status_t writeString16Vector(const std::vector<String16>& val); status_t writeUtf8VectorAsUtf16Vector( + const std::optional<std::vector<std::optional<std::string>>>& val); + status_t writeUtf8VectorAsUtf16Vector( const std::unique_ptr<std::vector<std::unique_ptr<std::string>>>& val); status_t writeUtf8VectorAsUtf16Vector(const std::vector<std::string>& val); + status_t writeStrongBinderVector(const std::optional<std::vector<sp<IBinder>>>& val); status_t writeStrongBinderVector(const std::unique_ptr<std::vector<sp<IBinder>>>& val); status_t writeStrongBinderVector(const std::vector<sp<IBinder>>& val); @@ -167,14 +183,20 @@ public: template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> status_t writeEnumVector(const std::vector<T>& val); template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> + status_t writeEnumVector(const std::optional<std::vector<T>>& val); + template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> status_t writeEnumVector(const std::unique_ptr<std::vector<T>>& val); // Write an Enum vector with underlying type != int8_t. template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> status_t writeEnumVector(const std::vector<T>& val); template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> + status_t writeEnumVector(const std::optional<std::vector<T>>& val); + template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> status_t writeEnumVector(const std::unique_ptr<std::vector<T>>& val); template<typename T> + status_t writeParcelableVector(const std::optional<std::vector<std::optional<T>>>& val); + template<typename T> status_t writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val); template<typename T> status_t writeParcelableVector(const std::shared_ptr<std::vector<std::unique_ptr<T>>>& val); @@ -182,6 +204,8 @@ public: status_t writeParcelableVector(const std::vector<T>& val); template<typename T> + status_t writeNullableParcelable(const std::optional<T>& parcelable); + template<typename T> status_t writeNullableParcelable(const std::unique_ptr<T>& parcelable); status_t writeParcelable(const Parcelable& parcelable); @@ -195,6 +219,8 @@ public: template<typename T> status_t writeVectorSize(const std::vector<T>& val); template<typename T> + status_t writeVectorSize(const std::optional<std::vector<T>>& val); + template<typename T> status_t writeVectorSize(const std::unique_ptr<std::vector<T>>& val); // Place a native_handle into the parcel (the native_handle's file- @@ -230,6 +256,8 @@ public: // Place a vector of file desciptors into the parcel. Each descriptor is // dup'd as in writeDupFileDescriptor status_t writeUniqueFileDescriptorVector( + const std::optional<std::vector<base::unique_fd>>& val); + status_t writeUniqueFileDescriptorVector( const std::unique_ptr<std::vector<base::unique_fd>>& val); status_t writeUniqueFileDescriptorVector( const std::vector<base::unique_fd>& val); @@ -279,6 +307,7 @@ public: // Read a UTF16 encoded string, convert to UTF8 status_t readUtf8FromUtf16(std::string* str) const; + status_t readUtf8FromUtf16(std::optional<std::string>* str) const; status_t readUtf8FromUtf16(std::unique_ptr<std::string>* str) const; const char* readCString() const; @@ -287,27 +316,34 @@ public: const char* readString8Inplace(size_t* outLen) const; String16 readString16() const; status_t readString16(String16* pArg) const; + status_t readString16(std::optional<String16>* pArg) const; status_t readString16(std::unique_ptr<String16>* pArg) const; const char16_t* readString16Inplace(size_t* outLen) const; sp<IBinder> readStrongBinder() const; status_t readStrongBinder(sp<IBinder>* val) const; status_t readNullableStrongBinder(sp<IBinder>* val) const; - // Read an Enum vector with underlying type int8_t. // Does not use padding; each byte is contiguous. template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> status_t readEnumVector(std::vector<T>* val) const; template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> status_t readEnumVector(std::unique_ptr<std::vector<T>>* val) const; + template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> + status_t readEnumVector(std::optional<std::vector<T>>* val) const; // Read an Enum vector with underlying type != int8_t. template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> status_t readEnumVector(std::vector<T>* val) const; template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> status_t readEnumVector(std::unique_ptr<std::vector<T>>* val) const; + template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool> = 0> + status_t readEnumVector(std::optional<std::vector<T>>* val) const; template<typename T> status_t readParcelableVector( + std::optional<std::vector<std::optional<T>>>* val) const; + template<typename T> + status_t readParcelableVector( std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const; template<typename T> status_t readParcelableVector(std::vector<T>* val) const; @@ -315,6 +351,8 @@ public: status_t readParcelable(Parcelable* parcelable) const; template<typename T> + status_t readParcelable(std::optional<T>* parcelable) const; + template<typename T> status_t readParcelable(std::unique_ptr<T>* parcelable) const; template<typename T> @@ -323,31 +361,45 @@ public: template<typename T> status_t readNullableStrongBinder(sp<T>* val) const; + status_t readStrongBinderVector(std::optional<std::vector<sp<IBinder>>>* val) const; status_t readStrongBinderVector(std::unique_ptr<std::vector<sp<IBinder>>>* val) const; status_t readStrongBinderVector(std::vector<sp<IBinder>>* val) const; + status_t readByteVector(std::optional<std::vector<int8_t>>* val) const; status_t readByteVector(std::unique_ptr<std::vector<int8_t>>* val) const; status_t readByteVector(std::vector<int8_t>* val) const; + status_t readByteVector(std::optional<std::vector<uint8_t>>* val) const; status_t readByteVector(std::unique_ptr<std::vector<uint8_t>>* val) const; status_t readByteVector(std::vector<uint8_t>* val) const; + status_t readInt32Vector(std::optional<std::vector<int32_t>>* val) const; status_t readInt32Vector(std::unique_ptr<std::vector<int32_t>>* val) const; status_t readInt32Vector(std::vector<int32_t>* val) const; + status_t readInt64Vector(std::optional<std::vector<int64_t>>* val) const; status_t readInt64Vector(std::unique_ptr<std::vector<int64_t>>* val) const; status_t readInt64Vector(std::vector<int64_t>* val) const; + status_t readUint64Vector(std::optional<std::vector<uint64_t>>* val) const; status_t readUint64Vector(std::unique_ptr<std::vector<uint64_t>>* val) const; status_t readUint64Vector(std::vector<uint64_t>* val) const; + status_t readFloatVector(std::optional<std::vector<float>>* val) const; status_t readFloatVector(std::unique_ptr<std::vector<float>>* val) const; status_t readFloatVector(std::vector<float>* val) const; + status_t readDoubleVector(std::optional<std::vector<double>>* val) const; status_t readDoubleVector(std::unique_ptr<std::vector<double>>* val) const; status_t readDoubleVector(std::vector<double>* val) const; + status_t readBoolVector(std::optional<std::vector<bool>>* val) const; status_t readBoolVector(std::unique_ptr<std::vector<bool>>* val) const; status_t readBoolVector(std::vector<bool>* val) const; + status_t readCharVector(std::optional<std::vector<char16_t>>* val) const; status_t readCharVector(std::unique_ptr<std::vector<char16_t>>* val) const; status_t readCharVector(std::vector<char16_t>* val) const; status_t readString16Vector( + std::optional<std::vector<std::optional<String16>>>* val) const; + status_t readString16Vector( std::unique_ptr<std::vector<std::unique_ptr<String16>>>* val) const; status_t readString16Vector(std::vector<String16>* val) const; status_t readUtf8VectorFromUtf16Vector( + std::optional<std::vector<std::optional<std::string>>>* val) const; + status_t readUtf8VectorFromUtf16Vector( std::unique_ptr<std::vector<std::unique_ptr<std::string>>>* val) const; status_t readUtf8VectorFromUtf16Vector(std::vector<std::string>* val) const; @@ -360,10 +412,15 @@ public: template<typename T> status_t resizeOutVector(std::vector<T>* val) const; template<typename T> + status_t resizeOutVector(std::optional<std::vector<T>>* val) const; + template<typename T> status_t resizeOutVector(std::unique_ptr<std::vector<T>>* val) const; template<typename T> status_t reserveOutVector(std::vector<T>* val, size_t* size) const; template<typename T> + status_t reserveOutVector(std::optional<std::vector<T>>* val, + size_t* size) const; + template<typename T> status_t reserveOutVector(std::unique_ptr<std::vector<T>>* val, size_t* size) const; @@ -399,6 +456,8 @@ public: // Retrieve a vector of smart file descriptors from the parcel. status_t readUniqueFileDescriptorVector( + std::optional<std::vector<base::unique_fd>>* val) const; + status_t readUniqueFileDescriptorVector( std::unique_ptr<std::vector<base::unique_fd>>* val) const; status_t readUniqueFileDescriptorVector( std::vector<base::unique_fd>* val) const; @@ -492,6 +551,9 @@ private: status_t unsafeReadTypedVector(std::vector<T>* val, status_t(Parcel::*read_func)(U*) const) const; template<typename T> + status_t readNullableTypedVector(std::optional<std::vector<T>>* val, + status_t(Parcel::*read_func)(T*) const) const; + template<typename T> status_t readNullableTypedVector(std::unique_ptr<std::vector<T>>* val, status_t(Parcel::*read_func)(T*) const) const; template<typename T> @@ -501,9 +563,15 @@ private: status_t unsafeWriteTypedVector(const std::vector<T>& val, status_t(Parcel::*write_func)(U)); template<typename T> + status_t writeNullableTypedVector(const std::optional<std::vector<T>>& val, + status_t(Parcel::*write_func)(const T&)); + template<typename T> status_t writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val, status_t(Parcel::*write_func)(const T&)); template<typename T> + status_t writeNullableTypedVector(const std::optional<std::vector<T>>& val, + status_t(Parcel::*write_func)(T)); + template<typename T> status_t writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val, status_t(Parcel::*write_func)(T)); template<typename T> @@ -691,6 +759,15 @@ status_t Parcel::writeVectorSize(const std::vector<T>& val) { } template<typename T> +status_t Parcel::writeVectorSize(const std::optional<std::vector<T>>& val) { + if (!val) { + return writeInt32(-1); + } + + return writeVectorSize(*val); +} + +template<typename T> status_t Parcel::writeVectorSize(const std::unique_ptr<std::vector<T>>& val) { if (!val) { return writeInt32(-1); @@ -715,6 +792,22 @@ status_t Parcel::resizeOutVector(std::vector<T>* val) const { } template<typename T> +status_t Parcel::resizeOutVector(std::optional<std::vector<T>>* val) const { + int32_t size; + status_t err = readInt32(&size); + if (err != NO_ERROR) { + return err; + } + + val->reset(); + if (size >= 0) { + val->emplace(size_t(size)); + } + + return OK; +} + +template<typename T> status_t Parcel::resizeOutVector(std::unique_ptr<std::vector<T>>* val) const { int32_t size; status_t err = readInt32(&size); @@ -747,6 +840,25 @@ status_t Parcel::reserveOutVector(std::vector<T>* val, size_t* size) const { } template<typename T> +status_t Parcel::reserveOutVector(std::optional<std::vector<T>>* val, size_t* size) const { + int32_t read_size; + status_t err = readInt32(&read_size); + if (err != NO_ERROR) { + return err; + } + + if (read_size >= 0) { + *size = static_cast<size_t>(read_size); + val->emplace(); + (*val)->reserve(*size); + } else { + val->reset(); + } + + return OK; +} + +template<typename T> status_t Parcel::reserveOutVector(std::unique_ptr<std::vector<T>>* val, size_t* size) const { int32_t read_size; @@ -841,6 +953,30 @@ status_t Parcel::readTypedVector(std::vector<T>* val, } template<typename T> +status_t Parcel::readNullableTypedVector(std::optional<std::vector<T>>* val, + status_t(Parcel::*read_func)(T*) const) const { + const size_t start = dataPosition(); + int32_t size; + status_t status = readInt32(&size); + val->reset(); + + if (status != OK || size < 0) { + return status; + } + + setDataPosition(start); + val->emplace(); + + status = unsafeReadTypedVector(&**val, read_func); + + if (status != OK) { + val->reset(); + } + + return status; +} + +template<typename T> status_t Parcel::readNullableTypedVector(std::unique_ptr<std::vector<T>>* val, status_t(Parcel::*read_func)(T*) const) const { const size_t start = dataPosition(); @@ -901,6 +1037,16 @@ status_t Parcel::writeTypedVector(const std::vector<T>& val, } template<typename T> +status_t Parcel::writeNullableTypedVector(const std::optional<std::vector<T>>& val, + status_t(Parcel::*write_func)(const T&)) { + if (!val) { + return this->writeInt32(-1); + } + + return unsafeWriteTypedVector(*val, write_func); +} + +template<typename T> status_t Parcel::writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val, status_t(Parcel::*write_func)(const T&)) { if (val.get() == nullptr) { @@ -911,6 +1057,16 @@ status_t Parcel::writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& } template<typename T> +status_t Parcel::writeNullableTypedVector(const std::optional<std::vector<T>>& val, + status_t(Parcel::*write_func)(T)) { + if (!val) { + return this->writeInt32(-1); + } + + return unsafeWriteTypedVector(*val, write_func); +} + +template<typename T> status_t Parcel::writeNullableTypedVector(const std::unique_ptr<std::vector<T>>& val, status_t(Parcel::*write_func)(T)) { if (val.get() == nullptr) { @@ -926,6 +1082,30 @@ status_t Parcel::readParcelableVector(std::vector<T>* val) const { } template<typename T> +status_t Parcel::readParcelableVector(std::optional<std::vector<std::optional<T>>>* val) const { + const size_t start = dataPosition(); + int32_t size; + status_t status = readInt32(&size); + val->reset(); + + if (status != OK || size < 0) { + return status; + } + + setDataPosition(start); + val->emplace(); + + using NullableT = std::optional<T>; + status = unsafeReadTypedVector<NullableT, NullableT>(&**val, &Parcel::readParcelable); + + if (status != OK) { + val->reset(); + } + + return status; +} + +template<typename T> status_t Parcel::readParcelableVector(std::unique_ptr<std::vector<std::unique_ptr<T>>>* val) const { const size_t start = dataPosition(); int32_t size; @@ -939,7 +1119,8 @@ status_t Parcel::readParcelableVector(std::unique_ptr<std::vector<std::unique_pt setDataPosition(start); val->reset(new std::vector<std::unique_ptr<T>>()); - status = unsafeReadTypedVector(val->get(), &Parcel::readParcelable<T>); + using NullableT = std::unique_ptr<T>; + status = unsafeReadTypedVector<NullableT, NullableT>(val->get(), &Parcel::readParcelable); if (status != OK) { val->reset(); @@ -949,6 +1130,29 @@ status_t Parcel::readParcelableVector(std::unique_ptr<std::vector<std::unique_pt } template<typename T> +status_t Parcel::readParcelable(std::optional<T>* parcelable) const { + const size_t start = dataPosition(); + int32_t present; + status_t status = readInt32(&present); + parcelable->reset(); + + if (status != OK || !present) { + return status; + } + + setDataPosition(start); + parcelable->emplace(); + + status = readParcelable(&**parcelable); + + if (status != OK) { + parcelable->reset(); + } + + return status; +} + +template<typename T> status_t Parcel::readParcelable(std::unique_ptr<T>* parcelable) const { const size_t start = dataPosition(); int32_t present; @@ -972,6 +1176,11 @@ status_t Parcel::readParcelable(std::unique_ptr<T>* parcelable) const { } template<typename T> +status_t Parcel::writeNullableParcelable(const std::optional<T>& parcelable) { + return writeRawNullableParcelable(parcelable ? &*parcelable : nullptr); +} + +template<typename T> status_t Parcel::writeNullableParcelable(const std::unique_ptr<T>& parcelable) { return writeRawNullableParcelable(parcelable.get()); } @@ -982,6 +1191,16 @@ status_t Parcel::writeParcelableVector(const std::vector<T>& val) { } template<typename T> +status_t Parcel::writeParcelableVector(const std::optional<std::vector<std::optional<T>>>& val) { + if (!val) { + return this->writeInt32(-1); + } + + using NullableT = std::optional<T>; + return unsafeWriteTypedVector<NullableT, const NullableT&>(*val, &Parcel::writeNullableParcelable); +} + +template<typename T> status_t Parcel::writeParcelableVector(const std::unique_ptr<std::vector<std::unique_ptr<T>>>& val) { if (val.get() == nullptr) { return this->writeInt32(-1); @@ -996,7 +1215,8 @@ status_t Parcel::writeParcelableVector(const std::shared_ptr<std::vector<std::un return this->writeInt32(-1); } - return unsafeWriteTypedVector(*val, &Parcel::writeNullableParcelable<T>); + using NullableT = std::unique_ptr<T>; + return unsafeWriteTypedVector<NullableT, const NullableT&>(*val, &Parcel::writeNullableParcelable); } template<typename T, std::enable_if_t<std::is_same_v<typename std::underlying_type_t<T>,int32_t>, bool>> @@ -1013,6 +1233,11 @@ status_t Parcel::writeEnumVector(const std::vector<T>& val) { return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val.data()), val.size()); } template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>> +status_t Parcel::writeEnumVector(const std::optional<std::vector<T>>& val) { + if (!val) return writeInt32(-1); + return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val->data()), val->size()); +} +template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>> status_t Parcel::writeEnumVector(const std::unique_ptr<std::vector<T>>& val) { if (!val) return writeInt32(-1); return writeByteVectorInternal(reinterpret_cast<const int8_t*>(val->data()), val->size()); @@ -1022,6 +1247,10 @@ status_t Parcel::writeEnumVector(const std::vector<T>& val) { return writeTypedVector(val, &Parcel::writeEnum); } template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>> +status_t Parcel::writeEnumVector(const std::optional<std::vector<T>>& val) { + return writeNullableTypedVector(val, &Parcel::writeEnum); +} +template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>> status_t Parcel::writeEnumVector(const std::unique_ptr<std::vector<T>>& val) { return writeNullableTypedVector(val, &Parcel::writeEnum); } @@ -1053,6 +1282,17 @@ status_t Parcel::readEnumVector(std::vector<T>* val) const { return readByteVectorInternal(val, size); } template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>> +status_t Parcel::readEnumVector(std::optional<std::vector<T>>* val) const { + size_t size; + if (status_t status = reserveOutVector(val, &size); status != OK) return status; + if (!*val) { + // reserveOutVector does not create the out vector if size is < 0. + // This occurs when writing a null Enum vector. + return OK; + } + return readByteVectorInternal(&**val, size); +} +template<typename T, std::enable_if_t<std::is_enum_v<T> && std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>> status_t Parcel::readEnumVector(std::unique_ptr<std::vector<T>>* val) const { size_t size; if (status_t status = reserveOutVector(val, &size); status != OK) return status; @@ -1068,6 +1308,10 @@ status_t Parcel::readEnumVector(std::vector<T>* val) const { return readTypedVector(val, &Parcel::readEnum); } template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>> +status_t Parcel::readEnumVector(std::optional<std::vector<T>>* val) const { + return readNullableTypedVector(val, &Parcel::readEnum); +} +template<typename T, std::enable_if_t<std::is_enum_v<T> && !std::is_same_v<typename std::underlying_type_t<T>,int8_t>, bool>> status_t Parcel::readEnumVector(std::unique_ptr<std::vector<T>>* val) const { return readNullableTypedVector(val, &Parcel::readEnum); } diff --git a/libs/binder/include/binder/ParcelFileDescriptor.h b/libs/binder/include/binder/ParcelFileDescriptor.h index 4635ad84c6..71e1d3cbb4 100644 --- a/libs/binder/include/binder/ParcelFileDescriptor.h +++ b/libs/binder/include/binder/ParcelFileDescriptor.h @@ -31,7 +31,8 @@ class ParcelFileDescriptor : public android::Parcelable { public: ParcelFileDescriptor(); explicit ParcelFileDescriptor(android::base::unique_fd fd); - ParcelFileDescriptor(ParcelFileDescriptor&& other) : mFd(std::move(other.mFd)) { } + ParcelFileDescriptor(ParcelFileDescriptor&& other) noexcept : mFd(std::move(other.mFd)) { } + ParcelFileDescriptor& operator=(ParcelFileDescriptor&& other) noexcept = default; ~ParcelFileDescriptor() override; int get() const { return mFd.get(); } diff --git a/libs/binder/include/binder/Parcelable.h b/libs/binder/include/binder/Parcelable.h index a9166e2408..83c2f1991d 100644 --- a/libs/binder/include/binder/Parcelable.h +++ b/libs/binder/include/binder/Parcelable.h @@ -52,6 +52,21 @@ public: // // Returns android::OK on success and an appropriate error otherwise. virtual status_t readFromParcel(const Parcel* parcel) = 0; + + // WARNING: for use by auto-generated code only (AIDL). Should not be used + // manually, or there is a risk of breaking CTS, GTS, VTS, or CTS-on-GSI + // tests. + enum class Stability { + STABILITY_LOCAL, + STABILITY_VINTF, // corresponds to @VintfStability + }; + + // 'Stable' means this parcelable is guaranteed to be stable for multiple + // years. + // It must be guaranteed by setting stability field in aidl_interface. + // WARNING: getStability() is only expected to be overridden by auto-generated + // code. Returns true if this parcelable is stable. + virtual Stability getStability() const { return Stability::STABILITY_LOCAL; } }; // class Parcelable #if defined(__clang__) diff --git a/libs/binder/include/binder/ProcessState.h b/libs/binder/include/binder/ProcessState.h index e57ff1c260..efb95f4316 100644 --- a/libs/binder/include/binder/ProcessState.h +++ b/libs/binder/include/binder/ProcessState.h @@ -42,20 +42,16 @@ public: * any call to ProcessState::self(). The default is /dev/vndbinder * for processes built with the VNDK and /dev/binder for those * which are not. + * + * If this is called with nullptr, the behavior is the same as selfOrNull. */ static sp<ProcessState> initWithDriver(const char *driver); sp<IBinder> getContextObject(const sp<IBinder>& caller); void startThreadPool(); - - typedef bool (*context_check_func)(const String16& name, - const sp<IBinder>& caller, - void* userData); - bool becomeContextManager( - context_check_func checkFunc, - void* userData); + bool becomeContextManager(); sp<IBinder> getStrongProxyForHandle(int32_t handle); void expungeHandle(int32_t handle, IBinder* binder); @@ -90,6 +86,8 @@ public: void setCallRestriction(CallRestriction restriction); private: + static sp<ProcessState> init(const char *defaultDriver, bool requireDefault); + friend class IPCThreadState; explicit ProcessState(const char* driver); @@ -124,9 +122,6 @@ private: Vector<handle_entry>mHandleToObject; - context_check_func mBinderContextCheckFunc; - void* mBinderContextUserData; - String8 mRootDir; bool mThreadPoolStarted; volatile int32_t mThreadPoolSeq; diff --git a/libs/binder/include/binder/Stability.h b/libs/binder/include/binder/Stability.h index 2894482f55..6566285155 100644 --- a/libs/binder/include/binder/Stability.h +++ b/libs/binder/include/binder/Stability.h @@ -81,11 +81,8 @@ private: VINTF = 0b111111, }; -#if defined(__ANDROID_VNDK__) && !defined(__ANDROID_APEX__) - static constexpr Level kLocalStability = Level::VENDOR; -#else - static constexpr Level kLocalStability = Level::SYSTEM; -#endif + // returns the stability according to how this was built + static Level getLocalStability(); // applies stability to binder if stability level is known __attribute__((warn_unused_result)) diff --git a/libs/binder/include/binder/TextOutput.h b/libs/binder/include/binder/TextOutput.h index f66406f7d4..c7e1e14218 100644 --- a/libs/binder/include/binder/TextOutput.h +++ b/libs/binder/include/binder/TextOutput.h @@ -50,12 +50,18 @@ public: // --------------------------------------------------------------------------- +// DO NOT USE: prefer libutils/libbase logs, which don't require static data to +// be allocated. // Text output stream for printing to the log (via utils/Log.h). extern TextOutput& alog; +// DO NOT USE: prefer libutils/libbase logs, which don't require static data to +// be allocated. // Text output stream for printing to stdout. extern TextOutput& aout; +// DO NOT USE: prefer libutils/libbase logs, which don't require static data to +// be allocated. // Text output stream for printing to stderr. extern TextOutput& aerr; diff --git a/libs/binder/ndk/Android.bp b/libs/binder/ndk/Android.bp index b37db434a5..4fd06573b7 100644 --- a/libs/binder/ndk/Android.bp +++ b/libs/binder/ndk/Android.bp @@ -36,6 +36,7 @@ cc_library { host_supported: true, export_include_dirs: [ + "include_cpp", "include_ndk", "include_platform", ], @@ -101,6 +102,17 @@ ndk_headers { license: "NOTICE", } +// TODO(b/160624671): package with the aidl compiler +ndk_headers { + name: "libbinder_ndk_helper_headers", + from: "include_cpp/android", + to: "android", + srcs: [ + "include_cpp/android/*.h", + ], + license: "NOTICE", +} + ndk_library { name: "libbinder_ndk", symbol_file: "libbinder_ndk.map.txt", @@ -111,6 +123,7 @@ llndk_library { name: "libbinder_ndk", symbol_file: "libbinder_ndk.map.txt", export_include_dirs: [ + "include_cpp", "include_ndk", "include_platform", ], diff --git a/libs/binder/ndk/ibinder.cpp b/libs/binder/ndk/ibinder.cpp index 919150d740..d287290a8d 100644 --- a/libs/binder/ndk/ibinder.cpp +++ b/libs/binder/ndk/ibinder.cpp @@ -15,6 +15,7 @@ */ #include <android/binder_ibinder.h> +#include <android/binder_ibinder_platform.h> #include "ibinder_internal.h" #include <android/binder_stability.h> @@ -99,8 +100,14 @@ bool AIBinder::associateClass(const AIBinder_Class* clazz) { String8 descriptor(getBinder()->getInterfaceDescriptor()); if (descriptor != newDescriptor) { - LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor.c_str() - << "' but descriptor is actually '" << descriptor.c_str() << "'."; + if (getBinder()->isBinderAlive()) { + LOG(ERROR) << __func__ << ": Expecting binder to have class '" << newDescriptor.c_str() + << "' but descriptor is actually '" << descriptor.c_str() << "'."; + } else { + // b/155793159 + LOG(ERROR) << __func__ << ": Cannot associate class '" << newDescriptor.c_str() + << "' to dead binder."; + } return false; } @@ -161,7 +168,7 @@ status_t ABBinder::onTransact(transaction_code_t code, const Parcel& data, Parce binder_status_t status = getClass()->onTransact(this, code, &in, &out); return PruneStatusT(status); - } else if (code == SHELL_COMMAND_TRANSACTION && getClass()->handleShellCommand != nullptr) { + } else if (code == SHELL_COMMAND_TRANSACTION) { int in = data.readFileDescriptor(); int out = data.readFileDescriptor(); int err = data.readFileDescriptor(); @@ -676,3 +683,29 @@ binder_status_t AIBinder_setExtension(AIBinder* binder, AIBinder* ext) { rawBinder->setExtension(ext->getBinder()); return STATUS_OK; } + +// platform methods follow + +void AIBinder_setRequestingSid(AIBinder* binder, bool requestingSid) { + ABBinder* localBinder = binder->asABBinder(); + if (localBinder == nullptr) { + LOG(FATAL) << "AIBinder_setRequestingSid must be called on a local binder"; + } + + localBinder->setRequestingSid(requestingSid); +} + +const char* AIBinder_getCallingSid() { + return ::android::IPCThreadState::self()->getCallingSid(); +} + +android::sp<android::IBinder> AIBinder_toPlatformBinder(AIBinder* binder) { + if (binder == nullptr) return nullptr; + return binder->getBinder(); +} + +AIBinder* AIBinder_fromPlatformBinder(const android::sp<android::IBinder>& binder) { + sp<AIBinder> ndkBinder = ABpBinder::lookupOrCreateFromBinder(binder); + AIBinder_incStrong(ndkBinder.get()); + return ndkBinder.get(); +} diff --git a/libs/binder/ndk/ibinder_internal.h b/libs/binder/ndk/ibinder_internal.h index 902fe7934d..57794279f2 100644 --- a/libs/binder/ndk/ibinder_internal.h +++ b/libs/binder/ndk/ibinder_internal.h @@ -110,13 +110,13 @@ struct AIBinder_Class { const ::android::String16& getInterfaceDescriptor() const { return mInterfaceDescriptor; } // required to be non-null, implemented for every class - const AIBinder_Class_onCreate onCreate = nullptr; - const AIBinder_Class_onDestroy onDestroy = nullptr; - const AIBinder_Class_onTransact onTransact = nullptr; + const AIBinder_Class_onCreate onCreate; + const AIBinder_Class_onDestroy onDestroy; + const AIBinder_Class_onTransact onTransact; // optional methods for a class - AIBinder_onDump onDump = nullptr; - AIBinder_handleShellCommand handleShellCommand = nullptr; + 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_auto_utils.h b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h index 2b61cf18c2..f59bb75cc1 100644 --- a/libs/binder/ndk/include_ndk/android/binder_auto_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_auto_utils.h @@ -189,6 +189,7 @@ class ScopedAParcel : public impl::ScopedAResource<AParcel*, void, AParcel_delet explicit ScopedAParcel(AParcel* a = nullptr) : ScopedAResource(a) {} ~ScopedAParcel() {} ScopedAParcel(ScopedAParcel&&) = default; + ScopedAParcel& operator=(ScopedAParcel&&) = default; }; /** @@ -273,6 +274,7 @@ class ScopedAIBinder_DeathRecipient : ScopedAResource(a) {} ~ScopedAIBinder_DeathRecipient() {} ScopedAIBinder_DeathRecipient(ScopedAIBinder_DeathRecipient&&) = default; + ScopedAIBinder_DeathRecipient& operator=(ScopedAIBinder_DeathRecipient&&) = default; }; /** @@ -287,6 +289,7 @@ class ScopedAIBinder_Weak explicit ScopedAIBinder_Weak(AIBinder_Weak* a = nullptr) : ScopedAResource(a) {} ~ScopedAIBinder_Weak() {} ScopedAIBinder_Weak(ScopedAIBinder_Weak&&) = default; + ScopedAIBinder_Weak& operator=(ScopedAIBinder_Weak&&) = default; /** * See AIBinder_Weak_promote. @@ -305,6 +308,7 @@ class ScopedFileDescriptor : public impl::ScopedAResource<int, int, close, -1> { explicit ScopedFileDescriptor(int a = -1) : ScopedAResource(a) {} ~ScopedFileDescriptor() {} ScopedFileDescriptor(ScopedFileDescriptor&&) = default; + ScopedFileDescriptor& operator=(ScopedFileDescriptor&&) = default; }; } // namespace ndk diff --git a/libs/binder/ndk/include_ndk/android/binder_enums.h b/libs/binder/ndk/include_cpp/android/binder_enums.h index ee819c0b23..ee819c0b23 100644 --- a/libs/binder/ndk/include_ndk/android/binder_enums.h +++ b/libs/binder/ndk/include_cpp/android/binder_enums.h diff --git a/libs/binder/ndk/include_ndk/android/binder_interface_utils.h b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h index 33e4586137..f44ce0c63a 100644 --- a/libs/binder/ndk/include_ndk/android/binder_interface_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_interface_utils.h @@ -88,13 +88,21 @@ class SharedRefBase { static void operator delete(void* p) { std::free(p); } - private: - std::once_flag mFlagThis; - std::weak_ptr<SharedRefBase> mThis; - + // Once minSdkVersion is 30, we are guaranteed to be building with the + // Android 11 AIDL compiler which supports the SharedRefBase::make API. + // // Use 'SharedRefBase::make<T>(...)' to make. SharedRefBase has implicit // ownership. Making this operator private to avoid double-ownership. +#if !defined(__ANDROID_API__) || __ANDROID_API__ >= 30 + private: +#else + [[deprecated("Prefer SharedRefBase::make<T>(...) if possible.")]] +#endif static void* operator new(size_t s) { return std::malloc(s); } + + private: + std::once_flag mFlagThis; + std::weak_ptr<SharedRefBase> mThis; }; /** diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h index df5df13c19..09949ea259 100644 --- a/libs/binder/ndk/include_ndk/android/binder_parcel_utils.h +++ b/libs/binder/ndk/include_cpp/android/binder_parcel_utils.h @@ -831,34 +831,34 @@ inline binder_status_t AParcel_readVector(const AParcel* parcel, } /** - * Writes a vector of int8_t to the next location in a non-null parcel. + * Writes a vector of uint8_t to the next location in a non-null parcel. */ -inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<int8_t>& vec) { - return AParcel_writeByteArray(parcel, vec.data(), vec.size()); +inline binder_status_t AParcel_writeVector(AParcel* parcel, const std::vector<uint8_t>& vec) { + return AParcel_writeByteArray(parcel, reinterpret_cast<const int8_t*>(vec.data()), vec.size()); } /** - * Writes an optional vector of int8_t to the next location in a non-null parcel. + * Writes an optional vector of uint8_t to the next location in a non-null parcel. */ inline binder_status_t AParcel_writeVector(AParcel* parcel, - const std::optional<std::vector<int8_t>>& vec) { + const std::optional<std::vector<uint8_t>>& vec) { if (!vec) return AParcel_writeByteArray(parcel, nullptr, -1); return AParcel_writeVector(parcel, *vec); } /** - * Reads a vector of int8_t from the next location in a non-null parcel. + * Reads a vector of uint8_t from the next location in a non-null parcel. */ -inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<int8_t>* vec) { +inline binder_status_t AParcel_readVector(const AParcel* parcel, std::vector<uint8_t>* vec) { void* vectorData = static_cast<void*>(vec); return AParcel_readByteArray(parcel, vectorData, AParcel_stdVectorAllocator<int8_t>); } /** - * Reads an optional vector of int8_t from the next location in a non-null parcel. + * Reads an optional vector of uint8_t from the next location in a non-null parcel. */ inline binder_status_t AParcel_readVector(const AParcel* parcel, - std::optional<std::vector<int8_t>>* vec) { + std::optional<std::vector<uint8_t>>* vec) { void* vectorData = static_cast<void*>(vec); return AParcel_readByteArray(parcel, vectorData, AParcel_nullableStdVectorAllocator<int8_t>); } diff --git a/libs/binder/ndk/include_ndk/android/binder_ibinder.h b/libs/binder/ndk/include_ndk/android/binder_ibinder.h index 4560f222cb..33763d58be 100644 --- a/libs/binder/ndk/include_ndk/android/binder_ibinder.h +++ b/libs/binder/ndk/include_ndk/android/binder_ibinder.h @@ -26,6 +26,7 @@ #pragma once +#include <stdbool.h> #include <stdint.h> #include <sys/cdefs.h> #include <sys/types.h> @@ -407,6 +408,8 @@ int32_t AIBinder_debugGetRefCount(AIBinder* binder) __INTRODUCED_IN(29); * This returns true if the class association succeeds. If it fails, no change is made to the * binder object. * + * Warning: this may fail if the binder is dead. + * * Available since API level 29. * * \param binder the object to attach the class to. diff --git a/libs/binder/ndk/include_ndk/android/binder_parcel.h b/libs/binder/ndk/include_ndk/android/binder_parcel.h index 86b75b8c61..a031e2973d 100644 --- a/libs/binder/ndk/include_ndk/android/binder_parcel.h +++ b/libs/binder/ndk/include_ndk/android/binder_parcel.h @@ -26,6 +26,7 @@ #pragma once +#include <stdbool.h> #include <stddef.h> #include <sys/cdefs.h> diff --git a/libs/binder/ndk/include_ndk/android/binder_status.h b/libs/binder/ndk/include_ndk/android/binder_status.h index ab9a144c53..3a55f9480c 100644 --- a/libs/binder/ndk/include_ndk/android/binder_status.h +++ b/libs/binder/ndk/include_ndk/android/binder_status.h @@ -26,6 +26,7 @@ #pragma once #include <errno.h> +#include <stdbool.h> #include <stdint.h> #include <sys/cdefs.h> diff --git a/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h new file mode 100644 index 0000000000..5811760d3a --- /dev/null +++ b/libs/binder/ndk/include_platform/android/binder_ibinder_platform.h @@ -0,0 +1,81 @@ +/* + * 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> + +#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__) +#include <binder/IBinder.h> +#endif + +__BEGIN_DECLS + +/** + * Makes calls to AIBinder_getCallingSid work if the kernel supports it. This + * must be called on a local binder server before it is sent out to any othe + * process. If this is a remote binder, it will abort. If the kernel doesn't + * support this feature, you'll always get null from AIBinder_getCallingSid. + * + * \param binder local server binder to request security contexts on + */ +__attribute__((weak)) void AIBinder_setRequestingSid(AIBinder* binder, bool requestingSid) + __INTRODUCED_IN(31); + +/** + * Returns the selinux context of the callee. + * + * In order for this to work, the following conditions must be met: + * - The kernel must be new enough to support this feature. + * - The server must have called AIBinder_setRequestingSid. + * - The callee must be a remote process. + * + * \return security context or null if unavailable. The lifetime of this context + * is the lifetime of the transaction. + */ +__attribute__((weak, warn_unused_result)) const char* AIBinder_getCallingSid() __INTRODUCED_IN(31); + +__END_DECLS + +#if !defined(__ANDROID_APEX__) && !defined(__ANDROID_VNDK__) + +/** + * Get libbinder version of binder from AIBinder. + * + * WARNING: function calls to a local object on the other side of this function + * will parcel. When converting between binders, keep in mind it is not as + * efficient as a direct function call. + * + * \param binder binder with ownership retained by the client + * \return platform binder object + */ +android::sp<android::IBinder> AIBinder_toPlatformBinder(AIBinder* binder); + +/** + * Get libbinder_ndk version of binder from platform binder. + * + * WARNING: function calls to a local object on the other side of this function + * will parcel. When converting between binders, keep in mind it is not as + * efficient as a direct function call. + * + * \param binder platform binder which may be from anywhere (doesn't have to be + * created with libbinder_ndK) + * \return binder with one reference count of ownership given to the client. See + * AIBinder_decStrong + */ +AIBinder* AIBinder_fromPlatformBinder(const android::sp<android::IBinder>& binder); + +#endif diff --git a/libs/binder/ndk/include_platform/android/binder_manager.h b/libs/binder/ndk/include_platform/android/binder_manager.h index 055c79bca1..2784aa823d 100644 --- a/libs/binder/ndk/include_platform/android/binder_manager.h +++ b/libs/binder/ndk/include_platform/android/binder_manager.h @@ -18,6 +18,7 @@ #include <android/binder_ibinder.h> #include <android/binder_status.h> +#include <sys/cdefs.h> __BEGIN_DECLS @@ -28,9 +29,9 @@ __BEGIN_DECLS * \param binder object to register globally with the service manager. * \param instance identifier of the service. This will be used to lookup the service. * - * \return STATUS_OK on success. + * \return EX_NONE on success. */ -binder_status_t AServiceManager_addService(AIBinder* binder, const char* instance); +binder_exception_t AServiceManager_addService(AIBinder* binder, const char* instance); /** * Gets a binder object with this specific instance name. Will return nullptr immediately if the @@ -50,4 +51,47 @@ __attribute__((warn_unused_result)) AIBinder* AServiceManager_checkService(const */ __attribute__((warn_unused_result)) AIBinder* AServiceManager_getService(const char* instance); +/** + * Registers a lazy service with the default service manager under the 'instance' name. + * Does not take ownership of binder. + * The service must be configured statically with init so it can be restarted with + * ctl.interface.* messages from servicemanager. + * AServiceManager_registerLazyService cannot safely be used with AServiceManager_addService + * in the same process. If one service is registered with AServiceManager_registerLazyService, + * the entire process will have its lifetime controlled by servicemanager. + * Instead, all services in the process should be registered using + * AServiceManager_registerLazyService. + * + * \param binder object to register globally with the service manager. + * \param instance identifier of the service. This will be used to lookup the service. + * + * \return STATUS_OK on success. + */ +binder_status_t AServiceManager_registerLazyService(AIBinder* binder, const char* instance) + __INTRODUCED_IN(31); + +/** + * Gets a binder object with this specific instance name. Efficiently waits for the service. + * If the service is not declared, it will wait indefinitely. Requires the threadpool + * to be started in the service. + * This also implicitly calls AIBinder_incStrong (so the caller of this function is responsible + * for calling AIBinder_decStrong). + * + * \param instance identifier of the service used to lookup the service. + * + * \return service if registered, null if not. + */ +__attribute__((warn_unused_result)) AIBinder* AServiceManager_waitForService(const char* instance) + __INTRODUCED_IN(31); + +/** + * Check if a service is declared (e.g. VINTF manifest). + * + * \param instance identifier of the service. + * + * \return true on success, meaning AServiceManager_waitForService should always + * be able to return the service. + */ +bool AServiceManager_isDeclared(const char* instance) __INTRODUCED_IN(31); + __END_DECLS diff --git a/libs/binder/ndk/include_platform/android/binder_parcel_platform.h b/libs/binder/ndk/include_platform/android/binder_parcel_platform.h index ac46cb80f4..114a781232 100644 --- a/libs/binder/ndk/include_platform/android/binder_parcel_platform.h +++ b/libs/binder/ndk/include_platform/android/binder_parcel_platform.h @@ -20,6 +20,10 @@ __BEGIN_DECLS +#if defined(__ANDROID_APEX__) || defined(__ANDROID_VNDK__) +#error this is only for platform code +#endif + /** * Gets whether or not FDs are allowed by this AParcel * @@ -29,4 +33,4 @@ __BEGIN_DECLS */ bool AParcel_getAllowFds(const AParcel*); -__END_DECLS
\ No newline at end of file +__END_DECLS diff --git a/libs/binder/ndk/include_platform/android/binder_process.h b/libs/binder/ndk/include_platform/android/binder_process.h index fdefbb4b8a..f408fadee0 100644 --- a/libs/binder/ndk/include_platform/android/binder_process.h +++ b/libs/binder/ndk/include_platform/android/binder_process.h @@ -19,10 +19,15 @@ #include <stdint.h> #include <sys/cdefs.h> +#include <android/binder_status.h> + __BEGIN_DECLS /** * This creates a threadpool for incoming binder transactions if it has not already been created. + * + * When using this, it is expected that ABinderProcess_setupPolling and + * ABinderProcess_handlePolledCommands are not used. */ void ABinderProcess_startThreadPool(); /** @@ -37,4 +42,27 @@ bool ABinderProcess_setThreadPoolMaxThreadCount(uint32_t numThreads); */ void ABinderProcess_joinThreadPool(); +/** + * This gives you an fd to wait on. Whenever data is available on the fd, + * ABinderProcess_handlePolledCommands can be called to handle binder queries. + * This is expected to be used in a single threaded process which waits on + * events from multiple different fds. + * + * When using this, it is expected ABinderProcess_startThreadPool and + * ABinderProcess_joinThreadPool are not used. + * + * \param fd out param corresponding to the binder domain opened in this + * process. + * \return STATUS_OK on success + */ +__attribute__((weak)) binder_status_t ABinderProcess_setupPolling(int* fd) __INTRODUCED_IN(31); + +/** + * This will handle all queued binder commands in this process and then return. + * It is expected to be called whenever there is data on the fd. + * + * \return STATUS_OK on success + */ +__attribute__((weak)) binder_status_t ABinderProcess_handlePolledCommands() __INTRODUCED_IN(31); + __END_DECLS diff --git a/libs/binder/ndk/libbinder_ndk.map.txt b/libs/binder/ndk/libbinder_ndk.map.txt index a9eba47380..1701fb5705 100644 --- a/libs/binder/ndk/libbinder_ndk.map.txt +++ b/libs/binder/ndk/libbinder_ndk.map.txt @@ -95,8 +95,6 @@ LIBBINDER_NDK { # introduced=29 AServiceManager_addService; # apex llndk AServiceManager_checkService; # apex llndk AServiceManager_getService; # apex llndk - local: - *; }; LIBBINDER_NDK30 { # introduced=30 @@ -111,11 +109,26 @@ LIBBINDER_NDK30 { # introduced=30 AIBinder_markVendorStability; # llndk AIBinder_markVintfStability; # apex llndk AIBinder_Class_setHandleShellCommand; # apex llndk - local: - *; +}; + +LIBBINDER_NDK31 { # introduced=31 + global: + ABinderProcess_handlePolledCommands; # apex + ABinderProcess_setupPolling; # apex + AIBinder_getCallingSid; # apex + AIBinder_setRequestingSid; # apex + AServiceManager_isDeclared; # apex llndk + AServiceManager_registerLazyService; # llndk + AServiceManager_waitForService; # apex llndk }; LIBBINDER_NDK_PLATFORM { global: AParcel_getAllowFds; + extern "C++" { + AIBinder_fromPlatformBinder*; + AIBinder_toPlatformBinder*; + }; + local: + *; }; diff --git a/libs/binder/ndk/process.cpp b/libs/binder/ndk/process.cpp index c89caaf349..ac582a412e 100644 --- a/libs/binder/ndk/process.cpp +++ b/libs/binder/ndk/process.cpp @@ -34,3 +34,11 @@ bool ABinderProcess_setThreadPoolMaxThreadCount(uint32_t numThreads) { void ABinderProcess_joinThreadPool() { IPCThreadState::self()->joinThreadPool(); } + +binder_status_t ABinderProcess_setupPolling(int* fd) { + return IPCThreadState::self()->setupPolling(fd); +} + +binder_status_t ABinderProcess_handlePolledCommands() { + return IPCThreadState::self()->handlePolledCommands(); +} diff --git a/libs/binder/ndk/service_manager.cpp b/libs/binder/ndk/service_manager.cpp index d0b166d318..c782d47c1c 100644 --- a/libs/binder/ndk/service_manager.cpp +++ b/libs/binder/ndk/service_manager.cpp @@ -20,6 +20,7 @@ #include "status_internal.h" #include <binder/IServiceManager.h> +#include <binder/LazyServiceRegistrar.h> using ::android::defaultServiceManager; using ::android::IBinder; @@ -28,14 +29,14 @@ using ::android::sp; using ::android::status_t; using ::android::String16; -binder_status_t AServiceManager_addService(AIBinder* binder, const char* instance) { +binder_exception_t AServiceManager_addService(AIBinder* binder, const char* instance) { if (binder == nullptr || instance == nullptr) { - return STATUS_UNEXPECTED_NULL; + return EX_ILLEGAL_ARGUMENT; } sp<IServiceManager> sm = defaultServiceManager(); - status_t status = sm->addService(String16(instance), binder->getBinder()); - return PruneStatusT(status); + status_t exception = sm->addService(String16(instance), binder->getBinder()); + return PruneException(exception); } AIBinder* AServiceManager_checkService(const char* instance) { if (instance == nullptr) { @@ -61,3 +62,33 @@ AIBinder* AServiceManager_getService(const char* instance) { AIBinder_incStrong(ret.get()); return ret.get(); } +binder_status_t AServiceManager_registerLazyService(AIBinder* binder, const char* instance) { + if (binder == nullptr || instance == nullptr) { + return STATUS_UNEXPECTED_NULL; + } + + auto serviceRegistrar = android::binder::LazyServiceRegistrar::getInstance(); + status_t status = serviceRegistrar.registerService(binder->getBinder(), instance); + + return PruneStatusT(status); +} +AIBinder* AServiceManager_waitForService(const char* instance) { + if (instance == nullptr) { + return nullptr; + } + + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder = sm->waitForService(String16(instance)); + + sp<AIBinder> ret = ABpBinder::lookupOrCreateFromBinder(binder); + AIBinder_incStrong(ret.get()); + return ret.get(); +} +bool AServiceManager_isDeclared(const char* instance) { + if (instance == nullptr) { + return false; + } + + sp<IServiceManager> sm = defaultServiceManager(); + return sm->isDeclared(String16(instance)); +} diff --git a/libs/binder/ndk/status.cpp b/libs/binder/ndk/status.cpp index 87e1341f36..d889593474 100644 --- a/libs/binder/ndk/status.cpp +++ b/libs/binder/ndk/status.cpp @@ -123,8 +123,8 @@ binder_status_t PruneStatusT(status_t status) { return STATUS_UNKNOWN_ERROR; default: - LOG(WARNING) << __func__ - << ": Unknown status_t pruned into STATUS_UNKNOWN_ERROR: " << status; + LOG(WARNING) << __func__ << ": Unknown status_t (" << status + << ") pruned into STATUS_UNKNOWN_ERROR"; return STATUS_UNKNOWN_ERROR; } } @@ -155,8 +155,8 @@ binder_exception_t PruneException(int32_t exception) { return EX_TRANSACTION_FAILED; default: - LOG(WARNING) << __func__ - << ": Unknown status_t pruned into EX_TRANSACTION_FAILED: " << exception; + LOG(WARNING) << __func__ << ": Unknown binder exception (" << exception + << ") pruned into EX_TRANSACTION_FAILED"; return EX_TRANSACTION_FAILED; } } diff --git a/libs/binder/ndk/test/Android.bp b/libs/binder/ndk/tests/Android.bp index 5f5265c90e..46e6270eb0 100644 --- a/libs/binder/ndk/test/Android.bp +++ b/libs/binder/ndk/tests/Android.bp @@ -40,6 +40,7 @@ cc_library_static { cc_defaults { name: "test_libbinder_ndk_test_defaults", defaults: ["test_libbinder_ndk_defaults"], + // critical that libbinder/libbinder_ndk are shared for VTS shared_libs: [ "libandroid_runtime_lazy", "libbase", @@ -63,11 +64,8 @@ cc_test { "IBinderNdkUnitTest-cpp", "IBinderNdkUnitTest-ndk_platform", ], - test_suites: ["general-tests"], + test_suites: ["general-tests", "vts"], require_root: true, - - // force since binderVendorDoubleLoadTest has its own - auto_gen_config: true, } cc_test { @@ -81,13 +79,15 @@ cc_test { "IBinderVendorDoubleLoadTest-ndk_platform", "libbinder_aidl_test_stub-ndk_platform", ], + // critical that libbinder/libbinder_ndk are shared for VTS shared_libs: [ "libbase", "libbinder", "libbinder_ndk", "libutils", ], - test_suites: ["general-tests"], + test_suites: ["general-tests", "vts"], + require_root: true, } aidl_interface { diff --git a/libs/binder/ndk/test/IBinderNdkUnitTest.aidl b/libs/binder/ndk/tests/IBinderNdkUnitTest.aidl index 6e8e463ff1..dc77467d8c 100644 --- a/libs/binder/ndk/test/IBinderNdkUnitTest.aidl +++ b/libs/binder/ndk/tests/IBinderNdkUnitTest.aidl @@ -22,6 +22,10 @@ import IEmpty; interface IBinderNdkUnitTest { + int repeatInt(int a); + void takeInterface(IEmpty test); void forceFlushCommands(); + + boolean getsRequestedSid(); } diff --git a/libs/binder/ndk/test/IBinderVendorDoubleLoadTest.aidl b/libs/binder/ndk/tests/IBinderVendorDoubleLoadTest.aidl index 3a5bd9cc56..3a5bd9cc56 100644 --- a/libs/binder/ndk/test/IBinderVendorDoubleLoadTest.aidl +++ b/libs/binder/ndk/tests/IBinderVendorDoubleLoadTest.aidl diff --git a/libs/binder/ndk/test/IEmpty.aidl b/libs/binder/ndk/tests/IEmpty.aidl index 95e4341531..95e4341531 100644 --- a/libs/binder/ndk/test/IEmpty.aidl +++ b/libs/binder/ndk/tests/IEmpty.aidl diff --git a/libs/binder/ndk/test/binderVendorDoubleLoadTest.cpp b/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp index ad78e319a2..ad78e319a2 100644 --- a/libs/binder/ndk/test/binderVendorDoubleLoadTest.cpp +++ b/libs/binder/ndk/tests/binderVendorDoubleLoadTest.cpp diff --git a/libs/binder/ndk/test/iface.cpp b/libs/binder/ndk/tests/iface.cpp index a5889856fc..64832f3081 100644 --- a/libs/binder/ndk/test/iface.cpp +++ b/libs/binder/ndk/tests/iface.cpp @@ -118,7 +118,7 @@ IFoo::~IFoo() { AIBinder_Weak_delete(mWeakBinder); } -AIBinder* IFoo::getBinder() { +binder_status_t IFoo::addService(const char* instance) { AIBinder* binder = nullptr; if (mWeakBinder != nullptr) { @@ -132,18 +132,8 @@ AIBinder* IFoo::getBinder() { AIBinder_Weak_delete(mWeakBinder); } mWeakBinder = AIBinder_Weak_new(binder); - - // WARNING: it is important that this class does not implement debug or - // shell functions because it does not use special C++ wrapper - // functions, and so this is how we test those functions. } - return binder; -} - -binder_status_t IFoo::addService(const char* instance) { - AIBinder* binder = getBinder(); - binder_status_t status = AServiceManager_addService(binder, instance); // Strong references we care about kept by remote process AIBinder_decStrong(binder); diff --git a/libs/binder/ndk/test/include/iface/iface.h b/libs/binder/ndk/tests/include/iface/iface.h index d9dd64b8a6..cdf5493216 100644 --- a/libs/binder/ndk/test/include/iface/iface.h +++ b/libs/binder/ndk/tests/include/iface/iface.h @@ -30,9 +30,6 @@ class IFoo : public virtual ::android::RefBase { static AIBinder_Class* kClass; - // binder representing this interface with one reference count - AIBinder* getBinder(); - // Takes ownership of IFoo binder_status_t addService(const char* instance); static ::android::sp<IFoo> getService(const char* instance, AIBinder** outBinder = nullptr); diff --git a/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp index aaf36b97a4..6281014b3e 100644 --- a/libs/binder/ndk/test/libbinder_ndk_unit_test.cpp +++ b/libs/binder/ndk/tests/libbinder_ndk_unit_test.cpp @@ -19,10 +19,12 @@ #include <aidl/BnEmpty.h> #include <android-base/logging.h> #include <android/binder_ibinder_jni.h> +#include <android/binder_ibinder_platform.h> #include <android/binder_manager.h> #include <android/binder_process.h> #include <gtest/gtest.h> #include <iface/iface.h> +#include <utils/Looper.h> // warning: this is assuming that libbinder_ndk is using the same copy // of libbinder that we are. @@ -34,14 +36,20 @@ #include <sys/prctl.h> #include <chrono> #include <condition_variable> +#include <iostream> #include <mutex> using namespace android; constexpr char kExistingNonNdkService[] = "SurfaceFlinger"; constexpr char kBinderNdkUnitTestService[] = "BinderNdkUnitTest"; +constexpr char kLazyBinderNdkUnitTestService[] = "LazyBinderNdkUnitTest"; class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest { + ndk::ScopedAStatus repeatInt(int32_t in, int32_t* out) { + *out = in; + return ndk::ScopedAStatus::ok(); + } ndk::ScopedAStatus takeInterface(const std::shared_ptr<aidl::IEmpty>& empty) { (void)empty; return ndk::ScopedAStatus::ok(); @@ -52,6 +60,12 @@ class MyBinderNdkUnitTest : public aidl::BnBinderNdkUnitTest { android::IPCThreadState::self()->flushCommands(); return ndk::ScopedAStatus::ok(); } + ndk::ScopedAStatus getsRequestedSid(bool* out) { + const char* sid = AIBinder_getCallingSid(); + std::cout << "Got security context: " << (sid ?: "null") << std::endl; + *out = sid != nullptr; + 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++) { @@ -66,11 +80,15 @@ int generatedService() { ABinderProcess_setThreadPoolMaxThreadCount(0); auto service = ndk::SharedRefBase::make<MyBinderNdkUnitTest>(); - binder_status_t status = - AServiceManager_addService(service->asBinder().get(), kBinderNdkUnitTestService); + auto binder = service->asBinder(); - if (status != STATUS_OK) { - LOG(FATAL) << "Could not register: " << status << " " << kBinderNdkUnitTestService; + AIBinder_setRequestingSid(binder.get(), true); + + binder_exception_t exception = + AServiceManager_addService(binder.get(), kBinderNdkUnitTestService); + + if (exception != EX_NONE) { + LOG(FATAL) << "Could not register: " << exception << " " << kBinderNdkUnitTestService; } ABinderProcess_joinThreadPool(); @@ -92,12 +110,53 @@ class MyFoo : public IFoo { } }; -int manualService(const char* instance) { +void manualService(const char* instance) { + // Strong reference to MyFoo kept by service manager. + binder_exception_t exception = (new MyFoo)->addService(instance); + + if (exception != EX_NONE) { + LOG(FATAL) << "Could not register: " << exception << " " << instance; + } +} +int manualPollingService(const char* instance) { + int fd; + CHECK(STATUS_OK == ABinderProcess_setupPolling(&fd)); + manualService(instance); + + class Handler : public LooperCallback { + int handleEvent(int /*fd*/, int /*events*/, void* /*data*/) override { + ABinderProcess_handlePolledCommands(); + return 1; // Continue receiving callbacks. + } + }; + + sp<Looper> looper = Looper::prepare(0 /* opts */); + looper->addFd(fd, Looper::POLL_CALLBACK, Looper::EVENT_INPUT, new Handler(), nullptr /*data*/); + // normally, would add additional fds + while (true) { + looper->pollAll(-1 /* timeoutMillis */); + } + return 1; // should not reach +} +int manualThreadPoolService(const char* instance) { ABinderProcess_setThreadPoolMaxThreadCount(0); + manualService(instance); + ABinderProcess_joinThreadPool(); + return 1; +} - // Strong reference to MyFoo kept by service manager. - binder_status_t status = (new MyFoo)->addService(instance); +int lazyService(const char* instance) { + ABinderProcess_setThreadPoolMaxThreadCount(0); + // Wait to register this service to make sure the main test process will + // actually wait for the service to be available. Tested with sleep(60), + // and reduced for sake of time. + sleep(1); + // Strong reference to MyBinderNdkUnitTest kept by service manager. + // This is just for testing, it has no corresponding init behavior. + auto service = ndk::SharedRefBase::make<MyBinderNdkUnitTest>(); + auto binder = service->asBinder(); + binder_status_t status = AServiceManager_registerLazyService(binder.get(), instance); if (status != STATUS_OK) { LOG(FATAL) << "Could not register: " << status << " " << instance; } @@ -107,11 +166,10 @@ int manualService(const char* instance) { 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, GetServiceThatDoesntExist) { + sp<IFoo> foo = IFoo::getService("asdfghkl;"); + EXPECT_EQ(nullptr, foo.get()); +} TEST(NdkBinder, CheckServiceThatDoesntExist) { AIBinder* binder = AServiceManager_checkService("asdfghkl;"); @@ -126,33 +184,40 @@ TEST(NdkBinder, CheckServiceThatDoesExist) { AIBinder_decStrong(binder); } -TEST(NdkBinder, UnimplementedDump) { +TEST(NdkBinder, DoubleNumber) { sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName); ASSERT_NE(foo, nullptr); - AIBinder* binder = foo->getBinder(); - EXPECT_EQ(OK, AIBinder_dump(binder, STDOUT_FILENO, nullptr, 0)); - AIBinder_decStrong(binder); + + int32_t out; + EXPECT_EQ(STATUS_OK, foo->doubleNumber(1, &out)); + EXPECT_EQ(2, out); } -TEST(NdkBinder, UnimplementedShell) { - // libbinder_ndk doesn't support calling shell, so we are calling from the - // libbinder across processes to the NDK service which doesn't implement - // shell - static const sp<android::IServiceManager> sm(android::defaultServiceManager()); - sp<IBinder> testService = sm->getService(String16(IFoo::kSomeInstanceName)); +TEST(NdkBinder, GetLazyService) { + // Not declared in the vintf manifest + ASSERT_FALSE(AServiceManager_isDeclared(kLazyBinderNdkUnitTestService)); + ndk::SpAIBinder binder(AServiceManager_waitForService(kLazyBinderNdkUnitTestService)); + std::shared_ptr<aidl::IBinderNdkUnitTest> service = + aidl::IBinderNdkUnitTest::fromBinder(binder); + ASSERT_NE(service, nullptr); - Vector<String16> argsVec; - EXPECT_EQ(OK, IBinder::shellCommand(testService, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, - argsVec, nullptr, nullptr)); + EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get())); } -TEST(NdkBinder, DoubleNumber) { - sp<IFoo> foo = IFoo::getService(IFoo::kSomeInstanceName); - ASSERT_NE(foo, nullptr); +// This is too slow +TEST(NdkBinder, CheckLazyServiceShutDown) { + ndk::SpAIBinder binder(AServiceManager_waitForService(kLazyBinderNdkUnitTestService)); + std::shared_ptr<aidl::IBinderNdkUnitTest> service = + aidl::IBinderNdkUnitTest::fromBinder(binder); + ASSERT_NE(service, nullptr); - int32_t out; - EXPECT_EQ(STATUS_OK, foo->doubleNumber(1, &out)); - EXPECT_EQ(2, out); + EXPECT_EQ(STATUS_OK, AIBinder_ping(binder.get())); + binder = nullptr; + service = nullptr; + IPCThreadState::self()->flushCommands(); + // Make sure the service is dead after some time of no use + sleep(10); + ASSERT_EQ(nullptr, AServiceManager_checkService(kLazyBinderNdkUnitTestService)); } void LambdaOnDeath(void* cookie) { @@ -238,11 +303,20 @@ class MyTestFoo : public IFoo { } }; +TEST(NdkBinder, AddNullService) { + EXPECT_EQ(EX_ILLEGAL_ARGUMENT, AServiceManager_addService(nullptr, "any-service-name")); +} + +TEST(NdkBinder, AddInvalidServiceName) { + sp<IFoo> foo = new MyTestFoo; + EXPECT_EQ(EX_ILLEGAL_ARGUMENT, foo->addService("!@#$%^&")); +} + TEST(NdkBinder, GetServiceInProcess) { static const char* kInstanceName = "test-get-service-in-process"; sp<IFoo> foo = new MyTestFoo; - EXPECT_EQ(STATUS_OK, foo->addService(kInstanceName)); + EXPECT_EQ(EX_NONE, foo->addService(kInstanceName)); sp<IFoo> getFoo = IFoo::getService(kInstanceName); EXPECT_EQ(foo.get(), getFoo.get()); @@ -289,11 +363,21 @@ 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(EX_NONE, foo->addService(kInstanceName1)); + EXPECT_EQ(EX_NONE, foo->addService(kInstanceName2)); EXPECT_EQ(IFoo::getService(kInstanceName1), IFoo::getService(kInstanceName2)); } +TEST(NdkBinder, RequestedSidWorks) { + ndk::SpAIBinder binder(AServiceManager_getService(kBinderNdkUnitTestService)); + std::shared_ptr<aidl::IBinderNdkUnitTest> service = + aidl::IBinderNdkUnitTest::fromBinder(binder); + + bool gotSid = false; + EXPECT_TRUE(service->getsRequestedSid(&gotSid).isOk()); + EXPECT_TRUE(gotSid); +} + TEST(NdkBinder, SentAidlBinderCanBeDestroyed) { static volatile bool destroyed = false; static std::mutex dMutex; @@ -328,6 +412,30 @@ TEST(NdkBinder, SentAidlBinderCanBeDestroyed) { EXPECT_TRUE(destroyed); } +TEST(NdkBinder, ConvertToPlatformBinder) { + for (const ndk::SpAIBinder& binder : + {// remote + ndk::SpAIBinder(AServiceManager_getService(kBinderNdkUnitTestService)), + // local + ndk::SharedRefBase::make<MyBinderNdkUnitTest>()->asBinder()}) { + // convert to platform binder + EXPECT_NE(binder.get(), nullptr); + sp<IBinder> platformBinder = AIBinder_toPlatformBinder(binder.get()); + EXPECT_NE(platformBinder.get(), nullptr); + auto proxy = interface_cast<IBinderNdkUnitTest>(platformBinder); + EXPECT_NE(proxy, nullptr); + + // use platform binder + int out; + EXPECT_TRUE(proxy->repeatInt(4, &out).isOk()); + EXPECT_EQ(out, 4); + + // convert back + ndk::SpAIBinder backBinder = ndk::SpAIBinder(AIBinder_fromPlatformBinder(platformBinder)); + EXPECT_EQ(backBinder.get(), binder.get()); + } +} + class MyResultReceiver : public BnResultReceiver { public: Mutex mMutex; @@ -419,11 +527,15 @@ int main(int argc, char* argv[]) { if (fork() == 0) { prctl(PR_SET_PDEATHSIG, SIGHUP); - return manualService(IFoo::kInstanceNameToDieFor); + return manualThreadPoolService(IFoo::kInstanceNameToDieFor); + } + if (fork() == 0) { + prctl(PR_SET_PDEATHSIG, SIGHUP); + return manualPollingService(IFoo::kSomeInstanceName); } if (fork() == 0) { prctl(PR_SET_PDEATHSIG, SIGHUP); - return manualService(IFoo::kSomeInstanceName); + return lazyService(kLazyBinderNdkUnitTestService); } if (fork() == 0) { prctl(PR_SET_PDEATHSIG, SIGHUP); diff --git a/libs/binder/rust/Android.bp b/libs/binder/rust/Android.bp new file mode 100644 index 0000000000..0234820b02 --- /dev/null +++ b/libs/binder/rust/Android.bp @@ -0,0 +1,86 @@ +rust_library { + name: "libbinder_rs", + crate_name: "binder", + srcs: ["src/lib.rs"], + shared_libs: [ + "libutils", + ], + rustlibs: [ + "liblibc", + "libbinder_ndk_sys", + ], + host_supported: true, +} + +rust_library { + name: "libbinder_ndk_sys", + crate_name: "binder_ndk_sys", + srcs: [ + "sys/lib.rs", + ":libbinder_ndk_bindgen", + ], + shared_libs: [ + "libbinder_ndk", + ], + host_supported: true, +} + +rust_bindgen { + name: "libbinder_ndk_bindgen", + crate_name: "binder_ndk_bindgen", + wrapper_src: "sys/BinderBindings.h", + source_stem: "bindings", + cflags: [ + "-x c++", + ], + bindgen_flags: [ + // Unfortunately the only way to specify the rust_non_exhaustive enum + // style for a type is to make it the default + "--default-enum-style", "rust_non_exhaustive", + // and then specify constified enums for the enums we don't want + // rustified + "--constified-enum", "android::c_interface::consts::.*", + + "--whitelist-type", "android::c_interface::.*", + "--whitelist-type", "AStatus", + "--whitelist-type", "AIBinder_Class", + "--whitelist-type", "AIBinder", + "--whitelist-type", "AIBinder_Weak", + "--whitelist-type", "AIBinder_DeathRecipient", + "--whitelist-type", "AParcel", + "--whitelist-type", "binder_status_t", + "--whitelist-function", ".*", + ], + shared_libs: [ + "libbinder_ndk", + ], + host_supported: true, + + // Currently necessary for host builds + // TODO(b/31559095): bionic on host should define this + target: { + host: { + cflags: [ + "-D__INTRODUCED_IN(n)=", + "-D__assert(a,b,c)=", + // We want all the APIs to be available on the host. + "-D__ANDROID_API__=10000", + ], + }, + }, +} + +rust_test { + name: "libbinder_rs-internal_test", + crate_name: "binder", + srcs: ["src/lib.rs"], + test_suites: ["general-tests"], + auto_gen_config: true, + shared_libs: [ + "libbinder_ndk", + ], + rustlibs: [ + "liblibc", + "libbinder_ndk_sys", + ], +} diff --git a/libs/binder/rust/TEST_MAPPING b/libs/binder/rust/TEST_MAPPING new file mode 100644 index 0000000000..50c474cdf2 --- /dev/null +++ b/libs/binder/rust/TEST_MAPPING @@ -0,0 +1,12 @@ +{ + "presubmit": [ + { + "name": "libbinder_rs-internal_test" + } + ], + "postsubmit": [ + { + "name": "rustBinderTest" + } + ] +} diff --git a/libs/binder/rust/src/binder.rs b/libs/binder/rust/src/binder.rs new file mode 100644 index 0000000000..d55eafee67 --- /dev/null +++ b/libs/binder/rust/src/binder.rs @@ -0,0 +1,585 @@ +/* + * 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. + */ + +//! Trait definitions for binder objects + +use crate::error::{status_t, Result}; +use crate::parcel::Parcel; +use crate::proxy::{DeathRecipient, SpIBinder}; +use crate::sys; + +use std::ffi::{c_void, CString}; +use std::os::unix::io::AsRawFd; +use std::ptr; + +/// Binder action to perform. +/// +/// This must be a number between [`IBinder::FIRST_CALL_TRANSACTION`] and +/// [`IBinder::LAST_CALL_TRANSACTION`]. +pub type TransactionCode = u32; + +/// Additional operation flags. +/// +/// Can be either 0 for a normal RPC, or [`IBinder::FLAG_ONEWAY`] for a +/// one-way RPC. +pub type TransactionFlags = u32; + +/// Super-trait for Binder interfaces. +/// +/// This trait allows conversion of a Binder interface trait object into an +/// IBinder object for IPC calls. All Binder remotable interface (i.e. AIDL +/// interfaces) must implement this trait. +/// +/// This is equivalent `IInterface` in C++. +pub trait Interface { + /// Convert this binder object into a generic [`SpIBinder`] reference. + fn as_binder(&self) -> SpIBinder { + panic!("This object was not a Binder object and cannot be converted into an SpIBinder.") + } +} + +/// A local service that can be remotable via Binder. +/// +/// An object that implement this interface made be made into a Binder service +/// via `Binder::new(object)`. +/// +/// This is a low-level interface that should normally be automatically +/// generated from AIDL via the [`declare_binder_interface!`] macro. When using +/// the AIDL backend, users need only implement the high-level AIDL-defined +/// interface. The AIDL compiler then generates a container struct that wraps +/// the user-defined service and implements `Remotable`. +pub trait Remotable: Send + Sync { + /// The Binder interface descriptor string. + /// + /// This string is a unique identifier for a Binder interface, and should be + /// the same between all implementations of that interface. + fn get_descriptor() -> &'static str; + + /// Handle and reply to a request to invoke a transaction on this object. + /// + /// `reply` may be [`None`] if the sender does not expect a reply. + fn on_transact(&self, code: TransactionCode, data: &Parcel, reply: &mut Parcel) -> Result<()>; + + /// Retrieve the class of this remote object. + /// + /// This method should always return the same InterfaceClass for the same + /// type. + fn get_class() -> InterfaceClass; +} + +/// Interface of binder local or remote objects. +/// +/// This trait corresponds to the interface of the C++ `IBinder` class. +pub trait IBinder { + /// First transaction code available for user commands (inclusive) + const FIRST_CALL_TRANSACTION: TransactionCode = sys::FIRST_CALL_TRANSACTION; + /// Last transaction code available for user commands (inclusive) + const LAST_CALL_TRANSACTION: TransactionCode = sys::LAST_CALL_TRANSACTION; + + /// Corresponds to TF_ONE_WAY -- an asynchronous call. + const FLAG_ONEWAY: TransactionFlags = sys::FLAG_ONEWAY; + + /// Is this object still alive? + fn is_binder_alive(&self) -> bool; + + /// Send a ping transaction to this object + fn ping_binder(&mut self) -> Result<()>; + + /// Dump this object to the given file handle + fn dump<F: AsRawFd>(&mut self, fp: &F, args: &[&str]) -> Result<()>; + + /// Get a new interface that exposes additional extension functionality, if + /// available. + fn get_extension(&mut self) -> Result<Option<SpIBinder>>; + + /// Perform a generic operation with the object. + /// + /// # Arguments + /// * `code` - Transaction code for the operation + /// * `data` - [`Parcel`] with input data + /// * `reply` - Optional [`Parcel`] for reply data + /// * `flags` - Transaction flags, e.g. marking the transaction as + /// asynchronous ([`FLAG_ONEWAY`](IBinder::FLAG_ONEWAY)) + fn transact<F: FnOnce(&mut Parcel) -> Result<()>>( + &self, + code: TransactionCode, + flags: TransactionFlags, + input_callback: F, + ) -> Result<Parcel>; + + /// Register the recipient for a notification if this binder + /// goes away. If this binder object unexpectedly goes away + /// (typically because its hosting process has been killed), + /// then DeathRecipient::binder_died() will be called with a reference + /// to this. + /// + /// You will only receive death notifications for remote binders, + /// as local binders by definition can't die without you dying as well. + /// Trying to use this function on a local binder will result in an + /// INVALID_OPERATION code being returned and nothing happening. + /// + /// This link always holds a weak reference to its recipient. + /// + /// You will only receive a weak reference to the dead + /// binder. You should not try to promote this to a strong reference. + /// (Nor should you need to, as there is nothing useful you can + /// directly do with it now that it has passed on.) + fn link_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()>; + + /// Remove a previously registered death notification. + /// The recipient will no longer be called if this object + /// dies. + fn unlink_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()>; +} + +/// Opaque reference to the type of a Binder interface. +/// +/// This object encapsulates the Binder interface descriptor string, along with +/// the binder transaction callback, if the class describes a local service. +/// +/// A Binder remotable object may only have a single interface class, and any +/// given object can only be associated with one class. Two objects with +/// different classes are incompatible, even if both classes have the same +/// interface descriptor. +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct InterfaceClass(*const sys::AIBinder_Class); + +impl InterfaceClass { + /// Get a Binder NDK `AIBinder_Class` pointer for this object type. + /// + /// Note: the returned pointer will not be constant. Calling this method + /// multiple times for the same type will result in distinct class + /// pointers. A static getter for this value is implemented in + /// [`declare_binder_interface!`]. + pub fn new<I: InterfaceClassMethods>() -> InterfaceClass { + let descriptor = CString::new(I::get_descriptor()).unwrap(); + let ptr = unsafe { + // Safety: `AIBinder_Class_define` expects a valid C string, and + // three valid callback functions, all non-null pointers. The C + // string is copied and need not be valid for longer than the call, + // so we can drop it after the call. We can safely assign null to + // the onDump and handleShellCommand callbacks as long as the class + // pointer was non-null. Rust None for a Option<fn> is guaranteed to + // be a NULL pointer. Rust retains ownership of the pointer after it + // is defined. + let class = sys::AIBinder_Class_define( + descriptor.as_ptr(), + Some(I::on_create), + Some(I::on_destroy), + Some(I::on_transact), + ); + if class.is_null() { + panic!("Expected non-null class pointer from AIBinder_Class_define!"); + } + sys::AIBinder_Class_setOnDump(class, None); + sys::AIBinder_Class_setHandleShellCommand(class, None); + class + }; + InterfaceClass(ptr) + } + + /// Construct an `InterfaceClass` out of a raw, non-null `AIBinder_Class` + /// pointer. + /// + /// # Safety + /// + /// This function is safe iff `ptr` is a valid, non-null pointer to an + /// `AIBinder_Class`. + pub(crate) unsafe fn from_ptr(ptr: *const sys::AIBinder_Class) -> InterfaceClass { + InterfaceClass(ptr) + } +} + +impl From<InterfaceClass> for *const sys::AIBinder_Class { + fn from(class: InterfaceClass) -> *const sys::AIBinder_Class { + class.0 + } +} + +/// Create a function implementing a static getter for an interface class. +/// +/// Each binder interface (i.e. local [`Remotable`] service or remote proxy +/// [`Interface`]) must have global, static class that uniquely identifies +/// it. This macro implements an [`InterfaceClass`] getter to simplify these +/// implementations. +/// +/// The type of a structure that implements [`InterfaceClassMethods`] must be +/// passed to this macro. For local services, this should be `Binder<Self>` +/// since [`Binder`] implements [`InterfaceClassMethods`]. +/// +/// # Examples +/// +/// When implementing a local [`Remotable`] service `ExampleService`, the +/// `get_class` method is required in the [`Remotable`] impl block. This macro +/// should be used as follows to implement this functionality: +/// +/// ```rust +/// impl Remotable for ExampleService { +/// fn get_descriptor() -> &'static str { +/// "android.os.IExampleInterface" +/// } +/// +/// fn on_transact( +/// &self, +/// code: TransactionCode, +/// data: &Parcel, +/// reply: &mut Parcel, +/// ) -> Result<()> { +/// // ... +/// } +/// +/// binder_fn_get_class!(Binder<Self>); +/// } +/// ``` +macro_rules! binder_fn_get_class { + ($class:ty) => { + binder_fn_get_class!($crate::InterfaceClass::new::<$class>()); + }; + + ($constructor:expr) => { + fn get_class() -> $crate::InterfaceClass { + static CLASS_INIT: std::sync::Once = std::sync::Once::new(); + static mut CLASS: Option<$crate::InterfaceClass> = None; + + CLASS_INIT.call_once(|| unsafe { + // Safety: This assignment is guarded by the `CLASS_INIT` `Once` + // variable, and therefore is thread-safe, as it can only occur + // once. + CLASS = Some($constructor); + }); + unsafe { + // Safety: The `CLASS` variable can only be mutated once, above, + // and is subsequently safe to read from any thread. + CLASS.unwrap() + } + } + }; +} + +pub trait InterfaceClassMethods { + /// Get the interface descriptor string for this object type. + fn get_descriptor() -> &'static str + where + Self: Sized; + + /// Called during construction of a new `AIBinder` object of this interface + /// class. + /// + /// The opaque pointer parameter will be the parameter provided to + /// `AIBinder_new`. Returns an opaque userdata to be associated with the new + /// `AIBinder` object. + /// + /// # Safety + /// + /// Callback called from C++. The parameter argument provided to + /// `AIBinder_new` must match the type expected here. The `AIBinder` object + /// will take ownership of the returned pointer, which it will free via + /// `on_destroy`. + unsafe extern "C" fn on_create(args: *mut c_void) -> *mut c_void; + + /// Called when a transaction needs to be processed by the local service + /// implementation. + /// + /// # Safety + /// + /// Callback called from C++. The `binder` parameter must be a valid pointer + /// to a binder object of this class with userdata initialized via this + /// class's `on_create`. The parcel parameters must be valid pointers to + /// parcel objects. + unsafe extern "C" fn on_transact( + binder: *mut sys::AIBinder, + code: u32, + data: *const sys::AParcel, + reply: *mut sys::AParcel, + ) -> status_t; + + /// Called whenever an `AIBinder` object is no longer referenced and needs + /// to be destroyed. + /// + /// # Safety + /// + /// Callback called from C++. The opaque pointer parameter must be the value + /// returned by `on_create` for this class. This function takes ownership of + /// the provided pointer and destroys it. + unsafe extern "C" fn on_destroy(object: *mut c_void); +} + +/// Interface for transforming a generic SpIBinder into a specific remote +/// interface trait. +/// +/// # Example +/// +/// For Binder interface `IFoo`, the following implementation should be made: +/// ```no_run +/// # use binder::{FromIBinder, SpIBinder, Result}; +/// # trait IFoo {} +/// impl FromIBinder for dyn IFoo { +/// fn try_from(ibinder: SpIBinder) -> Result<Box<Self>> { +/// // ... +/// # Err(binder::StatusCode::OK) +/// } +/// } +/// ``` +pub trait FromIBinder { + /// Try to interpret a generic Binder object as this interface. + /// + /// Returns a trait object for the `Self` interface if this object + /// implements that interface. + fn try_from(ibinder: SpIBinder) -> Result<Box<Self>>; +} + +/// Trait for transparent Rust wrappers around android C++ native types. +/// +/// The pointer return by this trait's methods should be immediately passed to +/// C++ and not stored by Rust. The pointer is valid only as long as the +/// underlying C++ object is alive, so users must be careful to take this into +/// account, as Rust cannot enforce this. +/// +/// # Safety +/// +/// For this trait to be a correct implementation, `T` must be a valid android +/// C++ type. Since we cannot constrain this via the type system, this trait is +/// marked as unsafe. +pub unsafe trait AsNative<T> { + /// Return a pointer to the native version of `self` + fn as_native(&self) -> *const T; + + /// Return a mutable pointer to the native version of `self` + fn as_native_mut(&mut self) -> *mut T; +} + +unsafe impl<T, V: AsNative<T>> AsNative<T> for Option<V> { + fn as_native(&self) -> *const T { + self.as_ref().map_or(ptr::null(), |v| v.as_native()) + } + + fn as_native_mut(&mut self) -> *mut T { + self.as_mut().map_or(ptr::null_mut(), |v| v.as_native_mut()) + } +} + +/// Declare typed interfaces for a binder object. +/// +/// Given an interface trait and descriptor string, create a native and remote +/// proxy wrapper for this interface. The native service object (`$native`) +/// implements `Remotable` and will dispatch to the function `$on_transact` to +/// handle transactions. The typed proxy object (`$proxy`) wraps remote binder +/// objects for this interface and can optionally contain additional fields. +/// +/// Assuming the interface trait is `Interface`, `$on_transact` function must +/// have the following type: +/// +/// ``` +/// # use binder::{Interface, TransactionCode, Parcel}; +/// # trait Placeholder { +/// fn on_transact( +/// service: &dyn Interface, +/// code: TransactionCode, +/// data: &Parcel, +/// reply: &mut Parcel, +/// ) -> binder::Result<()>; +/// # } +/// ``` +/// +/// # Examples +/// +/// The following example declares the local service type `BnServiceManager` and +/// a remote proxy type `BpServiceManager` (the `n` and `p` stand for native and +/// proxy respectively) for the `IServiceManager` Binder interface. The +/// interfaces will be identified by the descriptor string +/// "android.os.IServiceManager". The local service will dispatch transactions +/// using the provided function, `on_transact`. +/// +/// ``` +/// use binder::{declare_binder_interface, Binder, Interface, TransactionCode, Parcel}; +/// +/// pub trait IServiceManager: Interface { +/// // remote methods... +/// } +/// +/// declare_binder_interface! { +/// IServiceManager["android.os.IServiceManager"] { +/// native: BnServiceManager(on_transact), +/// proxy: BpServiceManager, +/// } +/// } +/// +/// fn on_transact( +/// service: &dyn IServiceManager, +/// code: TransactionCode, +/// data: &Parcel, +/// reply: &mut Parcel, +/// ) -> binder::Result<()> { +/// // ... +/// Ok(()) +/// } +/// +/// impl IServiceManager for BpServiceManager { +/// // parceling/unparceling code for the IServiceManager emitted here +/// } +/// +/// impl IServiceManager for Binder<BnServiceManager> { +/// // Forward calls to local implementation +/// } +/// ``` +#[macro_export] +macro_rules! declare_binder_interface { + { + $interface:path[$descriptor:expr] { + native: $native:ident($on_transact:path), + proxy: $proxy:ident, + } + } => { + $crate::declare_binder_interface! { + $interface[$descriptor] { + native: $native($on_transact), + proxy: $proxy {}, + } + } + }; + + { + $interface:path[$descriptor:expr] { + native: $native:ident($on_transact:path), + proxy: $proxy:ident { + $($fname:ident: $fty:ty = $finit:expr),* + }, + } + } => { + $crate::declare_binder_interface! { + $interface[$descriptor] { + @doc[concat!("A binder [`Remotable`]($crate::Remotable) that holds an [`", stringify!($interface), "`] object.")] + native: $native($on_transact), + @doc[concat!("A binder [`Proxy`]($crate::Proxy) that holds an [`", stringify!($interface), "`] remote interface.")] + proxy: $proxy { + $($fname: $fty = $finit),* + }, + } + } + }; + + { + $interface:path[$descriptor:expr] { + @doc[$native_doc:expr] + native: $native:ident($on_transact:path), + + @doc[$proxy_doc:expr] + proxy: $proxy:ident { + $($fname:ident: $fty:ty = $finit:expr),* + }, + } + } => { + #[doc = $proxy_doc] + pub struct $proxy { + binder: $crate::SpIBinder, + $($fname: $fty,)* + } + + impl $crate::Interface for $proxy { + fn as_binder(&self) -> $crate::SpIBinder { + self.binder.clone() + } + } + + impl $crate::Proxy for $proxy + where + $proxy: $interface, + { + fn get_descriptor() -> &'static str { + $descriptor + } + + fn from_binder(mut binder: $crate::SpIBinder) -> $crate::Result<Self> { + use $crate::AssociateClass; + if binder.associate_class(<$native as $crate::Remotable>::get_class()) { + Ok(Self { binder, $($fname: $finit),* }) + } else { + Err($crate::StatusCode::BAD_TYPE) + } + } + } + + #[doc = $native_doc] + #[repr(transparent)] + pub struct $native(Box<dyn $interface + Sync + Send + 'static>); + + impl $native { + /// Create a new binder service. + pub fn new_binder<T: $interface + Sync + Send + 'static>(inner: T) -> impl $interface { + $crate::Binder::new($native(Box::new(inner))) + } + } + + impl $crate::Remotable for $native { + fn get_descriptor() -> &'static str { + $descriptor + } + + fn on_transact(&self, code: $crate::TransactionCode, data: &$crate::Parcel, reply: &mut $crate::Parcel) -> $crate::Result<()> { + $on_transact(&*self.0, code, data, reply) + } + + fn get_class() -> $crate::InterfaceClass { + static CLASS_INIT: std::sync::Once = std::sync::Once::new(); + static mut CLASS: Option<$crate::InterfaceClass> = None; + + CLASS_INIT.call_once(|| unsafe { + // Safety: This assignment is guarded by the `CLASS_INIT` `Once` + // variable, and therefore is thread-safe, as it can only occur + // once. + CLASS = Some($crate::InterfaceClass::new::<$crate::Binder<$native>>()); + }); + unsafe { + // Safety: The `CLASS` variable can only be mutated once, above, + // and is subsequently safe to read from any thread. + CLASS.unwrap() + } + } + } + + impl $crate::FromIBinder for dyn $interface { + fn try_from(mut ibinder: $crate::SpIBinder) -> $crate::Result<Box<dyn $interface>> { + use $crate::AssociateClass; + if !ibinder.associate_class(<$native as $crate::Remotable>::get_class()) { + return Err($crate::StatusCode::BAD_TYPE.into()); + } + + let service: $crate::Result<$crate::Binder<$native>> = std::convert::TryFrom::try_from(ibinder.clone()); + if let Ok(service) = service { + Ok(Box::new(service)) + } else { + Ok(Box::new(<$proxy as $crate::Proxy>::from_binder(ibinder)?)) + } + } + } + + impl $crate::parcel::Serialize for dyn $interface + '_ + where + $interface: $crate::Interface + { + fn serialize(&self, parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> { + let binder = $crate::Interface::as_binder(self); + parcel.write(&binder) + } + } + + impl $crate::parcel::SerializeOption for dyn $interface + '_ { + fn serialize_option(this: Option<&Self>, parcel: &mut $crate::parcel::Parcel) -> $crate::Result<()> { + parcel.write(&this.map($crate::Interface::as_binder)) + } + } + }; +} diff --git a/libs/binder/rust/src/error.rs b/libs/binder/rust/src/error.rs new file mode 100644 index 0000000000..4492cf72f4 --- /dev/null +++ b/libs/binder/rust/src/error.rs @@ -0,0 +1,365 @@ +/* + * 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. + */ + +use crate::binder::AsNative; +use crate::sys; + +use std::error; +use std::ffi::CStr; +use std::fmt::{Debug, Display, Formatter, Result as FmtResult}; +use std::result; + +pub use sys::binder_status_t as status_t; + +/// Low-level status codes from Android `libutils`. +// All error codes are negative integer values. Derived from the anonymous enum +// in utils/Errors.h +pub use sys::android_c_interface_StatusCode as StatusCode; + +/// A specialized [`Result`](result::Result) for binder operations. +pub type Result<T> = result::Result<T, StatusCode>; + +/// Convert a low-level status code into an empty result. +/// +/// An OK status is converted into an `Ok` result, any other status is converted +/// into an `Err` result holding the status code. +pub fn status_result(status: status_t) -> Result<()> { + match parse_status_code(status) { + StatusCode::OK => Ok(()), + e => Err(e), + } +} + +fn parse_status_code(code: i32) -> StatusCode { + match code { + e if e == StatusCode::OK as i32 => StatusCode::OK, + e if e == StatusCode::NO_MEMORY as i32 => StatusCode::NO_MEMORY, + e if e == StatusCode::INVALID_OPERATION as i32 => StatusCode::INVALID_OPERATION, + e if e == StatusCode::BAD_VALUE as i32 => StatusCode::BAD_VALUE, + e if e == StatusCode::BAD_TYPE as i32 => StatusCode::BAD_TYPE, + e if e == StatusCode::NAME_NOT_FOUND as i32 => StatusCode::NAME_NOT_FOUND, + e if e == StatusCode::PERMISSION_DENIED as i32 => StatusCode::PERMISSION_DENIED, + e if e == StatusCode::NO_INIT as i32 => StatusCode::NO_INIT, + e if e == StatusCode::ALREADY_EXISTS as i32 => StatusCode::ALREADY_EXISTS, + e if e == StatusCode::DEAD_OBJECT as i32 => StatusCode::DEAD_OBJECT, + e if e == StatusCode::FAILED_TRANSACTION as i32 => StatusCode::FAILED_TRANSACTION, + e if e == StatusCode::BAD_INDEX as i32 => StatusCode::BAD_INDEX, + e if e == StatusCode::NOT_ENOUGH_DATA as i32 => StatusCode::NOT_ENOUGH_DATA, + e if e == StatusCode::WOULD_BLOCK as i32 => StatusCode::WOULD_BLOCK, + e if e == StatusCode::TIMED_OUT as i32 => StatusCode::TIMED_OUT, + e if e == StatusCode::UNKNOWN_TRANSACTION as i32 => StatusCode::UNKNOWN_TRANSACTION, + e if e == StatusCode::FDS_NOT_ALLOWED as i32 => StatusCode::FDS_NOT_ALLOWED, + e if e == StatusCode::UNEXPECTED_NULL as i32 => StatusCode::UNEXPECTED_NULL, + _ => StatusCode::UNKNOWN_ERROR, + } +} + +pub use sys::android_c_interface_ExceptionCode as ExceptionCode; + +fn parse_exception_code(code: i32) -> ExceptionCode { + match code { + e if e == ExceptionCode::NONE as i32 => ExceptionCode::NONE, + e if e == ExceptionCode::SECURITY as i32 => ExceptionCode::SECURITY, + e if e == ExceptionCode::BAD_PARCELABLE as i32 => ExceptionCode::BAD_PARCELABLE, + e if e == ExceptionCode::ILLEGAL_ARGUMENT as i32 => ExceptionCode::ILLEGAL_ARGUMENT, + e if e == ExceptionCode::NULL_POINTER as i32 => ExceptionCode::NULL_POINTER, + e if e == ExceptionCode::ILLEGAL_STATE as i32 => ExceptionCode::ILLEGAL_STATE, + e if e == ExceptionCode::NETWORK_MAIN_THREAD as i32 => { + ExceptionCode::NETWORK_MAIN_THREAD + } + e if e == ExceptionCode::UNSUPPORTED_OPERATION as i32 => { + ExceptionCode::UNSUPPORTED_OPERATION + } + e if e == ExceptionCode::SERVICE_SPECIFIC as i32 => ExceptionCode::SERVICE_SPECIFIC, + _ => ExceptionCode::TRANSACTION_FAILED, + } +} + +// Safety: `Status` always contains a owning pointer to a valid `AStatus`. The +// lifetime of the contained pointer is the same as the `Status` object. +/// High-level binder status object that encapsulates a standard way to keep +/// track of and chain binder errors along with service specific errors. +/// +/// Used in AIDL transactions to represent failed transactions. +pub struct Status(*mut sys::AStatus); + +impl Status { + /// Create a status object representing a successful transaction. + pub fn ok() -> Self { + let ptr = unsafe { + // Safety: `AStatus_newOk` always returns a new, heap allocated + // pointer to an `ASTatus` object, so we know this pointer will be + // valid. + // + // Rust takes ownership of the returned pointer. + sys::AStatus_newOk() + }; + Self(ptr) + } + + /// Create a status object from a service specific error + pub fn new_service_specific_error(err: i32, message: Option<&CStr>) -> Status { + let ptr = if let Some(message) = message { + unsafe { + // Safety: Any i32 is a valid service specific error for the + // error code parameter. We construct a valid, null-terminated + // `CString` from the message, which must be a valid C-style + // string to pass as the message. This function always returns a + // new, heap allocated pointer to an `AStatus` object, so we + // know the returned pointer will be valid. + // + // Rust takes ownership of the returned pointer. + sys::AStatus_fromServiceSpecificErrorWithMessage(err, message.as_ptr()) + } + } else { + unsafe { + // Safety: Any i32 is a valid service specific error for the + // error code parameter. This function always returns a new, + // heap allocated pointer to an `AStatus` object, so we know the + // returned pointer will be valid. + // + // Rust takes ownership of the returned pointer. + sys::AStatus_fromServiceSpecificError(err) + } + }; + Self(ptr) + } + + /// Create a status object from an exception code + pub fn new_exception(exception: ExceptionCode, message: Option<&CStr>) -> Status { + if let Some(message) = message { + let ptr = unsafe { + sys::AStatus_fromExceptionCodeWithMessage(exception as i32, message.as_ptr()) + }; + Self(ptr) + } else { + exception.into() + } + } + + /// Create a status object from a raw `AStatus` pointer. + /// + /// # Safety + /// + /// This constructor is safe iff `ptr` is a valid pointer to an `AStatus`. + pub(crate) unsafe fn from_ptr(ptr: *mut sys::AStatus) -> Self { + Self(ptr) + } + + /// Returns `true` if this status represents a successful transaction. + pub fn is_ok(&self) -> bool { + unsafe { + // Safety: `Status` always contains a valid `AStatus` pointer, so we + // are always passing a valid pointer to `AStatus_isOk` here. + sys::AStatus_isOk(self.as_native()) + } + } + + /// Returns a description of the status. + pub fn get_description(&self) -> String { + let description_ptr = unsafe { + // Safety: `Status` always contains a valid `AStatus` pointer, so we + // are always passing a valid pointer to `AStatus_getDescription` + // here. + // + // `AStatus_getDescription` always returns a valid pointer to a null + // terminated C string. Rust is responsible for freeing this pointer + // via `AStatus_deleteDescription`. + sys::AStatus_getDescription(self.as_native()) + }; + let description = unsafe { + // Safety: `AStatus_getDescription` always returns a valid C string, + // which can be safely converted to a `CStr`. + CStr::from_ptr(description_ptr) + }; + let description = description.to_string_lossy().to_string(); + unsafe { + // Safety: `description_ptr` was returned from + // `AStatus_getDescription` above, and must be freed via + // `AStatus_deleteDescription`. We must not access the pointer after + // this call, so we copy it into an owned string above and return + // that string. + sys::AStatus_deleteDescription(description_ptr); + } + description + } + + /// Returns the exception code of the status. + pub fn exception_code(&self) -> ExceptionCode { + let code = unsafe { + // Safety: `Status` always contains a valid `AStatus` pointer, so we + // are always passing a valid pointer to `AStatus_getExceptionCode` + // here. + sys::AStatus_getExceptionCode(self.as_native()) + }; + parse_exception_code(code) + } + + /// Return a status code representing a transaction failure, or + /// `StatusCode::OK` if there was no transaction failure. + /// + /// If this method returns `OK`, the status may still represent a different + /// exception or a service specific error. To find out if this transaction + /// as a whole is okay, use [`is_ok`](Self::is_ok) instead. + pub fn transaction_error(&self) -> StatusCode { + let code = unsafe { + // Safety: `Status` always contains a valid `AStatus` pointer, so we + // are always passing a valid pointer to `AStatus_getStatus` here. + sys::AStatus_getStatus(self.as_native()) + }; + parse_status_code(code) + } + + /// Return a service specific error if this status represents one. + /// + /// This function will only ever return a non-zero result if + /// [`exception_code`](Self::exception_code) returns + /// `ExceptionCode::SERVICE_SPECIFIC`. If this function returns 0, the + /// status object may still represent a different exception or status. To + /// find out if this transaction as a whole is okay, use + /// [`is_ok`](Self::is_ok) instead. + pub fn service_specific_error(&self) -> i32 { + unsafe { + // Safety: `Status` always contains a valid `AStatus` pointer, so we + // are always passing a valid pointer to + // `AStatus_getServiceSpecificError` here. + sys::AStatus_getServiceSpecificError(self.as_native()) + } + } + + /// Calls `op` if the status was ok, otherwise returns an `Err` value of + /// `self`. + pub fn and_then<T, F>(self, op: F) -> result::Result<T, Status> + where + F: FnOnce() -> result::Result<T, Status>, + { + <result::Result<(), Status>>::from(self)?; + op() + } +} + +impl error::Error for Status {} + +impl Display for Status { + fn fmt(&self, f: &mut Formatter) -> FmtResult { + f.write_str(&self.get_description()) + } +} + +impl Debug for Status { + fn fmt(&self, f: &mut Formatter) -> FmtResult { + f.write_str(&self.get_description()) + } +} + +impl PartialEq for Status { + fn eq(&self, other: &Status) -> bool { + let self_code = self.exception_code(); + let other_code = other.exception_code(); + + match (self_code, other_code) { + (ExceptionCode::NONE, ExceptionCode::NONE) => true, + (ExceptionCode::TRANSACTION_FAILED, ExceptionCode::TRANSACTION_FAILED) => { + self.transaction_error() == other.transaction_error() + && self.get_description() == other.get_description() + } + (ExceptionCode::SERVICE_SPECIFIC, ExceptionCode::SERVICE_SPECIFIC) => { + self.service_specific_error() == other.service_specific_error() + && self.get_description() == other.get_description() + } + (e1, e2) => e1 == e2 && self.get_description() == other.get_description(), + } + } +} + +impl Eq for Status {} + +impl From<StatusCode> for Status { + fn from(status: StatusCode) -> Status { + (status as status_t).into() + } +} + +impl From<status_t> for Status { + fn from(status: status_t) -> Status { + let ptr = unsafe { + // Safety: `AStatus_fromStatus` expects any `status_t` integer, so + // this is a safe FFI call. Unknown values will be coerced into + // UNKNOWN_ERROR. + sys::AStatus_fromStatus(status) + }; + Self(ptr) + } +} + +impl From<ExceptionCode> for Status { + fn from(code: ExceptionCode) -> Status { + let ptr = unsafe { + // Safety: `AStatus_fromExceptionCode` expects any + // `binder_exception_t` (i32) integer, so this is a safe FFI call. + // Unknown values will be coerced into EX_TRANSACTION_FAILED. + sys::AStatus_fromExceptionCode(code as i32) + }; + Self(ptr) + } +} + +// TODO: impl Try for Status when try_trait is stabilized +// https://github.com/rust-lang/rust/issues/42327 +impl From<Status> for result::Result<(), Status> { + fn from(status: Status) -> result::Result<(), Status> { + if status.is_ok() { + Ok(()) + } else { + Err(status) + } + } +} + +impl From<Status> for status_t { + fn from(status: Status) -> status_t { + status.transaction_error() as status_t + } +} + +impl Drop for Status { + fn drop(&mut self) { + unsafe { + // Safety: `Status` manages the lifetime of its inner `AStatus` + // pointee, so we need to delete it here. We know that the pointer + // will be valid here since `Status` always contains a valid pointer + // while it is alive. + sys::AStatus_delete(self.0); + } + } +} + +/// # Safety +/// +/// `Status` always contains a valid pointer to an `AStatus` object, so we can +/// trivially convert it to a correctly-typed raw pointer. +/// +/// Care must be taken that the returned pointer is only dereferenced while the +/// `Status` object is still alive. +unsafe impl AsNative<sys::AStatus> for Status { + fn as_native(&self) -> *const sys::AStatus { + self.0 + } + + fn as_native_mut(&mut self) -> *mut sys::AStatus { + self.0 + } +} diff --git a/libs/binder/rust/src/lib.rs b/libs/binder/rust/src/lib.rs new file mode 100644 index 0000000000..8ee6a62180 --- /dev/null +++ b/libs/binder/rust/src/lib.rs @@ -0,0 +1,130 @@ +/* + * 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. + */ + +//! Safe Rust interface to Android `libbinder`. +//! +//! This crate is primarily designed as an target for a Rust AIDL compiler +//! backend, and should generally not be used directly by users. It is built on +//! top of the binder NDK library to be usable by APEX modules, and therefore +//! only exposes functionality available in the NDK interface. +//! +//! # Example +//! +//! The following example illustrates how the AIDL backend will use this crate. +//! +//! ``` +//! use binder::{ +//! declare_binder_interface, Binder, IBinder, Interface, Remotable, Parcel, SpIBinder, +//! StatusCode, TransactionCode, +//! }; +//! +//! // Generated by AIDL compiler +//! pub trait ITest: Interface { +//! fn test(&self) -> binder::Result<String>; +//! } +//! +//! // Creates a new local (native) service object, BnTest, and a remote proxy +//! // object, BpTest, that are the typed interfaces for their respective ends +//! // of the binder transaction. Generated by AIDL compiler. +//! declare_binder_interface! { +//! ITest["android.os.ITest"] { +//! native: BnTest(on_transact), +//! proxy: BpTest, +//! } +//! } +//! +//! // Generated by AIDL compiler +//! fn on_transact( +//! service: &dyn ITest, +//! code: TransactionCode, +//! _data: &Parcel, +//! reply: &mut Parcel, +//! ) -> binder::Result<()> { +//! match code { +//! SpIBinder::FIRST_CALL_TRANSACTION => { +//! reply.write(&service.test()?)?; +//! Ok(()) +//! } +//! _ => Err(StatusCode::UNKNOWN_TRANSACTION), +//! } +//! } +//! +//! // Generated by AIDL compiler +//! impl ITest for Binder<BnTest> { +//! fn test(&self) -> binder::Result<String> { +//! self.0.test() +//! } +//! } +//! +//! // Generated by AIDL compiler +//! impl ITest for BpTest { +//! fn test(&self) -> binder::Result<String> { +//! let reply = self +//! .as_binder() +//! .transact(SpIBinder::FIRST_CALL_TRANSACTION, 0, |_| Ok(()))?; +//! reply.read() +//! } +//! } +//! +//! // User implemented: +//! +//! // Local implementation of the ITest remotable interface. +//! struct TestService; +//! +//! impl Interface for TestService {} +//! +//! impl ITest for TestService { +//! fn test(&self) -> binder::Result<String> { +//! Ok("testing service".to_string()) +//! } +//! } +//! ``` + +#[macro_use] +mod proxy; + +#[macro_use] +mod binder; +mod error; +mod native; +mod state; + +use binder_ndk_sys as sys; + +pub mod parcel; + +pub use crate::binder::{ + FromIBinder, IBinder, Interface, InterfaceClass, Remotable, TransactionCode, TransactionFlags, +}; +pub use error::{status_t, ExceptionCode, Result, Status, StatusCode}; +pub use native::add_service; +pub use native::Binder; +pub use parcel::Parcel; +pub use proxy::{get_interface, get_service}; +pub use proxy::{AssociateClass, DeathRecipient, Proxy, SpIBinder, WpIBinder}; +pub use state::{ProcessState, ThreadState}; + +/// The public API usable outside AIDL-generated interface crates. +pub mod public_api { + pub use super::parcel::ParcelFileDescriptor; + pub use super::{add_service, get_interface}; + pub use super::{ + ExceptionCode, Interface, ProcessState, SpIBinder, Status, StatusCode, + }; + + /// Binder result containing a [`Status`] on error. + pub type Result<T> = std::result::Result<T, Status>; +} diff --git a/libs/binder/rust/src/native.rs b/libs/binder/rust/src/native.rs new file mode 100644 index 0000000000..185645ef4a --- /dev/null +++ b/libs/binder/rust/src/native.rs @@ -0,0 +1,370 @@ +/* + * 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. + */ + +use crate::binder::{AsNative, Interface, InterfaceClassMethods, Remotable, TransactionCode}; +use crate::error::{status_result, status_t, Result, StatusCode}; +use crate::parcel::{Parcel, Serialize}; +use crate::proxy::SpIBinder; +use crate::sys; + +use std::convert::TryFrom; +use std::ffi::{c_void, CString}; +use std::mem::ManuallyDrop; +use std::ops::Deref; +use std::ptr; + +/// Rust wrapper around Binder remotable objects. +/// +/// Implements the C++ `BBinder` class, and therefore implements the C++ +/// `IBinder` interface. +#[repr(C)] +pub struct Binder<T: Remotable> { + ibinder: *mut sys::AIBinder, + rust_object: *mut T, +} + +/// # Safety +/// +/// A `Binder<T>` is a pair of unique owning pointers to two values: +/// * a C++ ABBinder which the C++ API guarantees can be passed between threads +/// * a Rust object which implements `Remotable`; this trait requires `Send + Sync` +/// +/// Both pointers are unique (never escape the `Binder<T>` object and are not copied) +/// so we can essentially treat `Binder<T>` as a box-like containing the two objects; +/// the box-like object inherits `Send` from the two inner values, similarly +/// to how `Box<T>` is `Send` if `T` is `Send`. +unsafe impl<T: Remotable> Send for Binder<T> {} + +impl<T: Remotable> Binder<T> { + /// Create a new Binder remotable object. + /// + /// This moves the `rust_object` into an owned [`Box`] and Binder will + /// manage its lifetime. + pub fn new(rust_object: T) -> Binder<T> { + let class = T::get_class(); + let rust_object = Box::into_raw(Box::new(rust_object)); + let ibinder = unsafe { + // Safety: `AIBinder_new` expects a valid class pointer (which we + // initialize via `get_class`), and an arbitrary pointer + // argument. The caller owns the returned `AIBinder` pointer, which + // is a strong reference to a `BBinder`. This reference should be + // decremented via `AIBinder_decStrong` when the reference lifetime + // ends. + sys::AIBinder_new(class.into(), rust_object as *mut c_void) + }; + Binder { + ibinder, + rust_object, + } + } + + /// Set the extension of a binder interface. This allows a downstream + /// developer to add an extension to an interface without modifying its + /// interface file. This should be called immediately when the object is + /// created before it is passed to another thread. + /// + /// # Examples + /// + /// For instance, imagine if we have this Binder AIDL interface definition: + /// interface IFoo { void doFoo(); } + /// + /// If an unrelated owner (perhaps in a downstream codebase) wants to make a + /// change to the interface, they have two options: + /// + /// 1) Historical option that has proven to be BAD! Only the original + /// author of an interface should change an interface. If someone + /// downstream wants additional functionality, they should not ever + /// change the interface or use this method. + /// ```AIDL + /// BAD TO DO: interface IFoo { BAD TO DO + /// BAD TO DO: void doFoo(); BAD TO DO + /// BAD TO DO: + void doBar(); // adding a method BAD TO DO + /// BAD TO DO: } BAD TO DO + /// ``` + /// + /// 2) Option that this method enables! + /// Leave the original interface unchanged (do not change IFoo!). + /// Instead, create a new AIDL interface in a downstream package: + /// ```AIDL + /// package com.<name>; // new functionality in a new package + /// interface IBar { void doBar(); } + /// ``` + /// + /// When registering the interface, add: + /// + /// # use binder::{Binder, Interface}; + /// # type MyFoo = (); + /// # type MyBar = (); + /// # let my_foo = (); + /// # let my_bar = (); + /// let mut foo: Binder<MyFoo> = Binder::new(my_foo); // class in AOSP codebase + /// let bar: Binder<MyBar> = Binder::new(my_bar); // custom extension class + /// foo.set_extension(&mut bar.as_binder()); // use method in Binder + /// + /// Then, clients of `IFoo` can get this extension: + /// + /// # use binder::{declare_binder_interface, Binder, TransactionCode, Parcel}; + /// # trait IBar {} + /// # declare_binder_interface! { + /// # IBar["test"] { + /// # native: BnBar(on_transact), + /// # proxy: BpBar, + /// # } + /// # } + /// # fn on_transact( + /// # service: &dyn IBar, + /// # code: TransactionCode, + /// # data: &Parcel, + /// # reply: &mut Parcel, + /// # ) -> binder::Result<()> { + /// # Ok(()) + /// # } + /// # impl IBar for BpBar {} + /// # impl IBar for Binder<BnBar> {} + /// # fn main() -> binder::Result<()> { + /// # let binder = Binder::new(()); + /// if let Some(barBinder) = binder.get_extension()? { + /// let bar = BpBar::new(barBinder) + /// .expect("Extension was not of type IBar"); + /// } else { + /// // There was no extension + /// } + /// # } + pub fn set_extension(&mut self, extension: &mut SpIBinder) -> Result<()> { + let status = unsafe { + // Safety: `AIBinder_setExtension` expects two valid, mutable + // `AIBinder` pointers. We are guaranteed that both `self` and + // `extension` contain valid `AIBinder` pointers, because they + // cannot be initialized without a valid + // pointer. `AIBinder_setExtension` does not take ownership of + // either parameter. + sys::AIBinder_setExtension(self.as_native_mut(), extension.as_native_mut()) + }; + status_result(status) + } + + /// Retrieve the interface descriptor string for this object's Binder + /// interface. + pub fn get_descriptor() -> &'static str { + T::get_descriptor() + } +} + +impl<T: Remotable> Interface for Binder<T> { + /// Converts the local remotable object into a generic `SpIBinder` + /// reference. + /// + /// The resulting `SpIBinder` will hold its own strong reference to this + /// remotable object, which will prevent the object from being dropped while + /// the `SpIBinder` is alive. + fn as_binder(&self) -> SpIBinder { + unsafe { + // Safety: `self.ibinder` is guaranteed to always be a valid pointer + // to an `AIBinder` by the `Binder` constructor. We are creating a + // copy of the `self.ibinder` strong reference, but + // `SpIBinder::from_raw` assumes it receives an owned pointer with + // its own strong reference. We first increment the reference count, + // so that the new `SpIBinder` will be tracked as a new reference. + sys::AIBinder_incStrong(self.ibinder); + SpIBinder::from_raw(self.ibinder).unwrap() + } + } +} + +impl<T: Remotable> InterfaceClassMethods for Binder<T> { + fn get_descriptor() -> &'static str { + <T as Remotable>::get_descriptor() + } + + /// Called whenever a transaction needs to be processed by a local + /// implementation. + /// + /// # Safety + /// + /// Must be called with a non-null, valid pointer to a local `AIBinder` that + /// contains a `T` pointer in its user data. The `data` and `reply` parcel + /// parameters must be valid pointers to `AParcel` objects. This method does + /// not take ownership of any of its parameters. + /// + /// These conditions hold when invoked by `ABBinder::onTransact`. + unsafe extern "C" fn on_transact( + binder: *mut sys::AIBinder, + code: u32, + data: *const sys::AParcel, + reply: *mut sys::AParcel, + ) -> status_t { + let res = { + let mut reply = Parcel::borrowed(reply).unwrap(); + let data = Parcel::borrowed(data as *mut sys::AParcel).unwrap(); + let object = sys::AIBinder_getUserData(binder); + let binder: &T = &*(object as *const T); + binder.on_transact(code, &data, &mut reply) + }; + match res { + Ok(()) => 0i32, + Err(e) => e as i32, + } + } + + /// Called whenever an `AIBinder` object is no longer referenced and needs + /// destroyed. + /// + /// # Safety + /// + /// Must be called with a valid pointer to a `T` object. After this call, + /// the pointer will be invalid and should not be dereferenced. + unsafe extern "C" fn on_destroy(object: *mut c_void) { + ptr::drop_in_place(object as *mut T) + } + + /// Called whenever a new, local `AIBinder` object is needed of a specific + /// class. + /// + /// Constructs the user data pointer that will be stored in the object, + /// which will be a heap-allocated `T` object. + /// + /// # Safety + /// + /// Must be called with a valid pointer to a `T` object allocated via `Box`. + unsafe extern "C" fn on_create(args: *mut c_void) -> *mut c_void { + // We just return the argument, as it is already a pointer to the rust + // object created by Box. + args + } +} + +impl<T: Remotable> Drop for Binder<T> { + // This causes C++ to decrease the strong ref count of the `AIBinder` + // object. We specifically do not drop the `rust_object` here. When C++ + // actually destroys the object, it calls `on_destroy` and we can drop the + // `rust_object` then. + fn drop(&mut self) { + unsafe { + // Safety: When `self` is dropped, we can no longer access the + // reference, so can decrement the reference count. `self.ibinder` + // is always a valid `AIBinder` pointer, so is valid to pass to + // `AIBinder_decStrong`. + sys::AIBinder_decStrong(self.ibinder); + } + } +} + +impl<T: Remotable> Deref for Binder<T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + unsafe { + // Safety: While `self` is alive, the reference count of the + // underlying object is > 0 and therefore `on_destroy` cannot be + // called. Therefore while `self` is alive, we know that + // `rust_object` is still a valid pointer to a heap allocated object + // of type `T`. + &*self.rust_object + } + } +} + +impl<B: Remotable> Serialize for Binder<B> { + fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + parcel.write_binder(Some(&self.as_binder())) + } +} + +// This implementation is an idiomatic implementation of the C++ +// `IBinder::localBinder` interface if the binder object is a Rust binder +// service. +impl<B: Remotable> TryFrom<SpIBinder> for Binder<B> { + type Error = StatusCode; + + fn try_from(mut ibinder: SpIBinder) -> Result<Self> { + let class = B::get_class(); + if Some(class) != ibinder.get_class() { + return Err(StatusCode::BAD_TYPE); + } + let userdata = unsafe { + // Safety: `SpIBinder` always holds a valid pointer pointer to an + // `AIBinder`, which we can safely pass to + // `AIBinder_getUserData`. `ibinder` retains ownership of the + // returned pointer. + sys::AIBinder_getUserData(ibinder.as_native_mut()) + }; + if userdata.is_null() { + return Err(StatusCode::UNEXPECTED_NULL); + } + // We are transferring the ownership of the AIBinder into the new Binder + // object. + let mut ibinder = ManuallyDrop::new(ibinder); + Ok(Binder { + ibinder: ibinder.as_native_mut(), + rust_object: userdata as *mut B, + }) + } +} + +/// # Safety +/// +/// The constructor for `Binder` guarantees that `self.ibinder` will contain a +/// valid, non-null pointer to an `AIBinder`, so this implementation is type +/// safe. `self.ibinder` will remain valid for the entire lifetime of `self` +/// because we hold a strong reference to the `AIBinder` until `self` is +/// dropped. +unsafe impl<B: Remotable> AsNative<sys::AIBinder> for Binder<B> { + fn as_native(&self) -> *const sys::AIBinder { + self.ibinder + } + + fn as_native_mut(&mut self) -> *mut sys::AIBinder { + self.ibinder + } +} + +/// Register a new service with the default service manager. +/// +/// Registers the given binder object with the given identifier. If successful, +/// this service can then be retrieved using that identifier. +pub fn add_service(identifier: &str, mut binder: SpIBinder) -> Result<()> { + let instance = CString::new(identifier).unwrap(); + let status = unsafe { + // Safety: `AServiceManager_addService` expects valid `AIBinder` and C + // string pointers. Caller retains ownership of both + // pointers. `AServiceManager_addService` creates a new strong reference + // and copies the string, so both pointers need only be valid until the + // call returns. + sys::AServiceManager_addService(binder.as_native_mut(), instance.as_ptr()) + }; + status_result(status) +} + +/// Tests often create a base BBinder instance; so allowing the unit +/// type to be remotable translates nicely to Binder::new(()). +impl Remotable for () { + fn get_descriptor() -> &'static str { + "" + } + + fn on_transact( + &self, + _code: TransactionCode, + _data: &Parcel, + _reply: &mut Parcel, + ) -> Result<()> { + Ok(()) + } + + binder_fn_get_class!(Binder::<Self>); +} + +impl Interface for () {} diff --git a/libs/binder/rust/src/parcel.rs b/libs/binder/rust/src/parcel.rs new file mode 100644 index 0000000000..a248f5c510 --- /dev/null +++ b/libs/binder/rust/src/parcel.rs @@ -0,0 +1,542 @@ +/* + * 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. + */ + +//! Container for messages that are sent via binder. + +use crate::binder::AsNative; +use crate::error::{status_result, Result, StatusCode}; +use crate::proxy::SpIBinder; +use crate::sys; + +use std::cell::RefCell; +use std::convert::TryInto; +use std::mem::ManuallyDrop; +use std::ptr; + +mod file_descriptor; +mod parcelable; + +pub use self::file_descriptor::ParcelFileDescriptor; +pub use self::parcelable::{ + Deserialize, DeserializeArray, DeserializeOption, Serialize, SerializeArray, SerializeOption, +}; + +/// Container for a message (data and object references) that can be sent +/// through Binder. +/// +/// A Parcel can contain both serialized data that will be deserialized on the +/// other side of the IPC, and references to live Binder objects that will +/// result in the other side receiving a proxy Binder connected with the +/// original Binder in the Parcel. +pub enum Parcel { + /// Owned parcel pointer + Owned(*mut sys::AParcel), + /// Borrowed parcel pointer (will not be destroyed on drop) + Borrowed(*mut sys::AParcel), +} + +/// # Safety +/// +/// The `Parcel` constructors guarantee that a `Parcel` object will always +/// contain a valid pointer to an `AParcel`. +unsafe impl AsNative<sys::AParcel> for Parcel { + fn as_native(&self) -> *const sys::AParcel { + match *self { + Self::Owned(x) | Self::Borrowed(x) => x, + } + } + + fn as_native_mut(&mut self) -> *mut sys::AParcel { + match *self { + Self::Owned(x) | Self::Borrowed(x) => x, + } + } +} + +impl Parcel { + /// Create a borrowed reference to a parcel object from a raw pointer. + /// + /// # Safety + /// + /// This constructor is safe if the raw pointer parameter is either null + /// (resulting in `None`), or a valid pointer to an `AParcel` object. + pub(crate) unsafe fn borrowed(ptr: *mut sys::AParcel) -> Option<Parcel> { + ptr.as_mut().map(|ptr| Self::Borrowed(ptr)) + } + + /// Create an owned reference to a parcel object from a raw pointer. + /// + /// # Safety + /// + /// This constructor is safe if the raw pointer parameter is either null + /// (resulting in `None`), or a valid pointer to an `AParcel` object. The + /// parcel object must be owned by the caller prior to this call, as this + /// constructor takes ownership of the parcel and will destroy it on drop. + pub(crate) unsafe fn owned(ptr: *mut sys::AParcel) -> Option<Parcel> { + ptr.as_mut().map(|ptr| Self::Owned(ptr)) + } + + /// Consume the parcel, transferring ownership to the caller if the parcel + /// was owned. + pub(crate) fn into_raw(mut self) -> *mut sys::AParcel { + let ptr = self.as_native_mut(); + let _ = ManuallyDrop::new(self); + ptr + } +} + +// Data serialization methods +impl Parcel { + /// Write a type that implements [`Serialize`] to the `Parcel`. + pub fn write<S: Serialize + ?Sized>(&mut self, parcelable: &S) -> Result<()> { + parcelable.serialize(self) + } + + /// Writes the length of a slice to the `Parcel`. + /// + /// This is used in AIDL-generated client side code to indicate the + /// allocated space for an output array parameter. + pub fn write_slice_size<T>(&mut self, slice: Option<&[T]>) -> Result<()> { + if let Some(slice) = slice { + let len: i32 = slice.len().try_into().or(Err(StatusCode::BAD_VALUE))?; + self.write(&len) + } else { + self.write(&-1i32) + } + } + + /// Perform a series of writes to the `Parcel`, prepended with the length + /// (in bytes) of the written data. + /// + /// The length `0i32` will be written to the parcel first, followed by the + /// writes performed by the callback. The initial length will then be + /// updated to the length of all data written by the callback, plus the + /// size of the length elemement itself (4 bytes). + /// + /// # Examples + /// + /// After the following call: + /// + /// ``` + /// # use binder::{Binder, Interface, Parcel}; + /// # let mut parcel = Parcel::Owned(std::ptr::null_mut()); + /// parcel.sized_write(|subparcel| { + /// subparcel.write(&1u32)?; + /// subparcel.write(&2u32)?; + /// subparcel.write(&3u32) + /// }); + /// ``` + /// + /// `parcel` will contain the following: + /// + /// ```ignore + /// [16i32, 1u32, 2u32, 3u32] + /// ``` + pub fn sized_write<F>(&mut self, f: F) -> Result<()> + where for<'a> + F: Fn(&'a WritableSubParcel<'a>) -> Result<()> + { + let start = self.get_data_position(); + self.write(&0i32)?; + { + let subparcel = WritableSubParcel(RefCell::new(self)); + f(&subparcel)?; + } + let end = self.get_data_position(); + unsafe { + self.set_data_position(start)?; + } + assert!(end >= start); + self.write(&(end - start))?; + unsafe { + self.set_data_position(end)?; + } + Ok(()) + } + + /// Returns the current position in the parcel data. + pub fn get_data_position(&self) -> i32 { + unsafe { + // Safety: `Parcel` always contains a valid pointer to an `AParcel`, + // and this call is otherwise safe. + sys::AParcel_getDataPosition(self.as_native()) + } + } + + /// Move the current read/write position in the parcel. + /// + /// The new position must be a position previously returned by + /// `self.get_data_position()`. + /// + /// # Safety + /// + /// This method is safe if `pos` is less than the current size of the parcel + /// data buffer. Otherwise, we are relying on correct bounds checking in the + /// Parcel C++ code on every subsequent read or write to this parcel. If all + /// accesses are bounds checked, this call is still safe, but we can't rely + /// on that. + pub unsafe fn set_data_position(&self, pos: i32) -> Result<()> { + status_result(sys::AParcel_setDataPosition(self.as_native(), pos)) + } +} + +/// A segment of a writable parcel, used for [`Parcel::sized_write`]. +pub struct WritableSubParcel<'a>(RefCell<&'a mut Parcel>); + +impl<'a> WritableSubParcel<'a> { + /// Write a type that implements [`Serialize`] to the sub-parcel. + pub fn write<S: Serialize + ?Sized>(&self, parcelable: &S) -> Result<()> { + parcelable.serialize(&mut *self.0.borrow_mut()) + } +} + +// Data deserialization methods +impl Parcel { + /// Attempt to read a type that implements [`Deserialize`] from this + /// `Parcel`. + pub fn read<D: Deserialize>(&self) -> Result<D> { + D::deserialize(self) + } + + /// Read a vector size from the `Parcel` and resize the given output vector + /// to be correctly sized for that amount of data. + /// + /// This method is used in AIDL-generated server side code for methods that + /// take a mutable slice reference parameter. + pub fn resize_out_vec<D: Default + Deserialize>(&self, out_vec: &mut Vec<D>) -> Result<()> { + let len: i32 = self.read()?; + + if len < 0 { + return Err(StatusCode::UNEXPECTED_NULL); + } + + // usize in Rust may be 16-bit, so i32 may not fit + let len = len.try_into().unwrap(); + out_vec.resize_with(len, Default::default); + + Ok(()) + } + + /// Read a vector size from the `Parcel` and either create a correctly sized + /// vector for that amount of data or set the output parameter to None if + /// the vector should be null. + /// + /// This method is used in AIDL-generated server side code for methods that + /// take a mutable slice reference parameter. + pub fn resize_nullable_out_vec<D: Default + Deserialize>( + &self, + out_vec: &mut Option<Vec<D>>, + ) -> Result<()> { + let len: i32 = self.read()?; + + if len < 0 { + *out_vec = None; + } else { + // usize in Rust may be 16-bit, so i32 may not fit + let len = len.try_into().unwrap(); + let mut vec = Vec::with_capacity(len); + vec.resize_with(len, Default::default); + *out_vec = Some(vec); + } + + Ok(()) + } +} + +// Internal APIs +impl Parcel { + pub(crate) fn write_binder(&mut self, binder: Option<&SpIBinder>) -> Result<()> { + unsafe { + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. `AsNative` for `Option<SpIBinder`> will either return + // null or a valid pointer to an `AIBinder`, both of which are + // valid, safe inputs to `AParcel_writeStrongBinder`. + // + // This call does not take ownership of the binder. However, it does + // require a mutable pointer, which we cannot extract from an + // immutable reference, so we clone the binder, incrementing the + // refcount before the call. The refcount will be immediately + // decremented when this temporary is dropped. + status_result(sys::AParcel_writeStrongBinder( + self.as_native_mut(), + binder.cloned().as_native_mut(), + )) + } + } + + pub(crate) fn read_binder(&self) -> Result<Option<SpIBinder>> { + let mut binder = ptr::null_mut(); + let status = unsafe { + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. We pass a valid, mutable out pointer to the `binder` + // parameter. After this call, `binder` will be either null or a + // valid pointer to an `AIBinder` owned by the caller. + sys::AParcel_readStrongBinder(self.as_native(), &mut binder) + }; + + status_result(status)?; + + Ok(unsafe { + // Safety: `binder` is either null or a valid, owned pointer at this + // point, so can be safely passed to `SpIBinder::from_raw`. + SpIBinder::from_raw(binder) + }) + } +} + +impl Drop for Parcel { + fn drop(&mut self) { + // Run the C++ Parcel complete object destructor + if let Self::Owned(ptr) = *self { + unsafe { + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. If we own the parcel, we can safely delete it + // here. + sys::AParcel_delete(ptr) + } + } + } +} + +#[cfg(test)] +impl Parcel { + /// Create a new parcel tied to a bogus binder. TESTING ONLY! + /// + /// This can only be used for testing! All real parcel operations must be + /// done in the callback to [`IBinder::transact`] or in + /// [`Remotable::on_transact`] using the parcels provided to these methods. + pub(crate) fn new_for_test(binder: &mut SpIBinder) -> Result<Self> { + let mut input = ptr::null_mut(); + let status = unsafe { + // Safety: `SpIBinder` guarantees that `binder` always contains a + // valid pointer to an `AIBinder`. We pass a valid, mutable out + // pointer to receive a newly constructed parcel. When successful + // this function assigns a new pointer to an `AParcel` to `input` + // and transfers ownership of this pointer to the caller. Thus, + // after this call, `input` will either be null or point to a valid, + // owned `AParcel`. + sys::AIBinder_prepareTransaction(binder.as_native_mut(), &mut input) + }; + status_result(status)?; + unsafe { + // Safety: `input` is either null or a valid, owned pointer to an + // `AParcel`, so is valid to safe to + // `Parcel::owned`. `Parcel::owned` takes ownership of the parcel + // pointer. + Parcel::owned(input).ok_or(StatusCode::UNEXPECTED_NULL) + } + } +} + +#[test] +fn test_read_write() { + use crate::binder::Interface; + use crate::native::Binder; + use std::ffi::CString; + + let mut service = Binder::new(()).as_binder(); + let mut parcel = Parcel::new_for_test(&mut service).unwrap(); + let start = parcel.get_data_position(); + + assert_eq!(parcel.read::<bool>(), Err(StatusCode::NOT_ENOUGH_DATA)); + assert_eq!(parcel.read::<i8>(), Err(StatusCode::NOT_ENOUGH_DATA)); + assert_eq!(parcel.read::<u16>(), Err(StatusCode::NOT_ENOUGH_DATA)); + assert_eq!(parcel.read::<i32>(), Err(StatusCode::NOT_ENOUGH_DATA)); + assert_eq!(parcel.read::<u32>(), Err(StatusCode::NOT_ENOUGH_DATA)); + assert_eq!(parcel.read::<i64>(), Err(StatusCode::NOT_ENOUGH_DATA)); + assert_eq!(parcel.read::<u64>(), Err(StatusCode::NOT_ENOUGH_DATA)); + assert_eq!(parcel.read::<f32>(), Err(StatusCode::NOT_ENOUGH_DATA)); + assert_eq!(parcel.read::<f64>(), Err(StatusCode::NOT_ENOUGH_DATA)); + assert_eq!(parcel.read::<Option<CString>>(), Ok(None)); + assert_eq!(parcel.read::<String>(), Err(StatusCode::UNEXPECTED_NULL)); + + assert_eq!(parcel.read_binder().err(), Some(StatusCode::BAD_TYPE)); + + parcel.write(&1i32).unwrap(); + + unsafe { + parcel.set_data_position(start).unwrap(); + } + + let i: i32 = parcel.read().unwrap(); + assert_eq!(i, 1i32); +} + +#[test] +#[allow(clippy::float_cmp)] +fn test_read_data() { + use crate::binder::Interface; + use crate::native::Binder; + + let mut service = Binder::new(()).as_binder(); + let mut parcel = Parcel::new_for_test(&mut service).unwrap(); + let str_start = parcel.get_data_position(); + + parcel.write(&b"Hello, Binder!\0"[..]).unwrap(); + // Skip over string length + unsafe { + assert!(parcel.set_data_position(str_start).is_ok()); + } + assert_eq!(parcel.read::<i32>().unwrap(), 15); + let start = parcel.get_data_position(); + + assert_eq!(parcel.read::<bool>().unwrap(), true); + + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + assert_eq!(parcel.read::<i8>().unwrap(), 72i8); + + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + assert_eq!(parcel.read::<u16>().unwrap(), 25928); + + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + assert_eq!(parcel.read::<i32>().unwrap(), 1819043144); + + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + assert_eq!(parcel.read::<u32>().unwrap(), 1819043144); + + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + assert_eq!(parcel.read::<i64>().unwrap(), 4764857262830019912); + + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + assert_eq!(parcel.read::<u64>().unwrap(), 4764857262830019912); + + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + assert_eq!( + parcel.read::<f32>().unwrap(), + 1143139100000000000000000000.0 + ); + assert_eq!(parcel.read::<f32>().unwrap(), 40.043392); + + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + assert_eq!(parcel.read::<f64>().unwrap(), 34732488246.197815); + + // Skip back to before the string length + unsafe { + assert!(parcel.set_data_position(str_start).is_ok()); + } + + assert_eq!(parcel.read::<Vec<u8>>().unwrap(), b"Hello, Binder!\0"); +} + +#[test] +fn test_utf8_utf16_conversions() { + use crate::binder::Interface; + use crate::native::Binder; + + let mut service = Binder::new(()).as_binder(); + let mut parcel = Parcel::new_for_test(&mut service).unwrap(); + let start = parcel.get_data_position(); + + assert!(parcel.write("Hello, Binder!").is_ok()); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + assert_eq!( + parcel.read::<Option<String>>().unwrap().unwrap(), + "Hello, Binder!" + ); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + assert!(parcel.write(&["str1", "str2", "str3"][..]).is_ok()); + assert!(parcel + .write( + &[ + String::from("str4"), + String::from("str5"), + String::from("str6"), + ][..] + ) + .is_ok()); + + let s1 = "Hello, Binder!"; + let s2 = "This is a utf8 string."; + let s3 = "Some more text here."; + + assert!(parcel.write(&[s1, s2, s3][..]).is_ok()); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + assert_eq!( + parcel.read::<Vec<String>>().unwrap(), + ["str1", "str2", "str3"] + ); + assert_eq!( + parcel.read::<Vec<String>>().unwrap(), + ["str4", "str5", "str6"] + ); + assert_eq!(parcel.read::<Vec<String>>().unwrap(), [s1, s2, s3]); +} + +#[test] +fn test_sized_write() { + use crate::binder::Interface; + use crate::native::Binder; + + let mut service = Binder::new(()).as_binder(); + let mut parcel = Parcel::new_for_test(&mut service).unwrap(); + let start = parcel.get_data_position(); + + let arr = [1i32, 2i32, 3i32]; + + parcel.sized_write(|subparcel| { + subparcel.write(&arr[..]) + }).expect("Could not perform sized write"); + + // i32 sub-parcel length + i32 array length + 3 i32 elements + let expected_len = 20i32; + + assert_eq!(parcel.get_data_position(), start + expected_len); + + unsafe { + parcel.set_data_position(start).unwrap(); + } + + assert_eq!( + expected_len, + parcel.read().unwrap(), + ); + + assert_eq!( + parcel.read::<Vec<i32>>().unwrap(), + &arr, + ); +} diff --git a/libs/binder/rust/src/parcel/file_descriptor.rs b/libs/binder/rust/src/parcel/file_descriptor.rs new file mode 100644 index 0000000000..8a89ab09e0 --- /dev/null +++ b/libs/binder/rust/src/parcel/file_descriptor.rs @@ -0,0 +1,126 @@ +/* + * 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. + */ + +use super::{ + Deserialize, DeserializeArray, DeserializeOption, Parcel, Serialize, SerializeArray, + SerializeOption, +}; +use crate::binder::AsNative; +use crate::error::{status_result, Result, StatusCode}; +use crate::sys; + +use std::fs::File; +use std::os::unix::io::{AsRawFd, FromRawFd}; + +/// Rust version of the Java class android.os.ParcelFileDescriptor +pub struct ParcelFileDescriptor(File); + +impl ParcelFileDescriptor { + /// Create a new `ParcelFileDescriptor` + pub fn new(file: File) -> Self { + Self(file) + } +} + +impl AsRef<File> for ParcelFileDescriptor { + fn as_ref(&self) -> &File { + &self.0 + } +} + +impl From<ParcelFileDescriptor> for File { + fn from(file: ParcelFileDescriptor) -> File { + file.0 + } +} + +impl Serialize for ParcelFileDescriptor { + fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + let fd = self.0.as_raw_fd(); + let status = unsafe { + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. Likewise, `ParcelFileDescriptor` always contains a + // valid file, so we can borrow a valid file + // descriptor. `AParcel_writeParcelFileDescriptor` does NOT take + // ownership of the fd, so we need not duplicate it first. + sys::AParcel_writeParcelFileDescriptor(parcel.as_native_mut(), fd) + }; + status_result(status) + } +} + +impl SerializeArray for ParcelFileDescriptor {} + +impl SerializeOption for ParcelFileDescriptor { + fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + if let Some(f) = this { + f.serialize(parcel) + } else { + let status = unsafe { + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. `AParcel_writeParcelFileDescriptor` accepts the + // value `-1` as the file descriptor to signify serializing a + // null file descriptor. + sys::AParcel_writeParcelFileDescriptor(parcel.as_native_mut(), -1i32) + }; + status_result(status) + } + } +} + +impl SerializeArray for Option<ParcelFileDescriptor> {} + +impl DeserializeOption for ParcelFileDescriptor { + fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> { + let mut fd = -1i32; + unsafe { + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. We pass a valid mutable pointer to an i32, which + // `AParcel_readParcelFileDescriptor` assigns the valid file + // descriptor into, or `-1` if deserializing a null file + // descriptor. The read function passes ownership of the file + // descriptor to its caller if it was non-null, so we must take + // ownership of the file and ensure that it is eventually closed. + status_result(sys::AParcel_readParcelFileDescriptor( + parcel.as_native(), + &mut fd, + ))?; + } + if fd < 0 { + Ok(None) + } else { + let file = unsafe { + // Safety: At this point, we know that the file descriptor was + // not -1, so must be a valid, owned file descriptor which we + // can safely turn into a `File`. + File::from_raw_fd(fd) + }; + Ok(Some(ParcelFileDescriptor::new(file))) + } + } +} + +impl DeserializeArray for Option<ParcelFileDescriptor> {} + +impl Deserialize for ParcelFileDescriptor { + fn deserialize(parcel: &Parcel) -> Result<Self> { + Deserialize::deserialize(parcel) + .transpose() + .unwrap_or(Err(StatusCode::UNEXPECTED_NULL)) + } +} + +impl DeserializeArray for ParcelFileDescriptor {} diff --git a/libs/binder/rust/src/parcel/parcelable.rs b/libs/binder/rust/src/parcel/parcelable.rs new file mode 100644 index 0000000000..78b3d2cfbe --- /dev/null +++ b/libs/binder/rust/src/parcel/parcelable.rs @@ -0,0 +1,922 @@ +/* + * 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. + */ + +use crate::binder::{AsNative, FromIBinder}; +use crate::error::{status_result, Result, Status, StatusCode}; +use crate::parcel::Parcel; +use crate::proxy::SpIBinder; +use crate::sys; + +use std::convert::TryInto; +use std::ffi::{c_void, CStr, CString}; +use std::ptr; + +/// A struct whose instances can be written to a [`Parcel`]. +// Might be able to hook this up as a serde backend in the future? +pub trait Serialize { + /// Serialize this instance into the given [`Parcel`]. + fn serialize(&self, parcel: &mut Parcel) -> Result<()>; +} + +/// A struct whose instances can be restored from a [`Parcel`]. +// Might be able to hook this up as a serde backend in the future? +pub trait Deserialize: Sized { + /// Deserialize an instance from the given [`Parcel`]. + fn deserialize(parcel: &Parcel) -> Result<Self>; +} + +/// Helper trait for types that can be serialized as arrays. +/// Defaults to calling Serialize::serialize() manually for every element, +/// but can be overridden for custom implementations like `writeByteArray`. +// Until specialization is stabilized in Rust, we need this to be a separate +// trait because it's the only way to have a default implementation for a method. +// We want the default implementation for most types, but an override for +// a few special ones like `readByteArray` for `u8`. +pub trait SerializeArray: Serialize + Sized { + /// Serialize an array of this type into the given [`Parcel`]. + fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> { + parcel.write_slice_size(Some(slice))?; + + for item in slice { + parcel.write(item)?; + } + + Ok(()) + } +} + +/// Helper trait for types that can be deserialized as arrays. +/// Defaults to calling Deserialize::deserialize() manually for every element, +/// but can be overridden for custom implementations like `readByteArray`. +pub trait DeserializeArray: Deserialize { + /// Deserialize an array of type from the given [`Parcel`]. + fn deserialize_array(parcel: &Parcel) -> Result<Option<Vec<Self>>> { + let len: i32 = parcel.read()?; + if len < 0 { + return Ok(None); + } + + // TODO: Assumes that usize is at least 32 bits + let mut vec = Vec::with_capacity(len as usize); + + for _ in 0..len { + vec.push(parcel.read()?); + } + + Ok(Some(vec)) + } +} + +/// Helper trait for types that can be nullable when serialized. +// We really need this trait instead of implementing `Serialize for Option<T>` +// because of the Rust orphan rule which prevents us from doing +// `impl Serialize for Option<&dyn IFoo>` for AIDL interfaces. +// Instead we emit `impl SerializeOption for dyn IFoo` which is allowed. +// We also use it to provide a default implementation for AIDL-generated +// parcelables. +pub trait SerializeOption: Serialize { + /// Serialize an Option of this type into the given [`Parcel`]. + fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + if let Some(inner) = this { + parcel.write(&1i32)?; + parcel.write(inner) + } else { + parcel.write(&0i32) + } + } +} + +/// Helper trait for types that can be nullable when deserialized. +pub trait DeserializeOption: Deserialize { + /// Deserialize an Option of this type from the given [`Parcel`]. + fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> { + let null: i32 = parcel.read()?; + if null == 0 { + Ok(None) + } else { + parcel.read().map(Some) + } + } +} + +/// Callback to allocate a vector for parcel array read functions. +/// +/// # Safety +/// +/// The opaque data pointer passed to the array read function must be a mutable +/// pointer to an `Option<Vec<T>>`. `buffer` will be assigned a mutable pointer +/// to the allocated vector data if this function returns true. +unsafe extern "C" fn allocate_vec<T: Clone + Default>( + data: *mut c_void, + len: i32, + buffer: *mut *mut T, +) -> bool { + let vec = &mut *(data as *mut Option<Vec<T>>); + if len < 0 { + *vec = None; + return true; + } + let mut new_vec: Vec<T> = Vec::with_capacity(len as usize); + new_vec.resize_with(len as usize, Default::default); + *buffer = new_vec.as_mut_ptr(); + *vec = Some(new_vec); + true +} + +macro_rules! parcelable_primitives { + { + $( + impl $trait:ident for $ty:ty = $fn:path; + )* + } => { + $(impl_parcelable!{$trait, $ty, $fn})* + }; +} + +macro_rules! impl_parcelable { + {Serialize, $ty:ty, $write_fn:path} => { + impl Serialize for $ty { + fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + unsafe { + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`, and any `$ty` literal value is safe to pass to + // `$write_fn`. + status_result($write_fn(parcel.as_native_mut(), *self)) + } + } + } + }; + + {Deserialize, $ty:ty, $read_fn:path} => { + impl Deserialize for $ty { + fn deserialize(parcel: &Parcel) -> Result<Self> { + let mut val = Self::default(); + unsafe { + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. We pass a valid, mutable pointer to `val`, a + // literal of type `$ty`, and `$read_fn` will write the + // value read into `val` if successful + status_result($read_fn(parcel.as_native(), &mut val))? + }; + Ok(val) + } + } + }; + + {SerializeArray, $ty:ty, $write_array_fn:path} => { + impl SerializeArray for $ty { + fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> { + let status = unsafe { + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` + // will be a valid pointer to an array of elements of type + // `$ty`. If the slice length is 0, `slice.as_ptr()` may be + // dangling, but this is safe since the pointer is not + // dereferenced if the length parameter is 0. + $write_array_fn( + parcel.as_native_mut(), + slice.as_ptr(), + slice + .len() + .try_into() + .or(Err(StatusCode::BAD_VALUE))?, + ) + }; + status_result(status) + } + } + }; + + {DeserializeArray, $ty:ty, $read_array_fn:path} => { + impl DeserializeArray for $ty { + fn deserialize_array(parcel: &Parcel) -> Result<Option<Vec<Self>>> { + let mut vec: Option<Vec<Self>> = None; + let status = unsafe { + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. `allocate_vec<T>` expects the opaque pointer to + // be of type `*mut Option<Vec<T>>`, so `&mut vec` is + // correct for it. + $read_array_fn( + parcel.as_native(), + &mut vec as *mut _ as *mut c_void, + Some(allocate_vec), + ) + }; + status_result(status)?; + Ok(vec) + } + } + }; +} + +parcelable_primitives! { + impl Serialize for bool = sys::AParcel_writeBool; + impl Deserialize for bool = sys::AParcel_readBool; + + // This is only safe because `Option<Vec<u8>>` is interchangeable with + // `Option<Vec<i8>>` (what the allocator function actually allocates. + impl DeserializeArray for u8 = sys::AParcel_readByteArray; + + impl Serialize for i8 = sys::AParcel_writeByte; + impl Deserialize for i8 = sys::AParcel_readByte; + impl SerializeArray for i8 = sys::AParcel_writeByteArray; + impl DeserializeArray for i8 = sys::AParcel_readByteArray; + + impl Serialize for u16 = sys::AParcel_writeChar; + impl Deserialize for u16 = sys::AParcel_readChar; + impl SerializeArray for u16 = sys::AParcel_writeCharArray; + impl DeserializeArray for u16 = sys::AParcel_readCharArray; + + // This is only safe because `Option<Vec<i16>>` is interchangeable with + // `Option<Vec<u16>>` (what the allocator function actually allocates. + impl DeserializeArray for i16 = sys::AParcel_readCharArray; + + impl Serialize for u32 = sys::AParcel_writeUint32; + impl Deserialize for u32 = sys::AParcel_readUint32; + impl SerializeArray for u32 = sys::AParcel_writeUint32Array; + impl DeserializeArray for u32 = sys::AParcel_readUint32Array; + + impl Serialize for i32 = sys::AParcel_writeInt32; + impl Deserialize for i32 = sys::AParcel_readInt32; + impl SerializeArray for i32 = sys::AParcel_writeInt32Array; + impl DeserializeArray for i32 = sys::AParcel_readInt32Array; + + impl Serialize for u64 = sys::AParcel_writeUint64; + impl Deserialize for u64 = sys::AParcel_readUint64; + impl SerializeArray for u64 = sys::AParcel_writeUint64Array; + impl DeserializeArray for u64 = sys::AParcel_readUint64Array; + + impl Serialize for i64 = sys::AParcel_writeInt64; + impl Deserialize for i64 = sys::AParcel_readInt64; + impl SerializeArray for i64 = sys::AParcel_writeInt64Array; + impl DeserializeArray for i64 = sys::AParcel_readInt64Array; + + impl Serialize for f32 = sys::AParcel_writeFloat; + impl Deserialize for f32 = sys::AParcel_readFloat; + impl SerializeArray for f32 = sys::AParcel_writeFloatArray; + impl DeserializeArray for f32 = sys::AParcel_readFloatArray; + + impl Serialize for f64 = sys::AParcel_writeDouble; + impl Deserialize for f64 = sys::AParcel_readDouble; + impl SerializeArray for f64 = sys::AParcel_writeDoubleArray; + impl DeserializeArray for f64 = sys::AParcel_readDoubleArray; +} + +impl SerializeArray for bool {} +impl DeserializeArray for bool {} + +impl Serialize for u8 { + fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + (*self as i8).serialize(parcel) + } +} + +impl Deserialize for u8 { + fn deserialize(parcel: &Parcel) -> Result<Self> { + i8::deserialize(parcel).map(|v| v as u8) + } +} + +impl SerializeArray for u8 { + fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> { + let status = unsafe { + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a + // valid pointer to an array of elements of type `$ty`. If the slice + // length is 0, `slice.as_ptr()` may be dangling, but this is safe + // since the pointer is not dereferenced if the length parameter is + // 0. + sys::AParcel_writeByteArray( + parcel.as_native_mut(), + slice.as_ptr() as *const i8, + slice.len().try_into().or(Err(StatusCode::BAD_VALUE))?, + ) + }; + status_result(status) + } +} + +impl Serialize for i16 { + fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + (*self as u16).serialize(parcel) + } +} + +impl Deserialize for i16 { + fn deserialize(parcel: &Parcel) -> Result<Self> { + u16::deserialize(parcel).map(|v| v as i16) + } +} + +impl SerializeArray for i16 { + fn serialize_array(slice: &[Self], parcel: &mut Parcel) -> Result<()> { + let status = unsafe { + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. If the slice is > 0 length, `slice.as_ptr()` will be a + // valid pointer to an array of elements of type `$ty`. If the slice + // length is 0, `slice.as_ptr()` may be dangling, but this is safe + // since the pointer is not dereferenced if the length parameter is + // 0. + sys::AParcel_writeCharArray( + parcel.as_native_mut(), + slice.as_ptr() as *const u16, + slice.len().try_into().or(Err(StatusCode::BAD_VALUE))?, + ) + }; + status_result(status) + } +} + +impl SerializeOption for CStr { + fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + match this { + None => unsafe { + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. If the string pointer is null, + // `AParcel_writeString` requires that the length is -1 to + // indicate that we want to serialize a null string. + status_result(sys::AParcel_writeString( + parcel.as_native_mut(), + ptr::null(), + -1, + )) + }, + Some(s) => unsafe { + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. `AParcel_writeString` assumes that we pass a + // null-terminated C string pointer with no nulls in the middle + // of the string. Rust guarantees exactly that for a valid CStr + // instance. + status_result(sys::AParcel_writeString( + parcel.as_native_mut(), + s.as_ptr(), + s.to_bytes() + .len() + .try_into() + .or(Err(StatusCode::BAD_VALUE))?, + )) + }, + } + } +} + +impl SerializeArray for Option<&CStr> {} + +impl Serialize for CStr { + fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + Some(self).serialize(parcel) + } +} + +impl Serialize for CString { + fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + Some(self.as_c_str()).serialize(parcel) + } +} + +impl SerializeArray for CString {} + +impl SerializeOption for CString { + fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + SerializeOption::serialize_option(this.map(CString::as_c_str), parcel) + } +} + +impl SerializeArray for Option<CString> {} + +impl Serialize for String { + fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + Some(self.as_str()).serialize(parcel) + } +} + +impl SerializeArray for String {} + +impl SerializeOption for String { + fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + SerializeOption::serialize_option(this.map(String::as_str), parcel) + } +} + +impl SerializeArray for Option<String> {} + +impl Deserialize for Option<CString> { + fn deserialize(parcel: &Parcel) -> Result<Self> { + let mut vec: Option<Vec<u8>> = None; + let status = unsafe { + // Safety: `Parcel` always contains a valid pointer to an `AParcel`. + // `Option<Vec<u8>>` is equivalent to the expected `Option<Vec<i8>>` + // for `allocate_vec`, so `vec` is safe to pass as the opaque data + // pointer on platforms where char is signed. + sys::AParcel_readString( + parcel.as_native(), + &mut vec as *mut _ as *mut c_void, + Some(allocate_vec), + ) + }; + + status_result(status)?; + vec.map(|mut s| { + // The vector includes a null-terminator and CString::new requires + // no nulls, including terminating. + s.pop(); + CString::new(s).or(Err(StatusCode::BAD_VALUE)) + }) + .transpose() + } +} + +impl DeserializeArray for Option<CString> {} + +impl DeserializeOption for String { + fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> { + let c_str = <Option<CString>>::deserialize(parcel)?; + c_str + .map(|s| s.into_string().or(Err(StatusCode::BAD_VALUE))) + .transpose() + } +} + +impl DeserializeArray for Option<String> {} + +impl Deserialize for String { + fn deserialize(parcel: &Parcel) -> Result<Self> { + Deserialize::deserialize(parcel) + .transpose() + .unwrap_or(Err(StatusCode::UNEXPECTED_NULL)) + } +} + +impl DeserializeArray for String {} + +impl SerializeOption for str { + fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + match this { + None => parcel.write(&-1i32), + Some(s) => { + let c_str = CString::new(s).or(Err(StatusCode::BAD_VALUE))?; + parcel.write(&c_str) + } + } + } +} + +impl Serialize for str { + fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + Some(self).serialize(parcel) + } +} + +impl SerializeArray for &str {} + +impl SerializeArray for Option<&str> {} + +impl<T: SerializeArray> Serialize for [T] { + fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + SerializeArray::serialize_array(self, parcel) + } +} + +impl<T: SerializeArray> Serialize for Vec<T> { + fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + SerializeArray::serialize_array(&self[..], parcel) + } +} + +impl<T: SerializeArray> SerializeOption for [T] { + fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + if let Some(v) = this { + SerializeArray::serialize_array(v, parcel) + } else { + parcel.write(&-1i32) + } + } +} + +impl<T: SerializeArray> SerializeOption for Vec<T> { + fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + SerializeOption::serialize_option(this.map(Vec::as_slice), parcel) + } +} + +impl<T: DeserializeArray> Deserialize for Vec<T> { + fn deserialize(parcel: &Parcel) -> Result<Self> { + DeserializeArray::deserialize_array(parcel) + .transpose() + .unwrap_or(Err(StatusCode::UNEXPECTED_NULL)) + } +} + +impl<T: DeserializeArray> DeserializeOption for Vec<T> { + fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> { + DeserializeArray::deserialize_array(parcel) + } +} + +impl Serialize for Status { + fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + unsafe { + // Safety: `Parcel` always contains a valid pointer to an `AParcel` + // and `Status` always contains a valid pointer to an `AStatus`, so + // both parameters are valid and safe. This call does not take + // ownership of either of its parameters. + status_result(sys::AParcel_writeStatusHeader( + parcel.as_native_mut(), + self.as_native(), + )) + } + } +} + +impl Deserialize for Status { + fn deserialize(parcel: &Parcel) -> Result<Self> { + let mut status_ptr = ptr::null_mut(); + let ret_status = unsafe { + // Safety: `Parcel` always contains a valid pointer to an + // `AParcel`. We pass a mutable out pointer which will be + // assigned a valid `AStatus` pointer if the function returns + // status OK. This function passes ownership of the status + // pointer to the caller, if it was assigned. + sys::AParcel_readStatusHeader(parcel.as_native(), &mut status_ptr) + }; + status_result(ret_status)?; + Ok(unsafe { + // Safety: At this point, the return status of the read call was ok, + // so we know that `status_ptr` is a valid, owned pointer to an + // `AStatus`, from which we can safely construct a `Status` object. + Status::from_ptr(status_ptr) + }) + } +} + +impl<T: Serialize + ?Sized> Serialize for Box<T> { + fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + Serialize::serialize(&**self, parcel) + } +} + +impl<T: SerializeOption + ?Sized> SerializeOption for Box<T> { + fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + SerializeOption::serialize_option(this.map(|b| &**b), parcel) + } +} + +impl<T: FromIBinder + ?Sized> Deserialize for Box<T> { + fn deserialize(parcel: &Parcel) -> Result<Self> { + let ibinder: SpIBinder = parcel.read()?; + FromIBinder::try_from(ibinder) + } +} + +impl<T: FromIBinder + ?Sized> DeserializeOption for Box<T> { + fn deserialize_option(parcel: &Parcel) -> Result<Option<Self>> { + let ibinder: Option<SpIBinder> = parcel.read()?; + ibinder.map(FromIBinder::try_from).transpose() + } +} + +// We need these to support Option<&T> for all T +impl<T: Serialize + ?Sized> Serialize for &T { + fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + Serialize::serialize(*self, parcel) + } +} + +impl<T: SerializeOption + ?Sized> SerializeOption for &T { + fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + SerializeOption::serialize_option(this.copied(), parcel) + } +} + +impl<T: SerializeOption> Serialize for Option<T> { + fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + SerializeOption::serialize_option(self.as_ref(), parcel) + } +} + +impl<T: DeserializeOption> Deserialize for Option<T> { + fn deserialize(parcel: &Parcel) -> Result<Self> { + DeserializeOption::deserialize_option(parcel) + } +} + +#[test] +fn test_custom_parcelable() { + use crate::binder::Interface; + use crate::native::Binder; + let mut service = Binder::new(()).as_binder(); + + struct Custom(u32, bool, String, Vec<String>); + + impl Serialize for Custom { + fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + self.0.serialize(parcel)?; + self.1.serialize(parcel)?; + self.2.serialize(parcel)?; + self.3.serialize(parcel) + } + } + + impl Deserialize for Custom { + fn deserialize(parcel: &Parcel) -> Result<Self> { + Ok(Custom( + parcel.read()?, + parcel.read()?, + parcel.read()?, + parcel.read::<Option<Vec<String>>>()?.unwrap(), + )) + } + } + + let string8 = "Custom Parcelable".to_string(); + + let s1 = "str1".to_string(); + let s2 = "str2".to_string(); + let s3 = "str3".to_string(); + + let strs = vec![s1, s2, s3]; + + let custom = Custom(123_456_789, true, string8, strs); + + let mut parcel = Parcel::new_for_test(&mut service).unwrap(); + let start = parcel.get_data_position(); + + assert!(custom.serialize(&mut parcel).is_ok()); + + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + let custom2 = Custom::deserialize(&parcel).unwrap(); + + assert_eq!(custom2.0, 123_456_789); + assert!(custom2.1); + assert_eq!(custom2.2, custom.2); + assert_eq!(custom2.3, custom.3); +} + +#[test] +#[allow(clippy::excessive_precision)] +fn test_slice_parcelables() { + use crate::binder::Interface; + use crate::native::Binder; + let mut service = Binder::new(()).as_binder(); + + let bools = [true, false, false, true]; + + let mut parcel = Parcel::new_for_test(&mut service).unwrap(); + let start = parcel.get_data_position(); + + assert!(bools.serialize(&mut parcel).is_ok()); + + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + assert_eq!(parcel.read::<u32>().unwrap(), 4); + assert_eq!(parcel.read::<u32>().unwrap(), 1); + assert_eq!(parcel.read::<u32>().unwrap(), 0); + assert_eq!(parcel.read::<u32>().unwrap(), 0); + assert_eq!(parcel.read::<u32>().unwrap(), 1); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + let vec = Vec::<bool>::deserialize(&parcel).unwrap(); + + assert_eq!(vec, [true, false, false, true]); + + let u8s = [101u8, 255, 42, 117]; + + let mut parcel = Parcel::new_for_test(&mut service).unwrap(); + let start = parcel.get_data_position(); + + assert!(parcel.write(&u8s[..]).is_ok()); + + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items + assert_eq!(parcel.read::<u32>().unwrap(), 0x752aff65); // bytes + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + let vec = Vec::<u8>::deserialize(&parcel).unwrap(); + assert_eq!(vec, [101, 255, 42, 117]); + + let i8s = [-128i8, 127, 42, -117]; + + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + assert!(parcel.write(&i8s[..]).is_ok()); + + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items + assert_eq!(parcel.read::<u32>().unwrap(), 0x8b2a7f80); // bytes + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + let vec = Vec::<u8>::deserialize(&parcel).unwrap(); + assert_eq!(vec, [-128i8 as u8, 127, 42, -117i8 as u8]); + + let u16s = [u16::max_value(), 12_345, 42, 117]; + + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + assert!(u16s.serialize(&mut parcel).is_ok()); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items + assert_eq!(parcel.read::<u32>().unwrap(), 0xffff); // u16::max_value() + assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345 + assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42 + assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117 + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + let vec = Vec::<u16>::deserialize(&parcel).unwrap(); + + assert_eq!(vec, [u16::max_value(), 12_345, 42, 117]); + + let i16s = [i16::max_value(), i16::min_value(), 42, -117]; + + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + assert!(i16s.serialize(&mut parcel).is_ok()); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items + assert_eq!(parcel.read::<u32>().unwrap(), 0x7fff); // i16::max_value() + assert_eq!(parcel.read::<u32>().unwrap(), 0x8000); // i16::min_value() + assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42 + assert_eq!(parcel.read::<u32>().unwrap(), 0xff8b); // -117 + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + let vec = Vec::<i16>::deserialize(&parcel).unwrap(); + + assert_eq!(vec, [i16::max_value(), i16::min_value(), 42, -117]); + + let u32s = [u32::max_value(), 12_345, 42, 117]; + + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + assert!(u32s.serialize(&mut parcel).is_ok()); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items + assert_eq!(parcel.read::<u32>().unwrap(), 0xffffffff); // u32::max_value() + assert_eq!(parcel.read::<u32>().unwrap(), 12345); // 12,345 + assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42 + assert_eq!(parcel.read::<u32>().unwrap(), 117); // 117 + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + let vec = Vec::<u32>::deserialize(&parcel).unwrap(); + + assert_eq!(vec, [u32::max_value(), 12_345, 42, 117]); + + let i32s = [i32::max_value(), i32::min_value(), 42, -117]; + + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + assert!(i32s.serialize(&mut parcel).is_ok()); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + assert_eq!(parcel.read::<u32>().unwrap(), 4); // 4 items + assert_eq!(parcel.read::<u32>().unwrap(), 0x7fffffff); // i32::max_value() + assert_eq!(parcel.read::<u32>().unwrap(), 0x80000000); // i32::min_value() + assert_eq!(parcel.read::<u32>().unwrap(), 42); // 42 + assert_eq!(parcel.read::<u32>().unwrap(), 0xffffff8b); // -117 + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + let vec = Vec::<i32>::deserialize(&parcel).unwrap(); + + assert_eq!(vec, [i32::max_value(), i32::min_value(), 42, -117]); + + let u64s = [u64::max_value(), 12_345, 42, 117]; + + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + assert!(u64s.serialize(&mut parcel).is_ok()); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + let vec = Vec::<u64>::deserialize(&parcel).unwrap(); + + assert_eq!(vec, [u64::max_value(), 12_345, 42, 117]); + + let i64s = [i64::max_value(), i64::min_value(), 42, -117]; + + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + assert!(i64s.serialize(&mut parcel).is_ok()); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + let vec = Vec::<i64>::deserialize(&parcel).unwrap(); + + assert_eq!(vec, [i64::max_value(), i64::min_value(), 42, -117]); + + let f32s = [ + std::f32::NAN, + std::f32::INFINITY, + 1.23456789, + std::f32::EPSILON, + ]; + + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + assert!(f32s.serialize(&mut parcel).is_ok()); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + let vec = Vec::<f32>::deserialize(&parcel).unwrap(); + + // NAN != NAN so we can't use it in the assert_eq: + assert!(vec[0].is_nan()); + assert_eq!(vec[1..], f32s[1..]); + + let f64s = [ + std::f64::NAN, + std::f64::INFINITY, + 1.234567890123456789, + std::f64::EPSILON, + ]; + + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + assert!(f64s.serialize(&mut parcel).is_ok()); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + let vec = Vec::<f64>::deserialize(&parcel).unwrap(); + + // NAN != NAN so we can't use it in the assert_eq: + assert!(vec[0].is_nan()); + assert_eq!(vec[1..], f64s[1..]); + + let s1 = "Hello, Binder!"; + let s2 = "This is a utf8 string."; + let s3 = "Some more text here."; + + let strs = [s1, s2, s3]; + + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + assert!(strs.serialize(&mut parcel).is_ok()); + unsafe { + assert!(parcel.set_data_position(start).is_ok()); + } + + let vec = Vec::<String>::deserialize(&parcel).unwrap(); + + assert_eq!(vec, strs); +} diff --git a/libs/binder/rust/src/proxy.rs b/libs/binder/rust/src/proxy.rs new file mode 100644 index 0000000000..13e5619237 --- /dev/null +++ b/libs/binder/rust/src/proxy.rs @@ -0,0 +1,532 @@ +/* + * 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. + */ + +//! Rust API for interacting with a remote binder service. + +use crate::binder::{ + AsNative, FromIBinder, IBinder, Interface, InterfaceClass, TransactionCode, TransactionFlags, +}; +use crate::error::{status_result, Result, StatusCode}; +use crate::parcel::{ + Deserialize, DeserializeArray, DeserializeOption, Parcel, Serialize, SerializeArray, + SerializeOption, +}; +use crate::sys; + +use std::convert::TryInto; +use std::ffi::{c_void, CString}; +use std::os::unix::io::AsRawFd; +use std::ptr; + +/// A strong reference to a Binder remote object. +/// +/// This struct encapsulates the generic C++ `sp<IBinder>` class. This wrapper +/// is untyped; typed interface access is implemented by the AIDL compiler. +pub struct SpIBinder(*mut sys::AIBinder); + +/// # Safety +/// +/// An `SpIBinder` is a handle to a C++ IBinder, which is thread-safe +unsafe impl Send for SpIBinder {} + +impl SpIBinder { + /// Create an `SpIBinder` wrapper object from a raw `AIBinder` pointer. + /// + /// # Safety + /// + /// This constructor is safe iff `ptr` is a null pointer or a valid pointer + /// to an `AIBinder`. + /// + /// In the non-null case, this method conceptually takes ownership of a strong + /// reference to the object, so `AIBinder_incStrong` must have been called + /// on the pointer before passing it to this constructor. This is generally + /// done by Binder NDK methods that return an `AIBinder`, but care should be + /// taken to ensure this invariant. + /// + /// All `SpIBinder` objects that are constructed will hold a valid pointer + /// to an `AIBinder`, which will remain valid for the entire lifetime of the + /// `SpIBinder` (we keep a strong reference, and only decrement on drop). + pub(crate) unsafe fn from_raw(ptr: *mut sys::AIBinder) -> Option<Self> { + ptr.as_mut().map(|p| Self(p)) + } + + /// Return true if this binder object is hosted in a different process than + /// the current one. + pub fn is_remote(&self) -> bool { + unsafe { + // Safety: `SpIBinder` guarantees that it always contains a valid + // `AIBinder` pointer. + sys::AIBinder_isRemote(self.as_native()) + } + } + + /// Try to convert this Binder object into a trait object for the given + /// Binder interface. + /// + /// If this object does not implement the expected interface, the error + /// `StatusCode::BAD_TYPE` is returned. + pub fn into_interface<I: FromIBinder + ?Sized>(self) -> Result<Box<I>> { + FromIBinder::try_from(self) + } + + /// Return the interface class of this binder object, if associated with + /// one. + pub(crate) fn get_class(&mut self) -> Option<InterfaceClass> { + unsafe { + // Safety: `SpIBinder` guarantees that it always contains a valid + // `AIBinder` pointer. `AIBinder_getClass` returns either a null + // pointer or a valid pointer to an `AIBinder_Class`. After mapping + // null to None, we can safely construct an `InterfaceClass` if the + // pointer was non-null. + let class = sys::AIBinder_getClass(self.as_native_mut()); + class.as_ref().map(|p| InterfaceClass::from_ptr(p)) + } + } +} + +/// An object that can be associate with an [`InterfaceClass`]. +pub trait AssociateClass { + /// Check if this object is a valid object for the given interface class + /// `I`. + /// + /// Returns `Some(self)` if this is a valid instance of the interface, and + /// `None` otherwise. + /// + /// Classes constructed by `InterfaceClass` are unique per type, so + /// repeatedly calling this method for the same `InterfaceClass` is allowed. + fn associate_class(&mut self, class: InterfaceClass) -> bool; +} + +impl AssociateClass for SpIBinder { + fn associate_class(&mut self, class: InterfaceClass) -> bool { + unsafe { + // Safety: `SpIBinder` guarantees that it always contains a valid + // `AIBinder` pointer. An `InterfaceClass` can always be converted + // into a valid `AIBinder_Class` pointer, so these parameters are + // always safe. + sys::AIBinder_associateClass(self.as_native_mut(), class.into()) + } + } +} + +impl PartialEq for SpIBinder { + fn eq(&self, other: &Self) -> bool { + ptr::eq(self.0, other.0) + } +} + +impl Eq for SpIBinder {} + +impl Clone for SpIBinder { + fn clone(&self) -> Self { + unsafe { + // Safety: Cloning a strong reference must increment the reference + // count. We are guaranteed by the `SpIBinder` constructor + // invariants that `self.0` is always a valid `AIBinder` pointer. + sys::AIBinder_incStrong(self.0); + } + Self(self.0) + } +} + +impl Drop for SpIBinder { + // We hold a strong reference to the IBinder in SpIBinder and need to give up + // this reference on drop. + fn drop(&mut self) { + unsafe { + // Safety: SpIBinder always holds a valid `AIBinder` pointer, so we + // know this pointer is safe to pass to `AIBinder_decStrong` here. + sys::AIBinder_decStrong(self.as_native_mut()); + } + } +} + +impl<T: AsNative<sys::AIBinder>> IBinder for T { + /// Perform a binder transaction + fn transact<F: FnOnce(&mut Parcel) -> Result<()>>( + &self, + code: TransactionCode, + flags: TransactionFlags, + input_callback: F, + ) -> Result<Parcel> { + let mut input = ptr::null_mut(); + let status = unsafe { + // Safety: `SpIBinder` guarantees that `self` always contains a + // valid pointer to an `AIBinder`. It is safe to cast from an + // immutable pointer to a mutable pointer here, because + // `AIBinder_prepareTransaction` only calls immutable `AIBinder` + // methods but the parameter is unfortunately not marked as const. + // + // After the call, input will be either a valid, owned `AParcel` + // pointer, or null. + sys::AIBinder_prepareTransaction(self.as_native() as *mut sys::AIBinder, &mut input) + }; + status_result(status)?; + let mut input = unsafe { + // Safety: At this point, `input` is either a valid, owned `AParcel` + // pointer, or null. `Parcel::owned` safely handles both cases, + // taking ownership of the parcel. + Parcel::owned(input).ok_or(StatusCode::UNEXPECTED_NULL)? + }; + input_callback(&mut input)?; + let mut reply = ptr::null_mut(); + let status = unsafe { + // Safety: `SpIBinder` guarantees that `self` always contains a + // valid pointer to an `AIBinder`. Although `IBinder::transact` is + // not a const method, it is still safe to cast our immutable + // pointer to mutable for the call. First, `IBinder::transact` is + // thread-safe, so concurrency is not an issue. The only way that + // `transact` can affect any visible, mutable state in the current + // process is by calling `onTransact` for a local service. However, + // in order for transactions to be thread-safe, this method must + // dynamically lock its data before modifying it. We enforce this + // property in Rust by requiring `Sync` for remotable objects and + // only providing `on_transact` with an immutable reference to + // `self`. + // + // This call takes ownership of the `input` parcel pointer, and + // passes ownership of the `reply` out parameter to its caller. It + // does not affect ownership of the `binder` parameter. + sys::AIBinder_transact( + self.as_native() as *mut sys::AIBinder, + code, + &mut input.into_raw(), + &mut reply, + flags, + ) + }; + status_result(status)?; + + unsafe { + // Safety: `reply` is either a valid `AParcel` pointer or null + // after the call to `AIBinder_transact` above, so we can + // construct a `Parcel` out of it. `AIBinder_transact` passes + // ownership of the `reply` parcel to Rust, so we need to + // construct an owned variant. `Parcel::owned` takes ownership + // of the parcel pointer. + Parcel::owned(reply).ok_or(StatusCode::UNEXPECTED_NULL) + } + } + + fn is_binder_alive(&self) -> bool { + unsafe { + // Safety: `SpIBinder` guarantees that `self` always contains a + // valid pointer to an `AIBinder`. + // + // This call does not affect ownership of its pointer parameter. + sys::AIBinder_isAlive(self.as_native()) + } + } + + fn ping_binder(&mut self) -> Result<()> { + let status = unsafe { + // Safety: `SpIBinder` guarantees that `self` always contains a + // valid pointer to an `AIBinder`. + // + // This call does not affect ownership of its pointer parameter. + sys::AIBinder_ping(self.as_native_mut()) + }; + status_result(status) + } + + fn dump<F: AsRawFd>(&mut self, fp: &F, args: &[&str]) -> Result<()> { + let args: Vec<_> = args.iter().map(|a| CString::new(*a).unwrap()).collect(); + let mut arg_ptrs: Vec<_> = args.iter().map(|a| a.as_ptr()).collect(); + let status = unsafe { + // Safety: `SpIBinder` guarantees that `self` always contains a + // valid pointer to an `AIBinder`. `AsRawFd` guarantees that the + // file descriptor parameter is always be a valid open file. The + // `args` pointer parameter is a valid pointer to an array of C + // strings that will outlive the call since `args` lives for the + // whole function scope. + // + // This call does not affect ownership of its binder pointer + // parameter and does not take ownership of the file or args array + // parameters. + sys::AIBinder_dump( + self.as_native_mut(), + fp.as_raw_fd(), + arg_ptrs.as_mut_ptr(), + arg_ptrs.len().try_into().unwrap(), + ) + }; + status_result(status) + } + + fn get_extension(&mut self) -> Result<Option<SpIBinder>> { + let mut out = ptr::null_mut(); + let status = unsafe { + // Safety: `SpIBinder` guarantees that `self` always contains a + // valid pointer to an `AIBinder`. After this call, the `out` + // parameter will be either null, or a valid pointer to an + // `AIBinder`. + // + // This call passes ownership of the out pointer to its caller + // (assuming it is set to a non-null value). + sys::AIBinder_getExtension(self.as_native_mut(), &mut out) + }; + let ibinder = unsafe { + // Safety: The call above guarantees that `out` is either null or a + // valid, owned pointer to an `AIBinder`, both of which are safe to + // pass to `SpIBinder::from_raw`. + SpIBinder::from_raw(out) + }; + + status_result(status)?; + Ok(ibinder) + } + + fn link_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()> { + status_result(unsafe { + // Safety: `SpIBinder` guarantees that `self` always contains a + // valid pointer to an `AIBinder`. `recipient` can always be + // converted into a valid pointer to an + // `AIBinder_DeatRecipient`. Any value is safe to pass as the + // cookie, although we depend on this value being set by + // `get_cookie` when the death recipient callback is called. + sys::AIBinder_linkToDeath( + self.as_native_mut(), + recipient.as_native_mut(), + recipient.get_cookie(), + ) + }) + } + + fn unlink_to_death(&mut self, recipient: &mut DeathRecipient) -> Result<()> { + status_result(unsafe { + // Safety: `SpIBinder` guarantees that `self` always contains a + // valid pointer to an `AIBinder`. `recipient` can always be + // converted into a valid pointer to an + // `AIBinder_DeatRecipient`. Any value is safe to pass as the + // cookie, although we depend on this value being set by + // `get_cookie` when the death recipient callback is called. + sys::AIBinder_unlinkToDeath( + self.as_native_mut(), + recipient.as_native_mut(), + recipient.get_cookie(), + ) + }) + } +} + +impl Serialize for SpIBinder { + fn serialize(&self, parcel: &mut Parcel) -> Result<()> { + parcel.write_binder(Some(self)) + } +} + +impl SerializeOption for SpIBinder { + fn serialize_option(this: Option<&Self>, parcel: &mut Parcel) -> Result<()> { + parcel.write_binder(this) + } +} + +impl SerializeArray for SpIBinder {} +impl SerializeArray for Option<&SpIBinder> {} + +impl Deserialize for SpIBinder { + fn deserialize(parcel: &Parcel) -> Result<SpIBinder> { + parcel.read_binder().transpose().unwrap() + } +} + +impl DeserializeOption for SpIBinder { + fn deserialize_option(parcel: &Parcel) -> Result<Option<SpIBinder>> { + parcel.read_binder() + } +} + +impl DeserializeArray for SpIBinder {} +impl DeserializeArray for Option<SpIBinder> {} + +/// A weak reference to a Binder remote object. +/// +/// This struct encapsulates the C++ `wp<IBinder>` class. However, this wrapper +/// is untyped, so properly typed versions implementing a particular binder +/// interface should be crated with [`declare_binder_interface!`]. +pub struct WpIBinder(*mut sys::AIBinder_Weak); + +impl WpIBinder { + /// Create a new weak reference from an object that can be converted into a + /// raw `AIBinder` pointer. + pub fn new<B: AsNative<sys::AIBinder>>(binder: &mut B) -> WpIBinder { + let ptr = unsafe { + // Safety: `SpIBinder` guarantees that `binder` always contains a + // valid pointer to an `AIBinder`. + sys::AIBinder_Weak_new(binder.as_native_mut()) + }; + assert!(!ptr.is_null()); + Self(ptr) + } + + /// Promote this weak reference to a strong reference to the binder object. + pub fn promote(&self) -> Option<SpIBinder> { + unsafe { + // Safety: `WpIBinder` always contains a valid weak reference, so we + // can pass this pointer to `AIBinder_Weak_promote`. Returns either + // null or an AIBinder owned by the caller, both of which are valid + // to pass to `SpIBinder::from_raw`. + let ptr = sys::AIBinder_Weak_promote(self.0); + SpIBinder::from_raw(ptr) + } + } +} + +/// Rust wrapper around DeathRecipient objects. +#[repr(C)] +pub struct DeathRecipient { + recipient: *mut sys::AIBinder_DeathRecipient, + callback: Box<dyn Fn() + Send + 'static>, +} + +impl DeathRecipient { + /// Create a new death recipient that will call the given callback when its + /// associated object dies. + pub fn new<F>(callback: F) -> DeathRecipient + where + F: Fn() + Send + 'static, + { + let callback = Box::new(callback); + let recipient = unsafe { + // Safety: The function pointer is a valid death recipient callback. + // + // This call returns an owned `AIBinder_DeathRecipient` pointer + // which must be destroyed via `AIBinder_DeathRecipient_delete` when + // no longer needed. + sys::AIBinder_DeathRecipient_new(Some(Self::binder_died::<F>)) + }; + DeathRecipient { + recipient, + callback, + } + } + + /// Get the opaque cookie that identifies this death recipient. + /// + /// This cookie will be used to link and unlink this death recipient to a + /// binder object and will be passed to the `binder_died` callback as an + /// opaque userdata pointer. + fn get_cookie(&self) -> *mut c_void { + &*self.callback as *const _ as *mut c_void + } + + /// Callback invoked from C++ when the binder object dies. + /// + /// # Safety + /// + /// The `cookie` parameter must have been created with the `get_cookie` + /// method of this object. + unsafe extern "C" fn binder_died<F>(cookie: *mut c_void) + where + F: Fn() + Send + 'static, + { + let callback = (cookie as *mut F).as_ref().unwrap(); + callback(); + } +} + +/// # Safety +/// +/// A `DeathRecipient` is always constructed with a valid raw pointer to an +/// `AIBinder_DeathRecipient`, so it is always type-safe to extract this +/// pointer. +unsafe impl AsNative<sys::AIBinder_DeathRecipient> for DeathRecipient { + fn as_native(&self) -> *const sys::AIBinder_DeathRecipient { + self.recipient + } + + fn as_native_mut(&mut self) -> *mut sys::AIBinder_DeathRecipient { + self.recipient + } +} + +impl Drop for DeathRecipient { + fn drop(&mut self) { + unsafe { + // Safety: `self.recipient` is always a valid, owned + // `AIBinder_DeathRecipient` pointer returned by + // `AIBinder_DeathRecipient_new` when `self` was created. This + // delete method can only be called once when `self` is dropped. + sys::AIBinder_DeathRecipient_delete(self.recipient); + } + } +} + +/// Generic interface to remote binder objects. +/// +/// Corresponds to the C++ `BpInterface` class. +pub trait Proxy: Sized + Interface { + /// The Binder interface descriptor string. + /// + /// This string is a unique identifier for a Binder interface, and should be + /// the same between all implementations of that interface. + fn get_descriptor() -> &'static str; + + /// Create a new interface from the given proxy, if it matches the expected + /// type of this interface. + fn from_binder(binder: SpIBinder) -> Result<Self>; +} + +/// # Safety +/// +/// This is a convenience method that wraps `AsNative` for `SpIBinder` to allow +/// invocation of `IBinder` methods directly from `Interface` objects. It shares +/// the same safety as the implementation for `SpIBinder`. +unsafe impl<T: Proxy> AsNative<sys::AIBinder> for T { + fn as_native(&self) -> *const sys::AIBinder { + self.as_binder().as_native() + } + + fn as_native_mut(&mut self) -> *mut sys::AIBinder { + self.as_binder().as_native_mut() + } +} + +/// Retrieve an existing service, blocking for a few seconds if it doesn't yet +/// exist. +pub fn get_service(name: &str) -> Option<SpIBinder> { + let name = CString::new(name).ok()?; + unsafe { + // Safety: `AServiceManager_getService` returns either a null pointer or + // a valid pointer to an owned `AIBinder`. Either of these values is + // safe to pass to `SpIBinder::from_raw`. + SpIBinder::from_raw(sys::AServiceManager_getService(name.as_ptr())) + } +} + +/// Retrieve an existing service for a particular interface, blocking for a few +/// seconds if it doesn't yet exist. +pub fn get_interface<T: FromIBinder + ?Sized>(name: &str) -> Result<Box<T>> { + let service = get_service(name); + match service { + Some(service) => FromIBinder::try_from(service), + None => Err(StatusCode::NAME_NOT_FOUND), + } +} + +/// # Safety +/// +/// `SpIBinder` guarantees that `binder` always contains a valid pointer to an +/// `AIBinder`, so we can trivially extract this pointer here. +unsafe impl AsNative<sys::AIBinder> for SpIBinder { + fn as_native(&self) -> *const sys::AIBinder { + self.0 + } + + fn as_native_mut(&mut self) -> *mut sys::AIBinder { + self.0 + } +} diff --git a/libs/binder/rust/src/state.rs b/libs/binder/rust/src/state.rs new file mode 100644 index 0000000000..992f074f26 --- /dev/null +++ b/libs/binder/rust/src/state.rs @@ -0,0 +1,101 @@ +/* + * 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. + */ + +use crate::sys; + +use libc::{pid_t, uid_t}; + +/// Static utility functions to manage Binder process state. +pub struct ProcessState; + +impl ProcessState { + /// Start the Binder IPC thread pool + pub fn start_thread_pool() { + unsafe { + // Safety: Safe FFI + sys::ABinderProcess_startThreadPool(); + } + } + + /// Set the maximum number of threads that can be started in the threadpool. + /// + /// By default, after startThreadPool is called, this is 15. If it is called + /// additional times, it will only prevent the kernel from starting new + /// threads and will not delete already existing threads. + pub fn set_thread_pool_max_thread_count(num_threads: u32) { + unsafe { + // Safety: Safe FFI + sys::ABinderProcess_setThreadPoolMaxThreadCount(num_threads); + } + } + + /// Block on the Binder IPC thread pool + pub fn join_thread_pool() { + unsafe { + // Safety: Safe FFI + sys::ABinderProcess_joinThreadPool(); + } + } +} + +/// Static utility functions to manage Binder thread state. +pub struct ThreadState; + +impl ThreadState { + /// This returns the calling UID assuming that this thread is called from a + /// thread that is processing a binder transaction (for instance, in the + /// implementation of + /// [`Remotable::on_transact`](crate::Remotable::on_transact)). + /// + /// This can be used with higher-level system services to determine the + /// caller's identity and check permissions. + /// + /// Available since API level 29. + /// + /// \return calling uid or the current process's UID if this thread isn't + /// processing a transaction. + pub fn get_calling_uid() -> uid_t { + unsafe { + // Safety: Safe FFI + sys::AIBinder_getCallingUid() + } + } + + /// This returns the calling PID assuming that this thread is called from a + /// thread that is processing a binder transaction (for instance, in the + /// implementation of + /// [`Remotable::on_transact`](crate::Remotable::on_transact)). + /// + /// This can be used with higher-level system services to determine the + /// caller's identity and check permissions. However, when doing this, one + /// should be aware of possible TOCTOU problems when the calling process + /// dies and is replaced with another process with elevated permissions and + /// the same PID. + /// + /// Available since API level 29. + /// + /// \return calling pid or the current process's PID if this thread isn't + /// processing a transaction. + /// + /// If the transaction being processed is a oneway transaction, then this + /// method will return 0. + pub fn get_calling_pid() -> pid_t { + unsafe { + // Safety: Safe FFI + sys::AIBinder_getCallingPid() + } + } +} diff --git a/libs/binder/rust/sys/BinderBindings.h b/libs/binder/rust/sys/BinderBindings.h new file mode 100644 index 0000000000..c7a06d9a1c --- /dev/null +++ b/libs/binder/rust/sys/BinderBindings.h @@ -0,0 +1,86 @@ +/* + * 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_ibinder.h> +#include <android/binder_manager.h> +#include <android/binder_parcel.h> +#include <android/binder_process.h> +#include <android/binder_shell.h> +#include <android/binder_status.h> + +namespace android { + +namespace c_interface { + +// Expose error codes from anonymous enum in binder_status.h +enum StatusCode { + OK = STATUS_OK, + UNKNOWN_ERROR = STATUS_UNKNOWN_ERROR, + NO_MEMORY = STATUS_NO_MEMORY, + INVALID_OPERATION = STATUS_INVALID_OPERATION, + BAD_VALUE = STATUS_BAD_VALUE, + BAD_TYPE = STATUS_BAD_TYPE, + NAME_NOT_FOUND = STATUS_NAME_NOT_FOUND, + PERMISSION_DENIED = STATUS_PERMISSION_DENIED, + NO_INIT = STATUS_NO_INIT, + ALREADY_EXISTS = STATUS_ALREADY_EXISTS, + DEAD_OBJECT = STATUS_DEAD_OBJECT, + FAILED_TRANSACTION = STATUS_FAILED_TRANSACTION, + BAD_INDEX = STATUS_BAD_INDEX, + NOT_ENOUGH_DATA = STATUS_NOT_ENOUGH_DATA, + WOULD_BLOCK = STATUS_WOULD_BLOCK, + TIMED_OUT = STATUS_TIMED_OUT, + UNKNOWN_TRANSACTION = STATUS_UNKNOWN_TRANSACTION, + FDS_NOT_ALLOWED = STATUS_FDS_NOT_ALLOWED, + UNEXPECTED_NULL = STATUS_UNEXPECTED_NULL, +}; + +// Expose exception codes from anonymous enum in binder_status.h +enum ExceptionCode { + NONE = EX_NONE, + SECURITY = EX_SECURITY, + BAD_PARCELABLE = EX_BAD_PARCELABLE, + ILLEGAL_ARGUMENT = EX_ILLEGAL_ARGUMENT, + NULL_POINTER = EX_NULL_POINTER, + ILLEGAL_STATE = EX_ILLEGAL_STATE, + NETWORK_MAIN_THREAD = EX_NETWORK_MAIN_THREAD, + UNSUPPORTED_OPERATION = EX_UNSUPPORTED_OPERATION, + SERVICE_SPECIFIC = EX_SERVICE_SPECIFIC, + PARCELABLE = EX_PARCELABLE, + + /** + * This is special, and indicates to native binder proxies that the + * transaction has failed at a low level. + */ + TRANSACTION_FAILED = EX_TRANSACTION_FAILED, +}; + +namespace consts { + +enum { + FIRST_CALL_TRANSACTION = FIRST_CALL_TRANSACTION, + LAST_CALL_TRANSACTION = LAST_CALL_TRANSACTION, +}; + +enum { + FLAG_ONEWAY = FLAG_ONEWAY, +}; + +} // namespace consts + +} // namespace c_interface + +} // namespace android diff --git a/libs/binder/rust/sys/lib.rs b/libs/binder/rust/sys/lib.rs new file mode 100644 index 0000000000..9095af29e0 --- /dev/null +++ b/libs/binder/rust/sys/lib.rs @@ -0,0 +1,38 @@ +/* + * 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. + */ + +//! Generated Rust bindings to libbinder_ndk + +#![allow( + non_camel_case_types, + non_snake_case, + non_upper_case_globals, + unused, + improper_ctypes, + missing_docs +)] +use std::error::Error; +use std::fmt; + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + +impl Error for android_c_interface_StatusCode {} + +impl fmt::Display for android_c_interface_StatusCode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "StatusCode::{:?}", self) + } +} diff --git a/libs/binder/rust/tests/Android.bp b/libs/binder/rust/tests/Android.bp new file mode 100644 index 0000000000..622604f0ed --- /dev/null +++ b/libs/binder/rust/tests/Android.bp @@ -0,0 +1,28 @@ +rust_test { + name: "rustBinderTest", + srcs: ["integration.rs"], + rustlibs: [ + "libbinder_rs", + ], + // For the binaries to be pushed properly as specified in AndroidTest.xml, + // this cannot be the same as the module name. + stem: "rustBinderTestClientBinary", + test_suites: ["general-tests"], +} + +rust_test { + name: "rustBinderTestService", + srcs: ["integration.rs"], + rustlibs: [ + "libbinder_rs", + "liblibc", + ], + // For the binaries to be pushed properly as specified in AndroidTest.xml, + // this cannot be the same as the module name. + stem: "rustBinderTestServiceBinary", + test_harness: false, + // TODO(b/164473602): Remove this setting and add the module to `data` + // attribute of rustBinderTest. + auto_gen_config: false, + test_suites: ["general-tests"], +} diff --git a/libs/binder/ndk/test/AndroidTest.xml b/libs/binder/rust/tests/AndroidTest.xml index 89646f7776..d8d735d3ee 100644 --- a/libs/binder/ndk/test/AndroidTest.xml +++ b/libs/binder/rust/tests/AndroidTest.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2019 The Android Open Source Project +<!-- 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. @@ -13,20 +13,16 @@ See the License for the specific language governing permissions and limitations under the License. --> -<configuration description="Runs binderVendorDoubleLoadTest."> - <option name="test-suite-tag" value="apct" /> - <option name="test-suite-tag" value="apct-native" /> - +<configuration description="Runs Binder Rust integration tests."> <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer"/> - <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer"> <option name="cleanup" value="true" /> - <option name="push" value="binderVendorDoubleLoadTest->/data/nativetest/vendor/binderVendorDoubleLoadTest" /> + <option name="push" value="rustBinderTestClientBinary->/data/local/tmp/rustBinderTest" /> + <option name="push" value="rustBinderTestServiceBinary->/data/local/tmp/rustBinderTestService" /> </target_preparer> - <test class="com.android.tradefed.testtype.GTest" > - <option name="native-test-device-path" value="/data/nativetest/vendor" /> - <option name="module-name" value="binderVendorDoubleLoadTest" /> + <test class="com.android.tradefed.testtype.rust.RustBinaryTest" > + <option name="test-device-path" value="/data/local/tmp" /> + <option name="module-name" value="rustBinderTest" /> </test> </configuration> - diff --git a/libs/binder/rust/tests/integration.rs b/libs/binder/rust/tests/integration.rs new file mode 100644 index 0000000000..fe59416a99 --- /dev/null +++ b/libs/binder/rust/tests/integration.rs @@ -0,0 +1,371 @@ +/* + * 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. + */ + +//! Rust Binder crate integration tests + +use binder::declare_binder_interface; +use binder::parcel::Parcel; +use binder::{Binder, IBinder, Interface, SpIBinder, TransactionCode}; + +/// Name of service runner. +/// +/// Must match the binary name in Android.bp +const RUST_SERVICE_BINARY: &str = "rustBinderTestService"; + +/// Binary to run a test service. +/// +/// This needs to be in a separate process from the tests, so we spawn this +/// binary as a child, providing the service name as an argument. +fn main() -> Result<(), &'static str> { + // Ensure that we can handle all transactions on the main thread. + binder::ProcessState::set_thread_pool_max_thread_count(0); + binder::ProcessState::start_thread_pool(); + + let mut args = std::env::args().skip(1); + if args.len() < 1 || args.len() > 2 { + print_usage(); + return Err(""); + } + let service_name = args.next().ok_or_else(|| { + print_usage(); + "Missing SERVICE_NAME argument" + })?; + let extension_name = args.next(); + + { + let mut service = Binder::new(BnTest(Box::new(TestService { + s: service_name.clone(), + }))); + if let Some(extension_name) = extension_name { + let extension = BnTest::new_binder(TestService { s: extension_name }); + service + .set_extension(&mut extension.as_binder()) + .expect("Could not add extension"); + } + binder::add_service(&service_name, service.as_binder()) + .expect("Could not register service"); + } + + binder::ProcessState::join_thread_pool(); + Err("Unexpected exit after join_thread_pool") +} + +fn print_usage() { + eprintln!( + "Usage: {} SERVICE_NAME [EXTENSION_NAME]", + RUST_SERVICE_BINARY + ); + eprintln!(concat!( + "Spawn a Binder test service identified by SERVICE_NAME,", + " optionally with an extesion named EXTENSION_NAME", + )); +} + +#[derive(Clone)] +struct TestService { + s: String, +} + +impl Interface for TestService {} + +impl ITest for TestService { + fn test(&self) -> binder::Result<String> { + Ok(self.s.clone()) + } +} + +/// Trivial testing binder interface +pub trait ITest: Interface { + /// Returns a test string + fn test(&self) -> binder::Result<String>; +} + +declare_binder_interface! { + ITest["android.os.ITest"] { + native: BnTest(on_transact), + proxy: BpTest { + x: i32 = 100 + }, + } +} + +fn on_transact( + service: &dyn ITest, + _code: TransactionCode, + _data: &Parcel, + reply: &mut Parcel, +) -> binder::Result<()> { + reply.write(&service.test()?)?; + Ok(()) +} + +impl ITest for BpTest { + fn test(&self) -> binder::Result<String> { + let reply = self + .binder + .transact(SpIBinder::FIRST_CALL_TRANSACTION, 0, |_| Ok(()))?; + reply.read() + } +} + +impl ITest for Binder<BnTest> { + fn test(&self) -> binder::Result<String> { + self.0.test() + } +} + +#[cfg(test)] +mod tests { + use std::fs::File; + use std::process::{Child, Command}; + use std::sync::atomic::{AtomicBool, Ordering}; + use std::sync::Arc; + use std::thread; + use std::time::Duration; + + use binder::{DeathRecipient, FromIBinder, IBinder, SpIBinder, StatusCode}; + + use super::{ITest, RUST_SERVICE_BINARY}; + + pub struct ScopedServiceProcess(Child); + + impl ScopedServiceProcess { + pub fn new(identifier: &str) -> Self { + Self::new_internal(identifier, None) + } + + pub fn new_with_extension(identifier: &str, extension: &str) -> Self { + Self::new_internal(identifier, Some(extension)) + } + + fn new_internal(identifier: &str, extension: Option<&str>) -> Self { + let mut binary_path = + std::env::current_exe().expect("Could not retrieve current executable path"); + binary_path.pop(); + binary_path.push(RUST_SERVICE_BINARY); + let mut command = Command::new(&binary_path); + command.arg(identifier); + if let Some(ext) = extension { + command.arg(ext); + } + let child = command.spawn().expect("Could not start service"); + Self(child) + } + } + + impl Drop for ScopedServiceProcess { + fn drop(&mut self) { + self.0.kill().expect("Could not kill child process"); + self.0 + .wait() + .expect("Could not wait for child process to die"); + } + } + + #[test] + fn check_services() { + let mut sm = binder::get_service("manager").expect("Did not get manager binder service"); + assert!(sm.is_binder_alive()); + assert!(sm.ping_binder().is_ok()); + + assert!(binder::get_service("this_service_does_not_exist").is_none()); + assert_eq!( + binder::get_interface::<dyn ITest>("this_service_does_not_exist").err(), + Some(StatusCode::NAME_NOT_FOUND) + ); + + // The service manager service isn't an ITest, so this must fail. + assert_eq!( + binder::get_interface::<dyn ITest>("manager").err(), + Some(StatusCode::BAD_TYPE) + ); + } + + #[test] + fn trivial_client() { + let service_name = "trivial_client_test"; + let _process = ScopedServiceProcess::new(service_name); + let test_client: Box<dyn ITest> = + binder::get_interface(service_name).expect("Did not get manager binder service"); + assert_eq!(test_client.test().unwrap(), "trivial_client_test"); + } + + fn register_death_notification(binder: &mut SpIBinder) -> (Arc<AtomicBool>, DeathRecipient) { + let binder_died = Arc::new(AtomicBool::new(false)); + + let mut death_recipient = { + let flag = binder_died.clone(); + DeathRecipient::new(move || { + flag.store(true, Ordering::Relaxed); + }) + }; + + binder + .link_to_death(&mut death_recipient) + .expect("link_to_death failed"); + + (binder_died, death_recipient) + } + + /// Killing a remote service should unregister the service and trigger + /// death notifications. + #[test] + fn test_death_notifications() { + binder::ProcessState::start_thread_pool(); + + let service_name = "test_death_notifications"; + let service_process = ScopedServiceProcess::new(service_name); + let mut remote = binder::get_service(service_name).expect("Could not retrieve service"); + + let (binder_died, _recipient) = register_death_notification(&mut remote); + + drop(service_process); + remote + .ping_binder() + .expect_err("Service should have died already"); + + // Pause to ensure any death notifications get delivered + thread::sleep(Duration::from_secs(1)); + + assert!( + binder_died.load(Ordering::Relaxed), + "Did not receive death notification" + ); + } + + /// Test unregistering death notifications. + #[test] + fn test_unregister_death_notifications() { + binder::ProcessState::start_thread_pool(); + + let service_name = "test_unregister_death_notifications"; + let service_process = ScopedServiceProcess::new(service_name); + let mut remote = binder::get_service(service_name).expect("Could not retrieve service"); + + let (binder_died, mut recipient) = register_death_notification(&mut remote); + + remote + .unlink_to_death(&mut recipient) + .expect("Could not unlink death notifications"); + + drop(service_process); + remote + .ping_binder() + .expect_err("Service should have died already"); + + // Pause to ensure any death notifications get delivered + thread::sleep(Duration::from_secs(1)); + + assert!( + !binder_died.load(Ordering::Relaxed), + "Received unexpected death notification after unlinking", + ); + } + + /// Dropping a remote handle should unregister any death notifications. + #[test] + fn test_death_notification_registration_lifetime() { + binder::ProcessState::start_thread_pool(); + + let service_name = "test_death_notification_registration_lifetime"; + let service_process = ScopedServiceProcess::new(service_name); + let mut remote = binder::get_service(service_name).expect("Could not retrieve service"); + + let (binder_died, _recipient) = register_death_notification(&mut remote); + + // This should automatically unregister our death notification. + drop(remote); + + drop(service_process); + + // Pause to ensure any death notifications get delivered + thread::sleep(Duration::from_secs(1)); + + // We dropped the remote handle, so we should not receive the death + // notification when the remote process dies here. + assert!( + !binder_died.load(Ordering::Relaxed), + "Received unexpected death notification after dropping remote handle" + ); + } + + /// Test IBinder interface methods not exercised elsewhere. + #[test] + fn test_misc_ibinder() { + let service_name = "rust_test_ibinder"; + + { + let _process = ScopedServiceProcess::new(service_name); + + let mut remote = binder::get_service(service_name); + assert!(remote.is_binder_alive()); + remote.ping_binder().expect("Could not ping remote service"); + + // We're not testing the output of dump here, as that's really a + // property of the C++ implementation. There is the risk that the + // method just does nothing, but we don't want to depend on any + // particular output from the underlying library. + let null_out = File::open("/dev/null").expect("Could not open /dev/null"); + remote + .dump(&null_out, &[]) + .expect("Could not dump remote service"); + } + + // get/set_extensions is tested in test_extensions() + + // transact is tested everywhere else, and we can't make raw + // transactions outside the [FIRST_CALL_TRANSACTION, + // LAST_CALL_TRANSACTION] range from the NDK anyway. + + // link_to_death is tested in test_*_death_notification* tests. + } + + #[test] + fn test_extensions() { + let service_name = "rust_test_extensions"; + let extension_name = "rust_test_extensions_ext"; + + { + let _process = ScopedServiceProcess::new(service_name); + + let mut remote = binder::get_service(service_name); + assert!(remote.is_binder_alive()); + + let extension = remote + .get_extension() + .expect("Could not check for an extension"); + assert!(extension.is_none()); + } + + { + let _process = ScopedServiceProcess::new_with_extension(service_name, extension_name); + + let mut remote = binder::get_service(service_name); + assert!(remote.is_binder_alive()); + + let maybe_extension = remote + .get_extension() + .expect("Could not check for an extension"); + + let extension = maybe_extension.expect("Remote binder did not have an extension"); + + let extension: Box<dyn ITest> = FromIBinder::try_from(extension) + .expect("Extension could not be converted to the expected interface"); + + assert_eq!(extension.test().unwrap(), extension_name); + } + } +} diff --git a/libs/binder/tests/Android.bp b/libs/binder/tests/Android.bp index c0da2cd8d4..a03835b34c 100644 --- a/libs/binder/tests/Android.bp +++ b/libs/binder/tests/Android.bp @@ -155,6 +155,7 @@ cc_test { "binderStabilityTest.cpp", ], + // critical that libbinder/libbinder_ndk are shared for VTS shared_libs: [ "libbinder_ndk", "libbinder", @@ -166,6 +167,21 @@ cc_test { "binderStabilityTestIface-ndk_platform", ], + test_suites: ["device-tests", "vts"], + require_root: true, +} + +cc_test { + name: "binderAllocationLimits", + defaults: ["binder_test_defaults"], + srcs: ["binderAllocationLimits.cpp"], + shared_libs: [ + "libbinder", + "liblog", + "libutils", + "libutilscallstack", + "libbase", + ], test_suites: ["device-tests"], require_root: true, } diff --git a/libs/binder/tests/binderAllocationLimits.cpp b/libs/binder/tests/binderAllocationLimits.cpp new file mode 100644 index 0000000000..e1f5ed5381 --- /dev/null +++ b/libs/binder/tests/binderAllocationLimits.cpp @@ -0,0 +1,186 @@ +/* + * 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-base/logging.h> +#include <binder/Parcel.h> +#include <binder/IServiceManager.h> +#include <gtest/gtest.h> +#include <utils/CallStack.h> + +#include <malloc.h> +#include <functional> +#include <vector> + +struct DestructionAction { + DestructionAction(std::function<void()> f) : mF(std::move(f)) {} + ~DestructionAction() { mF(); }; +private: + std::function<void()> mF; +}; + +// Group of hooks +struct MallocHooks { + decltype(__malloc_hook) malloc_hook; + decltype(__realloc_hook) realloc_hook; + + static MallocHooks save() { + return { + .malloc_hook = __malloc_hook, + .realloc_hook = __realloc_hook, + }; + } + + void overwrite() const { + __malloc_hook = malloc_hook; + __realloc_hook = realloc_hook; + } +}; + +static const MallocHooks orig_malloc_hooks = MallocHooks::save(); + +// When malloc is hit, executes lambda. +namespace LambdaHooks { + using AllocationHook = std::function<void(size_t)>; + static std::vector<AllocationHook> lambdas = {}; + + static void* lambda_realloc_hook(void* ptr, size_t bytes, const void* arg); + static void* lambda_malloc_hook(size_t bytes, const void* arg); + + static const MallocHooks lambda_malloc_hooks = { + .malloc_hook = lambda_malloc_hook, + .realloc_hook = lambda_realloc_hook, + }; + + static void* lambda_malloc_hook(size_t bytes, const void* arg) { + { + orig_malloc_hooks.overwrite(); + lambdas.at(lambdas.size() - 1)(bytes); + lambda_malloc_hooks.overwrite(); + } + return orig_malloc_hooks.malloc_hook(bytes, arg); + } + + static void* lambda_realloc_hook(void* ptr, size_t bytes, const void* arg) { + { + orig_malloc_hooks.overwrite(); + lambdas.at(lambdas.size() - 1)(bytes); + lambda_malloc_hooks.overwrite(); + } + return orig_malloc_hooks.realloc_hook(ptr, bytes, arg); + } + +} + +// Action to execute when malloc is hit. Supports nesting. Malloc is not +// restricted when the allocation hook is being processed. +__attribute__((warn_unused_result)) +DestructionAction OnMalloc(LambdaHooks::AllocationHook f) { + MallocHooks before = MallocHooks::save(); + LambdaHooks::lambdas.emplace_back(std::move(f)); + LambdaHooks::lambda_malloc_hooks.overwrite(); + return DestructionAction([before]() { + before.overwrite(); + LambdaHooks::lambdas.pop_back(); + }); +} + +// exported symbol, to force compiler not to optimize away pointers we set here +const void* imaginary_use; + +TEST(TestTheTest, OnMalloc) { + size_t mallocs = 0; + { + const auto on_malloc = OnMalloc([&](size_t bytes) { + mallocs++; + EXPECT_EQ(bytes, 40); + }); + + imaginary_use = new int[10]; + } + EXPECT_EQ(mallocs, 1); +} + + +__attribute__((warn_unused_result)) +DestructionAction ScopeDisallowMalloc() { + return OnMalloc([&](size_t bytes) { + ADD_FAILURE() << "Unexpected allocation: " << bytes; + using android::CallStack; + std::cout << CallStack::stackToString("UNEXPECTED ALLOCATION", CallStack::getCurrent(4 /*ignoreDepth*/).get()) + << std::endl; + }); +} + +using android::IBinder; +using android::Parcel; +using android::String16; +using android::defaultServiceManager; +using android::sp; +using android::IServiceManager; + +static sp<IBinder> GetRemoteBinder() { + // This gets binder representing the service manager + // the current IServiceManager API doesn't expose the binder, and + // I want to avoid adding usages of the AIDL generated interface it + // is using underneath, so to avoid people copying it. + sp<IBinder> binder = defaultServiceManager()->checkService(String16("manager")); + EXPECT_NE(nullptr, binder); + return binder; +} + +TEST(BinderAllocation, ParcelOnStack) { + const auto m = ScopeDisallowMalloc(); + Parcel p; + imaginary_use = p.data(); +} + +TEST(BinderAllocation, GetServiceManager) { + defaultServiceManager(); // first call may alloc + const auto m = ScopeDisallowMalloc(); + defaultServiceManager(); +} + +// note, ping does not include interface descriptor +TEST(BinderAllocation, PingTransaction) { + sp<IBinder> a_binder = GetRemoteBinder(); + const auto m = ScopeDisallowMalloc(); + a_binder->pingBinder(); +} + +TEST(BinderAllocation, SmallTransaction) { + String16 empty_descriptor = String16(""); + sp<IServiceManager> manager = defaultServiceManager(); + + size_t mallocs = 0; + const auto on_malloc = OnMalloc([&](size_t bytes) { + mallocs++; + // Parcel should allocate a small amount by default + EXPECT_EQ(bytes, 128); + }); + manager->checkService(empty_descriptor); + + EXPECT_EQ(mallocs, 1); +} + +int main(int argc, char** argv) { + if (getenv("LIBC_HOOKS_ENABLE") == nullptr) { + CHECK(0 == setenv("LIBC_HOOKS_ENABLE", "1", true /*overwrite*/)); + execv(argv[0], argv); + return 1; + } + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/libs/binder/tests/binderThroughputTest.cpp b/libs/binder/tests/binderThroughputTest.cpp index b7909972ff..3b1faa8c2f 100644 --- a/libs/binder/tests/binderThroughputTest.cpp +++ b/libs/binder/tests/binderThroughputTest.cpp @@ -116,7 +116,7 @@ struct ProcResults { if (time > max_time_bucket) { m_long_transactions++; } - m_buckets[min(time, max_time_bucket-1) / time_per_bucket] += 1; + m_buckets[min((uint32_t)(time / time_per_bucket), num_buckets - 1)] += 1; m_best = min(time, m_best); m_worst = max(time, m_worst); m_transactions += 1; diff --git a/libs/binderthreadstate/test.cpp b/libs/binderthreadstate/test.cpp index 68cc225057..44e2fd19b1 100644 --- a/libs/binderthreadstate/test.cpp +++ b/libs/binderthreadstate/test.cpp @@ -165,7 +165,6 @@ int server(size_t thisId, size_t otherId) { 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())); @@ -176,7 +175,7 @@ int server(size_t thisId, size_t otherId) { int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); - setenv("TREBLE_TESTING_OVERRIDE", "true", true); + android::hardware::details::setTrebleTestingOverride(true); if (fork() == 0) { prctl(PR_SET_PDEATHSIG, SIGHUP); return server(kP1Id, kP2Id); diff --git a/libs/cputimeinstate/cputimeinstate.cpp b/libs/cputimeinstate/cputimeinstate.cpp index 50f6289726..5e785b67c8 100644 --- a/libs/cputimeinstate/cputimeinstate.cpp +++ b/libs/cputimeinstate/cputimeinstate.cpp @@ -251,7 +251,7 @@ std::optional<std::vector<std::vector<uint64_t>>> getUidCpuFreqTimes(uint32_t ui 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 {}; + if (errno != ENOENT || getFirstMapKey(gTisMapFd, &key)) return {}; continue; } @@ -362,7 +362,7 @@ std::optional<concurrent_time_t> getUidConcurrentTimes(uint32_t uid, bool retry) 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 {}; + if (errno != ENOENT || getFirstMapKey(gConcurrentMapFd, &key)) return {}; continue; } auto offset = key.bucket * CPUS_PER_ENTRY; diff --git a/libs/dumputils/dump_utils.cpp b/libs/dumputils/dump_utils.cpp index fd557b71dd..2e144084c1 100644 --- a/libs/dumputils/dump_utils.cpp +++ b/libs/dumputils/dump_utils.cpp @@ -56,6 +56,10 @@ static const char* hal_interfaces_to_dump[] { "android.hardware.audio@4.0::IDevicesFactory", "android.hardware.audio@5.0::IDevicesFactory", "android.hardware.audio@6.0::IDevicesFactory", + "android.hardware.automotive.audiocontrol@1.0::IAudioControl", + "android.hardware.automotive.audiocontrol@2.0::IAudioControl", + "android.hardware.automotive.evs@1.0::IEvsCamera", + "android.hardware.automotive.vehicle@2.0::IVehicle", "android.hardware.biometrics.face@1.0::IBiometricsFace", "android.hardware.biometrics.fingerprint@2.1::IBiometricsFingerprint", "android.hardware.bluetooth@1.0::IBluetoothHci", @@ -67,16 +71,12 @@ static const char* hal_interfaces_to_dump[] { "android.hardware.media.c2@1.0::IComponentStore", "android.hardware.media.omx@1.0::IOmx", "android.hardware.media.omx@1.0::IOmxStore", + "android.hardware.neuralnetworks@1.0::IDevice", "android.hardware.power@1.3::IPower", "android.hardware.power.stats@1.0::IPowerStats", "android.hardware.sensors@1.0::ISensors", "android.hardware.thermal@2.0::IThermal", "android.hardware.vr@1.0::IVr", - "android.hardware.automotive.audiocontrol@1.0::IAudioControl", - "android.hardware.automotive.audiocontrol@2.0::IAudioControl", - "android.hardware.automotive.vehicle@2.0::IVehicle", - "android.hardware.automotive.evs@1.0::IEvsCamera", - "android.hardware.neuralnetworks@1.0::IDevice", NULL, }; diff --git a/libs/fakeservicemanager/Android.bp b/libs/fakeservicemanager/Android.bp index de32ff4e64..69096373fd 100644 --- a/libs/fakeservicemanager/Android.bp +++ b/libs/fakeservicemanager/Android.bp @@ -1,5 +1,6 @@ cc_defaults { name: "fakeservicemanager_defaults", + host_supported: true, srcs: [ "ServiceManager.cpp", ], diff --git a/libs/gralloc/OWNERS b/libs/gralloc/OWNERS index 67743cdb0e..4a957787cc 100644 --- a/libs/gralloc/OWNERS +++ b/libs/gralloc/OWNERS @@ -1,2 +1,2 @@ -marissaw@google.com +chrisforbes@google.com vhau@google.com diff --git a/libs/gui/BufferHubProducer.cpp b/libs/gui/BufferHubProducer.cpp index 489f3c356b..1f71e23b4d 100644 --- a/libs/gui/BufferHubProducer.cpp +++ b/libs/gui/BufferHubProducer.cpp @@ -696,7 +696,7 @@ String8 BufferHubProducer::getConsumerName() const { // relationship, thus |getConsumerName| from the producer side does not // make any sense. ALOGE("BufferHubProducer::getConsumerName not supported."); - return String8("BufferHubQueue::DummyConsumer"); + return String8("BufferHubQueue::StubConsumer"); } status_t BufferHubProducer::setSharedBufferMode(bool shared_buffer_mode) { diff --git a/libs/gui/GLConsumer.cpp b/libs/gui/GLConsumer.cpp index 59f1bcd24e..30d19e3af4 100644 --- a/libs/gui/GLConsumer.cpp +++ b/libs/gui/GLConsumer.cpp @@ -280,7 +280,7 @@ status_t GLConsumer::releaseTexImage() { mCurrentFenceTime = FenceTime::NO_FENCE; if (mAttached) { - // This binds a dummy buffer (mReleasedTexImage). + // This binds a buffer placeholder (mReleasedTexImage). status_t result = bindTextureImageLocked(); if (result != NO_ERROR) { return result; diff --git a/libs/gui/IProducerListener.cpp b/libs/gui/IProducerListener.cpp index 5c81b9d7db..0683087211 100644 --- a/libs/gui/IProducerListener.cpp +++ b/libs/gui/IProducerListener.cpp @@ -119,7 +119,7 @@ status_t BnProducerListener::onTransact(uint32_t code, const Parcel& data, return BBinder::onTransact(code, data, reply, flags); } -DummyProducerListener::~DummyProducerListener() = default; +StubProducerListener::~StubProducerListener() = default; bool BnProducerListener::needsReleaseNotify() { return true; diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index e43446ac8c..0281279d69 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -176,7 +176,12 @@ status_t layer_state_t::read(const Parcel& input) sidebandStream = NativeHandle::create(input.readNativeHandle(), true); } - colorTransform = mat4(static_cast<const float*>(input.readInplace(16 * sizeof(float)))); + const void* color_transform_data = input.readInplace(16 * sizeof(float)); + if (color_transform_data) { + colorTransform = mat4(static_cast<const float*>(color_transform_data)); + } else { + return BAD_VALUE; + } cornerRadius = input.readFloat(); backgroundBlurRadius = input.readUint32(); cachedBuffer.token = input.readStrongBinder(); diff --git a/libs/gui/OWNERS b/libs/gui/OWNERS index c13401dc5c..ecccf29351 100644 --- a/libs/gui/OWNERS +++ b/libs/gui/OWNERS @@ -1,12 +1,25 @@ adyabr@google.com akrulec@google.com alecmouri@google.com +chaviw@google.com +chrisforbes@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 +vishnun@google.com + +per-file EndToEndNativeInputTest.cpp = svv@google.com + +# BufferQueue is feature-frozen +per-file BufferQueue* = set noparent +per-file BufferQueue* = jreck@google.com, sumir@google.com, alecmouri@google.com +per-file IGraphicBuffer* = set noparent +per-file IGraphicBuffer* = jreck@google.com, sumir@google.com, alecmouri@google.com +per-file include/gui/BufferQueue* = set noparent +per-file include/gui/BufferQueue* = jreck@google.com, sumir@google.com, alecmouri@google.com +per-file include/gui/IGraphicBuffer* = set noparent +per-file include/gui/IGraphicBuffer* = jreck@google.com, sumir@google.com, alecmouri@google.com
\ No newline at end of file diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index e1a17db3d9..d6f9e635f3 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -1519,7 +1519,7 @@ bool Surface::transformToDisplayInverse() { } int Surface::connect(int api) { - static sp<IProducerListener> listener = new DummyProducerListener(); + static sp<IProducerListener> listener = new StubProducerListener(); return connect(api, listener); } diff --git a/libs/gui/bufferqueue/OWNERS b/libs/gui/bufferqueue/OWNERS index cbe931707c..615dd79c63 100644 --- a/libs/gui/bufferqueue/OWNERS +++ b/libs/gui/bufferqueue/OWNERS @@ -1,5 +1,4 @@ -chz@google.com -lajos@google.com -pawin@google.com -taklee@google.com -wonsik@google.com +# BufferQueue is feature-frozen +jreck@google.com +sumir@google.com +alecmouri@google.com
\ No newline at end of file diff --git a/libs/gui/include/gui/GLConsumer.h b/libs/gui/include/gui/GLConsumer.h index ddd868d7d1..2f538ffb86 100644 --- a/libs/gui/include/gui/GLConsumer.h +++ b/libs/gui/include/gui/GLConsumer.h @@ -499,7 +499,7 @@ private: // protects static initialization static Mutex sStaticInitLock; - // mReleasedTexImageBuffer is a dummy buffer used when in single buffer + // mReleasedTexImageBuffer is a buffer placeholder used when in single buffer // mode and releaseTexImage() has been called static sp<GraphicBuffer> sReleasedTexImageBuffer; sp<EglImage> mReleasedTexImage; diff --git a/libs/gui/include/gui/IGraphicBufferProducer.h b/libs/gui/include/gui/IGraphicBufferProducer.h index d7f34920c4..45e0a134ba 100644 --- a/libs/gui/include/gui/IGraphicBufferProducer.h +++ b/libs/gui/include/gui/IGraphicBufferProducer.h @@ -458,7 +458,7 @@ public: // the producer wants to be notified when the consumer releases a buffer // back to the BufferQueue. It is also used to detect the death of the // producer. If only the latter functionality is desired, there is a - // DummyProducerListener class in IProducerListener.h that can be used. + // StubProducerListener class in IProducerListener.h that can be used. // // The api should be one of the NATIVE_WINDOW_API_* values in <window.h> // diff --git a/libs/gui/include/gui/IProducerListener.h b/libs/gui/include/gui/IProducerListener.h index 0b1f4b5838..f7ffbb99ea 100644 --- a/libs/gui/include/gui/IProducerListener.h +++ b/libs/gui/include/gui/IProducerListener.h @@ -80,10 +80,9 @@ class IProducerListener : public ProducerListener { class BnProducerListener : public IProducerListener { }; #endif -class DummyProducerListener : public BnProducerListener -{ +class StubProducerListener : public BnProducerListener { public: - virtual ~DummyProducerListener(); + virtual ~StubProducerListener(); virtual void onBufferReleased() {} virtual bool needsReleaseNotify() { return false; } }; diff --git a/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h b/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h index 99ab0857d3..004d87574a 100644 --- a/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h +++ b/libs/gui/include/gui/bufferqueue/1.0/WGraphicBufferProducer.h @@ -51,12 +51,14 @@ typedef ::android::IGraphicBufferProducer BGraphicBufferProducer; typedef ::android::IProducerListener BProducerListener; #ifndef LOG -struct LOG_dummy { +struct LOG_stub { template <typename T> - LOG_dummy& operator<< (const T&) { return *this; } + LOG_stub& operator<<(const T&) { + return *this; + } }; -#define LOG(x) LOG_dummy() +#define LOG(x) LOG_stub() #endif // Instantiate only if HGraphicBufferProducer is base of BASE. diff --git a/libs/gui/tests/BLASTBufferQueue_test.cpp b/libs/gui/tests/BLASTBufferQueue_test.cpp index d929a59f55..da5bbdd2e6 100644 --- a/libs/gui/tests/BLASTBufferQueue_test.cpp +++ b/libs/gui/tests/BLASTBufferQueue_test.cpp @@ -128,7 +128,7 @@ protected: ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(2)); IGraphicBufferProducer::QueueBufferOutput qbOutput; ASSERT_EQ(NO_ERROR, - igbProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false, + igbProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &qbOutput)); ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint); producer = igbProducer; diff --git a/libs/gui/tests/BufferItemConsumer_test.cpp b/libs/gui/tests/BufferItemConsumer_test.cpp index b87cbbdec8..fc6551c8e6 100644 --- a/libs/gui/tests/BufferItemConsumer_test.cpp +++ b/libs/gui/tests/BufferItemConsumer_test.cpp @@ -51,7 +51,7 @@ class BufferItemConsumerTest : public ::testing::Test { mBFL = new BufferFreedListener(this); mBIC->setBufferFreedListener(mBFL); - sp<IProducerListener> producerListener = new DummyProducerListener(); + sp<IProducerListener> producerListener = new StubProducerListener(); IGraphicBufferProducer::QueueBufferOutput bufferOutput; ASSERT_EQ(NO_ERROR, mProducer->connect(producerListener, NATIVE_WINDOW_API_CPU, @@ -131,7 +131,7 @@ class BufferItemConsumerTest : public ::testing::Test { // Test that detaching buffer from consumer side triggers onBufferFreed. TEST_F(BufferItemConsumerTest, TriggerBufferFreed_DetachBufferFromConsumer) { int slot; - // Producer: generate a dummy buffer. + // Producer: generate a placeholder buffer. DequeueBuffer(&slot); QueueBuffer(slot); diff --git a/libs/gui/tests/BufferQueue_test.cpp b/libs/gui/tests/BufferQueue_test.cpp index 6d7b6bb9c6..d1208ee5ae 100644 --- a/libs/gui/tests/BufferQueue_test.cpp +++ b/libs/gui/tests/BufferQueue_test.cpp @@ -17,7 +17,7 @@ #define LOG_TAG "BufferQueue_test" //#define LOG_NDEBUG 0 -#include "DummyConsumer.h" +#include "MockConsumer.h" #include <gui/BufferItem.h> #include <gui/BufferQueue.h> @@ -134,8 +134,8 @@ TEST_F(BufferQueueTest, DISABLED_BufferQueueInAnotherProcess) { mConsumer = interface_cast<IGraphicBufferConsumer>(binderConsumer); EXPECT_TRUE(mConsumer != nullptr); - sp<DummyConsumer> dc(new DummyConsumer); - ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false)); + sp<MockConsumer> mc(new MockConsumer); + ASSERT_EQ(OK, mConsumer->consumerConnect(mc, false)); IGraphicBufferProducer::QueueBufferOutput output; ASSERT_EQ(OK, mProducer->connect(nullptr, NATIVE_WINDOW_API_CPU, false, &output)); @@ -171,23 +171,22 @@ TEST_F(BufferQueueTest, DISABLED_BufferQueueInAnotherProcess) { TEST_F(BufferQueueTest, GetMaxBufferCountInQueueBufferOutput_Succeeds) { createBufferQueue(); - sp<DummyConsumer> dc(new DummyConsumer); - mConsumer->consumerConnect(dc, false); + sp<MockConsumer> mc(new MockConsumer); + mConsumer->consumerConnect(mc, false); int bufferCount = 50; mConsumer->setMaxBufferCount(bufferCount); IGraphicBufferProducer::QueueBufferOutput output; - mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false, &output); + mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &output); ASSERT_EQ(output.maxBufferCount, bufferCount); } TEST_F(BufferQueueTest, AcquireBuffer_ExceedsMaxAcquireCount_Fails) { createBufferQueue(); - sp<DummyConsumer> dc(new DummyConsumer); - mConsumer->consumerConnect(dc, false); + sp<MockConsumer> mc(new MockConsumer); + mConsumer->consumerConnect(mc, false); IGraphicBufferProducer::QueueBufferOutput qbo; - mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false, - &qbo); + mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &qbo); mProducer->setMaxDequeuedBufferCount(3); int slot; @@ -219,15 +218,14 @@ TEST_F(BufferQueueTest, AcquireBuffer_ExceedsMaxAcquireCount_Fails) { TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithIllegalValues_ReturnsError) { createBufferQueue(); - sp<DummyConsumer> dc(new DummyConsumer); - mConsumer->consumerConnect(dc, false); + sp<MockConsumer> mc(new MockConsumer); + mConsumer->consumerConnect(mc, false); EXPECT_EQ(OK, mConsumer->setMaxBufferCount(10)); EXPECT_EQ(BAD_VALUE, mConsumer->setMaxAcquiredBufferCount(10)); IGraphicBufferProducer::QueueBufferOutput qbo; - mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false, - &qbo); + mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &qbo); mProducer->setMaxDequeuedBufferCount(3); int minBufferCount; @@ -263,12 +261,11 @@ TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithIllegalValues_ReturnsError) TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithLegalValues_Succeeds) { createBufferQueue(); - sp<DummyConsumer> dc(new DummyConsumer); - mConsumer->consumerConnect(dc, false); + sp<MockConsumer> mc(new MockConsumer); + mConsumer->consumerConnect(mc, false); IGraphicBufferProducer::QueueBufferOutput qbo; - mProducer->connect(new DummyProducerListener, NATIVE_WINDOW_API_CPU, false, - &qbo); + mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &qbo); mProducer->setMaxDequeuedBufferCount(2); int minBufferCount; @@ -310,8 +307,8 @@ TEST_F(BufferQueueTest, SetMaxAcquiredBufferCountWithLegalValues_Succeeds) { TEST_F(BufferQueueTest, SetMaxBufferCountWithLegalValues_Succeeds) { createBufferQueue(); - sp<DummyConsumer> dc(new DummyConsumer); - mConsumer->consumerConnect(dc, false); + sp<MockConsumer> mc(new MockConsumer); + mConsumer->consumerConnect(mc, false); // Test shared buffer mode EXPECT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(1)); @@ -319,8 +316,8 @@ TEST_F(BufferQueueTest, SetMaxBufferCountWithLegalValues_Succeeds) { TEST_F(BufferQueueTest, SetMaxBufferCountWithIllegalValues_ReturnsError) { createBufferQueue(); - sp<DummyConsumer> dc(new DummyConsumer); - mConsumer->consumerConnect(dc, false); + sp<MockConsumer> mc(new MockConsumer); + mConsumer->consumerConnect(mc, false); EXPECT_EQ(BAD_VALUE, mConsumer->setMaxBufferCount(0)); EXPECT_EQ(BAD_VALUE, mConsumer->setMaxBufferCount( @@ -332,11 +329,11 @@ TEST_F(BufferQueueTest, SetMaxBufferCountWithIllegalValues_ReturnsError) { TEST_F(BufferQueueTest, DetachAndReattachOnProducerSide) { createBufferQueue(); - sp<DummyConsumer> dc(new DummyConsumer); - ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false)); + sp<MockConsumer> mc(new MockConsumer); + ASSERT_EQ(OK, mConsumer->consumerConnect(mc, false)); IGraphicBufferProducer::QueueBufferOutput output; - ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, - NATIVE_WINDOW_API_CPU, false, &output)); + ASSERT_EQ(OK, + mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &output)); ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer(-1)); // Index too low ASSERT_EQ(BAD_VALUE, mProducer->detachBuffer( @@ -386,11 +383,11 @@ TEST_F(BufferQueueTest, DetachAndReattachOnProducerSide) { TEST_F(BufferQueueTest, DetachAndReattachOnConsumerSide) { createBufferQueue(); - sp<DummyConsumer> dc(new DummyConsumer); - ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false)); + sp<MockConsumer> mc(new MockConsumer); + ASSERT_EQ(OK, mConsumer->consumerConnect(mc, false)); IGraphicBufferProducer::QueueBufferOutput output; - ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, - NATIVE_WINDOW_API_CPU, false, &output)); + ASSERT_EQ(OK, + mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &output)); int slot; sp<Fence> fence; @@ -445,11 +442,11 @@ TEST_F(BufferQueueTest, DetachAndReattachOnConsumerSide) { TEST_F(BufferQueueTest, MoveFromConsumerToProducer) { createBufferQueue(); - sp<DummyConsumer> dc(new DummyConsumer); - ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false)); + sp<MockConsumer> mc(new MockConsumer); + ASSERT_EQ(OK, mConsumer->consumerConnect(mc, false)); IGraphicBufferProducer::QueueBufferOutput output; - ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, - NATIVE_WINDOW_API_CPU, false, &output)); + ASSERT_EQ(OK, + mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &output)); int slot; sp<Fence> fence; @@ -488,11 +485,11 @@ TEST_F(BufferQueueTest, MoveFromConsumerToProducer) { TEST_F(BufferQueueTest, TestDisallowingAllocation) { createBufferQueue(); - sp<DummyConsumer> dc(new DummyConsumer); - ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true)); + sp<MockConsumer> mc(new MockConsumer); + ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true)); IGraphicBufferProducer::QueueBufferOutput output; - ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, - NATIVE_WINDOW_API_CPU, true, &output)); + ASSERT_EQ(OK, + mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output)); static const uint32_t WIDTH = 320; static const uint32_t HEIGHT = 240; @@ -526,11 +523,11 @@ TEST_F(BufferQueueTest, TestDisallowingAllocation) { TEST_F(BufferQueueTest, TestGenerationNumbers) { createBufferQueue(); - sp<DummyConsumer> dc(new DummyConsumer); - ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true)); + sp<MockConsumer> mc(new MockConsumer); + ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true)); IGraphicBufferProducer::QueueBufferOutput output; - ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, - NATIVE_WINDOW_API_CPU, true, &output)); + ASSERT_EQ(OK, + mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output)); ASSERT_EQ(OK, mProducer->setGenerationNumber(1)); @@ -568,11 +565,11 @@ TEST_F(BufferQueueTest, TestGenerationNumbers) { TEST_F(BufferQueueTest, TestSharedBufferModeWithoutAutoRefresh) { createBufferQueue(); - sp<DummyConsumer> dc(new DummyConsumer); - ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true)); + sp<MockConsumer> mc(new MockConsumer); + ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true)); IGraphicBufferProducer::QueueBufferOutput output; - ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, - NATIVE_WINDOW_API_CPU, true, &output)); + ASSERT_EQ(OK, + mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output)); ASSERT_EQ(OK, mProducer->setSharedBufferMode(true)); @@ -618,11 +615,11 @@ TEST_F(BufferQueueTest, TestSharedBufferModeWithoutAutoRefresh) { TEST_F(BufferQueueTest, TestSharedBufferModeWithAutoRefresh) { createBufferQueue(); - sp<DummyConsumer> dc(new DummyConsumer); - ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true)); + sp<MockConsumer> mc(new MockConsumer); + ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true)); IGraphicBufferProducer::QueueBufferOutput output; - ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, - NATIVE_WINDOW_API_CPU, true, &output)); + ASSERT_EQ(OK, + mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output)); ASSERT_EQ(OK, mProducer->setSharedBufferMode(true)); ASSERT_EQ(OK, mProducer->setAutoRefresh(true)); @@ -687,11 +684,11 @@ TEST_F(BufferQueueTest, TestSharedBufferModeWithAutoRefresh) { TEST_F(BufferQueueTest, TestSharedBufferModeUsingAlreadyDequeuedBuffer) { createBufferQueue(); - sp<DummyConsumer> dc(new DummyConsumer); - ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true)); + sp<MockConsumer> mc(new MockConsumer); + ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true)); IGraphicBufferProducer::QueueBufferOutput output; - ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, - NATIVE_WINDOW_API_CPU, true, &output)); + ASSERT_EQ(OK, + mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output)); // Dequeue a buffer int sharedSlot; @@ -738,11 +735,11 @@ TEST_F(BufferQueueTest, TestSharedBufferModeUsingAlreadyDequeuedBuffer) { TEST_F(BufferQueueTest, TestTimeouts) { createBufferQueue(); - sp<DummyConsumer> dc(new DummyConsumer); - ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true)); + sp<MockConsumer> mc(new MockConsumer); + ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true)); IGraphicBufferProducer::QueueBufferOutput output; - ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, - NATIVE_WINDOW_API_CPU, true, &output)); + ASSERT_EQ(OK, + mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output)); // Fill up the queue. Since the controlledByApp flags are set to true, this // queue should be in non-blocking mode, and we should be recycling the same @@ -800,11 +797,11 @@ TEST_F(BufferQueueTest, TestTimeouts) { TEST_F(BufferQueueTest, CanAttachWhileDisallowingAllocation) { createBufferQueue(); - sp<DummyConsumer> dc(new DummyConsumer); - ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true)); + sp<MockConsumer> mc(new MockConsumer); + ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true)); IGraphicBufferProducer::QueueBufferOutput output; - ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, - NATIVE_WINDOW_API_CPU, true, &output)); + ASSERT_EQ(OK, + mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output)); int slot = BufferQueue::INVALID_BUFFER_SLOT; sp<Fence> sourceFence; @@ -822,11 +819,11 @@ TEST_F(BufferQueueTest, CanAttachWhileDisallowingAllocation) { TEST_F(BufferQueueTest, CanRetrieveLastQueuedBuffer) { createBufferQueue(); - sp<DummyConsumer> dc(new DummyConsumer); - ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false)); + sp<MockConsumer> mc(new MockConsumer); + ASSERT_EQ(OK, mConsumer->consumerConnect(mc, false)); IGraphicBufferProducer::QueueBufferOutput output; - ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, - NATIVE_WINDOW_API_CPU, false, &output)); + ASSERT_EQ(OK, + mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &output)); // Dequeue and queue the first buffer, storing the handle int slot = BufferQueue::INVALID_BUFFER_SLOT; @@ -876,11 +873,11 @@ TEST_F(BufferQueueTest, CanRetrieveLastQueuedBuffer) { TEST_F(BufferQueueTest, TestOccupancyHistory) { createBufferQueue(); - sp<DummyConsumer> dc(new DummyConsumer); - ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false)); + sp<MockConsumer> mc(new MockConsumer); + ASSERT_EQ(OK, mConsumer->consumerConnect(mc, false)); IGraphicBufferProducer::QueueBufferOutput output; - ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, - NATIVE_WINDOW_API_CPU, false, &output)); + ASSERT_EQ(OK, + mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, &output)); int slot = BufferQueue::INVALID_BUFFER_SLOT; sp<Fence> fence = Fence::NO_FENCE; @@ -1030,8 +1027,8 @@ private: TEST_F(BufferQueueTest, TestDiscardFreeBuffers) { createBufferQueue(); - sp<DummyConsumer> dc(new DummyConsumer); - ASSERT_EQ(OK, mConsumer->consumerConnect(dc, false)); + sp<MockConsumer> mc(new MockConsumer); + ASSERT_EQ(OK, mConsumer->consumerConnect(mc, false)); IGraphicBufferProducer::QueueBufferOutput output; sp<BufferDiscardedListener> pl(new BufferDiscardedListener); ASSERT_EQ(OK, mProducer->connect(pl, @@ -1115,11 +1112,11 @@ TEST_F(BufferQueueTest, TestDiscardFreeBuffers) { TEST_F(BufferQueueTest, TestBufferReplacedInQueueBuffer) { createBufferQueue(); - sp<DummyConsumer> dc(new DummyConsumer); - ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true)); + sp<MockConsumer> mc(new MockConsumer); + ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true)); IGraphicBufferProducer::QueueBufferOutput output; - ASSERT_EQ(OK, mProducer->connect(new DummyProducerListener, - NATIVE_WINDOW_API_CPU, true, &output)); + ASSERT_EQ(OK, + mProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, true, &output)); ASSERT_EQ(OK, mConsumer->setMaxAcquiredBufferCount(1)); int slot = BufferQueue::INVALID_BUFFER_SLOT; @@ -1156,12 +1153,11 @@ TEST_F(BufferQueueTest, TestBufferReplacedInQueueBuffer) { TEST_F(BufferQueueTest, TestStaleBufferHandleSentAfterDisconnect) { createBufferQueue(); - sp<DummyConsumer> dc(new DummyConsumer); - ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true)); + sp<MockConsumer> mc(new MockConsumer); + ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true)); IGraphicBufferProducer::QueueBufferOutput output; - sp<IProducerListener> dummyListener(new DummyProducerListener); - ASSERT_EQ(OK, mProducer->connect(dummyListener, NATIVE_WINDOW_API_CPU, - true, &output)); + sp<IProducerListener> fakeListener(new StubProducerListener); + ASSERT_EQ(OK, mProducer->connect(fakeListener, NATIVE_WINDOW_API_CPU, true, &output)); int slot = BufferQueue::INVALID_BUFFER_SLOT; sp<Fence> fence = Fence::NO_FENCE; @@ -1215,15 +1211,13 @@ TEST_F(BufferQueueTest, TestStaleBufferHandleSentAfterDisconnect) { TEST_F(BufferQueueTest, TestProducerConnectDisconnect) { createBufferQueue(); - sp<DummyConsumer> dc(new DummyConsumer); - ASSERT_EQ(OK, mConsumer->consumerConnect(dc, true)); + sp<MockConsumer> mc(new MockConsumer); + ASSERT_EQ(OK, mConsumer->consumerConnect(mc, true)); IGraphicBufferProducer::QueueBufferOutput output; - sp<IProducerListener> dummyListener(new DummyProducerListener); + sp<IProducerListener> fakeListener(new StubProducerListener); ASSERT_EQ(NO_INIT, mProducer->disconnect(NATIVE_WINDOW_API_CPU)); - ASSERT_EQ(OK, mProducer->connect( - dummyListener, NATIVE_WINDOW_API_CPU, true, &output)); - ASSERT_EQ(BAD_VALUE, mProducer->connect( - dummyListener, NATIVE_WINDOW_API_MEDIA, true, &output)); + ASSERT_EQ(OK, mProducer->connect(fakeListener, NATIVE_WINDOW_API_CPU, true, &output)); + ASSERT_EQ(BAD_VALUE, mProducer->connect(fakeListener, NATIVE_WINDOW_API_MEDIA, true, &output)); ASSERT_EQ(BAD_VALUE, mProducer->disconnect(NATIVE_WINDOW_API_MEDIA)); ASSERT_EQ(OK, mProducer->disconnect(NATIVE_WINDOW_API_CPU)); diff --git a/libs/gui/tests/IGraphicBufferProducer_test.cpp b/libs/gui/tests/IGraphicBufferProducer_test.cpp index 103f7753c8..15bd32d354 100644 --- a/libs/gui/tests/IGraphicBufferProducer_test.cpp +++ b/libs/gui/tests/IGraphicBufferProducer_test.cpp @@ -17,7 +17,7 @@ #define LOG_TAG "IGraphicBufferProducer_test" //#define LOG_NDEBUG 0 -#include "DummyConsumer.h" +#include "MockConsumer.h" #include <gtest/gtest.h> @@ -89,7 +89,7 @@ protected: ALOGV("Begin test: %s.%s", testInfo->test_case_name(), testInfo->name()); - mDC = new DummyConsumer; + mMC = new MockConsumer; switch (GetParam()) { case USE_BUFFER_QUEUE_PRODUCER: { @@ -114,7 +114,7 @@ protected: } // Must connect consumer before producer connects will succeed. - ASSERT_OK(mConsumer->consumerConnect(mDC, /*controlledByApp*/false)); + ASSERT_OK(mConsumer->consumerConnect(mMC, /*controlledByApp*/ false)); } virtual void TearDown() { @@ -249,7 +249,7 @@ protected: } private: // hide from test body - sp<DummyConsumer> mDC; + sp<MockConsumer> mMC; protected: // accessible from test body sp<IGraphicBufferProducer> mProducer; diff --git a/libs/gui/tests/Malicious.cpp b/libs/gui/tests/Malicious.cpp index acd42979c2..58d7cc6f35 100644 --- a/libs/gui/tests/Malicious.cpp +++ b/libs/gui/tests/Malicious.cpp @@ -129,7 +129,7 @@ private: int32_t mExpectedSlot = 0; }; -class DummyListener : public BnConsumerListener { +class FakeListener : public BnConsumerListener { public: void onFrameAvailable(const BufferItem&) override {} void onBuffersReleased() override {} @@ -140,7 +140,7 @@ sp<MaliciousBQP> getMaliciousBQP() { sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); - sp<IConsumerListener> listener = new DummyListener; + sp<IConsumerListener> listener = new FakeListener; consumer->consumerConnect(listener, false); sp<MaliciousBQP> malicious = new MaliciousBQP(producer); diff --git a/libs/gui/tests/DummyConsumer.h b/libs/gui/tests/MockConsumer.h index 502bdf981b..4a6c51c17d 100644 --- a/libs/gui/tests/DummyConsumer.h +++ b/libs/gui/tests/MockConsumer.h @@ -18,7 +18,7 @@ namespace android { -struct DummyConsumer : public BnConsumerListener { +struct MockConsumer : public BnConsumerListener { void onFrameAvailable(const BufferItem& /* item */) override {} void onBuffersReleased() override {} void onSidebandStreamChanged() override {} diff --git a/libs/gui/tests/StreamSplitter_test.cpp b/libs/gui/tests/StreamSplitter_test.cpp index ad6e051684..b65cddaea3 100644 --- a/libs/gui/tests/StreamSplitter_test.cpp +++ b/libs/gui/tests/StreamSplitter_test.cpp @@ -48,7 +48,7 @@ protected: } }; -struct DummyListener : public BnConsumerListener { +struct FakeListener : public BnConsumerListener { virtual void onFrameAvailable(const BufferItem& /* item */) {} virtual void onBuffersReleased() {} virtual void onSidebandStreamChanged() {} @@ -64,7 +64,7 @@ TEST_F(StreamSplitterTest, OneInputOneOutput) { sp<IGraphicBufferProducer> outputProducer; sp<IGraphicBufferConsumer> outputConsumer; BufferQueue::createBufferQueue(&outputProducer, &outputConsumer); - ASSERT_EQ(OK, outputConsumer->consumerConnect(new DummyListener, false)); + ASSERT_EQ(OK, outputConsumer->consumerConnect(new FakeListener, false)); sp<StreamSplitter> splitter; status_t status = StreamSplitter::createSplitter(inputConsumer, &splitter); @@ -75,8 +75,9 @@ TEST_F(StreamSplitterTest, OneInputOneOutput) { ASSERT_EQ(OK, outputProducer->allowAllocation(false)); IGraphicBufferProducer::QueueBufferOutput qbOutput; - ASSERT_EQ(OK, inputProducer->connect(new DummyProducerListener, - NATIVE_WINDOW_API_CPU, false, &qbOutput)); + ASSERT_EQ(OK, + inputProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, + &qbOutput)); int slot; sp<Fence> fence; @@ -132,8 +133,7 @@ TEST_F(StreamSplitterTest, OneInputMultipleOutputs) { for (int output = 0; output < NUM_OUTPUTS; ++output) { BufferQueue::createBufferQueue(&outputProducers[output], &outputConsumers[output]); - ASSERT_EQ(OK, outputConsumers[output]->consumerConnect( - new DummyListener, false)); + ASSERT_EQ(OK, outputConsumers[output]->consumerConnect(new FakeListener, false)); } sp<StreamSplitter> splitter; @@ -147,8 +147,9 @@ TEST_F(StreamSplitterTest, OneInputMultipleOutputs) { } IGraphicBufferProducer::QueueBufferOutput qbOutput; - ASSERT_EQ(OK, inputProducer->connect(new DummyProducerListener, - NATIVE_WINDOW_API_CPU, false, &qbOutput)); + ASSERT_EQ(OK, + inputProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, + &qbOutput)); int slot; sp<Fence> fence; @@ -203,7 +204,7 @@ TEST_F(StreamSplitterTest, OutputAbandonment) { sp<IGraphicBufferProducer> outputProducer; sp<IGraphicBufferConsumer> outputConsumer; BufferQueue::createBufferQueue(&outputProducer, &outputConsumer); - ASSERT_EQ(OK, outputConsumer->consumerConnect(new DummyListener, false)); + ASSERT_EQ(OK, outputConsumer->consumerConnect(new FakeListener, false)); sp<StreamSplitter> splitter; status_t status = StreamSplitter::createSplitter(inputConsumer, &splitter); @@ -211,8 +212,9 @@ TEST_F(StreamSplitterTest, OutputAbandonment) { ASSERT_EQ(OK, splitter->addOutput(outputProducer)); IGraphicBufferProducer::QueueBufferOutput qbOutput; - ASSERT_EQ(OK, inputProducer->connect(new DummyProducerListener, - NATIVE_WINDOW_API_CPU, false, &qbOutput)); + ASSERT_EQ(OK, + inputProducer->connect(new StubProducerListener, NATIVE_WINDOW_API_CPU, false, + &qbOutput)); int slot; sp<Fence> fence; diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp index c85e84489d..c7458a3755 100644 --- a/libs/gui/tests/SurfaceTextureClient_test.cpp +++ b/libs/gui/tests/SurfaceTextureClient_test.cpp @@ -54,7 +54,7 @@ protected: mANW = mSTC; // We need a valid GL context so we can test updateTexImage() - // This initializes EGL and create a dummy GL context with a + // This initializes EGL and create a GL context placeholder with a // pbuffer render target. mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); ASSERT_EQ(EGL_SUCCESS, eglGetError()); diff --git a/libs/gui/tests/Surface_test.cpp b/libs/gui/tests/Surface_test.cpp index 9906166c67..592913c46b 100644 --- a/libs/gui/tests/Surface_test.cpp +++ b/libs/gui/tests/Surface_test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "DummyConsumer.h" +#include "MockConsumer.h" #include <gtest/gtest.h> @@ -56,12 +56,11 @@ class FakeProducerFrameEventHistory; static constexpr uint64_t NO_FRAME_INDEX = std::numeric_limits<uint64_t>::max(); -class DummySurfaceListener : public SurfaceListener { +class FakeSurfaceListener : public SurfaceListener { public: - DummySurfaceListener(bool enableReleasedCb = false) : - mEnableReleaseCb(enableReleasedCb), - mBuffersReleased(0) {} - virtual ~DummySurfaceListener() = default; + FakeSurfaceListener(bool enableReleasedCb = false) + : mEnableReleaseCb(enableReleasedCb), mBuffersReleased(0) {} + virtual ~FakeSurfaceListener() = default; virtual void onBufferReleased() { mBuffersReleased++; @@ -124,15 +123,15 @@ protected: sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); - sp<DummyConsumer> dummyConsumer(new DummyConsumer); - consumer->consumerConnect(dummyConsumer, false); + sp<MockConsumer> mockConsumer(new MockConsumer); + consumer->consumerConnect(mockConsumer, false); consumer->setConsumerName(String8("TestConsumer")); sp<Surface> surface = new Surface(producer); sp<ANativeWindow> window(surface); - sp<DummySurfaceListener> listener; + sp<FakeSurfaceListener> listener; if (hasSurfaceListener) { - listener = new DummySurfaceListener(enableReleasedCb); + listener = new FakeSurfaceListener(enableReleasedCb); } ASSERT_EQ(OK, surface->connect( NATIVE_WINDOW_API_CPU, @@ -381,8 +380,8 @@ TEST_F(SurfaceTest, GetConsumerName) { sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); - sp<DummyConsumer> dummyConsumer(new DummyConsumer); - consumer->consumerConnect(dummyConsumer, false); + sp<MockConsumer> mockConsumer(new MockConsumer); + consumer->consumerConnect(mockConsumer, false); consumer->setConsumerName(String8("TestConsumer")); sp<Surface> surface = new Surface(producer); @@ -397,8 +396,8 @@ TEST_F(SurfaceTest, GetWideColorSupport) { sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); - sp<DummyConsumer> dummyConsumer(new DummyConsumer); - consumer->consumerConnect(dummyConsumer, false); + sp<MockConsumer> mockConsumer(new MockConsumer); + consumer->consumerConnect(mockConsumer, false); consumer->setConsumerName(String8("TestConsumer")); sp<Surface> surface = new Surface(producer); @@ -428,8 +427,8 @@ TEST_F(SurfaceTest, GetHdrSupport) { sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); - sp<DummyConsumer> dummyConsumer(new DummyConsumer); - consumer->consumerConnect(dummyConsumer, false); + sp<MockConsumer> mockConsumer(new MockConsumer); + consumer->consumerConnect(mockConsumer, false); consumer->setConsumerName(String8("TestConsumer")); sp<Surface> surface = new Surface(producer); @@ -452,8 +451,8 @@ TEST_F(SurfaceTest, SetHdrMetadata) { sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); - sp<DummyConsumer> dummyConsumer(new DummyConsumer); - consumer->consumerConnect(dummyConsumer, false); + sp<MockConsumer> mockConsumer(new MockConsumer); + consumer->consumerConnect(mockConsumer, false); consumer->setConsumerName(String8("TestConsumer")); sp<Surface> surface = new Surface(producer); @@ -497,8 +496,8 @@ TEST_F(SurfaceTest, DynamicSetBufferCount) { sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); - sp<DummyConsumer> dummyConsumer(new DummyConsumer); - consumer->consumerConnect(dummyConsumer, false); + sp<MockConsumer> mockConsumer(new MockConsumer); + consumer->consumerConnect(mockConsumer, false); consumer->setConsumerName(String8("TestConsumer")); sp<Surface> surface = new Surface(producer); @@ -523,13 +522,13 @@ TEST_F(SurfaceTest, GetAndFlushRemovedBuffers) { sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); - sp<DummyConsumer> dummyConsumer(new DummyConsumer); - consumer->consumerConnect(dummyConsumer, false); + sp<MockConsumer> mockConsumer(new MockConsumer); + consumer->consumerConnect(mockConsumer, false); consumer->setConsumerName(String8("TestConsumer")); sp<Surface> surface = new Surface(producer); sp<ANativeWindow> window(surface); - sp<DummyProducerListener> listener = new DummyProducerListener(); + sp<StubProducerListener> listener = new StubProducerListener(); ASSERT_EQ(OK, surface->connect( NATIVE_WINDOW_API_CPU, /*listener*/listener, @@ -1910,8 +1909,8 @@ TEST_F(SurfaceTest, DequeueWithConsumerDrivenSize) { sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); - sp<DummyConsumer> dummyConsumer(new DummyConsumer); - consumer->consumerConnect(dummyConsumer, false); + sp<MockConsumer> mockConsumer(new MockConsumer); + consumer->consumerConnect(mockConsumer, false); consumer->setDefaultBufferSize(10, 10); sp<Surface> surface = new Surface(producer); @@ -1980,8 +1979,8 @@ TEST_F(SurfaceTest, DefaultMaxBufferCountSetAndUpdated) { sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); - sp<DummyConsumer> dummyConsumer(new DummyConsumer); - consumer->consumerConnect(dummyConsumer, false); + sp<MockConsumer> mockConsumer(new MockConsumer); + consumer->consumerConnect(mockConsumer, false); sp<Surface> surface = new Surface(producer); sp<ANativeWindow> window(surface); diff --git a/libs/input/Android.bp b/libs/input/Android.bp index 8efaf3d90b..6a5d434515 100644 --- a/libs/input/Android.bp +++ b/libs/input/Android.bp @@ -28,12 +28,16 @@ cc_library { "Keyboard.cpp", "KeyCharacterMap.cpp", "KeyLayoutMap.cpp", + "PropertyMap.cpp", "TouchVideoFrame.cpp", "VirtualKeyMap.cpp", ], clang: true, + header_libs: ["jni_headers"], + export_header_lib_headers: ["jni_headers"], + shared_libs: [ "libbase", "liblog", @@ -71,4 +75,23 @@ cc_library { }, } +cc_defaults { + name: "libinput_fuzz_defaults", + host_supported: true, + shared_libs: [ + "libutils", + "libbase", + "liblog", + ], +} + +cc_fuzz { + name: "libinput_fuzz_propertymap", + defaults: ["libinput_fuzz_defaults"], + srcs: [ + "PropertyMap.cpp", + "PropertyMap_fuzz.cpp", + ], +} + subdirs = ["tests"] diff --git a/libs/input/PropertyMap.cpp b/libs/input/PropertyMap.cpp new file mode 100644 index 0000000000..4833eb9c05 --- /dev/null +++ b/libs/input/PropertyMap.cpp @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2008 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 "PropertyMap" + +#include <input/PropertyMap.h> + +// Enables debug output for the parser. +#define DEBUG_PARSER 0 + +// Enables debug output for parser performance. +#define DEBUG_PARSER_PERFORMANCE 0 + +namespace android { + +static const char* WHITESPACE = " \t\r"; +static const char* WHITESPACE_OR_PROPERTY_DELIMITER = " \t\r="; + +// --- PropertyMap --- + +PropertyMap::PropertyMap() {} + +PropertyMap::~PropertyMap() {} + +void PropertyMap::clear() { + mProperties.clear(); +} + +void PropertyMap::addProperty(const String8& key, const String8& value) { + mProperties.add(key, value); +} + +bool PropertyMap::hasProperty(const String8& key) const { + return mProperties.indexOfKey(key) >= 0; +} + +bool PropertyMap::tryGetProperty(const String8& key, String8& outValue) const { + ssize_t index = mProperties.indexOfKey(key); + if (index < 0) { + return false; + } + + outValue = mProperties.valueAt(index); + return true; +} + +bool PropertyMap::tryGetProperty(const String8& key, bool& outValue) const { + int32_t intValue; + if (!tryGetProperty(key, intValue)) { + return false; + } + + outValue = intValue; + return true; +} + +bool PropertyMap::tryGetProperty(const String8& key, int32_t& outValue) const { + String8 stringValue; + if (!tryGetProperty(key, stringValue) || stringValue.length() == 0) { + return false; + } + + char* end; + int value = strtol(stringValue.string(), &end, 10); + if (*end != '\0') { + ALOGW("Property key '%s' has invalid value '%s'. Expected an integer.", key.string(), + stringValue.string()); + return false; + } + outValue = value; + return true; +} + +bool PropertyMap::tryGetProperty(const String8& key, float& outValue) const { + String8 stringValue; + if (!tryGetProperty(key, stringValue) || stringValue.length() == 0) { + return false; + } + + char* end; + float value = strtof(stringValue.string(), &end); + if (*end != '\0') { + ALOGW("Property key '%s' has invalid value '%s'. Expected a float.", key.string(), + stringValue.string()); + return false; + } + outValue = value; + return true; +} + +void PropertyMap::addAll(const PropertyMap* map) { + for (size_t i = 0; i < map->mProperties.size(); i++) { + mProperties.add(map->mProperties.keyAt(i), map->mProperties.valueAt(i)); + } +} + +status_t PropertyMap::load(const String8& filename, PropertyMap** outMap) { + *outMap = nullptr; + + Tokenizer* tokenizer; + status_t status = Tokenizer::open(filename, &tokenizer); + if (status) { + ALOGE("Error %d opening property file %s.", status, filename.string()); + } else { + PropertyMap* map = new PropertyMap(); + if (!map) { + ALOGE("Error allocating property map."); + status = NO_MEMORY; + } else { +#if DEBUG_PARSER_PERFORMANCE + nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); +#endif + Parser parser(map, tokenizer); + status = parser.parse(); +#if DEBUG_PARSER_PERFORMANCE + nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; + ALOGD("Parsed property file '%s' %d lines in %0.3fms.", + tokenizer->getFilename().string(), tokenizer->getLineNumber(), + elapsedTime / 1000000.0); +#endif + if (status) { + delete map; + } else { + *outMap = map; + } + } + delete tokenizer; + } + return status; +} + +// --- PropertyMap::Parser --- + +PropertyMap::Parser::Parser(PropertyMap* map, Tokenizer* tokenizer) + : mMap(map), mTokenizer(tokenizer) {} + +PropertyMap::Parser::~Parser() {} + +status_t PropertyMap::Parser::parse() { + while (!mTokenizer->isEof()) { +#if DEBUG_PARSER + ALOGD("Parsing %s: '%s'.", mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); +#endif + + mTokenizer->skipDelimiters(WHITESPACE); + + if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { + String8 keyToken = mTokenizer->nextToken(WHITESPACE_OR_PROPERTY_DELIMITER); + if (keyToken.isEmpty()) { + ALOGE("%s: Expected non-empty property key.", mTokenizer->getLocation().string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + + if (mTokenizer->nextChar() != '=') { + ALOGE("%s: Expected '=' between property key and value.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + + String8 valueToken = mTokenizer->nextToken(WHITESPACE); + if (valueToken.find("\\", 0) >= 0 || valueToken.find("\"", 0) >= 0) { + ALOGE("%s: Found reserved character '\\' or '\"' in property value.", + mTokenizer->getLocation().string()); + return BAD_VALUE; + } + + mTokenizer->skipDelimiters(WHITESPACE); + if (!mTokenizer->isEol()) { + ALOGE("%s: Expected end of line, got '%s'.", mTokenizer->getLocation().string(), + mTokenizer->peekRemainderOfLine().string()); + return BAD_VALUE; + } + + if (mMap->hasProperty(keyToken)) { + ALOGE("%s: Duplicate property value for key '%s'.", + mTokenizer->getLocation().string(), keyToken.string()); + return BAD_VALUE; + } + + mMap->addProperty(keyToken, valueToken); + } + + mTokenizer->nextLine(); + } + return OK; +} + +} // namespace android diff --git a/libs/input/PropertyMap_fuzz.cpp b/libs/input/PropertyMap_fuzz.cpp new file mode 100755 index 0000000000..23ead0e469 --- /dev/null +++ b/libs/input/PropertyMap_fuzz.cpp @@ -0,0 +1,76 @@ +/* + * Copyright 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-base/file.h" +#include "fuzzer/FuzzedDataProvider.h" +#include "input/PropertyMap.h" +#include "utils/String8.h" + +static constexpr int MAX_FILE_SIZE = 256; +static constexpr int MAX_STR_LEN = 2048; +static constexpr int MAX_OPERATIONS = 1000; + +static const std::vector<std::function<void(FuzzedDataProvider*, android::PropertyMap)>> + operations = { + [](FuzzedDataProvider*, android::PropertyMap propertyMap) -> void { + propertyMap.getProperties(); + }, + [](FuzzedDataProvider*, android::PropertyMap propertyMap) -> void { + propertyMap.clear(); + }, + [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void { + std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN); + android::String8 key = android::String8(keyStr.c_str()); + propertyMap.hasProperty(key); + }, + [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void { + std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN); + android::String8 key = android::String8(keyStr.c_str()); + android::String8 out; + propertyMap.tryGetProperty(key, out); + }, + [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void { + TemporaryFile tf; + // Generate file contents + std::string contents = dataProvider->ConsumeRandomLengthString(MAX_FILE_SIZE); + // If we have string contents, dump them into the file. + // Otherwise, just leave it as an empty file. + if (contents.length() > 0) { + const char* bytes = contents.c_str(); + android::base::WriteStringToFd(bytes, tf.fd); + } + android::PropertyMap* mapPtr = &propertyMap; + android::PropertyMap::load(android::String8(tf.path), &mapPtr); + }, + [](FuzzedDataProvider* dataProvider, android::PropertyMap propertyMap) -> void { + std::string keyStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN); + std::string valStr = dataProvider->ConsumeRandomLengthString(MAX_STR_LEN); + android::String8 key = android::String8(keyStr.c_str()); + android::String8 val = android::String8(valStr.c_str()); + propertyMap.addProperty(key, val); + }, +}; +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FuzzedDataProvider dataProvider(data, size); + android::PropertyMap proprtyMap = android::PropertyMap(); + + int opsRun = 0; + while (dataProvider.remaining_bytes() > 0 && opsRun++ < MAX_OPERATIONS) { + uint8_t op = dataProvider.ConsumeIntegralInRange<uint8_t>(0, operations.size() - 1); + operations[op](&dataProvider, proprtyMap); + } + return 0; +} diff --git a/libs/renderengine/OWNERS b/libs/renderengine/OWNERS index c00fbbaad2..b44456b705 100644 --- a/libs/renderengine/OWNERS +++ b/libs/renderengine/OWNERS @@ -1,2 +1,4 @@ +alecmouri@google.com +jreck@google.com lpy@google.com stoza@google.com diff --git a/libs/renderengine/gl/GLESRenderEngine.cpp b/libs/renderengine/gl/GLESRenderEngine.cpp index 0285c2f6f0..af973673d8 100644 --- a/libs/renderengine/gl/GLESRenderEngine.cpp +++ b/libs/renderengine/gl/GLESRenderEngine.cpp @@ -245,22 +245,22 @@ std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(const RenderEngineCre // if can't create a GL context, we can only abort. LOG_ALWAYS_FATAL_IF(ctxt == EGL_NO_CONTEXT, "EGLContext creation failed"); - EGLSurface dummy = EGL_NO_SURFACE; + EGLSurface stub = EGL_NO_SURFACE; if (!extensions.hasSurfacelessContext()) { - dummy = createDummyEglPbufferSurface(display, config, args.pixelFormat, - Protection::UNPROTECTED); - LOG_ALWAYS_FATAL_IF(dummy == EGL_NO_SURFACE, "can't create dummy pbuffer"); + stub = createStubEglPbufferSurface(display, config, args.pixelFormat, + Protection::UNPROTECTED); + LOG_ALWAYS_FATAL_IF(stub == EGL_NO_SURFACE, "can't create stub pbuffer"); } - EGLBoolean success = eglMakeCurrent(display, dummy, dummy, ctxt); - LOG_ALWAYS_FATAL_IF(!success, "can't make dummy pbuffer current"); + EGLBoolean success = eglMakeCurrent(display, stub, stub, ctxt); + LOG_ALWAYS_FATAL_IF(!success, "can't make stub pbuffer current"); extensions.initWithGLStrings(glGetString(GL_VENDOR), glGetString(GL_RENDERER), glGetString(GL_VERSION), glGetString(GL_EXTENSIONS)); - EGLSurface protectedDummy = EGL_NO_SURFACE; + EGLSurface protectedStub = EGL_NO_SURFACE; if (protectedContext != EGL_NO_CONTEXT && !extensions.hasSurfacelessContext()) { - protectedDummy = createDummyEglPbufferSurface(display, config, args.pixelFormat, - Protection::PROTECTED); - ALOGE_IF(protectedDummy == EGL_NO_SURFACE, "can't create protected dummy pbuffer"); + protectedStub = createStubEglPbufferSurface(display, config, args.pixelFormat, + Protection::PROTECTED); + ALOGE_IF(protectedStub == EGL_NO_SURFACE, "can't create protected stub pbuffer"); } // now figure out what version of GL did we actually get @@ -278,8 +278,8 @@ std::unique_ptr<GLESRenderEngine> GLESRenderEngine::create(const RenderEngineCre break; case GLES_VERSION_2_0: case GLES_VERSION_3_0: - engine = std::make_unique<GLESRenderEngine>(args, display, config, ctxt, dummy, - protectedContext, protectedDummy); + engine = std::make_unique<GLESRenderEngine>(args, display, config, ctxt, stub, + protectedContext, protectedStub); break; } @@ -334,15 +334,15 @@ EGLConfig GLESRenderEngine::chooseEglConfig(EGLDisplay display, int format, bool } GLESRenderEngine::GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, - EGLConfig config, EGLContext ctxt, EGLSurface dummy, - EGLContext protectedContext, EGLSurface protectedDummy) + EGLConfig config, EGLContext ctxt, EGLSurface stub, + EGLContext protectedContext, EGLSurface protectedStub) : renderengine::impl::RenderEngine(args), mEGLDisplay(display), mEGLConfig(config), mEGLContext(ctxt), - mDummySurface(dummy), + mStubSurface(stub), mProtectedEGLContext(protectedContext), - mProtectedDummySurface(protectedDummy), + mProtectedStubSurface(protectedStub), mVpWidth(0), mVpHeight(0), mFramebufferImageCacheSize(args.imageCacheSize), @@ -355,12 +355,12 @@ GLESRenderEngine::GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisp // Initialize protected EGL Context. if (mProtectedEGLContext != EGL_NO_CONTEXT) { - EGLBoolean success = eglMakeCurrent(display, mProtectedDummySurface, mProtectedDummySurface, + EGLBoolean success = eglMakeCurrent(display, mProtectedStubSurface, mProtectedStubSurface, mProtectedEGLContext); ALOGE_IF(!success, "can't make protected context current"); glPixelStorei(GL_UNPACK_ALIGNMENT, 4); glPixelStorei(GL_PACK_ALIGNMENT, 4); - success = eglMakeCurrent(display, mDummySurface, mDummySurface, mEGLContext); + success = eglMakeCurrent(display, mStubSurface, mStubSurface, mEGLContext); LOG_ALWAYS_FATAL_IF(!success, "can't make default context current"); } @@ -942,7 +942,7 @@ bool GLESRenderEngine::cleanupPostRender(CleanupMode mode) { } } - // Bind the texture to dummy data so that backing image data can be freed. + // Bind the texture to placeholder so that backing image data can be freed. GLFramebuffer* glFramebuffer = static_cast<GLFramebuffer*>(getFramebufferForDrawing()); glFramebuffer->allocateBuffers(1, 1, mPlaceholderDrawBuffer); // Release the cached fence here, so that we don't churn reallocations when @@ -980,7 +980,7 @@ bool GLESRenderEngine::useProtectedContext(bool useProtectedContext) { if (useProtectedContext && mProtectedEGLContext == EGL_NO_CONTEXT) { return false; } - const EGLSurface surface = useProtectedContext ? mProtectedDummySurface : mDummySurface; + const EGLSurface surface = useProtectedContext ? mProtectedStubSurface : mStubSurface; const EGLContext context = useProtectedContext ? mProtectedEGLContext : mEGLContext; const bool success = eglMakeCurrent(mEGLDisplay, surface, surface, context) == EGL_TRUE; if (success) { @@ -1274,7 +1274,8 @@ void GLESRenderEngine::setupLayerBlending(bool premultipliedAlpha, bool opaque, if (color.a < 1.0f || !opaque || cornerRadius > 0.0f) { glEnable(GL_BLEND); - glBlendFunc(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBlendFuncSeparate(premultipliedAlpha ? GL_ONE : GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, + GL_ONE, GL_ONE_MINUS_SRC_ALPHA); } else { glDisable(GL_BLEND); } @@ -1608,11 +1609,11 @@ EGLContext GLESRenderEngine::createEglContext(EGLDisplay display, EGLConfig conf return context; } -EGLSurface GLESRenderEngine::createDummyEglPbufferSurface(EGLDisplay display, EGLConfig config, - int hwcFormat, Protection protection) { - EGLConfig dummyConfig = config; - if (dummyConfig == EGL_NO_CONFIG) { - dummyConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true); +EGLSurface GLESRenderEngine::createStubEglPbufferSurface(EGLDisplay display, EGLConfig config, + int hwcFormat, Protection protection) { + EGLConfig stubConfig = config; + if (stubConfig == EGL_NO_CONFIG) { + stubConfig = chooseEglConfig(display, hwcFormat, /*logConfig*/ true); } std::vector<EGLint> attributes; attributes.reserve(7); @@ -1626,7 +1627,7 @@ EGLSurface GLESRenderEngine::createDummyEglPbufferSurface(EGLDisplay display, EG } attributes.push_back(EGL_NONE); - return eglCreatePbufferSurface(display, dummyConfig, attributes.data()); + return eglCreatePbufferSurface(display, stubConfig, attributes.data()); } bool GLESRenderEngine::isHdrDataSpace(const Dataspace dataSpace) const { diff --git a/libs/renderengine/gl/GLESRenderEngine.h b/libs/renderengine/gl/GLESRenderEngine.h index d5254a8a80..9484011525 100644 --- a/libs/renderengine/gl/GLESRenderEngine.h +++ b/libs/renderengine/gl/GLESRenderEngine.h @@ -53,8 +53,8 @@ public: static std::unique_ptr<GLESRenderEngine> create(const RenderEngineCreationArgs& args); GLESRenderEngine(const RenderEngineCreationArgs& args, EGLDisplay display, EGLConfig config, - EGLContext ctxt, EGLSurface dummy, EGLContext protectedContext, - EGLSurface protectedDummy); + EGLContext ctxt, EGLSurface stub, EGLContext protectedContext, + EGLSurface protectedStub); ~GLESRenderEngine() override EXCLUDES(mRenderingMutex); void primeCache() const override; @@ -121,8 +121,8 @@ private: static EGLContext createEglContext(EGLDisplay display, EGLConfig config, EGLContext shareContext, bool useContextPriority, Protection protection); - static EGLSurface createDummyEglPbufferSurface(EGLDisplay display, EGLConfig config, - int hwcFormat, Protection protection); + static EGLSurface createStubEglPbufferSurface(EGLDisplay display, EGLConfig config, + int hwcFormat, Protection protection); std::unique_ptr<Framebuffer> createFramebuffer(); std::unique_ptr<Image> createImage(); void checkErrors() const; @@ -181,9 +181,9 @@ private: EGLDisplay mEGLDisplay; EGLConfig mEGLConfig; EGLContext mEGLContext; - EGLSurface mDummySurface; + EGLSurface mStubSurface; EGLContext mProtectedEGLContext; - EGLSurface mProtectedDummySurface; + EGLSurface mProtectedStubSurface; GLint mMaxViewportDims[2]; GLint mMaxTextureSize; GLuint mVpWidth; diff --git a/libs/renderengine/tests/RenderEngineTest.cpp b/libs/renderengine/tests/RenderEngineTest.cpp index f577eb3dc7..0fec99b9b6 100644 --- a/libs/renderengine/tests/RenderEngineTest.cpp +++ b/libs/renderengine/tests/RenderEngineTest.cpp @@ -917,7 +917,7 @@ void RenderEngineTest::fillRedBufferWithoutPremultiplyAlpha() { void RenderEngineTest::fillBufferWithoutPremultiplyAlpha() { fillRedBufferWithoutPremultiplyAlpha(); - expectBufferColor(fullscreenRect(), 128, 0, 0, 64, 1); + expectBufferColor(fullscreenRect(), 128, 0, 0, 128, 1); } void RenderEngineTest::clearLeftRegion() { @@ -927,7 +927,7 @@ void RenderEngineTest::clearLeftRegion() { settings.clip = Rect(4, 4); settings.clearRegion = Region(Rect(2, 4)); std::vector<const renderengine::LayerSettings*> layers; - // dummy layer, without bounds should not render anything + // fake layer, without bounds should not render anything renderengine::LayerSettings layer; layers.push_back(&layer); invokeDraw(settings, layers, mBuffer); diff --git a/libs/ui/OWNERS b/libs/ui/OWNERS index 97ead2168d..b1317b169e 100644 --- a/libs/ui/OWNERS +++ b/libs/ui/OWNERS @@ -1,7 +1,7 @@ +alecmouri@google.com +chrisforbes@google.com +jreck@google.com lpy@google.com -marissaw@google.com mathias@google.com romainguy@google.com stoza@google.com -jwcai@google.com -tianyuj@google.com diff --git a/libs/ui/include/ui/GraphicBuffer.h b/libs/ui/include/ui/GraphicBuffer.h index 013505a2c9..57be686592 100644 --- a/libs/ui/include/ui/GraphicBuffer.h +++ b/libs/ui/include/ui/GraphicBuffer.h @@ -260,6 +260,9 @@ private: uint64_t mId; + // Unused, but removing this may break GSI. + int32_t mBufferId = -1; + // Stores the generation number of this buffer. If this number does not // match the BufferQueue's internal generation number (set through // IGBP::setGenerationNumber), attempts to attach the buffer will fail. diff --git a/libs/vr/libbufferhub/Android.bp b/libs/vr/libbufferhub/Android.bp index 2fcee7bee6..37c19d43f7 100644 --- a/libs/vr/libbufferhub/Android.bp +++ b/libs/vr/libbufferhub/Android.bp @@ -16,6 +16,12 @@ cc_library_headers { name: "libbufferhub_headers", export_include_dirs: ["include"], vendor_available: true, // TODO(b/112338314): Does shouldn't be available to vendor. + apex_available: [ + "//apex_available:platform", + "com.android.media", + "com.android.media.swcodec", + ], + min_sdk_version: "29", } sourceFiles = [ diff --git a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp index 8cc7081e4f..fab10978d6 100644 --- a/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp +++ b/libs/vr/libbufferhubqueue/tests/buffer_hub_queue_producer-test.cpp @@ -108,7 +108,7 @@ class BufferHubQueueProducerTest : public ::testing::Test { void ConnectProducer() { IGraphicBufferProducer::QueueBufferOutput output; // Can connect the first time. - ASSERT_EQ(OK, mProducer->connect(kDummyListener, kTestApi, + ASSERT_EQ(OK, mProducer->connect(kStubListener, kTestApi, kTestControlledByApp, &output)); } @@ -140,7 +140,7 @@ class BufferHubQueueProducerTest : public ::testing::Test { return QueueBufferInputBuilder().build(); } - const sp<IProducerListener> kDummyListener{new DummyProducerListener}; + const sp<IProducerListener> kStubListener{new StubProducerListener}; sp<BufferHubProducer> mProducer; sp<Surface> mSurface; @@ -150,11 +150,11 @@ TEST_F(BufferHubQueueProducerTest, ConnectFirst_ReturnsError) { IGraphicBufferProducer::QueueBufferOutput output; // NULL output returns BAD_VALUE - EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApi, + EXPECT_EQ(BAD_VALUE, mProducer->connect(kStubListener, kTestApi, kTestControlledByApp, nullptr)); // Invalid API returns bad value - EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApiInvalid, + EXPECT_EQ(BAD_VALUE, mProducer->connect(kStubListener, kTestApiInvalid, kTestControlledByApp, &output)); } @@ -163,7 +163,7 @@ TEST_F(BufferHubQueueProducerTest, ConnectAgain_ReturnsError) { // Can't connect when there is already a producer connected. IGraphicBufferProducer::QueueBufferOutput output; - EXPECT_EQ(BAD_VALUE, mProducer->connect(kDummyListener, kTestApi, + EXPECT_EQ(BAD_VALUE, mProducer->connect(kStubListener, kTestApi, kTestControlledByApp, &output)); } @@ -554,18 +554,18 @@ TEST_F(BufferHubQueueProducerTest, TakeAsParcelable) { ProducerQueueParcelable producer_parcelable; EXPECT_EQ(mProducer->TakeAsParcelable(&producer_parcelable), BAD_VALUE); - // Create a valid dummy producer parcelable. - auto dummy_channel_parcelable = + // Create a valid fake producer parcelable. + auto fake_channel_parcelable = std::make_unique<pdx::default_transport::ChannelParcelable>( LocalHandle(0), LocalHandle(0), LocalHandle(0)); - EXPECT_TRUE(dummy_channel_parcelable->IsValid()); - ProducerQueueParcelable dummy_producer_parcelable( - std::move(dummy_channel_parcelable)); - EXPECT_TRUE(dummy_producer_parcelable.IsValid()); + EXPECT_TRUE(fake_channel_parcelable->IsValid()); + ProducerQueueParcelable fake_producer_parcelable( + std::move(fake_channel_parcelable)); + EXPECT_TRUE(fake_producer_parcelable.IsValid()); // Disconnect producer can be taken out, but only to an invalid parcelable. ASSERT_EQ(mProducer->disconnect(kTestApi), OK); - EXPECT_EQ(mProducer->TakeAsParcelable(&dummy_producer_parcelable), BAD_VALUE); + EXPECT_EQ(mProducer->TakeAsParcelable(&fake_producer_parcelable), BAD_VALUE); EXPECT_FALSE(producer_parcelable.IsValid()); EXPECT_EQ(mProducer->TakeAsParcelable(&producer_parcelable), OK); EXPECT_TRUE(producer_parcelable.IsValid()); @@ -583,7 +583,7 @@ TEST_F(BufferHubQueueProducerTest, TakeAsParcelable) { // But connect to API will fail. IGraphicBufferProducer::QueueBufferOutput output; - EXPECT_EQ(mProducer->connect(kDummyListener, kTestApi, kTestControlledByApp, + EXPECT_EQ(mProducer->connect(kStubListener, kTestApi, kTestControlledByApp, &output), BAD_VALUE); @@ -592,8 +592,8 @@ TEST_F(BufferHubQueueProducerTest, TakeAsParcelable) { sp<BufferHubProducer> new_producer = BufferHubProducer::Create(std::move(producer_parcelable)); ASSERT_TRUE(new_producer != nullptr); - EXPECT_EQ(new_producer->connect(kDummyListener, kTestApi, - kTestControlledByApp, &output), + EXPECT_EQ(new_producer->connect(kStubListener, kTestApi, kTestControlledByApp, + &output), OK); } diff --git a/libs/vr/libdvr/Android.bp b/libs/vr/libdvr/Android.bp index 81a9b2d26a..340d7bf19a 100644 --- a/libs/vr/libdvr/Android.bp +++ b/libs/vr/libdvr/Android.bp @@ -17,6 +17,12 @@ cc_library_headers { name: "libdvr_headers", export_include_dirs: ["include"], vendor_available: true, + apex_available: [ + "//apex_available:platform", + "com.android.media", + "com.android.media.swcodec", + ], + min_sdk_version: "29", } cc_library_headers { diff --git a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp index 7b3717e979..07e2121970 100644 --- a/libs/vr/libdvr/tests/dvr_display_manager-test.cpp +++ b/libs/vr/libdvr/tests/dvr_display_manager-test.cpp @@ -308,14 +308,6 @@ class TestDisplayManager { class DvrDisplayManagerTest : public Test { protected: void SetUp() override { - // dvr display manager test doesn't apply to standalone vr devices because - // tests cannot create display manager client on these devices. - if (property_get_bool("ro.boot.vr", false)) { - GTEST_SKIP() - << "All tests in DvrDisplayManagerTest test case are skipped " - "because the device boot to VR."; - } - int ret; DvrDisplayManager* display_manager; DvrSurfaceState* surface_state; diff --git a/libs/vr/libpdx/encoder_performance_test.cpp b/libs/vr/libpdx/encoder_performance_test.cpp index b7d94b3471..7b477c4ce2 100644 --- a/libs/vr/libpdx/encoder_performance_test.cpp +++ b/libs/vr/libpdx/encoder_performance_test.cpp @@ -158,12 +158,12 @@ std::chrono::nanoseconds SerializeBaseTest(MessageWriter* writer, size_t iterations, ResetFunc* write_reset, void* reset_data, size_t data_size) { - std::vector<uint8_t> dummy_data(data_size); + std::vector<uint8_t> fake_data(data_size); auto start = std::chrono::high_resolution_clock::now(); for (size_t i = 0; i < iterations; i++) { write_reset(reset_data); - memcpy(writer->GetNextWriteBufferSection(dummy_data.size()), - dummy_data.data(), dummy_data.size()); + memcpy(writer->GetNextWriteBufferSection(fake_data.size()), + fake_data.data(), fake_data.size()); } auto stop = std::chrono::high_resolution_clock::now(); return stop - start; @@ -177,17 +177,17 @@ std::chrono::nanoseconds DeserializeBaseTest( MessageReader* reader, MessageWriter* writer, size_t iterations, ResetFunc* read_reset, ResetFunc* write_reset, void* reset_data, size_t data_size) { - std::vector<uint8_t> dummy_data(data_size); + std::vector<uint8_t> fake_data(data_size); write_reset(reset_data); - memcpy(writer->GetNextWriteBufferSection(dummy_data.size()), - dummy_data.data(), dummy_data.size()); + memcpy(writer->GetNextWriteBufferSection(fake_data.size()), fake_data.data(), + fake_data.size()); auto start = std::chrono::high_resolution_clock::now(); for (size_t i = 0; i < iterations; i++) { read_reset(reset_data); auto section = reader->GetNextReadBufferSection(); - memcpy(dummy_data.data(), section.first, dummy_data.size()); + memcpy(fake_data.data(), section.first, fake_data.size()); reader->ConsumeReadBufferSectionData( - AdvancePointer(section.first, dummy_data.size())); + AdvancePointer(section.first, fake_data.size())); } auto stop = std::chrono::high_resolution_clock::now(); return stop - start; diff --git a/libs/vr/libpdx/fuzz/Android.bp b/libs/vr/libpdx/fuzz/Android.bp new file mode 100644 index 0000000000..b36e0deea0 --- /dev/null +++ b/libs/vr/libpdx/fuzz/Android.bp @@ -0,0 +1,62 @@ +cc_fuzz { + name: "libpdx_service_dispatcher_fuzzer", + clang: true, + srcs: [ + "service_dispatcher_fuzzer.cpp", + ], + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + ], + static_libs: [ + "libpdx", + ], + shared_libs: [ + "libutils", + "liblog", + "libcutils" + ], +} + +cc_fuzz { + name: "libpdx_message_fuzzer", + clang: true, + srcs: [ + "message_fuzzer.cpp", + ], + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + ], + static_libs: [ + "libpdx", + ], + shared_libs: [ + "libutils", + "liblog", + "libcutils" + ], +} + +cc_fuzz { + name: "libpdx_serialization_fuzzer", + clang: true, + srcs: [ + "serialization_fuzzer.cpp", + ], + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + ], + static_libs: [ + "libpdx", + ], + shared_libs: [ + "libutils", + "liblog", + "libcutils" + ], +} diff --git a/libs/vr/libpdx/fuzz/helpers.h b/libs/vr/libpdx/fuzz/helpers.h new file mode 100644 index 0000000000..83ec4096f6 --- /dev/null +++ b/libs/vr/libpdx/fuzz/helpers.h @@ -0,0 +1,294 @@ +/* + * Copyright 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. + */ +// Authors: corbin.souffrant@leviathansecurity.com +// brian.balling@leviathansecurity.com + +#ifndef LEV_FUZZERS_LIBPDX_HELPERS_H_ +#define LEV_FUZZERS_LIBPDX_HELPERS_H_ + +#define UNUSED(expr) \ + do { \ + (void)(expr); \ + } while (0) + +#include <fuzzer/FuzzedDataProvider.h> +#include <pdx/client.h> +#include <pdx/service.h> +#include <pdx/service_dispatcher.h> +#include <pdx/service_endpoint.h> +#include <sys/eventfd.h> +#include <memory> +#include <vector> + +using namespace android::pdx; + +// Vector of operations we can call in the dispatcher. +static const std::vector<std::function<void( + const std::unique_ptr<ServiceDispatcher>&, FuzzedDataProvider*)>> + dispatcher_operations = { + [](const std::unique_ptr<ServiceDispatcher>& dispatcher, + FuzzedDataProvider*) -> void { dispatcher->EnterDispatchLoop(); }, + [](const std::unique_ptr<ServiceDispatcher>& dispatcher, + FuzzedDataProvider*) -> void { dispatcher->ReceiveAndDispatch(); }, + [](const std::unique_ptr<ServiceDispatcher>& dispatcher, + FuzzedDataProvider* fdp) -> void { + dispatcher->ReceiveAndDispatch(fdp->ConsumeIntegral<int>()); + }}; + +// Most of the fuzzing occurs within the endpoint, which is derived from an +// abstract class. So we are returning garbage data for most functions besides +// the ones we added or need to actually use. +class FuzzEndpoint : public Endpoint { + public: + explicit FuzzEndpoint(FuzzedDataProvider* fdp) { + _fdp = fdp; + _epoll_fd = eventfd(0, 0); + } + + ~FuzzEndpoint() { close(_epoll_fd); } + + // Returns an fd that can be used with epoll() to wait for incoming messages + // from this endpoint. + int epoll_fd() const { return _epoll_fd; } + + // Associates a Service instance with an endpoint by setting the service + // context pointer to the address of the Service. Only one Service may be + // associated with a given endpoint. + Status<void> SetService(Service* service) { + _service = service; + return Status<void>(0); + } + + // Set the channel context for the given channel. + Status<void> SetChannel(int channel_id, Channel* channel) { + UNUSED(channel_id); + _channel = std::shared_ptr<Channel>(channel); + return Status<void>(0); + } + + // Receives a message on the given endpoint file descriptor. + // This is called by the dispatcher to determine what operations + // to make, so we are fuzzing the response. + Status<void> MessageReceive(Message* message) { + // Create a randomized MessageInfo struct. + MessageInfo info; + eventfd_t wakeup_val = 0; + info.pid = _fdp->ConsumeIntegral<int>(); + info.tid = _fdp->ConsumeIntegral<int>(); + info.cid = _fdp->ConsumeIntegral<int>(); + info.mid = _fdp->ConsumeIntegral<int>(); + info.euid = _fdp->ConsumeIntegral<int>(); + info.egid = _fdp->ConsumeIntegral<int>(); + info.op = _fdp->ConsumeIntegral<int32_t>(); + info.flags = _fdp->ConsumeIntegral<uint32_t>(); + info.service = _service; + info.channel = _channel.get(); + info.send_len = _fdp->ConsumeIntegral<size_t>(); + info.recv_len = _fdp->ConsumeIntegral<size_t>(); + info.fd_count = _fdp->ConsumeIntegral<size_t>(); + if (_fdp->remaining_bytes() >= 32) { + std::vector<uint8_t> impulse_vec = _fdp->ConsumeBytes<uint8_t>(32); + memcpy(info.impulse, impulse_vec.data(), 32); + } + + *message = Message(info); + eventfd_read(_epoll_fd, &wakeup_val); + + return Status<void>(); + } + + // Returns a tag that uniquely identifies a specific underlying IPC + // transport. + uint32_t GetIpcTag() const { return 0; } + + // Close a channel, signaling the client file object and freeing the channel + // id. Once closed, the client side of the channel always returns the error + // ESHUTDOWN and signals the poll/epoll events POLLHUP and POLLFREE. + Status<void> CloseChannel(int channel_id) { + UNUSED(channel_id); + return Status<void>(); + } + + // Update the event bits for the given channel (given by id), using the + // given clear and set masks. + Status<void> ModifyChannelEvents(int channel_id, int clear_mask, + int set_mask) { + UNUSED(channel_id); + UNUSED(clear_mask); + UNUSED(set_mask); + return Status<void>(); + } + + // Create a new channel and push it as a file descriptor to the process + // sending the |message|. |flags| may be set to O_NONBLOCK and/or + // O_CLOEXEC to control the initial behavior of the new file descriptor (the + // sending process may change these later using fcntl()). The internal + // Channel instance associated with this channel is set to |channel|, + // which may be nullptr. The new channel id allocated for this channel is + // returned in |channel_id|, which may also be nullptr if not needed. + Status<RemoteChannelHandle> PushChannel(Message* message, int flags, + Channel* channel, int* channel_id) { + UNUSED(message); + UNUSED(flags); + UNUSED(channel); + UNUSED(channel_id); + return Status<RemoteChannelHandle>(); + } + + // Check whether the |ref| is a reference to a channel to the service + // represented by the |endpoint|. If the channel reference in question is + // valid, the Channel object is returned in |channel| when non-nullptr and + // the channel ID is returned through the Status object. + Status<int> CheckChannel(const Message* message, ChannelReference ref, + Channel** channel) { + UNUSED(message); + UNUSED(ref); + UNUSED(channel); + return Status<int>(); + } + + // Replies to the message with a return code. + Status<void> MessageReply(Message* message, int return_code) { + UNUSED(message); + UNUSED(return_code); + return Status<void>(); + } + + // Replies to the message with a file descriptor. + Status<void> MessageReplyFd(Message* message, unsigned int push_fd) { + UNUSED(message); + UNUSED(push_fd); + return Status<void>(); + } + + // Replies to the message with a local channel handle. + Status<void> MessageReplyChannelHandle(Message* message, + const LocalChannelHandle& handle) { + UNUSED(message); + UNUSED(handle); + return Status<void>(); + } + + // Replies to the message with a borrowed local channel handle. + Status<void> MessageReplyChannelHandle(Message* message, + const BorrowedChannelHandle& handle) { + UNUSED(message); + UNUSED(handle); + return Status<void>(); + } + + // Replies to the message with a remote channel handle. + Status<void> MessageReplyChannelHandle(Message* message, + const RemoteChannelHandle& handle) { + UNUSED(message); + UNUSED(handle); + return Status<void>(); + } + + // Reads message data into an array of memory buffers. + Status<size_t> ReadMessageData(Message* message, const iovec* vector, + size_t vector_length) { + UNUSED(message); + UNUSED(vector); + UNUSED(vector_length); + return Status<size_t>(); + } + + // Sends reply data for message. + Status<size_t> WriteMessageData(Message* message, const iovec* vector, + size_t vector_length) { + UNUSED(message); + UNUSED(vector); + UNUSED(vector_length); + return Status<size_t>(); + } + + // Records a file descriptor into the message buffer and returns the + // remapped reference to be sent to the remote process. + Status<FileReference> PushFileHandle(Message* message, + const LocalHandle& handle) { + UNUSED(message); + UNUSED(handle); + return Status<FileReference>(); + } + + Status<FileReference> PushFileHandle(Message* message, + const BorrowedHandle& handle) { + UNUSED(message); + UNUSED(handle); + return Status<FileReference>(); + } + + Status<FileReference> PushFileHandle(Message* message, + const RemoteHandle& handle) { + UNUSED(message); + UNUSED(handle); + return Status<FileReference>(); + } + + Status<ChannelReference> PushChannelHandle(Message* message, + const LocalChannelHandle& handle) { + UNUSED(message); + UNUSED(handle); + return Status<ChannelReference>(); + } + + Status<ChannelReference> PushChannelHandle( + Message* message, const BorrowedChannelHandle& handle) { + UNUSED(message); + UNUSED(handle); + return Status<ChannelReference>(); + } + + Status<ChannelReference> PushChannelHandle( + Message* message, const RemoteChannelHandle& handle) { + UNUSED(message); + UNUSED(handle); + return Status<ChannelReference>(); + } + + // Obtains a file descriptor/channel handle from a message for the given + // reference. + LocalHandle GetFileHandle(Message* message, FileReference ref) const { + UNUSED(message); + UNUSED(ref); + return LocalHandle(); + } + + LocalChannelHandle GetChannelHandle(Message* message, + ChannelReference ref) const { + UNUSED(message); + UNUSED(ref); + return LocalChannelHandle(); + } + + // Transport-specific message state management. + void* AllocateMessageState() { return nullptr; } + + void FreeMessageState(void* state) { UNUSED(state); } + + // Cancels the endpoint, unblocking any receiver threads waiting for a + // message. + Status<void> Cancel() { return Status<void>(); } + + private: + FuzzedDataProvider* _fdp; + std::shared_ptr<Channel> _channel; + Service* _service; + int _epoll_fd; +}; + +#endif // LEV_FUZZERS_LIBPDX_HELPERS_H_ diff --git a/libs/vr/libpdx/fuzz/message_fuzzer.cpp b/libs/vr/libpdx/fuzz/message_fuzzer.cpp new file mode 100644 index 0000000000..b627045ab6 --- /dev/null +++ b/libs/vr/libpdx/fuzz/message_fuzzer.cpp @@ -0,0 +1,175 @@ +/* + * Copyright 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. + */ +// Authors: corbin.souffrant@leviathansecurity.com +// brian.balling@leviathansecurity.com + +#include <fuzzer/FuzzedDataProvider.h> +#include <helpers.h> +#include <pdx/client_channel.h> +#include <pdx/service.h> +#include <pdx/service_dispatcher.h> +#include <stddef.h> +#include <stdint.h> +#include <sys/eventfd.h> +#include <thread> + +using namespace android::pdx; + +// Fuzzer for Message object functions. +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FuzzedDataProvider fdp = FuzzedDataProvider(data, size); + + FuzzEndpoint* endpoint = new FuzzEndpoint(&fdp); + std::shared_ptr<Service> service( + new Service("FuzzService", std::unique_ptr<Endpoint>(endpoint))); + std::shared_ptr<Channel> channel(nullptr); + + // Generate a random Message object to call functions in. + MessageInfo info; + info.pid = fdp.ConsumeIntegral<int>(); + info.tid = fdp.ConsumeIntegral<int>(); + info.cid = fdp.ConsumeIntegral<int>(); + info.mid = fdp.ConsumeIntegral<int>(); + info.euid = fdp.ConsumeIntegral<int>(); + info.egid = fdp.ConsumeIntegral<int>(); + info.op = fdp.ConsumeIntegral<int32_t>(); + info.flags = fdp.ConsumeIntegral<uint32_t>(); + info.service = service.get(); + info.channel = channel.get(); + info.send_len = fdp.ConsumeIntegral<size_t>(); + info.recv_len = fdp.ConsumeIntegral<size_t>(); + info.fd_count = fdp.ConsumeIntegral<size_t>(); + if (fdp.remaining_bytes() >= 32) { + std::vector<uint8_t> impulse_vec = fdp.ConsumeBytes<uint8_t>(32); + memcpy(info.impulse, impulse_vec.data(), 32); + } + + Message message = Message(info); + + // A bunch of getters that probably won't do much, but might as well + // get coverage, while we are here. + message.GetProcessId(); + message.GetThreadId(); + message.GetEffectiveUserId(); + message.GetEffectiveGroupId(); + message.GetChannelId(); + message.GetMessageId(); + message.GetOp(); + message.GetFlags(); + message.GetSendLength(); + message.GetReceiveLength(); + message.GetFileDescriptorCount(); + message.ImpulseEnd(); + message.replied(); + message.IsChannelExpired(); + message.IsServiceExpired(); + message.GetState(); + message.GetState(); + + // Some misc. functions. + unsigned int fd = fdp.ConsumeIntegral<unsigned int>(); + int clear_mask = fdp.ConsumeIntegral<int>(); + int set_mask = fdp.ConsumeIntegral<int>(); + Status<void> status = {}; + message.ModifyChannelEvents(clear_mask, set_mask); + + // Fuzz the handle functions. + LocalHandle l_handle = {}; + BorrowedHandle b_handle = {}; + RemoteHandle r_handle = {}; + LocalChannelHandle lc_handle = {}; + BorrowedChannelHandle bc_handle = {}; + RemoteChannelHandle rc_handle = {}; + FileReference f_ref = fdp.ConsumeIntegral<int32_t>(); + ChannelReference c_ref = fdp.ConsumeIntegral<int32_t>(); + + // These don't actually modify any state in the Message or params. + // They can be called in any order. + message.PushFileHandle(b_handle); + message.PushFileHandle(r_handle); + message.PushChannelHandle(lc_handle); + message.PushChannelHandle(bc_handle); + message.PushChannelHandle(rc_handle); + message.GetFileHandle(f_ref, &l_handle); + message.GetChannelHandle(c_ref, &lc_handle); + + // Can only reply once, pick at random. + switch (fdp.ConsumeIntegral<uint8_t>()) { + case 0: + message.ReplyFileDescriptor(fd); + break; + case 1: + message.Reply(status); + break; + case 2: + message.Reply(l_handle); + break; + case 3: + message.Reply(b_handle); + break; + case 4: + message.Reply(r_handle); + break; + case 5: + message.Reply(lc_handle); + break; + case 6: + message.Reply(bc_handle); + break; + case 7: + message.Reply(rc_handle); + } + + // Fuzz the channel functions. + int flags = fdp.ConsumeIntegral<int>(); + int channel_id = 0; + message.PushChannel(flags, channel, &channel_id); + message.CheckChannel(service.get(), c_ref, &channel); + message.CheckChannel(c_ref, &channel); + message.PushChannel(service.get(), flags, channel, &channel_id); + size_t iovec_size = sizeof(iovec); + struct iovec* iovecs = nullptr; + + // Fuzz the read/write functions. Needs at least one iovec, plus one byte. + if (fdp.remaining_bytes() >= iovec_size + 1) { + std::vector<uint8_t> tmp_vec = fdp.ConsumeBytes<uint8_t>(iovec_size); + struct iovec* vector = reinterpret_cast<struct iovec*>(tmp_vec.data()); + std::vector<uint8_t> tmp_buf = + fdp.ConsumeBytes<uint8_t>(fdp.remaining_bytes()); + void* buf = reinterpret_cast<void*>(tmp_buf.data()); + size_t buf_size = fdp.ConsumeIntegral<size_t>(); + + // Capping num_vecs to 1024 so it doesn't allocate too much memory. + size_t num_vecs = fdp.ConsumeIntegralInRange<size_t>(0, 1024); + + if (num_vecs > 0) + iovecs = new struct iovec[num_vecs]; + for (size_t i = 0; i < num_vecs; i++) { + iovecs[i] = *vector; + } + + message.ReadAll(vector, buf_size); + message.WriteAll(buf, buf_size); + message.ReadVectorAll(vector, num_vecs); + message.WriteVectorAll(vector, num_vecs); + message.ReadVector(vector, buf_size); + message.WriteVector(vector, buf_size); + } + + if (iovecs != nullptr) + delete[] iovecs; + return 0; +} diff --git a/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp b/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp new file mode 100644 index 0000000000..afde5f7f32 --- /dev/null +++ b/libs/vr/libpdx/fuzz/serialization_fuzzer.cpp @@ -0,0 +1,111 @@ +/* + * Copyright 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. + */ +// Authors: corbin.souffrant@leviathansecurity.com +// brian.balling@leviathansecurity.com + +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> + +#include <memory> +#include <string> +#include <thread> +#include <utility> + +#include <fuzzer/FuzzedDataProvider.h> +#include <pdx/rpc/argument_encoder.h> +#include <pdx/rpc/array_wrapper.h> +#include <pdx/rpc/default_initialization_allocator.h> +#include <pdx/rpc/payload.h> +#include <pdx/rpc/serializable.h> +#include <pdx/rpc/serialization.h> +#include <pdx/rpc/string_wrapper.h> +#include <pdx/utility.h> + +using namespace android::pdx; +using namespace android::pdx::rpc; + +struct FuzzType { + int a; + float b; + std::string c; + + FuzzType() {} + FuzzType(int a, float b, const std::string& c) : a(a), b(b), c(c) {} + + private: + PDX_SERIALIZABLE_MEMBERS(FuzzType, a, b, c); +}; + +// Fuzzer for Serialization operations, this is mostly just lifted from the +// existing test cases to use fuzzed values as inputs. +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + FuzzedDataProvider fdp = FuzzedDataProvider(data, size); + Payload result; + + // Currently, only fuzzing subset of types. In the future, may want + // to add more difficult to generate types like array, map, enum, etc... + bool b_val = fdp.ConsumeBool(); + uint8_t u8_val = fdp.ConsumeIntegral<uint8_t>(); + uint16_t u16_val = fdp.ConsumeIntegral<uint16_t>(); + uint32_t u32_val = fdp.ConsumeIntegral<uint32_t>(); + uint64_t u64_val = fdp.ConsumeIntegral<uint64_t>(); + int8_t i8_val = fdp.ConsumeIntegral<int8_t>(); + int16_t i16_val = fdp.ConsumeIntegral<uint16_t>(); + int32_t i32_val = fdp.ConsumeIntegral<uint32_t>(); + int64_t i64_val = fdp.ConsumeIntegral<uint64_t>(); + float f_val = fdp.ConsumeFloatingPoint<float>(); + double d_val = fdp.ConsumeFloatingPoint<double>(); + std::string s_val = fdp.ConsumeRandomLengthString(fdp.remaining_bytes()); + std::vector<uint8_t> vec_val = + fdp.ConsumeBytes<uint8_t>(fdp.remaining_bytes()); + FuzzType t1_val{reinterpret_cast<int>(i32_val), f_val, s_val}; + + // Types need to be individually fuzzed because code path changes depending + // on which type is being serialized/deserialized. + Serialize(b_val, &result); + Deserialize(&b_val, &result); + Serialize(u8_val, &result); + Deserialize(&u8_val, &result); + Serialize(u16_val, &result); + Deserialize(&u16_val, &result); + Serialize(u32_val, &result); + Deserialize(&u32_val, &result); + Serialize(u64_val, &result); + Deserialize(&u64_val, &result); + Serialize(i8_val, &result); + Deserialize(&i8_val, &result); + Serialize(i16_val, &result); + Deserialize(&i16_val, &result); + Serialize(i32_val, &result); + Deserialize(&i32_val, &result); + Serialize(i64_val, &result); + Deserialize(&i64_val, &result); + Serialize(f_val, &result); + Deserialize(&f_val, &result); + Serialize(d_val, &result); + Deserialize(&d_val, &result); + Serialize(s_val, &result); + Deserialize(&s_val, &result); + Serialize(WrapString(s_val), &result); + Deserialize(&s_val, &result); + Serialize(vec_val, &result); + Deserialize(&vec_val, &result); + Serialize(t1_val, &result); + Deserialize(&t1_val, &result); + + return 0; +} diff --git a/libs/vr/libpdx/fuzz/service_dispatcher_fuzzer.cpp b/libs/vr/libpdx/fuzz/service_dispatcher_fuzzer.cpp new file mode 100644 index 0000000000..3a3bfd9bcf --- /dev/null +++ b/libs/vr/libpdx/fuzz/service_dispatcher_fuzzer.cpp @@ -0,0 +1,74 @@ +/* + * Copyright 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. + */ +// Authors: corbin.souffrant@leviathansecurity.com +// brian.balling@leviathansecurity.com + +#include <fuzzer/FuzzedDataProvider.h> +#include <helpers.h> +#include <pdx/client_channel.h> +#include <pdx/service.h> +#include <pdx/service_dispatcher.h> +#include <stddef.h> +#include <stdint.h> +#include <sys/eventfd.h> +#include <thread> + +using namespace android::pdx; + +// Dispatch fuzzer entry point. This fuzzer creates a ServiceDispatcher +// and creates an endpoint that returns fuzzed messages that are passed +// to the ReceiveAndDispatch and DispatchLoop functions. +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + eventfd_t wakeup_val = 1; + FuzzedDataProvider fdp = FuzzedDataProvider(data, size); + + // Endpoint is only used to be immediately wrapped as a unique_ptr, + // so it is ok to be using a raw ptr and new here without freeing. + FuzzEndpoint* endpoint = new FuzzEndpoint(&fdp); + std::unique_ptr<ServiceDispatcher> dispatcher = ServiceDispatcher::Create(); + std::shared_ptr<Channel> channel(nullptr); + std::shared_ptr<Client> client(nullptr); + std::shared_ptr<Service> service( + new Service("FuzzService", std::unique_ptr<Endpoint>(endpoint))); + + service->SetChannel(0, std::shared_ptr<Channel>(channel)); + dispatcher->AddService(service); + + // Dispatcher blocks, so needs to run in its own thread. + std::thread run_dispatcher([&]() { + uint8_t opt = 0; + + // Right now the only operations block, so the while loop is pointless + // but leaving it in, just in case that ever changes. + while (fdp.remaining_bytes() > sizeof(MessageInfo)) { + opt = fdp.ConsumeIntegral<uint8_t>() % dispatcher_operations.size(); + dispatcher_operations[opt](dispatcher, &fdp); + } + }); + + // Continuously wake up the epoll so the dispatcher can run. + while (fdp.remaining_bytes() > sizeof(MessageInfo)) { + eventfd_write(endpoint->epoll_fd(), wakeup_val); + } + + // Cleanup the dispatcher and thread. + dispatcher->SetCanceled(true); + if (run_dispatcher.joinable()) + run_dispatcher.join(); + dispatcher->RemoveService(service); + + return 0; +} diff --git a/libs/vr/libpdx/private/pdx/rpc/macros.h b/libs/vr/libpdx/private/pdx/rpc/macros.h index aeae9d3e5e..99325b5d65 100644 --- a/libs/vr/libpdx/private/pdx/rpc/macros.h +++ b/libs/vr/libpdx/private/pdx/rpc/macros.h @@ -28,7 +28,7 @@ // Clears any remaining contents wrapped in parentheses. #define _PDX_CLEAR(...) -// Introduces a first dummy argument and _PDX_CLEAR as second argument. +// Introduces a first stub argument and _PDX_CLEAR as second argument. #define _PDX_CLEAR_IF_LAST() _, _PDX_CLEAR // Returns the first argument of a list. @@ -45,7 +45,7 @@ // Returns next_func if the next element is not (), or _PDX_CLEAR // otherwise. // -// _PDX_CLEAR_IF_LAST inserts an extra first dummy argument if peek is (). +// _PDX_CLEAR_IF_LAST inserts an extra first stub argument if peek is (). #define _PDX_NEXT_FUNC(next_element, next_func) \ _PDX_EXPAND_NEXT_FUNC(_PDX_CLEAR_IF_LAST next_element, next_func) diff --git a/libs/vr/libpdx/service_dispatcher.cpp b/libs/vr/libpdx/service_dispatcher.cpp index b112fa30eb..ba0d69c178 100644 --- a/libs/vr/libpdx/service_dispatcher.cpp +++ b/libs/vr/libpdx/service_dispatcher.cpp @@ -92,9 +92,9 @@ int ServiceDispatcher::RemoveService(const std::shared_ptr<Service>& service) { if (thread_count_ > 0) return -EBUSY; - epoll_event dummy; // See BUGS in man 2 epoll_ctl. + epoll_event ee; // See BUGS in man 2 epoll_ctl. if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, service->endpoint()->epoll_fd(), - &dummy) < 0) { + &ee) < 0) { ALOGE("Failed to remove service from dispatcher because: %s\n", strerror(errno)); return -errno; diff --git a/libs/vr/libpdx_uds/service_endpoint.cpp b/libs/vr/libpdx_uds/service_endpoint.cpp index 9bc70ea9c0..810eb196ca 100644 --- a/libs/vr/libpdx_uds/service_endpoint.cpp +++ b/libs/vr/libpdx_uds/service_endpoint.cpp @@ -334,8 +334,8 @@ Status<void> Endpoint::CloseChannelLocked(int32_t channel_id) { int channel_fd = iter->second.data_fd.Get(); Status<void> status; - epoll_event dummy; // See BUGS in man 2 epoll_ctl. - if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, channel_fd, &dummy) < 0) { + epoll_event ee; // See BUGS in man 2 epoll_ctl. + if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, channel_fd, &ee) < 0) { status.SetError(errno); ALOGE( "Endpoint::CloseChannelLocked: Failed to remove channel from endpoint: " diff --git a/libs/vr/libvrflinger/epoll_event_dispatcher.cpp b/libs/vr/libvrflinger/epoll_event_dispatcher.cpp index 1cf5f17f62..0d5eb8080f 100644 --- a/libs/vr/libvrflinger/epoll_event_dispatcher.cpp +++ b/libs/vr/libvrflinger/epoll_event_dispatcher.cpp @@ -68,8 +68,8 @@ pdx::Status<void> EpollEventDispatcher::RemoveEventHandler(int fd) { ALOGD_IF(TRACE, "EpollEventDispatcher::RemoveEventHandler: fd=%d", fd); std::lock_guard<std::mutex> lock(lock_); - epoll_event dummy; // See BUGS in man 2 epoll_ctl. - if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, fd, &dummy) < 0) { + epoll_event ee; // See BUGS in man 2 epoll_ctl. + if (epoll_ctl(epoll_fd_.Get(), EPOLL_CTL_DEL, fd, &ee) < 0) { const int error = errno; ALOGE("Failed to remove fd from epoll set because: %s", strerror(error)); return pdx::ErrorStatus(error); diff --git a/libs/vr/libvrflinger/hardware_composer.cpp b/libs/vr/libvrflinger/hardware_composer.cpp index b77153835a..70f303b208 100644 --- a/libs/vr/libvrflinger/hardware_composer.cpp +++ b/libs/vr/libvrflinger/hardware_composer.cpp @@ -47,7 +47,6 @@ namespace dvr { namespace { const char kDvrPerformanceProperty[] = "sys.dvr.performance"; -const char kDvrStandaloneProperty[] = "ro.boot.vr"; const char kRightEyeOffsetProperty[] = "dvr.right_eye_offset_ns"; @@ -159,8 +158,6 @@ bool HardwareComposer::Initialize( return false; } - is_standalone_device_ = property_get_bool(kDvrStandaloneProperty, false); - request_display_callback_ = request_display_callback; primary_display_ = GetDisplayParams(composer, primary_display_id, true); @@ -206,8 +203,6 @@ void HardwareComposer::OnBootFinished() { return; boot_finished_ = true; post_thread_wait_.notify_one(); - if (is_standalone_device_) - request_display_callback_(true); } // Update the post thread quiescent state based on idle and suspended inputs. @@ -267,17 +262,11 @@ void HardwareComposer::OnPostThreadPaused() { layers_.clear(); // Phones create a new composer client on resume and destroy it on pause. - // Standalones only create the composer client once and then use SetPowerMode - // to control the screen on pause/resume. - if (!is_standalone_device_) { - if (composer_callback_ != nullptr) { - composer_callback_->SetVsyncService(nullptr); - composer_callback_ = nullptr; - } - composer_.reset(nullptr); - } else { - EnableDisplay(*target_display_, false); + if (composer_callback_ != nullptr) { + composer_callback_->SetVsyncService(nullptr); + composer_callback_ = nullptr; } + composer_.reset(nullptr); // Trigger target-specific performance mode change. property_set(kDvrPerformanceProperty, "idle"); @@ -588,7 +577,7 @@ void HardwareComposer::SetDisplaySurfaces( surfaces_changed_ = true; } - if (request_display_callback_ && !is_standalone_device_) + if (request_display_callback_) request_display_callback_(!display_idle); // Set idle state based on whether there are any surfaces to handle. @@ -773,28 +762,6 @@ void HardwareComposer::PostThread() { VsyncEyeOffsets vsync_eye_offsets = get_vsync_eye_offsets(); - if (is_standalone_device_) { - // First, wait until boot finishes. - std::unique_lock<std::mutex> lock(post_thread_mutex_); - if (PostThreadCondWait(lock, -1, [this] { return boot_finished_; })) { - return; - } - - // Then, wait until we're either leaving the quiescent state, or the boot - // finished display off timeout expires. - if (PostThreadCondWait(lock, kBootFinishedDisplayOffTimeoutSec, - [this] { return !post_thread_quiescent_; })) { - return; - } - - LOG_ALWAYS_FATAL_IF(post_thread_state_ & PostThreadState::Suspended, - "Vr flinger should own the display by now."); - post_thread_resumed_ = true; - post_thread_ready_.notify_all(); - if (!composer_) - CreateComposer(); - } - while (1) { ATRACE_NAME("HardwareComposer::PostThread"); diff --git a/libs/vr/libvrflinger/tests/vrflinger_test.cpp b/libs/vr/libvrflinger/tests/vrflinger_test.cpp index efd6d1dcd7..ac44f74151 100644 --- a/libs/vr/libvrflinger/tests/vrflinger_test.cpp +++ b/libs/vr/libvrflinger/tests/vrflinger_test.cpp @@ -43,9 +43,6 @@ constexpr auto kVrFlingerSwitchMaxTime = std::chrono::seconds(1); // completed. constexpr auto kVrFlingerSwitchPollInterval = std::chrono::milliseconds(50); -// How long to wait for a device that boots to VR to have vr flinger ready. -constexpr auto kBootVrFlingerWaitTimeout = std::chrono::seconds(30); - // A Binder connection to surface flinger. class SurfaceFlingerConnection { public: @@ -153,11 +150,6 @@ TEST(VrFlingerTest, ActivateDeactivate) { return; } - // This test doesn't apply to standalone vr devices. - if (property_get_bool("ro.boot.vr", false)) { - return; - } - auto surface_flinger_connection = SurfaceFlingerConnection::Create(); ASSERT_NE(surface_flinger_connection, nullptr); @@ -230,31 +222,5 @@ TEST(VrFlingerTest, ActivateDeactivate) { SurfaceFlingerConnection::VrFlingerSwitchResult::kSuccess); } -// This test runs only on devices that boot to vr. Such a device should boot to -// a state where vr flinger is running, and the test verifies this after a -// delay. -TEST(BootVrFlingerTest, BootsToVrFlinger) { - // Exit if we are not running on a device that boots to vr. - if (!property_get_bool("ro.boot.vr", false)) { - return; - } - - auto surface_flinger_connection = SurfaceFlingerConnection::Create(); - ASSERT_NE(surface_flinger_connection, nullptr); - - // Verify that vr flinger is enabled. - ASSERT_TRUE(surface_flinger_connection->IsAlive()); - auto vr_flinger_active = surface_flinger_connection->IsVrFlingerActive(); - ASSERT_TRUE(vr_flinger_active.has_value()); - - bool active_value = vr_flinger_active.value(); - if (!active_value) { - // Try again, but delay up to 30 seconds. - ASSERT_EQ(surface_flinger_connection->WaitForVrFlingerTimed(true, - kVrFlingerSwitchPollInterval, kBootVrFlingerWaitTimeout), - SurfaceFlingerConnection::VrFlingerSwitchResult::kSuccess); - } -} - } // namespace dvr } // namespace android diff --git a/opengl/libs/EGL/BlobCache.cpp b/opengl/libs/EGL/BlobCache.cpp index 74fb0194f9..a27c09f0fe 100644 --- a/opengl/libs/EGL/BlobCache.cpp +++ b/opengl/libs/EGL/BlobCache.cpp @@ -78,12 +78,12 @@ void BlobCache::set(const void* key, size_t keySize, const void* value, return; } - std::shared_ptr<Blob> dummyKey(new Blob(key, keySize, false)); - CacheEntry dummyEntry(dummyKey, nullptr); + std::shared_ptr<Blob> cacheKey(new Blob(key, keySize, false)); + CacheEntry cacheEntry(cacheKey, nullptr); while (true) { - auto index = std::lower_bound(mCacheEntries.begin(), mCacheEntries.end(), dummyEntry); - if (index == mCacheEntries.end() || dummyEntry < *index) { + auto index = std::lower_bound(mCacheEntries.begin(), mCacheEntries.end(), cacheEntry); + if (index == mCacheEntries.end() || cacheEntry < *index) { // Create a new cache entry. std::shared_ptr<Blob> keyBlob(new Blob(key, keySize, true)); std::shared_ptr<Blob> valueBlob(new Blob(value, valueSize, true)); @@ -138,10 +138,10 @@ size_t BlobCache::get(const void* key, size_t keySize, void* value, keySize, mMaxKeySize); return 0; } - std::shared_ptr<Blob> dummyKey(new Blob(key, keySize, false)); - CacheEntry dummyEntry(dummyKey, nullptr); - auto index = std::lower_bound(mCacheEntries.begin(), mCacheEntries.end(), dummyEntry); - if (index == mCacheEntries.end() || dummyEntry < *index) { + std::shared_ptr<Blob> cacheKey(new Blob(key, keySize, false)); + CacheEntry cacheEntry(cacheKey, nullptr); + auto index = std::lower_bound(mCacheEntries.begin(), mCacheEntries.end(), cacheEntry); + if (index == mCacheEntries.end() || cacheEntry < *index) { ALOGV("get: no cache entry found for key of size %zu", keySize); return 0; } diff --git a/opengl/libs/EGL/FileBlobCache.cpp b/opengl/libs/EGL/FileBlobCache.cpp index cc42ac7fef..751f3be81a 100644 --- a/opengl/libs/EGL/FileBlobCache.cpp +++ b/opengl/libs/EGL/FileBlobCache.cpp @@ -17,11 +17,13 @@ #include "FileBlobCache.h" #include <errno.h> +#include <fcntl.h> #include <inttypes.h> -#include <log/log.h> #include <sys/mman.h> #include <sys/stat.h> +#include <unistd.h> +#include <log/log.h> // Cache file header static const char* cacheFileMagic = "EGL$"; @@ -68,7 +70,7 @@ FileBlobCache::FileBlobCache(size_t maxKeySize, size_t maxValueSize, size_t maxT return; } - // Sanity check the size before trying to mmap it. + // Check the size before trying to mmap it. size_t fileSize = statBuf.st_size; if (fileSize > mMaxTotalSize * 2) { ALOGE("cache file is too large: %#" PRIx64, diff --git a/opengl/tests/EGLTest/EGL_test.cpp b/opengl/tests/EGLTest/EGL_test.cpp index 510226d051..bbd786d155 100644 --- a/opengl/tests/EGLTest/EGL_test.cpp +++ b/opengl/tests/EGLTest/EGL_test.cpp @@ -138,7 +138,7 @@ TEST_F(EGLTest, EGLTerminateSucceedsWithRemainingObjects) { }; EXPECT_TRUE(eglChooseConfig(mEglDisplay, attrs, &config, 1, &numConfigs)); - struct DummyConsumer : public BnConsumerListener { + struct MockConsumer : public BnConsumerListener { void onFrameAvailable(const BufferItem& /* item */) override {} void onBuffersReleased() override {} void onSidebandStreamChanged() override {} @@ -148,7 +148,7 @@ TEST_F(EGLTest, EGLTerminateSucceedsWithRemainingObjects) { sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); - consumer->consumerConnect(new DummyConsumer, false); + consumer->consumerConnect(new MockConsumer, false); sp<Surface> mSTC = new Surface(producer); sp<ANativeWindow> mANW = mSTC; @@ -258,7 +258,7 @@ TEST_F(EGLTest, EGLDisplayP3) { EXPECT_EQ(components[2], 8); EXPECT_EQ(components[3], 8); - struct DummyConsumer : public BnConsumerListener { + struct MockConsumer : public BnConsumerListener { void onFrameAvailable(const BufferItem& /* item */) override {} void onBuffersReleased() override {} void onSidebandStreamChanged() override {} @@ -268,7 +268,7 @@ TEST_F(EGLTest, EGLDisplayP3) { sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); - consumer->consumerConnect(new DummyConsumer, false); + consumer->consumerConnect(new MockConsumer, false); sp<Surface> mSTC = new Surface(producer); sp<ANativeWindow> mANW = mSTC; EGLint winAttrs[] = { @@ -306,7 +306,7 @@ TEST_F(EGLTest, EGLDisplayP3Passthrough) { get8BitConfig(config); - struct DummyConsumer : public BnConsumerListener { + struct MockConsumer : public BnConsumerListener { void onFrameAvailable(const BufferItem& /* item */) override {} void onBuffersReleased() override {} void onSidebandStreamChanged() override {} @@ -316,7 +316,7 @@ TEST_F(EGLTest, EGLDisplayP3Passthrough) { sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); - consumer->consumerConnect(new DummyConsumer, false); + consumer->consumerConnect(new MockConsumer, false); sp<Surface> mSTC = new Surface(producer); sp<ANativeWindow> mANW = mSTC; EGLint winAttrs[] = { @@ -398,7 +398,7 @@ TEST_F(EGLTest, EGLDisplayP31010102) { EXPECT_EQ(components[2], 10); EXPECT_EQ(components[3], 2); - struct DummyConsumer : public BnConsumerListener { + struct MockConsumer : public BnConsumerListener { void onFrameAvailable(const BufferItem& /* item */) override {} void onBuffersReleased() override {} void onSidebandStreamChanged() override {} @@ -408,7 +408,7 @@ TEST_F(EGLTest, EGLDisplayP31010102) { sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); - consumer->consumerConnect(new DummyConsumer, false); + consumer->consumerConnect(new MockConsumer, false); sp<Surface> mSTC = new Surface(producer); sp<ANativeWindow> mANW = mSTC; EGLint winAttrs[] = { @@ -570,7 +570,7 @@ TEST_F(EGLTest, EGLBT2020Linear) { ASSERT_NO_FATAL_FAILURE(get8BitConfig(config)); - struct DummyConsumer : public BnConsumerListener { + struct MockConsumer : public BnConsumerListener { void onFrameAvailable(const BufferItem& /* item */) override {} void onBuffersReleased() override {} void onSidebandStreamChanged() override {} @@ -580,7 +580,7 @@ TEST_F(EGLTest, EGLBT2020Linear) { sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); - consumer->consumerConnect(new DummyConsumer, false); + consumer->consumerConnect(new MockConsumer, false); sp<Surface> mSTC = new Surface(producer); sp<ANativeWindow> mANW = mSTC; @@ -622,7 +622,7 @@ TEST_F(EGLTest, EGLBT2020PQ) { ASSERT_NO_FATAL_FAILURE(get8BitConfig(config)); - struct DummyConsumer : public BnConsumerListener { + struct MockConsumer : public BnConsumerListener { void onFrameAvailable(const BufferItem& /* item */) override {} void onBuffersReleased() override {} void onSidebandStreamChanged() override {} @@ -632,7 +632,7 @@ TEST_F(EGLTest, EGLBT2020PQ) { sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); - consumer->consumerConnect(new DummyConsumer, false); + consumer->consumerConnect(new MockConsumer, false); sp<Surface> mSTC = new Surface(producer); sp<ANativeWindow> mANW = mSTC; std::vector<EGLint> winAttrs; @@ -705,7 +705,7 @@ TEST_F(EGLTest, EGLConfigFP16) { EXPECT_GE(components[2], 16); EXPECT_GE(components[3], 16); - struct DummyConsumer : public BnConsumerListener { + struct MockConsumer : public BnConsumerListener { void onFrameAvailable(const BufferItem& /* item */) override {} void onBuffersReleased() override {} void onSidebandStreamChanged() override {} @@ -714,7 +714,7 @@ TEST_F(EGLTest, EGLConfigFP16) { sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); - consumer->consumerConnect(new DummyConsumer, false); + consumer->consumerConnect(new MockConsumer, false); sp<Surface> mSTC = new Surface(producer); sp<ANativeWindow> mANW = mSTC; @@ -734,7 +734,7 @@ TEST_F(EGLTest, EGLNoConfigContext) { ASSERT_TRUE(hasEglExtension(mEglDisplay, "EGL_KHR_no_config_context")); - struct DummyConsumer : public BnConsumerListener { + struct MockConsumer : public BnConsumerListener { void onFrameAvailable(const BufferItem& /* item */) override {} void onBuffersReleased() override {} void onSidebandStreamChanged() override {} @@ -809,7 +809,7 @@ TEST_F(EGLTest, EGLConfig1010102) { EXPECT_EQ(components[2], 10); EXPECT_EQ(components[3], 2); - struct DummyConsumer : public BnConsumerListener { + struct MockConsumer : public BnConsumerListener { void onFrameAvailable(const BufferItem& /* item */) override {} void onBuffersReleased() override {} void onSidebandStreamChanged() override {} @@ -819,7 +819,7 @@ TEST_F(EGLTest, EGLConfig1010102) { sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); - consumer->consumerConnect(new DummyConsumer, false); + consumer->consumerConnect(new MockConsumer, false); sp<Surface> mSTC = new Surface(producer); sp<ANativeWindow> mANW = mSTC; @@ -835,7 +835,7 @@ TEST_F(EGLTest, EGLInvalidColorspaceAttribute) { ASSERT_NO_FATAL_FAILURE(get8BitConfig(config)); - struct DummyConsumer : public BnConsumerListener { + struct MockConsumer : public BnConsumerListener { void onFrameAvailable(const BufferItem& /* item */) override {} void onBuffersReleased() override {} void onSidebandStreamChanged() override {} @@ -845,7 +845,7 @@ TEST_F(EGLTest, EGLInvalidColorspaceAttribute) { sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); - consumer->consumerConnect(new DummyConsumer, false); + consumer->consumerConnect(new MockConsumer, false); sp<Surface> mSTC = new Surface(producer); sp<ANativeWindow> mANW = mSTC; @@ -882,7 +882,7 @@ TEST_F(EGLTest, EGLUnsupportedColorspaceFormatCombo) { ASSERT_EQ(EGL_UNSIGNED_TRUE, success); ASSERT_EQ(1, numConfigs); - struct DummyConsumer : public BnConsumerListener { + struct MockConsumer : public BnConsumerListener { void onFrameAvailable(const BufferItem& /* item */) override {} void onBuffersReleased() override {} void onSidebandStreamChanged() override {} @@ -892,7 +892,7 @@ TEST_F(EGLTest, EGLUnsupportedColorspaceFormatCombo) { sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); - consumer->consumerConnect(new DummyConsumer, false); + consumer->consumerConnect(new MockConsumer, false); sp<Surface> mSTC = new Surface(producer); sp<ANativeWindow> mANW = mSTC; @@ -913,7 +913,7 @@ TEST_F(EGLTest, EGLCreateWindowFailAndSucceed) { ASSERT_NO_FATAL_FAILURE(get8BitConfig(config)); - struct DummyConsumer : public BnConsumerListener { + struct MockConsumer : public BnConsumerListener { void onFrameAvailable(const BufferItem& /* item */) override {} void onBuffersReleased() override {} void onSidebandStreamChanged() override {} @@ -923,7 +923,7 @@ TEST_F(EGLTest, EGLCreateWindowFailAndSucceed) { sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); - consumer->consumerConnect(new DummyConsumer, false); + consumer->consumerConnect(new MockConsumer, false); sp<Surface> mSTC = new Surface(producer); sp<ANativeWindow> mANW = mSTC; @@ -953,7 +953,7 @@ TEST_F(EGLTest, EGLCreateWindowTwoColorspaces) { ASSERT_NO_FATAL_FAILURE(get8BitConfig(config)); - struct DummyConsumer : public BnConsumerListener { + struct MockConsumer : public BnConsumerListener { void onFrameAvailable(const BufferItem& /* item */) override {} void onBuffersReleased() override {} void onSidebandStreamChanged() override {} @@ -963,7 +963,7 @@ TEST_F(EGLTest, EGLCreateWindowTwoColorspaces) { sp<IGraphicBufferProducer> producer; sp<IGraphicBufferConsumer> consumer; BufferQueue::createBufferQueue(&producer, &consumer); - consumer->consumerConnect(new DummyConsumer, false); + consumer->consumerConnect(new MockConsumer, false); sp<Surface> mSTC = new Surface(producer); sp<ANativeWindow> mANW = mSTC; diff --git a/opengl/tests/gl2_jni/Android.bp b/opengl/tests/gl2_jni/Android.bp index 65f89b159e..8d4323f9ca 100644 --- a/opengl/tests/gl2_jni/Android.bp +++ b/opengl/tests/gl2_jni/Android.bp @@ -17,6 +17,7 @@ cc_library_shared { "-Werror", "-Wno-error=unused-parameter", ], + header_libs: ["jni_headers"], srcs: ["jni/gl_code.cpp"], shared_libs: [ "liblog", diff --git a/opengl/tests/gl_jni/Android.bp b/opengl/tests/gl_jni/Android.bp index 5bec336dd1..0cb129a117 100644 --- a/opengl/tests/gl_jni/Android.bp +++ b/opengl/tests/gl_jni/Android.bp @@ -19,6 +19,7 @@ cc_library_shared { "-Werror", "-Wno-error=unused-parameter", ], + header_libs: ["jni_headers"], srcs: ["jni/gl_code.cpp"], shared_libs: [ "liblog", diff --git a/opengl/tests/gl_perfapp/Android.bp b/opengl/tests/gl_perfapp/Android.bp index cf899acfee..66afb6a82c 100644 --- a/opengl/tests/gl_perfapp/Android.bp +++ b/opengl/tests/gl_perfapp/Android.bp @@ -17,6 +17,7 @@ cc_library_shared { "-Werror", "-Wno-error=unused-parameter", ], + header_libs: ["jni_headers"], srcs: ["jni/gl_code.cpp"], shared_libs: [ "liblog", diff --git a/opengl/tests/gldual/Android.bp b/opengl/tests/gldual/Android.bp index 24325666e6..1006d44e47 100644 --- a/opengl/tests/gldual/Android.bp +++ b/opengl/tests/gldual/Android.bp @@ -20,6 +20,7 @@ cc_library_shared { "-Werror", "-Wno-error=unused-parameter", ], + header_libs: ["jni_headers"], srcs: ["jni/gl_code.cpp"], shared_libs: [ "liblog", diff --git a/opengl/tools/glgen/gen b/opengl/tools/glgen/gen index 41fcf1bd95..7fd9c3a735 100755 --- a/opengl/tools/glgen/gen +++ b/opengl/tools/glgen/gen @@ -1,11 +1,23 @@ #!/bin/bash set -u set -e + +if [ -z "$ANDROID_BUILD_TOP" ] ; then + echo "ANDROID_BUILD_TOP is not set, did you run lunch?" + exit 1 +fi + +# Avoid spewing files in any location other than the intended one. +if [ ! -x "$PWD/gen" ] ; then + echo "Run this script from its parent directory". + exit 1 +fi + rm -rf out generated mkdir out -# Create dummy Java files for Android APIs that are used by the code we generate. +# Create stub Java files for Android APIs that are used by the code we generate. # This allows us to test the generated code without building the rest of Android. mkdir -p out/javax/microedition/khronos/opengles @@ -92,7 +104,7 @@ rm src/*.class # Add UnsupportedAppUsage.java to known sources. mkdir -p out/android/compat/annotation -cp ../../../../../tools/platform-compat/annotation/src/java/android/compat/annotation/UnsupportedAppUsage.java out/android/compat/annotation +cp ${ANDROID_BUILD_TOP}/tools/platform-compat/java/android/compat/annotation/UnsupportedAppUsage.java out/android/compat/annotation pushd out > /dev/null mkdir classes @@ -153,23 +165,23 @@ compareGenerated() { fi } -compareGenerated ../../../../base/core/jni generated/C com_google_android_gles_jni_GLImpl.cpp -compareGenerated ../../../../base/opengl/java/com/google/android/gles_jni generated/com/google/android/gles_jni GLImpl.java +compareGenerated ${ANDROID_BUILD_TOP}/frameworks/base/core/jni generated/C com_google_android_gles_jni_GLImpl.cpp +compareGenerated ${ANDROID_BUILD_TOP}/frameworks/base/opengl/java/com/google/android/gles_jni generated/com/google/android/gles_jni GLImpl.java for x in GL.java GL10.java GL10Ext.java GL11.java GL11Ext.java GL11ExtensionPack.java do - compareGenerated ../../../../base/opengl/java/javax/microedition/khronos/opengles generated/javax/microedition/khronos/opengles $x + compareGenerated ${ANDROID_BUILD_TOP}/frameworks/base/opengl/java/javax/microedition/khronos/opengles generated/javax/microedition/khronos/opengles $x done for x in EGL14 EGL15 EGLExt GLES10 GLES10Ext GLES11 GLES11Ext GLES20 GLES30 GLES31 GLES31Ext GLES32 do - compareGenerated ../../../../base/opengl/java/android/opengl generated/android/opengl ${x}.java - compareGenerated ../../../../base/core/jni generated/C android_opengl_${x}.cpp + compareGenerated ${ANDROID_BUILD_TOP}/frameworks/base/opengl/java/android/opengl generated/android/opengl ${x}.java + compareGenerated ${ANDROID_BUILD_TOP}/frameworks/base/core/jni generated/C android_opengl_${x}.cpp done for x in EGLConfig EGLContext EGLDisplay EGLObjectHandle EGLSurface EGLImage EGLSync do - compareGenerated ../../../../base/opengl/java/android/opengl generated/android/opengl ${x}.java + compareGenerated ${ANDROID_BUILD_TOP}/frameworks/base/opengl/java/android/opengl generated/android/opengl ${x}.java done if [ $KEEP_GENERATED == "0" ] ; then diff --git a/opengl/tools/glgen/stubs/egl/EGL14Header.java-if b/opengl/tools/glgen/stubs/egl/EGL14Header.java-if index 9932556d27..951ecffc32 100644 --- a/opengl/tools/glgen/stubs/egl/EGL14Header.java-if +++ b/opengl/tools/glgen/stubs/egl/EGL14Header.java-if @@ -21,8 +21,8 @@ package android.opengl; import android.compat.annotation.UnsupportedAppUsage; import android.graphics.SurfaceTexture; import android.view.Surface; -import android.view.SurfaceView; import android.view.SurfaceHolder; +import android.view.SurfaceView; /** * EGL 1.4 diff --git a/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp b/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp index 93203fd529..b2ea041cd5 100644 --- a/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp +++ b/opengl/tools/glgen/stubs/egl/EGL14cHeader.cpp @@ -20,7 +20,7 @@ #pragma GCC diagnostic ignored "-Wunused-function" #include "jni.h" -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <android_runtime/AndroidRuntime.h> #include <android_runtime/android_view_Surface.h> #include <android_runtime/android_graphics_SurfaceTexture.h> diff --git a/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp b/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp index 34cb3e1a99..6dffac5945 100644 --- a/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp +++ b/opengl/tools/glgen/stubs/egl/EGL15cHeader.cpp @@ -20,7 +20,7 @@ #pragma GCC diagnostic ignored "-Wunused-function" #include "jni.h" -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <android_runtime/AndroidRuntime.h> #include <utils/misc.h> diff --git a/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp b/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp index b3b069082a..be8b3e3977 100644 --- a/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp +++ b/opengl/tools/glgen/stubs/egl/EGLExtcHeader.cpp @@ -20,7 +20,7 @@ #pragma GCC diagnostic ignored "-Wunused-function" #include "jni.h" -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <android_runtime/AndroidRuntime.h> #include <android_runtime/android_view_Surface.h> #include <android_runtime/android_graphics_SurfaceTexture.h> diff --git a/opengl/tools/glgen/stubs/gles11/common.cpp b/opengl/tools/glgen/stubs/gles11/common.cpp index e763b4e983..d84a693e6c 100644 --- a/opengl/tools/glgen/stubs/gles11/common.cpp +++ b/opengl/tools/glgen/stubs/gles11/common.cpp @@ -1,5 +1,5 @@ #include <jni.h> -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <android_runtime/AndroidRuntime.h> #include <utils/misc.h> #include <assert.h> diff --git a/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp b/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp index c12efc3795..9cab1d6a59 100644 --- a/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp +++ b/opengl/tools/glgen/stubs/jsr239/GLCHeader.cpp @@ -20,7 +20,7 @@ #pragma GCC diagnostic ignored "-Wunused-function" #include "jni.h" -#include <nativehelper/JNIHelp.h> +#include <nativehelper/JNIPlatformHelp.h> #include <android_runtime/AndroidRuntime.h> #include <utils/misc.h> diff --git a/opengl/tools/glgen/stubs/jsr239/GLImplHeader.java-impl b/opengl/tools/glgen/stubs/jsr239/GLImplHeader.java-impl index afcc3ebe7f..32c9d7d09f 100644 --- a/opengl/tools/glgen/stubs/jsr239/GLImplHeader.java-impl +++ b/opengl/tools/glgen/stubs/jsr239/GLImplHeader.java-impl @@ -19,6 +19,7 @@ package com.google.android.gles_jni; import android.app.AppGlobals; +import android.compat.annotation.UnsupportedAppUsage; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.os.Build; @@ -26,6 +27,7 @@ import android.os.UserHandle; import android.util.Log; import java.nio.Buffer; + import javax.microedition.khronos.opengles.GL10; import javax.microedition.khronos.opengles.GL10Ext; import javax.microedition.khronos.opengles.GL11; @@ -55,6 +57,7 @@ public class GLImpl implements GL10, GL10Ext, GL11, GL11Ext, GL11ExtensionPack { private boolean have_OES_framebuffer_object; private boolean have_OES_texture_cube_map; + @UnsupportedAppUsage public GLImpl() { } diff --git a/services/inputflinger/host/Android.bp b/services/inputflinger/host/Android.bp index cbe0190dfa..b56f356dfd 100644 --- a/services/inputflinger/host/Android.bp +++ b/services/inputflinger/host/Android.bp @@ -21,6 +21,7 @@ cc_library_shared { "InputHost.cpp", ], + header_libs: ["jni_headers"], shared_libs: [ "libbinder", "libcrypto", @@ -42,6 +43,7 @@ cc_library_shared { //-fvisibility=hidden ], + export_header_lib_headers: ["jni_headers"], export_include_dirs: ["."], } diff --git a/services/inputflinger/host/InputDriver.cpp b/services/inputflinger/host/InputDriver.cpp index 683c05d8a0..c9001b0224 100644 --- a/services/inputflinger/host/InputDriver.cpp +++ b/services/inputflinger/host/InputDriver.cpp @@ -29,8 +29,8 @@ #include <hardware/input.h> #include <input/InputDevice.h> +#include <input/PropertyMap.h> #include <utils/Log.h> -#include <utils/PropertyMap.h> #include <utils/String8.h> #define INDENT2 " " diff --git a/services/inputflinger/include/InputReaderBase.h b/services/inputflinger/include/InputReaderBase.h index f8d535152e..0fa878759b 100644 --- a/services/inputflinger/include/InputReaderBase.h +++ b/services/inputflinger/include/InputReaderBase.h @@ -335,7 +335,8 @@ public: virtual void getReaderConfiguration(InputReaderConfiguration* outConfig) = 0; /* Gets a pointer controller associated with the specified cursor device (ie. a mouse). */ - virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId) = 0; + virtual std::shared_ptr<PointerControllerInterface> obtainPointerController( + int32_t deviceId) = 0; /* Notifies the input reader policy that some input devices have changed * and provides information about all current input devices. diff --git a/services/inputflinger/include/PointerControllerInterface.h b/services/inputflinger/include/PointerControllerInterface.h index 194c66547f..85d7247915 100644 --- a/services/inputflinger/include/PointerControllerInterface.h +++ b/services/inputflinger/include/PointerControllerInterface.h @@ -33,7 +33,7 @@ namespace android { * The pointer controller is responsible for providing synchronization and for tracking * display orientation changes if needed. */ -class PointerControllerInterface : public virtual RefBase { +class PointerControllerInterface { protected: PointerControllerInterface() { } virtual ~PointerControllerInterface() { } @@ -59,11 +59,11 @@ public: /* Gets the absolute location of the pointer. */ virtual void getPosition(float* outX, float* outY) const = 0; - enum Transition { + enum class Transition { // Fade/unfade immediately. - TRANSITION_IMMEDIATE, + IMMEDIATE, // Fade/unfade gradually. - TRANSITION_GRADUAL, + GRADUAL, }; /* Fades the pointer out now. */ @@ -75,11 +75,11 @@ public: * wants to ensure that the pointer becomes visible again. */ virtual void unfade(Transition transition) = 0; - enum Presentation { + enum class Presentation { // Show the mouse pointer. - PRESENTATION_POINTER, + POINTER, // Show spots and a spot anchor in place of the mouse pointer. - PRESENTATION_SPOT, + SPOT, }; /* Sets the mode of the pointer controller. */ diff --git a/services/inputflinger/reader/InputReader.cpp b/services/inputflinger/reader/InputReader.cpp index 657a134865..06e374353c 100644 --- a/services/inputflinger/reader/InputReader.cpp +++ b/services/inputflinger/reader/InputReader.cpp @@ -390,8 +390,9 @@ bool InputReader::shouldDropVirtualKeyLocked(nsecs_t now, int32_t keyCode, int32 } } -sp<PointerControllerInterface> InputReader::getPointerControllerLocked(int32_t deviceId) { - sp<PointerControllerInterface> controller = mPointerController.promote(); +std::shared_ptr<PointerControllerInterface> InputReader::getPointerControllerLocked( + int32_t deviceId) { + std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock(); if (controller == nullptr) { controller = mPolicy->obtainPointerController(deviceId); mPointerController = controller; @@ -401,7 +402,7 @@ sp<PointerControllerInterface> InputReader::getPointerControllerLocked(int32_t d } void InputReader::updatePointerDisplayLocked() { - sp<PointerControllerInterface> controller = mPointerController.promote(); + std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock(); if (controller == nullptr) { return; } @@ -424,9 +425,9 @@ void InputReader::updatePointerDisplayLocked() { } void InputReader::fadePointerLocked() { - sp<PointerControllerInterface> controller = mPointerController.promote(); + std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock(); if (controller != nullptr) { - controller->fade(PointerControllerInterface::TRANSITION_GRADUAL); + controller->fade(PointerControllerInterface::Transition::GRADUAL); } } @@ -725,7 +726,8 @@ void InputReader::ContextImpl::fadePointer() { mReader->fadePointerLocked(); } -sp<PointerControllerInterface> InputReader::ContextImpl::getPointerController(int32_t deviceId) { +std::shared_ptr<PointerControllerInterface> InputReader::ContextImpl::getPointerController( + int32_t deviceId) { // lock is already held by the input loop return mReader->getPointerControllerLocked(deviceId); } diff --git a/services/inputflinger/reader/include/EventHub.h b/services/inputflinger/reader/include/EventHub.h index c17f3a184d..f5451d7fef 100644 --- a/services/inputflinger/reader/include/EventHub.h +++ b/services/inputflinger/reader/include/EventHub.h @@ -24,6 +24,7 @@ #include <input/KeyCharacterMap.h> #include <input/KeyLayoutMap.h> #include <input/Keyboard.h> +#include <input/PropertyMap.h> #include <input/VirtualKeyMap.h> #include <utils/BitSet.h> #include <utils/Errors.h> @@ -31,7 +32,6 @@ #include <utils/List.h> #include <utils/Log.h> #include <utils/Mutex.h> -#include <utils/PropertyMap.h> #include <linux/input.h> #include <sys/epoll.h> diff --git a/services/inputflinger/reader/include/InputDevice.h b/services/inputflinger/reader/include/InputDevice.h index 71313fc86f..7c17102826 100644 --- a/services/inputflinger/reader/include/InputDevice.h +++ b/services/inputflinger/reader/include/InputDevice.h @@ -19,8 +19,8 @@ #include <input/DisplayViewport.h> #include <input/InputDevice.h> +#include <input/PropertyMap.h> #include <stdint.h> -#include <utils/PropertyMap.h> #include <optional> #include <unordered_map> diff --git a/services/inputflinger/reader/include/InputReader.h b/services/inputflinger/reader/include/InputReader.h index 693ec30b7d..108b9c236c 100644 --- a/services/inputflinger/reader/include/InputReader.h +++ b/services/inputflinger/reader/include/InputReader.h @@ -17,19 +17,20 @@ #ifndef _UI_INPUTREADER_INPUT_READER_H #define _UI_INPUTREADER_INPUT_READER_H -#include "EventHub.h" -#include "InputListener.h" -#include "InputReaderBase.h" -#include "InputReaderContext.h" -#include "InputThread.h" - #include <PointerControllerInterface.h> #include <utils/Condition.h> #include <utils/Mutex.h> +#include <memory> #include <unordered_map> #include <vector> +#include "EventHub.h" +#include "InputListener.h" +#include "InputReaderBase.h" +#include "InputReaderContext.h" +#include "InputThread.h" + namespace android { class InputDevice; @@ -104,7 +105,8 @@ protected: virtual void disableVirtualKeysUntil(nsecs_t time) override; virtual bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) override; virtual void fadePointer() override; - virtual sp<PointerControllerInterface> getPointerController(int32_t deviceId) override; + virtual std::shared_ptr<PointerControllerInterface> getPointerController( + int32_t deviceId) override; virtual void requestTimeoutAtTime(nsecs_t when) override; virtual int32_t bumpGeneration() override; virtual void getExternalStylusDevices(std::vector<InputDeviceInfo>& outDevices) override; @@ -160,8 +162,8 @@ private: void dispatchExternalStylusState(const StylusState& state); // The PointerController that is shared among all the input devices that need it. - wp<PointerControllerInterface> mPointerController; - sp<PointerControllerInterface> getPointerControllerLocked(int32_t deviceId); + std::weak_ptr<PointerControllerInterface> mPointerController; + std::shared_ptr<PointerControllerInterface> getPointerControllerLocked(int32_t deviceId); void updatePointerDisplayLocked(); void fadePointerLocked(); diff --git a/services/inputflinger/reader/include/InputReaderContext.h b/services/inputflinger/reader/include/InputReaderContext.h index 85701e4f63..ffb8d8c44a 100644 --- a/services/inputflinger/reader/include/InputReaderContext.h +++ b/services/inputflinger/reader/include/InputReaderContext.h @@ -46,7 +46,7 @@ public: virtual bool shouldDropVirtualKey(nsecs_t now, int32_t keyCode, int32_t scanCode) = 0; virtual void fadePointer() = 0; - virtual sp<PointerControllerInterface> getPointerController(int32_t deviceId) = 0; + virtual std::shared_ptr<PointerControllerInterface> getPointerController(int32_t deviceId) = 0; virtual void requestTimeoutAtTime(nsecs_t when) = 0; virtual int32_t bumpGeneration() = 0; diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.cpp b/services/inputflinger/reader/mapper/CursorInputMapper.cpp index 887ab53c76..1a4d5517e2 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.cpp +++ b/services/inputflinger/reader/mapper/CursorInputMapper.cpp @@ -20,6 +20,7 @@ #include "CursorButtonAccumulator.h" #include "CursorScrollAccumulator.h" +#include "PointerControllerInterface.h" #include "TouchCursorInputMapperCommon.h" namespace android { @@ -154,7 +155,7 @@ void CursorInputMapper::configure(nsecs_t when, const InputReaderConfiguration* mParameters.mode = Parameters::MODE_POINTER_RELATIVE; mSource = AINPUT_SOURCE_MOUSE_RELATIVE; // Keep PointerController around in order to preserve the pointer position. - mPointerController->fade(PointerControllerInterface::TRANSITION_IMMEDIATE); + mPointerController->fade(PointerControllerInterface::Transition::IMMEDIATE); } else { ALOGE("Cannot request pointer capture, device is not in MODE_POINTER"); } @@ -316,7 +317,7 @@ void CursorInputMapper::sync(nsecs_t when) { float yCursorPosition = AMOTION_EVENT_INVALID_CURSOR_POSITION; if (mSource == AINPUT_SOURCE_MOUSE) { if (moved || scrolled || buttonsChanged) { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); + mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER); if (moved) { mPointerController->move(deltaX, deltaY); @@ -326,7 +327,7 @@ void CursorInputMapper::sync(nsecs_t when) { mPointerController->setButtonState(currentButtonState); } - mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); + mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE); } mPointerController->getPosition(&xCursorPosition, &yCursorPosition); diff --git a/services/inputflinger/reader/mapper/CursorInputMapper.h b/services/inputflinger/reader/mapper/CursorInputMapper.h index f65ac3934a..05bbb26716 100644 --- a/services/inputflinger/reader/mapper/CursorInputMapper.h +++ b/services/inputflinger/reader/mapper/CursorInputMapper.h @@ -106,7 +106,7 @@ private: int32_t mOrientation; - sp<PointerControllerInterface> mPointerController; + std::shared_ptr<PointerControllerInterface> mPointerController; int32_t mButtonState; nsecs_t mDownTime; diff --git a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h index 2a3e2637f1..a86443dee1 100644 --- a/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h +++ b/services/inputflinger/reader/mapper/TouchCursorInputMapperCommon.h @@ -17,12 +17,13 @@ #ifndef _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H #define _UI_INPUTREADER_TOUCH_CURSOR_INPUT_MAPPER_COMMON_H +#include <input/DisplayViewport.h> +#include <stdint.h> + #include "EventHub.h" #include "InputListener.h" #include "InputReaderContext.h" -#include <stdint.h> - namespace android { // --- Static Definitions --- diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp index 99a572a5fd..efdc84fd8f 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp +++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp @@ -765,7 +765,7 @@ void TouchInputMapper::configureSurface(nsecs_t when, bool* outResetNeeded) { mPointerController = getContext()->getPointerController(getDeviceId()); } } else { - mPointerController.clear(); + mPointerController.reset(); } if (viewportChanged || deviceModeChanged) { @@ -1383,7 +1383,7 @@ void TouchInputMapper::reset(nsecs_t when) { resetExternalStylus(); if (mPointerController != nullptr) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + mPointerController->fade(PointerControllerInterface::Transition::GRADUAL); mPointerController->clearSpots(); } @@ -1589,8 +1589,8 @@ void TouchInputMapper::cookAndDispatch(nsecs_t when) { } else { if (mDeviceMode == DEVICE_MODE_DIRECT && mConfig.showTouches && mPointerController != nullptr) { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_SPOT); - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + mPointerController->setPresentation(PointerControllerInterface::Presentation::SPOT); + mPointerController->fade(PointerControllerInterface::Transition::GRADUAL); mPointerController->setButtonState(mCurrentRawState.buttonState); mPointerController->setSpots(mCurrentCookedState.cookedPointerData.pointerCoords, @@ -2327,7 +2327,7 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag // Update the pointer presentation and spots. if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); + mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER); if (finishPreviousGesture || cancelPreviousGesture) { mPointerController->clearSpots(); } @@ -2339,7 +2339,7 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag mPointerController->getDisplayId()); } } else { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); + mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER); } // Show or hide the pointer if needed. @@ -2349,7 +2349,7 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH && mPointerGesture.lastGestureMode == PointerGesture::FREEFORM) { // Remind the user of where the pointer is after finishing a gesture with spots. - mPointerController->unfade(PointerControllerInterface::TRANSITION_GRADUAL); + mPointerController->unfade(PointerControllerInterface::Transition::GRADUAL); } break; case PointerGesture::TAP: @@ -2360,15 +2360,15 @@ void TouchInputMapper::dispatchPointerGestures(nsecs_t when, uint32_t policyFlag case PointerGesture::SWIPE: // Unfade the pointer when the current gesture manipulates the // area directly under the pointer. - mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); + mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE); break; case PointerGesture::FREEFORM: // Fade the pointer when the current gesture manipulates a different // area and there are spots to guide the user experience. if (mParameters.gestureMode == Parameters::GESTURE_MODE_MULTI_TOUCH) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + mPointerController->fade(PointerControllerInterface::Transition::GRADUAL); } else { - mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); + mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE); } break; } @@ -2537,7 +2537,7 @@ void TouchInputMapper::abortPointerGestures(nsecs_t when, uint32_t policyFlags) // Remove any current spots. if (mPointerController != nullptr) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + mPointerController->fade(PointerControllerInterface::Transition::GRADUAL); mPointerController->clearSpots(); } } @@ -3396,12 +3396,12 @@ void TouchInputMapper::dispatchPointerSimple(nsecs_t when, uint32_t policyFlags, int32_t displayId = mViewport.displayId; if (down || hovering) { - mPointerController->setPresentation(PointerControllerInterface::PRESENTATION_POINTER); + mPointerController->setPresentation(PointerControllerInterface::Presentation::POINTER); mPointerController->clearSpots(); mPointerController->setButtonState(mCurrentRawState.buttonState); - mPointerController->unfade(PointerControllerInterface::TRANSITION_IMMEDIATE); + mPointerController->unfade(PointerControllerInterface::Transition::IMMEDIATE); } else if (!down && !hovering && (mPointerSimple.down || mPointerSimple.hovering)) { - mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); + mPointerController->fade(PointerControllerInterface::Transition::GRADUAL); } displayId = mPointerController->getDisplayId(); diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.h b/services/inputflinger/reader/mapper/TouchInputMapper.h index 58bfc5c596..7f811a0e1b 100644 --- a/services/inputflinger/reader/mapper/TouchInputMapper.h +++ b/services/inputflinger/reader/mapper/TouchInputMapper.h @@ -376,7 +376,7 @@ protected: nsecs_t mDownTime; // The pointer controller, or null if the device is not a pointer. - sp<PointerControllerInterface> mPointerController; + std::shared_ptr<PointerControllerInterface> mPointerController; std::vector<VirtualKey> mVirtualKeys; diff --git a/services/inputflinger/tests/InputReader_test.cpp b/services/inputflinger/tests/InputReader_test.cpp index c457a1525f..18bd3d06e1 100644 --- a/services/inputflinger/tests/InputReader_test.cpp +++ b/services/inputflinger/tests/InputReader_test.cpp @@ -27,12 +27,13 @@ #include <TestInputListener.h> #include <TouchInputMapper.h> #include <UinputDevice.h> - #include <android-base/thread_annotations.h> #include <gtest/gtest.h> #include <inttypes.h> #include <math.h> +#include <memory> + namespace android { using std::chrono_literals::operator""ms; @@ -76,15 +77,14 @@ class FakePointerController : public PointerControllerInterface { int32_t mButtonState; int32_t mDisplayId; -protected: - virtual ~FakePointerController() { } - public: FakePointerController() : mHaveBounds(false), mMinX(0), mMinY(0), mMaxX(0), mMaxY(0), mX(0), mY(0), mButtonState(0), mDisplayId(ADISPLAY_ID_DEFAULT) { } + virtual ~FakePointerController() {} + void setBounds(float minX, float minY, float maxX, float maxY) { mHaveBounds = true; mMinX = minX; @@ -176,7 +176,7 @@ class FakeInputReaderPolicy : public InputReaderPolicyInterface { std::condition_variable mDevicesChangedCondition; InputReaderConfiguration mConfig; - KeyedVector<int32_t, sp<FakePointerController> > mPointerControllers; + std::unordered_map<int32_t, std::shared_ptr<FakePointerController>> mPointerControllers; std::vector<InputDeviceInfo> mInputDevices GUARDED_BY(mLock); bool mInputDevicesChanged GUARDED_BY(mLock){false}; std::vector<DisplayViewport> mViewports; @@ -256,8 +256,8 @@ public: void removeDisabledDevice(int32_t deviceId) { mConfig.disabledDevices.erase(deviceId); } - void setPointerController(int32_t deviceId, const sp<FakePointerController>& controller) { - mPointerControllers.add(deviceId, controller); + void setPointerController(int32_t deviceId, std::shared_ptr<FakePointerController> controller) { + mPointerControllers.insert_or_assign(deviceId, std::move(controller)); } const InputReaderConfiguration* getReaderConfiguration() const { @@ -318,8 +318,8 @@ private: *outConfig = mConfig; } - virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId) { - return mPointerControllers.valueFor(deviceId); + virtual std::shared_ptr<PointerControllerInterface> obtainPointerController(int32_t deviceId) { + return mPointerControllers[deviceId]; } virtual void notifyInputDevicesChanged(const std::vector<InputDeviceInfo>& inputDevices) { @@ -847,7 +847,7 @@ class FakeInputReaderContext : public InputReaderContext { bool mUpdateGlobalMetaStateWasCalled; int32_t mGeneration; int32_t mNextId; - wp<PointerControllerInterface> mPointerController; + std::weak_ptr<PointerControllerInterface> mPointerController; public: FakeInputReaderContext(std::shared_ptr<EventHubInterface> eventHub, @@ -876,7 +876,7 @@ public: } void updatePointerDisplay() { - sp<PointerControllerInterface> controller = mPointerController.promote(); + std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock(); if (controller != nullptr) { InputReaderConfiguration config; mPolicy->getReaderConfiguration(&config); @@ -913,8 +913,8 @@ private: virtual bool shouldDropVirtualKey(nsecs_t, int32_t, int32_t) { return false; } - virtual sp<PointerControllerInterface> getPointerController(int32_t deviceId) { - sp<PointerControllerInterface> controller = mPointerController.promote(); + virtual std::shared_ptr<PointerControllerInterface> getPointerController(int32_t deviceId) { + std::shared_ptr<PointerControllerInterface> controller = mPointerController.lock(); if (controller == nullptr) { controller = mPolicy->obtainPointerController(deviceId); mPointerController = controller; @@ -2348,9 +2348,9 @@ protected: ASSERT_NEAR(distance, coords.getAxisValue(AMOTION_EVENT_AXIS_DISTANCE), EPSILON); } - static void assertPosition(const sp<FakePointerController>& controller, float x, float y) { + static void assertPosition(const FakePointerController& controller, float x, float y) { float actualX, actualY; - controller->getPosition(&actualX, &actualY); + controller.getPosition(&actualX, &actualY); ASSERT_NEAR(x, actualX, 1); ASSERT_NEAR(y, actualY, 1); } @@ -3021,12 +3021,12 @@ class CursorInputMapperTest : public InputMapperTest { protected: static const int32_t TRACKBALL_MOVEMENT_THRESHOLD; - sp<FakePointerController> mFakePointerController; + std::shared_ptr<FakePointerController> mFakePointerController; virtual void SetUp() override { InputMapperTest::SetUp(); - mFakePointerController = new FakePointerController(); + mFakePointerController = std::make_shared<FakePointerController>(); mFakePolicy->setPointerController(mDevice->getId(), mFakePointerController); } @@ -3682,7 +3682,7 @@ TEST_F(CursorInputMapperTest, Process_WhenModeIsPointer_ShouldMoveThePointerArou ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action); ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], 110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); - ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 110.0f, 220.0f)); + ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f)); } TEST_F(CursorInputMapperTest, Process_PointerCapture) { @@ -3710,7 +3710,7 @@ TEST_F(CursorInputMapperTest, Process_PointerCapture) { ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], 10.0f, 20.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); - ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 100.0f, 200.0f)); + ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 100.0f, 200.0f)); // Button press. process(mapper, ARBITRARY_TIME, EV_KEY, BTN_MOUSE, 1); @@ -3749,7 +3749,7 @@ TEST_F(CursorInputMapperTest, Process_PointerCapture) { ASSERT_EQ(AMOTION_EVENT_ACTION_MOVE, args.action); ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], 30.0f, 40.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); - ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 100.0f, 200.0f)); + ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 100.0f, 200.0f)); // Disable pointer capture and check that the device generation got bumped // and events are generated the usual way. @@ -3770,7 +3770,7 @@ TEST_F(CursorInputMapperTest, Process_PointerCapture) { ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action); ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], 110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); - ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 110.0f, 220.0f)); + ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f)); } TEST_F(CursorInputMapperTest, Process_ShouldHandleDisplayId) { @@ -3798,7 +3798,7 @@ TEST_F(CursorInputMapperTest, Process_ShouldHandleDisplayId) { ASSERT_EQ(AMOTION_EVENT_ACTION_HOVER_MOVE, args.action); ASSERT_NO_FATAL_FAILURE(assertPointerCoords(args.pointerCoords[0], 110.0f, 220.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f)); - ASSERT_NO_FATAL_FAILURE(assertPosition(mFakePointerController, 110.0f, 220.0f)); + ASSERT_NO_FATAL_FAILURE(assertPosition(*mFakePointerController, 110.0f, 220.0f)); ASSERT_EQ(SECOND_DISPLAY_ID, args.displayId); } @@ -6806,7 +6806,8 @@ TEST_F(MultiTouchInputMapperTest, Configure_AssignsDisplayPort) { TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShouldHandleDisplayId) { // Setup for second display. - sp<FakePointerController> fakePointerController = new FakePointerController(); + std::shared_ptr<FakePointerController> fakePointerController = + std::make_shared<FakePointerController>(); fakePointerController->setBounds(0, 0, DISPLAY_WIDTH - 1, DISPLAY_HEIGHT - 1); fakePointerController->setPosition(100, 200); fakePointerController->setButtonState(0); @@ -6866,7 +6867,8 @@ TEST_F(MultiTouchInputMapperTest, Process_Pointer_ShowTouches) { device2->reset(ARBITRARY_TIME); // Setup PointerController. - sp<FakePointerController> fakePointerController = new FakePointerController(); + std::shared_ptr<FakePointerController> fakePointerController = + std::make_shared<FakePointerController>(); mFakePolicy->setPointerController(mDevice->getId(), fakePointerController); mFakePolicy->setPointerController(SECOND_DEVICE_ID, fakePointerController); diff --git a/services/sensorservice/hidl/Android.bp b/services/sensorservice/hidl/Android.bp index d0c83d6002..0e1af595f0 100644 --- a/services/sensorservice/hidl/Android.bp +++ b/services/sensorservice/hidl/Android.bp @@ -10,6 +10,7 @@ cc_library_shared { "-Wall", "-Werror", ], + header_libs: ["jni_headers"], shared_libs: [ "libbase", "libhidlbase", @@ -24,6 +25,7 @@ cc_library_shared { export_include_dirs: [ "include/" ], + export_header_lib_headers: ["jni_headers"], local_include_dirs: [ "include/sensorservicehidl/" ] diff --git a/services/surfaceflinger/CompositionEngine/Android.bp b/services/surfaceflinger/CompositionEngine/Android.bp index b3b9fe5981..b37ca33d34 100644 --- a/services/surfaceflinger/CompositionEngine/Android.bp +++ b/services/surfaceflinger/CompositionEngine/Android.bp @@ -121,13 +121,6 @@ cc_test { // // You can either "make dist tests" before flashing, or set this // option to false temporarily. - - - // FIXME: ASAN build is broken for a while, but was not discovered - // since new PM silently suppressed ASAN. Temporarily turn off ASAN - // to unblock the compiler upgrade process. - // address: true, - // http://b/139747256 - address: false, + address: true, }, } diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h index 8a9763bbeb..b4ed92f965 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/LayerFECompositionState.h @@ -116,7 +116,7 @@ struct LayerFECompositionState { FloatRect geomLayerBounds; // length of the shadow in screen space - float shadowRadius; + float shadowRadius{0.f}; /* * Geometry state diff --git a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h index 75394fadb7..d2b38d1815 100644 --- a/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h +++ b/services/surfaceflinger/CompositionEngine/include/compositionengine/impl/OutputLayerCompositionState.h @@ -110,7 +110,7 @@ struct OutputLayerCompositionState { void dump(std::string& result) const; // Timestamp for when the layer is queued for client composition - nsecs_t clientCompositionTimestamp; + nsecs_t clientCompositionTimestamp{0}; }; } // namespace compositionengine::impl diff --git a/services/surfaceflinger/CompositionEngine/src/Output.cpp b/services/surfaceflinger/CompositionEngine/src/Output.cpp index e8f54f57b1..34dc53648c 100644 --- a/services/surfaceflinger/CompositionEngine/src/Output.cpp +++ b/services/surfaceflinger/CompositionEngine/src/Output.cpp @@ -952,7 +952,7 @@ std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests( const bool useIdentityTransform = false; bool firstLayer = true; // Used when a layer clears part of the buffer. - Region dummyRegion; + Region stubRegion; for (auto* layer : getOutputLayersOrderedByZ()) { const auto& layerState = layer->getState(); @@ -991,7 +991,7 @@ std::vector<LayerFE::LayerSettings> Output::generateClientCompositionRequests( layer->needsFiltering() || outputState.needsFiltering, outputState.isSecure, supportsProtectedContent, - clientComposition ? clearRegion : dummyRegion, + clientComposition ? clearRegion : stubRegion, outputState.viewport, outputDataspace, realContentIsVisible, diff --git a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp index 59ed72e928..7a06400d62 100644 --- a/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp +++ b/services/surfaceflinger/CompositionEngine/tests/OutputTest.cpp @@ -3583,15 +3583,15 @@ TEST_F(GenerateClientCompositionRequestsTest_ThreeLayers, clearsHWCLayersIfOpaqu mLayers[1].mLayerFEState.isOpaque = true; mLayers[2].mLayerFEState.isOpaque = true; Region accumClearRegion(Rect(10, 11, 12, 13)); - Region dummyRegion; + Region stubRegion; compositionengine::LayerFE::ClientCompositionTargetSettings layer1TargetSettings{ Region(kDisplayFrame), - false, /* identity transform */ - false, /* needs filtering */ - false, /* secure */ - false, /* supports protected content */ - dummyRegion, /* clear region */ + false, /* identity transform */ + false, /* needs filtering */ + false, /* secure */ + false, /* supports protected content */ + stubRegion, /* clear region */ kDisplayViewport, kDisplayDataspace, false /* realContentIsVisible */, diff --git a/services/surfaceflinger/OWNERS b/services/surfaceflinger/OWNERS index f2bc65db35..c5a4689dda 100644 --- a/services/surfaceflinger/OWNERS +++ b/services/surfaceflinger/OWNERS @@ -3,8 +3,8 @@ akrulec@google.com alecmouri@google.com chaviw@google.com lpy@google.com -marissaw@google.com racarr@google.com steventhomas@google.com stoza@google.com vhau@google.com +vishnun@google.com diff --git a/services/surfaceflinger/Scheduler/DispSync.cpp b/services/surfaceflinger/Scheduler/DispSync.cpp index ff91bf7bc0..46112f5cf4 100644 --- a/services/surfaceflinger/Scheduler/DispSync.cpp +++ b/services/surfaceflinger/Scheduler/DispSync.cpp @@ -340,7 +340,7 @@ private: return nextEventTime; } - // Sanity check that the duration is close enough in length to a period without + // Check that the duration is close enough in length to a period without // falling into double-rate vsyncs. bool isCloseToPeriod(nsecs_t duration) { // Ratio of 3/5 is arbitrary, but it must be greater than 1/2. diff --git a/services/surfaceflinger/Scheduler/DispSync.h b/services/surfaceflinger/Scheduler/DispSync.h index 832f08e07b..6fb5654b8a 100644 --- a/services/surfaceflinger/Scheduler/DispSync.h +++ b/services/surfaceflinger/Scheduler/DispSync.h @@ -227,7 +227,7 @@ private: // mZeroErrSamplesCount keeps track of how many times in a row there were // zero timestamps available in the mPresentFences array. - // Used to sanity check that we are able to calculate the model error. + // Used to check that we are able to calculate the model error. size_t mZeroErrSamplesCount; // Whether we have updated the vsync event model since the last resync. diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 148749e6f0..bfd8438e29 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -57,6 +57,7 @@ #include <gui/LayerMetadata.h> #include <gui/LayerState.h> #include <gui/Surface.h> +#include <hidl/ServiceManagement.h> #include <input/IInputFlinger.h> #include <layerproto/LayerProtoParser.h> #include <log/log.h> @@ -441,7 +442,7 @@ SurfaceFlinger::SurfaceFlinger(Factory& factory) : SurfaceFlinger(factory, SkipI // deriving the setting from the set service name, but it // would be brittle if the name that's not 'default' is used // for production purposes later on. - setenv("TREBLE_TESTING_OVERRIDE", "true", true); + android::hardware::details::setTrebleTestingOverride(true); } useFrameRateApi = use_frame_rate_api(true); diff --git a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop index cfc301bd22..7b1f0fb0a8 100644 --- a/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop +++ b/services/surfaceflinger/sysprop/SurfaceFlingerProperties.sysprop @@ -328,7 +328,7 @@ prop { prop { api_name: "refresh_rate_switching" type: Boolean - scope: System + scope: Public access: Readonly prop_name: "ro.surface_flinger.refresh_rate_switching" deprecated: true diff --git a/services/surfaceflinger/tests/BufferGenerator.cpp b/services/surfaceflinger/tests/BufferGenerator.cpp index 293738cfcb..4868c1224a 100644 --- a/services/surfaceflinger/tests/BufferGenerator.cpp +++ b/services/surfaceflinger/tests/BufferGenerator.cpp @@ -88,7 +88,7 @@ private: sp<Surface> mSurface; }; -/* Used to generate valid fences. It is not possible to create a dummy sync +/* Used to generate valid fences. It is not possible to create a placeholder sync * fence for testing. Egl can generate buffers along with a valid fence. * The buffer cannot be guaranteed to be the same format across all devices so * a CPU filled buffer is used instead. The Egl fence is used along with the diff --git a/services/surfaceflinger/tests/LayerTransaction_test.cpp b/services/surfaceflinger/tests/LayerTransaction_test.cpp index 97cba63192..1f8f7ed892 100644 --- a/services/surfaceflinger/tests/LayerTransaction_test.cpp +++ b/services/surfaceflinger/tests/LayerTransaction_test.cpp @@ -118,10 +118,9 @@ TEST_F(LayerTransactionTest, DISABLED_BufferQueueLayerMergeDamageRegionWhenDropp sp<SurfaceControl> layer; ASSERT_NO_FATAL_FAILURE(layer = createLayer("test", width, height)); const auto producer = layer->getIGraphicBufferProducer(); - const sp<IProducerListener> dummyListener(new DummyProducerListener); + const sp<IProducerListener> stubListener(new StubProducerListener); IGraphicBufferProducer::QueueBufferOutput queueBufferOutput; - ASSERT_EQ(OK, - producer->connect(dummyListener, NATIVE_WINDOW_API_CPU, true, &queueBufferOutput)); + ASSERT_EQ(OK, producer->connect(stubListener, NATIVE_WINDOW_API_CPU, true, &queueBufferOutput)); std::map<int, sp<GraphicBuffer>> slotMap; auto slotToBuffer = [&](int slot, sp<GraphicBuffer>* buf) { diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp index 96a7541919..1cea25a80f 100644 --- a/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp +++ b/services/surfaceflinger/tests/fakehwc/FakeComposerUtils.cpp @@ -29,8 +29,8 @@ #include "SurfaceFlinger.h" // Get the name of the service... #include <binder/IServiceManager.h> - #include <cutils/properties.h> +#include <hidl/ServiceManagement.h> #include <iomanip> #include <thread> @@ -173,7 +173,7 @@ void FakeHwcEnvironment::SetUp() { property_set("debug.sf.hwc_service_name", "mock"); // This allows tests/SF to register/load a HIDL service not listed in manifest files. - setenv("TREBLE_TESTING_OVERRIDE", "true", true); + android::hardware::details::setTrebleTestingOverride(true); property_set("debug.sf.treble_testing_override", "true"); } diff --git a/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp index 68cb52fe87..a119e27d41 100644 --- a/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp +++ b/services/surfaceflinger/tests/unittests/FrameTracerTest.cpp @@ -77,6 +77,22 @@ public: return tracingSession; } + std::vector<perfetto::protos::TracePacket> readGraphicsFramePacketsBlocking( + perfetto::TracingSession* tracingSession) { + std::vector<char> raw_trace = tracingSession->ReadTraceBlocking(); + perfetto::protos::Trace trace; + EXPECT_TRUE(trace.ParseFromArray(raw_trace.data(), int(raw_trace.size()))); + + std::vector<perfetto::protos::TracePacket> packets; + for (const auto& packet : trace.packet()) { + if (!packet.has_graphics_frame_event()) { + continue; + } + packets.emplace_back(packet); + } + return packets; + } + std::unique_ptr<FrameTracer> mFrameTracer; FenceToFenceTimeMap fenceFactory; }; @@ -142,40 +158,29 @@ TEST_F(FrameTracerTest, canTraceAfterAddingLayer) { auto tracingSession = getTracingSessionForTest(); tracingSession->StartBlocking(); - // Clean up irrelevant traces. - tracingSession->ReadTraceBlocking(); - mFrameTracer->traceTimestamp(layerId, bufferID, frameNumber, timestamp, type, duration); // Create second trace packet to finalize the previous one. mFrameTracer->traceTimestamp(layerId, 0, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED); tracingSession->StopBlocking(); - std::vector<char> raw_trace = tracingSession->ReadTraceBlocking(); - EXPECT_EQ(raw_trace.size(), 0); + auto packets = readGraphicsFramePacketsBlocking(tracingSession.get()); + EXPECT_EQ(packets.size(), 0); } { auto tracingSession = getTracingSessionForTest(); tracingSession->StartBlocking(); - // Clean up irrelevant traces. - tracingSession->ReadTraceBlocking(); - mFrameTracer->traceNewLayer(layerId, layerName); mFrameTracer->traceTimestamp(layerId, bufferID, frameNumber, timestamp, type, duration); // Create second trace packet to finalize the previous one. mFrameTracer->traceTimestamp(layerId, 0, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED); tracingSession->StopBlocking(); - std::vector<char> raw_trace = tracingSession->ReadTraceBlocking(); - ASSERT_GT(raw_trace.size(), 0); - - perfetto::protos::Trace trace; - ASSERT_TRUE(trace.ParseFromArray(raw_trace.data(), int(raw_trace.size()))); - ASSERT_FALSE(trace.packet().empty()); - EXPECT_EQ(trace.packet().size(), 1); + auto packets = readGraphicsFramePacketsBlocking(tracingSession.get()); + EXPECT_EQ(packets.size(), 1); - const auto& packet = trace.packet().Get(0); + const auto& packet = packets[0]; ASSERT_TRUE(packet.has_timestamp()); EXPECT_EQ(packet.timestamp(), timestamp); ASSERT_TRUE(packet.has_graphics_frame_event()); @@ -205,24 +210,21 @@ TEST_F(FrameTracerTest, traceFenceTriggersOnNextTraceAfterFenceFired) { fenceFactory.signalAllForTest(Fence::NO_FENCE, Fence::SIGNAL_TIME_PENDING); auto tracingSession = getTracingSessionForTest(); tracingSession->StartBlocking(); - // Clean up irrelevant traces. - tracingSession->ReadTraceBlocking(); // Trace. mFrameTracer->traceNewLayer(layerId, layerName); mFrameTracer->traceFence(layerId, bufferID, frameNumber, fenceTime, type); // Create extra trace packet to (hopefully not) trigger and finalize the fence packet. mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED); tracingSession->StopBlocking(); - std::vector<char> raw_trace = tracingSession->ReadTraceBlocking(); - EXPECT_EQ(raw_trace.size(), 0); + + auto packets = readGraphicsFramePacketsBlocking(tracingSession.get()); + EXPECT_EQ(packets.size(), 0); } { auto fenceTime = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); auto tracingSession = getTracingSessionForTest(); tracingSession->StartBlocking(); - // Clean up irrelevant traces. - tracingSession->ReadTraceBlocking(); mFrameTracer->traceNewLayer(layerId, layerName); mFrameTracer->traceFence(layerId, bufferID, frameNumber, fenceTime, type); const nsecs_t timestamp = systemTime(); @@ -231,15 +233,10 @@ TEST_F(FrameTracerTest, traceFenceTriggersOnNextTraceAfterFenceFired) { mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED); tracingSession->StopBlocking(); - std::vector<char> raw_trace = tracingSession->ReadTraceBlocking(); - ASSERT_GT(raw_trace.size(), 0); + auto packets = readGraphicsFramePacketsBlocking(tracingSession.get()); + EXPECT_EQ(packets.size(), 2); // Two packets because of the extra trace made above. - perfetto::protos::Trace trace; - ASSERT_TRUE(trace.ParseFromArray(raw_trace.data(), int(raw_trace.size()))); - ASSERT_FALSE(trace.packet().empty()); - EXPECT_EQ(trace.packet().size(), 2); // Two packets because of the extra trace made above. - - const auto& packet = trace.packet().Get(1); + const auto& packet = packets[1]; ASSERT_TRUE(packet.has_timestamp()); EXPECT_EQ(packet.timestamp(), timestamp); ASSERT_TRUE(packet.has_graphics_frame_event()); @@ -266,8 +263,6 @@ TEST_F(FrameTracerTest, traceFenceWithStartTimeAfterSignalTime_ShouldHaveNoDurat auto tracingSession = getTracingSessionForTest(); tracingSession->StartBlocking(); - // Clean up irrelevant traces. - tracingSession->ReadTraceBlocking(); mFrameTracer->traceNewLayer(layerId, layerName); // traceFence called after fence signalled. @@ -288,22 +283,17 @@ TEST_F(FrameTracerTest, traceFenceWithStartTimeAfterSignalTime_ShouldHaveNoDurat mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED); tracingSession->StopBlocking(); - std::vector<char> raw_trace = tracingSession->ReadTraceBlocking(); - ASSERT_GT(raw_trace.size(), 0); + auto packets = readGraphicsFramePacketsBlocking(tracingSession.get()); + EXPECT_EQ(packets.size(), 2); - perfetto::protos::Trace trace; - ASSERT_TRUE(trace.ParseFromArray(raw_trace.data(), int(raw_trace.size()))); - ASSERT_FALSE(trace.packet().empty()); - EXPECT_EQ(trace.packet().size(), 2); - - const auto& packet1 = trace.packet().Get(0); + const auto& packet1 = packets[0]; ASSERT_TRUE(packet1.has_timestamp()); EXPECT_EQ(packet1.timestamp(), signalTime1); ASSERT_TRUE(packet1.has_graphics_frame_event()); ASSERT_TRUE(packet1.graphics_frame_event().has_buffer_event()); ASSERT_FALSE(packet1.graphics_frame_event().buffer_event().has_duration_ns()); - const auto& packet2 = trace.packet().Get(1); + const auto& packet2 = packets[1]; ASSERT_TRUE(packet2.has_timestamp()); EXPECT_EQ(packet2.timestamp(), signalTime2); ASSERT_TRUE(packet2.has_graphics_frame_event()); @@ -323,8 +313,6 @@ TEST_F(FrameTracerTest, traceFenceOlderThanDeadline_ShouldBeIgnored) { auto fence = fenceFactory.createFenceTimeForTest(Fence::NO_FENCE); tracingSession->StartBlocking(); - // Clean up irrelevant traces. - tracingSession->ReadTraceBlocking(); mFrameTracer->traceNewLayer(layerId, layerName); mFrameTracer->traceFence(layerId, bufferID, frameNumber, fence, type); fenceFactory.signalAllForTest(Fence::NO_FENCE, signalTime); @@ -332,8 +320,8 @@ TEST_F(FrameTracerTest, traceFenceOlderThanDeadline_ShouldBeIgnored) { mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED); tracingSession->StopBlocking(); - std::vector<char> raw_trace = tracingSession->ReadTraceBlocking(); - EXPECT_EQ(raw_trace.size(), 0); + auto packets = readGraphicsFramePacketsBlocking(tracingSession.get()); + EXPECT_EQ(packets.size(), 0); } TEST_F(FrameTracerTest, traceFenceWithValidStartTime_ShouldHaveCorrectDuration) { @@ -347,8 +335,6 @@ TEST_F(FrameTracerTest, traceFenceWithValidStartTime_ShouldHaveCorrectDuration) auto tracingSession = getTracingSessionForTest(); tracingSession->StartBlocking(); - // Clean up irrelevant traces. - tracingSession->ReadTraceBlocking(); mFrameTracer->traceNewLayer(layerId, layerName); // traceFence called after fence signalled. @@ -369,15 +355,10 @@ TEST_F(FrameTracerTest, traceFenceWithValidStartTime_ShouldHaveCorrectDuration) mFrameTracer->traceTimestamp(layerId, bufferID, 0, 0, FrameTracer::FrameEvent::UNSPECIFIED); tracingSession->StopBlocking(); - std::vector<char> raw_trace = tracingSession->ReadTraceBlocking(); - ASSERT_GT(raw_trace.size(), 0); - - perfetto::protos::Trace trace; - ASSERT_TRUE(trace.ParseFromArray(raw_trace.data(), int(raw_trace.size()))); - ASSERT_FALSE(trace.packet().empty()); - EXPECT_EQ(trace.packet().size(), 2); + auto packets = readGraphicsFramePacketsBlocking(tracingSession.get()); + EXPECT_EQ(packets.size(), 2); - const auto& packet1 = trace.packet().Get(0); + const auto& packet1 = packets[0]; ASSERT_TRUE(packet1.has_timestamp()); EXPECT_EQ(packet1.timestamp(), startTime1); ASSERT_TRUE(packet1.has_graphics_frame_event()); @@ -386,7 +367,7 @@ TEST_F(FrameTracerTest, traceFenceWithValidStartTime_ShouldHaveCorrectDuration) const auto& buffer_event1 = packet1.graphics_frame_event().buffer_event(); EXPECT_EQ(buffer_event1.duration_ns(), duration); - const auto& packet2 = trace.packet().Get(1); + const auto& packet2 = packets[1]; ASSERT_TRUE(packet2.has_timestamp()); EXPECT_EQ(packet2.timestamp(), startTime2); ASSERT_TRUE(packet2.has_graphics_frame_event()); diff --git a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp index 63a34af8a2..0a246505ca 100644 --- a/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp +++ b/services/surfaceflinger/tests/unittests/TimeStatsTest.cpp @@ -221,7 +221,7 @@ std::string TimeStatsTest::inputCommand(InputCommand cmd, bool useProto) { } static std::string genLayerName(int32_t layerId) { - return (layerId < 0 ? "PopupWindow:b54fcd1#0" : "com.dummy#") + std::to_string(layerId); + return (layerId < 0 ? "PopupWindow:b54fcd1#0" : "com.example.fake#") + std::to_string(layerId); } void TimeStatsTest::setTimeStamp(TimeStamp type, int32_t id, uint64_t frameNumber, nsecs_t ts) { @@ -424,7 +424,7 @@ TEST_F(TimeStatsTest, canAverageRenderEngineTimings) { std::chrono::duration_cast<std::chrono::nanoseconds>(8ms) .count()); - // Push a dummy present fence to trigger flushing the RenderEngine timings. + // Push a fake present fence to trigger flushing the RenderEngine timings. mTimeStats->setPowerMode(PowerMode::ON); mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>( std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count())); @@ -505,7 +505,7 @@ TEST_F(TimeStatsTest, canInsertGlobalRenderEngineTiming) { ASSERT_TRUE(preFlushProto.ParseFromString(inputCommand(InputCommand::DUMP_ALL, FMT_PROTO))); ASSERT_EQ(0, preFlushProto.render_engine_timing_size()); - // Push a dummy present fence to trigger flushing the RenderEngine timings. + // Push a fake present fence to trigger flushing the RenderEngine timings. mTimeStats->setPowerMode(PowerMode::ON); mTimeStats->setPresentFenceGlobal(std::make_shared<FenceTime>( std::chrono::duration_cast<std::chrono::nanoseconds>(1ms).count())); diff --git a/services/vr/virtual_touchpad/Android.bp b/services/vr/virtual_touchpad/Android.bp index dcaa663160..9cf4905b68 100644 --- a/services/vr/virtual_touchpad/Android.bp +++ b/services/vr/virtual_touchpad/Android.bp @@ -14,6 +14,7 @@ shared_libs = [ ] header_libraries = [ + "jni_headers", "libdvr_headers", ] diff --git a/vulkan/libvulkan/driver.cpp b/vulkan/libvulkan/driver.cpp index 7bcb2c1441..01cbf399ff 100644 --- a/vulkan/libvulkan/driver.cpp +++ b/vulkan/libvulkan/driver.cpp @@ -161,7 +161,7 @@ void* LoadLibrary(const android_dlextinfo& dlextinfo, } const std::array<const char*, 2> HAL_SUBNAME_KEY_PROPERTIES = {{ - "ro.hardware." HWVULKAN_HARDWARE_MODULE_ID, + "ro.hardware.vulkan", "ro.board.platform", }}; @@ -964,9 +964,7 @@ VkResult EnumerateDeviceExtensionProperties( // conditionally add VK_GOOGLE_display_timing if present timestamps are // supported by the driver: - const std::string timestamp_property("service.sf.present_timestamp"); - android::base::WaitForPropertyCreation(timestamp_property); - if (android::base::GetBoolProperty(timestamp_property, true)) { + if (android::base::GetBoolProperty("service.sf.present_timestamp", false)) { loader_extensions.push_back({ VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME, VK_GOOGLE_DISPLAY_TIMING_SPEC_VERSION}); |