commit 1bc6933877afefa2d84963ee7084188dde9761fb Author: Andy CrossGate Yan Date: Thu Oct 29 03:02:26 2020 +0000 Initial commit for Android 11, syncing up to v300.f diff --git a/patches/platform_bionic/0001-Use-vndk_lite-ld.config-only-on-same-version-vendor.patch b/patches/platform_bionic/0001-Use-vndk_lite-ld.config-only-on-same-version-vendor.patch new file mode 100644 index 0000000..0bb4f31 --- /dev/null +++ b/patches/platform_bionic/0001-Use-vndk_lite-ld.config-only-on-same-version-vendor.patch @@ -0,0 +1,36 @@ +From e3c7e720c893a6ecef90b4b950787ed820792581 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Thu, 12 Sep 2019 12:54:23 +0200 +Subject: [PATCH 1/2] Use vndk_lite ld.config only on same-version vendor + +When running Q over P lite, there is currently absolutely no chance the +device boots, because it will be using Q vndk. +Thus using ld.config.28.txt when running Q over P lite gives a little more +chance for the device to boot, than when using vndk_lite ld.config. +Also, once this patch is applied, the required effort to boot +Q over P lite is exclusively in vndk, which is manageable. + +Change-Id: I55257cd7c738b1d20582e198e1d5621e1c87a03e +--- + linker/linker.cpp | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/linker/linker.cpp b/linker/linker.cpp +index f24167722..a82d7c430 100644 +--- a/linker/linker.cpp ++++ b/linker/linker.cpp +@@ -3372,7 +3372,10 @@ static std::string get_ld_config_file_apex_path(const char* executable_path) { + } + + static std::string get_ld_config_file_vndk_path() { +- if (android::base::GetBoolProperty("ro.vndk.lite", false)) { ++ bool same_version_system_vendor = false; ++ if(std::to_string(__ANDROID_API__) == Config::get_vndk_version_string('.')) ++ same_version_system_vendor = true; ++ if (android::base::GetBoolProperty("ro.vndk.lite", false) && same_version_system_vendor) { + return kLdConfigVndkLiteFilePath; + } + +-- +2.17.1 + diff --git a/patches/platform_bootable_recovery/0001-Don-t-reboot-if-we-couldn-t-get-bootctrl.patch b/patches/platform_bootable_recovery/0001-Don-t-reboot-if-we-couldn-t-get-bootctrl.patch new file mode 100644 index 0000000..9058b20 --- /dev/null +++ b/patches/platform_bootable_recovery/0001-Don-t-reboot-if-we-couldn-t-get-bootctrl.patch @@ -0,0 +1,26 @@ +From a1336cadfe7ff1afa46e87ba11849ebef622183e Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Thu, 12 Sep 2019 20:31:07 +0200 +Subject: [PATCH] Don't reboot if we couldn't get bootctrl + +Change-Id: Id1793660bd1c97ab369607f58a772ca3512ec1af +--- + update_verifier/update_verifier.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/update_verifier/update_verifier.cpp b/update_verifier/update_verifier.cpp +index a042f900..ea3645af 100644 +--- a/update_verifier/update_verifier.cpp ++++ b/update_verifier/update_verifier.cpp +@@ -310,7 +310,7 @@ int update_verifier(int argc, char** argv) { + sp module = IBootControl::getService(); + if (module == nullptr) { + LOG(ERROR) << "Error getting bootctrl module."; +- return reboot_device(); ++ return 0; + } + + uint32_t current_slot = module->getCurrentSlot(); +-- +2.17.1 + diff --git a/patches/platform_external_selinux/0001-Enable-multipl_decls-by-default.-This-is-needed-beca.patch b/patches/platform_external_selinux/0001-Enable-multipl_decls-by-default.-This-is-needed-beca.patch new file mode 100644 index 0000000..fe7c967 --- /dev/null +++ b/patches/platform_external_selinux/0001-Enable-multipl_decls-by-default.-This-is-needed-beca.patch @@ -0,0 +1,27 @@ +From ddbe45ddd09f30beab8980d14c506f48ca1f8399 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Fri, 2 Mar 2018 22:49:55 +0100 +Subject: [PATCH 01/10] Enable multipl_decls by default. This is needed because + 8.0 init doesn't add -m + +Change-Id: I43dc661d519f7b8576d72a828d8cbd444592bf5e +--- + secilc/secilc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/secilc/secilc.c b/secilc/secilc.c +index 186c5a73..b422175e 100644 +--- a/secilc/secilc.c ++++ b/secilc/secilc.c +@@ -91,7 +91,7 @@ int main(int argc, char *argv[]) + int target = SEPOL_TARGET_SELINUX; + int mls = -1; + int disable_dontaudit = 0; +- int multiple_decls = 0; ++ int multiple_decls = 1; + int disable_neverallow = 0; + int preserve_tunables = 0; + int handle_unknown = -1; +-- +2.17.1 + diff --git a/patches/platform_external_selinux/0002-Increase-default-log_level-to-get-actual-selinux-err.patch b/patches/platform_external_selinux/0002-Increase-default-log_level-to-get-actual-selinux-err.patch new file mode 100644 index 0000000..4d4229b --- /dev/null +++ b/patches/platform_external_selinux/0002-Increase-default-log_level-to-get-actual-selinux-err.patch @@ -0,0 +1,26 @@ +From ff296de7912209e6fa20e1b28db93c395cc0bb75 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Mon, 9 Apr 2018 00:19:49 +0200 +Subject: [PATCH 02/10] Increase default log_level to get actual selinux error + in kmsg + +--- + secilc/secilc.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/secilc/secilc.c b/secilc/secilc.c +index b422175e..1e550ab4 100644 +--- a/secilc/secilc.c ++++ b/secilc/secilc.c +@@ -103,7 +103,7 @@ int main(int argc, char *argv[]) + int opt_index = 0; + char *fc_buf = NULL; + size_t fc_size; +- enum cil_log_level log_level = CIL_ERR; ++ enum cil_log_level log_level = CIL_WARN; + static struct option long_opts[] = { + {"help", no_argument, 0, 'h'}, + {"verbose", no_argument, 0, 'v'}, +-- +2.17.1 + diff --git a/patches/platform_external_selinux/0003-Kirin-Workaround-some-conflicting-Kirin-tether-SELin.patch b/patches/platform_external_selinux/0003-Kirin-Workaround-some-conflicting-Kirin-tether-SELin.patch new file mode 100644 index 0000000..0f59301 --- /dev/null +++ b/patches/platform_external_selinux/0003-Kirin-Workaround-some-conflicting-Kirin-tether-SELin.patch @@ -0,0 +1,40 @@ +From ecb4746fb8ba4e97cc53e6290dfa7a75495d19a8 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Mon, 3 Dec 2018 20:54:54 +0100 +Subject: [PATCH 03/10] ::Kirin:: Workaround some conflicting Kirin tether + SELinux context + +Some Kirin devices declared some android.hardware.tetheroffload HALs, +but they didn't use AOSP contexts. +This leads to libselinux aborting when loading hwservice_contexts. + +Workaround it the ugly way, by making them match. +This most likely kills tetheroffload for those devices. +--- + libselinux/src/label_backends_android.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/libselinux/src/label_backends_android.c b/libselinux/src/label_backends_android.c +index eaca5947..ab92985b 100644 +--- a/libselinux/src/label_backends_android.c ++++ b/libselinux/src/label_backends_android.c +@@ -62,6 +62,16 @@ static int nodups_specs(struct saved_data *data) + curr_spec->property_key)) { + if (strcmp(spec_arr[jj].lr.ctx_raw, + curr_spec->lr.ctx_raw)) { ++ if(strcmp(spec_arr[jj].lr.ctx_raw, "u:object_r:hal_ipacm_hwservice:s0") == 0) { ++ free(spec_arr[jj].lr.ctx_raw); ++ spec_arr[jj].lr.ctx_raw = strdup("u:object_r:hal_tetheroffload_hwservice:s0"); ++ continue; ++ } ++ if(strcmp(curr_spec->lr.ctx_raw, "u:object_r:hal_ipacm_hwservice:s0") == 0) { ++ free(curr_spec->lr.ctx_raw); ++ curr_spec->lr.ctx_raw = strdup("u:object_r:hal_tetheroffload_hwservice:s0"); ++ continue; ++ } + rc = -1; + errno = EINVAL; + selinux_log +-- +2.17.1 + diff --git a/patches/platform_external_selinux/0004-Allow-devices-virtual-block-genfscon-conflict-seen-o.patch b/patches/platform_external_selinux/0004-Allow-devices-virtual-block-genfscon-conflict-seen-o.patch new file mode 100644 index 0000000..718282f --- /dev/null +++ b/patches/platform_external_selinux/0004-Allow-devices-virtual-block-genfscon-conflict-seen-o.patch @@ -0,0 +1,43 @@ +From f014cf6460159f820fc0d72c6d0bece2851f9873 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Fri, 6 Sep 2019 15:07:25 +0200 +Subject: [PATCH 04/10] Allow /devices/virtual/block/ genfscon conflict (seen + on Xiaomi Mi 9) + +Change-Id: I06e4e9d5b82d61a8aeab595b47e2589249675895 +--- + libsepol/cil/src/cil_post.c | 18 +++++++++++++++++- + 1 file changed, 17 insertions(+), 1 deletion(-) + +diff --git a/libsepol/cil/src/cil_post.c b/libsepol/cil/src/cil_post.c +index a0cadfde..68c0318e 100644 +--- a/libsepol/cil/src/cil_post.c ++++ b/libsepol/cil/src/cil_post.c +@@ -477,7 +477,23 @@ int cil_post_genfscon_context_compare(const void *a, const void *b) + { + struct cil_genfscon *a_genfscon = *(struct cil_genfscon**)a; + struct cil_genfscon *b_genfscon = *(struct cil_genfscon**)b; +- return context_compare(a_genfscon->context, b_genfscon->context); ++ int rc = context_compare(a_genfscon->context, b_genfscon->context); ++ if(rc) { ++ fprintf(stderr, "hello %s\n", a_genfscon->fs_str); ++ int bypass = 0; ++ /* ++ * This conflict has been seen on Xiaomi Mi 9: ++ * - AOSP Q says (genfscon sysfs /devices/virtual/block/ (u object_r sysfs_devices_block ((s0) (s0)))) ++ * - stock rom says (genfscon sysfs /devices/virtual/block/ (u object_r sysfs_ufs_target ((s0) (s0)))) ++ */ ++ if(strcmp(a_genfscon->path_str, "/devices/virtual/block/") == 0) ++ bypass = 1; ++ if(bypass == 1) { ++ fprintf(stderr, "Received conflicting %s vs %s but ignore\n", a_genfscon->path_str, b_genfscon->path_str); ++ return 0; ++ } ++ } ++ return rc; + } + + int cil_post_netifcon_context_compare(const void *a, const void *b) +-- +2.17.1 + diff --git a/patches/platform_external_selinux/0005-Most-horrific-Remove-ramdisk-s-zygote-init-scripts.patch b/patches/platform_external_selinux/0005-Most-horrific-Remove-ramdisk-s-zygote-init-scripts.patch new file mode 100644 index 0000000..7b08b16 --- /dev/null +++ b/patches/platform_external_selinux/0005-Most-horrific-Remove-ramdisk-s-zygote-init-scripts.patch @@ -0,0 +1,44 @@ +From 37d5e860031c84803c2533d940847dc214cb2597 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Thu, 12 Sep 2019 20:34:28 +0200 +Subject: [PATCH 05/10] Most horrific: Remove ramdisk's zygote init scripts + +This is needed because: +- only secilc is run soon enough in /system to +remove it +- placing an init.zygote in system won't have init replace it, it's the +first that appears that wins + +Change-Id: I8be31ceb9ef2124d04994d9fb08fc8012a2f819e +--- + secilc/secilc.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/secilc/secilc.c b/secilc/secilc.c +index 1e550ab4..375320e6 100644 +--- a/secilc/secilc.c ++++ b/secilc/secilc.c +@@ -34,6 +34,8 @@ + #include + #include + ++#include ++ + #ifdef ANDROID + #include + #else +@@ -124,6 +126,11 @@ int main(int argc, char *argv[]) + }; + int i; + ++ unlink("/init.zygote32.rc"); ++ unlink("/init.zygote64_32.rc"); ++ unlink("/init.zygote64.rc"); ++ unlink("/init.zygote32_64.rc"); ++ + while (1) { + opt_char = getopt_long(argc, argv, "o:f:U:hvt:M:PDmNOc:GX:n", long_opts, &opt_index); + if (opt_char == -1) { +-- +2.17.1 + diff --git a/patches/platform_external_selinux/0006-if-service-is-rcs-accept-conflict.-Seen-on-Moto-E5.patch b/patches/platform_external_selinux/0006-if-service-is-rcs-accept-conflict.-Seen-on-Moto-E5.patch new file mode 100644 index 0000000..4c8efbc --- /dev/null +++ b/patches/platform_external_selinux/0006-if-service-is-rcs-accept-conflict.-Seen-on-Moto-E5.patch @@ -0,0 +1,44 @@ +From 04fdf9096557accdea52b8b66665717f6c8a454a Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Thu, 12 Sep 2019 20:37:04 +0200 +Subject: [PATCH 06/10] if service is "rcs", accept conflict. Seen on Moto E5 + +Change-Id: I0cc2d0fad83f403f2b5d7458039b1564ce5ed9dd +--- + libselinux/src/label_backends_android.c | 14 ++++++++++++-- + 1 file changed, 12 insertions(+), 2 deletions(-) + +diff --git a/libselinux/src/label_backends_android.c b/libselinux/src/label_backends_android.c +index ab92985b..ca16327c 100644 +--- a/libselinux/src/label_backends_android.c ++++ b/libselinux/src/label_backends_android.c +@@ -72,14 +72,24 @@ static int nodups_specs(struct saved_data *data) + curr_spec->lr.ctx_raw = strdup("u:object_r:hal_tetheroffload_hwservice:s0"); + continue; + } +- rc = -1; +- errno = EINVAL; + selinux_log + (SELINUX_ERROR, + "Multiple different specifications for %s (%s and %s).\n", + curr_spec->property_key, + spec_arr[jj].lr.ctx_raw, + curr_spec->lr.ctx_raw); ++ int ignore = 0; ++ /* ++ * This issue has been found on Moto E5 ++ * E SELinux : Multiple different specifications for rcs (u:object_r:radio_service:s0 and u:object_r:mot_rcs_service:s0). ++ */ ++ if(!strcmp(curr_spec->property_key, "rcs")) ++ ignore = 1; ++ ++ if(!ignore) { ++ rc = -1; ++ errno = EINVAL; ++ } + } else { + selinux_log + (SELINUX_WARNING, +-- +2.17.1 + diff --git a/patches/platform_external_selinux/0007-Fix-boot-on-Moto-devices-using-unknown-class.patch b/patches/platform_external_selinux/0007-Fix-boot-on-Moto-devices-using-unknown-class.patch new file mode 100644 index 0000000..f9191a3 --- /dev/null +++ b/patches/platform_external_selinux/0007-Fix-boot-on-Moto-devices-using-unknown-class.patch @@ -0,0 +1,84 @@ +From ef94ddbd76328611893ace7870c0d3137fd26cdd Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Fri, 25 Oct 2019 13:29:20 +0200 +Subject: [PATCH 07/10] Fix boot on Moto devices using unknown class + +vendor sepolicy never contains new class or classorder, and are not +allowed to. +Though this is not tested, and it turns out Moto did it anyway. +This raises an issue, because class need to be ordered, and thus the cil +contains the ordering. This ordering needs to be merged. +Android 10 added new classes, so the ordering can no longer be merged, +and secilc fails on those devices, preventing boot. + +Considering vendor are not supposed to declare new class (and thus +declare classorder), this fix ignores class-es/classorder in vendor +SELinux policy. + +Since the vendor selinux policy has allows rules based on this context, +those allows will fail since the class doesn't exist. +Workaround this by ignoring rules with the problematic class +( keystore_moto_key ) + +Lucky us, this new class `keystore_moto_key` is used by Moto for +framework to framework (more accurately priv app to keymaster), since +our own framework doesn't use this class, simply ignoring it fixes the +issue. + +Change-Id: I66339857634ebfdba359f12a99dfd0bff709d80b +--- + libsepol/cil/src/cil_build_ast.c | 24 ++++++++++++++++++++++++ + 1 file changed, 24 insertions(+) + +diff --git a/libsepol/cil/src/cil_build_ast.c b/libsepol/cil/src/cil_build_ast.c +index 307b1ee3..02cdcc65 100644 +--- a/libsepol/cil/src/cil_build_ast.c ++++ b/libsepol/cil/src/cil_build_ast.c +@@ -386,6 +386,14 @@ int cil_gen_class(struct cil_db *db, struct cil_tree_node *parse_current, struct + struct cil_tree_node *perms = NULL; + int rc = SEPOL_ERR; + ++ { ++ const char* path = cil_tree_get_cil_path(parse_current); ++ if(strstr(path, "vendor")) { ++ cil_clear_node(ast_node); ++ return SEPOL_OK; ++ } ++ } ++ + rc = __cil_verify_syntax(parse_current, syntax, syntax_len); + if (rc != SEPOL_OK) { + goto exit; +@@ -452,6 +460,14 @@ int cil_gen_classorder(struct cil_db *db, struct cil_tree_node *parse_current, s + struct cil_list_item *head = NULL; + int rc = SEPOL_ERR; + ++ { ++ const char* path = cil_tree_get_cil_path(parse_current); ++ if(strstr(path, "vendor")) { ++ cil_clear_node(ast_node); ++ return SEPOL_OK; ++ } ++ } ++ + if (db == NULL || parse_current == NULL || ast_node == NULL) { + goto exit; + } +@@ -2050,6 +2066,14 @@ int cil_gen_avrule(struct cil_tree_node *parse_current, struct cil_tree_node *as + rule->src_str = parse_current->next->data; + rule->tgt_str = parse_current->next->next->data; + ++ { ++ const char *classname = parse_current->next->next->next->cl_head->data; ++ if(strcmp(classname, "keystore_moto_key") == 0) { ++ cil_clear_node(ast_node); ++ return SEPOL_OK; ++ } ++ } ++ + rc = cil_fill_classperms_list(parse_current->next->next->next, &rule->perms.classperms); + if (rc != SEPOL_OK) { + goto exit; +-- +2.17.1 + diff --git a/patches/platform_external_selinux/0008-Allow-mismatches-of-exfat-genfscon.patch b/patches/platform_external_selinux/0008-Allow-mismatches-of-exfat-genfscon.patch new file mode 100644 index 0000000..dc2400a --- /dev/null +++ b/patches/platform_external_selinux/0008-Allow-mismatches-of-exfat-genfscon.patch @@ -0,0 +1,27 @@ +From 3d356da4654b5ae9eb2207f824555f0219687763 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Sun, 24 May 2020 17:22:22 +0200 +Subject: [PATCH 08/10] Allow mismatches of exfat genfscon + +--- + libsepol/cil/src/cil_post.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/libsepol/cil/src/cil_post.c b/libsepol/cil/src/cil_post.c +index 68c0318e..75e5128c 100644 +--- a/libsepol/cil/src/cil_post.c ++++ b/libsepol/cil/src/cil_post.c +@@ -488,6 +488,10 @@ int cil_post_genfscon_context_compare(const void *a, const void *b) + */ + if(strcmp(a_genfscon->path_str, "/devices/virtual/block/") == 0) + bypass = 1; ++ if(strcmp(a_genfscon->fs_str, "exfat") == 0 || strcmp(a_genfscon->fs_str, "esdfs") == 0) { ++ if(strcmp(a_genfscon->path_str, "/") == 0) ++ bypass = 1; ++ } + if(bypass == 1) { + fprintf(stderr, "Received conflicting %s vs %s but ignore\n", a_genfscon->path_str, b_genfscon->path_str); + return 0; +-- +2.17.1 + diff --git a/patches/platform_external_selinux/0009-Revert-libsepol-Make-an-unknown-permission-an-error-.patch b/patches/platform_external_selinux/0009-Revert-libsepol-Make-an-unknown-permission-an-error-.patch new file mode 100644 index 0000000..4bd3d3c --- /dev/null +++ b/patches/platform_external_selinux/0009-Revert-libsepol-Make-an-unknown-permission-an-error-.patch @@ -0,0 +1,45 @@ +From f3d5e2eb212ebd4189428d6adb915880573962f9 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Wed, 9 Sep 2020 22:36:42 +0200 +Subject: [PATCH 09/10] Revert "libsepol: Make an unknown permission an error + in CIL" + +This reverts commit dc4e54126bf25dea4d51820922ccd1959be68fbc. + +This is required because some targets calls undefined permissions: +- Realme X2 Pro calls sigcont +- Honor 7X calls perf_event +--- + libsepol/cil/src/cil_resolve_ast.c | 12 ++++++++---- + 1 file changed, 8 insertions(+), 4 deletions(-) + +diff --git a/libsepol/cil/src/cil_resolve_ast.c b/libsepol/cil/src/cil_resolve_ast.c +index 87575860..e72abdeb 100644 +--- a/libsepol/cil/src/cil_resolve_ast.c ++++ b/libsepol/cil/src/cil_resolve_ast.c +@@ -131,14 +131,18 @@ static int __cil_resolve_perms(symtab_t *class_symtab, symtab_t *common_symtab, + } + } + if (rc != SEPOL_OK) { ++ struct cil_list *empty_list; + if (class_flavor == CIL_MAP_CLASS) { + cil_log(CIL_ERR, "Failed to resolve permission %s for map class\n", (char*)curr->data); +- } else { +- cil_log(CIL_ERR, "Failed to resolve permission %s\n", (char*)curr->data); ++ goto exit; + } +- goto exit; ++ cil_log(CIL_WARN, "Failed to resolve permission %s\n", (char*)curr->data); ++ /* Use an empty list to represent unknown perm */ ++ cil_list_init(&empty_list, perm_strs->flavor); ++ cil_list_append(*perm_datums, CIL_LIST, empty_list); ++ } else { ++ cil_list_append(*perm_datums, CIL_DATUM, perm_datum); + } +- cil_list_append(*perm_datums, CIL_DATUM, perm_datum); + } else { + cil_list_append(*perm_datums, curr->flavor, curr->data); + } +-- +2.17.1 + diff --git a/patches/platform_external_selinux/0010-HACK-For-System-as-System-devices-init-won-t-call-us.patch b/patches/platform_external_selinux/0010-HACK-For-System-as-System-devices-init-won-t-call-us.patch new file mode 100644 index 0000000..ef964a6 --- /dev/null +++ b/patches/platform_external_selinux/0010-HACK-For-System-as-System-devices-init-won-t-call-us.patch @@ -0,0 +1,83 @@ +From 00f7989d79ad8e219dae29de8e8f20e4dbab008a Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Sun, 27 Sep 2020 20:20:35 +0200 +Subject: [PATCH 10/10] [HACK] For System-as-System devices, init won't call us + with /system_ext sepolicy + +So add system_ext sepolicy ourselves +--- + secilc/secilc.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 44 insertions(+) + +diff --git a/secilc/secilc.c b/secilc/secilc.c +index 375320e6..162b87fb 100644 +--- a/secilc/secilc.c ++++ b/secilc/secilc.c +@@ -233,6 +233,7 @@ int main(int argc, char *argv[]) + usage(argv[0]); + } + } ++ + if (optind >= argc) { + fprintf(stderr, "No cil files specified\n"); + usage(argv[0]); +@@ -240,6 +241,11 @@ int main(int argc, char *argv[]) + + cil_set_log_level(log_level); + ++ int needSystemExt = 1; ++ for (i = optind; i < argc; i++) { ++ if(strstr(argv[i], "system_ext")) needSystemExt = 0; ++ } ++ + cil_db_init(&db); + cil_set_disable_dontaudit(db, disable_dontaudit); + cil_set_multiple_decls(db, multiple_decls); +@@ -261,6 +267,44 @@ int main(int argc, char *argv[]) + } + + for (i = optind; i < argc; i++) { ++ fprintf(stderr, "Hello, parsing %s\n", argv[i]); ++ if(needSystemExt && strstr(argv[i], "/etc/selinux/")) { ++ fprintf(stderr, "Hello, I'm adding system_ext to the mix!\n"); ++ char *path = "/system/system_ext/etc/selinux/system_ext_sepolicy.cil"; ++ file = fopen(path, "r"); ++ if (!file) { ++ fprintf(stderr, "Could not open file: %s\n", path); ++ rc = SEPOL_ERR; ++ goto exit; ++ } ++ rc = stat(path, &filedata); ++ if (rc == -1) { ++ fprintf(stderr, "Could not stat file: %s\n", path); ++ rc = SEPOL_ERR; ++ goto exit; ++ } ++ file_size = filedata.st_size; ++ ++ buffer = malloc(file_size); ++ rc = fread(buffer, file_size, 1, file); ++ if (rc != 1) { ++ fprintf(stderr, "Failure reading file: %s\n", path); ++ rc = SEPOL_ERR; ++ goto exit; ++ } ++ fclose(file); ++ file = NULL; ++ ++ rc = cil_add_file(db, path, buffer, file_size); ++ if (rc != SEPOL_OK) { ++ fprintf(stderr, "Failure adding %s\n", path); ++ goto exit; ++ } ++ ++ free(buffer); ++ buffer = NULL; ++ ++ } + file = fopen(argv[i], "r"); + if (!file) { + fprintf(stderr, "Could not open file: %s\n", argv[i]); +-- +2.17.1 + diff --git a/patches/platform_external_skia/0001-GrGLCaps-allow-ignoring-vendor-supplied-texture-swiz.patch b/patches/platform_external_skia/0001-GrGLCaps-allow-ignoring-vendor-supplied-texture-swiz.patch new file mode 100644 index 0000000..c77ffb6 --- /dev/null +++ b/patches/platform_external_skia/0001-GrGLCaps-allow-ignoring-vendor-supplied-texture-swiz.patch @@ -0,0 +1,54 @@ +From 163f3f2b4a89df73d3ffede558d26bb0df966bbe Mon Sep 17 00:00:00 2001 +From: Peter Cai +Date: Tue, 22 Sep 2020 10:38:04 +0200 +Subject: [PATCH] GrGLCaps: allow ignoring vendor-supplied texture swizzle flag + +* This is broken on MTK 8.1 vendor + +Change-Id: I1ccae06f643f01e4ea6539e1d4e3c7df8d6e30ae +--- + Android.bp | 1 + + src/gpu/gl/GrGLCaps.cpp | 8 ++++++++ + 2 files changed, 9 insertions(+) + +diff --git a/Android.bp b/Android.bp +index 122324c6ef..5ec949966a 100644 +--- a/Android.bp ++++ b/Android.bp +@@ -3,6 +3,7 @@ + cc_library_static { + name: "libskia", + host_supported: true, ++ shared_libs: [ "libbase" ], + cflags: [ + "-U_FORTIFY_SOURCE", + "-DATRACE_TAG=ATRACE_TAG_VIEW", +diff --git a/src/gpu/gl/GrGLCaps.cpp b/src/gpu/gl/GrGLCaps.cpp +index 6a1bf56474..df007ba70c 100644 +--- a/src/gpu/gl/GrGLCaps.cpp ++++ b/src/gpu/gl/GrGLCaps.cpp +@@ -22,6 +22,11 @@ + #include "src/gpu/gl/GrGLTexture.h" + #include "src/utils/SkJSONWriter.h" + ++#if defined(SK_BUILD_FOR_ANDROID) ++#include "android-base/properties.h" ++using android::base::GetBoolProperty; ++#endif ++ + GrGLCaps::GrGLCaps(const GrContextOptions& contextOptions, + const GrGLContextInfo& ctxInfo, + const GrGLInterface* glInterface) : INHERITED(contextOptions) { +@@ -273,6 +278,9 @@ void GrGLCaps::init(const GrContextOptions& contextOptions, + this->fShaderCaps->fTextureSwizzleAppliedInShader = false; + } + } // no WebGL support ++ if(GetBoolProperty("ro.skia.ignore_swizzle", false)) { ++ this->fShaderCaps->fTextureSwizzleAppliedInShader = true; ++ } + + if (GR_IS_GR_GL(standard)) { + fMipMapLevelAndLodControlSupport = true; +-- +2.17.1 + diff --git a/patches/platform_frameworks_av/0001-Fix-WiFi-Display-on-Huawei-devices-EMUI-8.0.patch b/patches/platform_frameworks_av/0001-Fix-WiFi-Display-on-Huawei-devices-EMUI-8.0.patch new file mode 100644 index 0000000..c117294 --- /dev/null +++ b/patches/platform_frameworks_av/0001-Fix-WiFi-Display-on-Huawei-devices-EMUI-8.0.patch @@ -0,0 +1,31 @@ +From f85268cf7b0e928d741d316b59b3b31773fae7c8 Mon Sep 17 00:00:00 2001 +From: Alexander Pohl +Date: Fri, 15 Jun 2018 19:58:07 +0200 +Subject: [PATCH 01/14] Fix WiFi-Display on Huawei devices (EMUI 8.0) + +Huaweis media stack doesn't handle intra-refresh-mode, so skip the error instead. + +Thanks to Chris Vandomelen for pointing that out. +--- + media/libstagefright/ACodec.cpp | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp +index 63ab6548b4..e25f8f6558 100644 +--- a/media/libstagefright/ACodec.cpp ++++ b/media/libstagefright/ACodec.cpp +@@ -4432,9 +4432,8 @@ status_t ACodec::setupAVCEncoderParameters(const sp &msg) { + if (msg->findInt32("intra-refresh-mode", &intraRefreshMode)) { + err = setCyclicIntraMacroblockRefresh(msg, intraRefreshMode); + if (err != OK) { +- ALOGE("Setting intra macroblock refresh mode (%d) failed: 0x%x", +- err, intraRefreshMode); +- return err; ++ ALOGE("setupAVCEncoderParameters(): set intra-refresh-mode failed, ignoring.."); ++ //return err; + } + } + +-- +2.17.1 + diff --git a/patches/platform_frameworks_av/0002-Kirin-Remove-lock-to-prevent-self-lock.patch b/patches/platform_frameworks_av/0002-Kirin-Remove-lock-to-prevent-self-lock.patch new file mode 100644 index 0000000..4bcd296 --- /dev/null +++ b/patches/platform_frameworks_av/0002-Kirin-Remove-lock-to-prevent-self-lock.patch @@ -0,0 +1,36 @@ +From 4ab15a33857452d990581a3d0073e35215e1c542 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Sun, 19 Aug 2018 22:59:06 +0200 +Subject: [PATCH 02/14] ::Kirin:: Remove lock to prevent self-lock + +With Huawei Camera HAL, we get the following call order: +cameraserver CameraService::enumerateProviders (*) +=> HAL ICameraProvider::getVendorTags +=> HAL ICameraProviderCallback::cameraDeviceStatusChange +=> cameraserver CameraService::addState +=> cameraserver CameraService::updateCameraNumAndIds (*) + +The two functions marked with (*) take mServiceLock +Hence the safe-lock + +Change-Id: I464255b5a3af8d75d72b1772ab55aa655f395a60 +--- + services/camera/libcameraservice/CameraService.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp +index af1e01d8e1..05252d6308 100644 +--- a/services/camera/libcameraservice/CameraService.cpp ++++ b/services/camera/libcameraservice/CameraService.cpp +@@ -301,7 +301,7 @@ status_t CameraService::getSystemCameraKind(const String8& cameraId, SystemCamer + } + + void CameraService::updateCameraNumAndIds() { +- Mutex::Autolock l(mServiceLock); ++ //Mutex::Autolock l(mServiceLock); + std::pair systemAndNonSystemCameras = mCameraProviderManager->getCameraCount(); + // Excludes hidden secure cameras + mNumberOfCameras = +-- +2.17.1 + diff --git a/patches/platform_frameworks_av/0003-We-might-not-have-a-mFlashlight-at-this-state-but-th.patch b/patches/platform_frameworks_av/0003-We-might-not-have-a-mFlashlight-at-this-state-but-th.patch new file mode 100644 index 0000000..4e900e3 --- /dev/null +++ b/patches/platform_frameworks_av/0003-We-might-not-have-a-mFlashlight-at-this-state-but-th.patch @@ -0,0 +1,26 @@ +From 1083cc0b59ed11aa69af22103ebaf74ae11f272c Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Sun, 19 Aug 2018 23:05:26 +0200 +Subject: [PATCH 03/14] We might not have a mFlashlight at this state, but + that's ok + +--- + services/camera/libcameraservice/CameraService.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp +index 05252d6308..e1d23073f4 100644 +--- a/services/camera/libcameraservice/CameraService.cpp ++++ b/services/camera/libcameraservice/CameraService.cpp +@@ -337,7 +337,7 @@ void CameraService::addStates(const String8 id) { + conflicting, deviceKind)); + } + +- if (mFlashlight->hasFlashUnit(id)) { ++ if (mFlashlight != nullptr && mFlashlight->hasFlashUnit(id)) { + Mutex::Autolock al(mTorchStatusMutex); + mTorchStatusMap.add(id, TorchModeStatus::AVAILABLE_OFF); + +-- +2.17.1 + diff --git a/patches/platform_frameworks_av/0004-CameraService-Support-calling-addStates-in-enumerate.patch b/patches/platform_frameworks_av/0004-CameraService-Support-calling-addStates-in-enumerate.patch new file mode 100644 index 0000000..a75ba7b --- /dev/null +++ b/patches/platform_frameworks_av/0004-CameraService-Support-calling-addStates-in-enumerate.patch @@ -0,0 +1,62 @@ +From 1c0e912c7bdef868de97dc07dd5227d298cd66a9 Mon Sep 17 00:00:00 2001 +From: Artem Borisov +Date: Tue, 25 Sep 2018 12:39:22 +0300 +Subject: [PATCH 04/14] CameraService: Support calling addStates in + enumerateProviders + +Some pre-P camera HALs trigger onDeviceStatusChange callback during HAL init. +This is horribly wrong and causes a race condition between enumerateProviders +and onDeviceStatusChange. While it wasn't really harmful in O, in P call sequence +was changed and now this race condition leads to two problems: null pointer dereference +in addStates because mFlashlight is not initialized at a proper timing; mServiceLock +deadlock because updateCameraNumAndIds and enumerateProviders are called at the same time. +Moving addStates back to enumerateProviders makes the sequence more similar to O, and doesn't +let these two issues to happen. +Enable TARGET_CAMERA_NEEDS_ADD_STATES_IN_ENUMERATE when it is necessary. + +@Dil3mm4 edit: Instead of TARGET_CAMERA_NEEDS_ADD_STATES_IN_ENUMERATE let's use a system property to make it more generic and suitable for GSI use. + +Change-Id: Ife25b9753fdb679ab0c77f385e1b8527551a4711 +--- + .../camera/libcameraservice/CameraService.cpp | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp +index e1d23073f4..83dea10f3a 100644 +--- a/services/camera/libcameraservice/CameraService.cpp ++++ b/services/camera/libcameraservice/CameraService.cpp +@@ -221,6 +221,20 @@ status_t CameraService::enumerateProviders() { + + for (auto& cameraId : deviceIds) { + String8 id8 = String8(cameraId.c_str()); ++ if (property_get_bool("persist.sys.camera.huawei", false)) { ++ bool cameraFound = false; ++ { ++ Mutex::Autolock lock(mCameraStatesLock); ++ auto iter = mCameraStates.find(id8); ++ if (iter != mCameraStates.end()) { ++ cameraFound = true; ++ } ++ } ++ if (!cameraFound) { ++ addStates(id8); ++ } ++ } ++ + if (getCameraState(id8) == nullptr) { + onDeviceStatusChanged(id8, CameraDeviceStatus::PRESENT); + } +@@ -375,8 +389,10 @@ void CameraService::onDeviceStatusChanged(const String8& id, + ALOGI("%s: Unknown camera ID %s, a new camera is added", + __FUNCTION__, id.string()); + ++ if (!property_get_bool("persist.sys.camera.huawei", false)) { + // First add as absent to make sure clients are notified below + addStates(id); ++ } + + updateStatus(newStatus, id); + } else { +-- +2.17.1 + diff --git a/patches/platform_frameworks_av/0005-Fix-BT-in-call-on-CAF-devices.patch b/patches/platform_frameworks_av/0005-Fix-BT-in-call-on-CAF-devices.patch new file mode 100644 index 0000000..3e13fca --- /dev/null +++ b/patches/platform_frameworks_av/0005-Fix-BT-in-call-on-CAF-devices.patch @@ -0,0 +1,131 @@ +From 3afbad626827104662f61ab04ba3fbe6af32f73f Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Mon, 5 Aug 2019 18:09:50 +0200 +Subject: [PATCH 05/14] Fix BT in-call on CAF devices + +See https://github.com/phhusson/treble_experimentations/issues/374 + +In Qualcomm's BSP audio_policy_configuration.xml, one route is missing, +from primary output and telephony to BT SCO. + +Add it if we detect telephony and bt sco, but no such route. + +Change-Id: Ifea0f88276ec9a0811f3cb1973c4b06f2c82077b +--- + .../managerdefinitions/src/Serializer.cpp | 93 +++++++++++++++++++ + 1 file changed, 93 insertions(+) + +diff --git a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp +index 883e713326..d85dbdf113 100644 +--- a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp ++++ b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp +@@ -596,6 +596,98 @@ Return RouteTraits::deserialize(const xmlNode *cur, PtrSer + return route; + } + ++static void fixupQualcommBtScoRoute(RouteTraits::Collection& routes, DevicePortTraits::Collection& devicePorts, HwModule* ctx) { ++ // On many Qualcomm devices, there is a BT SCO Headset Mic => primary input mix ++ // But Telephony Rx => BT SCO Headset route is missing ++ // When we detect such case, add the missing route ++ ++ // If we have: ++ // ++ // ++ // ++ // And no ++ ++ // Add: ++ // ++ bool foundBtScoHeadsetDevice = false; ++ for(const auto& device: devicePorts) { ++ if(device->getTagName() == "BT SCO Headset") { ++ foundBtScoHeadsetDevice = true; ++ break; ++ } ++ } ++ if(!foundBtScoHeadsetDevice) { ++ ALOGE("No BT SCO Headset device found, don't patch policy"); ++ return; ++ } ++ ++ bool foundTelephony = false; ++ bool foundBtScoInput = false; ++ bool foundScoHeadsetRoute = false; ++ for(const auto& route: routes) { ++ ALOGE("Looking at route %d\n", route->getType()); ++ if(route->getType() != AUDIO_ROUTE_MIX) ++ continue; ++ auto sink = route->getSink(); ++ ALOGE("... With sink %s\n", sink->getTagName().c_str()); ++ if(sink->getTagName() == "Telephony Tx") { ++ foundTelephony = true; ++ continue; ++ } ++ if(sink->getTagName() == "BT SCO Headset") { ++ foundScoHeadsetRoute = true; ++ break; ++ } ++ for(const auto& source: route->getSources()) { ++ ALOGE("... With source %s\n", source->getTagName().c_str()); ++ if(source->getTagName() == "BT SCO Headset Mic") { ++ foundBtScoInput = true; ++ break; ++ } ++ } ++ } ++ //The route we want to add is already there ++ ALOGE("Done looking for existing routes"); ++ if(foundScoHeadsetRoute) ++ return; ++ ++ ALOGE("No existing route found... %d %d", foundTelephony ? 1 : 0, foundBtScoInput ? 1 : 0); ++ //We couldn't find the routes we assume are required for the function we want to add ++ if(!foundTelephony || !foundBtScoInput) ++ return; ++ ALOGE("Adding our own."); ++ ++ // Add: ++ // ++ AudioRoute *newRoute = new AudioRoute(AUDIO_ROUTE_MIX); ++ ++ auto sink = ctx->findPortByTagName("BT SCO Headset"); ++ ALOGE("Got sink %p\n", sink.get()); ++ newRoute->setSink(sink); ++ ++ Vector> sources; ++ for(const auto& sourceName: { ++ "primary output", ++ "deep_buffer", ++ "compressed_offload", ++ "Telephony Rx" ++ }) { ++ auto source = ctx->findPortByTagName(sourceName); ++ ALOGE("Got source %p\n", source.get()); ++ if (source.get() != nullptr) { ++ sources.add(source); ++ source->addRoute(newRoute); ++ } ++ } ++ ++ newRoute->setSources(sources); ++ ++ sink->addRoute(newRoute); ++ ++ auto ret = routes.add(newRoute); ++ ALOGE("route add returned %zd", ret); ++} ++ + Return ModuleTraits::deserialize(const xmlNode *cur, PtrSerializingCtx ctx) + { + std::string name = getXmlAttribute(cur, Attributes::name); +@@ -635,6 +727,7 @@ Return ModuleTraits::deserialize(const xmlNode *cur, PtrS + if (status != NO_ERROR) { + return Status::fromStatusT(status); + } ++ fixupQualcommBtScoRoute(routes, devicePorts, module.get()); + module->setRoutes(routes); + + for (const xmlNode *children = cur->xmlChildrenNode; children != NULL; +-- +2.17.1 + diff --git a/patches/platform_frameworks_av/0006-Add-partial-cam-id-is-hardcoded-support-for-Asus-ZF6.patch b/patches/platform_frameworks_av/0006-Add-partial-cam-id-is-hardcoded-support-for-Asus-ZF6.patch new file mode 100644 index 0000000..244528a --- /dev/null +++ b/patches/platform_frameworks_av/0006-Add-partial-cam-id-is-hardcoded-support-for-Asus-ZF6.patch @@ -0,0 +1,94 @@ +From 4f7930e14f979ec5be530bd630ca9bb664050420 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Tue, 1 Oct 2019 13:35:49 +0200 +Subject: [PATCH 06/14] Add (partial, cam id is hardcoded) support for Asus ZF6 + motor camera + +Change-Id: Iea6e1370780a1d16f728748d1d948d092532d8fe +--- + .../camera/libcameraservice/CameraService.cpp | 26 +++++++++++++++++++ + .../camera/libcameraservice/CameraService.h | 3 +++ + 2 files changed, 29 insertions(+) + +diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp +index 83dea10f3a..37ea512bdd 100644 +--- a/services/camera/libcameraservice/CameraService.cpp ++++ b/services/camera/libcameraservice/CameraService.cpp +@@ -25,6 +25,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -139,6 +140,7 @@ Mutex CameraService::sProxyMutex; + sp CameraService::sCameraServiceProxy; + + CameraService::CameraService() : ++ mPhysicalFrontCamStatus(false), + mEventLog(DEFAULT_EVENT_LOG_LENGTH), + mNumberOfCameras(0), + mNumberOfCamerasWithoutSystemCamera(0), +@@ -1785,6 +1787,7 @@ Status CameraService::connectHelper(const sp& cameraCb, const String8& + mServiceLock.lock(); + } else { + // Otherwise, add client to active clients list ++ physicalFrontCam(cameraId == "1"); + finishConnectLocked(client, partial); + } + } // lock is destroyed, allow further connect calls +@@ -1866,6 +1869,27 @@ status_t CameraService::addOfflineClient(String8 cameraId, sp offli + return OK; + } + ++void CameraService::physicalFrontCam(bool on) { ++ if(on == mPhysicalFrontCamStatus) return; ++ mPhysicalFrontCamStatus = on; ++ ++ if(access("/dev/asusMotoDrv", F_OK) == 0) { ++ int pid = fork(); ++ if(pid == 0) { ++ const char* cmd[] = { ++ "/system/bin/asus-motor", ++ "0", ++ NULL ++ }; ++ cmd[1] = on ? "0" : "1"; ++ execve("/system/bin/asus-motor", (char**)cmd, environ); ++ _exit(1); ++ } else { ++ waitpid(pid, NULL, 0); ++ } ++ } ++} ++ + Status CameraService::setTorchMode(const String16& cameraId, bool enabled, + const sp& clientBinder) { + Mutex::Autolock lock(mServiceLock); +@@ -2840,6 +2864,8 @@ binder::Status CameraService::BasicClient::disconnect() { + } + mDisconnected = true; + ++ sCameraService->physicalFrontCam(false); ++ + sCameraService->removeByClient(this); + sCameraService->logDisconnected(mCameraIdStr, mClientPid, String8(mClientPackageName)); + sCameraService->mCameraProviderManager->removeRef(CameraProviderManager::DeviceMode::CAMERA, +diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h +index 4321201100..85b1706996 100644 +--- a/services/camera/libcameraservice/CameraService.h ++++ b/services/camera/libcameraservice/CameraService.h +@@ -202,6 +202,9 @@ public: + // Register an offline client for a given active camera id + status_t addOfflineClient(String8 cameraId, sp offlineClient); + ++ bool mPhysicalFrontCamStatus; ++ void physicalFrontCam(bool on); ++ + ///////////////////////////////////////////////////////////////////// + // Client functionality + +-- +2.17.1 + diff --git a/patches/platform_frameworks_av/0007-audiopolicy-try-again-with-trimmed-audio-port-name-i.patch b/patches/platform_frameworks_av/0007-audiopolicy-try-again-with-trimmed-audio-port-name-i.patch new file mode 100644 index 0000000..afc2766 --- /dev/null +++ b/patches/platform_frameworks_av/0007-audiopolicy-try-again-with-trimmed-audio-port-name-i.patch @@ -0,0 +1,59 @@ +From 93d21e226a4cac72ebc73f8fcec3001160d8570f Mon Sep 17 00:00:00 2001 +From: Peter Cai +Date: Thu, 23 Jan 2020 11:13:43 +0800 +Subject: [PATCH 07/14] audiopolicy: try again with trimmed audio port name if + not found + +* In Spreadtrum BSP, some audio routes may contain ports with extra + spaces at the beginning and the end, causing audiopolicy to refuse to + load and leading to broken audio. + +* Fix this by retrying with trimmed port name when not found. Do not + use trimmed name all the time because a white space is a valid + character in port name, and we cannot be sure nobody is using it for + legitimite purposes. + +Change-Id: I993708b28e8404bc8c483d71a850ac69382231bd +--- + .../managerdefinitions/src/Serializer.cpp | 18 ++++++++++++++++-- + 1 file changed, 16 insertions(+), 2 deletions(-) + +diff --git a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp +index d85dbdf113..e25dda07c7 100644 +--- a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp ++++ b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp +@@ -539,6 +539,17 @@ Return DevicePortTraits::deserialize(const xmlNode *c + return deviceDesc; + } + ++char* trim(char * s) { ++ int l = strlen(s); ++ ++ if (l > 0) { ++ while (isspace(s[l - 1])) --l; ++ while (*s && isspace(*s)) ++s, --l; ++ } ++ ++ return strndup(s, l); ++} ++ + Return RouteTraits::deserialize(const xmlNode *cur, PtrSerializingCtx ctx) + { + std::string type = getXmlAttribute(cur, Attributes::type); +@@ -579,8 +590,11 @@ Return RouteTraits::deserialize(const xmlNode *cur, PtrSer + if (strlen(devTag) != 0) { + sp source = ctx->findPortByTagName(devTag); + if (source == NULL) { +- ALOGE("%s: no source found with name=%s", __func__, devTag); +- return Status::fromStatusT(BAD_VALUE); ++ source = ctx->findPortByTagName(trim(devTag)); ++ if (source == NULL) { ++ ALOGE("%s: no source found with name=%s", __func__, devTag); ++ return Status::fromStatusT(BAD_VALUE); ++ } + } + sources.add(source); + } +-- +2.17.1 + diff --git a/patches/platform_frameworks_av/0008-There-are-three-SCO-devices.-Fallback-from-one-to-th.patch b/patches/platform_frameworks_av/0008-There-are-three-SCO-devices.-Fallback-from-one-to-th.patch new file mode 100644 index 0000000..c2fa555 --- /dev/null +++ b/patches/platform_frameworks_av/0008-There-are-three-SCO-devices.-Fallback-from-one-to-th.patch @@ -0,0 +1,46 @@ +From 2c80a9c1453230a62f538fc128f286005d91b802 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Mon, 13 Apr 2020 21:01:16 +0200 +Subject: [PATCH 08/14] There are three SCO devices. Fallback from one to the + others if needed + +Change-Id: I414dcb6b154855c00cb8520b23dc1069827864b2 +--- + .../managerdefinitions/src/HwModule.cpp | 21 +++++++++++++++++++ + 1 file changed, 21 insertions(+) + +diff --git a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp +index d31e4437e1..b2ca5e85de 100644 +--- a/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp ++++ b/services/audiopolicy/common/managerdefinitions/src/HwModule.cpp +@@ -292,6 +292,27 @@ sp HwModuleCollection::getModuleForDeviceType(audio_devices_t type, + } + } + } ++ //We didn't find one? Ok but all SCOs are equivalent surely? ++ if(type == AUDIO_DEVICE_OUT_BLUETOOTH_SCO || ++ type == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET || ++ type == AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT) { ++ ALOGE("Fallback SCO"); ++ if(type != AUDIO_DEVICE_OUT_BLUETOOTH_SCO) { ++ auto ret = getModuleForDeviceType(AUDIO_DEVICE_OUT_BLUETOOTH_SCO, encodedFormat); ++ ALOGE("Fallback SCO simple? %s", (ret != nullptr) ? "yes" : "no"); ++ if(ret != nullptr) return ret; ++ } ++ if(type != AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET) { ++ auto ret = getModuleForDeviceType(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET, encodedFormat); ++ ALOGE("Fallback SCO headset? %s", (ret != nullptr) ? "yes" : "no"); ++ if(ret != nullptr) return ret; ++ } ++ if(type != AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT) { ++ auto ret = getModuleForDeviceType(AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT, encodedFormat); ++ ALOGE("Fallback SCO carkit? %s", (ret != nullptr) ? "yes" : "no"); ++ if(ret != nullptr) return ret; ++ } ++ } + return nullptr; + } + +-- +2.17.1 + diff --git a/patches/platform_frameworks_av/0010-Add-persist.sys.phh.samsung.camera_ids-property-to-a.patch b/patches/platform_frameworks_av/0010-Add-persist.sys.phh.samsung.camera_ids-property-to-a.patch new file mode 100644 index 0000000..12fbaf1 --- /dev/null +++ b/patches/platform_frameworks_av/0010-Add-persist.sys.phh.samsung.camera_ids-property-to-a.patch @@ -0,0 +1,82 @@ +From 9ac09c1e3d8408ce08a6e99e6403fb75cccf20ba Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Thu, 14 May 2020 19:54:55 +0200 +Subject: [PATCH 10/14] Add persist.sys.phh.samsung.camera_ids property to + access hidden Samsung cameras + +Change-Id: I2c7bf535272acc28ed2277e96c78ddd28a0b4593 +--- + services/camera/libcameraservice/Android.bp | 3 ++- + .../common/CameraProviderManager.cpp | 14 ++++++++++++-- + 2 files changed, 14 insertions(+), 3 deletions(-) + +diff --git a/services/camera/libcameraservice/Android.bp b/services/camera/libcameraservice/Android.bp +index 501d92226f..be275e9346 100644 +--- a/services/camera/libcameraservice/Android.bp ++++ b/services/camera/libcameraservice/Android.bp +@@ -127,7 +127,8 @@ cc_library_shared { + "android.hardware.camera.device@3.3", + "android.hardware.camera.device@3.4", + "android.hardware.camera.device@3.5", +- "android.hardware.camera.device@3.6" ++ "android.hardware.camera.device@3.6", ++ "vendor.samsung.hardware.camera.provider@3.0", + ], + + static_libs: [ +diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp +index 32d118d6b0..eb27e055ac 100644 +--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp ++++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp +@@ -21,6 +21,7 @@ + #include "CameraProviderManager.h" + + #include ++#include + + #include + #include +@@ -1330,6 +1331,7 @@ status_t CameraProviderManager::ProviderInfo::initialize( + mMinorVersion = 6; + } + } ++ + // We need to check again since cast2_6.isOk() succeeds even if the provider + // version isn't actually 2.6. + if (interface2_6 == nullptr){ +@@ -1344,6 +1346,9 @@ status_t CameraProviderManager::ProviderInfo::initialize( + } + } + ++ auto samsungCast = vendor::samsung::hardware::camera::provider::V3_0::ISehCameraProvider::castFrom(interface); ++ auto samsungProvider = samsungCast.isOk() ? static_cast>(samsungCast) : nullptr; ++ + hardware::Return linked = interface->linkToDeath(this, /*cookie*/ mId); + if (!linked.isOk()) { + ALOGE("%s: Transaction error in linking to camera provider '%s' death: %s", +@@ -1375,7 +1380,7 @@ status_t CameraProviderManager::ProviderInfo::initialize( + Status status; + // Get initial list of camera devices, if any + std::vector devices; +- hardware::Return ret = interface->getCameraIdList([&status, this, &devices]( ++ auto cb = [&status, this, &devices]( + Status idStatus, + const hardware::hidl_vec& cameraDeviceNames) { + status = idStatus; +@@ -1392,7 +1397,12 @@ status_t CameraProviderManager::ProviderInfo::initialize( + mProviderPublicCameraIds.push_back(id); + } + } +- } }); ++ } }; ++ hardware::Return ret; ++ if(samsungProvider != nullptr && property_get_bool("persist.sys.phh.samsung.camera_ids", false)) ++ ret = samsungProvider->sehGetCameraIdList(cb); ++ else ++ ret = interface->getCameraIdList(cb); + if (!ret.isOk()) { + ALOGE("%s: Transaction error in getting camera ID list from provider '%s': %s", + __FUNCTION__, mProviderName.c_str(), linked.description().c_str()); +-- +2.17.1 + diff --git a/patches/platform_frameworks_av/0011-Add-a-property-to-force-camera-timestamp-source.patch b/patches/platform_frameworks_av/0011-Add-a-property-to-force-camera-timestamp-source.patch new file mode 100644 index 0000000..b1690db --- /dev/null +++ b/patches/platform_frameworks_av/0011-Add-a-property-to-force-camera-timestamp-source.patch @@ -0,0 +1,45 @@ +From 6dff7fa42818b525faa0a80d161ad54055ebc38e Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Tue, 19 May 2020 14:01:14 +0200 +Subject: [PATCH 11/14] Add a property to force camera timestamp source + +Some devices wrongly report their timesource +Camera's timesource can either be CLOCK_MONOTONIC, or CLOCK_BOOTTIME +The former doesn't increment in sleep, while the later does. +There is a camera HAL property for that, though some devices don't +report it properly. + +This issue happens on Xiaomi Redmi 7A, it needs to force the value to 1 + +Add a property persist.sys.phh.camera.force_timestampsource to force +timestamp source. +--- + .../libcameraservice/device3/Camera3Device.cpp | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp +index 4a509aa745..cdf9fc783e 100644 +--- a/services/camera/libcameraservice/device3/Camera3Device.cpp ++++ b/services/camera/libcameraservice/device3/Camera3Device.cpp +@@ -316,8 +316,16 @@ status_t Camera3Device::initializeCommonLocked() { + // Measure the clock domain offset between camera and video/hw_composer + camera_metadata_entry timestampSource = + mDeviceInfo.find(ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE); +- if (timestampSource.count > 0 && timestampSource.data.u8[0] == +- ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME) { ++ int timestampSourceValue = 0; ++ if ((timestampSource.count > 0 && timestampSource.data.u8[0] == ++ ANDROID_SENSOR_INFO_TIMESTAMP_SOURCE_REALTIME)) { ++ timestampSourceValue = 1; ++ } ++ int forceTimestampSource = property_get_int32("persist.sys.phh.camera.force_timestampsource", -1); ++ //Don't override if it's -1, default value ++ if(forceTimestampSource == 0) timestampSourceValue = 0; ++ if(forceTimestampSource == 1) timestampSourceValue = 1; ++ if (timestampSourceValue == 1) { + mTimestampOffset = getMonoToBoottimeOffset(); + } + +-- +2.17.1 + diff --git a/patches/platform_frameworks_av/0012-FIH-devices-Fix-Earpiece-audio-output.patch b/patches/platform_frameworks_av/0012-FIH-devices-Fix-Earpiece-audio-output.patch new file mode 100644 index 0000000..bd3475f --- /dev/null +++ b/patches/platform_frameworks_av/0012-FIH-devices-Fix-Earpiece-audio-output.patch @@ -0,0 +1,79 @@ +From 7f14fcbf4bcf6d8f8c4b89f6ac4b91bc4961ed6f Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Tue, 24 Apr 2018 00:14:28 +0200 +Subject: [PATCH 12/14] FIH devices: Fix "Earpiece" audio output + +On some FIH devices (confirmed on Razer, and probably on Aquos SS2), +Earpiece is not listed in attachedDevices, and devicePort's profile +mentions it is AUDIO_CHANNEL_IN_MONO, instead of AUDIO_CHANNEL_OUT_MONO. + +Detect such cases (output device, but got only AUDIO_CHANNEL_IN_MONO), +and fix both channelMasks and attachedDevices + +Change-Id: I4a88ba6d34d0fcd346eeea2ca903772f0271040a +--- + .../managerdefinitions/src/Serializer.cpp | 27 ++++++++++++++++--- + 1 file changed, 24 insertions(+), 3 deletions(-) + +diff --git a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp +index e25dda07c7..a8eb142c81 100644 +--- a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp ++++ b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp +@@ -396,15 +396,24 @@ Return AudioGainTraits::deserialize(const xmlNode *cur + } + } + ++static bool fixedEarpieceChannels = false; + Return AudioProfileTraits::deserialize(const xmlNode *cur, +- PtrSerializingCtx /*serializingContext*/) ++ PtrSerializingCtx serializingContext) + { ++ bool isOutput = serializingContext != nullptr; + std::string samplingRates = getXmlAttribute(cur, Attributes::samplingRates); + std::string format = getXmlAttribute(cur, Attributes::format); + std::string channels = getXmlAttribute(cur, Attributes::channelMasks); ++ ChannelTraits::Collection channelsMask = channelMasksFromString(channels, ","); ++ ++ //Some Foxconn devices have wrong earpiece channel mask, leading to no channel mask ++ if(channelsMask.size() == 1 && *channelsMask.begin() == AUDIO_CHANNEL_IN_MONO && isOutput) { ++ fixedEarpieceChannels = true; ++ channelsMask = channelMasksFromString("AUDIO_CHANNEL_OUT_MONO", ","); ++ } + + Element profile = new AudioProfile(formatFromString(format, gDynamicFormat), +- channelMasksFromString(channels, ","), ++ channelsMask, + samplingRatesFromString(samplingRates, ",")); + + profile->setDynamicFormat(profile->getFormat() == gDynamicFormat); +@@ -517,7 +526,11 @@ Return DevicePortTraits::deserialize(const xmlNode *c + Element deviceDesc = new DeviceDescriptor(type, name, address, encodedFormats); + + AudioProfileTraits::Collection profiles; +- status_t status = deserializeCollection(cur, &profiles, NULL); ++ status_t status; ++ if(audio_is_output_devices(type)) ++ status = deserializeCollection(cur, &profiles, (PtrSerializingCtx)1); ++ else ++ status = deserializeCollection(cur, &profiles, NULL); + if (status != NO_ERROR) { + return Status::fromStatusT(status); + } +@@ -782,6 +795,14 @@ Return ModuleTraits::deserialize(const xmlNode *cur, PtrS + } + } + } ++ ++ if(fixedEarpieceChannels) { ++ sp device = ++ module->getDeclaredDevices().getDeviceFromTagName("Earpiece"); ++ if(device != 0) ++ ctx->addDevice(device); ++ fixedEarpieceChannels = false; ++ } + return module; + } + +-- +2.17.1 + diff --git a/patches/platform_frameworks_av/0013-audiopolicy-No-longer-make-re-assigning-legacy-audio.patch b/patches/platform_frameworks_av/0013-audiopolicy-No-longer-make-re-assigning-legacy-audio.patch new file mode 100644 index 0000000..c28be83 --- /dev/null +++ b/patches/platform_frameworks_av/0013-audiopolicy-No-longer-make-re-assigning-legacy-audio.patch @@ -0,0 +1,32 @@ +From 4b6dc11462996df5987ac339bf2fb750f500a067 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Sat, 12 Sep 2020 12:32:50 +0200 +Subject: [PATCH 13/14] [audiopolicy] No longer make re-assigning legacy audio + groups fatal. Mi9 declares AUDIO_STREAM_PATCH and AUDIO_STREAM_REROUTING + which is defined by framework too + +Change-Id: I794fe22d63a8af705be4f5f09b9879ecaab3eae5 +--- + services/audiopolicy/engine/common/src/EngineBase.cpp | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +diff --git a/services/audiopolicy/engine/common/src/EngineBase.cpp b/services/audiopolicy/engine/common/src/EngineBase.cpp +index 1bc7fe3dcc..fe18d0d794 100644 +--- a/services/audiopolicy/engine/common/src/EngineBase.cpp ++++ b/services/audiopolicy/engine/common/src/EngineBase.cpp +@@ -206,9 +206,9 @@ engineConfig::ParsingResult EngineBase::loadAudioPolicyEngineConfig() + } + if (group.stream != AUDIO_STREAM_DEFAULT) { + // A legacy stream can be assigned once to a volume group +- LOG_ALWAYS_FATAL_IF(checkStreamForGroups(group.stream, mVolumeGroups), +- "stream %s already assigned to a volume group, " +- "review the configuration", toString(group.stream).c_str()); ++ if(checkStreamForGroups(group.stream, mVolumeGroups)) { ++ ALOGE("stream %s already assigned to a volume group, review the configuration", toString(group.stream).c_str()); ++ } + volumeGroup->addSupportedStream(group.stream); + } + addSupportedAttributesToGroup(group, volumeGroup, strategy); +-- +2.17.1 + diff --git a/patches/platform_frameworks_av/0014-Add-persist.sys.phh.disable_a2dp_offload-property-to.patch b/patches/platform_frameworks_av/0014-Add-persist.sys.phh.disable_a2dp_offload-property-to.patch new file mode 100644 index 0000000..9ecb4da --- /dev/null +++ b/patches/platform_frameworks_av/0014-Add-persist.sys.phh.disable_a2dp_offload-property-to.patch @@ -0,0 +1,341 @@ +From d24b9bfa7113578d6a9e7a98a01e0ef6dfb970ea Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Mon, 25 May 2020 21:26:54 +0200 +Subject: [PATCH 14/14] Add persist.sys.phh.disable_a2dp_offload property to + force a2dp offload + +Change-Id: Id474540c33c594cc4010a1a398d82bff8aadaeea +--- + media/libstagefright/ACodec.cpp | 20 +- + .../managerdefinitions/src/Serializer.cpp | 181 +++++++++++++++++- + .../audiopolicy/enginedefault/src/Engine.cpp | 2 +- + .../managerdefault/AudioPolicyManager.cpp | 3 + + 4 files changed, 199 insertions(+), 7 deletions(-) + +diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp +index e25f8f6558..7ff3c8f61b 100644 +--- a/media/libstagefright/ACodec.cpp ++++ b/media/libstagefright/ACodec.cpp +@@ -1166,6 +1166,9 @@ status_t ACodec::configureOutputBuffersFromNativeWindow( + return err; + } + ++ ALOGE("Window undequeued buffers is %d\n", *minUndequeuedBuffers); ++ ALOGE("Consumerrequested %d\n", def.nBufferCountMin); ++ + // FIXME: assume that surface is controlled by app (native window + // returns the number for the case when surface is not controlled by app) + // FIXME2: This means that minUndeqeueudBufs can be 1 larger than reported +@@ -1178,22 +1181,29 @@ status_t ACodec::configureOutputBuffersFromNativeWindow( + // 2. try to allocate two (2) additional buffers to reduce starvation from + // the consumer + // plus an extra buffer to account for incorrect minUndequeuedBufs +- for (OMX_U32 extraBuffers = 2 + 1; /* condition inside loop */; extraBuffers--) { +- OMX_U32 newBufferCount = +- def.nBufferCountMin + *minUndequeuedBuffers + extraBuffers; ++ for (int extraBuffers = 2 + 1; /* condition inside loop */; extraBuffers--) { ++ int a = def.nBufferCountMin; ++ int b = *minUndequeuedBuffers; ++ int c = extraBuffers; ++ int newBufferCount = a+b+c; + def.nBufferCountActual = newBufferCount; + err = mOMXNode->setParameter( + OMX_IndexParamPortDefinition, &def, sizeof(def)); + + if (err == OK) { +- *minUndequeuedBuffers += extraBuffers; ++ ALOGE("Managed to allocate %d buffers (%d extra)\n", newBufferCount, extraBuffers); ++ if(extraBuffers > 0) { ++ *minUndequeuedBuffers += extraBuffers; ++ } else { ++ *minUndequeuedBuffers -= -extraBuffers; ++ } + break; + } + + ALOGW("[%s] setting nBufferCountActual to %u failed: %d", + mComponentName.c_str(), newBufferCount, err); + /* exit condition */ +- if (extraBuffers == 0) { ++ if (extraBuffers == -2 || newBufferCount == 1) { + return err; + } + } +diff --git a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp +index a8eb142c81..73e4a3e096 100644 +--- a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp ++++ b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp +@@ -29,6 +29,7 @@ + #include + #include + #include ++#include + #include "Serializer.h" + #include "TypeConverter.h" + +@@ -36,6 +37,8 @@ namespace android { + + namespace { + ++static bool forceDisableA2dpOffload = false; ++ + // TODO(mnaganov): Consider finding an alternative for using HIDL code. + using hardware::Return; + using hardware::Status; +@@ -318,7 +321,7 @@ status_t deserializeCollection(const xmlNode *cur, + return status; + } + } else { +- return BAD_VALUE; ++ ALOGE("Ignoring..."); + } + } + } +@@ -734,12 +737,34 @@ Return ModuleTraits::deserialize(const xmlNode *cur, PtrS + + Element module = new HwModule(name.c_str(), versionMajor, versionMinor); + ++ bool isA2dpModule = strcmp(name.c_str(), "a2dp") == 0; ++ bool isPrimaryModule = strcmp(name.c_str(), "primary") == 0; ++ + // Deserialize childrens: Audio Mix Port, Audio Device Ports (Source/Sink), Audio Routes + MixPortTraits::Collection mixPorts; + status_t status = deserializeCollection(cur, &mixPorts, NULL); + if (status != NO_ERROR) { + return Status::fromStatusT(status); + } ++ if(forceDisableA2dpOffload && isA2dpModule) { ++ for(const auto& mixPort: mixPorts) { ++ ALOGE("Disable a2dp offload...? %s", mixPort->getTagName().c_str()); ++ //"a2dp" sw module already has a2dp out ++ if(mixPort->getTagName() == "a2dp output") { ++ forceDisableA2dpOffload = false; ++ break; ++ } ++ } ++ } ++ if(forceDisableA2dpOffload && isA2dpModule) { ++ //Add ++ // ++ auto mixPort = new IOProfile("a2dp output", AUDIO_PORT_ROLE_SOURCE); ++ AudioProfileTraits::Collection profiles; ++ profiles.add(AudioProfile::createFullDynamic()); ++ mixPort->setAudioProfiles(profiles); ++ mixPorts.push_back(mixPort); ++ } + module->setProfiles(mixPorts); + + DevicePortTraits::Collection devicePorts; +@@ -747,6 +772,90 @@ Return ModuleTraits::deserialize(const xmlNode *cur, PtrS + if (status != NO_ERROR) { + return Status::fromStatusT(status); + } ++ Vector a2dpOuts; ++ a2dpOuts.push_back("BT A2DP Out"); ++ a2dpOuts.push_back("BT A2DP Headphones"); ++ a2dpOuts.push_back("BT A2DP Speaker"); ++ if(forceDisableA2dpOffload) { ++ if(isA2dpModule) { ++ // ++ // ++ // ++ if(true) { ++ FormatVector formats; ++ //auto devicePortOut = new DeviceDescriptor(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, formats, "BT A2DP Out"); ++ auto devicePortOut = new DeviceDescriptor(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP, "BT A2DP Out"); ++ AudioProfileTraits::Collection profiles; ++ ChannelTraits::Collection channels; ++ channels.insert(AUDIO_CHANNEL_OUT_STEREO); ++ SampleRateSet sampleRates; ++ sampleRates.insert(44100); ++ sampleRates.insert(48000); ++ sampleRates.insert(96000); ++ auto profile = new AudioProfile(AUDIO_FORMAT_PCM_16_BIT, channels, sampleRates); ++ profiles.add(profile); ++ devicePortOut->setAudioProfiles(profiles); ++ devicePortOut->setAddress("lhdc_a2dp"); ++ devicePorts.add(devicePortOut); ++ } ++ // ++ // ++ // ++ if(true) { ++ FormatVector formats; ++ auto devicePortOut = new DeviceDescriptor(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES, "BT A2DP Headphones"); ++ AudioProfileTraits::Collection profiles; ++ ChannelTraits::Collection channels; ++ channels.insert(AUDIO_CHANNEL_OUT_STEREO); ++ SampleRateSet sampleRates; ++ sampleRates.insert(44100); ++ sampleRates.insert(48000); ++ sampleRates.insert(96000); ++ auto profile = new AudioProfile(AUDIO_FORMAT_PCM_16_BIT, channels, sampleRates); ++ profiles.add(profile); ++ devicePortOut->setAudioProfiles(profiles); ++ devicePortOut->setAddress("lhdc_a2dp"); ++ devicePorts.add(devicePortOut); ++ } ++ // ++ // ++ // ++ if(true) { ++ FormatVector formats; ++ auto devicePortOut = new DeviceDescriptor(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER, "BT A2DP Speaker"); ++ AudioProfileTraits::Collection profiles; ++ ChannelTraits::Collection channels; ++ channels.insert(AUDIO_CHANNEL_OUT_STEREO); ++ SampleRateSet sampleRates; ++ sampleRates.insert(44100); ++ sampleRates.insert(48000); ++ sampleRates.insert(96000); ++ auto profile = new AudioProfile(AUDIO_FORMAT_PCM_16_BIT, channels, sampleRates); ++ profiles.add(profile); ++ devicePortOut->setAudioProfiles(profiles); ++ devicePortOut->setAddress("lhdc_a2dp"); ++ devicePorts.add(devicePortOut); ++ ++ } ++ } else if(isPrimaryModule) { ++ for(const auto& out: a2dpOuts) { ++ auto iterA = std::find_if(devicePorts.begin(), devicePorts.end(), [out](const auto port) { ++ if(port->getTagName() == out) return true; ++ return false; ++ }); ++ if(iterA != devicePorts.end()) { ++ ALOGE("Erasing device port %s", (*iterA)->getTagName().c_str()); ++ devicePorts.erase(iterA); ++ } ++ } ++ } ++ } + module->setDeclaredDevices(devicePorts); + + RouteTraits::Collection routes; +@@ -754,7 +863,76 @@ Return ModuleTraits::deserialize(const xmlNode *cur, PtrS + if (status != NO_ERROR) { + return Status::fromStatusT(status); + } ++ if(forceDisableA2dpOffload) { ++ if(strcmp(name.c_str(), "primary") == 0) { ++ for(const auto& out: a2dpOuts) { ++ auto iterA = std::find_if(routes.begin(), routes.end(), [out](const auto route) { ++ if(route->getType() != AUDIO_ROUTE_MIX) ++ return false; ++ auto sink = route->getSink(); ++ if(sink->getTagName() == out) { ++ return true; ++ } ++ return false; ++ }); ++ if(iterA != routes.end()) { ++ auto sink = (*iterA)->getSink()->getTagName(); ++ ALOGE("Erasing route %s", sink.c_str()); ++ routes.erase(iterA); ++ } ++ } ++ } else if(isA2dpModule) { ++ // ++ if(true) { ++ auto newRoute = new AudioRoute(AUDIO_ROUTE_MIX); ++ auto sink = module->findPortByTagName("BT A2DP Out"); ++ auto source = module->findPortByTagName("a2dp output"); ++ newRoute->setSink(sink); ++ Vector> sources; ++ sources.add(source); ++ ++ sink->addRoute(newRoute); ++ source->addRoute(newRoute); ++ newRoute->setSources(sources); ++ ++ routes.add(newRoute); ++ } ++ // ++ if(true) { ++ auto newRoute = new AudioRoute(AUDIO_ROUTE_MIX); ++ auto sink = module->findPortByTagName("BT A2DP Headphones"); ++ auto source = module->findPortByTagName("a2dp output"); ++ newRoute->setSink(sink); ++ Vector> sources; ++ sources.add(source); ++ ++ sink->addRoute(newRoute); ++ source->addRoute(newRoute); ++ newRoute->setSources(sources); ++ routes.add(newRoute); ++ } ++ // ++ if(true) { ++ auto newRoute = new AudioRoute(AUDIO_ROUTE_MIX); ++ auto sink = module->findPortByTagName("BT A2DP Speaker"); ++ auto source = module->findPortByTagName("a2dp output"); ++ newRoute->setSink(sink); ++ Vector> sources; ++ sources.add(source); ++ ++ sink->addRoute(newRoute); ++ source->addRoute(newRoute); ++ newRoute->setSources(sources); ++ routes.add(newRoute); ++ } ++ } ++ } ++ ALOGE("Good morning"); + fixupQualcommBtScoRoute(routes, devicePorts, module.get()); ++ ALOGE("Good morning2"); + module->setRoutes(routes); + + for (const xmlNode *children = cur->xmlChildrenNode; children != NULL; +@@ -932,6 +1110,7 @@ status_t PolicySerializer::deserialize(const char *configFile, AudioPolicyConfig + status_t deserializeAudioPolicyFile(const char *fileName, AudioPolicyConfig *config) + { + PolicySerializer serializer; ++ forceDisableA2dpOffload = property_get_bool("persist.sys.phh.disable_a2dp_offload", false); + return serializer.deserialize(fileName, config); + } + +diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp +index b14d2bbb0b..16a7a4bbac 100755 +--- a/services/audiopolicy/enginedefault/src/Engine.cpp ++++ b/services/audiopolicy/enginedefault/src/Engine.cpp +@@ -52,7 +52,7 @@ static const std::vector& getLegacyStrategy() { + { "STRATEGY_ENFORCED_AUDIBLE", STRATEGY_ENFORCED_AUDIBLE }, + { "STRATEGY_TRANSMITTED_THROUGH_SPEAKER", STRATEGY_TRANSMITTED_THROUGH_SPEAKER }, + { "STRATEGY_ACCESSIBILITY", STRATEGY_ACCESSIBILITY }, +- { "STRATEGY_REROUTING", STRATEGY_REROUTING }, ++ //{ "STRATEGY_REROUTING", STRATEGY_REROUTING }, + { "STRATEGY_PATCH", STRATEGY_REROUTING }, // boiler to manage stream patch volume + { "STRATEGY_CALL_ASSISTANT", STRATEGY_CALL_ASSISTANT }, + }; +diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +index c5c13e9a5a..1c5ed86b86 100644 +--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp ++++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +@@ -93,6 +93,8 @@ bool operator!= (const SortedVector &left, const SortedVector &right) + return !(left == right); + } + ++static bool forceDisableA2dpOffload = false; ++ + // ---------------------------------------------------------------------------- + // AudioPolicyInterface implementation + // ---------------------------------------------------------------------------- +@@ -4458,6 +4460,7 @@ static status_t deserializeAudioPolicyXmlConfig(AudioPolicyConfig &config) { + } else if (property_get_bool("persist.bluetooth.bluetooth_audio_hal.disabled", false)) { + fileNames.push_back(AUDIO_POLICY_BLUETOOTH_LEGACY_HAL_XML_CONFIG_FILE_NAME); + } ++ forceDisableA2dpOffload = property_get_bool("persist.sys.phh.disable_a2dp_offload", false); + fileNames.push_back(AUDIO_POLICY_XML_CONFIG_FILE_NAME); + + for (const char* fileName : fileNames) { +-- +2.17.1 + diff --git a/patches/platform_frameworks_base/0001-Relax-requirement-for-visible-flag-to-sdcards.patch b/patches/platform_frameworks_base/0001-Relax-requirement-for-visible-flag-to-sdcards.patch new file mode 100644 index 0000000..ef62fb2 --- /dev/null +++ b/patches/platform_frameworks_base/0001-Relax-requirement-for-visible-flag-to-sdcards.patch @@ -0,0 +1,31 @@ +From 494741f30b1c6a1ce223e6331d6ade70e9bc0b52 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Tue, 28 Nov 2017 18:28:04 +0100 +Subject: [PATCH] Relax requirement for visible flag to sdcards + +The vast majority of sdcard readers are stable enough to be declared by +the API. (I see no counter-example) +FBE broke adoptable storage with SDCard, hence this need. + +Change-Id: Ia616671c03562d1eadaff5531a5c708a62d7ad3a +--- + .../core/java/com/android/server/StorageManagerService.java | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java +index 498c52a5aa6..dd7f4560000 100644 +--- a/services/core/java/com/android/server/StorageManagerService.java ++++ b/services/core/java/com/android/server/StorageManagerService.java +@@ -1525,7 +1525,8 @@ class StorageManagerService extends IStorageManager.Stub + + // Adoptable public disks are visible to apps, since they meet + // public API requirement of being in a stable location. +- if (vol.disk.isAdoptable()) { ++ // Assume all SDs match this as well ++ if (vol.disk.isAdoptable() || vol.disk.isSd()) { + vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE; + } else if (vol.disk.isSd()) { + vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE; +-- +2.25.1 + diff --git a/patches/platform_frameworks_base/0002-backlight-Fix-backlight-control-on-Galaxy-S9.patch b/patches/platform_frameworks_base/0002-backlight-Fix-backlight-control-on-Galaxy-S9.patch new file mode 100644 index 0000000..c095b7a --- /dev/null +++ b/patches/platform_frameworks_base/0002-backlight-Fix-backlight-control-on-Galaxy-S9.patch @@ -0,0 +1,8112 @@ +From ed832370c42587b4d880ff3991492b67e1d97452 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Sat, 24 Mar 2018 08:01:48 +0100 +Subject: [PATCH] backlight: Fix backlight control on Galaxy S9(+) + +Change-Id: I1fbbb47939c377597ef8ad6b88b2acea5f4acaa6 + +:backlight: S9 brightness override only for screen + +Change-Id: Ie16a46336fa64850014b962429f7a20ff569222f + +:backlight: [WIP] Fix OP6 brightness + +Change-Id: If08959ece6cac1f27e1f1a0bd966ee8e1813241d + +:backlight: Try to make brightness more generic using property set by rw-system + +Change-Id: I0f20ca4b1f0fa1fcfd19833aa291fbdf16d6eedd + +:backlight: Add Qualcomm starlte + +Change-Id: I12a445344deb8b2e59a2f6ce6b24c1ffe5675092 + +:backlight: Switch samsung light fingerprint match to regexp, to include Note9 + +Change-Id: I2995f7bab615aec125927a5a027ad8f9ae43405f + +Add a property toggle to enable high brightness range on samsung device + +Change-Id: I649a3985ef87f46a5515a63935fdae9cdcbd8ec5 + +:backlight: Add japanese S9 + +Change-Id: I5e245469f5f51fed14c6080e5be72506e10389e0 + +:backlight: Fix backlight on S10*. Add an additional property to check, so testers can try it more easily + +Change-Id: Ia224e641cad8561201b4dee3d896362bee80c903 + +:backlight: Make samsung light HAL more overridable + +Change-Id: Ie04f394f8a614da8070f330bcadbcbe12895bed0 + +Use new backlight control API only for backlight, not for other lights + +Change-Id: I35c35fabff8b275f35671dcb8578b96dcad526f1 + +:backlight: fixup + +Change-Id: I4e85178327d2bb63d5d0a37786058843662a89ba +--- + core/java/android/view/KeyEvent.java.orig | 3170 +++++++++++ + .../server/StorageManagerService.java.orig | 4790 +++++++++++++++++ + .../android/server/lights/LightsService.java | 49 +- + 3 files changed, 8008 insertions(+), 1 deletion(-) + create mode 100644 core/java/android/view/KeyEvent.java.orig + create mode 100644 services/core/java/com/android/server/StorageManagerService.java.orig + +diff --git a/core/java/android/view/KeyEvent.java.orig b/core/java/android/view/KeyEvent.java.orig +new file mode 100644 +index 00000000000..144dd8b3c36 +--- /dev/null ++++ b/core/java/android/view/KeyEvent.java.orig +@@ -0,0 +1,3170 @@ ++/* ++ * Copyright (C) 2006 The Android Open Source Project ++ * ++ * Licensed under the Apache License, Version 2.0 (the "License"); ++ * you may not use this file except in compliance with the License. ++ * You may obtain a copy of the License at ++ * ++ * http://www.apache.org/licenses/LICENSE-2.0 ++ * ++ * Unless required by applicable law or agreed to in writing, software ++ * distributed under the License is distributed on an "AS IS" BASIS, ++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ * See the License for the specific language governing permissions and ++ * limitations under the License. ++ */ ++ ++package android.view; ++ ++import static android.view.Display.INVALID_DISPLAY; ++ ++import android.annotation.NonNull; ++import android.annotation.Nullable; ++import android.annotation.TestApi; ++import android.compat.annotation.UnsupportedAppUsage; ++import android.os.Build; ++import android.os.Parcel; ++import android.os.Parcelable; ++import android.text.method.MetaKeyKeyListener; ++import android.util.Log; ++import android.util.SparseIntArray; ++import android.view.KeyCharacterMap.KeyData; ++ ++/** ++ * Object used to report key and button events. ++ *

++ * Each key press is described by a sequence of key events. A key press ++ * starts with a key event with {@link #ACTION_DOWN}. If the key is held ++ * sufficiently long that it repeats, then the initial down is followed ++ * additional key events with {@link #ACTION_DOWN} and a non-zero value for ++ * {@link #getRepeatCount()}. The last key event is a {@link #ACTION_UP} ++ * for the key up. If the key press is canceled, the key up event will have the ++ * {@link #FLAG_CANCELED} flag set. ++ *

++ * Key events are generally accompanied by a key code ({@link #getKeyCode()}), ++ * scan code ({@link #getScanCode()}) and meta state ({@link #getMetaState()}). ++ * Key code constants are defined in this class. Scan code constants are raw ++ * device-specific codes obtained from the OS and so are not generally meaningful ++ * to applications unless interpreted using the {@link KeyCharacterMap}. ++ * Meta states describe the pressed state of key modifiers ++ * such as {@link #META_SHIFT_ON} or {@link #META_ALT_ON}. ++ *

++ * Key codes typically correspond one-to-one with individual keys on an input device. ++ * Many keys and key combinations serve quite different functions on different ++ * input devices so care must be taken when interpreting them. Always use the ++ * {@link KeyCharacterMap} associated with the input device when mapping keys ++ * to characters. Be aware that there may be multiple key input devices active ++ * at the same time and each will have its own key character map. ++ *

++ * As soft input methods can use multiple and inventive ways of inputting text, ++ * there is no guarantee that any key press on a soft keyboard will generate a key ++ * event: this is left to the IME's discretion, and in fact sending such events is ++ * discouraged. You should never rely on receiving KeyEvents for any key on a soft ++ * input method. In particular, the default software keyboard will never send any ++ * key event to any application targetting Jelly Bean or later, and will only send ++ * events for some presses of the delete and return keys to applications targetting ++ * Ice Cream Sandwich or earlier. Be aware that other software input methods may ++ * never send key events regardless of the version. Consider using editor actions ++ * like {@link android.view.inputmethod.EditorInfo#IME_ACTION_DONE} if you need ++ * specific interaction with the software keyboard, as it gives more visibility to ++ * the user as to how your application will react to key presses. ++ *

++ * When interacting with an IME, the framework may deliver key events ++ * with the special action {@link #ACTION_MULTIPLE} that either specifies ++ * that single repeated key code or a sequence of characters to insert. ++ *

++ * In general, the framework cannot guarantee that the key events it delivers ++ * to a view always constitute complete key sequences since some events may be dropped ++ * or modified by containing views before they are delivered. The view implementation ++ * should be prepared to handle {@link #FLAG_CANCELED} and should tolerate anomalous ++ * situations such as receiving a new {@link #ACTION_DOWN} without first having ++ * received an {@link #ACTION_UP} for the prior key press. ++ *

++ * Refer to {@link InputDevice} for more information about how different kinds of ++ * input devices and sources represent keys and buttons. ++ *

++ */ ++public class KeyEvent extends InputEvent implements Parcelable { ++ /** Key code constant: Unknown key code. */ ++ public static final int KEYCODE_UNKNOWN = 0; ++ /** Key code constant: Soft Left key. ++ * Usually situated below the display on phones and used as a multi-function ++ * feature key for selecting a software defined function shown on the bottom left ++ * of the display. */ ++ public static final int KEYCODE_SOFT_LEFT = 1; ++ /** Key code constant: Soft Right key. ++ * Usually situated below the display on phones and used as a multi-function ++ * feature key for selecting a software defined function shown on the bottom right ++ * of the display. */ ++ public static final int KEYCODE_SOFT_RIGHT = 2; ++ /** Key code constant: Home key. ++ * This key is handled by the framework and is never delivered to applications. */ ++ public static final int KEYCODE_HOME = 3; ++ /** Key code constant: Back key. */ ++ public static final int KEYCODE_BACK = 4; ++ /** Key code constant: Call key. */ ++ public static final int KEYCODE_CALL = 5; ++ /** Key code constant: End Call key. */ ++ public static final int KEYCODE_ENDCALL = 6; ++ /** Key code constant: '0' key. */ ++ public static final int KEYCODE_0 = 7; ++ /** Key code constant: '1' key. */ ++ public static final int KEYCODE_1 = 8; ++ /** Key code constant: '2' key. */ ++ public static final int KEYCODE_2 = 9; ++ /** Key code constant: '3' key. */ ++ public static final int KEYCODE_3 = 10; ++ /** Key code constant: '4' key. */ ++ public static final int KEYCODE_4 = 11; ++ /** Key code constant: '5' key. */ ++ public static final int KEYCODE_5 = 12; ++ /** Key code constant: '6' key. */ ++ public static final int KEYCODE_6 = 13; ++ /** Key code constant: '7' key. */ ++ public static final int KEYCODE_7 = 14; ++ /** Key code constant: '8' key. */ ++ public static final int KEYCODE_8 = 15; ++ /** Key code constant: '9' key. */ ++ public static final int KEYCODE_9 = 16; ++ /** Key code constant: '*' key. */ ++ public static final int KEYCODE_STAR = 17; ++ /** Key code constant: '#' key. */ ++ public static final int KEYCODE_POUND = 18; ++ /** Key code constant: Directional Pad Up key. ++ * May also be synthesized from trackball motions. */ ++ public static final int KEYCODE_DPAD_UP = 19; ++ /** Key code constant: Directional Pad Down key. ++ * May also be synthesized from trackball motions. */ ++ public static final int KEYCODE_DPAD_DOWN = 20; ++ /** Key code constant: Directional Pad Left key. ++ * May also be synthesized from trackball motions. */ ++ public static final int KEYCODE_DPAD_LEFT = 21; ++ /** Key code constant: Directional Pad Right key. ++ * May also be synthesized from trackball motions. */ ++ public static final int KEYCODE_DPAD_RIGHT = 22; ++ /** Key code constant: Directional Pad Center key. ++ * May also be synthesized from trackball motions. */ ++ public static final int KEYCODE_DPAD_CENTER = 23; ++ /** Key code constant: Volume Up key. ++ * Adjusts the speaker volume up. */ ++ public static final int KEYCODE_VOLUME_UP = 24; ++ /** Key code constant: Volume Down key. ++ * Adjusts the speaker volume down. */ ++ public static final int KEYCODE_VOLUME_DOWN = 25; ++ /** Key code constant: Power key. */ ++ public static final int KEYCODE_POWER = 26; ++ /** Key code constant: Camera key. ++ * Used to launch a camera application or take pictures. */ ++ public static final int KEYCODE_CAMERA = 27; ++ /** Key code constant: Clear key. */ ++ public static final int KEYCODE_CLEAR = 28; ++ /** Key code constant: 'A' key. */ ++ public static final int KEYCODE_A = 29; ++ /** Key code constant: 'B' key. */ ++ public static final int KEYCODE_B = 30; ++ /** Key code constant: 'C' key. */ ++ public static final int KEYCODE_C = 31; ++ /** Key code constant: 'D' key. */ ++ public static final int KEYCODE_D = 32; ++ /** Key code constant: 'E' key. */ ++ public static final int KEYCODE_E = 33; ++ /** Key code constant: 'F' key. */ ++ public static final int KEYCODE_F = 34; ++ /** Key code constant: 'G' key. */ ++ public static final int KEYCODE_G = 35; ++ /** Key code constant: 'H' key. */ ++ public static final int KEYCODE_H = 36; ++ /** Key code constant: 'I' key. */ ++ public static final int KEYCODE_I = 37; ++ /** Key code constant: 'J' key. */ ++ public static final int KEYCODE_J = 38; ++ /** Key code constant: 'K' key. */ ++ public static final int KEYCODE_K = 39; ++ /** Key code constant: 'L' key. */ ++ public static final int KEYCODE_L = 40; ++ /** Key code constant: 'M' key. */ ++ public static final int KEYCODE_M = 41; ++ /** Key code constant: 'N' key. */ ++ public static final int KEYCODE_N = 42; ++ /** Key code constant: 'O' key. */ ++ public static final int KEYCODE_O = 43; ++ /** Key code constant: 'P' key. */ ++ public static final int KEYCODE_P = 44; ++ /** Key code constant: 'Q' key. */ ++ public static final int KEYCODE_Q = 45; ++ /** Key code constant: 'R' key. */ ++ public static final int KEYCODE_R = 46; ++ /** Key code constant: 'S' key. */ ++ public static final int KEYCODE_S = 47; ++ /** Key code constant: 'T' key. */ ++ public static final int KEYCODE_T = 48; ++ /** Key code constant: 'U' key. */ ++ public static final int KEYCODE_U = 49; ++ /** Key code constant: 'V' key. */ ++ public static final int KEYCODE_V = 50; ++ /** Key code constant: 'W' key. */ ++ public static final int KEYCODE_W = 51; ++ /** Key code constant: 'X' key. */ ++ public static final int KEYCODE_X = 52; ++ /** Key code constant: 'Y' key. */ ++ public static final int KEYCODE_Y = 53; ++ /** Key code constant: 'Z' key. */ ++ public static final int KEYCODE_Z = 54; ++ /** Key code constant: ',' key. */ ++ public static final int KEYCODE_COMMA = 55; ++ /** Key code constant: '.' key. */ ++ public static final int KEYCODE_PERIOD = 56; ++ /** Key code constant: Left Alt modifier key. */ ++ public static final int KEYCODE_ALT_LEFT = 57; ++ /** Key code constant: Right Alt modifier key. */ ++ public static final int KEYCODE_ALT_RIGHT = 58; ++ /** Key code constant: Left Shift modifier key. */ ++ public static final int KEYCODE_SHIFT_LEFT = 59; ++ /** Key code constant: Right Shift modifier key. */ ++ public static final int KEYCODE_SHIFT_RIGHT = 60; ++ /** Key code constant: Tab key. */ ++ public static final int KEYCODE_TAB = 61; ++ /** Key code constant: Space key. */ ++ public static final int KEYCODE_SPACE = 62; ++ /** Key code constant: Symbol modifier key. ++ * Used to enter alternate symbols. */ ++ public static final int KEYCODE_SYM = 63; ++ /** Key code constant: Explorer special function key. ++ * Used to launch a browser application. */ ++ public static final int KEYCODE_EXPLORER = 64; ++ /** Key code constant: Envelope special function key. ++ * Used to launch a mail application. */ ++ public static final int KEYCODE_ENVELOPE = 65; ++ /** Key code constant: Enter key. */ ++ public static final int KEYCODE_ENTER = 66; ++ /** Key code constant: Backspace key. ++ * Deletes characters before the insertion point, unlike {@link #KEYCODE_FORWARD_DEL}. */ ++ public static final int KEYCODE_DEL = 67; ++ /** Key code constant: '`' (backtick) key. */ ++ public static final int KEYCODE_GRAVE = 68; ++ /** Key code constant: '-'. */ ++ public static final int KEYCODE_MINUS = 69; ++ /** Key code constant: '=' key. */ ++ public static final int KEYCODE_EQUALS = 70; ++ /** Key code constant: '[' key. */ ++ public static final int KEYCODE_LEFT_BRACKET = 71; ++ /** Key code constant: ']' key. */ ++ public static final int KEYCODE_RIGHT_BRACKET = 72; ++ /** Key code constant: '\' key. */ ++ public static final int KEYCODE_BACKSLASH = 73; ++ /** Key code constant: ';' key. */ ++ public static final int KEYCODE_SEMICOLON = 74; ++ /** Key code constant: ''' (apostrophe) key. */ ++ public static final int KEYCODE_APOSTROPHE = 75; ++ /** Key code constant: '/' key. */ ++ public static final int KEYCODE_SLASH = 76; ++ /** Key code constant: '@' key. */ ++ public static final int KEYCODE_AT = 77; ++ /** Key code constant: Number modifier key. ++ * Used to enter numeric symbols. ++ * This key is not Num Lock; it is more like {@link #KEYCODE_ALT_LEFT} and is ++ * interpreted as an ALT key by {@link android.text.method.MetaKeyKeyListener}. */ ++ public static final int KEYCODE_NUM = 78; ++ /** Key code constant: Headset Hook key. ++ * Used to hang up calls and stop media. */ ++ public static final int KEYCODE_HEADSETHOOK = 79; ++ /** Key code constant: Camera Focus key. ++ * Used to focus the camera. */ ++ public static final int KEYCODE_FOCUS = 80; // *Camera* focus ++ /** Key code constant: '+' key. */ ++ public static final int KEYCODE_PLUS = 81; ++ /** Key code constant: Menu key. */ ++ public static final int KEYCODE_MENU = 82; ++ /** Key code constant: Notification key. */ ++ public static final int KEYCODE_NOTIFICATION = 83; ++ /** Key code constant: Search key. */ ++ public static final int KEYCODE_SEARCH = 84; ++ /** Key code constant: Play/Pause media key. */ ++ public static final int KEYCODE_MEDIA_PLAY_PAUSE= 85; ++ /** Key code constant: Stop media key. */ ++ public static final int KEYCODE_MEDIA_STOP = 86; ++ /** Key code constant: Play Next media key. */ ++ public static final int KEYCODE_MEDIA_NEXT = 87; ++ /** Key code constant: Play Previous media key. */ ++ public static final int KEYCODE_MEDIA_PREVIOUS = 88; ++ /** Key code constant: Rewind media key. */ ++ public static final int KEYCODE_MEDIA_REWIND = 89; ++ /** Key code constant: Fast Forward media key. */ ++ public static final int KEYCODE_MEDIA_FAST_FORWARD = 90; ++ /** Key code constant: Mute key. ++ * Mutes the microphone, unlike {@link #KEYCODE_VOLUME_MUTE}. */ ++ public static final int KEYCODE_MUTE = 91; ++ /** Key code constant: Page Up key. */ ++ public static final int KEYCODE_PAGE_UP = 92; ++ /** Key code constant: Page Down key. */ ++ public static final int KEYCODE_PAGE_DOWN = 93; ++ /** Key code constant: Picture Symbols modifier key. ++ * Used to switch symbol sets (Emoji, Kao-moji). */ ++ public static final int KEYCODE_PICTSYMBOLS = 94; // switch symbol-sets (Emoji,Kao-moji) ++ /** Key code constant: Switch Charset modifier key. ++ * Used to switch character sets (Kanji, Katakana). */ ++ public static final int KEYCODE_SWITCH_CHARSET = 95; // switch char-sets (Kanji,Katakana) ++ /** Key code constant: A Button key. ++ * On a game controller, the A button should be either the button labeled A ++ * or the first button on the bottom row of controller buttons. */ ++ public static final int KEYCODE_BUTTON_A = 96; ++ /** Key code constant: B Button key. ++ * On a game controller, the B button should be either the button labeled B ++ * or the second button on the bottom row of controller buttons. */ ++ public static final int KEYCODE_BUTTON_B = 97; ++ /** Key code constant: C Button key. ++ * On a game controller, the C button should be either the button labeled C ++ * or the third button on the bottom row of controller buttons. */ ++ public static final int KEYCODE_BUTTON_C = 98; ++ /** Key code constant: X Button key. ++ * On a game controller, the X button should be either the button labeled X ++ * or the first button on the upper row of controller buttons. */ ++ public static final int KEYCODE_BUTTON_X = 99; ++ /** Key code constant: Y Button key. ++ * On a game controller, the Y button should be either the button labeled Y ++ * or the second button on the upper row of controller buttons. */ ++ public static final int KEYCODE_BUTTON_Y = 100; ++ /** Key code constant: Z Button key. ++ * On a game controller, the Z button should be either the button labeled Z ++ * or the third button on the upper row of controller buttons. */ ++ public static final int KEYCODE_BUTTON_Z = 101; ++ /** Key code constant: L1 Button key. ++ * On a game controller, the L1 button should be either the button labeled L1 (or L) ++ * or the top left trigger button. */ ++ public static final int KEYCODE_BUTTON_L1 = 102; ++ /** Key code constant: R1 Button key. ++ * On a game controller, the R1 button should be either the button labeled R1 (or R) ++ * or the top right trigger button. */ ++ public static final int KEYCODE_BUTTON_R1 = 103; ++ /** Key code constant: L2 Button key. ++ * On a game controller, the L2 button should be either the button labeled L2 ++ * or the bottom left trigger button. */ ++ public static final int KEYCODE_BUTTON_L2 = 104; ++ /** Key code constant: R2 Button key. ++ * On a game controller, the R2 button should be either the button labeled R2 ++ * or the bottom right trigger button. */ ++ public static final int KEYCODE_BUTTON_R2 = 105; ++ /** Key code constant: Left Thumb Button key. ++ * On a game controller, the left thumb button indicates that the left (or only) ++ * joystick is pressed. */ ++ public static final int KEYCODE_BUTTON_THUMBL = 106; ++ /** Key code constant: Right Thumb Button key. ++ * On a game controller, the right thumb button indicates that the right ++ * joystick is pressed. */ ++ public static final int KEYCODE_BUTTON_THUMBR = 107; ++ /** Key code constant: Start Button key. ++ * On a game controller, the button labeled Start. */ ++ public static final int KEYCODE_BUTTON_START = 108; ++ /** Key code constant: Select Button key. ++ * On a game controller, the button labeled Select. */ ++ public static final int KEYCODE_BUTTON_SELECT = 109; ++ /** Key code constant: Mode Button key. ++ * On a game controller, the button labeled Mode. */ ++ public static final int KEYCODE_BUTTON_MODE = 110; ++ /** Key code constant: Escape key. */ ++ public static final int KEYCODE_ESCAPE = 111; ++ /** Key code constant: Forward Delete key. ++ * Deletes characters ahead of the insertion point, unlike {@link #KEYCODE_DEL}. */ ++ public static final int KEYCODE_FORWARD_DEL = 112; ++ /** Key code constant: Left Control modifier key. */ ++ public static final int KEYCODE_CTRL_LEFT = 113; ++ /** Key code constant: Right Control modifier key. */ ++ public static final int KEYCODE_CTRL_RIGHT = 114; ++ /** Key code constant: Caps Lock key. */ ++ public static final int KEYCODE_CAPS_LOCK = 115; ++ /** Key code constant: Scroll Lock key. */ ++ public static final int KEYCODE_SCROLL_LOCK = 116; ++ /** Key code constant: Left Meta modifier key. */ ++ public static final int KEYCODE_META_LEFT = 117; ++ /** Key code constant: Right Meta modifier key. */ ++ public static final int KEYCODE_META_RIGHT = 118; ++ /** Key code constant: Function modifier key. */ ++ public static final int KEYCODE_FUNCTION = 119; ++ /** Key code constant: System Request / Print Screen key. */ ++ public static final int KEYCODE_SYSRQ = 120; ++ /** Key code constant: Break / Pause key. */ ++ public static final int KEYCODE_BREAK = 121; ++ /** Key code constant: Home Movement key. ++ * Used for scrolling or moving the cursor around to the start of a line ++ * or to the top of a list. */ ++ public static final int KEYCODE_MOVE_HOME = 122; ++ /** Key code constant: End Movement key. ++ * Used for scrolling or moving the cursor around to the end of a line ++ * or to the bottom of a list. */ ++ public static final int KEYCODE_MOVE_END = 123; ++ /** Key code constant: Insert key. ++ * Toggles insert / overwrite edit mode. */ ++ public static final int KEYCODE_INSERT = 124; ++ /** Key code constant: Forward key. ++ * Navigates forward in the history stack. Complement of {@link #KEYCODE_BACK}. */ ++ public static final int KEYCODE_FORWARD = 125; ++ /** Key code constant: Play media key. */ ++ public static final int KEYCODE_MEDIA_PLAY = 126; ++ /** Key code constant: Pause media key. */ ++ public static final int KEYCODE_MEDIA_PAUSE = 127; ++ /** Key code constant: Close media key. ++ * May be used to close a CD tray, for example. */ ++ public static final int KEYCODE_MEDIA_CLOSE = 128; ++ /** Key code constant: Eject media key. ++ * May be used to eject a CD tray, for example. */ ++ public static final int KEYCODE_MEDIA_EJECT = 129; ++ /** Key code constant: Record media key. */ ++ public static final int KEYCODE_MEDIA_RECORD = 130; ++ /** Key code constant: F1 key. */ ++ public static final int KEYCODE_F1 = 131; ++ /** Key code constant: F2 key. */ ++ public static final int KEYCODE_F2 = 132; ++ /** Key code constant: F3 key. */ ++ public static final int KEYCODE_F3 = 133; ++ /** Key code constant: F4 key. */ ++ public static final int KEYCODE_F4 = 134; ++ /** Key code constant: F5 key. */ ++ public static final int KEYCODE_F5 = 135; ++ /** Key code constant: F6 key. */ ++ public static final int KEYCODE_F6 = 136; ++ /** Key code constant: F7 key. */ ++ public static final int KEYCODE_F7 = 137; ++ /** Key code constant: F8 key. */ ++ public static final int KEYCODE_F8 = 138; ++ /** Key code constant: F9 key. */ ++ public static final int KEYCODE_F9 = 139; ++ /** Key code constant: F10 key. */ ++ public static final int KEYCODE_F10 = 140; ++ /** Key code constant: F11 key. */ ++ public static final int KEYCODE_F11 = 141; ++ /** Key code constant: F12 key. */ ++ public static final int KEYCODE_F12 = 142; ++ /** Key code constant: Num Lock key. ++ * This is the Num Lock key; it is different from {@link #KEYCODE_NUM}. ++ * This key alters the behavior of other keys on the numeric keypad. */ ++ public static final int KEYCODE_NUM_LOCK = 143; ++ /** Key code constant: Numeric keypad '0' key. */ ++ public static final int KEYCODE_NUMPAD_0 = 144; ++ /** Key code constant: Numeric keypad '1' key. */ ++ public static final int KEYCODE_NUMPAD_1 = 145; ++ /** Key code constant: Numeric keypad '2' key. */ ++ public static final int KEYCODE_NUMPAD_2 = 146; ++ /** Key code constant: Numeric keypad '3' key. */ ++ public static final int KEYCODE_NUMPAD_3 = 147; ++ /** Key code constant: Numeric keypad '4' key. */ ++ public static final int KEYCODE_NUMPAD_4 = 148; ++ /** Key code constant: Numeric keypad '5' key. */ ++ public static final int KEYCODE_NUMPAD_5 = 149; ++ /** Key code constant: Numeric keypad '6' key. */ ++ public static final int KEYCODE_NUMPAD_6 = 150; ++ /** Key code constant: Numeric keypad '7' key. */ ++ public static final int KEYCODE_NUMPAD_7 = 151; ++ /** Key code constant: Numeric keypad '8' key. */ ++ public static final int KEYCODE_NUMPAD_8 = 152; ++ /** Key code constant: Numeric keypad '9' key. */ ++ public static final int KEYCODE_NUMPAD_9 = 153; ++ /** Key code constant: Numeric keypad '/' key (for division). */ ++ public static final int KEYCODE_NUMPAD_DIVIDE = 154; ++ /** Key code constant: Numeric keypad '*' key (for multiplication). */ ++ public static final int KEYCODE_NUMPAD_MULTIPLY = 155; ++ /** Key code constant: Numeric keypad '-' key (for subtraction). */ ++ public static final int KEYCODE_NUMPAD_SUBTRACT = 156; ++ /** Key code constant: Numeric keypad '+' key (for addition). */ ++ public static final int KEYCODE_NUMPAD_ADD = 157; ++ /** Key code constant: Numeric keypad '.' key (for decimals or digit grouping). */ ++ public static final int KEYCODE_NUMPAD_DOT = 158; ++ /** Key code constant: Numeric keypad ',' key (for decimals or digit grouping). */ ++ public static final int KEYCODE_NUMPAD_COMMA = 159; ++ /** Key code constant: Numeric keypad Enter key. */ ++ public static final int KEYCODE_NUMPAD_ENTER = 160; ++ /** Key code constant: Numeric keypad '=' key. */ ++ public static final int KEYCODE_NUMPAD_EQUALS = 161; ++ /** Key code constant: Numeric keypad '(' key. */ ++ public static final int KEYCODE_NUMPAD_LEFT_PAREN = 162; ++ /** Key code constant: Numeric keypad ')' key. */ ++ public static final int KEYCODE_NUMPAD_RIGHT_PAREN = 163; ++ /** Key code constant: Volume Mute key. ++ * Mutes the speaker, unlike {@link #KEYCODE_MUTE}. ++ * This key should normally be implemented as a toggle such that the first press ++ * mutes the speaker and the second press restores the original volume. */ ++ public static final int KEYCODE_VOLUME_MUTE = 164; ++ /** Key code constant: Info key. ++ * Common on TV remotes to show additional information related to what is ++ * currently being viewed. */ ++ public static final int KEYCODE_INFO = 165; ++ /** Key code constant: Channel up key. ++ * On TV remotes, increments the television channel. */ ++ public static final int KEYCODE_CHANNEL_UP = 166; ++ /** Key code constant: Channel down key. ++ * On TV remotes, decrements the television channel. */ ++ public static final int KEYCODE_CHANNEL_DOWN = 167; ++ /** Key code constant: Zoom in key. */ ++ public static final int KEYCODE_ZOOM_IN = 168; ++ /** Key code constant: Zoom out key. */ ++ public static final int KEYCODE_ZOOM_OUT = 169; ++ /** Key code constant: TV key. ++ * On TV remotes, switches to viewing live TV. */ ++ public static final int KEYCODE_TV = 170; ++ /** Key code constant: Window key. ++ * On TV remotes, toggles picture-in-picture mode or other windowing functions. ++ * On Android Wear devices, triggers a display offset. */ ++ public static final int KEYCODE_WINDOW = 171; ++ /** Key code constant: Guide key. ++ * On TV remotes, shows a programming guide. */ ++ public static final int KEYCODE_GUIDE = 172; ++ /** Key code constant: DVR key. ++ * On some TV remotes, switches to a DVR mode for recorded shows. */ ++ public static final int KEYCODE_DVR = 173; ++ /** Key code constant: Bookmark key. ++ * On some TV remotes, bookmarks content or web pages. */ ++ public static final int KEYCODE_BOOKMARK = 174; ++ /** Key code constant: Toggle captions key. ++ * Switches the mode for closed-captioning text, for example during television shows. */ ++ public static final int KEYCODE_CAPTIONS = 175; ++ /** Key code constant: Settings key. ++ * Starts the system settings activity. */ ++ public static final int KEYCODE_SETTINGS = 176; ++ /** Key code constant: TV power key. ++ * On TV remotes, toggles the power on a television screen. */ ++ public static final int KEYCODE_TV_POWER = 177; ++ /** Key code constant: TV input key. ++ * On TV remotes, switches the input on a television screen. */ ++ public static final int KEYCODE_TV_INPUT = 178; ++ /** Key code constant: Set-top-box power key. ++ * On TV remotes, toggles the power on an external Set-top-box. */ ++ public static final int KEYCODE_STB_POWER = 179; ++ /** Key code constant: Set-top-box input key. ++ * On TV remotes, switches the input mode on an external Set-top-box. */ ++ public static final int KEYCODE_STB_INPUT = 180; ++ /** Key code constant: A/V Receiver power key. ++ * On TV remotes, toggles the power on an external A/V Receiver. */ ++ public static final int KEYCODE_AVR_POWER = 181; ++ /** Key code constant: A/V Receiver input key. ++ * On TV remotes, switches the input mode on an external A/V Receiver. */ ++ public static final int KEYCODE_AVR_INPUT = 182; ++ /** Key code constant: Red "programmable" key. ++ * On TV remotes, acts as a contextual/programmable key. */ ++ public static final int KEYCODE_PROG_RED = 183; ++ /** Key code constant: Green "programmable" key. ++ * On TV remotes, actsas a contextual/programmable key. */ ++ public static final int KEYCODE_PROG_GREEN = 184; ++ /** Key code constant: Yellow "programmable" key. ++ * On TV remotes, acts as a contextual/programmable key. */ ++ public static final int KEYCODE_PROG_YELLOW = 185; ++ /** Key code constant: Blue "programmable" key. ++ * On TV remotes, acts as a contextual/programmable key. */ ++ public static final int KEYCODE_PROG_BLUE = 186; ++ /** Key code constant: App switch key. ++ * Should bring up the application switcher dialog. */ ++ public static final int KEYCODE_APP_SWITCH = 187; ++ /** Key code constant: Generic Game Pad Button #1.*/ ++ public static final int KEYCODE_BUTTON_1 = 188; ++ /** Key code constant: Generic Game Pad Button #2.*/ ++ public static final int KEYCODE_BUTTON_2 = 189; ++ /** Key code constant: Generic Game Pad Button #3.*/ ++ public static final int KEYCODE_BUTTON_3 = 190; ++ /** Key code constant: Generic Game Pad Button #4.*/ ++ public static final int KEYCODE_BUTTON_4 = 191; ++ /** Key code constant: Generic Game Pad Button #5.*/ ++ public static final int KEYCODE_BUTTON_5 = 192; ++ /** Key code constant: Generic Game Pad Button #6.*/ ++ public static final int KEYCODE_BUTTON_6 = 193; ++ /** Key code constant: Generic Game Pad Button #7.*/ ++ public static final int KEYCODE_BUTTON_7 = 194; ++ /** Key code constant: Generic Game Pad Button #8.*/ ++ public static final int KEYCODE_BUTTON_8 = 195; ++ /** Key code constant: Generic Game Pad Button #9.*/ ++ public static final int KEYCODE_BUTTON_9 = 196; ++ /** Key code constant: Generic Game Pad Button #10.*/ ++ public static final int KEYCODE_BUTTON_10 = 197; ++ /** Key code constant: Generic Game Pad Button #11.*/ ++ public static final int KEYCODE_BUTTON_11 = 198; ++ /** Key code constant: Generic Game Pad Button #12.*/ ++ public static final int KEYCODE_BUTTON_12 = 199; ++ /** Key code constant: Generic Game Pad Button #13.*/ ++ public static final int KEYCODE_BUTTON_13 = 200; ++ /** Key code constant: Generic Game Pad Button #14.*/ ++ public static final int KEYCODE_BUTTON_14 = 201; ++ /** Key code constant: Generic Game Pad Button #15.*/ ++ public static final int KEYCODE_BUTTON_15 = 202; ++ /** Key code constant: Generic Game Pad Button #16.*/ ++ public static final int KEYCODE_BUTTON_16 = 203; ++ /** Key code constant: Language Switch key. ++ * Toggles the current input language such as switching between English and Japanese on ++ * a QWERTY keyboard. On some devices, the same function may be performed by ++ * pressing Shift+Spacebar. */ ++ public static final int KEYCODE_LANGUAGE_SWITCH = 204; ++ /** Key code constant: Manner Mode key. ++ * Toggles silent or vibrate mode on and off to make the device behave more politely ++ * in certain settings such as on a crowded train. On some devices, the key may only ++ * operate when long-pressed. */ ++ public static final int KEYCODE_MANNER_MODE = 205; ++ /** Key code constant: 3D Mode key. ++ * Toggles the display between 2D and 3D mode. */ ++ public static final int KEYCODE_3D_MODE = 206; ++ /** Key code constant: Contacts special function key. ++ * Used to launch an address book application. */ ++ public static final int KEYCODE_CONTACTS = 207; ++ /** Key code constant: Calendar special function key. ++ * Used to launch a calendar application. */ ++ public static final int KEYCODE_CALENDAR = 208; ++ /** Key code constant: Music special function key. ++ * Used to launch a music player application. */ ++ public static final int KEYCODE_MUSIC = 209; ++ /** Key code constant: Calculator special function key. ++ * Used to launch a calculator application. */ ++ public static final int KEYCODE_CALCULATOR = 210; ++ /** Key code constant: Japanese full-width / half-width key. */ ++ public static final int KEYCODE_ZENKAKU_HANKAKU = 211; ++ /** Key code constant: Japanese alphanumeric key. */ ++ public static final int KEYCODE_EISU = 212; ++ /** Key code constant: Japanese non-conversion key. */ ++ public static final int KEYCODE_MUHENKAN = 213; ++ /** Key code constant: Japanese conversion key. */ ++ public static final int KEYCODE_HENKAN = 214; ++ /** Key code constant: Japanese katakana / hiragana key. */ ++ public static final int KEYCODE_KATAKANA_HIRAGANA = 215; ++ /** Key code constant: Japanese Yen key. */ ++ public static final int KEYCODE_YEN = 216; ++ /** Key code constant: Japanese Ro key. */ ++ public static final int KEYCODE_RO = 217; ++ /** Key code constant: Japanese kana key. */ ++ public static final int KEYCODE_KANA = 218; ++ /** Key code constant: Assist key. ++ * Launches the global assist activity. Not delivered to applications. */ ++ public static final int KEYCODE_ASSIST = 219; ++ /** Key code constant: Brightness Down key. ++ * Adjusts the screen brightness down. */ ++ public static final int KEYCODE_BRIGHTNESS_DOWN = 220; ++ /** Key code constant: Brightness Up key. ++ * Adjusts the screen brightness up. */ ++ public static final int KEYCODE_BRIGHTNESS_UP = 221; ++ /** Key code constant: Audio Track key. ++ * Switches the audio tracks. */ ++ public static final int KEYCODE_MEDIA_AUDIO_TRACK = 222; ++ /** Key code constant: Sleep key. ++ * Puts the device to sleep. Behaves somewhat like {@link #KEYCODE_POWER} but it ++ * has no effect if the device is already asleep. */ ++ public static final int KEYCODE_SLEEP = 223; ++ /** Key code constant: Wakeup key. ++ * Wakes up the device. Behaves somewhat like {@link #KEYCODE_POWER} but it ++ * has no effect if the device is already awake. */ ++ public static final int KEYCODE_WAKEUP = 224; ++ /** Key code constant: Pairing key. ++ * Initiates peripheral pairing mode. Useful for pairing remote control ++ * devices or game controllers, especially if no other input mode is ++ * available. */ ++ public static final int KEYCODE_PAIRING = 225; ++ /** Key code constant: Media Top Menu key. ++ * Goes to the top of media menu. */ ++ public static final int KEYCODE_MEDIA_TOP_MENU = 226; ++ /** Key code constant: '11' key. */ ++ public static final int KEYCODE_11 = 227; ++ /** Key code constant: '12' key. */ ++ public static final int KEYCODE_12 = 228; ++ /** Key code constant: Last Channel key. ++ * Goes to the last viewed channel. */ ++ public static final int KEYCODE_LAST_CHANNEL = 229; ++ /** Key code constant: TV data service key. ++ * Displays data services like weather, sports. */ ++ public static final int KEYCODE_TV_DATA_SERVICE = 230; ++ /** Key code constant: Voice Assist key. ++ * Launches the global voice assist activity. Not delivered to applications. */ ++ public static final int KEYCODE_VOICE_ASSIST = 231; ++ /** Key code constant: Radio key. ++ * Toggles TV service / Radio service. */ ++ public static final int KEYCODE_TV_RADIO_SERVICE = 232; ++ /** Key code constant: Teletext key. ++ * Displays Teletext service. */ ++ public static final int KEYCODE_TV_TELETEXT = 233; ++ /** Key code constant: Number entry key. ++ * Initiates to enter multi-digit channel nubmber when each digit key is assigned ++ * for selecting separate channel. Corresponds to Number Entry Mode (0x1D) of CEC ++ * User Control Code. */ ++ public static final int KEYCODE_TV_NUMBER_ENTRY = 234; ++ /** Key code constant: Analog Terrestrial key. ++ * Switches to analog terrestrial broadcast service. */ ++ public static final int KEYCODE_TV_TERRESTRIAL_ANALOG = 235; ++ /** Key code constant: Digital Terrestrial key. ++ * Switches to digital terrestrial broadcast service. */ ++ public static final int KEYCODE_TV_TERRESTRIAL_DIGITAL = 236; ++ /** Key code constant: Satellite key. ++ * Switches to digital satellite broadcast service. */ ++ public static final int KEYCODE_TV_SATELLITE = 237; ++ /** Key code constant: BS key. ++ * Switches to BS digital satellite broadcasting service available in Japan. */ ++ public static final int KEYCODE_TV_SATELLITE_BS = 238; ++ /** Key code constant: CS key. ++ * Switches to CS digital satellite broadcasting service available in Japan. */ ++ public static final int KEYCODE_TV_SATELLITE_CS = 239; ++ /** Key code constant: BS/CS key. ++ * Toggles between BS and CS digital satellite services. */ ++ public static final int KEYCODE_TV_SATELLITE_SERVICE = 240; ++ /** Key code constant: Toggle Network key. ++ * Toggles selecting broacast services. */ ++ public static final int KEYCODE_TV_NETWORK = 241; ++ /** Key code constant: Antenna/Cable key. ++ * Toggles broadcast input source between antenna and cable. */ ++ public static final int KEYCODE_TV_ANTENNA_CABLE = 242; ++ /** Key code constant: HDMI #1 key. ++ * Switches to HDMI input #1. */ ++ public static final int KEYCODE_TV_INPUT_HDMI_1 = 243; ++ /** Key code constant: HDMI #2 key. ++ * Switches to HDMI input #2. */ ++ public static final int KEYCODE_TV_INPUT_HDMI_2 = 244; ++ /** Key code constant: HDMI #3 key. ++ * Switches to HDMI input #3. */ ++ public static final int KEYCODE_TV_INPUT_HDMI_3 = 245; ++ /** Key code constant: HDMI #4 key. ++ * Switches to HDMI input #4. */ ++ public static final int KEYCODE_TV_INPUT_HDMI_4 = 246; ++ /** Key code constant: Composite #1 key. ++ * Switches to composite video input #1. */ ++ public static final int KEYCODE_TV_INPUT_COMPOSITE_1 = 247; ++ /** Key code constant: Composite #2 key. ++ * Switches to composite video input #2. */ ++ public static final int KEYCODE_TV_INPUT_COMPOSITE_2 = 248; ++ /** Key code constant: Component #1 key. ++ * Switches to component video input #1. */ ++ public static final int KEYCODE_TV_INPUT_COMPONENT_1 = 249; ++ /** Key code constant: Component #2 key. ++ * Switches to component video input #2. */ ++ public static final int KEYCODE_TV_INPUT_COMPONENT_2 = 250; ++ /** Key code constant: VGA #1 key. ++ * Switches to VGA (analog RGB) input #1. */ ++ public static final int KEYCODE_TV_INPUT_VGA_1 = 251; ++ /** Key code constant: Audio description key. ++ * Toggles audio description off / on. */ ++ public static final int KEYCODE_TV_AUDIO_DESCRIPTION = 252; ++ /** Key code constant: Audio description mixing volume up key. ++ * Louden audio description volume as compared with normal audio volume. */ ++ public static final int KEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP = 253; ++ /** Key code constant: Audio description mixing volume down key. ++ * Lessen audio description volume as compared with normal audio volume. */ ++ public static final int KEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN = 254; ++ /** Key code constant: Zoom mode key. ++ * Changes Zoom mode (Normal, Full, Zoom, Wide-zoom, etc.) */ ++ public static final int KEYCODE_TV_ZOOM_MODE = 255; ++ /** Key code constant: Contents menu key. ++ * Goes to the title list. Corresponds to Contents Menu (0x0B) of CEC User Control ++ * Code */ ++ public static final int KEYCODE_TV_CONTENTS_MENU = 256; ++ /** Key code constant: Media context menu key. ++ * Goes to the context menu of media contents. Corresponds to Media Context-sensitive ++ * Menu (0x11) of CEC User Control Code. */ ++ public static final int KEYCODE_TV_MEDIA_CONTEXT_MENU = 257; ++ /** Key code constant: Timer programming key. ++ * Goes to the timer recording menu. Corresponds to Timer Programming (0x54) of ++ * CEC User Control Code. */ ++ public static final int KEYCODE_TV_TIMER_PROGRAMMING = 258; ++ /** Key code constant: Help key. */ ++ public static final int KEYCODE_HELP = 259; ++ /** Key code constant: Navigate to previous key. ++ * Goes backward by one item in an ordered collection of items. */ ++ public static final int KEYCODE_NAVIGATE_PREVIOUS = 260; ++ /** Key code constant: Navigate to next key. ++ * Advances to the next item in an ordered collection of items. */ ++ public static final int KEYCODE_NAVIGATE_NEXT = 261; ++ /** Key code constant: Navigate in key. ++ * Activates the item that currently has focus or expands to the next level of a navigation ++ * hierarchy. */ ++ public static final int KEYCODE_NAVIGATE_IN = 262; ++ /** Key code constant: Navigate out key. ++ * Backs out one level of a navigation hierarchy or collapses the item that currently has ++ * focus. */ ++ public static final int KEYCODE_NAVIGATE_OUT = 263; ++ /** Key code constant: Primary stem key for Wear ++ * Main power/reset button on watch. */ ++ public static final int KEYCODE_STEM_PRIMARY = 264; ++ /** Key code constant: Generic stem key 1 for Wear */ ++ public static final int KEYCODE_STEM_1 = 265; ++ /** Key code constant: Generic stem key 2 for Wear */ ++ public static final int KEYCODE_STEM_2 = 266; ++ /** Key code constant: Generic stem key 3 for Wear */ ++ public static final int KEYCODE_STEM_3 = 267; ++ /** Key code constant: Directional Pad Up-Left */ ++ public static final int KEYCODE_DPAD_UP_LEFT = 268; ++ /** Key code constant: Directional Pad Down-Left */ ++ public static final int KEYCODE_DPAD_DOWN_LEFT = 269; ++ /** Key code constant: Directional Pad Up-Right */ ++ public static final int KEYCODE_DPAD_UP_RIGHT = 270; ++ /** Key code constant: Directional Pad Down-Right */ ++ public static final int KEYCODE_DPAD_DOWN_RIGHT = 271; ++ /** Key code constant: Skip forward media key. */ ++ public static final int KEYCODE_MEDIA_SKIP_FORWARD = 272; ++ /** Key code constant: Skip backward media key. */ ++ public static final int KEYCODE_MEDIA_SKIP_BACKWARD = 273; ++ /** Key code constant: Step forward media key. ++ * Steps media forward, one frame at a time. */ ++ public static final int KEYCODE_MEDIA_STEP_FORWARD = 274; ++ /** Key code constant: Step backward media key. ++ * Steps media backward, one frame at a time. */ ++ public static final int KEYCODE_MEDIA_STEP_BACKWARD = 275; ++ /** Key code constant: put device to sleep unless a wakelock is held. */ ++ public static final int KEYCODE_SOFT_SLEEP = 276; ++ /** Key code constant: Cut key. */ ++ public static final int KEYCODE_CUT = 277; ++ /** Key code constant: Copy key. */ ++ public static final int KEYCODE_COPY = 278; ++ /** Key code constant: Paste key. */ ++ public static final int KEYCODE_PASTE = 279; ++ /** Key code constant: Consumed by the system for navigation up */ ++ public static final int KEYCODE_SYSTEM_NAVIGATION_UP = 280; ++ /** Key code constant: Consumed by the system for navigation down */ ++ public static final int KEYCODE_SYSTEM_NAVIGATION_DOWN = 281; ++ /** Key code constant: Consumed by the system for navigation left*/ ++ public static final int KEYCODE_SYSTEM_NAVIGATION_LEFT = 282; ++ /** Key code constant: Consumed by the system for navigation right */ ++ public static final int KEYCODE_SYSTEM_NAVIGATION_RIGHT = 283; ++ /** Key code constant: Show all apps */ ++ public static final int KEYCODE_ALL_APPS = 284; ++ /** Key code constant: Refresh key. */ ++ public static final int KEYCODE_REFRESH = 285; ++ /** Key code constant: Thumbs up key. Apps can use this to let user upvote content. */ ++ public static final int KEYCODE_THUMBS_UP = 286; ++ /** Key code constant: Thumbs down key. Apps can use this to let user downvote content. */ ++ public static final int KEYCODE_THUMBS_DOWN = 287; ++ /** ++ * Key code constant: Used to switch current {@link android.accounts.Account} that is ++ * consuming content. May be consumed by system to set account globally. ++ */ ++ public static final int KEYCODE_PROFILE_SWITCH = 288; ++ ++ /** ++ * Integer value of the last KEYCODE. Increases as new keycodes are added to KeyEvent. ++ * @hide ++ */ ++ @TestApi ++ public static final int LAST_KEYCODE = KEYCODE_PROFILE_SWITCH; ++ ++ // NOTE: If you add a new keycode here you must also add it to: ++ // isSystem() ++ // isWakeKey() ++ // frameworks/native/include/android/keycodes.h ++ // frameworks/native/include/input/InputEventLabels.h ++ // frameworks/base/core/res/res/values/attrs.xml ++ // emulator? ++ // LAST_KEYCODE ++ // ++ // Also Android currently does not reserve code ranges for vendor- ++ // specific key codes. If you have new key codes to have, you ++ // MUST contribute a patch to the open source project to define ++ // those new codes. This is intended to maintain a consistent ++ // set of key code definitions across all Android devices. ++ ++ // Symbolic names of all metakeys in bit order from least significant to most significant. ++ // Accordingly there are exactly 32 values in this table. ++ @UnsupportedAppUsage ++ private static final String[] META_SYMBOLIC_NAMES = new String[] { ++ "META_SHIFT_ON", ++ "META_ALT_ON", ++ "META_SYM_ON", ++ "META_FUNCTION_ON", ++ "META_ALT_LEFT_ON", ++ "META_ALT_RIGHT_ON", ++ "META_SHIFT_LEFT_ON", ++ "META_SHIFT_RIGHT_ON", ++ "META_CAP_LOCKED", ++ "META_ALT_LOCKED", ++ "META_SYM_LOCKED", ++ "0x00000800", ++ "META_CTRL_ON", ++ "META_CTRL_LEFT_ON", ++ "META_CTRL_RIGHT_ON", ++ "0x00008000", ++ "META_META_ON", ++ "META_META_LEFT_ON", ++ "META_META_RIGHT_ON", ++ "0x00080000", ++ "META_CAPS_LOCK_ON", ++ "META_NUM_LOCK_ON", ++ "META_SCROLL_LOCK_ON", ++ "0x00800000", ++ "0x01000000", ++ "0x02000000", ++ "0x04000000", ++ "0x08000000", ++ "0x10000000", ++ "0x20000000", ++ "0x40000000", ++ "0x80000000", ++ }; ++ ++ private static final String LABEL_PREFIX = "KEYCODE_"; ++ ++ /** ++ * @deprecated There are now more than MAX_KEYCODE keycodes. ++ * Use {@link #getMaxKeyCode()} instead. ++ */ ++ @Deprecated ++ public static final int MAX_KEYCODE = 84; ++ ++ /** ++ * {@link #getAction} value: the key has been pressed down. ++ */ ++ public static final int ACTION_DOWN = 0; ++ /** ++ * {@link #getAction} value: the key has been released. ++ */ ++ public static final int ACTION_UP = 1; ++ /** ++ * @deprecated No longer used by the input system. ++ * {@link #getAction} value: multiple duplicate key events have ++ * occurred in a row, or a complex string is being delivered. If the ++ * key code is not {@link #KEYCODE_UNKNOWN} then the ++ * {@link #getRepeatCount()} method returns the number of times ++ * the given key code should be executed. ++ * Otherwise, if the key code is {@link #KEYCODE_UNKNOWN}, then ++ * this is a sequence of characters as returned by {@link #getCharacters}. ++ */ ++ @Deprecated ++ public static final int ACTION_MULTIPLE = 2; ++ ++ /** ++ * SHIFT key locked in CAPS mode. ++ * Reserved for use by {@link MetaKeyKeyListener} for a published constant in its API. ++ * @hide ++ */ ++ @UnsupportedAppUsage ++ public static final int META_CAP_LOCKED = 0x100; ++ ++ /** ++ * ALT key locked. ++ * Reserved for use by {@link MetaKeyKeyListener} for a published constant in its API. ++ * @hide ++ */ ++ @UnsupportedAppUsage ++ public static final int META_ALT_LOCKED = 0x200; ++ ++ /** ++ * SYM key locked. ++ * Reserved for use by {@link MetaKeyKeyListener} for a published constant in its API. ++ * @hide ++ */ ++ @UnsupportedAppUsage ++ public static final int META_SYM_LOCKED = 0x400; ++ ++ /** ++ * Text is in selection mode. ++ * Reserved for use by {@link MetaKeyKeyListener} for a private unpublished constant ++ * in its API that is currently being retained for legacy reasons. ++ * @hide ++ */ ++ @UnsupportedAppUsage ++ public static final int META_SELECTING = 0x800; ++ ++ /** ++ *

This mask is used to check whether one of the ALT meta keys is pressed.

++ * ++ * @see #isAltPressed() ++ * @see #getMetaState() ++ * @see #KEYCODE_ALT_LEFT ++ * @see #KEYCODE_ALT_RIGHT ++ */ ++ public static final int META_ALT_ON = 0x02; ++ ++ /** ++ *

This mask is used to check whether the left ALT meta key is pressed.

++ * ++ * @see #isAltPressed() ++ * @see #getMetaState() ++ * @see #KEYCODE_ALT_LEFT ++ */ ++ public static final int META_ALT_LEFT_ON = 0x10; ++ ++ /** ++ *

This mask is used to check whether the right the ALT meta key is pressed.

++ * ++ * @see #isAltPressed() ++ * @see #getMetaState() ++ * @see #KEYCODE_ALT_RIGHT ++ */ ++ public static final int META_ALT_RIGHT_ON = 0x20; ++ ++ /** ++ *

This mask is used to check whether one of the SHIFT meta keys is pressed.

++ * ++ * @see #isShiftPressed() ++ * @see #getMetaState() ++ * @see #KEYCODE_SHIFT_LEFT ++ * @see #KEYCODE_SHIFT_RIGHT ++ */ ++ public static final int META_SHIFT_ON = 0x1; ++ ++ /** ++ *

This mask is used to check whether the left SHIFT meta key is pressed.

++ * ++ * @see #isShiftPressed() ++ * @see #getMetaState() ++ * @see #KEYCODE_SHIFT_LEFT ++ */ ++ public static final int META_SHIFT_LEFT_ON = 0x40; ++ ++ /** ++ *

This mask is used to check whether the right SHIFT meta key is pressed.

++ * ++ * @see #isShiftPressed() ++ * @see #getMetaState() ++ * @see #KEYCODE_SHIFT_RIGHT ++ */ ++ public static final int META_SHIFT_RIGHT_ON = 0x80; ++ ++ /** ++ *

This mask is used to check whether the SYM meta key is pressed.

++ * ++ * @see #isSymPressed() ++ * @see #getMetaState() ++ */ ++ public static final int META_SYM_ON = 0x4; ++ ++ /** ++ *

This mask is used to check whether the FUNCTION meta key is pressed.

++ * ++ * @see #isFunctionPressed() ++ * @see #getMetaState() ++ */ ++ public static final int META_FUNCTION_ON = 0x8; ++ ++ /** ++ *

This mask is used to check whether one of the CTRL meta keys is pressed.

++ * ++ * @see #isCtrlPressed() ++ * @see #getMetaState() ++ * @see #KEYCODE_CTRL_LEFT ++ * @see #KEYCODE_CTRL_RIGHT ++ */ ++ public static final int META_CTRL_ON = 0x1000; ++ ++ /** ++ *

This mask is used to check whether the left CTRL meta key is pressed.

++ * ++ * @see #isCtrlPressed() ++ * @see #getMetaState() ++ * @see #KEYCODE_CTRL_LEFT ++ */ ++ public static final int META_CTRL_LEFT_ON = 0x2000; ++ ++ /** ++ *

This mask is used to check whether the right CTRL meta key is pressed.

++ * ++ * @see #isCtrlPressed() ++ * @see #getMetaState() ++ * @see #KEYCODE_CTRL_RIGHT ++ */ ++ public static final int META_CTRL_RIGHT_ON = 0x4000; ++ ++ /** ++ *

This mask is used to check whether one of the META meta keys is pressed.

++ * ++ * @see #isMetaPressed() ++ * @see #getMetaState() ++ * @see #KEYCODE_META_LEFT ++ * @see #KEYCODE_META_RIGHT ++ */ ++ public static final int META_META_ON = 0x10000; ++ ++ /** ++ *

This mask is used to check whether the left META meta key is pressed.

++ * ++ * @see #isMetaPressed() ++ * @see #getMetaState() ++ * @see #KEYCODE_META_LEFT ++ */ ++ public static final int META_META_LEFT_ON = 0x20000; ++ ++ /** ++ *

This mask is used to check whether the right META meta key is pressed.

++ * ++ * @see #isMetaPressed() ++ * @see #getMetaState() ++ * @see #KEYCODE_META_RIGHT ++ */ ++ public static final int META_META_RIGHT_ON = 0x40000; ++ ++ /** ++ *

This mask is used to check whether the CAPS LOCK meta key is on.

++ * ++ * @see #isCapsLockOn() ++ * @see #getMetaState() ++ * @see #KEYCODE_CAPS_LOCK ++ */ ++ public static final int META_CAPS_LOCK_ON = 0x100000; ++ ++ /** ++ *

This mask is used to check whether the NUM LOCK meta key is on.

++ * ++ * @see #isNumLockOn() ++ * @see #getMetaState() ++ * @see #KEYCODE_NUM_LOCK ++ */ ++ public static final int META_NUM_LOCK_ON = 0x200000; ++ ++ /** ++ *

This mask is used to check whether the SCROLL LOCK meta key is on.

++ * ++ * @see #isScrollLockOn() ++ * @see #getMetaState() ++ * @see #KEYCODE_SCROLL_LOCK ++ */ ++ public static final int META_SCROLL_LOCK_ON = 0x400000; ++ ++ /** ++ * This mask is a combination of {@link #META_SHIFT_ON}, {@link #META_SHIFT_LEFT_ON} ++ * and {@link #META_SHIFT_RIGHT_ON}. ++ */ ++ public static final int META_SHIFT_MASK = META_SHIFT_ON ++ | META_SHIFT_LEFT_ON | META_SHIFT_RIGHT_ON; ++ ++ /** ++ * This mask is a combination of {@link #META_ALT_ON}, {@link #META_ALT_LEFT_ON} ++ * and {@link #META_ALT_RIGHT_ON}. ++ */ ++ public static final int META_ALT_MASK = META_ALT_ON ++ | META_ALT_LEFT_ON | META_ALT_RIGHT_ON; ++ ++ /** ++ * This mask is a combination of {@link #META_CTRL_ON}, {@link #META_CTRL_LEFT_ON} ++ * and {@link #META_CTRL_RIGHT_ON}. ++ */ ++ public static final int META_CTRL_MASK = META_CTRL_ON ++ | META_CTRL_LEFT_ON | META_CTRL_RIGHT_ON; ++ ++ /** ++ * This mask is a combination of {@link #META_META_ON}, {@link #META_META_LEFT_ON} ++ * and {@link #META_META_RIGHT_ON}. ++ */ ++ public static final int META_META_MASK = META_META_ON ++ | META_META_LEFT_ON | META_META_RIGHT_ON; ++ ++ /** ++ * This mask is set if the device woke because of this key event. ++ * ++ * @deprecated This flag will never be set by the system since the system ++ * consumes all wake keys itself. ++ */ ++ @Deprecated ++ public static final int FLAG_WOKE_HERE = 0x1; ++ ++ /** ++ * This mask is set if the key event was generated by a software keyboard. ++ */ ++ public static final int FLAG_SOFT_KEYBOARD = 0x2; ++ ++ /** ++ * This mask is set if we don't want the key event to cause us to leave ++ * touch mode. ++ */ ++ public static final int FLAG_KEEP_TOUCH_MODE = 0x4; ++ ++ /** ++ * This mask is set if an event was known to come from a trusted part ++ * of the system. That is, the event is known to come from the user, ++ * and could not have been spoofed by a third party component. ++ */ ++ public static final int FLAG_FROM_SYSTEM = 0x8; ++ ++ /** ++ * This mask is used for compatibility, to identify enter keys that are ++ * coming from an IME whose enter key has been auto-labelled "next" or ++ * "done". This allows TextView to dispatch these as normal enter keys ++ * for old applications, but still do the appropriate action when ++ * receiving them. ++ */ ++ public static final int FLAG_EDITOR_ACTION = 0x10; ++ ++ /** ++ * When associated with up key events, this indicates that the key press ++ * has been canceled. Typically this is used with virtual touch screen ++ * keys, where the user can slide from the virtual key area on to the ++ * display: in that case, the application will receive a canceled up ++ * event and should not perform the action normally associated with the ++ * key. Note that for this to work, the application can not perform an ++ * action for a key until it receives an up or the long press timeout has ++ * expired. ++ */ ++ public static final int FLAG_CANCELED = 0x20; ++ ++ /** ++ * This key event was generated by a virtual (on-screen) hard key area. ++ * Typically this is an area of the touchscreen, outside of the regular ++ * display, dedicated to "hardware" buttons. ++ */ ++ public static final int FLAG_VIRTUAL_HARD_KEY = 0x40; ++ ++ /** ++ * This flag is set for the first key repeat that occurs after the ++ * long press timeout. ++ */ ++ public static final int FLAG_LONG_PRESS = 0x80; ++ ++ /** ++ * Set when a key event has {@link #FLAG_CANCELED} set because a long ++ * press action was executed while it was down. ++ */ ++ public static final int FLAG_CANCELED_LONG_PRESS = 0x100; ++ ++ /** ++ * Set for {@link #ACTION_UP} when this event's key code is still being ++ * tracked from its initial down. That is, somebody requested that tracking ++ * started on the key down and a long press has not caused ++ * the tracking to be canceled. ++ */ ++ public static final int FLAG_TRACKING = 0x200; ++ ++ /** ++ * Set when a key event has been synthesized to implement default behavior ++ * for an event that the application did not handle. ++ * Fallback key events are generated by unhandled trackball motions ++ * (to emulate a directional keypad) and by certain unhandled key presses ++ * that are declared in the key map (such as special function numeric keypad ++ * keys when numlock is off). ++ */ ++ public static final int FLAG_FALLBACK = 0x400; ++ ++ /** ++ * Signifies that the key is being predispatched. ++ * @hide ++ */ ++ public static final int FLAG_PREDISPATCH = 0x20000000; ++ ++ /** ++ * Private control to determine when an app is tracking a key sequence. ++ * @hide ++ */ ++ public static final int FLAG_START_TRACKING = 0x40000000; ++ ++ /** ++ * Private flag that indicates when the system has detected that this key event ++ * may be inconsistent with respect to the sequence of previously delivered key events, ++ * such as when a key up event is sent but the key was not down. ++ * ++ * @hide ++ * @see #isTainted ++ * @see #setTainted ++ */ ++ public static final int FLAG_TAINTED = 0x80000000; ++ ++ /** ++ * Returns the maximum keycode. ++ */ ++ public static int getMaxKeyCode() { ++ return LAST_KEYCODE; ++ } ++ ++ /** ++ * Get the character that is produced by putting accent on the character ++ * c. ++ * For example, getDeadChar('`', 'e') returns è. ++ */ ++ public static int getDeadChar(int accent, int c) { ++ return KeyCharacterMap.getDeadChar(accent, c); ++ } ++ ++ static final boolean DEBUG = false; ++ static final String TAG = "KeyEvent"; ++ ++ private static final int MAX_RECYCLED = 10; ++ private static final Object gRecyclerLock = new Object(); ++ private static int gRecyclerUsed; ++ private static KeyEvent gRecyclerTop; ++ ++ private KeyEvent mNext; ++ ++ private int mId; ++ @UnsupportedAppUsage ++ private int mDeviceId; ++ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) ++ private int mSource; ++ private int mDisplayId; ++ private @Nullable byte[] mHmac; ++ @UnsupportedAppUsage ++ private int mMetaState; ++ @UnsupportedAppUsage ++ private int mAction; ++ @UnsupportedAppUsage ++ private int mKeyCode; ++ @UnsupportedAppUsage ++ private int mScanCode; ++ @UnsupportedAppUsage ++ private int mRepeatCount; ++ @UnsupportedAppUsage ++ private int mFlags; ++ @UnsupportedAppUsage ++ private long mDownTime; ++ @UnsupportedAppUsage ++ private long mEventTime; ++ @UnsupportedAppUsage ++ private String mCharacters; ++ ++ public interface Callback { ++ /** ++ * Called when a key down event has occurred. If you return true, ++ * you can first call {@link KeyEvent#startTracking() ++ * KeyEvent.startTracking()} to have the framework track the event ++ * through its {@link #onKeyUp(int, KeyEvent)} and also call your ++ * {@link #onKeyLongPress(int, KeyEvent)} if it occurs. ++ * ++ * @param keyCode The value in event.getKeyCode(). ++ * @param event Description of the key event. ++ * ++ * @return If you handled the event, return true. If you want to allow ++ * the event to be handled by the next receiver, return false. ++ */ ++ boolean onKeyDown(int keyCode, KeyEvent event); ++ ++ /** ++ * Called when a long press has occurred. If you return true, ++ * the final key up will have {@link KeyEvent#FLAG_CANCELED} and ++ * {@link KeyEvent#FLAG_CANCELED_LONG_PRESS} set. Note that in ++ * order to receive this callback, someone in the event change ++ * must return true from {@link #onKeyDown} and ++ * call {@link KeyEvent#startTracking()} on the event. ++ * ++ * @param keyCode The value in event.getKeyCode(). ++ * @param event Description of the key event. ++ * ++ * @return If you handled the event, return true. If you want to allow ++ * the event to be handled by the next receiver, return false. ++ */ ++ boolean onKeyLongPress(int keyCode, KeyEvent event); ++ ++ /** ++ * Called when a key up event has occurred. ++ * ++ * @param keyCode The value in event.getKeyCode(). ++ * @param event Description of the key event. ++ * ++ * @return If you handled the event, return true. If you want to allow ++ * the event to be handled by the next receiver, return false. ++ */ ++ boolean onKeyUp(int keyCode, KeyEvent event); ++ ++ /** ++ * Called when a user's interaction with an analog control, such as ++ * flinging a trackball, generates simulated down/up events for the same ++ * key multiple times in quick succession. ++ * ++ * @param keyCode The value in event.getKeyCode(). ++ * @param count Number of pairs as returned by event.getRepeatCount(). ++ * @param event Description of the key event. ++ * ++ * @return If you handled the event, return true. If you want to allow ++ * the event to be handled by the next receiver, return false. ++ */ ++ boolean onKeyMultiple(int keyCode, int count, KeyEvent event); ++ } ++ ++ private static native String nativeKeyCodeToString(int keyCode); ++ private static native int nativeKeyCodeFromString(String keyCode); ++ private static native int nativeNextId(); ++ ++ private KeyEvent() {} ++ ++ /** ++ * Create a new key event. ++ * ++ * @param action Action code: either {@link #ACTION_DOWN}, ++ * {@link #ACTION_UP}, or {@link #ACTION_MULTIPLE}. ++ * @param code The key code. ++ */ ++ public KeyEvent(int action, int code) { ++ mId = nativeNextId(); ++ mAction = action; ++ mKeyCode = code; ++ mRepeatCount = 0; ++ mDeviceId = KeyCharacterMap.VIRTUAL_KEYBOARD; ++ } ++ ++ /** ++ * Create a new key event. ++ * ++ * @param downTime The time (in {@link android.os.SystemClock#uptimeMillis}) ++ * at which this key code originally went down. ++ * @param eventTime The time (in {@link android.os.SystemClock#uptimeMillis}) ++ * at which this event happened. ++ * @param action Action code: either {@link #ACTION_DOWN}, ++ * {@link #ACTION_UP}, or {@link #ACTION_MULTIPLE}. ++ * @param code The key code. ++ * @param repeat A repeat count for down events (> 0 if this is after the ++ * initial down) or event count for multiple events. ++ */ ++ public KeyEvent(long downTime, long eventTime, int action, ++ int code, int repeat) { ++ mId = nativeNextId(); ++ mDownTime = downTime; ++ mEventTime = eventTime; ++ mAction = action; ++ mKeyCode = code; ++ mRepeatCount = repeat; ++ mDeviceId = KeyCharacterMap.VIRTUAL_KEYBOARD; ++ } ++ ++ /** ++ * Create a new key event. ++ * ++ * @param downTime The time (in {@link android.os.SystemClock#uptimeMillis}) ++ * at which this key code originally went down. ++ * @param eventTime The time (in {@link android.os.SystemClock#uptimeMillis}) ++ * at which this event happened. ++ * @param action Action code: either {@link #ACTION_DOWN}, ++ * {@link #ACTION_UP}, or {@link #ACTION_MULTIPLE}. ++ * @param code The key code. ++ * @param repeat A repeat count for down events (> 0 if this is after the ++ * initial down) or event count for multiple events. ++ * @param metaState Flags indicating which meta keys are currently pressed. ++ */ ++ public KeyEvent(long downTime, long eventTime, int action, ++ int code, int repeat, int metaState) { ++ mId = nativeNextId(); ++ mDownTime = downTime; ++ mEventTime = eventTime; ++ mAction = action; ++ mKeyCode = code; ++ mRepeatCount = repeat; ++ mMetaState = metaState; ++ mDeviceId = KeyCharacterMap.VIRTUAL_KEYBOARD; ++ } ++ ++ /** ++ * Create a new key event. ++ * ++ * @param downTime The time (in {@link android.os.SystemClock#uptimeMillis}) ++ * at which this key code originally went down. ++ * @param eventTime The time (in {@link android.os.SystemClock#uptimeMillis}) ++ * at which this event happened. ++ * @param action Action code: either {@link #ACTION_DOWN}, ++ * {@link #ACTION_UP}, or {@link #ACTION_MULTIPLE}. ++ * @param code The key code. ++ * @param repeat A repeat count for down events (> 0 if this is after the ++ * initial down) or event count for multiple events. ++ * @param metaState Flags indicating which meta keys are currently pressed. ++ * @param deviceId The device ID that generated the key event. ++ * @param scancode Raw device scan code of the event. ++ */ ++ public KeyEvent(long downTime, long eventTime, int action, ++ int code, int repeat, int metaState, ++ int deviceId, int scancode) { ++ mId = nativeNextId(); ++ mDownTime = downTime; ++ mEventTime = eventTime; ++ mAction = action; ++ mKeyCode = code; ++ mRepeatCount = repeat; ++ mMetaState = metaState; ++ mDeviceId = deviceId; ++ mScanCode = scancode; ++ } ++ ++ /** ++ * Create a new key event. ++ * ++ * @param downTime The time (in {@link android.os.SystemClock#uptimeMillis}) ++ * at which this key code originally went down. ++ * @param eventTime The time (in {@link android.os.SystemClock#uptimeMillis}) ++ * at which this event happened. ++ * @param action Action code: either {@link #ACTION_DOWN}, ++ * {@link #ACTION_UP}, or {@link #ACTION_MULTIPLE}. ++ * @param code The key code. ++ * @param repeat A repeat count for down events (> 0 if this is after the ++ * initial down) or event count for multiple events. ++ * @param metaState Flags indicating which meta keys are currently pressed. ++ * @param deviceId The device ID that generated the key event. ++ * @param scancode Raw device scan code of the event. ++ * @param flags The flags for this key event ++ */ ++ public KeyEvent(long downTime, long eventTime, int action, ++ int code, int repeat, int metaState, ++ int deviceId, int scancode, int flags) { ++ mId = nativeNextId(); ++ mDownTime = downTime; ++ mEventTime = eventTime; ++ mAction = action; ++ mKeyCode = code; ++ mRepeatCount = repeat; ++ mMetaState = metaState; ++ mDeviceId = deviceId; ++ mScanCode = scancode; ++ mFlags = flags; ++ } ++ ++ /** ++ * Create a new key event. ++ * ++ * @param downTime The time (in {@link android.os.SystemClock#uptimeMillis}) ++ * at which this key code originally went down. ++ * @param eventTime The time (in {@link android.os.SystemClock#uptimeMillis}) ++ * at which this event happened. ++ * @param action Action code: either {@link #ACTION_DOWN}, ++ * {@link #ACTION_UP}, or {@link #ACTION_MULTIPLE}. ++ * @param code The key code. ++ * @param repeat A repeat count for down events (> 0 if this is after the ++ * initial down) or event count for multiple events. ++ * @param metaState Flags indicating which meta keys are currently pressed. ++ * @param deviceId The device ID that generated the key event. ++ * @param scancode Raw device scan code of the event. ++ * @param flags The flags for this key event ++ * @param source The input source such as {@link InputDevice#SOURCE_KEYBOARD}. ++ */ ++ public KeyEvent(long downTime, long eventTime, int action, ++ int code, int repeat, int metaState, ++ int deviceId, int scancode, int flags, int source) { ++ mId = nativeNextId(); ++ mDownTime = downTime; ++ mEventTime = eventTime; ++ mAction = action; ++ mKeyCode = code; ++ mRepeatCount = repeat; ++ mMetaState = metaState; ++ mDeviceId = deviceId; ++ mScanCode = scancode; ++ mFlags = flags; ++ mSource = source; ++ mDisplayId = INVALID_DISPLAY; ++ } ++ ++ /** ++ * Create a new key event for a string of characters. The key code, ++ * action, repeat count and source will automatically be set to ++ * {@link #KEYCODE_UNKNOWN}, {@link #ACTION_MULTIPLE}, 0, and ++ * {@link InputDevice#SOURCE_KEYBOARD} for you. ++ * ++ * @param time The time (in {@link android.os.SystemClock#uptimeMillis}) ++ * at which this event occured. ++ * @param characters The string of characters. ++ * @param deviceId The device ID that generated the key event. ++ * @param flags The flags for this key event ++ */ ++ public KeyEvent(long time, String characters, int deviceId, int flags) { ++ mId = nativeNextId(); ++ mDownTime = time; ++ mEventTime = time; ++ mCharacters = characters; ++ mAction = ACTION_MULTIPLE; ++ mKeyCode = KEYCODE_UNKNOWN; ++ mRepeatCount = 0; ++ mDeviceId = deviceId; ++ mFlags = flags; ++ mSource = InputDevice.SOURCE_KEYBOARD; ++ mDisplayId = INVALID_DISPLAY; ++ } ++ ++ /** ++ * Make an exact copy of an existing key event. ++ */ ++ public KeyEvent(KeyEvent origEvent) { ++ mId = origEvent.mId; ++ mDownTime = origEvent.mDownTime; ++ mEventTime = origEvent.mEventTime; ++ mAction = origEvent.mAction; ++ mKeyCode = origEvent.mKeyCode; ++ mRepeatCount = origEvent.mRepeatCount; ++ mMetaState = origEvent.mMetaState; ++ mDeviceId = origEvent.mDeviceId; ++ mSource = origEvent.mSource; ++ mDisplayId = origEvent.mDisplayId; ++ mHmac = origEvent.mHmac == null ? null : origEvent.mHmac.clone(); ++ mScanCode = origEvent.mScanCode; ++ mFlags = origEvent.mFlags; ++ mCharacters = origEvent.mCharacters; ++ } ++ ++ /** ++ * Copy an existing key event, modifying its time and repeat count. ++ * ++ * @deprecated Use {@link #changeTimeRepeat(KeyEvent, long, int)} ++ * instead. ++ * ++ * @param origEvent The existing event to be copied. ++ * @param eventTime The new event time ++ * (in {@link android.os.SystemClock#uptimeMillis}) of the event. ++ * @param newRepeat The new repeat count of the event. ++ */ ++ @Deprecated ++ public KeyEvent(KeyEvent origEvent, long eventTime, int newRepeat) { ++ mId = nativeNextId(); // Not an exact copy so assign a new ID. ++ mDownTime = origEvent.mDownTime; ++ mEventTime = eventTime; ++ mAction = origEvent.mAction; ++ mKeyCode = origEvent.mKeyCode; ++ mRepeatCount = newRepeat; ++ mMetaState = origEvent.mMetaState; ++ mDeviceId = origEvent.mDeviceId; ++ mSource = origEvent.mSource; ++ mDisplayId = origEvent.mDisplayId; ++ mHmac = null; // Don't copy HMAC, it will be invalid because eventTime is changing ++ mScanCode = origEvent.mScanCode; ++ mFlags = origEvent.mFlags; ++ mCharacters = origEvent.mCharacters; ++ } ++ ++ private static KeyEvent obtain() { ++ final KeyEvent ev; ++ synchronized (gRecyclerLock) { ++ ev = gRecyclerTop; ++ if (ev == null) { ++ return new KeyEvent(); ++ } ++ gRecyclerTop = ev.mNext; ++ gRecyclerUsed -= 1; ++ } ++ ev.mNext = null; ++ ev.prepareForReuse(); ++ return ev; ++ } ++ ++ /** ++ * Obtains a (potentially recycled) key event. Used by native code to create a Java object. ++ * ++ * @hide ++ */ ++ public static KeyEvent obtain(int id, long downTime, long eventTime, int action, ++ int code, int repeat, int metaState, ++ int deviceId, int scancode, int flags, int source, int displayId, @Nullable byte[] hmac, ++ String characters) { ++ KeyEvent ev = obtain(); ++ ev.mId = id; ++ ev.mDownTime = downTime; ++ ev.mEventTime = eventTime; ++ ev.mAction = action; ++ ev.mKeyCode = code; ++ ev.mRepeatCount = repeat; ++ ev.mMetaState = metaState; ++ ev.mDeviceId = deviceId; ++ ev.mScanCode = scancode; ++ ev.mFlags = flags; ++ ev.mSource = source; ++ ev.mDisplayId = displayId; ++ ev.mHmac = hmac; ++ ev.mCharacters = characters; ++ return ev; ++ } ++ ++ /** ++ * Obtains a (potentially recycled) key event. ++ * ++ * @hide ++ */ ++ public static KeyEvent obtain(long downTime, long eventTime, int action, ++ int code, int repeat, int metaState, ++ int deviceId, int scanCode, int flags, int source, int displayId, String characters) { ++ return obtain(nativeNextId(), downTime, eventTime, action, code, repeat, metaState, ++ deviceId, scanCode, flags, source, displayId, null /* hmac */, characters); ++ } ++ ++ /** ++ * Obtains a (potentially recycled) key event. ++ * ++ * @hide ++ */ ++ @UnsupportedAppUsage ++ public static KeyEvent obtain(long downTime, long eventTime, int action, ++ int code, int repeat, int metaState, ++ int deviceId, int scancode, int flags, int source, String characters) { ++ return obtain(downTime, eventTime, action, code, repeat, metaState, deviceId, scancode, ++ flags, source, INVALID_DISPLAY, characters); ++ } ++ ++ /** ++ ++ /** ++ * Obtains a (potentially recycled) copy of another key event. ++ * ++ * @hide ++ */ ++ public static KeyEvent obtain(KeyEvent other) { ++ KeyEvent ev = obtain(); ++ ev.mId = other.mId; ++ ev.mDownTime = other.mDownTime; ++ ev.mEventTime = other.mEventTime; ++ ev.mAction = other.mAction; ++ ev.mKeyCode = other.mKeyCode; ++ ev.mRepeatCount = other.mRepeatCount; ++ ev.mMetaState = other.mMetaState; ++ ev.mDeviceId = other.mDeviceId; ++ ev.mScanCode = other.mScanCode; ++ ev.mFlags = other.mFlags; ++ ev.mSource = other.mSource; ++ ev.mDisplayId = other.mDisplayId; ++ ev.mHmac = other.mHmac == null ? null : other.mHmac.clone(); ++ ev.mCharacters = other.mCharacters; ++ return ev; ++ } ++ ++ /** @hide */ ++ @Override ++ public KeyEvent copy() { ++ return obtain(this); ++ } ++ ++ /** ++ * Recycles a key event. ++ * Key events should only be recycled if they are owned by the system since user ++ * code expects them to be essentially immutable, "tracking" notwithstanding. ++ * ++ * @hide ++ */ ++ @Override ++ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) ++ public final void recycle() { ++ super.recycle(); ++ mCharacters = null; ++ ++ synchronized (gRecyclerLock) { ++ if (gRecyclerUsed < MAX_RECYCLED) { ++ gRecyclerUsed++; ++ mNext = gRecyclerTop; ++ gRecyclerTop = this; ++ } ++ } ++ } ++ ++ /** @hide */ ++ @Override ++ public final void recycleIfNeededAfterDispatch() { ++ // Do nothing. ++ } ++ ++ /** @hide */ ++ @Override ++ public int getId() { ++ return mId; ++ } ++ ++ /** ++ * Create a new key event that is the same as the given one, but whose ++ * event time and repeat count are replaced with the given value. ++ * ++ * @param event The existing event to be copied. This is not modified. ++ * @param eventTime The new event time ++ * (in {@link android.os.SystemClock#uptimeMillis}) of the event. ++ * @param newRepeat The new repeat count of the event. ++ */ ++ public static KeyEvent changeTimeRepeat(KeyEvent event, long eventTime, ++ int newRepeat) { ++ return new KeyEvent(event, eventTime, newRepeat); ++ } ++ ++ /** ++ * Create a new key event that is the same as the given one, but whose ++ * event time and repeat count are replaced with the given value. ++ * ++ * @param event The existing event to be copied. This is not modified. ++ * @param eventTime The new event time ++ * (in {@link android.os.SystemClock#uptimeMillis}) of the event. ++ * @param newRepeat The new repeat count of the event. ++ * @param newFlags New flags for the event, replacing the entire value ++ * in the original event. ++ */ ++ public static KeyEvent changeTimeRepeat(KeyEvent event, long eventTime, ++ int newRepeat, int newFlags) { ++ KeyEvent ret = new KeyEvent(event); ++ ret.mId = nativeNextId(); // Not an exact copy so assign a new ID. ++ ret.mEventTime = eventTime; ++ ret.mRepeatCount = newRepeat; ++ ret.mFlags = newFlags; ++ return ret; ++ } ++ ++ /** ++ * Copy an existing key event, modifying its action. ++ * ++ * @param origEvent The existing event to be copied. ++ * @param action The new action code of the event. ++ */ ++ private KeyEvent(KeyEvent origEvent, int action) { ++ mId = nativeNextId(); // Not an exact copy so assign a new ID. ++ mDownTime = origEvent.mDownTime; ++ mEventTime = origEvent.mEventTime; ++ mAction = action; ++ mKeyCode = origEvent.mKeyCode; ++ mRepeatCount = origEvent.mRepeatCount; ++ mMetaState = origEvent.mMetaState; ++ mDeviceId = origEvent.mDeviceId; ++ mSource = origEvent.mSource; ++ mDisplayId = origEvent.mDisplayId; ++ mHmac = null; // Don't copy the hmac, it will be invalid since action is changing ++ mScanCode = origEvent.mScanCode; ++ mFlags = origEvent.mFlags; ++ // Don't copy mCharacters, since one way or the other we'll lose it ++ // when changing the action. ++ } ++ ++ /** ++ * Create a new key event that is the same as the given one, but whose ++ * action is replaced with the given value. ++ * ++ * @param event The existing event to be copied. This is not modified. ++ * @param action The new action code of the event. ++ */ ++ public static KeyEvent changeAction(KeyEvent event, int action) { ++ return new KeyEvent(event, action); ++ } ++ ++ /** ++ * Create a new key event that is the same as the given one, but whose ++ * flags are replaced with the given value. ++ * ++ * @param event The existing event to be copied. This is not modified. ++ * @param flags The new flags constant. ++ */ ++ public static KeyEvent changeFlags(KeyEvent event, int flags) { ++ event = new KeyEvent(event); ++ event.mId = nativeNextId(); // Not an exact copy so assign a new ID. ++ event.mFlags = flags; ++ return event; ++ } ++ ++ /** @hide */ ++ @Override ++ public final boolean isTainted() { ++ return (mFlags & FLAG_TAINTED) != 0; ++ } ++ ++ /** @hide */ ++ @Override ++ public final void setTainted(boolean tainted) { ++ mFlags = tainted ? mFlags | FLAG_TAINTED : mFlags & ~FLAG_TAINTED; ++ } ++ ++ /** ++ * Don't use in new code, instead explicitly check ++ * {@link #getAction()}. ++ * ++ * @return If the action is ACTION_DOWN, returns true; else false. ++ * ++ * @deprecated ++ * @hide ++ */ ++ @UnsupportedAppUsage ++ @Deprecated public final boolean isDown() { ++ return mAction == ACTION_DOWN; ++ } ++ ++ /** Is this a system key? System keys can not be used for menu shortcuts. ++ */ ++ public final boolean isSystem() { ++ return isSystemKey(mKeyCode); ++ } ++ ++ /** @hide */ ++ public final boolean isWakeKey() { ++ return isWakeKey(mKeyCode); ++ } ++ ++ /** ++ * Returns true if the specified keycode is a gamepad button. ++ * @return True if the keycode is a gamepad button, such as {@link #KEYCODE_BUTTON_A}. ++ */ ++ public static final boolean isGamepadButton(int keyCode) { ++ switch (keyCode) { ++ case KeyEvent.KEYCODE_BUTTON_A: ++ case KeyEvent.KEYCODE_BUTTON_B: ++ case KeyEvent.KEYCODE_BUTTON_C: ++ case KeyEvent.KEYCODE_BUTTON_X: ++ case KeyEvent.KEYCODE_BUTTON_Y: ++ case KeyEvent.KEYCODE_BUTTON_Z: ++ case KeyEvent.KEYCODE_BUTTON_L1: ++ case KeyEvent.KEYCODE_BUTTON_R1: ++ case KeyEvent.KEYCODE_BUTTON_L2: ++ case KeyEvent.KEYCODE_BUTTON_R2: ++ case KeyEvent.KEYCODE_BUTTON_THUMBL: ++ case KeyEvent.KEYCODE_BUTTON_THUMBR: ++ case KeyEvent.KEYCODE_BUTTON_START: ++ case KeyEvent.KEYCODE_BUTTON_SELECT: ++ case KeyEvent.KEYCODE_BUTTON_MODE: ++ case KeyEvent.KEYCODE_BUTTON_1: ++ case KeyEvent.KEYCODE_BUTTON_2: ++ case KeyEvent.KEYCODE_BUTTON_3: ++ case KeyEvent.KEYCODE_BUTTON_4: ++ case KeyEvent.KEYCODE_BUTTON_5: ++ case KeyEvent.KEYCODE_BUTTON_6: ++ case KeyEvent.KEYCODE_BUTTON_7: ++ case KeyEvent.KEYCODE_BUTTON_8: ++ case KeyEvent.KEYCODE_BUTTON_9: ++ case KeyEvent.KEYCODE_BUTTON_10: ++ case KeyEvent.KEYCODE_BUTTON_11: ++ case KeyEvent.KEYCODE_BUTTON_12: ++ case KeyEvent.KEYCODE_BUTTON_13: ++ case KeyEvent.KEYCODE_BUTTON_14: ++ case KeyEvent.KEYCODE_BUTTON_15: ++ case KeyEvent.KEYCODE_BUTTON_16: ++ return true; ++ default: ++ return false; ++ } ++ } ++ ++ /** Whether key will, by default, trigger a click on the focused view. ++ * @hide ++ */ ++ @UnsupportedAppUsage ++ public static final boolean isConfirmKey(int keyCode) { ++ switch (keyCode) { ++ case KeyEvent.KEYCODE_DPAD_CENTER: ++ case KeyEvent.KEYCODE_ENTER: ++ case KeyEvent.KEYCODE_SPACE: ++ case KeyEvent.KEYCODE_NUMPAD_ENTER: ++ return true; ++ default: ++ return false; ++ } ++ } ++ ++ /** ++ * Returns whether this key will be sent to the ++ * {@link android.media.session.MediaSession.Callback} if not handled. ++ * ++ * @hide ++ */ ++ public static final boolean isMediaSessionKey(int keyCode) { ++ switch (keyCode) { ++ case KeyEvent.KEYCODE_MEDIA_PLAY: ++ case KeyEvent.KEYCODE_MEDIA_PAUSE: ++ case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: ++ case KeyEvent.KEYCODE_MUTE: ++ case KeyEvent.KEYCODE_HEADSETHOOK: ++ case KeyEvent.KEYCODE_MEDIA_STOP: ++ case KeyEvent.KEYCODE_MEDIA_NEXT: ++ case KeyEvent.KEYCODE_MEDIA_PREVIOUS: ++ case KeyEvent.KEYCODE_MEDIA_REWIND: ++ case KeyEvent.KEYCODE_MEDIA_RECORD: ++ case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: ++ return true; ++ } ++ return false; ++ } ++ ++ /** Is this a system key? System keys can not be used for menu shortcuts. ++ * @hide ++ */ ++ public static final boolean isSystemKey(int keyCode) { ++ switch (keyCode) { ++ case KeyEvent.KEYCODE_MENU: ++ case KeyEvent.KEYCODE_SOFT_RIGHT: ++ case KeyEvent.KEYCODE_HOME: ++ case KeyEvent.KEYCODE_BACK: ++ case KeyEvent.KEYCODE_CALL: ++ case KeyEvent.KEYCODE_ENDCALL: ++ case KeyEvent.KEYCODE_VOLUME_UP: ++ case KeyEvent.KEYCODE_VOLUME_DOWN: ++ case KeyEvent.KEYCODE_VOLUME_MUTE: ++ case KeyEvent.KEYCODE_MUTE: ++ case KeyEvent.KEYCODE_POWER: ++ case KeyEvent.KEYCODE_HEADSETHOOK: ++ case KeyEvent.KEYCODE_MEDIA_PLAY: ++ case KeyEvent.KEYCODE_MEDIA_PAUSE: ++ case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: ++ case KeyEvent.KEYCODE_MEDIA_STOP: ++ case KeyEvent.KEYCODE_MEDIA_NEXT: ++ case KeyEvent.KEYCODE_MEDIA_PREVIOUS: ++ case KeyEvent.KEYCODE_MEDIA_REWIND: ++ case KeyEvent.KEYCODE_MEDIA_RECORD: ++ case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: ++ case KeyEvent.KEYCODE_CAMERA: ++ case KeyEvent.KEYCODE_FOCUS: ++ case KeyEvent.KEYCODE_SEARCH: ++ case KeyEvent.KEYCODE_BRIGHTNESS_DOWN: ++ case KeyEvent.KEYCODE_BRIGHTNESS_UP: ++ case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: ++ case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP: ++ case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN: ++ case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_LEFT: ++ case KeyEvent.KEYCODE_SYSTEM_NAVIGATION_RIGHT: ++ return true; ++ } ++ ++ return false; ++ } ++ ++ /** @hide */ ++ public static final boolean isWakeKey(int keyCode) { ++ switch (keyCode) { ++ case KeyEvent.KEYCODE_CAMERA: ++ case KeyEvent.KEYCODE_FOCUS: ++ case KeyEvent.KEYCODE_MENU: ++ case KeyEvent.KEYCODE_PAIRING: ++ case KeyEvent.KEYCODE_STEM_1: ++ case KeyEvent.KEYCODE_STEM_2: ++ case KeyEvent.KEYCODE_STEM_3: ++ case KeyEvent.KEYCODE_WAKEUP: ++ case KeyEvent.KEYCODE_VOLUME_UP: ++ case KeyEvent.KEYCODE_VOLUME_DOWN: ++ case KeyEvent.KEYCODE_VOLUME_MUTE: ++ return true; ++ } ++ return false; ++ } ++ ++ /** @hide */ ++ public static final boolean isMetaKey(int keyCode) { ++ return keyCode == KeyEvent.KEYCODE_META_LEFT || keyCode == KeyEvent.KEYCODE_META_RIGHT; ++ } ++ ++ /** @hide */ ++ public static final boolean isAltKey(int keyCode) { ++ return keyCode == KeyEvent.KEYCODE_ALT_LEFT || keyCode == KeyEvent.KEYCODE_ALT_RIGHT; ++ } ++ ++ /** {@inheritDoc} */ ++ @Override ++ public final int getDeviceId() { ++ return mDeviceId; ++ } ++ ++ /** {@inheritDoc} */ ++ @Override ++ public final int getSource() { ++ return mSource; ++ } ++ ++ /** {@inheritDoc} */ ++ @Override ++ public final void setSource(int source) { ++ mSource = source; ++ } ++ ++ /** @hide */ ++ @Override ++ public final int getDisplayId() { ++ return mDisplayId; ++ } ++ ++ /** @hide */ ++ @TestApi ++ @Override ++ public final void setDisplayId(int displayId) { ++ mDisplayId = displayId; ++ } ++ ++ /** ++ *

Returns the state of the meta keys.

++ * ++ * @return an integer in which each bit set to 1 represents a pressed ++ * meta key ++ * ++ * @see #isAltPressed() ++ * @see #isShiftPressed() ++ * @see #isSymPressed() ++ * @see #isCtrlPressed() ++ * @see #isMetaPressed() ++ * @see #isFunctionPressed() ++ * @see #isCapsLockOn() ++ * @see #isNumLockOn() ++ * @see #isScrollLockOn() ++ * @see #META_ALT_ON ++ * @see #META_ALT_LEFT_ON ++ * @see #META_ALT_RIGHT_ON ++ * @see #META_SHIFT_ON ++ * @see #META_SHIFT_LEFT_ON ++ * @see #META_SHIFT_RIGHT_ON ++ * @see #META_SYM_ON ++ * @see #META_FUNCTION_ON ++ * @see #META_CTRL_ON ++ * @see #META_CTRL_LEFT_ON ++ * @see #META_CTRL_RIGHT_ON ++ * @see #META_META_ON ++ * @see #META_META_LEFT_ON ++ * @see #META_META_RIGHT_ON ++ * @see #META_CAPS_LOCK_ON ++ * @see #META_NUM_LOCK_ON ++ * @see #META_SCROLL_LOCK_ON ++ * @see #getModifiers ++ */ ++ public final int getMetaState() { ++ return mMetaState; ++ } ++ ++ /** ++ * Returns the state of the modifier keys. ++ *

++ * For the purposes of this function, {@link #KEYCODE_CAPS_LOCK}, ++ * {@link #KEYCODE_SCROLL_LOCK}, and {@link #KEYCODE_NUM_LOCK} are ++ * not considered modifier keys. Consequently, this function specifically masks out ++ * {@link #META_CAPS_LOCK_ON}, {@link #META_SCROLL_LOCK_ON} and {@link #META_NUM_LOCK_ON}. ++ *

++ * The value returned consists of the meta state (from {@link #getMetaState}) ++ * normalized using {@link #normalizeMetaState(int)} and then masked with ++ * {@link #getModifierMetaStateMask} so that only valid modifier bits are retained. ++ *

++ * ++ * @return An integer in which each bit set to 1 represents a pressed modifier key. ++ * @see #getMetaState ++ */ ++ public final int getModifiers() { ++ return normalizeMetaState(mMetaState) & META_MODIFIER_MASK; ++ } ++ ++ /** ++ * Modifies the flags of the event. ++ * ++ * @param newFlags New flags for the event, replacing the entire value. ++ * @hide ++ */ ++ public final void setFlags(int newFlags) { ++ mFlags = newFlags; ++ } ++ ++ /** ++ * Returns the flags for this key event. ++ * ++ * @see #FLAG_WOKE_HERE ++ */ ++ public final int getFlags() { ++ return mFlags; ++ } ++ ++ // Mask of all modifier key meta states. Specifically excludes locked keys like caps lock. ++ @UnsupportedAppUsage ++ private static final int META_MODIFIER_MASK = ++ META_SHIFT_ON | META_SHIFT_LEFT_ON | META_SHIFT_RIGHT_ON ++ | META_ALT_ON | META_ALT_LEFT_ON | META_ALT_RIGHT_ON ++ | META_CTRL_ON | META_CTRL_LEFT_ON | META_CTRL_RIGHT_ON ++ | META_META_ON | META_META_LEFT_ON | META_META_RIGHT_ON ++ | META_SYM_ON | META_FUNCTION_ON; ++ ++ // Mask of all lock key meta states. ++ @UnsupportedAppUsage ++ private static final int META_LOCK_MASK = ++ META_CAPS_LOCK_ON | META_NUM_LOCK_ON | META_SCROLL_LOCK_ON; ++ ++ // Mask of all valid meta states. ++ @UnsupportedAppUsage ++ private static final int META_ALL_MASK = META_MODIFIER_MASK | META_LOCK_MASK; ++ ++ // Mask of all synthetic meta states that are reserved for API compatibility with ++ // historical uses in MetaKeyKeyListener. ++ @UnsupportedAppUsage ++ private static final int META_SYNTHETIC_MASK = ++ META_CAP_LOCKED | META_ALT_LOCKED | META_SYM_LOCKED | META_SELECTING; ++ ++ // Mask of all meta states that are not valid use in specifying a modifier key. ++ // These bits are known to be used for purposes other than specifying modifiers. ++ @UnsupportedAppUsage ++ private static final int META_INVALID_MODIFIER_MASK = ++ META_LOCK_MASK | META_SYNTHETIC_MASK; ++ ++ /** ++ * Gets a mask that includes all valid modifier key meta state bits. ++ *

++ * For the purposes of this function, {@link #KEYCODE_CAPS_LOCK}, ++ * {@link #KEYCODE_SCROLL_LOCK}, and {@link #KEYCODE_NUM_LOCK} are ++ * not considered modifier keys. Consequently, the mask specifically excludes ++ * {@link #META_CAPS_LOCK_ON}, {@link #META_SCROLL_LOCK_ON} and {@link #META_NUM_LOCK_ON}. ++ *

++ * ++ * @return The modifier meta state mask which is a combination of ++ * {@link #META_SHIFT_ON}, {@link #META_SHIFT_LEFT_ON}, {@link #META_SHIFT_RIGHT_ON}, ++ * {@link #META_ALT_ON}, {@link #META_ALT_LEFT_ON}, {@link #META_ALT_RIGHT_ON}, ++ * {@link #META_CTRL_ON}, {@link #META_CTRL_LEFT_ON}, {@link #META_CTRL_RIGHT_ON}, ++ * {@link #META_META_ON}, {@link #META_META_LEFT_ON}, {@link #META_META_RIGHT_ON}, ++ * {@link #META_SYM_ON}, {@link #META_FUNCTION_ON}. ++ */ ++ public static int getModifierMetaStateMask() { ++ return META_MODIFIER_MASK; ++ } ++ ++ /** ++ * Returns true if this key code is a modifier key. ++ *

++ * For the purposes of this function, {@link #KEYCODE_CAPS_LOCK}, ++ * {@link #KEYCODE_SCROLL_LOCK}, and {@link #KEYCODE_NUM_LOCK} are ++ * not considered modifier keys. Consequently, this function return false ++ * for those keys. ++ *

++ * ++ * @return True if the key code is one of ++ * {@link #KEYCODE_SHIFT_LEFT} {@link #KEYCODE_SHIFT_RIGHT}, ++ * {@link #KEYCODE_ALT_LEFT}, {@link #KEYCODE_ALT_RIGHT}, ++ * {@link #KEYCODE_CTRL_LEFT}, {@link #KEYCODE_CTRL_RIGHT}, ++ * {@link #KEYCODE_META_LEFT}, or {@link #KEYCODE_META_RIGHT}, ++ * {@link #KEYCODE_SYM}, {@link #KEYCODE_NUM}, {@link #KEYCODE_FUNCTION}. ++ */ ++ public static boolean isModifierKey(int keyCode) { ++ switch (keyCode) { ++ case KEYCODE_SHIFT_LEFT: ++ case KEYCODE_SHIFT_RIGHT: ++ case KEYCODE_ALT_LEFT: ++ case KEYCODE_ALT_RIGHT: ++ case KEYCODE_CTRL_LEFT: ++ case KEYCODE_CTRL_RIGHT: ++ case KEYCODE_META_LEFT: ++ case KEYCODE_META_RIGHT: ++ case KEYCODE_SYM: ++ case KEYCODE_NUM: ++ case KEYCODE_FUNCTION: ++ return true; ++ default: ++ return false; ++ } ++ } ++ ++ /** ++ * Normalizes the specified meta state. ++ *

++ * The meta state is normalized such that if either the left or right modifier meta state ++ * bits are set then the result will also include the universal bit for that modifier. ++ *

++ * If the specified meta state contains {@link #META_ALT_LEFT_ON} then ++ * the result will also contain {@link #META_ALT_ON} in addition to {@link #META_ALT_LEFT_ON} ++ * and the other bits that were specified in the input. The same is process is ++ * performed for shift, control and meta. ++ *

++ * If the specified meta state contains synthetic meta states defined by ++ * {@link MetaKeyKeyListener}, then those states are translated here and the original ++ * synthetic meta states are removed from the result. ++ * {@link MetaKeyKeyListener#META_CAP_LOCKED} is translated to {@link #META_CAPS_LOCK_ON}. ++ * {@link MetaKeyKeyListener#META_ALT_LOCKED} is translated to {@link #META_ALT_ON}. ++ * {@link MetaKeyKeyListener#META_SYM_LOCKED} is translated to {@link #META_SYM_ON}. ++ *

++ * Undefined meta state bits are removed. ++ *

++ * ++ * @param metaState The meta state. ++ * @return The normalized meta state. ++ */ ++ public static int normalizeMetaState(int metaState) { ++ if ((metaState & (META_SHIFT_LEFT_ON | META_SHIFT_RIGHT_ON)) != 0) { ++ metaState |= META_SHIFT_ON; ++ } ++ if ((metaState & (META_ALT_LEFT_ON | META_ALT_RIGHT_ON)) != 0) { ++ metaState |= META_ALT_ON; ++ } ++ if ((metaState & (META_CTRL_LEFT_ON | META_CTRL_RIGHT_ON)) != 0) { ++ metaState |= META_CTRL_ON; ++ } ++ if ((metaState & (META_META_LEFT_ON | META_META_RIGHT_ON)) != 0) { ++ metaState |= META_META_ON; ++ } ++ if ((metaState & MetaKeyKeyListener.META_CAP_LOCKED) != 0) { ++ metaState |= META_CAPS_LOCK_ON; ++ } ++ if ((metaState & MetaKeyKeyListener.META_ALT_LOCKED) != 0) { ++ metaState |= META_ALT_ON; ++ } ++ if ((metaState & MetaKeyKeyListener.META_SYM_LOCKED) != 0) { ++ metaState |= META_SYM_ON; ++ } ++ return metaState & META_ALL_MASK; ++ } ++ ++ /** ++ * Returns true if no modifiers keys are pressed according to the specified meta state. ++ *

++ * For the purposes of this function, {@link #KEYCODE_CAPS_LOCK}, ++ * {@link #KEYCODE_SCROLL_LOCK}, and {@link #KEYCODE_NUM_LOCK} are ++ * not considered modifier keys. Consequently, this function ignores ++ * {@link #META_CAPS_LOCK_ON}, {@link #META_SCROLL_LOCK_ON} and {@link #META_NUM_LOCK_ON}. ++ *

++ * The meta state is normalized prior to comparison using {@link #normalizeMetaState(int)}. ++ *

++ * ++ * @param metaState The meta state to consider. ++ * @return True if no modifier keys are pressed. ++ * @see #hasNoModifiers() ++ */ ++ public static boolean metaStateHasNoModifiers(int metaState) { ++ return (normalizeMetaState(metaState) & META_MODIFIER_MASK) == 0; ++ } ++ ++ /** ++ * Returns true if only the specified modifier keys are pressed according to ++ * the specified meta state. Returns false if a different combination of modifier ++ * keys are pressed. ++ *

++ * For the purposes of this function, {@link #KEYCODE_CAPS_LOCK}, ++ * {@link #KEYCODE_SCROLL_LOCK}, and {@link #KEYCODE_NUM_LOCK} are ++ * not considered modifier keys. Consequently, this function ignores ++ * {@link #META_CAPS_LOCK_ON}, {@link #META_SCROLL_LOCK_ON} and {@link #META_NUM_LOCK_ON}. ++ *

++ * If the specified modifier mask includes directional modifiers, such as ++ * {@link #META_SHIFT_LEFT_ON}, then this method ensures that the ++ * modifier is pressed on that side. ++ * If the specified modifier mask includes non-directional modifiers, such as ++ * {@link #META_SHIFT_ON}, then this method ensures that the modifier ++ * is pressed on either side. ++ * If the specified modifier mask includes both directional and non-directional modifiers ++ * for the same type of key, such as {@link #META_SHIFT_ON} and {@link #META_SHIFT_LEFT_ON}, ++ * then this method throws an illegal argument exception. ++ *

++ * ++ * @param metaState The meta state to consider. ++ * @param modifiers The meta state of the modifier keys to check. May be a combination ++ * of modifier meta states as defined by {@link #getModifierMetaStateMask()}. May be 0 to ++ * ensure that no modifier keys are pressed. ++ * @return True if only the specified modifier keys are pressed. ++ * @throws IllegalArgumentException if the modifiers parameter contains invalid modifiers ++ * @see #hasModifiers ++ */ ++ public static boolean metaStateHasModifiers(int metaState, int modifiers) { ++ // Note: For forward compatibility, we allow the parameter to contain meta states ++ // that we do not recognize but we explicitly disallow meta states that ++ // are not valid modifiers. ++ if ((modifiers & META_INVALID_MODIFIER_MASK) != 0) { ++ throw new IllegalArgumentException("modifiers must not contain " ++ + "META_CAPS_LOCK_ON, META_NUM_LOCK_ON, META_SCROLL_LOCK_ON, " ++ + "META_CAP_LOCKED, META_ALT_LOCKED, META_SYM_LOCKED, " ++ + "or META_SELECTING"); ++ } ++ ++ metaState = normalizeMetaState(metaState) & META_MODIFIER_MASK; ++ metaState = metaStateFilterDirectionalModifiers(metaState, modifiers, ++ META_SHIFT_ON, META_SHIFT_LEFT_ON, META_SHIFT_RIGHT_ON); ++ metaState = metaStateFilterDirectionalModifiers(metaState, modifiers, ++ META_ALT_ON, META_ALT_LEFT_ON, META_ALT_RIGHT_ON); ++ metaState = metaStateFilterDirectionalModifiers(metaState, modifiers, ++ META_CTRL_ON, META_CTRL_LEFT_ON, META_CTRL_RIGHT_ON); ++ metaState = metaStateFilterDirectionalModifiers(metaState, modifiers, ++ META_META_ON, META_META_LEFT_ON, META_META_RIGHT_ON); ++ return metaState == modifiers; ++ } ++ ++ private static int metaStateFilterDirectionalModifiers(int metaState, ++ int modifiers, int basic, int left, int right) { ++ final boolean wantBasic = (modifiers & basic) != 0; ++ final int directional = left | right; ++ final boolean wantLeftOrRight = (modifiers & directional) != 0; ++ ++ if (wantBasic) { ++ if (wantLeftOrRight) { ++ throw new IllegalArgumentException("modifiers must not contain " ++ + metaStateToString(basic) + " combined with " ++ + metaStateToString(left) + " or " + metaStateToString(right)); ++ } ++ return metaState & ~directional; ++ } else if (wantLeftOrRight) { ++ return metaState & ~basic; ++ } else { ++ return metaState; ++ } ++ } ++ ++ /** ++ * Returns true if no modifier keys are pressed. ++ *

++ * For the purposes of this function, {@link #KEYCODE_CAPS_LOCK}, ++ * {@link #KEYCODE_SCROLL_LOCK}, and {@link #KEYCODE_NUM_LOCK} are ++ * not considered modifier keys. Consequently, this function ignores ++ * {@link #META_CAPS_LOCK_ON}, {@link #META_SCROLL_LOCK_ON} and {@link #META_NUM_LOCK_ON}. ++ *

++ * The meta state is normalized prior to comparison using {@link #normalizeMetaState(int)}. ++ *

++ * ++ * @return True if no modifier keys are pressed. ++ * @see #metaStateHasNoModifiers ++ */ ++ public final boolean hasNoModifiers() { ++ return metaStateHasNoModifiers(mMetaState); ++ } ++ ++ /** ++ * Returns true if only the specified modifiers keys are pressed. ++ * Returns false if a different combination of modifier keys are pressed. ++ *

++ * For the purposes of this function, {@link #KEYCODE_CAPS_LOCK}, ++ * {@link #KEYCODE_SCROLL_LOCK}, and {@link #KEYCODE_NUM_LOCK} are ++ * not considered modifier keys. Consequently, this function ignores ++ * {@link #META_CAPS_LOCK_ON}, {@link #META_SCROLL_LOCK_ON} and {@link #META_NUM_LOCK_ON}. ++ *

++ * If the specified modifier mask includes directional modifiers, such as ++ * {@link #META_SHIFT_LEFT_ON}, then this method ensures that the ++ * modifier is pressed on that side. ++ * If the specified modifier mask includes non-directional modifiers, such as ++ * {@link #META_SHIFT_ON}, then this method ensures that the modifier ++ * is pressed on either side. ++ * If the specified modifier mask includes both directional and non-directional modifiers ++ * for the same type of key, such as {@link #META_SHIFT_ON} and {@link #META_SHIFT_LEFT_ON}, ++ * then this method throws an illegal argument exception. ++ *

++ * ++ * @param modifiers The meta state of the modifier keys to check. May be a combination ++ * of modifier meta states as defined by {@link #getModifierMetaStateMask()}. May be 0 to ++ * ensure that no modifier keys are pressed. ++ * @return True if only the specified modifier keys are pressed. ++ * @throws IllegalArgumentException if the modifiers parameter contains invalid modifiers ++ * @see #metaStateHasModifiers ++ */ ++ public final boolean hasModifiers(int modifiers) { ++ return metaStateHasModifiers(mMetaState, modifiers); ++ } ++ ++ /** ++ *

Returns the pressed state of the ALT meta key.

++ * ++ * @return true if the ALT key is pressed, false otherwise ++ * ++ * @see #KEYCODE_ALT_LEFT ++ * @see #KEYCODE_ALT_RIGHT ++ * @see #META_ALT_ON ++ */ ++ public final boolean isAltPressed() { ++ return (mMetaState & META_ALT_ON) != 0; ++ } ++ ++ /** ++ *

Returns the pressed state of the SHIFT meta key.

++ * ++ * @return true if the SHIFT key is pressed, false otherwise ++ * ++ * @see #KEYCODE_SHIFT_LEFT ++ * @see #KEYCODE_SHIFT_RIGHT ++ * @see #META_SHIFT_ON ++ */ ++ public final boolean isShiftPressed() { ++ return (mMetaState & META_SHIFT_ON) != 0; ++ } ++ ++ /** ++ *

Returns the pressed state of the SYM meta key.

++ * ++ * @return true if the SYM key is pressed, false otherwise ++ * ++ * @see #KEYCODE_SYM ++ * @see #META_SYM_ON ++ */ ++ public final boolean isSymPressed() { ++ return (mMetaState & META_SYM_ON) != 0; ++ } ++ ++ /** ++ *

Returns the pressed state of the CTRL meta key.

++ * ++ * @return true if the CTRL key is pressed, false otherwise ++ * ++ * @see #KEYCODE_CTRL_LEFT ++ * @see #KEYCODE_CTRL_RIGHT ++ * @see #META_CTRL_ON ++ */ ++ public final boolean isCtrlPressed() { ++ return (mMetaState & META_CTRL_ON) != 0; ++ } ++ ++ /** ++ *

Returns the pressed state of the META meta key.

++ * ++ * @return true if the META key is pressed, false otherwise ++ * ++ * @see #KEYCODE_META_LEFT ++ * @see #KEYCODE_META_RIGHT ++ * @see #META_META_ON ++ */ ++ public final boolean isMetaPressed() { ++ return (mMetaState & META_META_ON) != 0; ++ } ++ ++ /** ++ *

Returns the pressed state of the FUNCTION meta key.

++ * ++ * @return true if the FUNCTION key is pressed, false otherwise ++ * ++ * @see #KEYCODE_FUNCTION ++ * @see #META_FUNCTION_ON ++ */ ++ public final boolean isFunctionPressed() { ++ return (mMetaState & META_FUNCTION_ON) != 0; ++ } ++ ++ /** ++ *

Returns the locked state of the CAPS LOCK meta key.

++ * ++ * @return true if the CAPS LOCK key is on, false otherwise ++ * ++ * @see #KEYCODE_CAPS_LOCK ++ * @see #META_CAPS_LOCK_ON ++ */ ++ public final boolean isCapsLockOn() { ++ return (mMetaState & META_CAPS_LOCK_ON) != 0; ++ } ++ ++ /** ++ *

Returns the locked state of the NUM LOCK meta key.

++ * ++ * @return true if the NUM LOCK key is on, false otherwise ++ * ++ * @see #KEYCODE_NUM_LOCK ++ * @see #META_NUM_LOCK_ON ++ */ ++ public final boolean isNumLockOn() { ++ return (mMetaState & META_NUM_LOCK_ON) != 0; ++ } ++ ++ /** ++ *

Returns the locked state of the SCROLL LOCK meta key.

++ * ++ * @return true if the SCROLL LOCK key is on, false otherwise ++ * ++ * @see #KEYCODE_SCROLL_LOCK ++ * @see #META_SCROLL_LOCK_ON ++ */ ++ public final boolean isScrollLockOn() { ++ return (mMetaState & META_SCROLL_LOCK_ON) != 0; ++ } ++ ++ /** ++ * Retrieve the action of this key event. May be either ++ * {@link #ACTION_DOWN}, {@link #ACTION_UP}, or {@link #ACTION_MULTIPLE}. ++ * ++ * @return The event action: ACTION_DOWN, ACTION_UP, or ACTION_MULTIPLE. ++ */ ++ public final int getAction() { ++ return mAction; ++ } ++ ++ /** ++ * For {@link #ACTION_UP} events, indicates that the event has been ++ * canceled as per {@link #FLAG_CANCELED}. ++ */ ++ public final boolean isCanceled() { ++ return (mFlags&FLAG_CANCELED) != 0; ++ } ++ ++ /** ++ * Set {@link #FLAG_CANCELED} flag for the key event. ++ * ++ * @hide ++ */ ++ @Override ++ public final void cancel() { ++ mFlags |= FLAG_CANCELED; ++ } ++ ++ /** ++ * Call this during {@link Callback#onKeyDown} to have the system track ++ * the key through its final up (possibly including a long press). Note ++ * that only one key can be tracked at a time -- if another key down ++ * event is received while a previous one is being tracked, tracking is ++ * stopped on the previous event. ++ */ ++ public final void startTracking() { ++ mFlags |= FLAG_START_TRACKING; ++ } ++ ++ /** ++ * For {@link #ACTION_UP} events, indicates that the event is still being ++ * tracked from its initial down event as per ++ * {@link #FLAG_TRACKING}. ++ */ ++ public final boolean isTracking() { ++ return (mFlags&FLAG_TRACKING) != 0; ++ } ++ ++ /** ++ * For {@link #ACTION_DOWN} events, indicates that the event has been ++ * canceled as per {@link #FLAG_LONG_PRESS}. ++ */ ++ public final boolean isLongPress() { ++ return (mFlags&FLAG_LONG_PRESS) != 0; ++ } ++ ++ /** ++ * Retrieve the key code of the key event. This is the physical key that ++ * was pressed, not the Unicode character. ++ * ++ * @return The key code of the event. ++ */ ++ public final int getKeyCode() { ++ return mKeyCode; ++ } ++ ++ /** ++ * For the special case of a {@link #ACTION_MULTIPLE} event with key ++ * code of {@link #KEYCODE_UNKNOWN}, this is a raw string of characters ++ * associated with the event. In all other cases it is null. ++ * ++ * @return Returns a String of 1 or more characters associated with ++ * the event. ++ * ++ * @deprecated no longer used by the input system. ++ */ ++ @Deprecated ++ public final String getCharacters() { ++ return mCharacters; ++ } ++ ++ /** ++ * Retrieve the hardware key id of this key event. These values are not ++ * reliable and vary from device to device. ++ * ++ * {@more} ++ * Mostly this is here for debugging purposes. ++ */ ++ public final int getScanCode() { ++ return mScanCode; ++ } ++ ++ /** ++ * Retrieve the repeat count of the event. For key down events, ++ * this is the number of times the key has repeated with the first ++ * down starting at 0 and counting up from there. For key up events, ++ * this is always equal to zero. For multiple key events, ++ * this is the number of down/up pairs that have occurred. ++ * ++ * @return The number of times the key has repeated. ++ */ ++ public final int getRepeatCount() { ++ return mRepeatCount; ++ } ++ ++ /** ++ * Modifies the down time and the event time of the event. ++ * ++ * @param downTime The new down time (in {@link android.os.SystemClock#uptimeMillis}) of the ++ * event. ++ * @param eventTime The new event time (in {@link android.os.SystemClock#uptimeMillis}) of the ++ * event. ++ * @hide ++ */ ++ public final void setTime(long downTime, long eventTime) { ++ mDownTime = downTime; ++ mEventTime = eventTime; ++ } ++ ++ /** ++ * Retrieve the time of the most recent key down event, ++ * in the {@link android.os.SystemClock#uptimeMillis} time base. If this ++ * is a down event, this will be the same as {@link #getEventTime()}. ++ * Note that when chording keys, this value is the down time of the ++ * most recently pressed key, which may not be the same physical ++ * key of this event. ++ * ++ * @return Returns the most recent key down time, in the ++ * {@link android.os.SystemClock#uptimeMillis} time base ++ */ ++ public final long getDownTime() { ++ return mDownTime; ++ } ++ ++ /** ++ * Retrieve the time this event occurred, ++ * in the {@link android.os.SystemClock#uptimeMillis} time base. ++ * ++ * @return Returns the time this event occurred, ++ * in the {@link android.os.SystemClock#uptimeMillis} time base. ++ */ ++ @Override ++ public final long getEventTime() { ++ return mEventTime; ++ } ++ ++ /** ++ * Retrieve the time this event occurred, ++ * in the {@link android.os.SystemClock#uptimeMillis} time base but with ++ * nanosecond (instead of millisecond) precision. ++ *

++ * The value is in nanosecond precision but it may not have nanosecond accuracy. ++ *

++ * ++ * @return Returns the time this event occurred, ++ * in the {@link android.os.SystemClock#uptimeMillis} time base but with ++ * nanosecond (instead of millisecond) precision. ++ * ++ * @hide ++ */ ++ @Override ++ public final long getEventTimeNano() { ++ return mEventTime * 1000000L; ++ } ++ ++ /** ++ * Renamed to {@link #getDeviceId}. ++ * ++ * @hide ++ * @deprecated use {@link #getDeviceId()} instead. ++ */ ++ @Deprecated ++ public final int getKeyboardDevice() { ++ return mDeviceId; ++ } ++ ++ /** ++ * Gets the {@link KeyCharacterMap} associated with the keyboard device. ++ * ++ * @return The associated key character map. ++ * @throws {@link KeyCharacterMap.UnavailableException} if the key character map ++ * could not be loaded because it was malformed or the default key character map ++ * is missing from the system. ++ * ++ * @see KeyCharacterMap#load ++ */ ++ public final KeyCharacterMap getKeyCharacterMap() { ++ return KeyCharacterMap.load(mDeviceId); ++ } ++ ++ /** ++ * Gets the primary character for this key. ++ * In other words, the label that is physically printed on it. ++ * ++ * @return The display label character, or 0 if none (eg. for non-printing keys). ++ */ ++ public char getDisplayLabel() { ++ return getKeyCharacterMap().getDisplayLabel(mKeyCode); ++ } ++ ++ /** ++ * Gets the Unicode character generated by the specified key and meta ++ * key state combination. ++ *

++ * Returns the Unicode character that the specified key would produce ++ * when the specified meta bits (see {@link MetaKeyKeyListener}) ++ * were active. ++ *

++ * Returns 0 if the key is not one that is used to type Unicode ++ * characters. ++ *

++ * If the return value has bit {@link KeyCharacterMap#COMBINING_ACCENT} set, the ++ * key is a "dead key" that should be combined with another to ++ * actually produce a character -- see {@link KeyCharacterMap#getDeadChar} -- ++ * after masking with {@link KeyCharacterMap#COMBINING_ACCENT_MASK}. ++ *

++ * ++ * @return The associated character or combining accent, or 0 if none. ++ */ ++ public int getUnicodeChar() { ++ return getUnicodeChar(mMetaState); ++ } ++ ++ /** ++ * Gets the Unicode character generated by the specified key and meta ++ * key state combination. ++ *

++ * Returns the Unicode character that the specified key would produce ++ * when the specified meta bits (see {@link MetaKeyKeyListener}) ++ * were active. ++ *

++ * Returns 0 if the key is not one that is used to type Unicode ++ * characters. ++ *

++ * If the return value has bit {@link KeyCharacterMap#COMBINING_ACCENT} set, the ++ * key is a "dead key" that should be combined with another to ++ * actually produce a character -- see {@link KeyCharacterMap#getDeadChar} -- ++ * after masking with {@link KeyCharacterMap#COMBINING_ACCENT_MASK}. ++ *

++ * ++ * @param metaState The meta key modifier state. ++ * @return The associated character or combining accent, or 0 if none. ++ */ ++ public int getUnicodeChar(int metaState) { ++ return getKeyCharacterMap().get(mKeyCode, metaState); ++ } ++ ++ /** ++ * Get the character conversion data for a given key code. ++ * ++ * @param results A {@link KeyCharacterMap.KeyData} instance that will be ++ * filled with the results. ++ * @return True if the key was mapped. If the key was not mapped, results is not modified. ++ * ++ * @deprecated instead use {@link #getDisplayLabel()}, ++ * {@link #getNumber()} or {@link #getUnicodeChar(int)}. ++ */ ++ @Deprecated ++ public boolean getKeyData(KeyData results) { ++ return getKeyCharacterMap().getKeyData(mKeyCode, results); ++ } ++ ++ /** ++ * Gets the first character in the character array that can be generated ++ * by the specified key code. ++ *

++ * This is a convenience function that returns the same value as ++ * {@link #getMatch(char[],int) getMatch(chars, 0)}. ++ *

++ * ++ * @param chars The array of matching characters to consider. ++ * @return The matching associated character, or 0 if none. ++ */ ++ public char getMatch(char[] chars) { ++ return getMatch(chars, 0); ++ } ++ ++ /** ++ * Gets the first character in the character array that can be generated ++ * by the specified key code. If there are multiple choices, prefers ++ * the one that would be generated with the specified meta key modifier state. ++ * ++ * @param chars The array of matching characters to consider. ++ * @param metaState The preferred meta key modifier state. ++ * @return The matching associated character, or 0 if none. ++ */ ++ public char getMatch(char[] chars, int metaState) { ++ return getKeyCharacterMap().getMatch(mKeyCode, chars, metaState); ++ } ++ ++ /** ++ * Gets the number or symbol associated with the key. ++ *

++ * The character value is returned, not the numeric value. ++ * If the key is not a number, but is a symbol, the symbol is retuned. ++ *

++ * This method is intended to to support dial pads and other numeric or ++ * symbolic entry on keyboards where certain keys serve dual function ++ * as alphabetic and symbolic keys. This method returns the number ++ * or symbol associated with the key independent of whether the user ++ * has pressed the required modifier. ++ *

++ * For example, on one particular keyboard the keys on the top QWERTY row generate ++ * numbers when ALT is pressed such that ALT-Q maps to '1'. So for that keyboard ++ * when {@link #getNumber} is called with {@link KeyEvent#KEYCODE_Q} it returns '1' ++ * so that the user can type numbers without pressing ALT when it makes sense. ++ *

++ * ++ * @return The associated numeric or symbolic character, or 0 if none. ++ */ ++ public char getNumber() { ++ return getKeyCharacterMap().getNumber(mKeyCode); ++ } ++ ++ /** ++ * Returns true if this key produces a glyph. ++ * ++ * @return True if the key is a printing key. ++ */ ++ public boolean isPrintingKey() { ++ return getKeyCharacterMap().isPrintingKey(mKeyCode); ++ } ++ ++ /** ++ * @deprecated Use {@link #dispatch(Callback, DispatcherState, Object)} instead. ++ */ ++ @Deprecated ++ public final boolean dispatch(Callback receiver) { ++ return dispatch(receiver, null, null); ++ } ++ ++ /** ++ * Deliver this key event to a {@link Callback} interface. If this is ++ * an ACTION_MULTIPLE event and it is not handled, then an attempt will ++ * be made to deliver a single normal event. ++ * ++ * @param receiver The Callback that will be given the event. ++ * @param state State information retained across events. ++ * @param target The target of the dispatch, for use in tracking. ++ * ++ * @return The return value from the Callback method that was called. ++ */ ++ public final boolean dispatch(Callback receiver, DispatcherState state, ++ Object target) { ++ switch (mAction) { ++ case ACTION_DOWN: { ++ mFlags &= ~FLAG_START_TRACKING; ++ if (DEBUG) Log.v(TAG, "Key down to " + target + " in " + state ++ + ": " + this); ++ boolean res = receiver.onKeyDown(mKeyCode, this); ++ if (state != null) { ++ if (res && mRepeatCount == 0 && (mFlags&FLAG_START_TRACKING) != 0) { ++ if (DEBUG) Log.v(TAG, " Start tracking!"); ++ state.startTracking(this, target); ++ } else if (isLongPress() && state.isTracking(this)) { ++ try { ++ if (receiver.onKeyLongPress(mKeyCode, this)) { ++ if (DEBUG) Log.v(TAG, " Clear from long press!"); ++ state.performedLongPress(this); ++ res = true; ++ } ++ } catch (AbstractMethodError e) { ++ } ++ } ++ } ++ return res; ++ } ++ case ACTION_UP: ++ if (DEBUG) Log.v(TAG, "Key up to " + target + " in " + state ++ + ": " + this); ++ if (state != null) { ++ state.handleUpEvent(this); ++ } ++ return receiver.onKeyUp(mKeyCode, this); ++ case ACTION_MULTIPLE: ++ final int count = mRepeatCount; ++ final int code = mKeyCode; ++ if (receiver.onKeyMultiple(code, count, this)) { ++ return true; ++ } ++ if (code != KeyEvent.KEYCODE_UNKNOWN) { ++ mAction = ACTION_DOWN; ++ mRepeatCount = 0; ++ boolean handled = receiver.onKeyDown(code, this); ++ if (handled) { ++ mAction = ACTION_UP; ++ receiver.onKeyUp(code, this); ++ } ++ mAction = ACTION_MULTIPLE; ++ mRepeatCount = count; ++ return handled; ++ } ++ return false; ++ } ++ return false; ++ } ++ ++ /** ++ * Use with {@link KeyEvent#dispatch(Callback, DispatcherState, Object)} ++ * for more advanced key dispatching, such as long presses. ++ */ ++ public static class DispatcherState { ++ int mDownKeyCode; ++ Object mDownTarget; ++ SparseIntArray mActiveLongPresses = new SparseIntArray(); ++ ++ /** ++ * Reset back to initial state. ++ */ ++ public void reset() { ++ if (DEBUG) Log.v(TAG, "Reset: " + this); ++ mDownKeyCode = 0; ++ mDownTarget = null; ++ mActiveLongPresses.clear(); ++ } ++ ++ /** ++ * Stop any tracking associated with this target. ++ */ ++ public void reset(Object target) { ++ if (mDownTarget == target) { ++ if (DEBUG) Log.v(TAG, "Reset in " + target + ": " + this); ++ mDownKeyCode = 0; ++ mDownTarget = null; ++ } ++ } ++ ++ /** ++ * Start tracking the key code associated with the given event. This ++ * can only be called on a key down. It will allow you to see any ++ * long press associated with the key, and will result in ++ * {@link KeyEvent#isTracking} return true on the long press and up ++ * events. ++ * ++ *

This is only needed if you are directly dispatching events, rather ++ * than handling them in {@link Callback#onKeyDown}. ++ */ ++ public void startTracking(KeyEvent event, Object target) { ++ if (event.getAction() != ACTION_DOWN) { ++ throw new IllegalArgumentException( ++ "Can only start tracking on a down event"); ++ } ++ if (DEBUG) Log.v(TAG, "Start trackingt in " + target + ": " + this); ++ mDownKeyCode = event.getKeyCode(); ++ mDownTarget = target; ++ } ++ ++ /** ++ * Return true if the key event is for a key code that is currently ++ * being tracked by the dispatcher. ++ */ ++ public boolean isTracking(KeyEvent event) { ++ return mDownKeyCode == event.getKeyCode(); ++ } ++ ++ /** ++ * Keep track of the given event's key code as having performed an ++ * action with a long press, so no action should occur on the up. ++ *

This is only needed if you are directly dispatching events, rather ++ * than handling them in {@link Callback#onKeyLongPress}. ++ */ ++ public void performedLongPress(KeyEvent event) { ++ mActiveLongPresses.put(event.getKeyCode(), 1); ++ } ++ ++ /** ++ * Handle key up event to stop tracking. This resets the dispatcher state, ++ * and updates the key event state based on it. ++ *

This is only needed if you are directly dispatching events, rather ++ * than handling them in {@link Callback#onKeyUp}. ++ */ ++ public void handleUpEvent(KeyEvent event) { ++ final int keyCode = event.getKeyCode(); ++ if (DEBUG) Log.v(TAG, "Handle key up " + event + ": " + this); ++ int index = mActiveLongPresses.indexOfKey(keyCode); ++ if (index >= 0) { ++ if (DEBUG) Log.v(TAG, " Index: " + index); ++ event.mFlags |= FLAG_CANCELED | FLAG_CANCELED_LONG_PRESS; ++ mActiveLongPresses.removeAt(index); ++ } ++ if (mDownKeyCode == keyCode) { ++ if (DEBUG) Log.v(TAG, " Tracking!"); ++ event.mFlags |= FLAG_TRACKING; ++ mDownKeyCode = 0; ++ mDownTarget = null; ++ } ++ } ++ } ++ ++ @Override ++ public String toString() { ++ StringBuilder msg = new StringBuilder(); ++ msg.append("KeyEvent { action=").append(actionToString(mAction)); ++ msg.append(", keyCode=").append(keyCodeToString(mKeyCode)); ++ msg.append(", scanCode=").append(mScanCode); ++ if (mCharacters != null) { ++ msg.append(", characters=\"").append(mCharacters).append("\""); ++ } ++ msg.append(", metaState=").append(metaStateToString(mMetaState)); ++ msg.append(", flags=0x").append(Integer.toHexString(mFlags)); ++ msg.append(", repeatCount=").append(mRepeatCount); ++ msg.append(", eventTime=").append(mEventTime); ++ msg.append(", downTime=").append(mDownTime); ++ msg.append(", deviceId=").append(mDeviceId); ++ msg.append(", source=0x").append(Integer.toHexString(mSource)); ++ msg.append(", displayId=").append(mDisplayId); ++ msg.append(" }"); ++ return msg.toString(); ++ } ++ ++ /** ++ * Returns a string that represents the symbolic name of the specified action ++ * such as "ACTION_DOWN", or an equivalent numeric constant such as "35" if unknown. ++ * ++ * @param action The action. ++ * @return The symbolic name of the specified action. ++ * @hide ++ */ ++ @TestApi ++ public static String actionToString(int action) { ++ switch (action) { ++ case ACTION_DOWN: ++ return "ACTION_DOWN"; ++ case ACTION_UP: ++ return "ACTION_UP"; ++ case ACTION_MULTIPLE: ++ return "ACTION_MULTIPLE"; ++ default: ++ return Integer.toString(action); ++ } ++ } ++ ++ /** ++ * Returns a string that represents the symbolic name of the specified keycode ++ * such as "KEYCODE_A", "KEYCODE_DPAD_UP", or an equivalent numeric constant ++ * such as "1001" if unknown. ++ * ++ * This function is intended to be used mostly for debugging, logging, and testing. It is not ++ * locale-specific and is not intended to be used in a user-facing manner. ++ * ++ * @param keyCode The key code. ++ * @return The symbolic name of the specified keycode. ++ * ++ * @see KeyCharacterMap#getDisplayLabel ++ */ ++ public static String keyCodeToString(int keyCode) { ++ String symbolicName = nativeKeyCodeToString(keyCode); ++ return symbolicName != null ? LABEL_PREFIX + symbolicName : Integer.toString(keyCode); ++ } ++ ++ /** ++ * Gets a keycode by its symbolic name such as "KEYCODE_A" or an equivalent ++ * numeric constant such as "29". For symbolic names, ++ * starting in {@link android.os.Build.VERSION_CODES#Q} the prefix "KEYCODE_" is optional. ++ * ++ * @param symbolicName The symbolic name of the keycode. ++ * @return The keycode or {@link #KEYCODE_UNKNOWN} if not found. ++ * @see #keyCodeToString(int) ++ */ ++ public static int keyCodeFromString(@NonNull String symbolicName) { ++ try { ++ int keyCode = Integer.parseInt(symbolicName); ++ if (keyCodeIsValid(keyCode)) { ++ return keyCode; ++ } ++ } catch (NumberFormatException ex) { ++ } ++ ++ if (symbolicName.startsWith(LABEL_PREFIX)) { ++ symbolicName = symbolicName.substring(LABEL_PREFIX.length()); ++ } ++ int keyCode = nativeKeyCodeFromString(symbolicName); ++ if (keyCodeIsValid(keyCode)) { ++ return keyCode; ++ } ++ return KEYCODE_UNKNOWN; ++ } ++ ++ private static boolean keyCodeIsValid(int keyCode) { ++ return keyCode >= KEYCODE_UNKNOWN && keyCode <= LAST_KEYCODE; ++ } ++ ++ /** ++ * Returns a string that represents the symbolic name of the specified combined meta ++ * key modifier state flags such as "0", "META_SHIFT_ON", ++ * "META_ALT_ON|META_SHIFT_ON" or an equivalent numeric constant such as "0x10000000" ++ * if unknown. ++ * ++ * @param metaState The meta state. ++ * @return The symbolic name of the specified combined meta state flags. ++ * @hide ++ */ ++ public static String metaStateToString(int metaState) { ++ if (metaState == 0) { ++ return "0"; ++ } ++ StringBuilder result = null; ++ int i = 0; ++ while (metaState != 0) { ++ final boolean isSet = (metaState & 1) != 0; ++ metaState >>>= 1; // unsigned shift! ++ if (isSet) { ++ final String name = META_SYMBOLIC_NAMES[i]; ++ if (result == null) { ++ if (metaState == 0) { ++ return name; ++ } ++ result = new StringBuilder(name); ++ } else { ++ result.append('|'); ++ result.append(name); ++ } ++ } ++ i += 1; ++ } ++ return result.toString(); ++ } ++ ++ public static final @android.annotation.NonNull Parcelable.Creator CREATOR ++ = new Parcelable.Creator() { ++ @Override ++ public KeyEvent createFromParcel(Parcel in) { ++ in.readInt(); // skip token, we already know this is a KeyEvent ++ return KeyEvent.createFromParcelBody(in); ++ } ++ ++ @Override ++ public KeyEvent[] newArray(int size) { ++ return new KeyEvent[size]; ++ } ++ }; ++ ++ /** @hide */ ++ public static KeyEvent createFromParcelBody(Parcel in) { ++ return new KeyEvent(in); ++ } ++ ++ private KeyEvent(Parcel in) { ++ mId = in.readInt(); ++ mDeviceId = in.readInt(); ++ mSource = in.readInt(); ++ mDisplayId = in.readInt(); ++ mHmac = in.createByteArray(); ++ mAction = in.readInt(); ++ mKeyCode = in.readInt(); ++ mRepeatCount = in.readInt(); ++ mMetaState = in.readInt(); ++ mScanCode = in.readInt(); ++ mFlags = in.readInt(); ++ mDownTime = in.readLong(); ++ mEventTime = in.readLong(); ++ mCharacters = in.readString(); ++ } ++ ++ @Override ++ public void writeToParcel(Parcel out, int flags) { ++ out.writeInt(PARCEL_TOKEN_KEY_EVENT); ++ ++ out.writeInt(mId); ++ out.writeInt(mDeviceId); ++ out.writeInt(mSource); ++ out.writeInt(mDisplayId); ++ out.writeByteArray(mHmac); ++ out.writeInt(mAction); ++ out.writeInt(mKeyCode); ++ out.writeInt(mRepeatCount); ++ out.writeInt(mMetaState); ++ out.writeInt(mScanCode); ++ out.writeInt(mFlags); ++ out.writeLong(mDownTime); ++ out.writeLong(mEventTime); ++ out.writeString(mCharacters); ++ } ++} +diff --git a/services/core/java/com/android/server/StorageManagerService.java.orig b/services/core/java/com/android/server/StorageManagerService.java.orig +new file mode 100644 +index 00000000000..498c52a5aa6 +--- /dev/null ++++ b/services/core/java/com/android/server/StorageManagerService.java.orig +@@ -0,0 +1,4790 @@ ++/* ++ * Copyright (C) 2007 The Android Open Source Project ++ * ++ * Licensed under the Apache License, Version 2.0 (the "License"); ++ * you may not use this file except in compliance with the License. ++ * You may obtain a copy of the License at ++ * ++ * http://www.apache.org/licenses/LICENSE-2.0 ++ * ++ * Unless required by applicable law or agreed to in writing, software ++ * distributed under the License is distributed on an "AS IS" BASIS, ++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ * See the License for the specific language governing permissions and ++ * limitations under the License. ++ */ ++ ++package com.android.server; ++ ++import static android.Manifest.permission.ACCESS_MTP; ++import static android.Manifest.permission.INSTALL_PACKAGES; ++import static android.Manifest.permission.READ_EXTERNAL_STORAGE; ++import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; ++import static android.Manifest.permission.WRITE_MEDIA_STORAGE; ++import static android.app.ActivityManager.PROCESS_STATE_NONEXISTENT; ++import static android.app.AppOpsManager.MODE_ALLOWED; ++import static android.app.AppOpsManager.OP_LEGACY_STORAGE; ++import static android.app.AppOpsManager.OP_MANAGE_EXTERNAL_STORAGE; ++import static android.app.AppOpsManager.OP_READ_EXTERNAL_STORAGE; ++import static android.app.AppOpsManager.OP_REQUEST_INSTALL_PACKAGES; ++import static android.app.AppOpsManager.OP_WRITE_EXTERNAL_STORAGE; ++import static android.content.pm.PackageManager.MATCH_ANY_USER; ++import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE; ++import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE; ++import static android.content.pm.PackageManager.MATCH_UNINSTALLED_PACKAGES; ++import static android.content.pm.PackageManager.PERMISSION_GRANTED; ++import static android.os.ParcelFileDescriptor.MODE_READ_WRITE; ++import static android.os.storage.OnObbStateChangeListener.ERROR_ALREADY_MOUNTED; ++import static android.os.storage.OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT; ++import static android.os.storage.OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT; ++import static android.os.storage.OnObbStateChangeListener.ERROR_INTERNAL; ++import static android.os.storage.OnObbStateChangeListener.ERROR_NOT_MOUNTED; ++import static android.os.storage.OnObbStateChangeListener.ERROR_PERMISSION_DENIED; ++import static android.os.storage.OnObbStateChangeListener.MOUNTED; ++import static android.os.storage.OnObbStateChangeListener.UNMOUNTED; ++import static android.os.storage.StorageManager.PROP_FORCED_SCOPED_STORAGE_WHITELIST; ++import static android.os.storage.StorageManager.PROP_FUSE; ++import static android.os.storage.StorageManager.PROP_SETTINGS_FUSE; ++ ++import static com.android.internal.util.XmlUtils.readIntAttribute; ++import static com.android.internal.util.XmlUtils.readLongAttribute; ++import static com.android.internal.util.XmlUtils.readStringAttribute; ++import static com.android.internal.util.XmlUtils.writeIntAttribute; ++import static com.android.internal.util.XmlUtils.writeLongAttribute; ++import static com.android.internal.util.XmlUtils.writeStringAttribute; ++ ++import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; ++import static org.xmlpull.v1.XmlPullParser.START_TAG; ++ ++import android.Manifest; ++import android.annotation.Nullable; ++import android.app.ActivityManager; ++import android.app.ActivityManagerInternal; ++import android.app.AppOpsManager; ++import android.app.IActivityManager; ++import android.app.KeyguardManager; ++import android.app.admin.SecurityLog; ++import android.app.usage.StorageStatsManager; ++import android.content.BroadcastReceiver; ++import android.content.ContentResolver; ++import android.content.Context; ++import android.content.Intent; ++import android.content.IntentFilter; ++import android.content.pm.ApplicationInfo; ++import android.content.pm.IPackageManager; ++import android.content.pm.IPackageMoveObserver; ++import android.content.pm.PackageManager; ++import android.content.pm.PackageManagerInternal; ++import android.content.pm.ProviderInfo; ++import android.content.pm.UserInfo; ++import android.content.res.Configuration; ++import android.content.res.ObbInfo; ++import android.database.ContentObserver; ++import android.net.Uri; ++import android.os.Binder; ++import android.os.DropBoxManager; ++import android.os.Environment; ++import android.os.Handler; ++import android.os.HandlerThread; ++import android.os.IBinder; ++import android.os.IStoraged; ++import android.os.IVold; ++import android.os.IVoldListener; ++import android.os.IVoldMountCallback; ++import android.os.IVoldTaskListener; ++import android.os.Looper; ++import android.os.Message; ++import android.os.ParcelFileDescriptor; ++import android.os.ParcelableException; ++import android.os.PersistableBundle; ++import android.os.PowerManager; ++import android.os.Process; ++import android.os.RemoteCallbackList; ++import android.os.RemoteException; ++import android.os.ServiceManager; ++import android.os.ServiceSpecificException; ++import android.os.SystemClock; ++import android.os.SystemProperties; ++import android.os.UserHandle; ++import android.os.UserManager; ++import android.os.UserManagerInternal; ++import android.os.storage.DiskInfo; ++import android.os.storage.IObbActionListener; ++import android.os.storage.IStorageEventListener; ++import android.os.storage.IStorageManager; ++import android.os.storage.IStorageShutdownObserver; ++import android.os.storage.OnObbStateChangeListener; ++import android.os.storage.StorageManager; ++import android.os.storage.StorageManagerInternal; ++import android.os.storage.StorageVolume; ++import android.os.storage.VolumeInfo; ++import android.os.storage.VolumeRecord; ++import android.provider.DeviceConfig; ++import android.provider.DocumentsContract; ++import android.provider.Downloads; ++import android.provider.MediaStore; ++import android.provider.Settings; ++import android.sysprop.VoldProperties; ++import android.text.TextUtils; ++import android.text.format.DateUtils; ++import android.util.ArrayMap; ++import android.util.ArraySet; ++import android.util.AtomicFile; ++import android.util.DataUnit; ++import android.util.FeatureFlagUtils; ++import android.util.Log; ++import android.util.Pair; ++import android.util.Slog; ++import android.util.TimeUtils; ++import android.util.Xml; ++ ++import com.android.internal.annotations.GuardedBy; ++import com.android.internal.app.IAppOpsCallback; ++import com.android.internal.app.IAppOpsService; ++import com.android.internal.content.PackageMonitor; ++import com.android.internal.os.AppFuseMount; ++import com.android.internal.os.BackgroundThread; ++import com.android.internal.os.FuseUnavailableMountException; ++import com.android.internal.os.SomeArgs; ++import com.android.internal.os.Zygote; ++import com.android.internal.util.ArrayUtils; ++import com.android.internal.util.CollectionUtils; ++import com.android.internal.util.DumpUtils; ++import com.android.internal.util.FastXmlSerializer; ++import com.android.internal.util.HexDump; ++import com.android.internal.util.IndentingPrintWriter; ++import com.android.internal.util.Preconditions; ++import com.android.internal.widget.LockPatternUtils; ++import com.android.server.pm.Installer; ++import com.android.server.storage.AppFuseBridge; ++import com.android.server.storage.StorageSessionController; ++import com.android.server.storage.StorageSessionController.ExternalStorageServiceException; ++import com.android.server.wm.ActivityTaskManagerInternal; ++import com.android.server.wm.ActivityTaskManagerInternal.ScreenObserver; ++ ++import libcore.io.IoUtils; ++import libcore.util.EmptyArray; ++ ++import org.xmlpull.v1.XmlPullParser; ++import org.xmlpull.v1.XmlPullParserException; ++import org.xmlpull.v1.XmlSerializer; ++ ++import java.io.File; ++import java.io.FileDescriptor; ++import java.io.FileInputStream; ++import java.io.FileNotFoundException; ++import java.io.FileOutputStream; ++import java.io.IOException; ++import java.io.PrintWriter; ++import java.math.BigInteger; ++import java.nio.charset.StandardCharsets; ++import java.security.GeneralSecurityException; ++import java.security.spec.KeySpec; ++import java.util.ArrayList; ++import java.util.Arrays; ++import java.util.HashMap; ++import java.util.Iterator; ++import java.util.LinkedList; ++import java.util.List; ++import java.util.Locale; ++import java.util.Map; ++import java.util.Map.Entry; ++import java.util.Objects; ++import java.util.Set; ++import java.util.concurrent.CopyOnWriteArrayList; ++import java.util.concurrent.CountDownLatch; ++import java.util.concurrent.TimeUnit; ++import java.util.concurrent.TimeoutException; ++import java.util.regex.Matcher; ++import java.util.regex.Pattern; ++ ++import javax.crypto.SecretKey; ++import javax.crypto.SecretKeyFactory; ++import javax.crypto.spec.PBEKeySpec; ++ ++/** ++ * Service responsible for various storage media. Connects to {@code vold} to ++ * watch for and manage dynamically added storage, such as SD cards and USB mass ++ * storage. Also decides how storage should be presented to users on the device. ++ */ ++class StorageManagerService extends IStorageManager.Stub ++ implements Watchdog.Monitor, ScreenObserver { ++ ++ // Static direct instance pointer for the tightly-coupled idle service to use ++ static StorageManagerService sSelf = null; ++ ++ /* Read during boot to decide whether to enable zram when available */ ++ private static final String ZRAM_ENABLED_PROPERTY = ++ "persist.sys.zram_enabled"; ++ ++ private static final boolean ENABLE_ISOLATED_STORAGE = StorageManager.hasIsolatedStorage(); ++ ++ // A system property to control if obb app data isolation is enabled in vold. ++ private static final String ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY = ++ "persist.sys.vold_app_data_isolation_enabled"; ++ ++ // How long we wait to reset storage, if we failed to call onMount on the ++ // external storage service. ++ public static final int FAILED_MOUNT_RESET_TIMEOUT_SECONDS = 10; ++ /** ++ * If {@code 1}, enables the isolated storage feature. If {@code -1}, ++ * disables the isolated storage feature. If {@code 0}, uses the default ++ * value from the build system. ++ */ ++ private static final String ISOLATED_STORAGE_ENABLED = "isolated_storage_enabled"; ++ ++ /** ++ * If {@code 1}, enables FuseDaemon to intercept file system ops. If {@code -1}, ++ * disables FuseDaemon. If {@code 0}, uses the default value from the build system. ++ */ ++ private static final String FUSE_ENABLED = "fuse_enabled"; ++ private static final boolean DEFAULT_FUSE_ENABLED = true; ++ ++ @GuardedBy("mLock") ++ private final Set mFuseMountedUser = new ArraySet<>(); ++ ++ public static class Lifecycle extends SystemService { ++ private StorageManagerService mStorageManagerService; ++ ++ public Lifecycle(Context context) { ++ super(context); ++ } ++ ++ @Override ++ public void onStart() { ++ mStorageManagerService = new StorageManagerService(getContext()); ++ publishBinderService("mount", mStorageManagerService); ++ mStorageManagerService.start(); ++ } ++ ++ @Override ++ public void onBootPhase(int phase) { ++ if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { ++ mStorageManagerService.servicesReady(); ++ } else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { ++ mStorageManagerService.systemReady(); ++ } else if (phase == SystemService.PHASE_BOOT_COMPLETED) { ++ mStorageManagerService.bootCompleted(); ++ } ++ } ++ ++ @Override ++ public void onSwitchUser(int userHandle) { ++ mStorageManagerService.mCurrentUserId = userHandle; ++ } ++ ++ @Override ++ public void onUnlockUser(int userHandle) { ++ mStorageManagerService.onUnlockUser(userHandle); ++ } ++ ++ @Override ++ public void onCleanupUser(int userHandle) { ++ mStorageManagerService.onCleanupUser(userHandle); ++ } ++ ++ @Override ++ public void onStopUser(int userHandle) { ++ mStorageManagerService.onStopUser(userHandle); ++ } ++ ++ @Override ++ public void onUserStarting(TargetUser user) { ++ mStorageManagerService.snapshotAndMonitorLegacyStorageAppOp(user.getUserHandle()); ++ } ++ } ++ ++ private static final boolean DEBUG_EVENTS = false; ++ private static final boolean DEBUG_OBB = false; ++ ++ /** ++ * We now talk to vold over Binder, and it has its own internal lock to ++ * serialize certain calls. All long-running operations have been migrated ++ * to be async with callbacks, so we want watchdog to fire if vold wedges. ++ */ ++ private static final boolean WATCHDOG_ENABLE = true; ++ ++ /** ++ * Our goal is for all Android devices to be usable as development devices, ++ * which includes the new Direct Boot mode added in N. For devices that ++ * don't have native FBE support, we offer an emulation mode for developer ++ * testing purposes, but if it's prohibitively difficult to support this ++ * mode, it can be disabled for specific products using this flag. ++ */ ++ private static final boolean EMULATE_FBE_SUPPORTED = true; ++ ++ private static final String TAG = "StorageManagerService"; ++ private static final boolean LOCAL_LOGV = Log.isLoggable(TAG, Log.VERBOSE); ++ ++ private static final String TAG_STORAGE_BENCHMARK = "storage_benchmark"; ++ private static final String TAG_STORAGE_TRIM = "storage_trim"; ++ ++ /** Magic value sent by MoveTask.cpp */ ++ private static final int MOVE_STATUS_COPY_FINISHED = 82; ++ ++ private static final int VERSION_INIT = 1; ++ private static final int VERSION_ADD_PRIMARY = 2; ++ private static final int VERSION_FIX_PRIMARY = 3; ++ ++ private static final String TAG_VOLUMES = "volumes"; ++ private static final String ATTR_VERSION = "version"; ++ private static final String ATTR_PRIMARY_STORAGE_UUID = "primaryStorageUuid"; ++ private static final String TAG_VOLUME = "volume"; ++ private static final String ATTR_TYPE = "type"; ++ private static final String ATTR_FS_UUID = "fsUuid"; ++ private static final String ATTR_PART_GUID = "partGuid"; ++ private static final String ATTR_NICKNAME = "nickname"; ++ private static final String ATTR_USER_FLAGS = "userFlags"; ++ private static final String ATTR_CREATED_MILLIS = "createdMillis"; ++ private static final String ATTR_LAST_SEEN_MILLIS = "lastSeenMillis"; ++ private static final String ATTR_LAST_TRIM_MILLIS = "lastTrimMillis"; ++ private static final String ATTR_LAST_BENCH_MILLIS = "lastBenchMillis"; ++ ++ private static final String[] ALL_STORAGE_PERMISSIONS = { ++ Manifest.permission.READ_EXTERNAL_STORAGE, ++ Manifest.permission.WRITE_EXTERNAL_STORAGE ++ }; ++ ++ @Nullable public static String sMediaStoreAuthorityProcessName; ++ ++ private final AtomicFile mSettingsFile; ++ ++ /** ++ * Never hold the lock while performing downcalls into vold, since ++ * unsolicited events can suddenly appear to update data structures. ++ */ ++ private final Object mLock = LockGuard.installNewLock(LockGuard.INDEX_STORAGE); ++ ++ /** ++ * Similar to {@link #mLock}, never hold this lock while performing downcalls into vold. ++ * Also, never hold this while calling into PackageManagerService since it is used in callbacks ++ * from PackageManagerService. ++ * ++ * If both {@link #mLock} and this lock need to be held, {@link #mLock} should be acquired ++ * before this. ++ * ++ * Use -PL suffix for methods that need to called with this lock held. ++ */ ++ private final Object mPackagesLock = new Object(); ++ ++ /** ++ * mLocalUnlockedUsers affects the return value of isUserUnlocked. If ++ * any value in the array changes, then the binder cache for ++ * isUserUnlocked must be invalidated. When adding mutating methods to ++ * WatchedLockedUsers, be sure to invalidate the cache in the new ++ * methods. ++ */ ++ private class WatchedLockedUsers { ++ private int[] users = EmptyArray.INT; ++ public WatchedLockedUsers() { ++ invalidateIsUserUnlockedCache(); ++ } ++ public void append(int userId) { ++ users = ArrayUtils.appendInt(users, userId); ++ invalidateIsUserUnlockedCache(); ++ } ++ public void remove(int userId) { ++ users = ArrayUtils.removeInt(users, userId); ++ invalidateIsUserUnlockedCache(); ++ } ++ public boolean contains(int userId) { ++ return ArrayUtils.contains(users, userId); ++ } ++ public int[] all() { ++ return users; ++ } ++ @Override ++ public String toString() { ++ return Arrays.toString(users); ++ } ++ private void invalidateIsUserUnlockedCache() { ++ UserManager.invalidateIsUserUnlockedCache(); ++ } ++ } ++ ++ /** Set of users that we know are unlocked. */ ++ @GuardedBy("mLock") ++ private WatchedLockedUsers mLocalUnlockedUsers = new WatchedLockedUsers(); ++ /** Set of users that system knows are unlocked. */ ++ @GuardedBy("mLock") ++ private int[] mSystemUnlockedUsers = EmptyArray.INT; ++ ++ /** Map from disk ID to disk */ ++ @GuardedBy("mLock") ++ private ArrayMap mDisks = new ArrayMap<>(); ++ /** Map from volume ID to disk */ ++ @GuardedBy("mLock") ++ private final ArrayMap mVolumes = new ArrayMap<>(); ++ ++ /** Map from UUID to record */ ++ @GuardedBy("mLock") ++ private ArrayMap mRecords = new ArrayMap<>(); ++ @GuardedBy("mLock") ++ private String mPrimaryStorageUuid; ++ ++ /** Map from disk ID to latches */ ++ @GuardedBy("mLock") ++ private ArrayMap mDiskScanLatches = new ArrayMap<>(); ++ ++ @GuardedBy("mLock") ++ private IPackageMoveObserver mMoveCallback; ++ @GuardedBy("mLock") ++ private String mMoveTargetUuid; ++ ++ private volatile int mMediaStoreAuthorityAppId = -1; ++ ++ private volatile int mDownloadsAuthorityAppId = -1; ++ ++ private volatile int mExternalStorageAuthorityAppId = -1; ++ ++ private volatile int mCurrentUserId = UserHandle.USER_SYSTEM; ++ ++ private final Installer mInstaller; ++ ++ /** Holding lock for AppFuse business */ ++ private final Object mAppFuseLock = new Object(); ++ ++ @GuardedBy("mAppFuseLock") ++ private int mNextAppFuseName = 0; ++ ++ @GuardedBy("mAppFuseLock") ++ private AppFuseBridge mAppFuseBridge = null; ++ ++ /** Matches known application dir paths. The first group contains the generic part of the path, ++ * the second group contains the user id (or null if it's a public volume without users), the ++ * third group contains the package name, and the fourth group the remainder of the path. ++ */ ++ public static final Pattern KNOWN_APP_DIR_PATHS = Pattern.compile( ++ "(?i)(^/storage/[^/]+/(?:([0-9]+)/)?Android/(?:data|media|obb|sandbox)/)([^/]+)(/.*)?"); ++ ++ ++ /** Automotive device unlockes users before system boot complete and this requires special ++ * handling as vold reset can lead into race conditions. When this is set, all users unlocked ++ * in {@code UserManager} level are unlocked after vold reset. ++ */ ++ private final boolean mIsAutomotive; ++ ++ private VolumeInfo findVolumeByIdOrThrow(String id) { ++ synchronized (mLock) { ++ final VolumeInfo vol = mVolumes.get(id); ++ if (vol != null) { ++ return vol; ++ } ++ } ++ throw new IllegalArgumentException("No volume found for ID " + id); ++ } ++ ++ private String findVolumeIdForPathOrThrow(String path) { ++ synchronized (mLock) { ++ for (int i = 0; i < mVolumes.size(); i++) { ++ final VolumeInfo vol = mVolumes.valueAt(i); ++ if (vol.path != null && path.startsWith(vol.path)) { ++ return vol.id; ++ } ++ } ++ } ++ throw new IllegalArgumentException("No volume found for path " + path); ++ } ++ ++ private VolumeRecord findRecordForPath(String path) { ++ synchronized (mLock) { ++ for (int i = 0; i < mVolumes.size(); i++) { ++ final VolumeInfo vol = mVolumes.valueAt(i); ++ if (vol.path != null && path.startsWith(vol.path)) { ++ return mRecords.get(vol.fsUuid); ++ } ++ } ++ } ++ return null; ++ } ++ ++ private String scrubPath(String path) { ++ if (path.startsWith(Environment.getDataDirectory().getAbsolutePath())) { ++ return "internal"; ++ } ++ final VolumeRecord rec = findRecordForPath(path); ++ if (rec == null || rec.createdMillis == 0) { ++ return "unknown"; ++ } else { ++ return "ext:" + (int) ((System.currentTimeMillis() - rec.createdMillis) ++ / DateUtils.WEEK_IN_MILLIS) + "w"; ++ } ++ } ++ ++ private @Nullable VolumeInfo findStorageForUuid(String volumeUuid) { ++ final StorageManager storage = mContext.getSystemService(StorageManager.class); ++ if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) { ++ return storage.findVolumeById(VolumeInfo.ID_EMULATED_INTERNAL + ";" + 0); ++ } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) { ++ return storage.getPrimaryPhysicalVolume(); ++ } else { ++ return storage.findEmulatedForPrivate(storage.findVolumeByUuid(volumeUuid)); ++ } ++ } ++ ++ private boolean shouldBenchmark() { ++ final long benchInterval = Settings.Global.getLong(mContext.getContentResolver(), ++ Settings.Global.STORAGE_BENCHMARK_INTERVAL, DateUtils.WEEK_IN_MILLIS); ++ if (benchInterval == -1) { ++ return false; ++ } else if (benchInterval == 0) { ++ return true; ++ } ++ ++ synchronized (mLock) { ++ for (int i = 0; i < mVolumes.size(); i++) { ++ final VolumeInfo vol = mVolumes.valueAt(i); ++ final VolumeRecord rec = mRecords.get(vol.fsUuid); ++ if (vol.isMountedWritable() && rec != null) { ++ final long benchAge = System.currentTimeMillis() - rec.lastBenchMillis; ++ if (benchAge >= benchInterval) { ++ return true; ++ } ++ } ++ } ++ return false; ++ } ++ } ++ ++ private CountDownLatch findOrCreateDiskScanLatch(String diskId) { ++ synchronized (mLock) { ++ CountDownLatch latch = mDiskScanLatches.get(diskId); ++ if (latch == null) { ++ latch = new CountDownLatch(1); ++ mDiskScanLatches.put(diskId, latch); ++ } ++ return latch; ++ } ++ } ++ ++ /** List of crypto types. ++ * These must match CRYPT_TYPE_XXX in cryptfs.h AND their ++ * corresponding commands in CommandListener.cpp */ ++ public static final String[] CRYPTO_TYPES ++ = { "password", "default", "pattern", "pin" }; ++ ++ private final Context mContext; ++ private final ContentResolver mResolver; ++ ++ private volatile IVold mVold; ++ private volatile IStoraged mStoraged; ++ ++ private volatile boolean mBootCompleted = false; ++ private volatile boolean mDaemonConnected = false; ++ private volatile boolean mSecureKeyguardShowing = true; ++ ++ private PackageManagerInternal mPmInternal; ++ ++ private IPackageManager mIPackageManager; ++ private IAppOpsService mIAppOpsService; ++ ++ private final Callbacks mCallbacks; ++ private final LockPatternUtils mLockPatternUtils; ++ ++ /** ++ * The size of the crypto algorithm key in bits for OBB files. Currently ++ * Twofish is used which takes 128-bit keys. ++ */ ++ private static final int CRYPTO_ALGORITHM_KEY_SIZE = 128; ++ ++ /** ++ * The number of times to run SHA1 in the PBKDF2 function for OBB files. ++ * 1024 is reasonably secure and not too slow. ++ */ ++ private static final int PBKDF2_HASH_ROUNDS = 1024; ++ ++ /** ++ * Mounted OBB tracking information. Used to track the current state of all ++ * OBBs. ++ */ ++ final private Map> mObbMounts = new HashMap>(); ++ ++ /** Map from raw paths to {@link ObbState}. */ ++ final private Map mObbPathToStateMap = new HashMap(); ++ ++ // Not guarded by a lock. ++ private final StorageManagerInternalImpl mStorageManagerInternal ++ = new StorageManagerInternalImpl(); ++ ++ // Not guarded by a lock. ++ private final StorageSessionController mStorageSessionController; ++ ++ private final boolean mIsFuseEnabled; ++ ++ private final boolean mVoldAppDataIsolationEnabled; ++ ++ @GuardedBy("mLock") ++ private final Set mUidsWithLegacyExternalStorage = new ArraySet<>(); ++ // Not guarded by lock, always used on the ActivityManager thread ++ private final Map mPackageMonitorsForUser = new ArrayMap<>(); ++ ++ ++ class ObbState implements IBinder.DeathRecipient { ++ public ObbState(String rawPath, String canonicalPath, int callingUid, ++ IObbActionListener token, int nonce, String volId) { ++ this.rawPath = rawPath; ++ this.canonicalPath = canonicalPath; ++ this.ownerGid = UserHandle.getSharedAppGid(callingUid); ++ this.token = token; ++ this.nonce = nonce; ++ this.volId = volId; ++ } ++ ++ final String rawPath; ++ final String canonicalPath; ++ ++ final int ownerGid; ++ ++ // Token of remote Binder caller ++ final IObbActionListener token; ++ ++ // Identifier to pass back to the token ++ final int nonce; ++ ++ String volId; ++ ++ public IBinder getBinder() { ++ return token.asBinder(); ++ } ++ ++ @Override ++ public void binderDied() { ++ ObbAction action = new UnmountObbAction(this, true); ++ mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action)); ++ } ++ ++ public void link() throws RemoteException { ++ getBinder().linkToDeath(this, 0); ++ } ++ ++ public void unlink() { ++ getBinder().unlinkToDeath(this, 0); ++ } ++ ++ @Override ++ public String toString() { ++ StringBuilder sb = new StringBuilder("ObbState{"); ++ sb.append("rawPath=").append(rawPath); ++ sb.append(",canonicalPath=").append(canonicalPath); ++ sb.append(",ownerGid=").append(ownerGid); ++ sb.append(",token=").append(token); ++ sb.append(",binder=").append(getBinder()); ++ sb.append(",volId=").append(volId); ++ sb.append('}'); ++ return sb.toString(); ++ } ++ } ++ ++ // OBB Action Handler ++ final private ObbActionHandler mObbActionHandler; ++ ++ // OBB action handler messages ++ private static final int OBB_RUN_ACTION = 1; ++ private static final int OBB_FLUSH_MOUNT_STATE = 2; ++ ++ // Last fstrim operation tracking ++ private static final String LAST_FSTRIM_FILE = "last-fstrim"; ++ private final File mLastMaintenanceFile; ++ private long mLastMaintenance; ++ ++ // Handler messages ++ private static final int H_SYSTEM_READY = 1; ++ private static final int H_DAEMON_CONNECTED = 2; ++ private static final int H_SHUTDOWN = 3; ++ private static final int H_FSTRIM = 4; ++ private static final int H_VOLUME_MOUNT = 5; ++ private static final int H_VOLUME_BROADCAST = 6; ++ private static final int H_INTERNAL_BROADCAST = 7; ++ private static final int H_VOLUME_UNMOUNT = 8; ++ private static final int H_PARTITION_FORGET = 9; ++ private static final int H_RESET = 10; ++ private static final int H_RUN_IDLE_MAINT = 11; ++ private static final int H_ABORT_IDLE_MAINT = 12; ++ private static final int H_BOOT_COMPLETED = 13; ++ private static final int H_COMPLETE_UNLOCK_USER = 14; ++ private static final int H_VOLUME_STATE_CHANGED = 15; ++ ++ class StorageManagerServiceHandler extends Handler { ++ public StorageManagerServiceHandler(Looper looper) { ++ super(looper); ++ } ++ ++ @Override ++ public void handleMessage(Message msg) { ++ switch (msg.what) { ++ case H_SYSTEM_READY: { ++ handleSystemReady(); ++ break; ++ } ++ case H_BOOT_COMPLETED: { ++ handleBootCompleted(); ++ break; ++ } ++ case H_DAEMON_CONNECTED: { ++ handleDaemonConnected(); ++ break; ++ } ++ case H_FSTRIM: { ++ Slog.i(TAG, "Running fstrim idle maintenance"); ++ ++ // Remember when we kicked it off ++ try { ++ mLastMaintenance = System.currentTimeMillis(); ++ mLastMaintenanceFile.setLastModified(mLastMaintenance); ++ } catch (Exception e) { ++ Slog.e(TAG, "Unable to record last fstrim!"); ++ } ++ ++ // TODO: Reintroduce shouldBenchmark() test ++ fstrim(0, null); ++ ++ // invoke the completion callback, if any ++ // TODO: fstrim is non-blocking, so remove this useless callback ++ Runnable callback = (Runnable) msg.obj; ++ if (callback != null) { ++ callback.run(); ++ } ++ break; ++ } ++ case H_SHUTDOWN: { ++ final IStorageShutdownObserver obs = (IStorageShutdownObserver) msg.obj; ++ boolean success = false; ++ try { ++ mVold.shutdown(); ++ success = true; ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ } ++ if (obs != null) { ++ try { ++ obs.onShutDownComplete(success ? 0 : -1); ++ } catch (Exception ignored) { ++ } ++ } ++ break; ++ } ++ case H_VOLUME_MOUNT: { ++ final VolumeInfo vol = (VolumeInfo) msg.obj; ++ if (isMountDisallowed(vol)) { ++ Slog.i(TAG, "Ignoring mount " + vol.getId() + " due to policy"); ++ break; ++ } ++ ++ mount(vol); ++ break; ++ } ++ case H_VOLUME_UNMOUNT: { ++ final VolumeInfo vol = (VolumeInfo) msg.obj; ++ unmount(vol); ++ break; ++ } ++ case H_VOLUME_BROADCAST: { ++ final StorageVolume userVol = (StorageVolume) msg.obj; ++ final String envState = userVol.getState(); ++ Slog.d(TAG, "Volume " + userVol.getId() + " broadcasting " + envState + " to " ++ + userVol.getOwner()); ++ ++ final String action = VolumeInfo.getBroadcastForEnvironment(envState); ++ if (action != null) { ++ final Intent intent = new Intent(action, ++ Uri.fromFile(userVol.getPathFile())); ++ intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, userVol); ++ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT ++ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); ++ mContext.sendBroadcastAsUser(intent, userVol.getOwner()); ++ } ++ break; ++ } ++ case H_INTERNAL_BROADCAST: { ++ // Internal broadcasts aimed at system components, not for ++ // third-party apps. ++ final Intent intent = (Intent) msg.obj; ++ mContext.sendBroadcastAsUser(intent, UserHandle.ALL, ++ android.Manifest.permission.WRITE_MEDIA_STORAGE); ++ break; ++ } ++ case H_PARTITION_FORGET: { ++ final VolumeRecord rec = (VolumeRecord) msg.obj; ++ forgetPartition(rec.partGuid, rec.fsUuid); ++ break; ++ } ++ case H_RESET: { ++ resetIfBootedAndConnected(); ++ break; ++ } ++ case H_RUN_IDLE_MAINT: { ++ Slog.i(TAG, "Running idle maintenance"); ++ runIdleMaint((Runnable)msg.obj); ++ break; ++ } ++ case H_ABORT_IDLE_MAINT: { ++ Slog.i(TAG, "Aborting idle maintenance"); ++ abortIdleMaint((Runnable)msg.obj); ++ break; ++ } ++ case H_COMPLETE_UNLOCK_USER: { ++ completeUnlockUser((int) msg.obj); ++ break; ++ } ++ case H_VOLUME_STATE_CHANGED: { ++ final SomeArgs args = (SomeArgs) msg.obj; ++ onVolumeStateChangedAsync((VolumeInfo) args.arg1, (int) args.arg2, ++ (int) args.arg3); ++ } ++ } ++ } ++ } ++ ++ private final Handler mHandler; ++ ++ private BroadcastReceiver mUserReceiver = new BroadcastReceiver() { ++ @Override ++ public void onReceive(Context context, Intent intent) { ++ final String action = intent.getAction(); ++ final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); ++ Preconditions.checkArgument(userId >= 0); ++ ++ try { ++ if (Intent.ACTION_USER_ADDED.equals(action)) { ++ final UserManager um = mContext.getSystemService(UserManager.class); ++ final int userSerialNumber = um.getUserSerialNumber(userId); ++ mVold.onUserAdded(userId, userSerialNumber); ++ } else if (Intent.ACTION_USER_REMOVED.equals(action)) { ++ synchronized (mVolumes) { ++ final int size = mVolumes.size(); ++ for (int i = 0; i < size; i++) { ++ final VolumeInfo vol = mVolumes.valueAt(i); ++ if (vol.mountUserId == userId) { ++ vol.mountUserId = UserHandle.USER_NULL; ++ mHandler.obtainMessage(H_VOLUME_UNMOUNT, vol).sendToTarget(); ++ } ++ } ++ } ++ mVold.onUserRemoved(userId); ++ } ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ } ++ } ++ }; ++ ++ private void waitForLatch(CountDownLatch latch, String condition, long timeoutMillis) ++ throws TimeoutException { ++ final long startMillis = SystemClock.elapsedRealtime(); ++ while (true) { ++ try { ++ if (latch.await(5000, TimeUnit.MILLISECONDS)) { ++ return; ++ } else { ++ Slog.w(TAG, "Thread " + Thread.currentThread().getName() ++ + " still waiting for " + condition + "..."); ++ } ++ } catch (InterruptedException e) { ++ Slog.w(TAG, "Interrupt while waiting for " + condition); ++ } ++ if (timeoutMillis > 0 && SystemClock.elapsedRealtime() > startMillis + timeoutMillis) { ++ throw new TimeoutException("Thread " + Thread.currentThread().getName() ++ + " gave up waiting for " + condition + " after " + timeoutMillis + "ms"); ++ } ++ } ++ } ++ ++ private void handleSystemReady() { ++ // Start scheduling nominally-daily fstrim operations ++ MountServiceIdler.scheduleIdlePass(mContext); ++ ++ // Toggle zram-enable system property in response to settings ++ mContext.getContentResolver().registerContentObserver( ++ Settings.Global.getUriFor(Settings.Global.ZRAM_ENABLED), ++ false /*notifyForDescendants*/, ++ new ContentObserver(null /* current thread */) { ++ @Override ++ public void onChange(boolean selfChange) { ++ refreshZramSettings(); ++ } ++ }); ++ refreshZramSettings(); ++ ++ // Schedule zram writeback unless zram is disabled by persist.sys.zram_enabled ++ String zramPropValue = SystemProperties.get(ZRAM_ENABLED_PROPERTY); ++ if (!zramPropValue.equals("0") ++ && mContext.getResources().getBoolean( ++ com.android.internal.R.bool.config_zramWriteback)) { ++ ZramWriteback.scheduleZramWriteback(mContext); ++ } ++ // Toggle isolated-enable system property in response to settings ++ mContext.getContentResolver().registerContentObserver( ++ Settings.Global.getUriFor(Settings.Global.ISOLATED_STORAGE_REMOTE), ++ false /*notifyForDescendants*/, ++ new ContentObserver(null /* current thread */) { ++ @Override ++ public void onChange(boolean selfChange) { ++ refreshIsolatedStorageSettings(); ++ } ++ }); ++ // For now, simply clone property when it changes ++ DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, ++ mContext.getMainExecutor(), (properties) -> { ++ refreshIsolatedStorageSettings(); ++ refreshFuseSettings(); ++ }); ++ refreshIsolatedStorageSettings(); ++ } ++ ++ /** ++ * Update the zram_enabled system property (which init reads to ++ * decide whether to enable zram) to reflect the zram_enabled ++ * preference (which we can change for experimentation purposes). ++ */ ++ private void refreshZramSettings() { ++ String propertyValue = SystemProperties.get(ZRAM_ENABLED_PROPERTY); ++ if ("".equals(propertyValue)) { ++ return; // System doesn't have zram toggling support ++ } ++ String desiredPropertyValue = ++ Settings.Global.getInt(mContext.getContentResolver(), ++ Settings.Global.ZRAM_ENABLED, ++ 1) != 0 ++ ? "1" : "0"; ++ if (!desiredPropertyValue.equals(propertyValue)) { ++ // Avoid redundant disk writes by setting only if we're ++ // changing the property value. There's no race: we're the ++ // sole writer. ++ SystemProperties.set(ZRAM_ENABLED_PROPERTY, desiredPropertyValue); ++ // Schedule writeback only if zram is being enabled. ++ if (desiredPropertyValue.equals("1") ++ && mContext.getResources().getBoolean( ++ com.android.internal.R.bool.config_zramWriteback)) { ++ ZramWriteback.scheduleZramWriteback(mContext); ++ } ++ } ++ } ++ ++ private void refreshIsolatedStorageSettings() { ++ // Always copy value from newer DeviceConfig location ++ Settings.Global.putString(mResolver, ++ Settings.Global.ISOLATED_STORAGE_REMOTE, ++ DeviceConfig.getProperty(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, ++ ISOLATED_STORAGE_ENABLED)); ++ ++ final int local = Settings.Global.getInt(mContext.getContentResolver(), ++ Settings.Global.ISOLATED_STORAGE_LOCAL, 0); ++ final int remote = Settings.Global.getInt(mContext.getContentResolver(), ++ Settings.Global.ISOLATED_STORAGE_REMOTE, 0); ++ ++ // Walk down precedence chain; we prefer local settings first, then ++ // remote settings, before finally falling back to hard-coded default. ++ final boolean res; ++ if (local == -1) { ++ res = false; ++ } else if (local == 1) { ++ res = true; ++ } else if (remote == -1) { ++ res = false; ++ } else if (remote == 1) { ++ res = true; ++ } else { ++ res = true; ++ } ++ ++ Slog.d(TAG, "Isolated storage local flag " + local + " and remote flag " ++ + remote + " resolved to " + res); ++ SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE, Boolean.toString(res)); ++ } ++ ++ /** ++ * The most recent flag change takes precedence. Change fuse Settings flag if Device Config is ++ * changed. Settings flag change will in turn change fuse system property (persist.sys.fuse) ++ * whenever the user reboots. ++ */ ++ private void refreshFuseSettings() { ++ int isFuseEnabled = DeviceConfig.getInt(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, ++ FUSE_ENABLED, 0); ++ if (isFuseEnabled == 1) { ++ Slog.d(TAG, "Device Config flag for FUSE is enabled, turn Settings fuse flag on"); ++ SystemProperties.set(FeatureFlagUtils.PERSIST_PREFIX ++ + FeatureFlagUtils.SETTINGS_FUSE_FLAG, "true"); ++ } else if (isFuseEnabled == -1) { ++ Slog.d(TAG, "Device Config flag for FUSE is disabled, turn Settings fuse flag off"); ++ SystemProperties.set(FeatureFlagUtils.PERSIST_PREFIX ++ + FeatureFlagUtils.SETTINGS_FUSE_FLAG, "false"); ++ } ++ // else, keep the build config. ++ // This can be overridden by direct adjustment of persist.sys.fflag.override.settings_fuse ++ } ++ ++ /** ++ * MediaProvider has a ton of code that makes assumptions about storage ++ * paths never changing, so we outright kill them to pick up new state. ++ */ ++ @Deprecated ++ private void killMediaProvider(List users) { ++ if (users == null) return; ++ ++ final long token = Binder.clearCallingIdentity(); ++ try { ++ for (UserInfo user : users) { ++ // System user does not have media provider, so skip. ++ if (user.isSystemOnly()) continue; ++ ++ final ProviderInfo provider = mPmInternal.resolveContentProvider( ++ MediaStore.AUTHORITY, PackageManager.MATCH_DIRECT_BOOT_AWARE ++ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, ++ user.id); ++ if (provider != null) { ++ final IActivityManager am = ActivityManager.getService(); ++ try { ++ am.killApplication(provider.applicationInfo.packageName, ++ UserHandle.getAppId(provider.applicationInfo.uid), ++ UserHandle.USER_ALL, "vold reset"); ++ // We only need to run this once. It will kill all users' media processes. ++ break; ++ } catch (RemoteException e) { ++ } ++ } ++ } ++ } finally { ++ Binder.restoreCallingIdentity(token); ++ } ++ } ++ ++ @GuardedBy("mLock") ++ private void addInternalVolumeLocked() { ++ // Create a stub volume that represents internal storage ++ final VolumeInfo internal = new VolumeInfo(VolumeInfo.ID_PRIVATE_INTERNAL, ++ VolumeInfo.TYPE_PRIVATE, null, null); ++ internal.state = VolumeInfo.STATE_MOUNTED; ++ internal.path = Environment.getDataDirectory().getAbsolutePath(); ++ mVolumes.put(internal.id, internal); ++ } ++ ++ private void initIfBootedAndConnected() { ++ Slog.d(TAG, "Thinking about init, mBootCompleted=" + mBootCompleted ++ + ", mDaemonConnected=" + mDaemonConnected); ++ if (mBootCompleted && mDaemonConnected ++ && !StorageManager.isFileEncryptedNativeOnly()) { ++ // When booting a device without native support, make sure that our ++ // user directories are locked or unlocked based on the current ++ // emulation status. ++ final boolean initLocked = StorageManager.isFileEncryptedEmulatedOnly(); ++ Slog.d(TAG, "Setting up emulation state, initlocked=" + initLocked); ++ final List users = mContext.getSystemService(UserManager.class).getUsers(); ++ for (UserInfo user : users) { ++ try { ++ if (initLocked) { ++ mVold.lockUserKey(user.id); ++ } else { ++ mVold.unlockUserKey(user.id, user.serialNumber, encodeBytes(null), ++ encodeBytes(null)); ++ } ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ } ++ } ++ } ++ } ++ ++ private void resetIfBootedAndConnected() { ++ Slog.d(TAG, "Thinking about reset, mBootCompleted=" + mBootCompleted ++ + ", mDaemonConnected=" + mDaemonConnected); ++ if (mBootCompleted && mDaemonConnected) { ++ final UserManager userManager = mContext.getSystemService(UserManager.class); ++ final List users = userManager.getUsers(); ++ ++ if (mIsFuseEnabled) { ++ mStorageSessionController.onReset(mVold, () -> { ++ mHandler.removeCallbacksAndMessages(null); ++ }); ++ } else { ++ killMediaProvider(users); ++ } ++ ++ final int[] systemUnlockedUsers; ++ synchronized (mLock) { ++ // make copy as sorting can change order ++ systemUnlockedUsers = Arrays.copyOf(mSystemUnlockedUsers, ++ mSystemUnlockedUsers.length); ++ ++ mDisks.clear(); ++ mVolumes.clear(); ++ ++ addInternalVolumeLocked(); ++ } ++ ++ try { ++ // TODO(b/135341433): Remove paranoid logging when FUSE is stable ++ Slog.i(TAG, "Resetting vold..."); ++ mVold.reset(); ++ Slog.i(TAG, "Reset vold"); ++ ++ // Tell vold about all existing and started users ++ for (UserInfo user : users) { ++ mVold.onUserAdded(user.id, user.serialNumber); ++ } ++ for (int userId : systemUnlockedUsers) { ++ mVold.onUserStarted(userId); ++ mStoraged.onUserStarted(userId); ++ } ++ if (mIsAutomotive) { ++ restoreAllUnlockedUsers(userManager, users, systemUnlockedUsers); ++ } ++ mVold.onSecureKeyguardStateChanged(mSecureKeyguardShowing); ++ mStorageManagerInternal.onReset(mVold); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ } ++ } ++ } ++ ++ private void restoreAllUnlockedUsers(UserManager userManager, List allUsers, ++ int[] systemUnlockedUsers) throws Exception { ++ Arrays.sort(systemUnlockedUsers); ++ UserManager.invalidateIsUserUnlockedCache(); ++ for (UserInfo user : allUsers) { ++ int userId = user.id; ++ if (!userManager.isUserRunning(userId)) { ++ continue; ++ } ++ if (Arrays.binarySearch(systemUnlockedUsers, userId) >= 0) { ++ continue; ++ } ++ boolean unlockingOrUnlocked = userManager.isUserUnlockingOrUnlocked(userId); ++ if (!unlockingOrUnlocked) { ++ continue; ++ } ++ Slog.w(TAG, "UNLOCK_USER lost from vold reset, will retry, user:" + userId); ++ mVold.onUserStarted(userId); ++ mStoraged.onUserStarted(userId); ++ mHandler.obtainMessage(H_COMPLETE_UNLOCK_USER, userId).sendToTarget(); ++ } ++ } ++ ++ private void onUnlockUser(int userId) { ++ Slog.d(TAG, "onUnlockUser " + userId); ++ ++ // We purposefully block here to make sure that user-specific ++ // staging area is ready so it's ready for zygote-forked apps to ++ // bind mount against. ++ try { ++ mStorageSessionController.onUnlockUser(userId); ++ mVold.onUserStarted(userId); ++ mStoraged.onUserStarted(userId); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ } ++ ++ mHandler.obtainMessage(H_COMPLETE_UNLOCK_USER, userId).sendToTarget(); ++ } ++ ++ private void completeUnlockUser(int userId) { ++ // If user 0 has completed unlock, perform a one-time migration of legacy obb data ++ // to its new location. This may take time depending on the size of the data to be copied ++ // so it's done on the StorageManager handler thread. ++ if (userId == 0) { ++ mPmInternal.migrateLegacyObbData(); ++ } ++ ++ onKeyguardStateChanged(false); ++ ++ // Record user as started so newly mounted volumes kick off events ++ // correctly, then synthesize events for any already-mounted volumes. ++ synchronized (mLock) { ++ if (mIsAutomotive) { ++ for (int unlockedUser : mSystemUnlockedUsers) { ++ if (unlockedUser == userId) { ++ // This can happen as restoreAllUnlockedUsers can double post the message. ++ Log.i(TAG, "completeUnlockUser called for already unlocked user:" + userId); ++ return; ++ } ++ } ++ } ++ for (int i = 0; i < mVolumes.size(); i++) { ++ final VolumeInfo vol = mVolumes.valueAt(i); ++ if (vol.isVisibleForRead(userId) && vol.isMountedReadable()) { ++ final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, false); ++ mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget(); ++ ++ final String envState = VolumeInfo.getEnvironmentForState(vol.getState()); ++ mCallbacks.notifyStorageStateChanged(userVol.getPath(), envState, envState); ++ } ++ } ++ mSystemUnlockedUsers = ArrayUtils.appendInt(mSystemUnlockedUsers, userId); ++ } ++ } ++ ++ private void onCleanupUser(int userId) { ++ Slog.d(TAG, "onCleanupUser " + userId); ++ ++ try { ++ mVold.onUserStopped(userId); ++ mStoraged.onUserStopped(userId); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ } ++ ++ synchronized (mLock) { ++ mSystemUnlockedUsers = ArrayUtils.removeInt(mSystemUnlockedUsers, userId); ++ } ++ } ++ ++ private void onStopUser(int userId) { ++ Slog.i(TAG, "onStopUser " + userId); ++ try { ++ mStorageSessionController.onUserStopping(userId); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ } ++ PackageMonitor monitor = mPackageMonitorsForUser.remove(userId); ++ if (monitor != null) { ++ monitor.unregister(); ++ } ++ } ++ ++ private boolean supportsBlockCheckpoint() throws RemoteException { ++ enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); ++ return mVold.supportsBlockCheckpoint(); ++ } ++ ++ @Override ++ public void onAwakeStateChanged(boolean isAwake) { ++ // Ignored ++ } ++ ++ @Override ++ public void onKeyguardStateChanged(boolean isShowing) { ++ // Push down current secure keyguard status so that we ignore malicious ++ // USB devices while locked. ++ mSecureKeyguardShowing = isShowing ++ && mContext.getSystemService(KeyguardManager.class).isDeviceSecure(mCurrentUserId); ++ try { ++ mVold.onSecureKeyguardStateChanged(mSecureKeyguardShowing); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ } ++ } ++ ++ void runIdleMaintenance(Runnable callback) { ++ mHandler.sendMessage(mHandler.obtainMessage(H_FSTRIM, callback)); ++ } ++ ++ // Binder entry point for kicking off an immediate fstrim ++ @Override ++ public void runMaintenance() { ++ enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); ++ runIdleMaintenance(null); ++ } ++ ++ @Override ++ public long lastMaintenance() { ++ return mLastMaintenance; ++ } ++ ++ public void onDaemonConnected() { ++ mDaemonConnected = true; ++ mHandler.obtainMessage(H_DAEMON_CONNECTED).sendToTarget(); ++ } ++ ++ private void handleDaemonConnected() { ++ initIfBootedAndConnected(); ++ resetIfBootedAndConnected(); ++ ++ // On an encrypted device we can't see system properties yet, so pull ++ // the system locale out of the mount service. ++ if ("".equals(VoldProperties.encrypt_progress().orElse(""))) { ++ copyLocaleFromMountService(); ++ } ++ } ++ ++ private void copyLocaleFromMountService() { ++ String systemLocale; ++ try { ++ systemLocale = getField(StorageManager.SYSTEM_LOCALE_KEY); ++ } catch (RemoteException e) { ++ return; ++ } ++ if (TextUtils.isEmpty(systemLocale)) { ++ return; ++ } ++ ++ Slog.d(TAG, "Got locale " + systemLocale + " from mount service"); ++ Locale locale = Locale.forLanguageTag(systemLocale); ++ Configuration config = new Configuration(); ++ config.setLocale(locale); ++ try { ++ ActivityManager.getService().updatePersistentConfiguration(config); ++ } catch (RemoteException e) { ++ Slog.e(TAG, "Error setting system locale from mount service", e); ++ } ++ ++ // Temporary workaround for http://b/17945169. ++ Slog.d(TAG, "Setting system properties to " + systemLocale + " from mount service"); ++ SystemProperties.set("persist.sys.locale", locale.toLanguageTag()); ++ } ++ ++ private final IVoldListener mListener = new IVoldListener.Stub() { ++ @Override ++ public void onDiskCreated(String diskId, int flags) { ++ synchronized (mLock) { ++ final String value = SystemProperties.get(StorageManager.PROP_ADOPTABLE); ++ switch (value) { ++ case "force_on": ++ flags |= DiskInfo.FLAG_ADOPTABLE; ++ break; ++ case "force_off": ++ flags &= ~DiskInfo.FLAG_ADOPTABLE; ++ break; ++ } ++ mDisks.put(diskId, new DiskInfo(diskId, flags)); ++ } ++ } ++ ++ @Override ++ public void onDiskScanned(String diskId) { ++ synchronized (mLock) { ++ final DiskInfo disk = mDisks.get(diskId); ++ if (disk != null) { ++ onDiskScannedLocked(disk); ++ } ++ } ++ } ++ ++ @Override ++ public void onDiskMetadataChanged(String diskId, long sizeBytes, String label, ++ String sysPath) { ++ synchronized (mLock) { ++ final DiskInfo disk = mDisks.get(diskId); ++ if (disk != null) { ++ disk.size = sizeBytes; ++ disk.label = label; ++ disk.sysPath = sysPath; ++ } ++ } ++ } ++ ++ @Override ++ public void onDiskDestroyed(String diskId) { ++ synchronized (mLock) { ++ final DiskInfo disk = mDisks.remove(diskId); ++ if (disk != null) { ++ mCallbacks.notifyDiskDestroyed(disk); ++ } ++ } ++ } ++ ++ @Override ++ public void onVolumeCreated(String volId, int type, String diskId, String partGuid, ++ int userId) { ++ synchronized (mLock) { ++ final DiskInfo disk = mDisks.get(diskId); ++ final VolumeInfo vol = new VolumeInfo(volId, type, disk, partGuid); ++ vol.mountUserId = userId; ++ mVolumes.put(volId, vol); ++ onVolumeCreatedLocked(vol); ++ } ++ } ++ ++ @Override ++ public void onVolumeStateChanged(String volId, int state) { ++ synchronized (mLock) { ++ final VolumeInfo vol = mVolumes.get(volId); ++ if (vol != null) { ++ final int oldState = vol.state; ++ final int newState = state; ++ vol.state = newState; ++ final SomeArgs args = SomeArgs.obtain(); ++ args.arg1 = vol; ++ args.arg2 = oldState; ++ args.arg3 = newState; ++ mHandler.obtainMessage(H_VOLUME_STATE_CHANGED, args).sendToTarget(); ++ onVolumeStateChangedLocked(vol, oldState, newState); ++ } ++ } ++ } ++ ++ @Override ++ public void onVolumeMetadataChanged(String volId, String fsType, String fsUuid, ++ String fsLabel) { ++ synchronized (mLock) { ++ final VolumeInfo vol = mVolumes.get(volId); ++ if (vol != null) { ++ vol.fsType = fsType; ++ vol.fsUuid = fsUuid; ++ vol.fsLabel = fsLabel; ++ } ++ } ++ } ++ ++ @Override ++ public void onVolumePathChanged(String volId, String path) { ++ synchronized (mLock) { ++ final VolumeInfo vol = mVolumes.get(volId); ++ if (vol != null) { ++ vol.path = path; ++ } ++ } ++ } ++ ++ @Override ++ public void onVolumeInternalPathChanged(String volId, String internalPath) { ++ synchronized (mLock) { ++ final VolumeInfo vol = mVolumes.get(volId); ++ if (vol != null) { ++ vol.internalPath = internalPath; ++ } ++ } ++ } ++ ++ @Override ++ public void onVolumeDestroyed(String volId) { ++ VolumeInfo vol = null; ++ synchronized (mLock) { ++ vol = mVolumes.remove(volId); ++ } ++ ++ if (vol != null) { ++ mStorageSessionController.onVolumeRemove(vol); ++ try { ++ if (vol.type == VolumeInfo.TYPE_PRIVATE) { ++ mInstaller.onPrivateVolumeRemoved(vol.getFsUuid()); ++ } ++ } catch (Installer.InstallerException e) { ++ Slog.i(TAG, "Failed when private volume unmounted " + vol, e); ++ } ++ } ++ } ++ }; ++ ++ @GuardedBy("mLock") ++ private void onDiskScannedLocked(DiskInfo disk) { ++ int volumeCount = 0; ++ for (int i = 0; i < mVolumes.size(); i++) { ++ final VolumeInfo vol = mVolumes.valueAt(i); ++ if (Objects.equals(disk.id, vol.getDiskId())) { ++ volumeCount++; ++ } ++ } ++ ++ final Intent intent = new Intent(DiskInfo.ACTION_DISK_SCANNED); ++ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT ++ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); ++ intent.putExtra(DiskInfo.EXTRA_DISK_ID, disk.id); ++ intent.putExtra(DiskInfo.EXTRA_VOLUME_COUNT, volumeCount); ++ mHandler.obtainMessage(H_INTERNAL_BROADCAST, intent).sendToTarget(); ++ ++ final CountDownLatch latch = mDiskScanLatches.remove(disk.id); ++ if (latch != null) { ++ latch.countDown(); ++ } ++ ++ disk.volumeCount = volumeCount; ++ mCallbacks.notifyDiskScanned(disk, volumeCount); ++ } ++ ++ @GuardedBy("mLock") ++ private void onVolumeCreatedLocked(VolumeInfo vol) { ++ if (mPmInternal.isOnlyCoreApps()) { ++ Slog.d(TAG, "System booted in core-only mode; ignoring volume " + vol.getId()); ++ return; ++ } ++ final ActivityManagerInternal amInternal = ++ LocalServices.getService(ActivityManagerInternal.class); ++ ++ if (mIsFuseEnabled && vol.mountUserId >= 0 ++ && !amInternal.isUserRunning(vol.mountUserId, 0)) { ++ Slog.d(TAG, "Ignoring volume " + vol.getId() + " because user " ++ + Integer.toString(vol.mountUserId) + " is no longer running."); ++ return; ++ } ++ ++ if (vol.type == VolumeInfo.TYPE_EMULATED) { ++ final StorageManager storage = mContext.getSystemService(StorageManager.class); ++ final VolumeInfo privateVol = storage.findPrivateForEmulated(vol); ++ ++ if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid) ++ && VolumeInfo.ID_PRIVATE_INTERNAL.equals(privateVol.id)) { ++ Slog.v(TAG, "Found primary storage at " + vol); ++ vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY; ++ vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE; ++ mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget(); ++ ++ } else if (Objects.equals(privateVol.fsUuid, mPrimaryStorageUuid)) { ++ Slog.v(TAG, "Found primary storage at " + vol); ++ vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY; ++ vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE; ++ mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget(); ++ } ++ ++ } else if (vol.type == VolumeInfo.TYPE_PUBLIC) { ++ // TODO: only look at first public partition ++ if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, mPrimaryStorageUuid) ++ && vol.disk.isDefaultPrimary()) { ++ Slog.v(TAG, "Found primary storage at " + vol); ++ vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY; ++ vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE; ++ } ++ ++ // Adoptable public disks are visible to apps, since they meet ++ // public API requirement of being in a stable location. ++ if (vol.disk.isAdoptable()) { ++ vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE; ++ } else if (vol.disk.isSd()) { ++ vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE; ++ } ++ ++ vol.mountUserId = mCurrentUserId; ++ mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget(); ++ ++ } else if (vol.type == VolumeInfo.TYPE_PRIVATE) { ++ mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget(); ++ ++ } else if (vol.type == VolumeInfo.TYPE_STUB) { ++ vol.mountUserId = mCurrentUserId; ++ mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget(); ++ } else { ++ Slog.d(TAG, "Skipping automatic mounting of " + vol); ++ } ++ } ++ ++ private boolean isBroadcastWorthy(VolumeInfo vol) { ++ switch (vol.getType()) { ++ case VolumeInfo.TYPE_PRIVATE: ++ case VolumeInfo.TYPE_PUBLIC: ++ case VolumeInfo.TYPE_EMULATED: ++ case VolumeInfo.TYPE_STUB: ++ break; ++ default: ++ return false; ++ } ++ ++ switch (vol.getState()) { ++ case VolumeInfo.STATE_MOUNTED: ++ case VolumeInfo.STATE_MOUNTED_READ_ONLY: ++ case VolumeInfo.STATE_EJECTING: ++ case VolumeInfo.STATE_UNMOUNTED: ++ case VolumeInfo.STATE_UNMOUNTABLE: ++ case VolumeInfo.STATE_BAD_REMOVAL: ++ break; ++ default: ++ return false; ++ } ++ ++ return true; ++ } ++ ++ ++ private void onVolumeStateChangedLocked(VolumeInfo vol, int oldState, int newState) { ++ if (vol.type == VolumeInfo.TYPE_EMULATED) { ++ if (newState != VolumeInfo.STATE_MOUNTED) { ++ mFuseMountedUser.remove(vol.getMountUserId()); ++ } else if (mVoldAppDataIsolationEnabled){ ++ final int userId = vol.getMountUserId(); ++ mFuseMountedUser.add(userId); ++ // Async remount app storage so it won't block the main thread. ++ new Thread(() -> { ++ Map pidPkgMap = null; ++ // getProcessesWithPendingBindMounts() could fail when a new app process is ++ // starting and it's not planning to mount storage dirs in zygote, but it's ++ // rare, so we retry 5 times and hope we can get the result successfully. ++ for (int i = 0; i < 5; i++) { ++ try { ++ pidPkgMap = LocalServices.getService(ActivityManagerInternal.class) ++ .getProcessesWithPendingBindMounts(vol.getMountUserId()); ++ break; ++ } catch (IllegalStateException e) { ++ Slog.i(TAG, "Some processes are starting, retry"); ++ // Wait 100ms and retry so hope the pending process is started. ++ SystemClock.sleep(100); ++ } ++ } ++ if (pidPkgMap != null) { ++ remountAppStorageDirs(pidPkgMap, userId); ++ } else { ++ Slog.wtf(TAG, "Not able to getStorageNotOptimizedProcesses() after" ++ + " 5 retries"); ++ } ++ }).start(); ++ } ++ } ++ } ++ ++ ++ private void onVolumeStateChangedAsync(VolumeInfo vol, int oldState, int newState) { ++ synchronized (mLock) { ++ // Remember that we saw this volume so we're ready to accept user ++ // metadata, or so we can annoy them when a private volume is ejected ++ if (!TextUtils.isEmpty(vol.fsUuid)) { ++ VolumeRecord rec = mRecords.get(vol.fsUuid); ++ if (rec == null) { ++ rec = new VolumeRecord(vol.type, vol.fsUuid); ++ rec.partGuid = vol.partGuid; ++ rec.createdMillis = System.currentTimeMillis(); ++ if (vol.type == VolumeInfo.TYPE_PRIVATE) { ++ rec.nickname = vol.disk.getDescription(); ++ } ++ mRecords.put(rec.fsUuid, rec); ++ } else { ++ // Handle upgrade case where we didn't store partition GUID ++ if (TextUtils.isEmpty(rec.partGuid)) { ++ rec.partGuid = vol.partGuid; ++ } ++ } ++ ++ rec.lastSeenMillis = System.currentTimeMillis(); ++ writeSettingsLocked(); ++ } ++ } ++ ++ if (newState == VolumeInfo.STATE_MOUNTED) { ++ // Private volumes can be unmounted and re-mounted even after a user has ++ // been unlocked; on devices that support encryption keys tied to the filesystem, ++ // this requires setting up the keys again. ++ prepareUserStorageIfNeeded(vol); ++ } ++ ++ // This is a blocking call to Storage Service which needs to process volume state changed ++ // before notifying other listeners. ++ // Intentionally called without the mLock to avoid deadlocking from the Storage Service. ++ try { ++ mStorageSessionController.notifyVolumeStateChanged(vol); ++ } catch (ExternalStorageServiceException e) { ++ Log.e(TAG, "Failed to notify volume state changed to the Storage Service", e); ++ } ++ synchronized (mLock) { ++ mCallbacks.notifyVolumeStateChanged(vol, oldState, newState); ++ ++ // Do not broadcast before boot has completed to avoid launching the ++ // processes that receive the intent unnecessarily. ++ if (mBootCompleted && isBroadcastWorthy(vol)) { ++ final Intent intent = new Intent(VolumeInfo.ACTION_VOLUME_STATE_CHANGED); ++ intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.id); ++ intent.putExtra(VolumeInfo.EXTRA_VOLUME_STATE, newState); ++ intent.putExtra(VolumeRecord.EXTRA_FS_UUID, vol.fsUuid); ++ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT ++ | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); ++ mHandler.obtainMessage(H_INTERNAL_BROADCAST, intent).sendToTarget(); ++ } ++ ++ final String oldStateEnv = VolumeInfo.getEnvironmentForState(oldState); ++ final String newStateEnv = VolumeInfo.getEnvironmentForState(newState); ++ ++ if (!Objects.equals(oldStateEnv, newStateEnv)) { ++ // Kick state changed event towards all started users. Any users ++ // started after this point will trigger additional ++ // user-specific broadcasts. ++ for (int userId : mSystemUnlockedUsers) { ++ if (vol.isVisibleForRead(userId)) { ++ final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, ++ false); ++ mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget(); ++ ++ mCallbacks.notifyStorageStateChanged(userVol.getPath(), oldStateEnv, ++ newStateEnv); ++ } ++ } ++ } ++ ++ if ((vol.type == VolumeInfo.TYPE_PUBLIC || vol.type == VolumeInfo.TYPE_STUB) ++ && vol.state == VolumeInfo.STATE_EJECTING) { ++ // TODO: this should eventually be handled by new ObbVolume state changes ++ /* ++ * Some OBBs might have been unmounted when this volume was ++ * unmounted, so send a message to the handler to let it know to ++ * remove those from the list of mounted OBBS. ++ */ ++ mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage( ++ OBB_FLUSH_MOUNT_STATE, vol.path)); ++ } ++ maybeLogMediaMount(vol, newState); ++ } ++ } ++ ++ private void maybeLogMediaMount(VolumeInfo vol, int newState) { ++ if (!SecurityLog.isLoggingEnabled()) { ++ return; ++ } ++ ++ final DiskInfo disk = vol.getDisk(); ++ if (disk == null || (disk.flags & (DiskInfo.FLAG_SD | DiskInfo.FLAG_USB)) == 0) { ++ return; ++ } ++ ++ // Sometimes there is a newline character. ++ final String label = disk.label != null ? disk.label.trim() : ""; ++ ++ if (newState == VolumeInfo.STATE_MOUNTED ++ || newState == VolumeInfo.STATE_MOUNTED_READ_ONLY) { ++ SecurityLog.writeEvent(SecurityLog.TAG_MEDIA_MOUNT, vol.path, label); ++ } else if (newState == VolumeInfo.STATE_UNMOUNTED ++ || newState == VolumeInfo.STATE_BAD_REMOVAL) { ++ SecurityLog.writeEvent(SecurityLog.TAG_MEDIA_UNMOUNT, vol.path, label); ++ } ++ } ++ ++ @GuardedBy("mLock") ++ private void onMoveStatusLocked(int status) { ++ if (mMoveCallback == null) { ++ Slog.w(TAG, "Odd, status but no move requested"); ++ return; ++ } ++ ++ // TODO: estimate remaining time ++ try { ++ mMoveCallback.onStatusChanged(-1, status, -1); ++ } catch (RemoteException ignored) { ++ } ++ ++ // We've finished copying and we're about to clean up old data, so ++ // remember that move was successful if we get rebooted ++ if (status == MOVE_STATUS_COPY_FINISHED) { ++ Slog.d(TAG, "Move to " + mMoveTargetUuid + " copy phase finshed; persisting"); ++ ++ mPrimaryStorageUuid = mMoveTargetUuid; ++ writeSettingsLocked(); ++ } ++ ++ if (PackageManager.isMoveStatusFinished(status)) { ++ Slog.d(TAG, "Move to " + mMoveTargetUuid + " finished with status " + status); ++ ++ mMoveCallback = null; ++ mMoveTargetUuid = null; ++ } ++ } ++ ++ private void enforcePermission(String perm) { ++ mContext.enforceCallingOrSelfPermission(perm, perm); ++ } ++ ++ /** ++ * Decide if volume is mountable per device policies. ++ */ ++ private boolean isMountDisallowed(VolumeInfo vol) { ++ UserManager userManager = mContext.getSystemService(UserManager.class); ++ ++ boolean isUsbRestricted = false; ++ if (vol.disk != null && vol.disk.isUsb()) { ++ isUsbRestricted = userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER, ++ Binder.getCallingUserHandle()); ++ } ++ ++ boolean isTypeRestricted = false; ++ if (vol.type == VolumeInfo.TYPE_PUBLIC || vol.type == VolumeInfo.TYPE_PRIVATE ++ || vol.type == VolumeInfo.TYPE_STUB) { ++ isTypeRestricted = userManager ++ .hasUserRestriction(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA, ++ Binder.getCallingUserHandle()); ++ } ++ ++ return isUsbRestricted || isTypeRestricted; ++ } ++ ++ private void enforceAdminUser() { ++ UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); ++ final int callingUserId = UserHandle.getCallingUserId(); ++ boolean isAdmin; ++ long token = Binder.clearCallingIdentity(); ++ try { ++ isAdmin = um.getUserInfo(callingUserId).isAdmin(); ++ } finally { ++ Binder.restoreCallingIdentity(token); ++ } ++ if (!isAdmin) { ++ throw new SecurityException("Only admin users can adopt sd cards"); ++ } ++ } ++ ++ /** ++ * Constructs a new StorageManagerService instance ++ * ++ * @param context Binder context for this service ++ */ ++ public StorageManagerService(Context context) { ++ sSelf = this; ++ ++ // Snapshot feature flag used for this boot ++ SystemProperties.set(StorageManager.PROP_ISOLATED_STORAGE_SNAPSHOT, Boolean.toString( ++ SystemProperties.getBoolean(StorageManager.PROP_ISOLATED_STORAGE, true))); ++ ++ // If there is no value in the property yet (first boot after data wipe), this value may be ++ // incorrect until #updateFusePropFromSettings where we set the correct value and reboot if ++ // different ++ mIsFuseEnabled = SystemProperties.getBoolean(PROP_FUSE, DEFAULT_FUSE_ENABLED); ++ mVoldAppDataIsolationEnabled = mIsFuseEnabled && SystemProperties.getBoolean( ++ ANDROID_VOLD_APP_DATA_ISOLATION_ENABLED_PROPERTY, false); ++ mContext = context; ++ mResolver = mContext.getContentResolver(); ++ mCallbacks = new Callbacks(FgThread.get().getLooper()); ++ mLockPatternUtils = new LockPatternUtils(mContext); ++ ++ HandlerThread hthread = new HandlerThread(TAG); ++ hthread.start(); ++ mHandler = new StorageManagerServiceHandler(hthread.getLooper()); ++ ++ // Add OBB Action Handler to StorageManagerService thread. ++ mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper()); ++ ++ mStorageSessionController = new StorageSessionController(mContext, mIsFuseEnabled); ++ ++ mInstaller = new Installer(mContext); ++ mInstaller.onStart(); ++ ++ // Initialize the last-fstrim tracking if necessary ++ File dataDir = Environment.getDataDirectory(); ++ File systemDir = new File(dataDir, "system"); ++ mLastMaintenanceFile = new File(systemDir, LAST_FSTRIM_FILE); ++ if (!mLastMaintenanceFile.exists()) { ++ // Not setting mLastMaintenance here means that we will force an ++ // fstrim during reboot following the OTA that installs this code. ++ try { ++ (new FileOutputStream(mLastMaintenanceFile)).close(); ++ } catch (IOException e) { ++ Slog.e(TAG, "Unable to create fstrim record " + mLastMaintenanceFile.getPath()); ++ } ++ } else { ++ mLastMaintenance = mLastMaintenanceFile.lastModified(); ++ } ++ ++ mSettingsFile = new AtomicFile( ++ new File(Environment.getDataSystemDirectory(), "storage.xml"), "storage-settings"); ++ ++ synchronized (mLock) { ++ readSettingsLocked(); ++ } ++ ++ LocalServices.addService(StorageManagerInternal.class, mStorageManagerInternal); ++ ++ final IntentFilter userFilter = new IntentFilter(); ++ userFilter.addAction(Intent.ACTION_USER_ADDED); ++ userFilter.addAction(Intent.ACTION_USER_REMOVED); ++ mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler); ++ ++ synchronized (mLock) { ++ addInternalVolumeLocked(); ++ } ++ ++ // Add ourself to the Watchdog monitors if enabled. ++ if (WATCHDOG_ENABLE) { ++ Watchdog.getInstance().addMonitor(this); ++ } ++ ++ mIsAutomotive = context.getPackageManager().hasSystemFeature( ++ PackageManager.FEATURE_AUTOMOTIVE); ++ } ++ ++ /** ++ * Checks if user changed the persistent settings_fuse flag from Settings UI ++ * and updates PROP_FUSE (reboots if changed). ++ */ ++ private void updateFusePropFromSettings() { ++ boolean settingsFuseFlag = SystemProperties.getBoolean(PROP_SETTINGS_FUSE, ++ DEFAULT_FUSE_ENABLED); ++ Slog.d(TAG, "FUSE flags. Settings: " + settingsFuseFlag ++ + ". Default: " + DEFAULT_FUSE_ENABLED); ++ ++ if (mIsFuseEnabled != settingsFuseFlag) { ++ Slog.i(TAG, "Toggling persist.sys.fuse to " + settingsFuseFlag); ++ // Set prop_fuse to match prop_settings_fuse because it is used by native daemons like ++ // init, zygote, installd and vold ++ SystemProperties.set(PROP_FUSE, Boolean.toString(settingsFuseFlag)); ++ // Then perform hard reboot to kick policy into place ++ mContext.getSystemService(PowerManager.class).reboot("fuse_prop"); ++ } ++ } ++ ++ private void start() { ++ connectStoraged(); ++ connectVold(); ++ } ++ ++ private void connectStoraged() { ++ IBinder binder = ServiceManager.getService("storaged"); ++ if (binder != null) { ++ try { ++ binder.linkToDeath(new DeathRecipient() { ++ @Override ++ public void binderDied() { ++ Slog.w(TAG, "storaged died; reconnecting"); ++ mStoraged = null; ++ connectStoraged(); ++ } ++ }, 0); ++ } catch (RemoteException e) { ++ binder = null; ++ } ++ } ++ ++ if (binder != null) { ++ mStoraged = IStoraged.Stub.asInterface(binder); ++ } else { ++ Slog.w(TAG, "storaged not found; trying again"); ++ } ++ ++ if (mStoraged == null) { ++ BackgroundThread.getHandler().postDelayed(() -> { ++ connectStoraged(); ++ }, DateUtils.SECOND_IN_MILLIS); ++ } else { ++ onDaemonConnected(); ++ } ++ } ++ ++ private void connectVold() { ++ IBinder binder = ServiceManager.getService("vold"); ++ if (binder != null) { ++ try { ++ binder.linkToDeath(new DeathRecipient() { ++ @Override ++ public void binderDied() { ++ Slog.w(TAG, "vold died; reconnecting"); ++ mVold = null; ++ connectVold(); ++ } ++ }, 0); ++ } catch (RemoteException e) { ++ binder = null; ++ } ++ } ++ ++ if (binder != null) { ++ mVold = IVold.Stub.asInterface(binder); ++ try { ++ mVold.setListener(mListener); ++ } catch (RemoteException e) { ++ mVold = null; ++ Slog.w(TAG, "vold listener rejected; trying again", e); ++ } ++ } else { ++ Slog.w(TAG, "vold not found; trying again"); ++ } ++ ++ if (mVold == null) { ++ BackgroundThread.getHandler().postDelayed(() -> { ++ connectVold(); ++ }, DateUtils.SECOND_IN_MILLIS); ++ } else { ++ onDaemonConnected(); ++ } ++ } ++ ++ private void servicesReady() { ++ mPmInternal = LocalServices.getService(PackageManagerInternal.class); ++ ++ mIPackageManager = IPackageManager.Stub.asInterface( ++ ServiceManager.getService("package")); ++ mIAppOpsService = IAppOpsService.Stub.asInterface( ++ ServiceManager.getService(Context.APP_OPS_SERVICE)); ++ ++ ProviderInfo provider = getProviderInfo(MediaStore.AUTHORITY); ++ if (provider != null) { ++ mMediaStoreAuthorityAppId = UserHandle.getAppId(provider.applicationInfo.uid); ++ sMediaStoreAuthorityProcessName = provider.applicationInfo.processName; ++ } ++ ++ provider = getProviderInfo(Downloads.Impl.AUTHORITY); ++ if (provider != null) { ++ mDownloadsAuthorityAppId = UserHandle.getAppId(provider.applicationInfo.uid); ++ } ++ ++ provider = getProviderInfo(DocumentsContract.EXTERNAL_STORAGE_PROVIDER_AUTHORITY); ++ if (provider != null) { ++ mExternalStorageAuthorityAppId = UserHandle.getAppId(provider.applicationInfo.uid); ++ } ++ ++ if (!mIsFuseEnabled) { ++ try { ++ mIAppOpsService.startWatchingMode(OP_REQUEST_INSTALL_PACKAGES, null, ++ mAppOpsCallback); ++ mIAppOpsService.startWatchingMode(OP_LEGACY_STORAGE, null, mAppOpsCallback); ++ } catch (RemoteException e) { ++ } ++ } ++ } ++ ++ private ProviderInfo getProviderInfo(String authority) { ++ return mPmInternal.resolveContentProvider( ++ authority, PackageManager.MATCH_DIRECT_BOOT_AWARE ++ | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, ++ UserHandle.getUserId(UserHandle.USER_SYSTEM)); ++ } ++ ++ private void updateLegacyStorageApps(String packageName, int uid, boolean hasLegacy) { ++ synchronized (mLock) { ++ if (hasLegacy) { ++ Slog.v(TAG, "Package " + packageName + " has legacy storage"); ++ mUidsWithLegacyExternalStorage.add(uid); ++ } else { ++ // TODO(b/149391976): Handle shared user id. Check if there's any other ++ // installed app with legacy external storage before removing ++ Slog.v(TAG, "Package " + packageName + " does not have legacy storage"); ++ mUidsWithLegacyExternalStorage.remove(uid); ++ } ++ } ++ } ++ ++ private void snapshotAndMonitorLegacyStorageAppOp(UserHandle user) { ++ int userId = user.getIdentifier(); ++ ++ // TODO(b/149391976): Use mIAppOpsService.getPackagesForOps instead of iterating below ++ // It should improve performance but the AppOps method doesn't return any app here :( ++ // This operation currently takes about ~20ms on a freshly flashed device ++ for (ApplicationInfo ai : mPmInternal.getInstalledApplications(MATCH_DIRECT_BOOT_AWARE ++ | MATCH_DIRECT_BOOT_UNAWARE | MATCH_UNINSTALLED_PACKAGES | MATCH_ANY_USER, ++ userId, Process.myUid())) { ++ try { ++ boolean hasLegacy = mIAppOpsService.checkOperation(OP_LEGACY_STORAGE, ai.uid, ++ ai.packageName) == MODE_ALLOWED; ++ updateLegacyStorageApps(ai.packageName, ai.uid, hasLegacy); ++ } catch (RemoteException e) { ++ Slog.e(TAG, "Failed to check legacy op for package " + ai.packageName, e); ++ } ++ } ++ ++ PackageMonitor monitor = new PackageMonitor() { ++ @Override ++ public void onPackageRemoved(String packageName, int uid) { ++ updateLegacyStorageApps(packageName, uid, false); ++ } ++ }; ++ // TODO(b/149391976): Use different handler? ++ monitor.register(mContext, user, true, mHandler); ++ mPackageMonitorsForUser.put(userId, monitor); ++ } ++ ++ private static long getLastAccessTime(AppOpsManager manager, ++ int uid, String packageName, int[] ops) { ++ long maxTime = 0; ++ final List pkgs = manager.getOpsForPackage(uid, packageName, ops); ++ for (AppOpsManager.PackageOps pkg : CollectionUtils.emptyIfNull(pkgs)) { ++ for (AppOpsManager.OpEntry op : CollectionUtils.emptyIfNull(pkg.getOps())) { ++ maxTime = Math.max(maxTime, op.getLastAccessTime( ++ AppOpsManager.OP_FLAGS_ALL_TRUSTED)); ++ } ++ } ++ return maxTime; ++ } ++ ++ private void systemReady() { ++ LocalServices.getService(ActivityTaskManagerInternal.class) ++ .registerScreenObserver(this); ++ ++ mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget(); ++ } ++ ++ private void bootCompleted() { ++ mBootCompleted = true; ++ mHandler.obtainMessage(H_BOOT_COMPLETED).sendToTarget(); ++ updateFusePropFromSettings(); ++ } ++ ++ private void handleBootCompleted() { ++ initIfBootedAndConnected(); ++ resetIfBootedAndConnected(); ++ } ++ ++ private String getDefaultPrimaryStorageUuid() { ++ if (SystemProperties.getBoolean(StorageManager.PROP_PRIMARY_PHYSICAL, false)) { ++ return StorageManager.UUID_PRIMARY_PHYSICAL; ++ } else { ++ return StorageManager.UUID_PRIVATE_INTERNAL; ++ } ++ } ++ ++ @GuardedBy("mLock") ++ private void readSettingsLocked() { ++ mRecords.clear(); ++ mPrimaryStorageUuid = getDefaultPrimaryStorageUuid(); ++ ++ FileInputStream fis = null; ++ try { ++ fis = mSettingsFile.openRead(); ++ final XmlPullParser in = Xml.newPullParser(); ++ in.setInput(fis, StandardCharsets.UTF_8.name()); ++ ++ int type; ++ while ((type = in.next()) != END_DOCUMENT) { ++ if (type == START_TAG) { ++ final String tag = in.getName(); ++ if (TAG_VOLUMES.equals(tag)) { ++ final int version = readIntAttribute(in, ATTR_VERSION, VERSION_INIT); ++ final boolean primaryPhysical = SystemProperties.getBoolean( ++ StorageManager.PROP_PRIMARY_PHYSICAL, false); ++ final boolean validAttr = (version >= VERSION_FIX_PRIMARY) ++ || (version >= VERSION_ADD_PRIMARY && !primaryPhysical); ++ if (validAttr) { ++ mPrimaryStorageUuid = readStringAttribute(in, ++ ATTR_PRIMARY_STORAGE_UUID); ++ } ++ } else if (TAG_VOLUME.equals(tag)) { ++ final VolumeRecord rec = readVolumeRecord(in); ++ mRecords.put(rec.fsUuid, rec); ++ } ++ } ++ } ++ } catch (FileNotFoundException e) { ++ // Missing metadata is okay, probably first boot ++ } catch (IOException e) { ++ Slog.wtf(TAG, "Failed reading metadata", e); ++ } catch (XmlPullParserException e) { ++ Slog.wtf(TAG, "Failed reading metadata", e); ++ } finally { ++ IoUtils.closeQuietly(fis); ++ } ++ } ++ ++ @GuardedBy("mLock") ++ private void writeSettingsLocked() { ++ FileOutputStream fos = null; ++ try { ++ fos = mSettingsFile.startWrite(); ++ ++ XmlSerializer out = new FastXmlSerializer(); ++ out.setOutput(fos, StandardCharsets.UTF_8.name()); ++ out.startDocument(null, true); ++ out.startTag(null, TAG_VOLUMES); ++ writeIntAttribute(out, ATTR_VERSION, VERSION_FIX_PRIMARY); ++ writeStringAttribute(out, ATTR_PRIMARY_STORAGE_UUID, mPrimaryStorageUuid); ++ final int size = mRecords.size(); ++ for (int i = 0; i < size; i++) { ++ final VolumeRecord rec = mRecords.valueAt(i); ++ writeVolumeRecord(out, rec); ++ } ++ out.endTag(null, TAG_VOLUMES); ++ out.endDocument(); ++ ++ mSettingsFile.finishWrite(fos); ++ } catch (IOException e) { ++ if (fos != null) { ++ mSettingsFile.failWrite(fos); ++ } ++ } ++ } ++ ++ public static VolumeRecord readVolumeRecord(XmlPullParser in) throws IOException { ++ final int type = readIntAttribute(in, ATTR_TYPE); ++ final String fsUuid = readStringAttribute(in, ATTR_FS_UUID); ++ final VolumeRecord meta = new VolumeRecord(type, fsUuid); ++ meta.partGuid = readStringAttribute(in, ATTR_PART_GUID); ++ meta.nickname = readStringAttribute(in, ATTR_NICKNAME); ++ meta.userFlags = readIntAttribute(in, ATTR_USER_FLAGS); ++ meta.createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS, 0); ++ meta.lastSeenMillis = readLongAttribute(in, ATTR_LAST_SEEN_MILLIS, 0); ++ meta.lastTrimMillis = readLongAttribute(in, ATTR_LAST_TRIM_MILLIS, 0); ++ meta.lastBenchMillis = readLongAttribute(in, ATTR_LAST_BENCH_MILLIS, 0); ++ return meta; ++ } ++ ++ public static void writeVolumeRecord(XmlSerializer out, VolumeRecord rec) throws IOException { ++ out.startTag(null, TAG_VOLUME); ++ writeIntAttribute(out, ATTR_TYPE, rec.type); ++ writeStringAttribute(out, ATTR_FS_UUID, rec.fsUuid); ++ writeStringAttribute(out, ATTR_PART_GUID, rec.partGuid); ++ writeStringAttribute(out, ATTR_NICKNAME, rec.nickname); ++ writeIntAttribute(out, ATTR_USER_FLAGS, rec.userFlags); ++ writeLongAttribute(out, ATTR_CREATED_MILLIS, rec.createdMillis); ++ writeLongAttribute(out, ATTR_LAST_SEEN_MILLIS, rec.lastSeenMillis); ++ writeLongAttribute(out, ATTR_LAST_TRIM_MILLIS, rec.lastTrimMillis); ++ writeLongAttribute(out, ATTR_LAST_BENCH_MILLIS, rec.lastBenchMillis); ++ out.endTag(null, TAG_VOLUME); ++ } ++ ++ /** ++ * Exposed API calls below here ++ */ ++ ++ @Override ++ public void registerListener(IStorageEventListener listener) { ++ mCallbacks.register(listener); ++ } ++ ++ @Override ++ public void unregisterListener(IStorageEventListener listener) { ++ mCallbacks.unregister(listener); ++ } ++ ++ @Override ++ public void shutdown(final IStorageShutdownObserver observer) { ++ enforcePermission(android.Manifest.permission.SHUTDOWN); ++ ++ Slog.i(TAG, "Shutting down"); ++ mHandler.obtainMessage(H_SHUTDOWN, observer).sendToTarget(); ++ } ++ ++ @Override ++ public void mount(String volId) { ++ enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); ++ ++ final VolumeInfo vol = findVolumeByIdOrThrow(volId); ++ if (isMountDisallowed(vol)) { ++ throw new SecurityException("Mounting " + volId + " restricted by policy"); ++ } ++ ++ mount(vol); ++ } ++ ++ private void remountAppStorageDirs(Map pidPkgMap, int userId) { ++ for (Entry entry : pidPkgMap.entrySet()) { ++ final int pid = entry.getKey(); ++ final String packageName = entry.getValue(); ++ Slog.i(TAG, "Remounting storage for pid: " + pid); ++ final String[] sharedPackages = ++ mPmInternal.getSharedUserPackagesForPackage(packageName, userId); ++ final int uid = mPmInternal.getPackageUidInternal(packageName, 0, userId); ++ final String[] packages = ++ sharedPackages.length != 0 ? sharedPackages : new String[]{packageName}; ++ try { ++ mVold.remountAppStorageDirs(uid, pid, packages); ++ } catch (RemoteException e) { ++ throw e.rethrowAsRuntimeException(); ++ } ++ } ++ } ++ ++ private void mount(VolumeInfo vol) { ++ try { ++ // TODO(b/135341433): Remove paranoid logging when FUSE is stable ++ Slog.i(TAG, "Mounting volume " + vol); ++ mVold.mount(vol.id, vol.mountFlags, vol.mountUserId, new IVoldMountCallback.Stub() { ++ @Override ++ public boolean onVolumeChecking(FileDescriptor fd, String path, ++ String internalPath) { ++ vol.path = path; ++ vol.internalPath = internalPath; ++ ParcelFileDescriptor pfd = new ParcelFileDescriptor(fd); ++ try { ++ mStorageSessionController.onVolumeMount(pfd, vol); ++ return true; ++ } catch (ExternalStorageServiceException e) { ++ Slog.e(TAG, "Failed to mount volume " + vol, e); ++ ++ int nextResetSeconds = FAILED_MOUNT_RESET_TIMEOUT_SECONDS; ++ Slog.i(TAG, "Scheduling reset in " + nextResetSeconds + "s"); ++ mHandler.removeMessages(H_RESET); ++ mHandler.sendMessageDelayed(mHandler.obtainMessage(H_RESET), ++ TimeUnit.SECONDS.toMillis(nextResetSeconds)); ++ return false; ++ } finally { ++ try { ++ pfd.close(); ++ } catch (Exception e) { ++ Slog.e(TAG, "Failed to close FUSE device fd", e); ++ } ++ } ++ } ++ }); ++ Slog.i(TAG, "Mounted volume " + vol); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ } ++ } ++ ++ @Override ++ public void unmount(String volId) { ++ enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); ++ ++ final VolumeInfo vol = findVolumeByIdOrThrow(volId); ++ unmount(vol); ++ } ++ ++ private void unmount(VolumeInfo vol) { ++ try { ++ try { ++ if (vol.type == VolumeInfo.TYPE_PRIVATE) { ++ mInstaller.onPrivateVolumeRemoved(vol.getFsUuid()); ++ } ++ } catch (Installer.InstallerException e) { ++ Slog.e(TAG, "Failed unmount mirror data", e); ++ } ++ mVold.unmount(vol.id); ++ mStorageSessionController.onVolumeUnmount(vol); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ } ++ } ++ ++ @Override ++ public void format(String volId) { ++ enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); ++ ++ final VolumeInfo vol = findVolumeByIdOrThrow(volId); ++ final String fsUuid = vol.fsUuid; ++ try { ++ mVold.format(vol.id, "auto"); ++ ++ // After a successful format above, we should forget about any ++ // records for the old partition, since it'll never appear again ++ if (!TextUtils.isEmpty(fsUuid)) { ++ forgetVolume(fsUuid); ++ } ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ } ++ } ++ ++ @Override ++ public void benchmark(String volId, IVoldTaskListener listener) { ++ enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); ++ ++ try { ++ mVold.benchmark(volId, new IVoldTaskListener.Stub() { ++ @Override ++ public void onStatus(int status, PersistableBundle extras) { ++ dispatchOnStatus(listener, status, extras); ++ } ++ ++ @Override ++ public void onFinished(int status, PersistableBundle extras) { ++ dispatchOnFinished(listener, status, extras); ++ ++ final String path = extras.getString("path"); ++ final String ident = extras.getString("ident"); ++ final long create = extras.getLong("create"); ++ final long run = extras.getLong("run"); ++ final long destroy = extras.getLong("destroy"); ++ ++ final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class); ++ dropBox.addText(TAG_STORAGE_BENCHMARK, scrubPath(path) ++ + " " + ident + " " + create + " " + run + " " + destroy); ++ ++ synchronized (mLock) { ++ final VolumeRecord rec = findRecordForPath(path); ++ if (rec != null) { ++ rec.lastBenchMillis = System.currentTimeMillis(); ++ writeSettingsLocked(); ++ } ++ } ++ } ++ }); ++ } catch (RemoteException e) { ++ throw e.rethrowAsRuntimeException(); ++ } ++ } ++ ++ @Override ++ public void partitionPublic(String diskId) { ++ enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); ++ ++ final CountDownLatch latch = findOrCreateDiskScanLatch(diskId); ++ try { ++ mVold.partition(diskId, IVold.PARTITION_TYPE_PUBLIC, -1); ++ waitForLatch(latch, "partitionPublic", 3 * DateUtils.MINUTE_IN_MILLIS); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ } ++ } ++ ++ @Override ++ public void partitionPrivate(String diskId) { ++ enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); ++ enforceAdminUser(); ++ ++ final CountDownLatch latch = findOrCreateDiskScanLatch(diskId); ++ try { ++ mVold.partition(diskId, IVold.PARTITION_TYPE_PRIVATE, -1); ++ waitForLatch(latch, "partitionPrivate", 3 * DateUtils.MINUTE_IN_MILLIS); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ } ++ } ++ ++ @Override ++ public void partitionMixed(String diskId, int ratio) { ++ enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); ++ enforceAdminUser(); ++ ++ final CountDownLatch latch = findOrCreateDiskScanLatch(diskId); ++ try { ++ mVold.partition(diskId, IVold.PARTITION_TYPE_MIXED, ratio); ++ waitForLatch(latch, "partitionMixed", 3 * DateUtils.MINUTE_IN_MILLIS); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ } ++ } ++ ++ @Override ++ public void setVolumeNickname(String fsUuid, String nickname) { ++ enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); ++ ++ Objects.requireNonNull(fsUuid); ++ synchronized (mLock) { ++ final VolumeRecord rec = mRecords.get(fsUuid); ++ rec.nickname = nickname; ++ mCallbacks.notifyVolumeRecordChanged(rec); ++ writeSettingsLocked(); ++ } ++ } ++ ++ @Override ++ public void setVolumeUserFlags(String fsUuid, int flags, int mask) { ++ enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); ++ ++ Objects.requireNonNull(fsUuid); ++ synchronized (mLock) { ++ final VolumeRecord rec = mRecords.get(fsUuid); ++ rec.userFlags = (rec.userFlags & ~mask) | (flags & mask); ++ mCallbacks.notifyVolumeRecordChanged(rec); ++ writeSettingsLocked(); ++ } ++ } ++ ++ @Override ++ public void forgetVolume(String fsUuid) { ++ enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); ++ ++ Objects.requireNonNull(fsUuid); ++ ++ synchronized (mLock) { ++ final VolumeRecord rec = mRecords.remove(fsUuid); ++ if (rec != null && !TextUtils.isEmpty(rec.partGuid)) { ++ mHandler.obtainMessage(H_PARTITION_FORGET, rec).sendToTarget(); ++ } ++ mCallbacks.notifyVolumeForgotten(fsUuid); ++ ++ // If this had been primary storage, revert back to internal and ++ // reset vold so we bind into new volume into place. ++ if (Objects.equals(mPrimaryStorageUuid, fsUuid)) { ++ mPrimaryStorageUuid = getDefaultPrimaryStorageUuid(); ++ mHandler.obtainMessage(H_RESET).sendToTarget(); ++ } ++ ++ writeSettingsLocked(); ++ } ++ } ++ ++ @Override ++ public void forgetAllVolumes() { ++ enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); ++ ++ synchronized (mLock) { ++ for (int i = 0; i < mRecords.size(); i++) { ++ final String fsUuid = mRecords.keyAt(i); ++ final VolumeRecord rec = mRecords.valueAt(i); ++ if (!TextUtils.isEmpty(rec.partGuid)) { ++ mHandler.obtainMessage(H_PARTITION_FORGET, rec).sendToTarget(); ++ } ++ mCallbacks.notifyVolumeForgotten(fsUuid); ++ } ++ mRecords.clear(); ++ ++ if (!Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)) { ++ mPrimaryStorageUuid = getDefaultPrimaryStorageUuid(); ++ } ++ ++ writeSettingsLocked(); ++ mHandler.obtainMessage(H_RESET).sendToTarget(); ++ } ++ } ++ ++ private void forgetPartition(String partGuid, String fsUuid) { ++ try { ++ mVold.forgetPartition(partGuid, fsUuid); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ } ++ } ++ ++ @Override ++ public void fstrim(int flags, IVoldTaskListener listener) { ++ enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); ++ ++ try { ++ // Block based checkpoint process runs fstrim. So, if checkpoint is in progress ++ // (first boot after OTA), We skip idle maintenance and make sure the last ++ // fstrim time is still updated. If file based checkpoints are used, we run ++ // idle maintenance (GC + fstrim) regardless of checkpoint status. ++ if (!needsCheckpoint() || !supportsBlockCheckpoint()) { ++ mVold.fstrim(flags, new IVoldTaskListener.Stub() { ++ @Override ++ public void onStatus(int status, PersistableBundle extras) { ++ dispatchOnStatus(listener, status, extras); ++ ++ // Ignore trim failures ++ if (status != 0) return; ++ ++ final String path = extras.getString("path"); ++ final long bytes = extras.getLong("bytes"); ++ final long time = extras.getLong("time"); ++ ++ final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class); ++ dropBox.addText(TAG_STORAGE_TRIM, scrubPath(path) + " " + bytes + " " + time); ++ ++ synchronized (mLock) { ++ final VolumeRecord rec = findRecordForPath(path); ++ if (rec != null) { ++ rec.lastTrimMillis = System.currentTimeMillis(); ++ writeSettingsLocked(); ++ } ++ } ++ } ++ ++ @Override ++ public void onFinished(int status, PersistableBundle extras) { ++ dispatchOnFinished(listener, status, extras); ++ ++ // TODO: benchmark when desired ++ } ++ }); ++ } else { ++ Slog.i(TAG, "Skipping fstrim - block based checkpoint in progress"); ++ } ++ } catch (RemoteException e) { ++ throw e.rethrowAsRuntimeException(); ++ } ++ } ++ ++ void runIdleMaint(Runnable callback) { ++ enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); ++ ++ try { ++ // Block based checkpoint process runs fstrim. So, if checkpoint is in progress ++ // (first boot after OTA), We skip idle maintenance and make sure the last ++ // fstrim time is still updated. If file based checkpoints are used, we run ++ // idle maintenance (GC + fstrim) regardless of checkpoint status. ++ if (!needsCheckpoint() || !supportsBlockCheckpoint()) { ++ mVold.runIdleMaint(new IVoldTaskListener.Stub() { ++ @Override ++ public void onStatus(int status, PersistableBundle extras) { ++ // Not currently used ++ } ++ @Override ++ public void onFinished(int status, PersistableBundle extras) { ++ if (callback != null) { ++ BackgroundThread.getHandler().post(callback); ++ } ++ } ++ }); ++ } else { ++ Slog.i(TAG, "Skipping idle maintenance - block based checkpoint in progress"); ++ } ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ } ++ } ++ ++ @Override ++ public void runIdleMaintenance() { ++ runIdleMaint(null); ++ } ++ ++ void abortIdleMaint(Runnable callback) { ++ enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); ++ ++ try { ++ mVold.abortIdleMaint(new IVoldTaskListener.Stub() { ++ @Override ++ public void onStatus(int status, PersistableBundle extras) { ++ // Not currently used ++ } ++ @Override ++ public void onFinished(int status, PersistableBundle extras) { ++ if (callback != null) { ++ BackgroundThread.getHandler().post(callback); ++ } ++ } ++ }); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ } ++ } ++ ++ @Override ++ public void abortIdleMaintenance() { ++ abortIdleMaint(null); ++ } ++ ++ private void remountUidExternalStorage(int uid, int mode) { ++ if (uid == Process.SYSTEM_UID) { ++ // No need to remount uid for system because it has all access anyways ++ return; ++ } ++ ++ try { ++ mVold.remountUid(uid, mode); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ } ++ } ++ ++ @Override ++ public void setDebugFlags(int flags, int mask) { ++ enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); ++ ++ if ((mask & StorageManager.DEBUG_EMULATE_FBE) != 0) { ++ if (!EMULATE_FBE_SUPPORTED) { ++ throw new IllegalStateException( ++ "Emulation not supported on this device"); ++ } ++ if (StorageManager.isFileEncryptedNativeOnly()) { ++ throw new IllegalStateException( ++ "Emulation not supported on device with native FBE"); ++ } ++ if (mLockPatternUtils.isCredentialRequiredToDecrypt(false)) { ++ throw new IllegalStateException( ++ "Emulation requires disabling 'Secure start-up' in Settings > Security"); ++ } ++ ++ final long token = Binder.clearCallingIdentity(); ++ try { ++ final boolean emulateFbe = (flags & StorageManager.DEBUG_EMULATE_FBE) != 0; ++ SystemProperties.set(StorageManager.PROP_EMULATE_FBE, Boolean.toString(emulateFbe)); ++ ++ // Perform hard reboot to kick policy into place ++ mContext.getSystemService(PowerManager.class).reboot(null); ++ } finally { ++ Binder.restoreCallingIdentity(token); ++ } ++ } ++ ++ if ((mask & (StorageManager.DEBUG_ADOPTABLE_FORCE_ON ++ | StorageManager.DEBUG_ADOPTABLE_FORCE_OFF)) != 0) { ++ final String value; ++ if ((flags & StorageManager.DEBUG_ADOPTABLE_FORCE_ON) != 0) { ++ value = "force_on"; ++ } else if ((flags & StorageManager.DEBUG_ADOPTABLE_FORCE_OFF) != 0) { ++ value = "force_off"; ++ } else { ++ value = ""; ++ } ++ ++ final long token = Binder.clearCallingIdentity(); ++ try { ++ SystemProperties.set(StorageManager.PROP_ADOPTABLE, value); ++ ++ // Reset storage to kick new setting into place ++ mHandler.obtainMessage(H_RESET).sendToTarget(); ++ } finally { ++ Binder.restoreCallingIdentity(token); ++ } ++ } ++ ++ if ((mask & (StorageManager.DEBUG_SDCARDFS_FORCE_ON ++ | StorageManager.DEBUG_SDCARDFS_FORCE_OFF)) != 0) { ++ final String value; ++ if ((flags & StorageManager.DEBUG_SDCARDFS_FORCE_ON) != 0) { ++ value = "force_on"; ++ } else if ((flags & StorageManager.DEBUG_SDCARDFS_FORCE_OFF) != 0) { ++ value = "force_off"; ++ } else { ++ value = ""; ++ } ++ ++ final long token = Binder.clearCallingIdentity(); ++ try { ++ SystemProperties.set(StorageManager.PROP_SDCARDFS, value); ++ ++ // Reset storage to kick new setting into place ++ mHandler.obtainMessage(H_RESET).sendToTarget(); ++ } finally { ++ Binder.restoreCallingIdentity(token); ++ } ++ } ++ ++ if ((mask & StorageManager.DEBUG_VIRTUAL_DISK) != 0) { ++ final boolean enabled = (flags & StorageManager.DEBUG_VIRTUAL_DISK) != 0; ++ ++ final long token = Binder.clearCallingIdentity(); ++ try { ++ SystemProperties.set(StorageManager.PROP_VIRTUAL_DISK, Boolean.toString(enabled)); ++ ++ // Reset storage to kick new setting into place ++ mHandler.obtainMessage(H_RESET).sendToTarget(); ++ } finally { ++ Binder.restoreCallingIdentity(token); ++ } ++ } ++ ++ if ((mask & (StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_ON ++ | StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_OFF)) != 0) { ++ final int value; ++ if ((flags & StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_ON) != 0) { ++ value = 1; ++ } else if ((flags & StorageManager.DEBUG_ISOLATED_STORAGE_FORCE_OFF) != 0) { ++ value = -1; ++ } else { ++ value = 0; ++ } ++ ++ final long token = Binder.clearCallingIdentity(); ++ try { ++ Settings.Global.putInt(mContext.getContentResolver(), ++ Settings.Global.ISOLATED_STORAGE_LOCAL, value); ++ refreshIsolatedStorageSettings(); ++ ++ // Perform hard reboot to kick policy into place ++ mHandler.post(() -> { ++ mContext.getSystemService(PowerManager.class).reboot(null); ++ }); ++ } finally { ++ Binder.restoreCallingIdentity(token); ++ } ++ } ++ } ++ ++ @Override ++ public String getPrimaryStorageUuid() { ++ synchronized (mLock) { ++ return mPrimaryStorageUuid; ++ } ++ } ++ ++ @Override ++ public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) { ++ enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); ++ ++ final VolumeInfo from; ++ final VolumeInfo to; ++ ++ synchronized (mLock) { ++ if (Objects.equals(mPrimaryStorageUuid, volumeUuid)) { ++ throw new IllegalArgumentException("Primary storage already at " + volumeUuid); ++ } ++ ++ if (mMoveCallback != null) { ++ throw new IllegalStateException("Move already in progress"); ++ } ++ mMoveCallback = callback; ++ mMoveTargetUuid = volumeUuid; ++ ++ // We need all the users unlocked to move their primary storage ++ final List users = mContext.getSystemService(UserManager.class).getUsers(); ++ for (UserInfo user : users) { ++ if (StorageManager.isFileEncryptedNativeOrEmulated() ++ && !isUserKeyUnlocked(user.id)) { ++ Slog.w(TAG, "Failing move due to locked user " + user.id); ++ onMoveStatusLocked(PackageManager.MOVE_FAILED_LOCKED_USER); ++ return; ++ } ++ } ++ ++ // When moving to/from primary physical volume, we probably just nuked ++ // the current storage location, so we have nothing to move. ++ if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, mPrimaryStorageUuid) ++ || Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) { ++ Slog.d(TAG, "Skipping move to/from primary physical"); ++ onMoveStatusLocked(MOVE_STATUS_COPY_FINISHED); ++ onMoveStatusLocked(PackageManager.MOVE_SUCCEEDED); ++ mHandler.obtainMessage(H_RESET).sendToTarget(); ++ return; ++ ++ } else { ++ from = findStorageForUuid(mPrimaryStorageUuid); ++ to = findStorageForUuid(volumeUuid); ++ ++ if (from == null) { ++ Slog.w(TAG, "Failing move due to missing from volume " + mPrimaryStorageUuid); ++ onMoveStatusLocked(PackageManager.MOVE_FAILED_INTERNAL_ERROR); ++ return; ++ } else if (to == null) { ++ Slog.w(TAG, "Failing move due to missing to volume " + volumeUuid); ++ onMoveStatusLocked(PackageManager.MOVE_FAILED_INTERNAL_ERROR); ++ return; ++ } ++ } ++ } ++ ++ try { ++ mVold.moveStorage(from.id, to.id, new IVoldTaskListener.Stub() { ++ @Override ++ public void onStatus(int status, PersistableBundle extras) { ++ synchronized (mLock) { ++ onMoveStatusLocked(status); ++ } ++ } ++ ++ @Override ++ public void onFinished(int status, PersistableBundle extras) { ++ // Not currently used ++ } ++ }); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ } ++ } ++ ++ private void warnOnNotMounted() { ++ synchronized (mLock) { ++ for (int i = 0; i < mVolumes.size(); i++) { ++ final VolumeInfo vol = mVolumes.valueAt(i); ++ if (vol.isPrimary() && vol.isMountedWritable()) { ++ // Cool beans, we have a mounted primary volume ++ return; ++ } ++ } ++ } ++ ++ Slog.w(TAG, "No primary storage mounted!"); ++ } ++ ++ private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) { ++ if (callerUid == android.os.Process.SYSTEM_UID) { ++ return true; ++ } ++ ++ if (packageName == null) { ++ return false; ++ } ++ ++ final int packageUid = mPmInternal.getPackageUid(packageName, ++ PackageManager.MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getUserId(callerUid)); ++ ++ if (DEBUG_OBB) { ++ Slog.d(TAG, "packageName = " + packageName + ", packageUid = " + ++ packageUid + ", callerUid = " + callerUid); ++ } ++ ++ return callerUid == packageUid; ++ } ++ ++ @Override ++ public String getMountedObbPath(String rawPath) { ++ Objects.requireNonNull(rawPath, "rawPath cannot be null"); ++ ++ warnOnNotMounted(); ++ ++ final ObbState state; ++ synchronized (mObbMounts) { ++ state = mObbPathToStateMap.get(rawPath); ++ } ++ if (state == null) { ++ Slog.w(TAG, "Failed to find OBB mounted at " + rawPath); ++ return null; ++ } ++ ++ return findVolumeByIdOrThrow(state.volId).getPath().getAbsolutePath(); ++ } ++ ++ @Override ++ public boolean isObbMounted(String rawPath) { ++ Objects.requireNonNull(rawPath, "rawPath cannot be null"); ++ synchronized (mObbMounts) { ++ return mObbPathToStateMap.containsKey(rawPath); ++ } ++ } ++ ++ @Override ++ public void mountObb(String rawPath, String canonicalPath, String key, ++ IObbActionListener token, int nonce, ObbInfo obbInfo) { ++ Objects.requireNonNull(rawPath, "rawPath cannot be null"); ++ Objects.requireNonNull(canonicalPath, "canonicalPath cannot be null"); ++ Objects.requireNonNull(token, "token cannot be null"); ++ Objects.requireNonNull(obbInfo, "obbIfno cannot be null"); ++ ++ final int callingUid = Binder.getCallingUid(); ++ final ObbState obbState = new ObbState(rawPath, canonicalPath, ++ callingUid, token, nonce, null); ++ final ObbAction action = new MountObbAction(obbState, key, callingUid, obbInfo); ++ mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action)); ++ ++ if (DEBUG_OBB) ++ Slog.i(TAG, "Send to OBB handler: " + action.toString()); ++ } ++ ++ @Override ++ public void unmountObb(String rawPath, boolean force, IObbActionListener token, int nonce) { ++ Objects.requireNonNull(rawPath, "rawPath cannot be null"); ++ ++ final ObbState existingState; ++ synchronized (mObbMounts) { ++ existingState = mObbPathToStateMap.get(rawPath); ++ } ++ ++ if (existingState != null) { ++ // TODO: separate state object from request data ++ final int callingUid = Binder.getCallingUid(); ++ final ObbState newState = new ObbState(rawPath, existingState.canonicalPath, ++ callingUid, token, nonce, existingState.volId); ++ final ObbAction action = new UnmountObbAction(newState, force); ++ mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action)); ++ ++ if (DEBUG_OBB) ++ Slog.i(TAG, "Send to OBB handler: " + action.toString()); ++ } else { ++ Slog.w(TAG, "Unknown OBB mount at " + rawPath); ++ } ++ } ++ ++ @Override ++ public int getEncryptionState() { ++ mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, ++ "no permission to access the crypt keeper"); ++ ++ try { ++ return mVold.fdeComplete(); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ return StorageManager.ENCRYPTION_STATE_ERROR_UNKNOWN; ++ } ++ } ++ ++ @Override ++ public int decryptStorage(String password) { ++ mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, ++ "no permission to access the crypt keeper"); ++ ++ if (TextUtils.isEmpty(password)) { ++ throw new IllegalArgumentException("password cannot be empty"); ++ } ++ ++ if (DEBUG_EVENTS) { ++ Slog.i(TAG, "decrypting storage..."); ++ } ++ ++ try { ++ mVold.fdeCheckPassword(password); ++ mHandler.postDelayed(() -> { ++ try { ++ mVold.fdeRestart(); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ } ++ }, DateUtils.SECOND_IN_MILLIS); ++ return 0; ++ } catch (ServiceSpecificException e) { ++ Slog.e(TAG, "fdeCheckPassword failed", e); ++ return e.errorCode; ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ return StorageManager.ENCRYPTION_STATE_ERROR_UNKNOWN; ++ } ++ } ++ ++ @Override ++ public int encryptStorage(int type, String password) { ++ mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, ++ "no permission to access the crypt keeper"); ++ ++ if (type == StorageManager.CRYPT_TYPE_DEFAULT) { ++ password = ""; ++ } else if (TextUtils.isEmpty(password)) { ++ throw new IllegalArgumentException("password cannot be empty"); ++ } ++ ++ if (DEBUG_EVENTS) { ++ Slog.i(TAG, "encrypting storage..."); ++ } ++ ++ try { ++ mVold.fdeEnable(type, password, 0); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ return -1; ++ } ++ ++ return 0; ++ } ++ ++ /** Set the password for encrypting the master key. ++ * @param type One of the CRYPTO_TYPE_XXX consts defined in StorageManager. ++ * @param password The password to set. ++ */ ++ @Override ++ public int changeEncryptionPassword(int type, String password) { ++ mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, ++ "no permission to access the crypt keeper"); ++ ++ if (StorageManager.isFileEncryptedNativeOnly()) { ++ // Not supported on FBE devices ++ return -1; ++ } ++ ++ if (type == StorageManager.CRYPT_TYPE_DEFAULT) { ++ password = ""; ++ } else if (TextUtils.isEmpty(password)) { ++ throw new IllegalArgumentException("password cannot be empty"); ++ } ++ ++ if (DEBUG_EVENTS) { ++ Slog.i(TAG, "changing encryption password..."); ++ } ++ ++ try { ++ mVold.fdeChangePassword(type, password); ++ return 0; ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ return -1; ++ } ++ } ++ ++ /** ++ * Validate a user-supplied password string with cryptfs ++ */ ++ @Override ++ public int verifyEncryptionPassword(String password) throws RemoteException { ++ // Only the system process is permitted to validate passwords ++ if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) { ++ throw new SecurityException("no permission to access the crypt keeper"); ++ } ++ ++ mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, ++ "no permission to access the crypt keeper"); ++ ++ if (TextUtils.isEmpty(password)) { ++ throw new IllegalArgumentException("password cannot be empty"); ++ } ++ ++ if (DEBUG_EVENTS) { ++ Slog.i(TAG, "validating encryption password..."); ++ } ++ ++ try { ++ mVold.fdeVerifyPassword(password); ++ return 0; ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ return -1; ++ } ++ } ++ ++ /** ++ * Get the type of encryption used to encrypt the master key. ++ * @return The type, one of the CRYPT_TYPE_XXX consts from StorageManager. ++ */ ++ @Override ++ public int getPasswordType() { ++ mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, ++ "no permission to access the crypt keeper"); ++ ++ try { ++ return mVold.fdeGetPasswordType(); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ return -1; ++ } ++ } ++ ++ /** ++ * Set a field in the crypto header. ++ * @param field field to set ++ * @param contents contents to set in field ++ */ ++ @Override ++ public void setField(String field, String contents) throws RemoteException { ++ mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, ++ "no permission to access the crypt keeper"); ++ ++ if (!StorageManager.isBlockEncrypted()) { ++ // Only supported on FDE devices ++ return; ++ } ++ ++ try { ++ mVold.fdeSetField(field, contents); ++ return; ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ return; ++ } ++ } ++ ++ /** ++ * Gets a field from the crypto header. ++ * @param field field to get ++ * @return contents of field ++ */ ++ @Override ++ public String getField(String field) throws RemoteException { ++ mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, ++ "no permission to access the crypt keeper"); ++ ++ if (!StorageManager.isBlockEncrypted()) { ++ // Only supported on FDE devices ++ return null; ++ } ++ ++ try { ++ return mVold.fdeGetField(field); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ return null; ++ } ++ } ++ ++ /** ++ * Is userdata convertible to file based encryption? ++ * @return non zero for convertible ++ */ ++ @Override ++ public boolean isConvertibleToFBE() throws RemoteException { ++ mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, ++ "no permission to access the crypt keeper"); ++ ++ try { ++ return mVold.isConvertibleToFbe(); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ return false; ++ } ++ } ++ ++ /** ++ * Check whether the device supports filesystem checkpointing. ++ * ++ * @return true if the device supports filesystem checkpointing, false otherwise. ++ */ ++ @Override ++ public boolean supportsCheckpoint() throws RemoteException { ++ return mVold.supportsCheckpoint(); ++ } ++ ++ /** ++ * Signal that checkpointing partitions should start a checkpoint on the next boot. ++ * ++ * @param numTries Number of times to try booting in checkpoint mode, before we will boot ++ * non-checkpoint mode and commit all changes immediately. Callers are ++ * responsible for ensuring that boot is safe (eg, by rolling back updates). ++ */ ++ @Override ++ public void startCheckpoint(int numTries) throws RemoteException { ++ // Only the root, system_server and shell processes are permitted to start checkpoints ++ final int callingUid = Binder.getCallingUid(); ++ if (callingUid != Process.SYSTEM_UID && callingUid != Process.ROOT_UID ++ && callingUid != Process.SHELL_UID) { ++ throw new SecurityException("no permission to start filesystem checkpoint"); ++ } ++ ++ mVold.startCheckpoint(numTries); ++ } ++ ++ /** ++ * Signal that checkpointing partitions should commit changes ++ */ ++ @Override ++ public void commitChanges() throws RemoteException { ++ // Only the system process is permitted to commit checkpoints ++ if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) { ++ throw new SecurityException("no permission to commit checkpoint changes"); ++ } ++ ++ mVold.commitChanges(); ++ } ++ ++ /** ++ * Check if we should be mounting with checkpointing or are checkpointing now ++ */ ++ @Override ++ public boolean needsCheckpoint() throws RemoteException { ++ enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); ++ return mVold.needsCheckpoint(); ++ } ++ ++ /** ++ * Abort the current set of changes and either try again, or abort entirely ++ */ ++ @Override ++ public void abortChanges(String message, boolean retry) throws RemoteException { ++ // Only the system process is permitted to abort checkpoints ++ if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) { ++ throw new SecurityException("no permission to commit checkpoint changes"); ++ } ++ ++ mVold.abortChanges(message, retry); ++ } ++ ++ @Override ++ public String getPassword() throws RemoteException { ++ mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, ++ "only keyguard can retrieve password"); ++ ++ try { ++ return mVold.fdeGetPassword(); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ return null; ++ } ++ } ++ ++ @Override ++ public void clearPassword() throws RemoteException { ++ mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, ++ "only keyguard can clear password"); ++ ++ try { ++ mVold.fdeClearPassword(); ++ return; ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ return; ++ } ++ } ++ ++ @Override ++ public void createUserKey(int userId, int serialNumber, boolean ephemeral) { ++ enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); ++ ++ try { ++ mVold.createUserKey(userId, serialNumber, ephemeral); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ } ++ } ++ ++ @Override ++ public void destroyUserKey(int userId) { ++ enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); ++ ++ try { ++ mVold.destroyUserKey(userId); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ } ++ } ++ ++ private String encodeBytes(byte[] bytes) { ++ if (ArrayUtils.isEmpty(bytes)) { ++ return "!"; ++ } else { ++ return HexDump.toHexString(bytes); ++ } ++ } ++ ++ /* ++ * Add this token/secret pair to the set of ways we can recover a disk encryption key. ++ * Changing the token/secret for a disk encryption key is done in two phases: first, adding ++ * a new token/secret pair with this call, then delting all other pairs with ++ * fixateNewestUserKeyAuth. This allows other places where a credential is used, such as ++ * Gatekeeper, to be updated between the two calls. ++ */ ++ @Override ++ public void addUserKeyAuth(int userId, int serialNumber, byte[] token, byte[] secret) { ++ enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); ++ ++ try { ++ mVold.addUserKeyAuth(userId, serialNumber, encodeBytes(token), encodeBytes(secret)); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ } ++ } ++ ++ /* ++ * Clear disk encryption key bound to the associated token / secret pair. Removing the user ++ * binding of the Disk encryption key is done in two phases: first, this call will retrieve ++ * the disk encryption key using the provided token / secret pair and store it by ++ * encrypting it with a keymaster key not bound to the user, then fixateNewestUserKeyAuth ++ * is called to delete all other bindings of the disk encryption key. ++ */ ++ @Override ++ public void clearUserKeyAuth(int userId, int serialNumber, byte[] token, byte[] secret) { ++ enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); ++ ++ try { ++ mVold.clearUserKeyAuth(userId, serialNumber, encodeBytes(token), encodeBytes(secret)); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ } ++ } ++ ++ /* ++ * Delete all disk encryption token/secret pairs except the most recently added one ++ */ ++ @Override ++ public void fixateNewestUserKeyAuth(int userId) { ++ enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); ++ ++ try { ++ mVold.fixateNewestUserKeyAuth(userId); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ } ++ } ++ ++ @Override ++ public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret) { ++ boolean isFsEncrypted = StorageManager.isFileEncryptedNativeOrEmulated(); ++ Slog.d(TAG, "unlockUserKey: " + userId ++ + " isFileEncryptedNativeOrEmulated: " + isFsEncrypted ++ + " hasToken: " + (token != null) ++ + " hasSecret: " + (secret != null)); ++ enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); ++ ++ if (isFsEncrypted) { ++ try { ++ mVold.unlockUserKey(userId, serialNumber, encodeBytes(token), ++ encodeBytes(secret)); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ return; ++ } ++ } ++ ++ synchronized (mLock) { ++ mLocalUnlockedUsers.append(userId); ++ } ++ } ++ ++ @Override ++ public void lockUserKey(int userId) { ++ enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); ++ ++ try { ++ mVold.lockUserKey(userId); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ return; ++ } ++ ++ synchronized (mLock) { ++ mLocalUnlockedUsers.remove(userId); ++ } ++ } ++ ++ @Override ++ public boolean isUserKeyUnlocked(int userId) { ++ synchronized (mLock) { ++ return mLocalUnlockedUsers.contains(userId); ++ } ++ } ++ ++ private boolean isSystemUnlocked(int userId) { ++ synchronized (mLock) { ++ return ArrayUtils.contains(mSystemUnlockedUsers, userId); ++ } ++ } ++ ++ private void prepareUserStorageIfNeeded(VolumeInfo vol) { ++ if (vol.type != VolumeInfo.TYPE_PRIVATE) { ++ return; ++ } ++ ++ final UserManager um = mContext.getSystemService(UserManager.class); ++ final UserManagerInternal umInternal = ++ LocalServices.getService(UserManagerInternal.class); ++ ++ for (UserInfo user : um.getUsers(false /* includeDying */)) { ++ final int flags; ++ if (umInternal.isUserUnlockingOrUnlocked(user.id)) { ++ flags = StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE; ++ } else if (umInternal.isUserRunning(user.id)) { ++ flags = StorageManager.FLAG_STORAGE_DE; ++ } else { ++ continue; ++ } ++ ++ prepareUserStorageInternal(vol.fsUuid, user.id, user.serialNumber, flags); ++ } ++ } ++ ++ @Override ++ public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) { ++ enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); ++ ++ prepareUserStorageInternal(volumeUuid, userId, serialNumber, flags); ++ } ++ ++ private void prepareUserStorageInternal(String volumeUuid, int userId, int serialNumber, ++ int flags) { ++ try { ++ mVold.prepareUserStorage(volumeUuid, userId, serialNumber, flags); ++ // After preparing user storage, we should check if we should mount data mirror again, ++ // and we do it for user 0 only as we only need to do once for all users. ++ if (volumeUuid != null) { ++ final StorageManager storage = mContext.getSystemService(StorageManager.class); ++ VolumeInfo info = storage.findVolumeByUuid(volumeUuid); ++ if (info != null && userId == 0 && info.type == VolumeInfo.TYPE_PRIVATE) { ++ mInstaller.tryMountDataMirror(volumeUuid); ++ } ++ } ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ } ++ } ++ ++ @Override ++ public void destroyUserStorage(String volumeUuid, int userId, int flags) { ++ enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); ++ ++ try { ++ mVold.destroyUserStorage(volumeUuid, userId, flags); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ } ++ } ++ ++ @Override ++ public void fixupAppDir(String path) { ++ final Matcher matcher = KNOWN_APP_DIR_PATHS.matcher(path); ++ if (matcher.matches()) { ++ if (matcher.group(2) == null) { ++ Log.e(TAG, "Asked to fixup an app dir without a userId: " + path); ++ return; ++ } ++ try { ++ int userId = Integer.parseInt(matcher.group(2)); ++ String packageName = matcher.group(3); ++ int uid = mContext.getPackageManager().getPackageUidAsUser(packageName, userId); ++ try { ++ mVold.fixupAppDir(path + "/", uid); ++ } catch (RemoteException | ServiceSpecificException e) { ++ Log.e(TAG, "Failed to fixup app dir for " + packageName, e); ++ } ++ } catch (NumberFormatException e) { ++ Log.e(TAG, "Invalid userId in path: " + path, e); ++ } catch (PackageManager.NameNotFoundException e) { ++ Log.e(TAG, "Couldn't find package to fixup app dir " + path, e); ++ } ++ } else { ++ Log.e(TAG, "Path " + path + " is not a valid application-specific directory"); ++ } ++ } ++ ++ /** Not thread safe */ ++ class AppFuseMountScope extends AppFuseBridge.MountScope { ++ private boolean mMounted = false; ++ ++ public AppFuseMountScope(int uid, int mountId) { ++ super(uid, mountId); ++ } ++ ++ @Override ++ public ParcelFileDescriptor open() throws NativeDaemonConnectorException { ++ try { ++ final FileDescriptor fd = mVold.mountAppFuse(uid, mountId); ++ mMounted = true; ++ return new ParcelFileDescriptor(fd); ++ } catch (Exception e) { ++ throw new NativeDaemonConnectorException("Failed to mount", e); ++ } ++ } ++ ++ @Override ++ public ParcelFileDescriptor openFile(int mountId, int fileId, int flags) ++ throws NativeDaemonConnectorException { ++ try { ++ return new ParcelFileDescriptor( ++ mVold.openAppFuseFile(uid, mountId, fileId, flags)); ++ } catch (Exception e) { ++ throw new NativeDaemonConnectorException("Failed to open", e); ++ } ++ } ++ ++ @Override ++ public void close() throws Exception { ++ if (mMounted) { ++ mVold.unmountAppFuse(uid, mountId); ++ mMounted = false; ++ } ++ } ++ } ++ ++ @Override ++ public @Nullable AppFuseMount mountProxyFileDescriptorBridge() { ++ Slog.v(TAG, "mountProxyFileDescriptorBridge"); ++ final int uid = Binder.getCallingUid(); ++ ++ while (true) { ++ synchronized (mAppFuseLock) { ++ boolean newlyCreated = false; ++ if (mAppFuseBridge == null) { ++ mAppFuseBridge = new AppFuseBridge(); ++ new Thread(mAppFuseBridge, AppFuseBridge.TAG).start(); ++ newlyCreated = true; ++ } ++ try { ++ final int name = mNextAppFuseName++; ++ try { ++ return new AppFuseMount( ++ name, mAppFuseBridge.addBridge(new AppFuseMountScope(uid, name))); ++ } catch (FuseUnavailableMountException e) { ++ if (newlyCreated) { ++ // If newly created bridge fails, it's a real error. ++ Slog.e(TAG, "", e); ++ return null; ++ } ++ // It seems the thread of mAppFuseBridge has already been terminated. ++ mAppFuseBridge = null; ++ } ++ } catch (NativeDaemonConnectorException e) { ++ throw e.rethrowAsParcelableException(); ++ } ++ } ++ } ++ } ++ ++ @Override ++ public @Nullable ParcelFileDescriptor openProxyFileDescriptor( ++ int mountId, int fileId, int mode) { ++ Slog.v(TAG, "mountProxyFileDescriptor"); ++ ++ // We only support a narrow set of incoming mode flags ++ mode &= MODE_READ_WRITE; ++ ++ try { ++ synchronized (mAppFuseLock) { ++ if (mAppFuseBridge == null) { ++ Slog.e(TAG, "FuseBridge has not been created"); ++ return null; ++ } ++ return mAppFuseBridge.openFile(mountId, fileId, mode); ++ } ++ } catch (FuseUnavailableMountException | InterruptedException error) { ++ Slog.v(TAG, "The mount point has already been invalid", error); ++ return null; ++ } ++ } ++ ++ @Override ++ public void mkdirs(String callingPkg, String appPath) { ++ final int callingUid = Binder.getCallingUid(); ++ final int userId = UserHandle.getUserId(callingUid); ++ final String propertyName = "sys.user." + userId + ".ce_available"; ++ ++ // Ignore requests to create directories while storage is locked ++ if (!isUserKeyUnlocked(userId)) { ++ throw new IllegalStateException("Failed to prepare " + appPath); ++ } ++ ++ // Ignore requests to create directories if CE storage is not available ++ if ((userId == UserHandle.USER_SYSTEM) ++ && !SystemProperties.getBoolean(propertyName, false)) { ++ throw new IllegalStateException("Failed to prepare " + appPath); ++ } ++ ++ // Validate that reported package name belongs to caller ++ final AppOpsManager appOps = (AppOpsManager) mContext.getSystemService( ++ Context.APP_OPS_SERVICE); ++ appOps.checkPackage(callingUid, callingPkg); ++ ++ File appFile = null; ++ try { ++ appFile = new File(appPath).getCanonicalFile(); ++ } catch (IOException e) { ++ throw new IllegalStateException("Failed to resolve " + appPath + ": " + e); ++ } ++ ++ appPath = appFile.getAbsolutePath(); ++ if (!appPath.endsWith("/")) { ++ appPath = appPath + "/"; ++ } ++ // Ensure that the path we're asked to create is a known application directory ++ // path. ++ final Matcher matcher = KNOWN_APP_DIR_PATHS.matcher(appPath); ++ if (matcher.matches()) { ++ // And that the package dir matches the calling package ++ if (!matcher.group(3).equals(callingPkg)) { ++ throw new SecurityException("Invalid mkdirs path: " + appFile ++ + " does not contain calling package " + callingPkg); ++ } ++ // And that the user id part of the path (if any) matches the calling user id, ++ // or if for a public volume (no user id), the user matches the current user ++ if ((matcher.group(2) != null && !matcher.group(2).equals(Integer.toString(userId))) ++ || (matcher.group(2) == null && userId != mCurrentUserId)) { ++ throw new SecurityException("Invalid mkdirs path: " + appFile ++ + " does not match calling user id " + userId); ++ } ++ try { ++ mVold.setupAppDir(appPath, callingUid); ++ } catch (RemoteException e) { ++ throw new IllegalStateException("Failed to prepare " + appPath + ": " + e); ++ } ++ ++ return; ++ } ++ throw new SecurityException("Invalid mkdirs path: " + appFile ++ + " is not a known app path."); ++ } ++ ++ @Override ++ public StorageVolume[] getVolumeList(int uid, String packageName, int flags) { ++ final int userId = UserHandle.getUserId(uid); ++ ++ final boolean forWrite = (flags & StorageManager.FLAG_FOR_WRITE) != 0; ++ final boolean realState = (flags & StorageManager.FLAG_REAL_STATE) != 0; ++ final boolean includeInvisible = (flags & StorageManager.FLAG_INCLUDE_INVISIBLE) != 0; ++ final boolean includeRecent = (flags & StorageManager.FLAG_INCLUDE_RECENT) != 0; ++ ++ // Report all volumes as unmounted until we've recorded that user 0 has unlocked. There ++ // are no guarantees that callers will see a consistent view of the volume before that ++ // point ++ final boolean systemUserUnlocked = isSystemUnlocked(UserHandle.USER_SYSTEM); ++ ++ // When the caller is the app actually hosting external storage, we ++ // should never attempt to augment the actual storage volume state, ++ // otherwise we risk confusing it with race conditions as users go ++ // through various unlocked states ++ final boolean callerIsMediaStore = UserHandle.isSameApp(Binder.getCallingUid(), ++ mMediaStoreAuthorityAppId); ++ ++ final boolean userIsDemo; ++ final boolean userKeyUnlocked; ++ final boolean storagePermission; ++ final long token = Binder.clearCallingIdentity(); ++ try { ++ userIsDemo = LocalServices.getService(UserManagerInternal.class) ++ .getUserInfo(userId).isDemo(); ++ userKeyUnlocked = isUserKeyUnlocked(userId); ++ storagePermission = mStorageManagerInternal.hasExternalStorage(uid, packageName); ++ } finally { ++ Binder.restoreCallingIdentity(token); ++ } ++ ++ boolean foundPrimary = false; ++ ++ final ArrayList res = new ArrayList<>(); ++ final ArraySet resUuids = new ArraySet<>(); ++ synchronized (mLock) { ++ for (int i = 0; i < mVolumes.size(); i++) { ++ final String volId = mVolumes.keyAt(i); ++ final VolumeInfo vol = mVolumes.valueAt(i); ++ switch (vol.getType()) { ++ case VolumeInfo.TYPE_PUBLIC: ++ case VolumeInfo.TYPE_STUB: ++ break; ++ case VolumeInfo.TYPE_EMULATED: ++ if (vol.getMountUserId() == userId) { ++ break; ++ } ++ // Skip if emulated volume not for userId ++ default: ++ continue; ++ } ++ ++ boolean match = false; ++ if (forWrite) { ++ match = vol.isVisibleForWrite(userId); ++ } else { ++ match = vol.isVisibleForRead(userId) ++ || (includeInvisible && vol.getPath() != null); ++ } ++ if (!match) continue; ++ ++ boolean reportUnmounted = false; ++ if (callerIsMediaStore) { ++ // When the caller is the app actually hosting external storage, we ++ // should never attempt to augment the actual storage volume state, ++ // otherwise we risk confusing it with race conditions as users go ++ // through various unlocked states ++ } else if (!systemUserUnlocked) { ++ reportUnmounted = true; ++ Slog.w(TAG, "Reporting " + volId + " unmounted due to system locked"); ++ } else if ((vol.getType() == VolumeInfo.TYPE_EMULATED) && !userKeyUnlocked) { ++ reportUnmounted = true; ++ Slog.w(TAG, "Reporting " + volId + "unmounted due to " + userId + " locked"); ++ } else if (!storagePermission && !realState) { ++ Slog.w(TAG, "Reporting " + volId + "unmounted due to missing permissions"); ++ reportUnmounted = true; ++ } ++ ++ final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, ++ reportUnmounted); ++ if (vol.isPrimary()) { ++ res.add(0, userVol); ++ foundPrimary = true; ++ } else { ++ res.add(userVol); ++ } ++ resUuids.add(userVol.getUuid()); ++ } ++ ++ if (includeRecent) { ++ final long lastWeek = System.currentTimeMillis() - DateUtils.WEEK_IN_MILLIS; ++ for (int i = 0; i < mRecords.size(); i++) { ++ final VolumeRecord rec = mRecords.valueAt(i); ++ ++ // Skip if we've already included it above ++ if (resUuids.contains(rec.fsUuid)) continue; ++ ++ // Treat as recent if mounted within the last week ++ if (rec.lastSeenMillis > 0 && rec.lastSeenMillis < lastWeek) { ++ final StorageVolume userVol = rec.buildStorageVolume(mContext); ++ res.add(userVol); ++ resUuids.add(userVol.getUuid()); ++ } ++ } ++ } ++ } ++ ++ // Synthesize a volume for preloaded media under demo users, so that ++ // it's scanned into MediaStore ++ if (userIsDemo) { ++ final String id = "demo"; ++ final File path = Environment.getDataPreloadsMediaDirectory(); ++ final boolean primary = false; ++ final boolean removable = false; ++ final boolean emulated = true; ++ final boolean allowMassStorage = false; ++ final long maxFileSize = 0; ++ final UserHandle user = new UserHandle(userId); ++ final String envState = Environment.MEDIA_MOUNTED_READ_ONLY; ++ final String description = mContext.getString(android.R.string.unknownName); ++ ++ res.add(new StorageVolume(id, path, path, description, primary, removable, ++ emulated, allowMassStorage, maxFileSize, user, id, envState)); ++ } ++ ++ if (!foundPrimary) { ++ Slog.w(TAG, "No primary storage defined yet; hacking together a stub"); ++ ++ final boolean primaryPhysical = SystemProperties.getBoolean( ++ StorageManager.PROP_PRIMARY_PHYSICAL, false); ++ ++ final String id = "stub_primary"; ++ final File path = Environment.getLegacyExternalStorageDirectory(); ++ final String description = mContext.getString(android.R.string.unknownName); ++ final boolean primary = true; ++ final boolean removable = primaryPhysical; ++ final boolean emulated = !primaryPhysical; ++ final boolean allowMassStorage = false; ++ final long maxFileSize = 0L; ++ final UserHandle owner = new UserHandle(userId); ++ final String uuid = null; ++ final String state = Environment.MEDIA_REMOVED; ++ ++ res.add(0, new StorageVolume(id, path, path, ++ description, primary, removable, emulated, ++ allowMassStorage, maxFileSize, owner, uuid, state)); ++ } ++ ++ return res.toArray(new StorageVolume[res.size()]); ++ } ++ ++ @Override ++ public DiskInfo[] getDisks() { ++ synchronized (mLock) { ++ final DiskInfo[] res = new DiskInfo[mDisks.size()]; ++ for (int i = 0; i < mDisks.size(); i++) { ++ res[i] = mDisks.valueAt(i); ++ } ++ return res; ++ } ++ } ++ ++ @Override ++ public VolumeInfo[] getVolumes(int flags) { ++ synchronized (mLock) { ++ final VolumeInfo[] res = new VolumeInfo[mVolumes.size()]; ++ for (int i = 0; i < mVolumes.size(); i++) { ++ res[i] = mVolumes.valueAt(i); ++ } ++ return res; ++ } ++ } ++ ++ @Override ++ public VolumeRecord[] getVolumeRecords(int flags) { ++ synchronized (mLock) { ++ final VolumeRecord[] res = new VolumeRecord[mRecords.size()]; ++ for (int i = 0; i < mRecords.size(); i++) { ++ res[i] = mRecords.valueAt(i); ++ } ++ return res; ++ } ++ } ++ ++ @Override ++ public long getCacheQuotaBytes(String volumeUuid, int uid) { ++ if (uid != Binder.getCallingUid()) { ++ mContext.enforceCallingPermission(android.Manifest.permission.STORAGE_INTERNAL, TAG); ++ } ++ final long token = Binder.clearCallingIdentity(); ++ final StorageStatsManager stats = mContext.getSystemService(StorageStatsManager.class); ++ try { ++ return stats.getCacheQuotaBytes(volumeUuid, uid); ++ } finally { ++ Binder.restoreCallingIdentity(token); ++ } ++ } ++ ++ @Override ++ public long getCacheSizeBytes(String volumeUuid, int uid) { ++ if (uid != Binder.getCallingUid()) { ++ mContext.enforceCallingPermission(android.Manifest.permission.STORAGE_INTERNAL, TAG); ++ } ++ final long token = Binder.clearCallingIdentity(); ++ try { ++ return mContext.getSystemService(StorageStatsManager.class) ++ .queryStatsForUid(volumeUuid, uid).getCacheBytes(); ++ } catch (IOException e) { ++ throw new ParcelableException(e); ++ } finally { ++ Binder.restoreCallingIdentity(token); ++ } ++ } ++ ++ private int adjustAllocateFlags(int flags, int callingUid, String callingPackage) { ++ // Require permission to allocate aggressively ++ if ((flags & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0) { ++ mContext.enforceCallingOrSelfPermission( ++ android.Manifest.permission.ALLOCATE_AGGRESSIVE, TAG); ++ } ++ ++ // Apps normally can't directly defy reserved space ++ flags &= ~StorageManager.FLAG_ALLOCATE_DEFY_ALL_RESERVED; ++ flags &= ~StorageManager.FLAG_ALLOCATE_DEFY_HALF_RESERVED; ++ ++ // However, if app is actively using the camera, then we're willing to ++ // clear up to half of the reserved cache space, since the user might be ++ // trying to capture an important memory. ++ final AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class); ++ final long token = Binder.clearCallingIdentity(); ++ try { ++ if (appOps.isOperationActive(AppOpsManager.OP_CAMERA, callingUid, callingPackage)) { ++ Slog.d(TAG, "UID " + callingUid + " is actively using camera;" ++ + " letting them defy reserved cached data"); ++ flags |= StorageManager.FLAG_ALLOCATE_DEFY_HALF_RESERVED; ++ } ++ } finally { ++ Binder.restoreCallingIdentity(token); ++ } ++ ++ return flags; ++ } ++ ++ @Override ++ public long getAllocatableBytes(String volumeUuid, int flags, String callingPackage) { ++ flags = adjustAllocateFlags(flags, Binder.getCallingUid(), callingPackage); ++ ++ final StorageManager storage = mContext.getSystemService(StorageManager.class); ++ final StorageStatsManager stats = mContext.getSystemService(StorageStatsManager.class); ++ final long token = Binder.clearCallingIdentity(); ++ try { ++ // In general, apps can allocate as much space as they want, except ++ // we never let them eat into either the minimum cache space or into ++ // the low disk warning space. To avoid user confusion, this logic ++ // should be kept in sync with getFreeBytes(). ++ final File path = storage.findPathForUuid(volumeUuid); ++ ++ long usable = 0; ++ long lowReserved = 0; ++ long fullReserved = 0; ++ long cacheClearable = 0; ++ ++ if ((flags & StorageManager.FLAG_ALLOCATE_CACHE_ONLY) == 0) { ++ usable = path.getUsableSpace(); ++ lowReserved = storage.getStorageLowBytes(path); ++ fullReserved = storage.getStorageFullBytes(path); ++ } ++ ++ if ((flags & StorageManager.FLAG_ALLOCATE_NON_CACHE_ONLY) == 0 ++ && stats.isQuotaSupported(volumeUuid)) { ++ final long cacheTotal = stats.getCacheBytes(volumeUuid); ++ final long cacheReserved = storage.getStorageCacheBytes(path, flags); ++ cacheClearable = Math.max(0, cacheTotal - cacheReserved); ++ } ++ ++ if ((flags & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0) { ++ return Math.max(0, (usable + cacheClearable) - fullReserved); ++ } else { ++ return Math.max(0, (usable + cacheClearable) - lowReserved); ++ } ++ } catch (IOException e) { ++ throw new ParcelableException(e); ++ } finally { ++ Binder.restoreCallingIdentity(token); ++ } ++ } ++ ++ @Override ++ public void allocateBytes(String volumeUuid, long bytes, int flags, String callingPackage) { ++ flags = adjustAllocateFlags(flags, Binder.getCallingUid(), callingPackage); ++ ++ final long allocatableBytes = getAllocatableBytes(volumeUuid, ++ flags | StorageManager.FLAG_ALLOCATE_NON_CACHE_ONLY, callingPackage); ++ if (bytes > allocatableBytes) { ++ // If we don't have room without taking cache into account, check to see if we'd have ++ // room if we included freeable cache space. ++ final long cacheClearable = getAllocatableBytes(volumeUuid, ++ flags | StorageManager.FLAG_ALLOCATE_CACHE_ONLY, callingPackage); ++ if (bytes > allocatableBytes + cacheClearable) { ++ throw new ParcelableException(new IOException("Failed to allocate " + bytes ++ + " because only " + (allocatableBytes + cacheClearable) + " allocatable")); ++ } ++ } ++ ++ final StorageManager storage = mContext.getSystemService(StorageManager.class); ++ final long token = Binder.clearCallingIdentity(); ++ try { ++ // Free up enough disk space to satisfy both the requested allocation ++ // and our low disk warning space. ++ final File path = storage.findPathForUuid(volumeUuid); ++ if ((flags & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0) { ++ bytes += storage.getStorageFullBytes(path); ++ } else { ++ bytes += storage.getStorageLowBytes(path); ++ } ++ ++ mPmInternal.freeStorage(volumeUuid, bytes, flags); ++ } catch (IOException e) { ++ throw new ParcelableException(e); ++ } finally { ++ Binder.restoreCallingIdentity(token); ++ } ++ } ++ ++ private IAppOpsCallback.Stub mAppOpsCallback = new IAppOpsCallback.Stub() { ++ @Override ++ public void opChanged(int op, int uid, String packageName) throws RemoteException { ++ if (!ENABLE_ISOLATED_STORAGE) return; ++ ++ int mountMode = getMountMode(uid, packageName); ++ boolean isUidActive = LocalServices.getService(ActivityManagerInternal.class) ++ .getUidProcessState(uid) != PROCESS_STATE_NONEXISTENT; ++ ++ if (isUidActive) { ++ remountUidExternalStorage(uid, mountMode); ++ } ++ } ++ }; ++ ++ private void addObbStateLocked(ObbState obbState) throws RemoteException { ++ final IBinder binder = obbState.getBinder(); ++ List obbStates = mObbMounts.get(binder); ++ ++ if (obbStates == null) { ++ obbStates = new ArrayList(); ++ mObbMounts.put(binder, obbStates); ++ } else { ++ for (final ObbState o : obbStates) { ++ if (o.rawPath.equals(obbState.rawPath)) { ++ throw new IllegalStateException("Attempt to add ObbState twice. " ++ + "This indicates an error in the StorageManagerService logic."); ++ } ++ } ++ } ++ ++ obbStates.add(obbState); ++ try { ++ obbState.link(); ++ } catch (RemoteException e) { ++ /* ++ * The binder died before we could link it, so clean up our state ++ * and return failure. ++ */ ++ obbStates.remove(obbState); ++ if (obbStates.isEmpty()) { ++ mObbMounts.remove(binder); ++ } ++ ++ // Rethrow the error so mountObb can get it ++ throw e; ++ } ++ ++ mObbPathToStateMap.put(obbState.rawPath, obbState); ++ } ++ ++ private void removeObbStateLocked(ObbState obbState) { ++ final IBinder binder = obbState.getBinder(); ++ final List obbStates = mObbMounts.get(binder); ++ if (obbStates != null) { ++ if (obbStates.remove(obbState)) { ++ obbState.unlink(); ++ } ++ if (obbStates.isEmpty()) { ++ mObbMounts.remove(binder); ++ } ++ } ++ ++ mObbPathToStateMap.remove(obbState.rawPath); ++ } ++ ++ private class ObbActionHandler extends Handler { ++ ++ ObbActionHandler(Looper l) { ++ super(l); ++ } ++ ++ @Override ++ public void handleMessage(Message msg) { ++ switch (msg.what) { ++ case OBB_RUN_ACTION: { ++ final ObbAction action = (ObbAction) msg.obj; ++ ++ if (DEBUG_OBB) ++ Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString()); ++ ++ action.execute(this); ++ break; ++ } ++ case OBB_FLUSH_MOUNT_STATE: { ++ final String path = (String) msg.obj; ++ ++ if (DEBUG_OBB) ++ Slog.i(TAG, "Flushing all OBB state for path " + path); ++ ++ synchronized (mObbMounts) { ++ final List obbStatesToRemove = new LinkedList(); ++ ++ final Iterator i = mObbPathToStateMap.values().iterator(); ++ while (i.hasNext()) { ++ final ObbState state = i.next(); ++ ++ /* ++ * If this entry's source file is in the volume path ++ * that got unmounted, remove it because it's no ++ * longer valid. ++ */ ++ if (state.canonicalPath.startsWith(path)) { ++ obbStatesToRemove.add(state); ++ } ++ } ++ ++ for (final ObbState obbState : obbStatesToRemove) { ++ if (DEBUG_OBB) ++ Slog.i(TAG, "Removing state for " + obbState.rawPath); ++ ++ removeObbStateLocked(obbState); ++ ++ try { ++ obbState.token.onObbResult(obbState.rawPath, obbState.nonce, ++ OnObbStateChangeListener.UNMOUNTED); ++ } catch (RemoteException e) { ++ Slog.i(TAG, "Couldn't send unmount notification for OBB: " ++ + obbState.rawPath); ++ } ++ } ++ } ++ break; ++ } ++ } ++ } ++ } ++ ++ private static class ObbException extends Exception { ++ public final int status; ++ ++ public ObbException(int status, String message) { ++ super(message); ++ this.status = status; ++ } ++ ++ public ObbException(int status, Throwable cause) { ++ super(cause.getMessage(), cause); ++ this.status = status; ++ } ++ } ++ ++ abstract class ObbAction { ++ ++ ObbState mObbState; ++ ++ ObbAction(ObbState obbState) { ++ mObbState = obbState; ++ } ++ ++ public void execute(ObbActionHandler handler) { ++ try { ++ if (DEBUG_OBB) ++ Slog.i(TAG, "Starting to execute action: " + toString()); ++ handleExecute(); ++ } catch (ObbException e) { ++ notifyObbStateChange(e); ++ } ++ } ++ ++ abstract void handleExecute() throws ObbException; ++ ++ protected void notifyObbStateChange(ObbException e) { ++ Slog.w(TAG, e); ++ notifyObbStateChange(e.status); ++ } ++ ++ protected void notifyObbStateChange(int status) { ++ if (mObbState == null || mObbState.token == null) { ++ return; ++ } ++ ++ try { ++ mObbState.token.onObbResult(mObbState.rawPath, mObbState.nonce, status); ++ } catch (RemoteException e) { ++ Slog.w(TAG, "StorageEventListener went away while calling onObbStateChanged"); ++ } ++ } ++ } ++ ++ class MountObbAction extends ObbAction { ++ private final String mKey; ++ private final int mCallingUid; ++ private ObbInfo mObbInfo; ++ ++ MountObbAction(ObbState obbState, String key, int callingUid, ObbInfo obbInfo) { ++ super(obbState); ++ mKey = key; ++ mCallingUid = callingUid; ++ mObbInfo = obbInfo; ++ } ++ ++ @Override ++ public void handleExecute() throws ObbException { ++ warnOnNotMounted(); ++ ++ if (!isUidOwnerOfPackageOrSystem(mObbInfo.packageName, mCallingUid)) { ++ throw new ObbException(ERROR_PERMISSION_DENIED, "Denied attempt to mount OBB " ++ + mObbInfo.filename + " which is owned by " + mObbInfo.packageName); ++ } ++ ++ final boolean isMounted; ++ synchronized (mObbMounts) { ++ isMounted = mObbPathToStateMap.containsKey(mObbState.rawPath); ++ } ++ if (isMounted) { ++ throw new ObbException(ERROR_ALREADY_MOUNTED, ++ "Attempt to mount OBB which is already mounted: " + mObbInfo.filename); ++ } ++ ++ final String hashedKey; ++ final String binderKey; ++ if (mKey == null) { ++ hashedKey = "none"; ++ binderKey = ""; ++ } else { ++ try { ++ SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); ++ ++ KeySpec ks = new PBEKeySpec(mKey.toCharArray(), mObbInfo.salt, ++ PBKDF2_HASH_ROUNDS, CRYPTO_ALGORITHM_KEY_SIZE); ++ SecretKey key = factory.generateSecret(ks); ++ BigInteger bi = new BigInteger(key.getEncoded()); ++ hashedKey = bi.toString(16); ++ binderKey = hashedKey; ++ } catch (GeneralSecurityException e) { ++ throw new ObbException(ERROR_INTERNAL, e); ++ } ++ } ++ ++ try { ++ mObbState.volId = mVold.createObb(mObbState.canonicalPath, binderKey, ++ mObbState.ownerGid); ++ mVold.mount(mObbState.volId, 0, -1, null); ++ ++ if (DEBUG_OBB) ++ Slog.d(TAG, "Successfully mounted OBB " + mObbState.canonicalPath); ++ ++ synchronized (mObbMounts) { ++ addObbStateLocked(mObbState); ++ } ++ ++ notifyObbStateChange(MOUNTED); ++ } catch (Exception e) { ++ throw new ObbException(ERROR_COULD_NOT_MOUNT, e); ++ } ++ } ++ ++ @Override ++ public String toString() { ++ StringBuilder sb = new StringBuilder(); ++ sb.append("MountObbAction{"); ++ sb.append(mObbState); ++ sb.append('}'); ++ return sb.toString(); ++ } ++ } ++ ++ class UnmountObbAction extends ObbAction { ++ private final boolean mForceUnmount; ++ ++ UnmountObbAction(ObbState obbState, boolean force) { ++ super(obbState); ++ mForceUnmount = force; ++ } ++ ++ @Override ++ public void handleExecute() throws ObbException { ++ warnOnNotMounted(); ++ ++ final ObbState existingState; ++ synchronized (mObbMounts) { ++ existingState = mObbPathToStateMap.get(mObbState.rawPath); ++ } ++ ++ if (existingState == null) { ++ throw new ObbException(ERROR_NOT_MOUNTED, "Missing existingState"); ++ } ++ ++ if (existingState.ownerGid != mObbState.ownerGid) { ++ notifyObbStateChange(new ObbException(ERROR_PERMISSION_DENIED, ++ "Permission denied to unmount OBB " + existingState.rawPath ++ + " (owned by GID " + existingState.ownerGid + ")")); ++ return; ++ } ++ ++ try { ++ mVold.unmount(mObbState.volId); ++ mVold.destroyObb(mObbState.volId); ++ mObbState.volId = null; ++ ++ synchronized (mObbMounts) { ++ removeObbStateLocked(existingState); ++ } ++ ++ notifyObbStateChange(UNMOUNTED); ++ } catch (Exception e) { ++ throw new ObbException(ERROR_COULD_NOT_UNMOUNT, e); ++ } ++ } ++ ++ @Override ++ public String toString() { ++ StringBuilder sb = new StringBuilder(); ++ sb.append("UnmountObbAction{"); ++ sb.append(mObbState); ++ sb.append(",force="); ++ sb.append(mForceUnmount); ++ sb.append('}'); ++ return sb.toString(); ++ } ++ } ++ ++ private void dispatchOnStatus(IVoldTaskListener listener, int status, ++ PersistableBundle extras) { ++ if (listener != null) { ++ try { ++ listener.onStatus(status, extras); ++ } catch (RemoteException ignored) { ++ } ++ } ++ } ++ ++ private void dispatchOnFinished(IVoldTaskListener listener, int status, ++ PersistableBundle extras) { ++ if (listener != null) { ++ try { ++ listener.onFinished(status, extras); ++ } catch (RemoteException ignored) { ++ } ++ } ++ } ++ ++ private int getMountMode(int uid, String packageName) { ++ final int mode = getMountModeInternal(uid, packageName); ++ if (LOCAL_LOGV) { ++ Slog.v(TAG, "Resolved mode " + mode + " for " + packageName + "/" ++ + UserHandle.formatUid(uid)); ++ } ++ return mode; ++ } ++ ++ private int getMountModeInternal(int uid, String packageName) { ++ try { ++ // Get some easy cases out of the way first ++ if (Process.isIsolated(uid)) { ++ return Zygote.MOUNT_EXTERNAL_NONE; ++ } ++ ++ final String[] packagesForUid = mIPackageManager.getPackagesForUid(uid); ++ if (ArrayUtils.isEmpty(packagesForUid)) { ++ // It's possible the package got uninstalled already, so just ignore. ++ return Zygote.MOUNT_EXTERNAL_NONE; ++ } ++ if (packageName == null) { ++ packageName = packagesForUid[0]; ++ } ++ ++ if (mPmInternal.isInstantApp(packageName, UserHandle.getUserId(uid))) { ++ return Zygote.MOUNT_EXTERNAL_NONE; ++ } ++ ++ if (mIsFuseEnabled && mStorageManagerInternal.isExternalStorageService(uid)) { ++ // Determine if caller requires pass_through mount; note that we do this for ++ // all processes that share a UID with MediaProvider; but this is fine, since ++ // those processes anyway share the same rights as MediaProvider. ++ return Zygote.MOUNT_EXTERNAL_PASS_THROUGH; ++ } ++ ++ if (mIsFuseEnabled && (mDownloadsAuthorityAppId == UserHandle.getAppId(uid) ++ || mExternalStorageAuthorityAppId == UserHandle.getAppId(uid))) { ++ // DownloadManager can write in app-private directories on behalf of apps; ++ // give it write access to Android/ ++ // ExternalStorageProvider can access Android/{data,obb} dirs in managed mode ++ return Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE; ++ } ++ ++ final boolean hasMtp = mIPackageManager.checkUidPermission(ACCESS_MTP, uid) == ++ PERMISSION_GRANTED; ++ if (mIsFuseEnabled && hasMtp) { ++ ApplicationInfo ai = mIPackageManager.getApplicationInfo(packageName, ++ 0, UserHandle.getUserId(uid)); ++ if (ai != null && ai.isSignedWithPlatformKey()) { ++ // Platform processes hosting the MTP server should be able to write in Android/ ++ return Zygote.MOUNT_EXTERNAL_ANDROID_WRITABLE; ++ } ++ } ++ ++ // Determine if caller is holding runtime permission ++ final boolean hasRead = StorageManager.checkPermissionAndCheckOp(mContext, false, 0, ++ uid, packageName, READ_EXTERNAL_STORAGE, OP_READ_EXTERNAL_STORAGE); ++ final boolean hasWrite = StorageManager.checkPermissionAndCheckOp(mContext, false, 0, ++ uid, packageName, WRITE_EXTERNAL_STORAGE, OP_WRITE_EXTERNAL_STORAGE); ++ ++ // We're only willing to give out broad access if they also hold ++ // runtime permission; this is a firm CDD requirement ++ final boolean hasFull = mIPackageManager.checkUidPermission(WRITE_MEDIA_STORAGE, ++ uid) == PERMISSION_GRANTED; ++ if (hasFull && hasWrite) { ++ return Zygote.MOUNT_EXTERNAL_FULL; ++ } ++ ++ // We're only willing to give out installer access if they also hold ++ // runtime permission; this is a firm CDD requirement ++ final boolean hasInstall = mIPackageManager.checkUidPermission(INSTALL_PACKAGES, ++ uid) == PERMISSION_GRANTED; ++ boolean hasInstallOp = false; ++ // OP_REQUEST_INSTALL_PACKAGES is granted/denied per package but vold can't ++ // update mountpoints of a specific package. So, check the appop for all packages ++ // sharing the uid and allow same level of storage access for all packages even if ++ // one of the packages has the appop granted. ++ for (String uidPackageName : packagesForUid) { ++ if (mIAppOpsService.checkOperation( ++ OP_REQUEST_INSTALL_PACKAGES, uid, uidPackageName) == MODE_ALLOWED) { ++ hasInstallOp = true; ++ break; ++ } ++ } ++ if ((hasInstall || hasInstallOp) && hasWrite) { ++ return Zygote.MOUNT_EXTERNAL_INSTALLER; ++ } ++ ++ // Otherwise we're willing to give out sandboxed or non-sandboxed if ++ // they hold the runtime permission ++ boolean hasLegacy = mIAppOpsService.checkOperation(OP_LEGACY_STORAGE, ++ uid, packageName) == MODE_ALLOWED; ++ ++ if (hasLegacy && hasWrite) { ++ return Zygote.MOUNT_EXTERNAL_WRITE; ++ } else if (hasLegacy && hasRead) { ++ return Zygote.MOUNT_EXTERNAL_READ; ++ } else { ++ return Zygote.MOUNT_EXTERNAL_DEFAULT; ++ } ++ } catch (RemoteException e) { ++ // Should not happen ++ } ++ return Zygote.MOUNT_EXTERNAL_NONE; ++ } ++ ++ private static class Callbacks extends Handler { ++ private static final int MSG_STORAGE_STATE_CHANGED = 1; ++ private static final int MSG_VOLUME_STATE_CHANGED = 2; ++ private static final int MSG_VOLUME_RECORD_CHANGED = 3; ++ private static final int MSG_VOLUME_FORGOTTEN = 4; ++ private static final int MSG_DISK_SCANNED = 5; ++ private static final int MSG_DISK_DESTROYED = 6; ++ ++ private final RemoteCallbackList ++ mCallbacks = new RemoteCallbackList<>(); ++ ++ public Callbacks(Looper looper) { ++ super(looper); ++ } ++ ++ public void register(IStorageEventListener callback) { ++ mCallbacks.register(callback); ++ } ++ ++ public void unregister(IStorageEventListener callback) { ++ mCallbacks.unregister(callback); ++ } ++ ++ @Override ++ public void handleMessage(Message msg) { ++ final SomeArgs args = (SomeArgs) msg.obj; ++ final int n = mCallbacks.beginBroadcast(); ++ for (int i = 0; i < n; i++) { ++ final IStorageEventListener callback = mCallbacks.getBroadcastItem(i); ++ try { ++ invokeCallback(callback, msg.what, args); ++ } catch (RemoteException ignored) { ++ } ++ } ++ mCallbacks.finishBroadcast(); ++ args.recycle(); ++ } ++ ++ private void invokeCallback(IStorageEventListener callback, int what, SomeArgs args) ++ throws RemoteException { ++ switch (what) { ++ case MSG_STORAGE_STATE_CHANGED: { ++ callback.onStorageStateChanged((String) args.arg1, (String) args.arg2, ++ (String) args.arg3); ++ break; ++ } ++ case MSG_VOLUME_STATE_CHANGED: { ++ callback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3); ++ break; ++ } ++ case MSG_VOLUME_RECORD_CHANGED: { ++ callback.onVolumeRecordChanged((VolumeRecord) args.arg1); ++ break; ++ } ++ case MSG_VOLUME_FORGOTTEN: { ++ callback.onVolumeForgotten((String) args.arg1); ++ break; ++ } ++ case MSG_DISK_SCANNED: { ++ callback.onDiskScanned((DiskInfo) args.arg1, args.argi2); ++ break; ++ } ++ case MSG_DISK_DESTROYED: { ++ callback.onDiskDestroyed((DiskInfo) args.arg1); ++ break; ++ } ++ } ++ } ++ ++ private void notifyStorageStateChanged(String path, String oldState, String newState) { ++ final SomeArgs args = SomeArgs.obtain(); ++ args.arg1 = path; ++ args.arg2 = oldState; ++ args.arg3 = newState; ++ obtainMessage(MSG_STORAGE_STATE_CHANGED, args).sendToTarget(); ++ } ++ ++ private void notifyVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { ++ final SomeArgs args = SomeArgs.obtain(); ++ args.arg1 = vol.clone(); ++ args.argi2 = oldState; ++ args.argi3 = newState; ++ obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget(); ++ } ++ ++ private void notifyVolumeRecordChanged(VolumeRecord rec) { ++ final SomeArgs args = SomeArgs.obtain(); ++ args.arg1 = rec.clone(); ++ obtainMessage(MSG_VOLUME_RECORD_CHANGED, args).sendToTarget(); ++ } ++ ++ private void notifyVolumeForgotten(String fsUuid) { ++ final SomeArgs args = SomeArgs.obtain(); ++ args.arg1 = fsUuid; ++ obtainMessage(MSG_VOLUME_FORGOTTEN, args).sendToTarget(); ++ } ++ ++ private void notifyDiskScanned(DiskInfo disk, int volumeCount) { ++ final SomeArgs args = SomeArgs.obtain(); ++ args.arg1 = disk.clone(); ++ args.argi2 = volumeCount; ++ obtainMessage(MSG_DISK_SCANNED, args).sendToTarget(); ++ } ++ ++ private void notifyDiskDestroyed(DiskInfo disk) { ++ final SomeArgs args = SomeArgs.obtain(); ++ args.arg1 = disk.clone(); ++ obtainMessage(MSG_DISK_DESTROYED, args).sendToTarget(); ++ } ++ } ++ ++ @Override ++ protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { ++ if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return; ++ ++ final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ", 160); ++ synchronized (mLock) { ++ pw.println("Disks:"); ++ pw.increaseIndent(); ++ for (int i = 0; i < mDisks.size(); i++) { ++ final DiskInfo disk = mDisks.valueAt(i); ++ disk.dump(pw); ++ } ++ pw.decreaseIndent(); ++ ++ pw.println(); ++ pw.println("Volumes:"); ++ pw.increaseIndent(); ++ for (int i = 0; i < mVolumes.size(); i++) { ++ final VolumeInfo vol = mVolumes.valueAt(i); ++ if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.id)) continue; ++ vol.dump(pw); ++ } ++ pw.decreaseIndent(); ++ ++ pw.println(); ++ pw.println("Records:"); ++ pw.increaseIndent(); ++ for (int i = 0; i < mRecords.size(); i++) { ++ final VolumeRecord note = mRecords.valueAt(i); ++ note.dump(pw); ++ } ++ pw.decreaseIndent(); ++ ++ pw.println(); ++ pw.println("Primary storage UUID: " + mPrimaryStorageUuid); ++ ++ pw.println(); ++ final Pair pair = StorageManager.getPrimaryStoragePathAndSize(); ++ if (pair == null) { ++ pw.println("Internal storage total size: N/A"); ++ } else { ++ pw.print("Internal storage ("); ++ pw.print(pair.first); ++ pw.print(") total size: "); ++ pw.print(pair.second); ++ pw.print(" ("); ++ pw.print(DataUnit.MEBIBYTES.toBytes(pair.second)); ++ pw.println(" MiB)"); ++ } ++ ++ pw.println(); ++ pw.println("Local unlocked users: " + mLocalUnlockedUsers); ++ pw.println("System unlocked users: " + Arrays.toString(mSystemUnlockedUsers)); ++ ++ final ContentResolver cr = mContext.getContentResolver(); ++ pw.println(); ++ pw.println("Isolated storage, local feature flag: " ++ + Settings.Global.getInt(cr, Settings.Global.ISOLATED_STORAGE_LOCAL, 0)); ++ pw.println("Isolated storage, remote feature flag: " ++ + Settings.Global.getInt(cr, Settings.Global.ISOLATED_STORAGE_REMOTE, 0)); ++ pw.println("Isolated storage, resolved: " + StorageManager.hasIsolatedStorage()); ++ pw.println("Forced scoped storage app list: " ++ + DeviceConfig.getProperty(DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT, ++ PROP_FORCED_SCOPED_STORAGE_WHITELIST)); ++ pw.println("isAutomotive:" + mIsAutomotive); ++ } ++ ++ synchronized (mObbMounts) { ++ pw.println(); ++ pw.println("mObbMounts:"); ++ pw.increaseIndent(); ++ final Iterator>> binders = mObbMounts.entrySet() ++ .iterator(); ++ while (binders.hasNext()) { ++ Entry> e = binders.next(); ++ pw.println(e.getKey() + ":"); ++ pw.increaseIndent(); ++ final List obbStates = e.getValue(); ++ for (final ObbState obbState : obbStates) { ++ pw.println(obbState); ++ } ++ pw.decreaseIndent(); ++ } ++ pw.decreaseIndent(); ++ ++ pw.println(); ++ pw.println("mObbPathToStateMap:"); ++ pw.increaseIndent(); ++ final Iterator> maps = ++ mObbPathToStateMap.entrySet().iterator(); ++ while (maps.hasNext()) { ++ final Entry e = maps.next(); ++ pw.print(e.getKey()); ++ pw.print(" -> "); ++ pw.println(e.getValue()); ++ } ++ pw.decreaseIndent(); ++ } ++ ++ pw.println(); ++ pw.print("Last maintenance: "); ++ pw.println(TimeUtils.formatForLogging(mLastMaintenance)); ++ } ++ ++ /** {@inheritDoc} */ ++ @Override ++ public void monitor() { ++ try { ++ mVold.monitor(); ++ } catch (Exception e) { ++ Slog.wtf(TAG, e); ++ } ++ } ++ ++ private final class StorageManagerInternalImpl extends StorageManagerInternal { ++ // Not guarded by a lock. ++ private final CopyOnWriteArrayList mPolicies = ++ new CopyOnWriteArrayList<>(); ++ ++ @GuardedBy("mResetListeners") ++ private final List mResetListeners = ++ new ArrayList<>(); ++ ++ @Override ++ public void addExternalStoragePolicy(ExternalStorageMountPolicy policy) { ++ // No locking - CopyOnWriteArrayList ++ mPolicies.add(policy); ++ } ++ ++ /** ++ * Check if fuse is running in target user, if it's running then setup its storage dirs. ++ * Return true if storage dirs are mounted. ++ */ ++ @Override ++ public boolean prepareStorageDirs(int userId, Set packageList, ++ String processName) { ++ synchronized (mLock) { ++ if (!mFuseMountedUser.contains(userId)) { ++ Slog.w(TAG, "User " + userId + " is not unlocked yet so skip mounting obb"); ++ return false; ++ } ++ } ++ try { ++ final IVold vold = IVold.Stub.asInterface( ++ ServiceManager.getServiceOrThrow("vold")); ++ for (String pkg : packageList) { ++ final String packageObbDir = ++ String.format("/storage/emulated/%d/Android/obb/%s/", userId, pkg); ++ final String packageDataDir = ++ String.format("/storage/emulated/%d/Android/data/%s/", ++ userId, pkg); ++ ++ // Create package obb and data dir if it doesn't exist. ++ int appUid = UserHandle.getUid(userId, mPmInternal.getPackage(pkg).getUid()); ++ File file = new File(packageObbDir); ++ if (!file.exists()) { ++ vold.setupAppDir(packageObbDir, appUid); ++ } ++ file = new File(packageDataDir); ++ if (!file.exists()) { ++ vold.setupAppDir(packageDataDir, appUid); ++ } ++ } ++ } catch (ServiceManager.ServiceNotFoundException | RemoteException e) { ++ Slog.e(TAG, "Unable to create obb and data directories for " + processName,e); ++ return false; ++ } ++ return true; ++ } ++ ++ @Override ++ public void onExternalStoragePolicyChanged(int uid, String packageName) { ++ final int mountMode = getExternalStorageMountMode(uid, packageName); ++ remountUidExternalStorage(uid, mountMode); ++ } ++ ++ @Override ++ public int getExternalStorageMountMode(int uid, String packageName) { ++ if (ENABLE_ISOLATED_STORAGE) { ++ return getMountMode(uid, packageName); ++ } ++ try { ++ if (packageName == null) { ++ final String[] packagesForUid = mIPackageManager.getPackagesForUid(uid); ++ packageName = packagesForUid[0]; ++ } ++ } catch (RemoteException e) { ++ // Should not happen - same process ++ } ++ // No locking - CopyOnWriteArrayList ++ int mountMode = Integer.MAX_VALUE; ++ for (ExternalStorageMountPolicy policy : mPolicies) { ++ final int policyMode = policy.getMountMode(uid, packageName); ++ if (policyMode == Zygote.MOUNT_EXTERNAL_NONE) { ++ return Zygote.MOUNT_EXTERNAL_NONE; ++ } ++ mountMode = Math.min(mountMode, policyMode); ++ } ++ if (mountMode == Integer.MAX_VALUE) { ++ return Zygote.MOUNT_EXTERNAL_NONE; ++ } ++ return mountMode; ++ } ++ ++ @Override ++ public void addResetListener(StorageManagerInternal.ResetListener listener) { ++ synchronized (mResetListeners) { ++ mResetListeners.add(listener); ++ } ++ } ++ ++ public void onReset(IVold vold) { ++ synchronized (mResetListeners) { ++ for (StorageManagerInternal.ResetListener listener : mResetListeners) { ++ listener.onReset(vold); ++ } ++ } ++ } ++ ++ @Override ++ public void resetUser(int userId) { ++ // TODO(b/145931219): ideally, we only reset storage for the user in question, ++ // but for now, reset everything. ++ mHandler.obtainMessage(H_RESET).sendToTarget(); ++ } ++ ++ @Override ++ public boolean hasLegacyExternalStorage(int uid) { ++ synchronized (mLock) { ++ return mUidsWithLegacyExternalStorage.contains(uid); ++ } ++ } ++ ++ @Override ++ public void prepareAppDataAfterInstall(String packageName, int uid) { ++ int userId = UserHandle.getUserId(uid); ++ final Environment.UserEnvironment userEnv = new Environment.UserEnvironment(userId); ++ ++ // The installer may have downloaded OBBs for this newly installed application; ++ // make sure the OBB dir for the application is setup correctly, if it exists. ++ File[] packageObbDirs = userEnv.buildExternalStorageAppObbDirs(packageName); ++ for (File packageObbDir : packageObbDirs) { ++ try { ++ mVold.fixupAppDir(packageObbDir.getCanonicalPath() + "/", uid); ++ } catch (IOException e) { ++ Log.e(TAG, "Failed to get canonical path for " + packageName); ++ } catch (RemoteException | ServiceSpecificException e) { ++ // TODO(b/149975102) there is a known case where this fails, when a new ++ // user is setup and we try to fixup app dirs for some existing apps. ++ // For now catch the exception and don't crash. ++ Log.e(TAG, "Failed to fixup app dir for " + packageName, e); ++ } ++ } ++ } ++ ++ @Override ++ public boolean isExternalStorageService(int uid) { ++ return mMediaStoreAuthorityAppId == UserHandle.getAppId(uid); ++ } ++ ++ public boolean hasExternalStorage(int uid, String packageName) { ++ // No need to check for system uid. This avoids a deadlock between ++ // PackageManagerService and AppOpsService. ++ if (uid == Process.SYSTEM_UID) { ++ return true; ++ } ++ if (ENABLE_ISOLATED_STORAGE) { ++ return getMountMode(uid, packageName) != Zygote.MOUNT_EXTERNAL_NONE; ++ } ++ // No locking - CopyOnWriteArrayList ++ for (ExternalStorageMountPolicy policy : mPolicies) { ++ final boolean policyHasStorage = policy.hasExternalStorage(uid, packageName); ++ if (!policyHasStorage) { ++ return false; ++ } ++ } ++ return true; ++ } ++ ++ private void killAppForOpChange(int code, int uid) { ++ final IActivityManager am = ActivityManager.getService(); ++ try { ++ am.killUid(UserHandle.getAppId(uid), UserHandle.USER_ALL, ++ AppOpsManager.opToName(code) + " changed."); ++ } catch (RemoteException e) { ++ } ++ } ++ ++ public void onAppOpsChanged(int code, int uid, @Nullable String packageName, int mode) { ++ final long token = Binder.clearCallingIdentity(); ++ try { ++ if (mIsFuseEnabled) { ++ // When using FUSE, we may need to kill the app if the op changes ++ switch(code) { ++ case OP_REQUEST_INSTALL_PACKAGES: ++ // Always kill regardless of op change, to remount apps /storage ++ killAppForOpChange(code, uid); ++ return; ++ case OP_MANAGE_EXTERNAL_STORAGE: ++ if (mode != MODE_ALLOWED) { ++ // Only kill if op is denied, to lose external_storage gid ++ // Killing when op is granted to pickup the gid automatically, ++ // results in a bad UX, especially since the gid only gives access ++ // to unreliable volumes, USB OTGs that are rarely mounted. The app ++ // will get the external_storage gid on next organic restart. ++ killAppForOpChange(code, uid); ++ } ++ return; ++ case OP_LEGACY_STORAGE: ++ updateLegacyStorageApps(packageName, uid, mode == MODE_ALLOWED); ++ return; ++ } ++ } ++ ++ if (mode == MODE_ALLOWED && (code == OP_READ_EXTERNAL_STORAGE ++ || code == OP_WRITE_EXTERNAL_STORAGE ++ || code == OP_REQUEST_INSTALL_PACKAGES)) { ++ final UserManagerInternal userManagerInternal = ++ LocalServices.getService(UserManagerInternal.class); ++ if (userManagerInternal.isUserInitialized(UserHandle.getUserId(uid))) { ++ onExternalStoragePolicyChanged(uid, packageName); ++ } ++ } ++ } finally { ++ Binder.restoreCallingIdentity(token); ++ } ++ } ++ } ++} +diff --git a/services/core/java/com/android/server/lights/LightsService.java b/services/core/java/com/android/server/lights/LightsService.java +index 16e42d21224..f0d7aa6abe7 100644 +--- a/services/core/java/com/android/server/lights/LightsService.java ++++ b/services/core/java/com/android/server/lights/LightsService.java +@@ -32,6 +32,7 @@ import android.os.Looper; + import android.os.PowerManager; + import android.os.RemoteException; + import android.os.ServiceManager; ++import android.os.SystemProperties; + import android.os.Trace; + import android.provider.Settings; + import android.util.Slog; +@@ -301,12 +302,13 @@ public class LightsService extends SystemService { + + ": brightness=" + brightness); + return; + } ++ + // Ideally, we'd like to set the brightness mode through the SF/HWC as well, but + // right now we just fall back to the old path through Lights brightessMode is + // anything but USER or the device shouldBeInLowPersistenceMode(). + if (brightnessMode == BRIGHTNESS_MODE_USER && !shouldBeInLowPersistenceMode() + && mHwLight.type == LightsManager.LIGHT_ID_BACKLIGHT +- && mSurfaceControlMaximumBrightness == 255) { ++ && mSurfaceControlMaximumBrightness == 255 && mHwLight.id == 0) { + // New system + // TODO: the last check should be mSurfaceControlMaximumBrightness != 0; the + // reason we enforce 255 right now is to stay consistent with the old path. In +@@ -321,6 +323,51 @@ public class LightsService extends SystemService { + // Old system + int brightnessInt = BrightnessSynchronizer.brightnessFloatToInt( + getContext(), brightness); ++ ++ if(mHwLight.id == 0) { ++ String fp = SystemProperties.get("ro.vendor.build.fingerprint", "hello"); ++ if(fp.matches(".*astarqlte.*")) { ++ int newBrightness = brightnessInt; ++ if(SystemProperties.getBoolean("persist.sys.samsung.full_brightness", false)) { ++ newBrightness = (int) (brightnessInt * 365.0 / 255.0); ++ } ++ setLightLocked(newBrightness, LIGHT_FLASH_HARDWARE, 0, 0, brightnessMode); ++ return; ++ } ++ ++ int useSamsungBacklight = SystemProperties.getInt("persist.sys.phh.samsung_backlight", -1); ++ if(useSamsungBacklight != 0) { ++ if(useSamsungBacklight > 0 || ++ fp.matches(".*beyond.*lte.*") || ++ fp.matches(".*(crown|star)[q2]*lte.*") || ++ fp.matches(".*(SC-0[23]K|SCV3[89]).*")) { ++ int ratio = 100; ++ if(useSamsungBacklight > 1) ++ ratio = useSamsungBacklight; ++ int newBrightness = brightnessInt * ratio; ++ if(SystemProperties.getBoolean("persist.sys.samsung.full_brightness", false)) { ++ newBrightness = (int) (brightnessInt * 40960.0 / 255.0); ++ } ++ setLightLocked(newBrightness, LIGHT_FLASH_HARDWARE, 0, 0, brightnessMode); ++ return; ++ } ++ } ++ ++ boolean qcomExtendBrightness = SystemProperties.getBoolean("persist.extend.brightness", false); ++ int scale = SystemProperties.getInt("persist.display.max_brightness", 1023); ++ //This is set by vndk-detect ++ int qcomScale = SystemProperties.getInt("persist.sys.qcom-brightness", -1); ++ if(qcomScale != -1) { ++ qcomExtendBrightness = true; ++ scale = qcomScale; ++ } ++ ++ if(qcomExtendBrightness) { ++ setLightLocked(brightnessInt * scale / 255, LIGHT_FLASH_NONE, 0, 0, brightnessMode); ++ return; ++ } ++ } ++ + int color = brightnessInt & 0x000000ff; + color = 0xff000000 | (color << 16) | (color << 8) | color; + setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, brightnessMode); +-- +2.25.1 + diff --git a/patches/platform_frameworks_base/0003-Also-scan-system-overlay.patch b/patches/platform_frameworks_base/0003-Also-scan-system-overlay.patch new file mode 100644 index 0000000..8d4beaf --- /dev/null +++ b/patches/platform_frameworks_base/0003-Also-scan-system-overlay.patch @@ -0,0 +1,43 @@ +From b236ed36a97c09a15fa18d3387ff20dadd5be965 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Tue, 1 May 2018 17:47:36 +0200 +Subject: [PATCH 03/25] Also scan /system/overlay + +Change-Id: Ib0223560606b80cdaaa986b159b34b4db0154589 +--- + core/jni/android_util_AssetManager.cpp | 4 ++++ + core/jni/fd_utils.cpp | 3 ++- + 2 files changed, 6 insertions(+), 1 deletion(-) + +diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp +index 12abc256a20..3133f2f3023 100644 +--- a/core/jni/android_util_AssetManager.cpp ++++ b/core/jni/android_util_AssetManager.cpp +@@ -140,6 +140,10 @@ static jobjectArray NativeCreateIdmapsForStaticOverlaysTargetingAndroid(JNIEnv* + input_dirs.push_back(AssetManager::OEM_OVERLAY_DIR); + } + ++ if (stat("/system/overlay", &st) == 0) { ++ input_dirs.push_back("/system/overlay"); ++ } ++ + if (input_dirs.empty()) { + LOG(WARNING) << "no directories for idmap2 to scan"; + return env->NewObjectArray(0, g_stringClass, nullptr); +diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp +index c72668f84fb..d4307ae6579 100644 +--- a/core/jni/fd_utils.cpp ++++ b/core/jni/fd_utils.cpp +@@ -135,7 +135,8 @@ bool FileDescriptorWhitelist::IsAllowed(const std::string& path) const { + || android::base::StartsWith(path, kSystemOdmOverlayDir) + || android::base::StartsWith(path, kOdmOverlayDir) + || android::base::StartsWith(path, kSystemOemOverlayDir) +- || android::base::StartsWith(path, kOemOverlayDir)) ++ || android::base::StartsWith(path, kOemOverlayDir) ++ || android::base::StartsWith(path, "/system/overlay")) + && android::base::EndsWith(path, kApkSuffix) + && path.find("/../") == std::string::npos) { + return true; +-- +2.17.1 + diff --git a/patches/platform_frameworks_base/0004-Don-t-crash-if-there-is-IR-HAL-is-not-declared.patch b/patches/platform_frameworks_base/0004-Don-t-crash-if-there-is-IR-HAL-is-not-declared.patch new file mode 100644 index 0000000..1cf1519 --- /dev/null +++ b/patches/platform_frameworks_base/0004-Don-t-crash-if-there-is-IR-HAL-is-not-declared.patch @@ -0,0 +1,25 @@ +From 2436b6da10c438a3b71ca3a2a5aa9705e7c00813 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Thu, 17 May 2018 20:28:35 +0200 +Subject: [PATCH 04/25] Don't crash if there is IR HAL is not declared + +--- + services/core/java/com/android/server/ConsumerIrService.java | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/services/core/java/com/android/server/ConsumerIrService.java b/services/core/java/com/android/server/ConsumerIrService.java +index 2ed6c77baa0..c574a03c9a3 100644 +--- a/services/core/java/com/android/server/ConsumerIrService.java ++++ b/services/core/java/com/android/server/ConsumerIrService.java +@@ -50,8 +50,6 @@ public class ConsumerIrService extends IConsumerIrService.Stub { + if (!mHasNativeHal) { + throw new RuntimeException("FEATURE_CONSUMER_IR present, but no IR HAL loaded!"); + } +- } else if (mHasNativeHal) { +- throw new RuntimeException("IR HAL present, but FEATURE_CONSUMER_IR is not set!"); + } + } + +-- +2.17.1 + diff --git a/patches/platform_frameworks_base/0005-Fix-62.patch b/patches/platform_frameworks_base/0005-Fix-62.patch new file mode 100644 index 0000000..d0032cb --- /dev/null +++ b/patches/platform_frameworks_base/0005-Fix-62.patch @@ -0,0 +1,28 @@ +From 25b2ec68fe9ce8a2e3b35fc91ff47ae65ba95be4 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Wed, 30 May 2018 14:05:30 +0200 +Subject: [PATCH 05/25] Fix(?) #62 + +--- + .../src/com/android/keyguard/KeyguardUpdateMonitor.java | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +index 3acbfb87c3f..358053343f2 100644 +--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java ++++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +@@ -1270,7 +1270,10 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab + + @Override + public void onAuthenticationError(int errMsgId, CharSequence errString) { +- handleFingerprintError(errMsgId, errString.toString()); ++ if(errString != null) ++ handleFingerprintError(errMsgId, errString.toString()); ++ else ++ handleFingerprintError(errMsgId, "unknown error"); + } + + @Override +-- +2.17.1 + diff --git a/patches/platform_frameworks_base/0006-property-matching-RROs-allow-to-prefix-the-value-wit.patch b/patches/platform_frameworks_base/0006-property-matching-RROs-allow-to-prefix-the-value-wit.patch new file mode 100644 index 0000000..1bd23cf --- /dev/null +++ b/patches/platform_frameworks_base/0006-property-matching-RROs-allow-to-prefix-the-value-wit.patch @@ -0,0 +1,38 @@ +From f3d0a467c4c1f9df19c3c4819b4c6cb4b26fe2a3 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Fri, 11 Sep 2020 23:42:37 +0200 +Subject: [PATCH] property-matching RROs: allow to prefix the value with + to + do glob match instead of exact match + +Change-Id: I1f5b7c907523eb6e48d41f5163984564d3db9506 +--- + cmds/idmap2/idmap2/Scan.cpp | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/cmds/idmap2/idmap2/Scan.cpp b/cmds/idmap2/idmap2/Scan.cpp +index 36250450cc7..6a5162dd0d9 100644 +--- a/cmds/idmap2/idmap2/Scan.cpp ++++ b/cmds/idmap2/idmap2/Scan.cpp +@@ -15,6 +15,7 @@ + */ + + #include ++#include + + #include + #include +@@ -187,7 +188,10 @@ Result Scan(const std::vector& args) { + // if property set & equal to value, then include overlay - otherwise skip + if (android::base::GetProperty(overlay_info->requiredSystemPropertyName, "") != + overlay_info->requiredSystemPropertyValue) { +- continue; ++ auto osValue = android::base::GetProperty(overlay_info->requiredSystemPropertyName, ""); ++ if(fnmatch(overlay_info->requiredSystemPropertyValue.c_str()+1, osValue.c_str(), 0) != 0) { ++ continue; ++ } + } + } + +-- +2.25.1 + diff --git a/patches/platform_frameworks_base/0007-Galaxy-S9-remaining-of-HAL-onEnroll-is-actually-a-pe.patch b/patches/platform_frameworks_base/0007-Galaxy-S9-remaining-of-HAL-onEnroll-is-actually-a-pe.patch new file mode 100644 index 0000000..2df98de --- /dev/null +++ b/patches/platform_frameworks_base/0007-Galaxy-S9-remaining-of-HAL-onEnroll-is-actually-a-pe.patch @@ -0,0 +1,33 @@ +From 649f6bd7789acae16c1e3fa8c57919bd4ae0d1e3 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Mon, 2 Jul 2018 23:36:39 +0200 +Subject: [PATCH 07/25] [Galaxy S9] "remaining" of HAL onEnroll is actually a + percent of progress + +Change-Id: I8a586163eca93ae3c5bd968d1e7ddbf994ddcc91 +--- + .../server/biometrics/fingerprint/FingerprintService.java | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java +index 6b7ba6a56d8..c247731be13 100644 +--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java ++++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java +@@ -588,7 +588,13 @@ public class FingerprintService extends BiometricServiceBase { + final Fingerprint fingerprint = + new Fingerprint(getBiometricUtils().getUniqueName(getContext(), groupId), + groupId, fingerId, deviceId); +- FingerprintService.super.handleEnrollResult(fingerprint, remaining); ++ ++ int remaining2 = remaining; ++ String fp = android.os.SystemProperties.get("ro.vendor.build.fingerprint"); ++ if(fp != null && (fp.contains("starlte") || fp.contains("star2lte") || fp.contains("starqlte") || fp.contains("star2qlte"))) ++ remaining2 = 100 - remaining2; ++ ++ FingerprintService.super.handleEnrollResult(fingerprint, remaining2); + }); + } + +-- +2.17.1 + diff --git a/patches/platform_frameworks_base/0008-Show-APN-Settings-for-CDMA-carriers.patch b/patches/platform_frameworks_base/0008-Show-APN-Settings-for-CDMA-carriers.patch new file mode 100644 index 0000000..1fd9734 --- /dev/null +++ b/patches/platform_frameworks_base/0008-Show-APN-Settings-for-CDMA-carriers.patch @@ -0,0 +1,25 @@ +From 4ee7b7947513a5481b0b69a3c59e0c7cf6509153 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Mon, 6 Aug 2018 12:49:00 +0200 +Subject: [PATCH 08/25] Show APN Settings for CDMA carriers + +--- + telephony/java/android/telephony/CarrierConfigManager.java | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java +index 3d455d51dd7..d351598a55e 100755 +--- a/telephony/java/android/telephony/CarrierConfigManager.java ++++ b/telephony/java/android/telephony/CarrierConfigManager.java +@@ -3898,7 +3898,7 @@ public class CarrierConfigManager { + sDefaults.putBoolean(KEY_MDN_IS_ADDITIONAL_VOICEMAIL_NUMBER_BOOL, false); + sDefaults.putBoolean(KEY_OPERATOR_SELECTION_EXPAND_BOOL, true); + sDefaults.putBoolean(KEY_PREFER_2G_BOOL, true); +- sDefaults.putBoolean(KEY_SHOW_APN_SETTING_CDMA_BOOL, false); ++ sDefaults.putBoolean(KEY_SHOW_APN_SETTING_CDMA_BOOL, true); + sDefaults.putBoolean(KEY_SHOW_CDMA_CHOICES_BOOL, false); + sDefaults.putBoolean(KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL, false); + sDefaults.putBoolean(KEY_SUPPORT_EMERGENCY_SMS_OVER_IMS_BOOL, false); +-- +2.17.1 + diff --git a/patches/platform_frameworks_base/0009-Don-t-wait-IR-HAL-to-the-infinity-and-beyond.patch b/patches/platform_frameworks_base/0009-Don-t-wait-IR-HAL-to-the-infinity-and-beyond.patch new file mode 100644 index 0000000..5ec0b80 --- /dev/null +++ b/patches/platform_frameworks_base/0009-Don-t-wait-IR-HAL-to-the-infinity-and-beyond.patch @@ -0,0 +1,45 @@ +From c53ddf67401c177fe29d0c2eca146e1548ed4ecb Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Tue, 21 Aug 2018 22:24:02 +0200 +Subject: [PATCH] Don't wait IR HAL to the infinity and beyond + +Change-Id: I3afded27441bbee8244d5fda544b3e6d1238dc1b +--- + .../core/java/com/android/server/ConsumerIrService.java | 7 ------- + services/core/jni/com_android_server_ConsumerIrService.cpp | 2 +- + 2 files changed, 1 insertion(+), 8 deletions(-) + +diff --git a/services/core/java/com/android/server/ConsumerIrService.java b/services/core/java/com/android/server/ConsumerIrService.java +index 2ed6c77baa0..82ec033bc30 100644 +--- a/services/core/java/com/android/server/ConsumerIrService.java ++++ b/services/core/java/com/android/server/ConsumerIrService.java +@@ -46,13 +46,6 @@ public class ConsumerIrService extends IConsumerIrService.Stub { + mWakeLock.setReferenceCounted(true); + + mHasNativeHal = halOpen(); +- if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CONSUMER_IR)) { +- if (!mHasNativeHal) { +- throw new RuntimeException("FEATURE_CONSUMER_IR present, but no IR HAL loaded!"); +- } +- } else if (mHasNativeHal) { +- throw new RuntimeException("IR HAL present, but FEATURE_CONSUMER_IR is not set!"); +- } + } + + @Override +diff --git a/services/core/jni/com_android_server_ConsumerIrService.cpp b/services/core/jni/com_android_server_ConsumerIrService.cpp +index 2ca348b3ae4..148fba9a688 100644 +--- a/services/core/jni/com_android_server_ConsumerIrService.cpp ++++ b/services/core/jni/com_android_server_ConsumerIrService.cpp +@@ -36,7 +36,7 @@ static sp mHal; + + static jboolean halOpen(JNIEnv* /* env */, jobject /* obj */) { + // TODO(b/31632518) +- mHal = IConsumerIr::getService(); ++ mHal = IConsumerIr::tryGetService(); + return mHal != nullptr; + } + +-- +2.25.1 + diff --git a/patches/platform_frameworks_base/0010-Re-order-services-so-that-it-works-even-without-qtag.patch b/patches/platform_frameworks_base/0010-Re-order-services-so-that-it-works-even-without-qtag.patch new file mode 100644 index 0000000..41089fe --- /dev/null +++ b/patches/platform_frameworks_base/0010-Re-order-services-so-that-it-works-even-without-qtag.patch @@ -0,0 +1,39 @@ +From 6befde035c29a499fd4320753909c98a2c239410 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Thu, 8 Nov 2018 23:04:03 +0100 +Subject: [PATCH 10/25] Re-order services so that it works even without qtaguid + +Change-Id: I0c0f527b3ae151d45c68f7ac6c205da3f34e74df +--- + .../android/server/net/NetworkPolicyManagerService.java | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +index d6557f6410e..e65edcdc12c 100644 +--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java ++++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +@@ -731,6 +731,10 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { + Trace.traceBegin(Trace.TRACE_TAG_NETWORK, "systemReady"); + final int oldPriority = Process.getThreadPriority(Process.myTid()); + try { ++ mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class); ++ mAppStandby = LocalServices.getService(AppStandbyInternal.class); ++ mNetworkStats = LocalServices.getService(NetworkStatsManagerInternal.class); ++ + // Boost thread's priority during system server init + Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND); + if (!isBandwidthControlEnabled()) { +@@ -738,10 +742,6 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { + return; + } + +- mUsageStats = LocalServices.getService(UsageStatsManagerInternal.class); +- mAppStandby = LocalServices.getService(AppStandbyInternal.class); +- mNetworkStats = LocalServices.getService(NetworkStatsManagerInternal.class); +- + synchronized (mUidRulesFirstLock) { + synchronized (mNetworkPoliciesSecondLock) { + updatePowerSaveWhitelistUL(); +-- +2.17.1 + diff --git a/patches/platform_frameworks_base/0011-Support-new-samsung-Pie-and-Q-light-hal-and-Samsung-.patch b/patches/platform_frameworks_base/0011-Support-new-samsung-Pie-and-Q-light-hal-and-Samsung-.patch new file mode 100644 index 0000000..b49178f --- /dev/null +++ b/patches/platform_frameworks_base/0011-Support-new-samsung-Pie-and-Q-light-hal-and-Samsung-.patch @@ -0,0 +1,175 @@ +From 04ada322124eca94d2e354f10c45d80d1ad522e3 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Sun, 24 Mar 2019 23:05:14 +0100 +Subject: [PATCH] Support new samsung Pie and Q light hal and Samsung Power + HALs + +--- + services/core/jni/Android.bp | 3 ++ + ...om_android_server_lights_LightsService.cpp | 51 +++++++++++++++++++ + ...droid_server_power_PowerManagerService.cpp | 22 ++++++++ + 3 files changed, 76 insertions(+) + +diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp +index eaa3c66fe0d..74f5b8667c4 100644 +--- a/services/core/jni/Android.bp ++++ b/services/core/jni/Android.bp +@@ -167,6 +167,9 @@ cc_defaults { + "service.incremental", + "suspend_control_aidl_interface-cpp", + "vendor.lineage.power@1.0", ++ "vendor.samsung.hardware.miscpower@2.0", ++ "vendor.samsung.hardware.light@2.0", ++ "vendor.samsung.hardware.light@3.0", + ], + + static_libs: [ +diff --git a/services/core/jni/com_android_server_lights_LightsService.cpp b/services/core/jni/com_android_server_lights_LightsService.cpp +index 35d8219651d..b45b2137d0e 100644 +--- a/services/core/jni/com_android_server_lights_LightsService.cpp ++++ b/services/core/jni/com_android_server_lights_LightsService.cpp +@@ -23,6 +23,10 @@ + + #include + #include ++#include ++#include ++#include ++#include + #include + #include + #include +@@ -40,8 +44,17 @@ using Type = ::android::hardware::light::V2_0::Type; + template + using Return = ::android::hardware::Return; + ++using ISecLight = ::vendor::samsung::hardware::light::V2_0::ISecLight; ++using SecType = ::vendor::samsung::hardware::light::V2_0::SecType; ++using ISehLight = ::vendor::samsung::hardware::light::V3_0::ISehLight; ++using SehType = ::vendor::samsung::hardware::light::V3_0::SehType; ++using SehLightState = ::vendor::samsung::hardware::light::V3_0::SehLightState; + static bool sLightSupported = true; + ++static sp sSecHal; ++static sp sSehHal; ++static bool sSecTried = false; ++ + static bool validate(jint light, jint flash, jint brightness) { + bool valid = true; + +@@ -152,6 +165,44 @@ static void setLight_native( + colorARGB = (colorAlpha << 24) + (colorARGB & 0x00FFFFFF); + } + ++ if(!sSecTried) { ++ sSecHal = ISecLight::getService(); ++ sSehHal = ISehLight::getService(); ++ //sSecTried = true; ++ } ++ ++ if(sSecHal != nullptr) { ++ SecType type = static_cast(light); ++ LightState state = constructState( ++ colorARGB, flashMode, onMS, offMS, brightnessMode); ++ ++ { ++ android::base::Timer t; ++ Return ret = sSecHal->setLightSec(type, state); ++ processReturn(ret, static_cast(light), state); ++ if (t.duration() > 50ms) ALOGD("Excessive delay setting light"); ++ } ++ } ++ ++ if(sSehHal != nullptr && light == 0 && flashMode == static_cast(Flash::HARDWARE)) { ++ SehType type = static_cast(light); ++ SehLightState state {}; ++ state.flashMode = Flash::NONE; ++ Brightness brightness = static_cast(brightnessMode); ++ state.brightnessMode = brightness; ++ state.extendedBrightness = colorARGB; ++ ++ { ++ android::base::Timer t; ++ Return ret = sSehHal->sehSetLight(type, state); ++ if(!ret.isOk()) { ++ ALOGE("Failed to issue set light command."); ++ } ++ if (t.duration() > 50ms) ALOGD("Excessive delay setting light"); ++ } ++ return; ++ } ++ + Type type = static_cast(light); + LightState state = constructState( + colorARGB, flashMode, onMS, offMS, brightnessMode); +diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp +index 33b3bad2b33..47fdcc9a87b 100644 +--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp ++++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp +@@ -24,6 +24,7 @@ + #include + #include + #include ++#include + #include + #include + #include "jni.h" +@@ -64,6 +65,7 @@ using IPowerV1_0 = android::hardware::power::V1_0::IPower; + using IPowerAidl = android::hardware::power::IPower; + using ILineagePowerV1_0 = vendor::lineage::power::V1_0::ILineagePower; + using vendor::lineage::power::V1_0::LineageFeature; ++using ISehMiscPower = vendor::samsung::hardware::miscpower::V2_0::ISehMiscPower; + + namespace android { + +@@ -80,6 +82,8 @@ static sp gPowerHalHidlV1_0_ = nullptr; + static sp gPowerHalHidlV1_1_ = nullptr; + static sp gPowerHalAidl_ = nullptr; + static sp gLineagePowerHalV1_0_ = nullptr; ++static sp gSehMiscPower = nullptr; ++static bool gPowerHalExists = true; + static std::mutex gPowerHalMutex; + + enum class HalVersion { +@@ -125,6 +129,10 @@ static HalVersion connectPowerHalLocked() { + gPowerHalAidlExists = false; + } + } ++ if (gPowerHalExists && gPowerHalHidlV1_0_ == nullptr) { ++ gSehMiscPower = ISehMiscPower::getService(); ++ gPowerHalHidlV1_0_ = IPowerV1_0::getService("miscpower"); ++ } + if (gPowerHalHidlExists && gPowerHalHidlV1_0_ == nullptr) { + gPowerHalHidlV1_0_ = IPowerV1_0::getService(); + if (gPowerHalHidlV1_0_) { +@@ -163,6 +171,12 @@ void connectLineagePowerHalLocked() { + } + } + ++sp getSehMiscPower() { ++ std::lock_guard lock(gPowerHalMutex); ++ connectPowerHalLocked(); ++ return gSehMiscPower; ++} ++ + // Retrieve a copy of PowerHAL HIDL V1_0 + sp getPowerHalHidlV1_0() { + std::lock_guard lock(gPowerHalMutex); +@@ -498,6 +512,14 @@ static void nativeSetInteractive(JNIEnv* /* env */, jclass /* clazz */, jboolean + return; + } + } ++ sp sehMiscPower = getSehMiscPower(); ++ if(sehMiscPower != nullptr) { ++ android::base::Timer t; ++ Return ret = sehMiscPower->setInteractiveAsync(enable, 0); ++ if(!ret.isOk()) { ++ ALOGE("set interactive async() failed: seh misc setInteractiveAsync"); ++ } ++ } + } + + static void nativeSetAutoSuspend(JNIEnv* /* env */, jclass /* clazz */, jboolean enable) { +-- +2.25.1 + diff --git a/patches/platform_frameworks_base/0012-Make-Samsung-fingerprint-broken-HAL-overridable.patch b/patches/platform_frameworks_base/0012-Make-Samsung-fingerprint-broken-HAL-overridable.patch new file mode 100644 index 0000000..704c2f2 --- /dev/null +++ b/patches/platform_frameworks_base/0012-Make-Samsung-fingerprint-broken-HAL-overridable.patch @@ -0,0 +1,29 @@ +From ea68a77524fc2cbfa571d7afa01442460b3ea284 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Tue, 2 Jul 2019 21:19:29 +0200 +Subject: [PATCH 12/25] Make Samsung fingerprint broken HAL overridable + +Change-Id: I8be38daa7c80fdb61e9209f12215e6daea171d03 +--- + .../server/biometrics/fingerprint/FingerprintService.java | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java +index c247731be13..5552086c17f 100644 +--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java ++++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java +@@ -590,8 +590,10 @@ public class FingerprintService extends BiometricServiceBase { + groupId, fingerId, deviceId); + + int remaining2 = remaining; ++ int overrideSamsung = android.os.SystemProperties.getInt("persist.sys.phh.samsung_fingerprint", -1); ++ + String fp = android.os.SystemProperties.get("ro.vendor.build.fingerprint"); +- if(fp != null && (fp.contains("starlte") || fp.contains("star2lte") || fp.contains("starqlte") || fp.contains("star2qlte"))) ++ if(overrideSamsung == 1 || (overrideSamsung != 0 && fp != null && fp.startsWith("samsung/"))) + remaining2 = 100 - remaining2; + + FingerprintService.super.handleEnrollResult(fingerprint, remaining2); +-- +2.17.1 + diff --git a/patches/platform_frameworks_base/0013-Add-property-to-use-linear-brightness-slider.patch b/patches/platform_frameworks_base/0013-Add-property-to-use-linear-brightness-slider.patch new file mode 100644 index 0000000..cb213fb --- /dev/null +++ b/patches/platform_frameworks_base/0013-Add-property-to-use-linear-brightness-slider.patch @@ -0,0 +1,38 @@ +From e9848dbafb20526eed8e5d8dfbd2df01b240cab8 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Sun, 11 Aug 2019 10:30:37 +0200 +Subject: [PATCH 13/25] Add property to use linear brightness slider + +Change-Id: I1af7eb923779fa60c7a735904ba8fc82d0622c1d +--- + .../com/android/settingslib/display/BrightnessUtils.java | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/packages/SettingsLib/src/com/android/settingslib/display/BrightnessUtils.java b/packages/SettingsLib/src/com/android/settingslib/display/BrightnessUtils.java +index 4f86afaa995..f1fe73a6a1d 100644 +--- a/packages/SettingsLib/src/com/android/settingslib/display/BrightnessUtils.java ++++ b/packages/SettingsLib/src/com/android/settingslib/display/BrightnessUtils.java +@@ -51,7 +51,12 @@ public class BrightnessUtils { + * @param max The maximum acceptable value for the setting. + * @return The corresponding setting value. + */ ++ private static final boolean useLinearBrightness = android.os.SystemProperties.getBoolean("persist.sys.phh.linear_brightness", false); + public static final int convertGammaToLinear(int val, int min, int max) { ++ if(useLinearBrightness) { ++ if(val < 4) return 1; ++ return val/4; ++ } + final float normalizedVal = MathUtils.norm(GAMMA_SPACE_MIN, GAMMA_SPACE_MAX, val); + final float ret; + if (normalizedVal <= R) { +@@ -127,6 +132,7 @@ public class BrightnessUtils { + * @return The corresponding slider value + */ + public static final int convertLinearToGammaFloat(float val, float min, float max) { ++ if(useLinearBrightness) return (int)(val*4); + // For some reason, HLG normalizes to the range [0, 12] rather than [0, 1] + final float normalizedVal = MathUtils.norm(min, max, val) * 12; + final float ret; +-- +2.17.1 + diff --git a/patches/platform_frameworks_base/0014-Add-support-for-samsung-touch-proximity-sensor-as-fa.patch b/patches/platform_frameworks_base/0014-Add-support-for-samsung-touch-proximity-sensor-as-fa.patch new file mode 100644 index 0000000..204af25 --- /dev/null +++ b/patches/platform_frameworks_base/0014-Add-support-for-samsung-touch-proximity-sensor-as-fa.patch @@ -0,0 +1,45 @@ +From 33ba0d099de0181f34b3795ad5b2ee25d8c252a4 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Mon, 12 Aug 2019 23:08:26 +0200 +Subject: [PATCH 14/25] Add support for samsung touch proximity sensor as + fallback to real proximity sensor + +--- + .../server/display/DisplayPowerController.java | 14 ++++++++++++++ + 1 file changed, 14 insertions(+) + +diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java +index 9411c562945..774e5a38777 100644 +--- a/services/core/java/com/android/server/display/DisplayPowerController.java ++++ b/services/core/java/com/android/server/display/DisplayPowerController.java +@@ -527,6 +527,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call + + if (!DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT) { + mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); ++ if(mProximitySensor == null) { ++ List sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL); ++ for(Sensor sensor: sensors) { ++ if("com.samsung.sensor.touch_proximity".equals(sensor.getStringType())) ++ mProximitySensor = sensor; ++ } ++ } + if (mProximitySensor != null) { + mProximityThreshold = Math.min(mProximitySensor.getMaximumRange(), + TYPICAL_PROXIMITY_THRESHOLD); +@@ -1970,6 +1977,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call + public void onSensorChanged(SensorEvent event) { + if (mProximitySensorEnabled) { + final long time = SystemClock.uptimeMillis(); ++ if("com.samsung.sensor.touch_proximity".equals(mProximitySensor.getStringType())) { ++ int v = (int)event.values[0]; ++ boolean positive = (v <= 4); ++ android.util.Log.d("PHH", "Samsung sensor changed " + positive + ":" + v); ++ handleProximitySensorEvent(time, positive); ++ return; ++ } + final float distance = event.values[0]; + boolean positive = distance >= 0.0f && distance < mProximityThreshold; + handleProximitySensorEvent(time, positive); +-- +2.17.1 + diff --git a/patches/platform_frameworks_base/0015-Also-add-com.samsung.sensor.physical_proximity-if-av.patch b/patches/platform_frameworks_base/0015-Also-add-com.samsung.sensor.physical_proximity-if-av.patch new file mode 100644 index 0000000..3ecc075 --- /dev/null +++ b/patches/platform_frameworks_base/0015-Also-add-com.samsung.sensor.physical_proximity-if-av.patch @@ -0,0 +1,31 @@ +From 298c7cd14541920c09b4bbcfb3255ec1ddcaad67 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Wed, 14 Aug 2019 23:36:45 +0200 +Subject: [PATCH 15/25] Also add com.samsung.sensor.physical_proximity (if + available, it is more a true proximity sensor than touch proximity sensor) + +--- + .../com/android/server/display/DisplayPowerController.java | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java +index 774e5a38777..4c12ebbd94f 100644 +--- a/services/core/java/com/android/server/display/DisplayPowerController.java ++++ b/services/core/java/com/android/server/display/DisplayPowerController.java +@@ -527,6 +527,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call + + if (!DEBUG_PRETEND_PROXIMITY_SENSOR_ABSENT) { + mProximitySensor = mSensorManager.getDefaultSensor(Sensor.TYPE_PROXIMITY); ++ if(mProximitySensor == null) { ++ List sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL); ++ for(Sensor sensor: sensors) { ++ if("com.samsung.sensor.physical_proximity".equals(sensor.getStringType())) ++ mProximitySensor = sensor; ++ } ++ } + if(mProximitySensor == null) { + List sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL); + for(Sensor sensor: sensors) { +-- +2.17.1 + diff --git a/patches/platform_frameworks_base/0016-fix-crashing-on-devices-with-higher-aspect-ratio-dow.patch b/patches/platform_frameworks_base/0016-fix-crashing-on-devices-with-higher-aspect-ratio-dow.patch new file mode 100644 index 0000000..a5358aa --- /dev/null +++ b/patches/platform_frameworks_base/0016-fix-crashing-on-devices-with-higher-aspect-ratio-dow.patch @@ -0,0 +1,56 @@ +From d2cdf9ba6dc99f28c94e093078c1307dd4414a56 Mon Sep 17 00:00:00 2001 +From: Peter Cai +Date: Thu, 2 Jan 2020 10:16:49 +0800 +Subject: [PATCH] fix crashing on devices with higher aspect ratio down to + sw288dp + +* This is what the Qin 2 (Pro) is. + +Change-Id: Ia39682b327fcf947f06219ce2dab996f53dcb01d +--- + .../SystemUI/res/values-sw288dp/dimens.xml | 33 +++++++++++++++++++ + 1 file changed, 33 insertions(+) + create mode 100644 packages/SystemUI/res/values-sw288dp/dimens.xml + +diff --git a/packages/SystemUI/res/values-sw288dp/dimens.xml b/packages/SystemUI/res/values-sw288dp/dimens.xml +new file mode 100644 +index 00000000000..40adc2e6879 +--- /dev/null ++++ b/packages/SystemUI/res/values-sw288dp/dimens.xml +@@ -0,0 +1,33 @@ ++ ++ ++ ++ ++ 3dp ++ 3dp ++ ++ 5dp ++ 4dp ++ 64dp ++ 64dp ++ ++ 20dp ++ 20dp ++ 12dp ++ 22dp ++ 4dp ++ ++ +-- +2.25.1 + diff --git a/patches/platform_frameworks_base/0017-Remove-orientation-4-from-sensor-earlier-in-the-proc.patch b/patches/platform_frameworks_base/0017-Remove-orientation-4-from-sensor-earlier-in-the-proc.patch new file mode 100644 index 0000000..ab63672 --- /dev/null +++ b/patches/platform_frameworks_base/0017-Remove-orientation-4-from-sensor-earlier-in-the-proc.patch @@ -0,0 +1,28 @@ +From 76cbbb0d09a80eaad75c64281d635e58976bd5be Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Sun, 1 Mar 2020 18:14:40 +0100 +Subject: [PATCH 17/25] Remove orientation 4 from sensor earlier in the process + chain + +Change-Id: Id8e2e860a73d417fc70db6cf5fc5fa5ef187aa50 +--- + .../com/android/server/policy/WindowOrientationListener.java | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/services/core/java/com/android/server/policy/WindowOrientationListener.java b/services/core/java/com/android/server/policy/WindowOrientationListener.java +index 0157706866c..732417b88f4 100644 +--- a/services/core/java/com/android/server/policy/WindowOrientationListener.java ++++ b/services/core/java/com/android/server/policy/WindowOrientationListener.java +@@ -1055,6 +1055,9 @@ public abstract class WindowOrientationListener { + + synchronized (mLock) { + mDesiredRotation = reportedRotation; ++ if(mDesiredRotation >= 4 || mDesiredRotation < 0) { ++ mDesiredRotation = 0; ++ } + newRotation = evaluateRotationChangeLocked(); + } + if (newRotation >=0) { +-- +2.17.1 + diff --git a/patches/platform_frameworks_base/0018-Allow-disabling-of-fingerprint-cleanups-needed-on-so.patch b/patches/platform_frameworks_base/0018-Allow-disabling-of-fingerprint-cleanups-needed-on-so.patch new file mode 100644 index 0000000..6d51a68 --- /dev/null +++ b/patches/platform_frameworks_base/0018-Allow-disabling-of-fingerprint-cleanups-needed-on-so.patch @@ -0,0 +1,39 @@ +From 0dc5a490d8aac60ce2ed3efafc9f6bcb6ced71f4 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Tue, 10 Mar 2020 23:30:17 +0100 +Subject: [PATCH 18/25] Allow disabling of fingerprint cleanups, needed on some + Realme devices that cant enumerate + +Change-Id: I8a486e707712b81711fb1a691faec029499fa897 +--- + .../android/server/biometrics/BiometricServiceBase.java | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java +index 75452ea5fb6..fd77ada1109 100644 +--- a/services/core/java/com/android/server/biometrics/BiometricServiceBase.java ++++ b/services/core/java/com/android/server/biometrics/BiometricServiceBase.java +@@ -344,6 +344,7 @@ public abstract class BiometricServiceBase extends SystemService + private List mEnrolledList; + // List of templates to remove from the HAL + private List mUnknownHALTemplates = new ArrayList<>(); ++ final boolean mNocleanup = android.os.SystemProperties.getBoolean("persist.sys.phh.fingerprint.nocleanup", false); + + InternalEnumerateClient(Context context, + DaemonWrapper daemon, long halDeviceId, IBinder token, +@@ -389,8 +390,10 @@ public abstract class BiometricServiceBase extends SystemService + Slog.e(getTag(), "doTemplateCleanup(): Removing dangling template from framework: " + + identifier.getBiometricId() + " " + + identifier.getName()); +- mUtils.removeBiometricForUser(getContext(), +- getTargetUserId(), identifier.getBiometricId()); ++ if(!mNocleanup) { ++ mUtils.removeBiometricForUser(getContext(), ++ getTargetUserId(), identifier.getBiometricId()); ++ } + FrameworkStatsLog.write(FrameworkStatsLog.BIOMETRIC_SYSTEM_HEALTH_ISSUE_DETECTED, + statsModality(), + BiometricsProtoEnums.ISSUE_UNKNOWN_TEMPLATE_ENROLLED_FRAMEWORK); +-- +2.17.1 + diff --git a/patches/platform_frameworks_base/0019-Support-Huawei-light-HAL.patch b/patches/platform_frameworks_base/0019-Support-Huawei-light-HAL.patch new file mode 100644 index 0000000..2f028f7 --- /dev/null +++ b/patches/platform_frameworks_base/0019-Support-Huawei-light-HAL.patch @@ -0,0 +1,88 @@ +From 858d6a97037b0f9eb751038a41d28564e7221222 Mon Sep 17 00:00:00 2001 +From: Andy CrossGate Yan +Date: Mon, 30 Mar 2020 10:07:00 +0000 +Subject: [PATCH 19/25] Support Huawei light HAL + +For https://github.com/phhusson/treble_experimentations/issues/1204 + +Change-Id: I719a13eb66a46703b7ff0e00113f9b98b3244996 +--- + services/core/jni/Android.bp | 1 + + ...om_android_server_lights_LightsService.cpp | 30 +++++++++++++++++++ + 2 files changed, 31 insertions(+) + +diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp +index 24a3c68cae2..472c925f76f 100644 +--- a/services/core/jni/Android.bp ++++ b/services/core/jni/Android.bp +@@ -170,6 +170,7 @@ cc_defaults { + "vendor.samsung.hardware.miscpower@2.0", + "vendor.samsung.hardware.light@2.0", + "vendor.samsung.hardware.light@3.0", ++ "vendor.huawei.hardware.light@2.0", + ], + + static_libs: [ +diff --git a/services/core/jni/com_android_server_lights_LightsService.cpp b/services/core/jni/com_android_server_lights_LightsService.cpp +index 268b59a5801..5e535e3dcbf 100644 +--- a/services/core/jni/com_android_server_lights_LightsService.cpp ++++ b/services/core/jni/com_android_server_lights_LightsService.cpp +@@ -26,6 +26,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -48,11 +49,15 @@ using SecType = ::vendor::samsung::hardware::light::V2_0::SecType; + using ISehLight = ::vendor::samsung::hardware::light::V3_0::ISehLight; + using SehType = ::vendor::samsung::hardware::light::V3_0::SehType; + using SehLightState = ::vendor::samsung::hardware::light::V3_0::SehLightState; ++using ILightHw = ::vendor::huawei::hardware::light::V2_0::ILight; ++using LightStateHw = ::android::hardware::light::V2_0::LightState; + static bool sLightSupported = true; + + static sp sSecHal; + static sp sSehHal; + static bool sSecTried = false; ++static sp sHwHal; ++static bool sHwTried = false; + + static bool validate(jint light, jint flash, jint brightness) { + bool valid = true; +@@ -192,6 +197,31 @@ static void setLight_native( + return; + } + ++ if (!sHwTried) { ++ sHwHal = ILightHw::getService(); ++ //sHwTried = true; ++ } ++ ++ if (sHwHal != nullptr && light == 0) { ++ ALOGE("sHwHal triggered!"); ++ int brightness = colorARGB & 0xff; ++ int hwBrightness = brightness << 4; ++ LightState state = constructState(hwBrightness, flashMode, onMS, offMS, brightnessMode); ++ bool got260 = false; ++ sHwHal->HWgetSupportedTypes([&](auto types) { ++ for (const auto& type: types) { ++ if (type == 260) { ++ ALOGE("sHwHal reports 260 as a supported type"); ++ got260 = true; ++ } ++ } ++ }); ++ if (got260) { ++ sHwHal->HWsetLight(260, state); ++ return; ++ } ++ } ++ + Type type = static_cast(light); + LightState state = constructState( + colorARGB, flashMode, onMS, offMS, brightnessMode); +-- +2.17.1 + diff --git a/patches/platform_frameworks_base/0020-Always-allow-overriding-the-number-of-work-profiles.patch b/patches/platform_frameworks_base/0020-Always-allow-overriding-the-number-of-work-profiles.patch new file mode 100644 index 0000000..6cf21c2 --- /dev/null +++ b/patches/platform_frameworks_base/0020-Always-allow-overriding-the-number-of-work-profiles.patch @@ -0,0 +1,32 @@ +From dc92080dc096ff77e5d751026d24decc1e424ca9 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Sun, 5 Apr 2020 16:32:46 +0200 +Subject: [PATCH 20/25] Always allow overriding the number of work profiles + +Change-Id: I6eb09aa71663c6fbe7563e3038bffcabdba0ff6a +--- + .../java/com/android/server/pm/UserManagerService.java | 8 ++------ + 1 file changed, 2 insertions(+), 6 deletions(-) + +diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java +index 27924a68ff4..4d687679f59 100644 +--- a/services/core/java/com/android/server/pm/UserManagerService.java ++++ b/services/core/java/com/android/server/pm/UserManagerService.java +@@ -5282,12 +5282,8 @@ public class UserManagerService extends IUserManager.Stub { + */ + private static int getMaxUsersOfTypePerParent(UserTypeDetails userTypeDetails) { + final int defaultMax = userTypeDetails.getMaxAllowedPerParent(); +- if (!Build.IS_DEBUGGABLE) { +- return defaultMax; +- } else { +- if (userTypeDetails.isManagedProfile()) { +- return SystemProperties.getInt("persist.sys.max_profiles", defaultMax); +- } ++ if (userTypeDetails.isManagedProfile()) { ++ return SystemProperties.getInt("persist.sys.max_profiles", defaultMax); + } + return defaultMax; + } +-- +2.17.1 + diff --git a/patches/platform_frameworks_base/0022-HOME-deserves-to-wake-up-devices-just-as-well-as-bac.patch b/patches/platform_frameworks_base/0022-HOME-deserves-to-wake-up-devices-just-as-well-as-bac.patch new file mode 100644 index 0000000..e4561ea --- /dev/null +++ b/patches/platform_frameworks_base/0022-HOME-deserves-to-wake-up-devices-just-as-well-as-bac.patch @@ -0,0 +1,26 @@ +From 3be1d7309cb7c496963c5d391b53ad3932563282 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Sat, 6 Jun 2020 18:21:56 +0200 +Subject: [PATCH] HOME deserves to wake-up devices just as well as back and + menu + +Change-Id: Ia562bafd8c620d00c17e8eb338e4701c6c4a3c3a +--- + core/java/android/view/KeyEvent.java | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java +index 144dd8b3c36..9a704296bbf 100644 +--- a/core/java/android/view/KeyEvent.java ++++ b/core/java/android/view/KeyEvent.java +@@ -1977,6 +1977,7 @@ public class KeyEvent extends InputEvent implements Parcelable { + case KeyEvent.KEYCODE_CAMERA: + case KeyEvent.KEYCODE_FOCUS: + case KeyEvent.KEYCODE_MENU: ++ case KeyEvent.KEYCODE_HOME: + case KeyEvent.KEYCODE_PAIRING: + case KeyEvent.KEYCODE_STEM_1: + case KeyEvent.KEYCODE_STEM_2: +-- +2.25.1 + diff --git a/patches/platform_frameworks_base/0023-On-old-inits-A-only-devices-multi-sim-basedband-prop.patch b/patches/platform_frameworks_base/0023-On-old-inits-A-only-devices-multi-sim-basedband-prop.patch new file mode 100644 index 0000000..db5bd67 --- /dev/null +++ b/patches/platform_frameworks_base/0023-On-old-inits-A-only-devices-multi-sim-basedband-prop.patch @@ -0,0 +1,33 @@ +From b8adbf5e35d5d1e40077b3226ad3f842fc9be6dc Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Mon, 21 Sep 2020 10:30:18 +0200 +Subject: [PATCH 23/25] On old inits (A-only devices), multi-sim basedband + property too long, catch that and make it shorter + +Change-Id: I2f52595409f0d43b148063d5fd90c80d1182ff8c +--- + telephony/java/android/telephony/TelephonyManager.java | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java +index 8ae1ee99b06..4659c7909bc 100644 +--- a/telephony/java/android/telephony/TelephonyManager.java ++++ b/telephony/java/android/telephony/TelephonyManager.java +@@ -10104,7 +10104,13 @@ public class TelephonyManager { + if (SubscriptionManager.isValidPhoneId(phoneId)) { + List newList = updateTelephonyProperty( + TelephonyProperties.baseband_version(), phoneId, version); +- TelephonyProperties.baseband_version(newList); ++ try { ++ TelephonyProperties.baseband_version(newList); ++ } catch(java.lang.IllegalArgumentException e) { ++ List list = new ArrayList<>(); ++ list.add(newList.get(0)); ++ TelephonyProperties.baseband_version(list); ++ } + } + } + +-- +2.17.1 + diff --git a/patches/platform_frameworks_base/0024-Some-devices-have-proximity-sensor-reporting-NaN-as-.patch b/patches/platform_frameworks_base/0024-Some-devices-have-proximity-sensor-reporting-NaN-as-.patch new file mode 100644 index 0000000..1c12480 --- /dev/null +++ b/patches/platform_frameworks_base/0024-Some-devices-have-proximity-sensor-reporting-NaN-as-.patch @@ -0,0 +1,28 @@ +From 11dae80179722994abd9e3501e6c14d4393c4c93 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Tue, 29 Sep 2020 22:39:47 +0200 +Subject: [PATCH 24/25] Some devices have proximity sensor reporting NaN as max + range for some reason. Make them behave standard way by setting 5 cm + +Change-Id: I3c39e3e914a05903c140235702e0480d2d58a612 +--- + .../com/android/server/display/DisplayPowerController.java | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java +index 4c12ebbd94f..01f367e8a2f 100644 +--- a/services/core/java/com/android/server/display/DisplayPowerController.java ++++ b/services/core/java/com/android/server/display/DisplayPowerController.java +@@ -544,6 +544,9 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call + if (mProximitySensor != null) { + mProximityThreshold = Math.min(mProximitySensor.getMaximumRange(), + TYPICAL_PROXIMITY_THRESHOLD); ++ if(Float.isNaN(mProximityThreshold)) { ++ mProximityThreshold = 5.0f; ++ } + } + } + mCurrentScreenBrightnessSetting = getScreenBrightnessSetting(); +-- +2.17.1 + diff --git a/patches/platform_frameworks_base/0025-Fix-brightness-range-not-being-complete-on-Samsung-d.patch b/patches/platform_frameworks_base/0025-Fix-brightness-range-not-being-complete-on-Samsung-d.patch new file mode 100644 index 0000000..28872ee --- /dev/null +++ b/patches/platform_frameworks_base/0025-Fix-brightness-range-not-being-complete-on-Samsung-d.patch @@ -0,0 +1,37 @@ +From c341fe6ac6366d2e78b8ec69d11729180ab5cb84 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Tue, 29 Sep 2020 22:40:10 +0200 +Subject: [PATCH 25/25] Fix brightness range not being complete on Samsung + devices + +On some devices, minimum brightness is 0, which totally messes with +Brightness computations (minimum float brightness becomes -1.0 instead +of 0.0...). +Cheat and have them report 1 as minimum instead, which fixes the slope + +Change-Id: I4d97cbc32490949e83272b81ec6320a5483310b1 +--- + .../java/com/android/server/power/PowerManagerService.java | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java +index 764ac969e18..40aeb32c40a 100644 +--- a/services/core/java/com/android/server/power/PowerManagerService.java ++++ b/services/core/java/com/android/server/power/PowerManagerService.java +@@ -913,9 +913,11 @@ public final class PowerManagerService extends SystemService + + if (min == INVALID_BRIGHTNESS_IN_CONFIG || max == INVALID_BRIGHTNESS_IN_CONFIG + || def == INVALID_BRIGHTNESS_IN_CONFIG) { ++ int correctedMin = mContext.getResources().getInteger(com.android.internal.R.integer ++ .config_screenBrightnessSettingMinimum); ++ if(correctedMin == 0) correctedMin = 1; + mScreenBrightnessMinimum = BrightnessSynchronizer.brightnessIntToFloat( +- mContext.getResources().getInteger(com.android.internal.R.integer +- .config_screenBrightnessSettingMinimum), ++ correctedMin, + PowerManager.BRIGHTNESS_OFF + 1, PowerManager.BRIGHTNESS_ON, + PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX); + mScreenBrightnessMaximum = BrightnessSynchronizer.brightnessIntToFloat( +-- +2.17.1 + diff --git a/patches/platform_frameworks_native/0001-Some-Samsung-devices-requires-lying-colorspace.patch b/patches/platform_frameworks_native/0001-Some-Samsung-devices-requires-lying-colorspace.patch new file mode 100644 index 0000000..1e9dc36 --- /dev/null +++ b/patches/platform_frameworks_native/0001-Some-Samsung-devices-requires-lying-colorspace.patch @@ -0,0 +1,32 @@ +From d8fbdb4092155a1c8f8e2b2ee77b4fcfb7102367 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Mon, 12 Aug 2019 23:48:37 +0200 +Subject: [PATCH 1/8] Some Samsung devices requires lying colorspace + +Change-Id: I4153b8e7abc10c519565e4e4386c6388621477b2 +--- + opengl/libs/EGL/egl_platform_entries.cpp | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp +index aa24e8ee6..1c6380f6f 100644 +--- a/opengl/libs/EGL/egl_platform_entries.cpp ++++ b/opengl/libs/EGL/egl_platform_entries.cpp +@@ -454,8 +454,14 @@ EGLBoolean eglGetConfigAttribImpl(EGLDisplay dpy, EGLConfig config, + // ---------------------------------------------------------------------------- + + // Translates EGL color spaces to Android data spaces. ++static int samsungColorspace = -1; + static android_dataspace dataSpaceFromEGLColorSpace(EGLint colorspace) { ++ if(samsungColorspace == -1) { ++ samsungColorspace = property_get_bool("persist.sys.phh.samsung_colorspace", false); ++ } + if (colorspace == EGL_GL_COLORSPACE_LINEAR_KHR) { ++ if(samsungColorspace) ++ return HAL_DATASPACE_UNKNOWN; + return HAL_DATASPACE_UNKNOWN; + } else if (colorspace == EGL_GL_COLORSPACE_SRGB_KHR) { + return HAL_DATASPACE_V0_SRGB; +-- +2.17.1 + diff --git a/patches/platform_frameworks_native/0002-On-Samsung-we-need-to-send-a-hack-message-to-HAL-to-.patch b/patches/platform_frameworks_native/0002-On-Samsung-we-need-to-send-a-hack-message-to-HAL-to-.patch new file mode 100644 index 0000000..8204295 --- /dev/null +++ b/patches/platform_frameworks_native/0002-On-Samsung-we-need-to-send-a-hack-message-to-HAL-to-.patch @@ -0,0 +1,51 @@ +From 61f5f4b804e8e14f15b928630489f22a5e7df04e Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Wed, 14 Aug 2019 23:37:10 +0200 +Subject: [PATCH 2/8] On Samsung, we need to send a hack-message to HAL to get + all Sensors + +Change-Id: Id6a1fa48340de61c418493668e9abd22c2599376 +--- + opengl/libs/EGL/egl_platform_entries.cpp | 2 +- + services/sensorservice/SensorDevice.cpp | 4 ++++ + 2 files changed, 5 insertions(+), 1 deletion(-) + +diff --git a/opengl/libs/EGL/egl_platform_entries.cpp b/opengl/libs/EGL/egl_platform_entries.cpp +index 1c6380f6f..6e810e592 100644 +--- a/opengl/libs/EGL/egl_platform_entries.cpp ++++ b/opengl/libs/EGL/egl_platform_entries.cpp +@@ -457,7 +457,7 @@ EGLBoolean eglGetConfigAttribImpl(EGLDisplay dpy, EGLConfig config, + static int samsungColorspace = -1; + static android_dataspace dataSpaceFromEGLColorSpace(EGLint colorspace) { + if(samsungColorspace == -1) { +- samsungColorspace = property_get_bool("persist.sys.phh.samsung_colorspace", false); ++ samsungColorspace = base::GetBoolProperty("persist.sys.phh.samsung_colorspace", false); + } + if (colorspace == EGL_GL_COLORSPACE_LINEAR_KHR) { + if(samsungColorspace) +diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp +index 8a282e238..53b686870 100644 +--- a/services/sensorservice/SensorDevice.cpp ++++ b/services/sensorservice/SensorDevice.cpp +@@ -34,7 +34,9 @@ + #include + #include + ++#include + using namespace android::hardware::sensors; ++ + using namespace android::hardware::sensors::V1_0; + using namespace android::hardware::sensors::V1_0::implementation; + using android::hardware::sensors::V2_0::EventQueueFlagBits; +@@ -134,6 +136,8 @@ SensorDevice::SensorDevice() + void SensorDevice::initializeSensorList() { + float minPowerMa = 0.001; // 1 microAmp + ++ if(::android::base::GetBoolProperty("persist.sys.phh.samsung_sensors", false)) ++ setMode(5555); + checkReturn(mSensors->getSensorsList( + [&](const auto &list) { + const size_t count = list.size(); +-- +2.17.1 + diff --git a/patches/platform_frameworks_native/0003-Use-BKK-s-hwcomposer-hacks-to-properly-display-FOD-l.patch b/patches/platform_frameworks_native/0003-Use-BKK-s-hwcomposer-hacks-to-properly-display-FOD-l.patch new file mode 100644 index 0000000..32dbde4 --- /dev/null +++ b/patches/platform_frameworks_native/0003-Use-BKK-s-hwcomposer-hacks-to-properly-display-FOD-l.patch @@ -0,0 +1,42 @@ +From 113f48e1e25d168e16e6a6717c7ea70fbef033e3 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Tue, 10 Mar 2020 23:26:55 +0100 +Subject: [PATCH 3/8] Use BKK's hwcomposer hacks to properly display FOD layers + +Insecure hack: Set FOD layers to magical Z values to trigger +OPPO/Realme/Oneplus' whole blitter behaviours: +- reports touch event to fingerprint driver +- removes dim on touched layer + +Change-Id: I1d924b1e5c2389c8df17174c079cee8ef2380112 +--- + .../CompositionEngine/src/OutputLayer.cpp | 13 ++++++++++++- + 1 file changed, 12 insertions(+), 1 deletion(-) + +diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +index 1faf775ed..deb0b0923 100644 +--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp ++++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +@@ -370,7 +370,18 @@ void OutputLayer::writeOutputDependentGeometryStateToHWC( + static_cast(error)); + } + +- if (auto error = hwcLayer->setZOrder(outputDependentState.z); error != hal::Error::NONE) { ++ uint32_t z = outputDependentState.z; ++ if(strstr(getLayerFE().getDebugName(), "Fingerprint on display") != nullptr) { ++ ALOGE("Found fingerprint on display!"); ++ z = 0x41000031; ++ } ++ ++ if(strstr(getLayerFE().getDebugName(), "Fingerprint on display.touched") != nullptr) { ++ ALOGE("Found fingerprint on display touched!"); ++ z = 0x41000033; ++ } ++ ++ if (auto error = hwcLayer->setZOrder(z); error != hal::Error::NONE) { + ALOGE("[%s] Failed to set Z %u: %s (%d)", getLayerFE().getDebugName(), + outputDependentState.z, to_string(error).c_str(), static_cast(error)); + } +-- +2.17.1 + diff --git a/patches/platform_frameworks_native/0004-Samsung-Xiaomi-new-FOD-HBM-controls.patch b/patches/platform_frameworks_native/0004-Samsung-Xiaomi-new-FOD-HBM-controls.patch new file mode 100644 index 0000000..a263875 --- /dev/null +++ b/patches/platform_frameworks_native/0004-Samsung-Xiaomi-new-FOD-HBM-controls.patch @@ -0,0 +1,122 @@ +From 0c7b289c3d724f32f4d64e8972736f3aa8e3261f Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Wed, 20 May 2020 21:16:56 +0200 +Subject: [PATCH 4/8] Samsung + Xiaomi new FOD HBM controls + +Change-Id: I5ab27fa70882efa85f0c917bf31bb32adaa09bb1 +--- + services/surfaceflinger/BufferQueueLayer.cpp | 19 ++++++++++++++++- + .../CompositionEngine/src/OutputLayer.cpp | 21 +++++++++++++++++++ + 2 files changed, 39 insertions(+), 1 deletion(-) + +diff --git a/services/surfaceflinger/BufferQueueLayer.cpp b/services/surfaceflinger/BufferQueueLayer.cpp +index 07be7916e..472ceda4d 100644 +--- a/services/surfaceflinger/BufferQueueLayer.cpp ++++ b/services/surfaceflinger/BufferQueueLayer.cpp +@@ -26,6 +26,7 @@ + #include + #include + #include ++#include + + #include "LayerRejecter.h" + #include "SurfaceInterceptor.h" +@@ -34,6 +35,14 @@ + #include "Scheduler/LayerHistory.h" + #include "TimeStats/TimeStats.h" + ++static bool sCheckedProps = false; ++static bool sSamsungFod = false; ++static void init_fod_props() { ++ if(sCheckedProps) return; ++ sCheckedProps = true; ++ sSamsungFod = property_get_bool("persist.sys.phh.fod.samsung", false); ++} ++ + namespace android { + + BufferQueueLayer::BufferQueueLayer(const LayerCreationArgs& args) : BufferLayer(args) {} +@@ -486,6 +495,7 @@ void BufferQueueLayer::onFirstRef() { + mConsumer = + mFlinger->getFactory().createBufferLayerConsumer(consumer, mFlinger->getRenderEngine(), + mTextureName, this); ++ init_fod_props(); + mConsumer->setConsumerUsageBits(getEffectiveUsage(0)); + + mContentsChangedListener = new ContentsChangedListener(this); +@@ -509,9 +519,16 @@ status_t BufferQueueLayer::setDefaultBufferProperties(uint32_t w, uint32_t h, Pi + return BAD_VALUE; + } + ++ init_fod_props(); ++ + setDefaultBufferSize(w, h); + mConsumer->setDefaultBufferFormat(format); +- mConsumer->setConsumerUsageBits(getEffectiveUsage(0)); ++ uint64_t usageBits = getEffectiveUsage(0); ++ if(sSamsungFod && strstr(mName.c_str(), "Fingerprint on display.touched") != nullptr) { ++ ALOGE("Found on touched layer!"); ++ usageBits |= 0x400000000LL; ++ } ++ mConsumer->setConsumerUsageBits(usageBits); + + return NO_ERROR; + } +diff --git a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +index deb0b0923..5f9620715 100644 +--- a/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp ++++ b/services/surfaceflinger/CompositionEngine/src/OutputLayer.cpp +@@ -22,6 +22,7 @@ + #include + #include + #include ++#include + + // TODO(b/129481165): remove the #pragma below and fix conversion issues + #pragma clang diagnostic push +@@ -32,6 +33,10 @@ + // TODO(b/129481165): remove the #pragma below and fix conversion issues + #pragma clang diagnostic pop // ignored "-Wconversion" + ++static bool sCheckedProps = false; ++static bool sBBKFod = false; ++static bool sXiaomiFod = false; ++ + namespace android::compositionengine { + + OutputLayer::~OutputLayer() = default; +@@ -370,15 +375,31 @@ void OutputLayer::writeOutputDependentGeometryStateToHWC( + static_cast(error)); + } + ++ if(!sCheckedProps) { ++ sCheckedProps = true; ++ sBBKFod = property_get_bool("persist.sys.phh.fod.bbk", false); ++ sXiaomiFod = property_get_bool("persist.sys.phh.fod.xiaomi", false); ++ } ++ + uint32_t z = outputDependentState.z; + if(strstr(getLayerFE().getDebugName(), "Fingerprint on display") != nullptr) { + ALOGE("Found fingerprint on display!"); + z = 0x41000031; ++ if(sBBKFod) { ++ z = 0x41000031; ++ } else if(sXiaomiFod) { ++ z |= 0x1000000; ++ } + } + + if(strstr(getLayerFE().getDebugName(), "Fingerprint on display.touched") != nullptr) { + ALOGE("Found fingerprint on display touched!"); + z = 0x41000033; ++ if(sBBKFod) { ++ z = 0x41000033; ++ } else if(sXiaomiFod) { ++ z |= 0x2000000; ++ } + } + + if (auto error = hwcLayer->setZOrder(z); error != hal::Error::NONE) { +-- +2.17.1 + diff --git a/patches/platform_frameworks_native/0005-vr_hwc-crashes-because-it-registers-a-2.3-hwc-while-.patch b/patches/platform_frameworks_native/0005-vr_hwc-crashes-because-it-registers-a-2.3-hwc-while-.patch new file mode 100644 index 0000000..5a09fc2 --- /dev/null +++ b/patches/platform_frameworks_native/0005-vr_hwc-crashes-because-it-registers-a-2.3-hwc-while-.patch @@ -0,0 +1,27 @@ +From 15125c6d89352f46742c0e575556e43720e1c03c Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Fri, 11 Sep 2020 21:33:05 +0200 +Subject: [PATCH 5/8] vr_hwc crashes because it registers a 2.3 hwc, while + manifest says 2.1 + +Change-Id: Icfaea3e471209e5773f52f880ffbcf5de744737e +--- + services/vr/hardware_composer/manifest_vr_hwc.xml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/services/vr/hardware_composer/manifest_vr_hwc.xml b/services/vr/hardware_composer/manifest_vr_hwc.xml +index 1068cac33..6694ac086 100644 +--- a/services/vr/hardware_composer/manifest_vr_hwc.xml ++++ b/services/vr/hardware_composer/manifest_vr_hwc.xml +@@ -2,7 +2,7 @@ + + android.hardware.graphics.composer + hwbinder +- 2.1 ++ 2.3 + + IComposer + vr +-- +2.17.1 + diff --git a/patches/platform_frameworks_native/0006-Add-persist.sys.phh.no_present_or_validate-property-.patch b/patches/platform_frameworks_native/0006-Add-persist.sys.phh.no_present_or_validate-property-.patch new file mode 100644 index 0000000..f52d686 --- /dev/null +++ b/patches/platform_frameworks_native/0006-Add-persist.sys.phh.no_present_or_validate-property-.patch @@ -0,0 +1,50 @@ +From 1003d0293dc372b8fc0bbefe160f7d6a770b184b Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Wed, 30 Sep 2020 22:41:39 +0200 +Subject: [PATCH 6/8] Add persist.sys.phh.no_present_or_validate property to + disable presentOrValidate since on vndk 26/27 presentOrValidate can be broken + +Change-Id: I5f0c6c5e129bc21348d6f279d3ba455a5a0f008b +--- + .../surfaceflinger/DisplayHardware/HWComposer.cpp | 13 ++++++++++++- + 1 file changed, 12 insertions(+), 1 deletion(-) + +diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp +index 7a2f0f34e..1ea104521 100644 +--- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp ++++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp +@@ -34,6 +34,7 @@ + #include + #include + #include ++#include + + #include "../Layer.h" // needed only for debugging + #include "../Promise.h" +@@ -500,12 +501,22 @@ status_t HWComposer::getDeviceCompositionChanges( + + hal::Error error = hal::Error::NONE; + ++ static int forceNoPresentOrValidate = -1; ++ if(forceNoPresentOrValidate == -1) { ++ bool res = android::base::GetBoolProperty("persist.sys.phh.no_present_or_validate", false); ++ if(res) { ++ forceNoPresentOrValidate = 1; ++ } else { ++ forceNoPresentOrValidate = 0; ++ } ++ } ++ + // First try to skip validate altogether when there is no client + // composition. When there is client composition, since we haven't + // rendered to the client target yet, we should not attempt to skip + // validate. + displayData.validateWasSkipped = false; +- if (!frameUsesClientComposition) { ++ if (forceNoPresentOrValidate == 0 && !frameUsesClientComposition) { + sp outPresentFence; + uint32_t state = UINT32_MAX; + error = hwcDisplay->presentOrValidate(&numTypes, &numRequests, &outPresentFence , &state); +-- +2.17.1 + diff --git a/patches/platform_frameworks_native/0007-Add-persist.sys.phh.disable_sensor_direct_report-pro.patch b/patches/platform_frameworks_native/0007-Add-persist.sys.phh.disable_sensor_direct_report-pro.patch new file mode 100644 index 0000000..76d7a46 --- /dev/null +++ b/patches/platform_frameworks_native/0007-Add-persist.sys.phh.disable_sensor_direct_report-pro.patch @@ -0,0 +1,41 @@ +From dc47d76a49d2a2d8dc2bd6bf68098cd910fcb364 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Wed, 30 Sep 2020 22:51:37 +0200 +Subject: [PATCH 7/8] Add persist.sys.phh.disable_sensor_direct_report property + to disable Sensors direct report (it seems to break Qin 2 Pro sensors HAL) + +Change-Id: I0763324a4bd6a3ba6716c396c4cb3f0772b85d62 +--- + services/sensorservice/SensorDevice.cpp | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp +index 53b686870..87899e2cd 100644 +--- a/services/sensorservice/SensorDevice.cpp ++++ b/services/sensorservice/SensorDevice.cpp +@@ -22,6 +22,7 @@ + #include "convertV2_1.h" + + #include ++#include + #include + #include + #include +@@ -129,8 +130,12 @@ SensorDevice::SensorDevice() + + initializeSensorList(); + +- mIsDirectReportSupported = +- (checkReturnAndGetStatus(mSensors->unregisterDirectChannel(-1)) != INVALID_OPERATION); ++ if(::android::base::GetBoolProperty("persist.sys.phh.disable_sensor_direct_report", false)) { ++ mIsDirectReportSupported = false; ++ } else { ++ mIsDirectReportSupported = ++ (checkReturnAndGetStatus(mSensors->unregisterDirectChannel(-1)) != INVALID_OPERATION); ++ } + } + + void SensorDevice::initializeSensorList() { +-- +2.17.1 + diff --git a/patches/platform_frameworks_native/0008-Don-t-quantize-max-range-with-a-resolution-of-0.patch b/patches/platform_frameworks_native/0008-Don-t-quantize-max-range-with-a-resolution-of-0.patch new file mode 100644 index 0000000..cc92347 --- /dev/null +++ b/patches/platform_frameworks_native/0008-Don-t-quantize-max-range-with-a-resolution-of-0.patch @@ -0,0 +1,52 @@ +From 251273e408c2090789f758c4f873df0cd044ee15 Mon Sep 17 00:00:00 2001 +From: Anthony Stange +Date: Thu, 9 Jul 2020 09:35:29 -0400 +Subject: [PATCH 8/8] Don't quantize max range with a resolution of 0 + +Sensors without a default resolution can be left with a resolution of 0 +during initialization. Then, when the framework attempted to requantize +the max range using the resolution value, it would perform a +divide-by-zero operation. Encapsulate this logic with a check of the +resolution value to ensure this case doesn't occur. + +Bug: 160862405 +Test: Run on device and verify no crashes occur. +Merged-In: I6ab02072a11078f05f65b61310d5637743e258ff +Change-Id: I6ab02072a11078f05f65b61310d5637743e258ff +(cherry picked from commit ec5b4b1eebd9919ec54ff2d03d17fbf387012d1d) +--- + services/sensorservice/SensorDevice.cpp | 18 ++++++++++++------ + 1 file changed, 12 insertions(+), 6 deletions(-) + +diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp +index 87899e2cd..eb5bfe281 100644 +--- a/services/sensorservice/SensorDevice.cpp ++++ b/services/sensorservice/SensorDevice.cpp +@@ -162,12 +162,18 @@ void SensorDevice::initializeSensorList() { + SensorDeviceUtils::defaultResolutionForType(sensor.type); + } + +- double promotedResolution = sensor.resolution; +- double promotedMaxRange = sensor.maxRange; +- if (fmod(promotedMaxRange, promotedResolution) != 0) { +- ALOGW("%s's max range %f is not a multiple of the resolution %f", +- sensor.name, sensor.maxRange, sensor.resolution); +- SensorDeviceUtils::quantizeValue(&sensor.maxRange, promotedResolution); ++ // Some sensors don't have a default resolution and will be left at 0. ++ // Don't crash in this case since CTS will verify that devices don't go to ++ // production with a resolution of 0. ++ if (sensor.resolution != 0) { ++ double promotedResolution = sensor.resolution; ++ double promotedMaxRange = sensor.maxRange; ++ if (fmod(promotedMaxRange, promotedResolution) != 0) { ++ ALOGW("%s's max range %f is not a multiple of the resolution %f", ++ sensor.name, sensor.maxRange, sensor.resolution); ++ SensorDeviceUtils::quantizeValue( ++ &sensor.maxRange, promotedResolution); ++ } + } + } + +-- +2.17.1 + diff --git a/patches/platform_system_bt/0001-Make-BTM_BYPASS_EXTRA_ACL_SETUP-dynamic.patch b/patches/platform_system_bt/0001-Make-BTM_BYPASS_EXTRA_ACL_SETUP-dynamic.patch new file mode 100644 index 0000000..5011919 --- /dev/null +++ b/patches/platform_system_bt/0001-Make-BTM_BYPASS_EXTRA_ACL_SETUP-dynamic.patch @@ -0,0 +1,105 @@ +From 3337759a488bf320bbebcb5b7d5da981555bb758 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Tue, 20 Feb 2018 23:04:50 +0100 +Subject: [PATCH 1/3] Make BTM_BYPASS_EXTRA_ACL_SETUP dynamic + +Change-Id: Icb0868566b29b053ed7e83c9fd32e225af3f2e46 +--- + hci/include/bt_hci_bdroid.h | 3 +++ + internal_include/bt_target.h | 3 +++ + stack/btm/btm_acl.cc | 20 ++++++++++---------- + stack/btm/btm_sec.cc | 18 +++++++++--------- + 4 files changed, 25 insertions(+), 19 deletions(-) + +diff --git a/hci/include/bt_hci_bdroid.h b/hci/include/bt_hci_bdroid.h +index 110354f05..b81185e2c 100644 +--- a/hci/include/bt_hci_bdroid.h ++++ b/hci/include/bt_hci_bdroid.h +@@ -32,6 +32,9 @@ + #ifdef HAS_BDROID_BUILDCFG + #include "bdroid_buildcfg.h" + #endif ++#ifndef BTM_BYPASS_EXTRA_ACL_SETUP ++#define BTM_BYPASS_EXTRA_ACL_SETUP TRUE ++#endif + + /****************************************************************************** + * Constants & Macros +diff --git a/internal_include/bt_target.h b/internal_include/bt_target.h +index 4aca86822..57bd69e9f 100644 +--- a/internal_include/bt_target.h ++++ b/internal_include/bt_target.h +@@ -32,6 +32,9 @@ + #ifdef HAS_BDROID_BUILDCFG + #include "bdroid_buildcfg.h" + #endif ++#ifndef BTM_BYPASS_EXTRA_ACL_SETUP ++#define BTM_BYPASS_EXTRA_ACL_SETUP TRUE ++#endif + + #include "bt_types.h" /* This must be defined AFTER buildcfg.h */ + +diff --git a/stack/btm/btm_acl.cc b/stack/btm/btm_acl.cc +index 3c8a6a69a..894459854 100644 +--- a/stack/btm/btm_acl.cc ++++ b/stack/btm/btm_acl.cc +@@ -1208,17 +1208,17 @@ void btm_read_remote_ext_features_failed(uint8_t status, uint16_t handle) { + void btm_establish_continue(tACL_CONN* p_acl_cb) { + tBTM_BL_EVENT_DATA evt_data; + BTM_TRACE_DEBUG("btm_establish_continue"); +-#if (BTM_BYPASS_EXTRA_ACL_SETUP == FALSE) +- if (p_acl_cb->transport == BT_TRANSPORT_BR_EDR) { +- /* For now there are a some devices that do not like sending */ +- /* commands events and data at the same time. */ +- /* Set the packet types to the default allowed by the device */ +- btm_set_packet_types(p_acl_cb, btm_cb.btm_acl_pkt_types_supported); +- +- if (btm_cb.btm_def_link_policy) +- BTM_SetLinkPolicy(p_acl_cb->remote_addr, &btm_cb.btm_def_link_policy); ++ if (!BTM_BYPASS_EXTRA_ACL_SETUP) { ++ if (p_acl_cb->transport == BT_TRANSPORT_BR_EDR) { ++ /* For now there are a some devices that do not like sending */ ++ /* commands events and data at the same time. */ ++ /* Set the packet types to the default allowed by the device */ ++ btm_set_packet_types(p_acl_cb, btm_cb.btm_acl_pkt_types_supported); ++ ++ if (btm_cb.btm_def_link_policy) ++ BTM_SetLinkPolicy(p_acl_cb->remote_addr, &btm_cb.btm_def_link_policy); ++ } + } +-#endif + if (p_acl_cb->link_up_issued) { + BTM_TRACE_ERROR("%s: Already link is up ", __func__); + return; +diff --git a/stack/btm/btm_sec.cc b/stack/btm/btm_sec.cc +index 63e4f6ce1..7dea81b44 100644 +--- a/stack/btm/btm_sec.cc ++++ b/stack/btm/btm_sec.cc +@@ -4168,15 +4168,15 @@ void btm_sec_connected(const RawAddress& bda, uint16_t handle, uint8_t status, + if (p_acl_cb) { + /* whatever is in btm_establish_continue() without reporting the BTM_BL_CONN_EVT + * event */ +-#if (BTM_BYPASS_EXTRA_ACL_SETUP == FALSE) +- /* For now there are a some devices that do not like sending */ +- /* commands events and data at the same time. */ +- /* Set the packet types to the default allowed by the device */ +- btm_set_packet_types(p_acl_cb, btm_cb.btm_acl_pkt_types_supported); +- +- if (btm_cb.btm_def_link_policy) +- BTM_SetLinkPolicy(p_acl_cb->remote_addr, &btm_cb.btm_def_link_policy); +-#endif ++ if(!BTM_BYPASS_EXTRA_ACL_SETUP) { ++ /* For now there are a some devices that do not like sending */ ++ /* commands events and data at the same time. */ ++ /* Set the packet types to the default allowed by the device */ ++ btm_set_packet_types(p_acl_cb, btm_cb.btm_acl_pkt_types_supported); ++ ++ if (btm_cb.btm_def_link_policy) ++ BTM_SetLinkPolicy(p_acl_cb->remote_addr, &btm_cb.btm_def_link_policy); ++ } + } + btm_acl_created(bda, p_dev_rec->dev_class, p_dev_rec->sec_bd_name, handle, + HCI_ROLE_SLAVE, BT_TRANSPORT_BR_EDR); +-- +2.17.1 + diff --git a/patches/platform_system_bt/0002-Add-props-to-control-supported-features-and-states-1.patch b/patches/platform_system_bt/0002-Add-props-to-control-supported-features-and-states-1.patch new file mode 100644 index 0000000..c929365 --- /dev/null +++ b/patches/platform_system_bt/0002-Add-props-to-control-supported-features-and-states-1.patch @@ -0,0 +1,153 @@ +From a39b9abfe1e7e1bea9c409a1680cb6f18a6ec354 Mon Sep 17 00:00:00 2001 +From: penn5 +Date: Mon, 4 Mar 2019 22:21:07 +0000 +Subject: [PATCH 2/3] Add props to control supported features and states (#1) + +* Add bitmask for supported fields +Use persist.sys.bt.unsupport.states, defaults to 0, left-aligned. +Huawei suggest to use 000000000000000000000011111 + +* Add bitmask to LOCAL_SUPPORTED_FEATURES +For Huawei, suggest to use 00000001 + +Documentation: +- persist.sys.bt.unsupport.features matches the values: +HCI_3_SLOT_PACKETS..HCI_LMP_EXTENDED_SUPPORTED (max 8bits * 8 bytes = +64 bits) +- persist.sys.bt.unsupport.states matches the values: +BTM_BLE_STATE_INVALID..BTM_BLE_STATE_SCAN_ADV (max = 0x3ff, 11 bits) +- persist.sys.bt.unsupport.stdfeatures ( max: 16 bits) +HCI_LE_ENCRYPTION..HCI_LE_PERIODIC_ADVERTISING +--- + hci/src/hci_packet_parser.cc | 77 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 77 insertions(+) + +diff --git a/hci/src/hci_packet_parser.cc b/hci/src/hci_packet_parser.cc +index b1efd444d..88dc4c6cd 100644 +--- a/hci/src/hci_packet_parser.cc ++++ b/hci/src/hci_packet_parser.cc +@@ -27,6 +27,8 @@ + #include "hcimsgs.h" + #include "osi/include/log.h" + ++#include ++ + static const command_opcode_t NO_OPCODE_CHECKING = 0; + + static const allocator_t* buffer_allocator; +@@ -108,6 +110,31 @@ static void parse_read_local_supported_commands_response( + buffer_allocator->free(response); + } + ++static void setup_bitmask(uint8_t *v, const char* property) { ++ char str[PROPERTY_VALUE_MAX]; ++ int len = property_get(property, str, ""); ++ memset(v, 255, 8); ++ for(int i = 0; ifree(response); + } + +@@ -148,6 +185,19 @@ static void parse_ble_read_buffer_size_response(BT_HDR* response, + buffer_allocator->free(response); + } + ++ ++static void filter_supported_states(uint8_t *v, int size) { ++ static int setup = 0; ++ static uint8_t unsupport_bitmask[8]; ++ if(!setup) { ++ setup = 1; ++ setup_bitmask(unsupport_bitmask, "persist.sys.bt.unsupport.states"); ++ } ++ ++ for(int i=0; ifree(response); + } + ++static void filter_supported_stdfeatures(uint8_t *v) { ++ static int setup = 0; ++ static uint8_t unsupport_bitmask[8]; ++ if(!setup) { ++ setup = 1; ++ setup_bitmask(unsupport_bitmask, "persist.sys.bt.unsupport.stdfeatures"); ++ } ++ ++ for(unsigned i=0; ias_array, stream, + (int)sizeof(bt_device_features_t)); + ++ for (int i = 0; i < ((int)sizeof(bt_device_features_t)); i++) ++ LOG_DEBUG(LOG_TAG, "supported state 0x%x is 0x%x", i, supported_features->as_array[i]); ++ filter_supported_stdfeatures(supported_features->as_array); ++ for (int i = 0; i < ((int)sizeof(bt_device_features_t)); i++) ++ LOG_DEBUG(LOG_TAG, "supported.2 state 0x%x is 0x%x", i, supported_features->as_array[i]); ++ + buffer_allocator->free(response); + } + +-- +2.17.1 + diff --git a/patches/platform_system_bt/0003-Add-persist.sys.phh.disable_a2dp_offload-property-to.patch b/patches/platform_system_bt/0003-Add-persist.sys.phh.disable_a2dp_offload-property-to.patch new file mode 100644 index 0000000..5dc9e17 --- /dev/null +++ b/patches/platform_system_bt/0003-Add-persist.sys.phh.disable_a2dp_offload-property-to.patch @@ -0,0 +1,67 @@ +From eba7ce1a14f942bfdc6ac93eac14188b7b2b91ec Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Mon, 25 May 2020 21:25:12 +0200 +Subject: [PATCH 3/3] Add persist.sys.phh.disable_a2dp_offload property to + force a2dp offload + +--- + btif/src/btif_av.cc | 7 ++++++- + stack/a2dp/a2dp_codec_config.cc | 9 +++++++-- + 2 files changed, 13 insertions(+), 3 deletions(-) + +diff --git a/btif/src/btif_av.cc b/btif/src/btif_av.cc +index 1003b0f82..b7832a478 100644 +--- a/btif/src/btif_av.cc ++++ b/btif/src/btif_av.cc +@@ -968,9 +968,14 @@ bt_status_t BtifAvSource::Init( + osi_property_get("ro.bluetooth.a2dp_offload.supported", value_sup, "false"); + osi_property_get("persist.bluetooth.a2dp_offload.disabled", value_dis, + "false"); ++ char value_phh[PROPERTY_VALUE_MAX] = {'\0'}; ++ osi_property_get("persist.sys.phh.disable_a2dp_offload", value_phh, "false"); + a2dp_offload_enabled_ = + (strcmp(value_sup, "true") == 0) && (strcmp(value_dis, "false") == 0); +- BTIF_TRACE_DEBUG("a2dp_offload.enable = %d", a2dp_offload_enabled_); ++ if(strcmp(value_phh, "true") == 0) ++ a2dp_offload_enabled_ = false; ++ ++ LOG_ERROR(LOG_TAG, "a2dp_offload.enable = %s", a2dp_offload_enabled_ ? "on" : "off"); + + callbacks_ = callbacks; + if (a2dp_offload_enabled_) { +diff --git a/stack/a2dp/a2dp_codec_config.cc b/stack/a2dp/a2dp_codec_config.cc +index edf7e0c46..795cbab9c 100644 +--- a/stack/a2dp/a2dp_codec_config.cc ++++ b/stack/a2dp/a2dp_codec_config.cc +@@ -560,13 +560,18 @@ bool A2dpCodecs::init() { + char* tok = NULL; + char* tmp_token = NULL; + bool offload_codec_support[BTAV_A2DP_CODEC_INDEX_MAX] = {false}; +- char value_sup[PROPERTY_VALUE_MAX], value_dis[PROPERTY_VALUE_MAX]; ++ char value_sup[PROPERTY_VALUE_MAX], value_dis[PROPERTY_VALUE_MAX], value_phh[PROPERTY_VALUE_MAX]; + + osi_property_get("ro.bluetooth.a2dp_offload.supported", value_sup, "false"); + osi_property_get("persist.bluetooth.a2dp_offload.disabled", value_dis, + "false"); ++ osi_property_get("persist.sys.phh.disable_a2dp_offload", value_phh, "false"); + a2dp_offload_status = + (strcmp(value_sup, "true") == 0) && (strcmp(value_dis, "false") == 0); ++ if(strcmp(value_phh, "true") == 0) ++ a2dp_offload_status = false; ++ ++ LOG_ERROR(LOG_TAG, "Got a2dp offload status %s", a2dp_offload_status ? "on" : "off"); + + if (a2dp_offload_status) { + char value_cap[PROPERTY_VALUE_MAX]; +@@ -654,7 +659,7 @@ bool A2dpCodecs::init() { + } + } + +- return (!ordered_source_codecs_.empty() && !ordered_sink_codecs_.empty()); ++ return (!ordered_source_codecs_.empty() && !ordered_sink_codecs_.empty()) && !a2dp_offload_status; + } + + A2dpCodecConfig* A2dpCodecs::findSourceCodecConfig( +-- +2.17.1 + diff --git a/patches/platform_system_core/0001-Detect-allowed-sdcard-options-based-on-vndk.patch b/patches/platform_system_core/0001-Detect-allowed-sdcard-options-based-on-vndk.patch new file mode 100644 index 0000000..587aba2 --- /dev/null +++ b/patches/platform_system_core/0001-Detect-allowed-sdcard-options-based-on-vndk.patch @@ -0,0 +1,29 @@ +From b7ff0d28b15d50e4c3aa6ff636eed67290d20e33 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Tue, 14 Aug 2018 19:33:03 +0200 +Subject: [PATCH 1/7] Detect allowed sdcard options based on vndk + +Some kernel crashes when using too recent sdcardfs options + +Change-Id: I632e485f9b2a09a46d4a1fde2ea15217f8d92eff +--- + sdcard/sdcard.cpp | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/sdcard/sdcard.cpp b/sdcard/sdcard.cpp +index 622de5b7a..955ddbb4c 100644 +--- a/sdcard/sdcard.cpp ++++ b/sdcard/sdcard.cpp +@@ -110,7 +110,8 @@ static bool sdcardfs_setup(const std::string& source_path, const std::string& de + if (unshared_obb) new_opts_list.push_back("unshared_obb,"); + // Try several attempts, each time with one less option, to gracefully + // handle older kernels that aren't updated yet. +- for (int i = 0; i <= new_opts_list.size(); ++i) { ++ int first_option_to_try = property_get_bool("persist.sys.phh.modern_sdcard", false) ? 0 : 2; ++ for (int i = first_option_to_try; i <= new_opts_list.size(); ++i) { + std::string new_opts; + for (int j = 0; j < new_opts_list.size() - i; ++j) { + new_opts += new_opts_list[j]; +-- +2.17.1 + diff --git a/patches/platform_system_core/0002-Ignore-proc-kmsg-if-reading-from-it-faults.patch b/patches/platform_system_core/0002-Ignore-proc-kmsg-if-reading-from-it-faults.patch new file mode 100644 index 0000000..32283e0 --- /dev/null +++ b/patches/platform_system_core/0002-Ignore-proc-kmsg-if-reading-from-it-faults.patch @@ -0,0 +1,33 @@ +From 79508b1e216217f4e9b8a8692049407e30bbb0b6 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Wed, 2 Jan 2019 17:17:20 +0100 +Subject: [PATCH 2/7] Ignore /proc/kmsg if reading from it faults + +On some devices, (The only known one is Y6 2018), reading from +/proc/kmsg fails, with a status EFAULT +This isn't just from logd, a simple cat /proc/kmsg does that as well. + +Simply stop reading logs from kernel in that case + +Change-Id: I4b902b7ec36107c722e2f5cc5dbb7964734bb71d +--- + logd/LogKlog.cpp | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/logd/LogKlog.cpp b/logd/LogKlog.cpp +index edd326aec..8ff719f6e 100644 +--- a/logd/LogKlog.cpp ++++ b/logd/LogKlog.cpp +@@ -238,6 +238,9 @@ bool LogKlog::onDataAvailable(SocketClient* cli) { + break; + } + if (retval < 0) { ++ if(errno == EFAULT) { ++ stopListener(); ++ } + return false; + } + len += retval; +-- +2.17.1 + diff --git a/patches/platform_system_core/0003-first-stage-If-Vboot2-fails-fall-back-to-Vboot1.patch b/patches/platform_system_core/0003-first-stage-If-Vboot2-fails-fall-back-to-Vboot1.patch new file mode 100644 index 0000000..56b3468 --- /dev/null +++ b/patches/platform_system_core/0003-first-stage-If-Vboot2-fails-fall-back-to-Vboot1.patch @@ -0,0 +1,41 @@ +From 87add47acc49be7c6b2eb9802f8d99382409847a Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Thu, 12 Sep 2019 13:05:37 +0200 +Subject: [PATCH 3/7] [first stage] If Vboot2 fails, fall-back to Vboot1 + +Some devices, for instance Honor View 10, running Pie vendor declares +vbmeta in their device-tree, but doesn't have a vbmeta partition. + +Test: without this fix the device reboots into bootloader +Test: with this fix, the device boots. + +Please note that other fixes are also required to get this device to +actually boot. + +Change-Id: I97a7042fb03f817c41b801d558e438d2c1f6c375 +--- + init/first_stage_mount.cpp | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/init/first_stage_mount.cpp b/init/first_stage_mount.cpp +index 8eb2f9748..990d76928 100644 +--- a/init/first_stage_mount.cpp ++++ b/init/first_stage_mount.cpp +@@ -809,7 +809,13 @@ bool DoFirstStageMount() { + LOG(ERROR) << "Failed to create FirstStageMount"; + return false; + } +- return handle->DoFirstStageMount(); ++ if(!handle->DoFirstStageMount()) { ++ handle = nullptr; ++ auto fstab = ReadFirstStageFstab(); ++ std::unique_ptr v = std::make_unique(std::move(fstab)); ++ return v->DoFirstStageMount(); ++ } ++ return true; + } + + void SetInitAvbVersionInRecovery() { +-- +2.17.1 + diff --git a/patches/platform_system_core/0004-Ugly-but-secure-Set-dev-uinput-as-0666-to-fix-finger.patch b/patches/platform_system_core/0004-Ugly-but-secure-Set-dev-uinput-as-0666-to-fix-finger.patch new file mode 100644 index 0000000..d51a74b --- /dev/null +++ b/patches/platform_system_core/0004-Ugly-but-secure-Set-dev-uinput-as-0666-to-fix-finger.patch @@ -0,0 +1,32 @@ +From c45bbb47bdbab406a5e24f82ec76dbe7a44adcca Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Fri, 1 Nov 2019 18:22:13 +0100 +Subject: [PATCH 4/7] Ugly but secure: Set /dev/uinput as 0666 to fix + fingerprint sensor on some devices + +cf https://github.com/phhusson/device_phh_treble/pull/122/commits/e000d69c286b6686777ea6f1867f379e30273e48 +This is safe because even though it's 0666, its SELinux policy is very +tight, and only bluetooth HAL, shell (and fingerprint HAL on Xiaomi) can +access it. + +Change-Id: Id374e781957927d5604cb96c7a39b3fb28b3a6c5 +--- + rootdir/ueventd.rc | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/rootdir/ueventd.rc b/rootdir/ueventd.rc +index 9c2cdf27f..081037d44 100644 +--- a/rootdir/ueventd.rc ++++ b/rootdir/ueventd.rc +@@ -45,7 +45,7 @@ subsystem sound + + # these should not be world writable + /dev/uhid 0660 uhid uhid +-/dev/uinput 0660 uhid uhid ++/dev/uinput 0666 uhid uhid + /dev/rtc0 0640 system system + /dev/tty0 0660 root system + /dev/graphics/* 0660 root graphics +-- +2.17.1 + diff --git a/patches/platform_system_core/0005-Have-sdcard-daemon-detect-sdcardfs-params-based-on-v.patch b/patches/platform_system_core/0005-Have-sdcard-daemon-detect-sdcardfs-params-based-on-v.patch new file mode 100644 index 0000000..30994c1 --- /dev/null +++ b/patches/platform_system_core/0005-Have-sdcard-daemon-detect-sdcardfs-params-based-on-v.patch @@ -0,0 +1,47 @@ +From 12d11df31c36c0d043efb30b8b3010ba816cb9d0 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Fri, 1 Nov 2019 18:22:13 +0100 +Subject: [PATCH 5/7] Have sdcard daemon detect sdcardfs params based on vndk + version + +Change-Id: Id374e781957927d5604cb96c7a39b3fb28b3a6c5 +--- + sdcard/sdcard.cpp | 11 ++++++----- + 1 file changed, 6 insertions(+), 5 deletions(-) + +diff --git a/sdcard/sdcard.cpp b/sdcard/sdcard.cpp +index 955ddbb4c..e580190e9 100644 +--- a/sdcard/sdcard.cpp ++++ b/sdcard/sdcard.cpp +@@ -32,6 +32,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -103,15 +104,15 @@ static bool sdcardfs_setup(const std::string& source_path, const std::string& de + mode_t mask, bool derive_gid, bool default_normal, bool unshared_obb, + bool use_esdfs) { + // Add new options at the end of the vector. ++ int vndk = android::base::GetIntProperty("ro.vndk.version", 29); + std::vector new_opts_list; + if (multi_user) new_opts_list.push_back("multiuser,"); +- if (derive_gid) new_opts_list.push_back("derive_gid,"); +- if (default_normal) new_opts_list.push_back("default_normal,"); +- if (unshared_obb) new_opts_list.push_back("unshared_obb,"); ++ if (derive_gid && vndk >= 27) new_opts_list.push_back("derive_gid,"); ++ if (default_normal && vndk >= 28) new_opts_list.push_back("default_normal,"); ++ if (unshared_obb && vndk >= 29) new_opts_list.push_back("unshared_obb,"); + // Try several attempts, each time with one less option, to gracefully + // handle older kernels that aren't updated yet. +- int first_option_to_try = property_get_bool("persist.sys.phh.modern_sdcard", false) ? 0 : 2; +- for (int i = first_option_to_try; i <= new_opts_list.size(); ++i) { ++ for (int i = 0; i <= new_opts_list.size(); ++i) { + std::string new_opts; + for (int j = 0; j < new_opts_list.size() - i; ++j) { + new_opts += new_opts_list[j]; +-- +2.17.1 + diff --git a/patches/platform_system_core/0006-Revert-Move-adbd-s-legacy-USB-implementation-to-fast.patch b/patches/platform_system_core/0006-Revert-Move-adbd-s-legacy-USB-implementation-to-fast.patch new file mode 100644 index 0000000..7a4b2ad --- /dev/null +++ b/patches/platform_system_core/0006-Revert-Move-adbd-s-legacy-USB-implementation-to-fast.patch @@ -0,0 +1,847 @@ +From 1fc45dd14daaa997ed09f0ceb674a08032186f7b Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Mon, 21 Sep 2020 18:13:18 +0200 +Subject: [PATCH 6/7] Revert "Move adbd's legacy USB implementation to + fastboot." + +This reverts commit 27831220711f5ab4ccb2fca6253786c13245559a. +--- + adb/Android.bp | 17 ++- + adb/adb.cpp | 4 - + adb/adb.h | 3 +- + adb/client/main.cpp | 1 - + adb/client/usb_dispatch.cpp | 3 +- + adb/client/usb_libusb.cpp | 5 +- + adb/client/usb_linux.cpp | 3 +- + adb/client/usb_osx.cpp | 2 - + adb/client/usb_windows.cpp | 2 - + .../device => adb/daemon/include/adbd}/usb.h | 9 +- + adb/daemon/usb.cpp | 30 ++++- + adb/daemon/{usb_ffs.h => usb_dummy.cpp} | 30 ++++- + adb/daemon/usb_ffs.cpp | 3 +- + .../usb.cpp => adb/daemon/usb_legacy.cpp | 111 +++++++++++++++++- + adb/transport.cpp | 2 - + adb/transport.h | 37 +++--- + adb/{client => }/transport_usb.cpp | 8 +- + adb/{client => }/usb.h | 42 +++---- + fastboot/Android.bp | 2 +- + fastboot/device/usb_client.h | 2 +- + 20 files changed, 229 insertions(+), 87 deletions(-) + rename {fastboot/device => adb/daemon/include/adbd}/usb.h (84%) + rename adb/daemon/{usb_ffs.h => usb_dummy.cpp} (52%) + rename fastboot/device/usb.cpp => adb/daemon/usb_legacy.cpp (70%) + rename adb/{client => }/transport_usb.cpp (97%) + rename adb/{client => }/usb.h (72%) + +diff --git a/adb/Android.bp b/adb/Android.bp +index dee48bf80..78c79702a 100644 +--- a/adb/Android.bp ++++ b/adb/Android.bp +@@ -160,6 +160,7 @@ libadb_srcs = [ + "transport.cpp", + "transport_fd.cpp", + "transport_local.cpp", ++ "transport_usb.cpp", + "types.cpp", + ] + +@@ -195,7 +196,6 @@ cc_library_host_static { + "client/usb_libusb.cpp", + "client/usb_dispatch.cpp", + "client/transport_mdns.cpp", +- "client/transport_usb.cpp", + "client/pairing/pairing_client.cpp", + ], + +@@ -375,6 +375,10 @@ cc_library_static { + "daemon/adb_wifi.cpp", + ], + ++ local_include_dirs: [ ++ "daemon/include", ++ ], ++ + generated_headers: ["platform_tools_version"], + + static_libs: [ +@@ -405,6 +409,12 @@ cc_library_static { + "daemon/transport_qemu.cpp", + "daemon/usb.cpp", + "daemon/usb_ffs.cpp", ++ "daemon/usb_legacy.cpp", ++ ] ++ }, ++ linux_glibc: { ++ srcs: [ ++ "daemon/usb_dummy.cpp", + ] + }, + recovery: { +@@ -547,9 +557,8 @@ cc_library { + "libmdnssd", + ], + +- visibility: [ +- "//bootable/recovery/minadbd", +- "//system/core/adb", ++ export_include_dirs: [ ++ "daemon/include", + ], + } + +diff --git a/adb/adb.cpp b/adb/adb.cpp +index c3e9731a3..efc1b853f 100644 +--- a/adb/adb.cpp ++++ b/adb/adb.cpp +@@ -66,10 +66,6 @@ using namespace std::chrono_literals; + #include "daemon/logging.h" + #endif + +-#if ADB_HOST +-#include "client/usb.h" +-#endif +- + std::string adb_version() { + // Don't change the format of this --- it's parsed by ddmlib. + return android::base::StringPrintf( +diff --git a/adb/adb.h b/adb/adb.h +index ce12a55f9..86d205c98 100644 +--- a/adb/adb.h ++++ b/adb/adb.h +@@ -29,6 +29,7 @@ + #include "fdevent/fdevent.h" + #include "socket.h" + #include "types.h" ++#include "usb.h" + + constexpr size_t MAX_PAYLOAD_V1 = 4 * 1024; + constexpr size_t MAX_PAYLOAD = 1024 * 1024; +@@ -138,6 +139,7 @@ int adb_server_main(int is_daemon, const std::string& socket_spec, int ack_reply + + /* initialize a transport object's func pointers and state */ + int init_socket_transport(atransport* t, unique_fd s, int port, int local); ++void init_usb_transport(atransport* t, usb_handle* usb); + + std::string getEmulatorSerialString(int console_port); + #if ADB_HOST +@@ -250,5 +252,4 @@ void update_transport_status(); + // Wait until device scan has completed and every transport is ready, or a timeout elapses. + void adb_wait_for_device_initialization(); + +-void usb_init(); + #endif +diff --git a/adb/client/main.cpp b/adb/client/main.cpp +index 78f7b8f20..a85a18c4e 100644 +--- a/adb/client/main.cpp ++++ b/adb/client/main.cpp +@@ -36,7 +36,6 @@ + #include "adb_listeners.h" + #include "adb_utils.h" + #include "adb_wifi.h" +-#include "client/usb.h" + #include "commandline.h" + #include "sysdeps/chrono.h" + #include "transport.h" +diff --git a/adb/client/usb_dispatch.cpp b/adb/client/usb_dispatch.cpp +index 7b97117de..f55ae9010 100644 +--- a/adb/client/usb_dispatch.cpp ++++ b/adb/client/usb_dispatch.cpp +@@ -15,8 +15,7 @@ + */ + + #include +- +-#include "client/usb.h" ++#include "usb.h" + + void usb_init() { + if (should_use_libusb()) { +diff --git a/adb/client/usb_libusb.cpp b/adb/client/usb_libusb.cpp +index 07cbc9418..53f01a0ce 100644 +--- a/adb/client/usb_libusb.cpp ++++ b/adb/client/usb_libusb.cpp +@@ -14,9 +14,9 @@ + * limitations under the License. + */ + +-#include "sysdeps.h" ++#include "usb.h" + +-#include "client/usb.h" ++#include "sysdeps.h" + + #include + #include +@@ -40,6 +40,7 @@ + #include "adb.h" + #include "adb_utils.h" + #include "transport.h" ++#include "usb.h" + + using android::base::StringPrintf; + +diff --git a/adb/client/usb_linux.cpp b/adb/client/usb_linux.cpp +index 95b1817dc..343e7b59a 100644 +--- a/adb/client/usb_linux.cpp ++++ b/adb/client/usb_linux.cpp +@@ -18,8 +18,6 @@ + + #include "sysdeps.h" + +-#include "client/usb.h" +- + #include + #include + #include +@@ -50,6 +48,7 @@ + + #include "adb.h" + #include "transport.h" ++#include "usb.h" + + using namespace std::chrono_literals; + using namespace std::literals; +diff --git a/adb/client/usb_osx.cpp b/adb/client/usb_osx.cpp +index a93fa3a6b..7207ca733 100644 +--- a/adb/client/usb_osx.cpp ++++ b/adb/client/usb_osx.cpp +@@ -18,8 +18,6 @@ + + #include "sysdeps.h" + +-#include "client/usb.h" +- + #include + + #include +diff --git a/adb/client/usb_windows.cpp b/adb/client/usb_windows.cpp +index e209230c7..197c6fa5c 100644 +--- a/adb/client/usb_windows.cpp ++++ b/adb/client/usb_windows.cpp +@@ -18,8 +18,6 @@ + + #include "sysdeps.h" + +-#include "client/usb.h" +- + // clang-format off + #include // winsock.h *must* be included before windows.h. + #include +diff --git a/fastboot/device/usb.h b/adb/daemon/include/adbd/usb.h +similarity index 84% +rename from fastboot/device/usb.h +rename to adb/daemon/include/adbd/usb.h +index 6c3f54252..2204246c1 100644 +--- a/fastboot/device/usb.h ++++ b/adb/daemon/include/adbd/usb.h +@@ -36,14 +36,17 @@ struct aio_block { + }; + + struct usb_handle { +- usb_handle() {} ++ usb_handle() : kicked(false) { ++ } + + std::condition_variable notify; + std::mutex lock; ++ std::atomic kicked; + bool open_new_connection = true; + + int (*write)(usb_handle* h, const void* data, int len); + int (*read)(usb_handle* h, void* data, int len, bool allow_partial); ++ void (*kick)(usb_handle* h); + void (*close)(usb_handle* h); + + // FunctionFS +@@ -60,4 +63,6 @@ struct usb_handle { + size_t io_size; + }; + +-usb_handle* create_usb_handle(unsigned num_bufs, unsigned io_size); ++usb_handle *create_usb_handle(unsigned num_bufs, unsigned io_size); ++bool open_functionfs(android::base::unique_fd* control, android::base::unique_fd* bulk_out, ++ android::base::unique_fd* bulk_in); +diff --git a/adb/daemon/usb.cpp b/adb/daemon/usb.cpp +index a66387193..b908094d3 100644 +--- a/adb/daemon/usb.cpp ++++ b/adb/daemon/usb.cpp +@@ -45,15 +45,19 @@ + #include + #include + ++#include ++ + #include "adb_unique_fd.h" + #include "adb_utils.h" +-#include "daemon/usb_ffs.h" + #include "sysdeps/chrono.h" + #include "transport.h" + #include "types.h" + + using android::base::StringPrintf; + ++// We can't find out whether we have support for AIO on ffs endpoints until we submit a read. ++static std::optional gFfsAioSupported; ++ + // Not all USB controllers support operations larger than 16k, so don't go above that. + // Also, each submitted operation does an allocation in the kernel of that size, so we want to + // minimize our queue depth while still maintaining a deep enough queue to keep the USB stack fed. +@@ -618,10 +622,17 @@ struct UsbFfsConnection : public Connection { + block->pending = true; + struct iocb* iocb = &block->control; + if (io_submit(aio_context_.get(), 1, &iocb) != 1) { ++ if (errno == EINVAL && !gFfsAioSupported.has_value()) { ++ HandleError("failed to submit first read, AIO on FFS not supported"); ++ gFfsAioSupported = false; ++ return false; ++ } ++ + HandleError(StringPrintf("failed to submit read: %s", strerror(errno))); + return false; + } + ++ gFfsAioSupported = true; + return true; + } + +@@ -738,10 +749,17 @@ struct UsbFfsConnection : public Connection { + static constexpr int kInterruptionSignal = SIGUSR1; + }; + ++void usb_init_legacy(); ++ + static void usb_ffs_open_thread() { + adb_thread_setname("usb ffs open"); + + while (true) { ++ if (gFfsAioSupported.has_value() && !gFfsAioSupported.value()) { ++ LOG(INFO) << "failed to use nonblocking ffs, falling back to legacy"; ++ return usb_init_legacy(); ++ } ++ + unique_fd control; + unique_fd bulk_out; + unique_fd bulk_in; +@@ -763,5 +781,13 @@ static void usb_ffs_open_thread() { + } + + void usb_init() { +- std::thread(usb_ffs_open_thread).detach(); ++ bool use_nonblocking = android::base::GetBoolProperty( ++ "persist.adb.nonblocking_ffs", ++ android::base::GetBoolProperty("ro.adb.nonblocking_ffs", true)); ++ ++ if (use_nonblocking) { ++ std::thread(usb_ffs_open_thread).detach(); ++ } else { ++ usb_init_legacy(); ++ } + } +diff --git a/adb/daemon/usb_ffs.h b/adb/daemon/usb_dummy.cpp +similarity index 52% +rename from adb/daemon/usb_ffs.h +rename to adb/daemon/usb_dummy.cpp +index a19d7ccce..c9bf79731 100644 +--- a/adb/daemon/usb_ffs.h ++++ b/adb/daemon/usb_dummy.cpp +@@ -1,5 +1,5 @@ + /* +- * Copyright (C) 2020 The Android Open Source Project ++ * Copyright (C) 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. +@@ -14,9 +14,29 @@ + * limitations under the License. + */ + +-#pragma once ++#include + +-#include ++#include + +-bool open_functionfs(android::base::unique_fd* control, android::base::unique_fd* bulk_out, +- android::base::unique_fd* bulk_in); ++int usb_write(usb_handle*, const void*, int) { ++ LOG(FATAL) << "unimplemented"; ++ return -1; ++} ++ ++int usb_read(usb_handle*, void*, int) { ++ LOG(FATAL) << "unimplemented"; ++ return -1; ++} ++ ++int usb_close(usb_handle*) { ++ LOG(FATAL) << "unimplemented"; ++ return -1; ++} ++ ++void usb_reset(usb_handle*) { ++ LOG(FATAL) << "unimplemented"; ++} ++ ++void usb_kick(usb_handle*) { ++ LOG(FATAL) << "unimplemented"; ++} +diff --git a/adb/daemon/usb_ffs.cpp b/adb/daemon/usb_ffs.cpp +index e538ca885..b19fa5d58 100644 +--- a/adb/daemon/usb_ffs.cpp ++++ b/adb/daemon/usb_ffs.cpp +@@ -18,8 +18,6 @@ + + #include "sysdeps.h" + +-#include "daemon/usb_ffs.h" +- + #include + #include + +@@ -28,6 +26,7 @@ + #include + + #include "adb.h" ++#include "adbd/usb.h" + + #define MAX_PACKET_SIZE_FS 64 + #define MAX_PACKET_SIZE_HS 512 +diff --git a/fastboot/device/usb.cpp b/adb/daemon/usb_legacy.cpp +similarity index 70% +rename from fastboot/device/usb.cpp +rename to adb/daemon/usb_legacy.cpp +index 4bee7b20c..fe80e7d59 100644 +--- a/fastboot/device/usb.cpp ++++ b/adb/daemon/usb_legacy.cpp +@@ -14,7 +14,9 @@ + * limitations under the License. + */ + +-#include "usb.h" ++#define TRACE_TAG USB ++ ++#include "sysdeps.h" + + #include + #include +@@ -39,9 +41,12 @@ + #include + #include + ++#include "adb.h" ++#include "adbd/usb.h" ++#include "transport.h" ++ + using namespace std::chrono_literals; + +-#define D(...) + #define MAX_PACKET_SIZE_FS 64 + #define MAX_PACKET_SIZE_HS 512 + #define MAX_PACKET_SIZE_SS 1024 +@@ -51,6 +56,8 @@ using namespace std::chrono_literals; + // Number of buffers needed to fit MAX_PAYLOAD, with an extra for ZLPs. + #define USB_FFS_NUM_BUFS ((4 * MAX_PAYLOAD / USB_FFS_BULK_SIZE) + 1) + ++static unique_fd& dummy_fd = *new unique_fd(); ++ + static void aio_block_init(aio_block* aiob, unsigned num_bufs) { + aiob->iocb.resize(num_bufs); + aiob->iocbs.resize(num_bufs); +@@ -75,6 +82,46 @@ static int getMaxPacketSize(int ffs_fd) { + } + } + ++static bool init_functionfs(struct usb_handle* h) { ++ LOG(INFO) << "initializing functionfs"; ++ if (!open_functionfs(&h->control, &h->bulk_out, &h->bulk_in)) { ++ return false; ++ } ++ ++ h->read_aiob.fd = h->bulk_out.get(); ++ h->write_aiob.fd = h->bulk_in.get(); ++ h->reads_zero_packets = true; ++ return true; ++} ++ ++static void usb_legacy_ffs_open_thread(usb_handle* usb) { ++ adb_thread_setname("usb legacy ffs open"); ++ ++ while (true) { ++ // wait until the USB device needs opening ++ std::unique_lock lock(usb->lock); ++ while (!usb->open_new_connection) { ++ usb->notify.wait(lock); ++ } ++ usb->open_new_connection = false; ++ lock.unlock(); ++ ++ while (true) { ++ if (init_functionfs(usb)) { ++ LOG(INFO) << "functionfs successfully initialized"; ++ break; ++ } ++ std::this_thread::sleep_for(1s); ++ } ++ ++ LOG(INFO) << "registering usb transport"; ++ register_usb_transport(usb, nullptr, nullptr, 1); ++ } ++ ++ // never gets here ++ abort(); ++} ++ + static int usb_ffs_write(usb_handle* h, const void* data, int len) { + D("about to write (fd=%d, len=%d)", h->bulk_in.get(), len); + +@@ -82,7 +129,7 @@ static int usb_ffs_write(usb_handle* h, const void* data, int len) { + int orig_len = len; + while (len > 0) { + int write_len = std::min(USB_FFS_BULK_SIZE, len); +- int n = write(h->bulk_in, buf, write_len); ++ int n = adb_write(h->bulk_in, buf, write_len); + if (n < 0) { + D("ERROR: fd = %d, n = %d: %s", h->bulk_in.get(), n, strerror(errno)); + return -1; +@@ -103,7 +150,7 @@ static int usb_ffs_read(usb_handle* h, void* data, int len, bool allow_partial) + unsigned count = 0; + while (len > 0) { + int read_len = std::min(USB_FFS_BULK_SIZE, len); +- int n = read(h->bulk_out, buf, read_len); ++ int n = adb_read(h->bulk_out, buf, read_len); + if (n < 0) { + D("ERROR: fd = %d, n = %d: %s", h->bulk_out.get(), n, strerror(errno)); + return -1; +@@ -185,7 +232,7 @@ static int usb_ffs_do_aio(usb_handle* h, const void* data, int len, bool read) { + } + } + +-static int usb_ffs_aio_read(usb_handle* h, void* data, int len, bool /* allow_partial */) { ++static int usb_ffs_aio_read(usb_handle* h, void* data, int len, bool allow_partial) { + return usb_ffs_do_aio(h, data, len, true); + } + +@@ -193,9 +240,32 @@ static int usb_ffs_aio_write(usb_handle* h, const void* data, int len) { + return usb_ffs_do_aio(h, data, len, false); + } + ++static void usb_ffs_kick(usb_handle* h) { ++ int err; ++ ++ err = ioctl(h->bulk_in.get(), FUNCTIONFS_CLEAR_HALT); ++ if (err < 0) { ++ D("[ kick: source (fd=%d) clear halt failed (%d) ]", h->bulk_in.get(), errno); ++ } ++ ++ err = ioctl(h->bulk_out.get(), FUNCTIONFS_CLEAR_HALT); ++ if (err < 0) { ++ D("[ kick: sink (fd=%d) clear halt failed (%d) ]", h->bulk_out.get(), errno); ++ } ++ ++ // don't close ep0 here, since we may not need to reinitialize it with ++ // the same descriptors again. if however ep1/ep2 fail to re-open in ++ // init_functionfs, only then would we close and open ep0 again. ++ // Ditto the comment in usb_adb_kick. ++ h->kicked = true; ++ TEMP_FAILURE_RETRY(dup2(dummy_fd.get(), h->bulk_out.get())); ++ TEMP_FAILURE_RETRY(dup2(dummy_fd.get(), h->bulk_in.get())); ++} ++ + static void usb_ffs_close(usb_handle* h) { + LOG(INFO) << "closing functionfs transport"; + ++ h->kicked = false; + h->bulk_out.reset(); + h->bulk_in.reset(); + +@@ -221,6 +291,37 @@ usb_handle* create_usb_handle(unsigned num_bufs, unsigned io_size) { + aio_block_init(&h->write_aiob, num_bufs); + } + h->io_size = io_size; ++ h->kick = usb_ffs_kick; + h->close = usb_ffs_close; + return h; + } ++ ++void usb_init_legacy() { ++ D("[ usb_init - using legacy FunctionFS ]"); ++ dummy_fd.reset(adb_open("/dev/null", O_WRONLY | O_CLOEXEC)); ++ CHECK_NE(-1, dummy_fd.get()); ++ ++ std::thread(usb_legacy_ffs_open_thread, create_usb_handle(USB_FFS_NUM_BUFS, USB_FFS_BULK_SIZE)) ++ .detach(); ++} ++ ++int usb_write(usb_handle* h, const void* data, int len) { ++ return h->write(h, data, len); ++} ++ ++int usb_read(usb_handle* h, void* data, int len) { ++ return h->read(h, data, len, false /* allow_partial */); ++} ++ ++int usb_close(usb_handle* h) { ++ h->close(h); ++ return 0; ++} ++ ++void usb_reset(usb_handle* h) { ++ usb_close(h); ++} ++ ++void usb_kick(usb_handle* h) { ++ h->kick(h); ++} +diff --git a/adb/transport.cpp b/adb/transport.cpp +index fe286dee4..f9d7aca53 100644 +--- a/adb/transport.cpp ++++ b/adb/transport.cpp +@@ -1453,7 +1453,6 @@ void kick_all_tcp_devices() { + + #endif + +-#if ADB_HOST + void register_usb_transport(usb_handle* usb, const char* serial, const char* devpath, + unsigned writeable) { + atransport* t = new atransport(writeable ? kCsOffline : kCsNoPerm); +@@ -1475,7 +1474,6 @@ void register_usb_transport(usb_handle* usb, const char* serial, const char* dev + + register_transport(t); + } +-#endif + + #if ADB_HOST + // This should only be used for transports with connection_state == kCsNoPerm. +diff --git a/adb/transport.h b/adb/transport.h +index 26d804b3f..895f63ee6 100644 +--- a/adb/transport.h ++++ b/adb/transport.h +@@ -40,6 +40,7 @@ + #include "adb.h" + #include "adb_unique_fd.h" + #include "types.h" ++#include "usb.h" + + typedef std::unordered_set FeatureSet; + +@@ -202,6 +203,20 @@ struct FdConnection : public BlockingConnection { + std::unique_ptr tls_; + }; + ++struct UsbConnection : public BlockingConnection { ++ explicit UsbConnection(usb_handle* handle) : handle_(handle) {} ++ ~UsbConnection(); ++ ++ bool Read(apacket* packet) override final; ++ bool Write(apacket* packet) override final; ++ bool DoTlsHandshake(RSA* key, std::string* auth_key) override final; ++ ++ void Close() override final; ++ virtual void Reset() override final; ++ ++ usb_handle* handle_; ++}; ++ + // Waits for a transport's connection to be not pending. This is a separate + // object so that the transport can be destroyed and another thread can be + // notified of it in a race-free way. +@@ -237,10 +252,6 @@ enum class ReconnectResult { + Abort, + }; + +-#if ADB_HOST +-struct usb_handle; +-#endif +- + class atransport : public enable_weak_from_this { + public: + // TODO(danalbert): We expose waaaaaaay too much stuff because this was +@@ -281,10 +292,8 @@ class atransport : public enable_weak_from_this { + return connection_; + } + +-#if ADB_HOST + void SetUsbHandle(usb_handle* h) { usb_handle_ = h; } + usb_handle* GetUsbHandle() { return usb_handle_; } +-#endif + + const TransportId id; + +@@ -391,10 +400,8 @@ class atransport : public enable_weak_from_this { + // The underlying connection object. + std::shared_ptr connection_ GUARDED_BY(mutex_); + +-#if ADB_HOST + // USB handle for the connection, if available. + usb_handle* usb_handle_ = nullptr; +-#endif + + // A callback that will be invoked when the atransport needs to reconnect. + ReconnectCallback reconnect_; +@@ -435,15 +442,8 @@ void kick_all_transports_by_auth_key(std::string_view auth_key); + #endif + + void register_transport(atransport* transport); +- +-#if ADB_HOST +-void init_usb_transport(atransport* t, usb_handle* usb); +-void register_usb_transport(usb_handle* h, const char* serial, const char* devpath, +- unsigned writeable); +- +-// This should only be used for transports with connection_state == kCsNoPerm. +-void unregister_usb_transport(usb_handle* usb); +-#endif ++void register_usb_transport(usb_handle* h, const char* serial, ++ const char* devpath, unsigned writeable); + + /* Connect to a network address and register it as a device */ + void connect_device(const std::string& address, std::string* response); +@@ -453,6 +453,9 @@ bool register_socket_transport(unique_fd s, std::string serial, int port, int lo + atransport::ReconnectCallback reconnect, bool use_tls, + int* error = nullptr); + ++// This should only be used for transports with connection_state == kCsNoPerm. ++void unregister_usb_transport(usb_handle* usb); ++ + bool check_header(apacket* p, atransport* t); + + void close_usb_devices(bool reset = false); +diff --git a/adb/client/transport_usb.cpp b/adb/transport_usb.cpp +similarity index 97% +rename from adb/client/transport_usb.cpp +rename to adb/transport_usb.cpp +index 777edde0b..fb81b37e0 100644 +--- a/adb/client/transport_usb.cpp ++++ b/adb/transport_usb.cpp +@@ -16,10 +16,6 @@ + + #define TRACE_TAG TRANSPORT + +-#include "sysdeps.h" +- +-#include "client/usb.h" +- + #include + + #include "sysdeps.h" +@@ -139,8 +135,8 @@ static int remote_read(apacket* p, usb_handle* usb) { + } + + p->payload.resize(p->msg.data_length); +- if (usb_read(usb, &p->payload[0], p->payload.size()) != +- static_cast(p->payload.size())) { ++ if (usb_read(usb, &p->payload[0], p->payload.size()) ++ != static_cast(p->payload.size())) { + PLOG(ERROR) << "remote usb: terminated (data)"; + return -1; + } +diff --git a/adb/client/usb.h b/adb/usb.h +similarity index 72% +rename from adb/client/usb.h +rename to adb/usb.h +index b371788cc..eb8ca6cc0 100644 +--- a/adb/client/usb.h ++++ b/adb/usb.h +@@ -18,9 +18,6 @@ + + #include + +-#include "adb.h" +-#include "transport.h" +- + // USB host/client interface. + + #define ADB_USB_INTERFACE(handle_ref_type) \ +@@ -33,38 +30,35 @@ + void usb_kick(handle_ref_type h); \ + size_t usb_get_max_packet_size(handle_ref_type) + ++#if !ADB_HOST ++// The daemon has a single implementation. ++ ++struct usb_handle; ++ADB_USB_INTERFACE(usb_handle*); ++ ++#else // linux host || darwin + // Linux and Darwin clients have native and libusb implementations. + + namespace libusb { +-struct usb_handle; +-ADB_USB_INTERFACE(libusb::usb_handle*); +-} // namespace libusb ++ struct usb_handle; ++ ADB_USB_INTERFACE(libusb::usb_handle*); ++} + + namespace native { +-struct usb_handle; +-ADB_USB_INTERFACE(native::usb_handle*); +-} // namespace native ++ struct usb_handle; ++ ADB_USB_INTERFACE(native::usb_handle*); ++} + + // Empty base that both implementations' opaque handles inherit from. +-struct usb_handle {}; ++struct usb_handle { ++}; + + ADB_USB_INTERFACE(::usb_handle*); + ++#endif // linux host || darwin ++ ++ + // USB device detection. + int is_adb_interface(int usb_class, int usb_subclass, int usb_protocol); + + bool should_use_libusb(); +- +-struct UsbConnection : public BlockingConnection { +- explicit UsbConnection(usb_handle* handle) : handle_(handle) {} +- ~UsbConnection(); +- +- bool Read(apacket* packet) override final; +- bool Write(apacket* packet) override final; +- bool DoTlsHandshake(RSA* key, std::string* auth_key) override final; +- +- void Close() override final; +- virtual void Reset() override final; +- +- usb_handle* handle_; +-}; +diff --git a/fastboot/Android.bp b/fastboot/Android.bp +index cf0f1ac93..aace6a6cb 100644 +--- a/fastboot/Android.bp ++++ b/fastboot/Android.bp +@@ -115,7 +115,6 @@ cc_binary { + "device/fastboot_device.cpp", + "device/flashing.cpp", + "device/main.cpp", +- "device/usb.cpp", + "device/usb_client.cpp", + "device/tcp_client.cpp", + "device/utility.cpp", +@@ -128,6 +127,7 @@ cc_binary { + "android.hardware.boot@1.1", + "android.hardware.fastboot@1.0", + "android.hardware.health@2.0", ++ "libadbd", + "libasyncio", + "libbase", + "libbootloader_message", +diff --git a/fastboot/device/usb_client.h b/fastboot/device/usb_client.h +index e702a0d22..e6a1a8b8c 100644 +--- a/fastboot/device/usb_client.h ++++ b/fastboot/device/usb_client.h +@@ -17,7 +17,7 @@ + + #include + +-#include "usb.h" ++#include + + #include "transport.h" + +-- +2.17.1 + diff --git a/patches/platform_system_core/0007-Enable-PASSCRED-incoditionnally.-This-is-needed-on-A.patch b/patches/platform_system_core/0007-Enable-PASSCRED-incoditionnally.-This-is-needed-on-A.patch new file mode 100644 index 0000000..cced693 --- /dev/null +++ b/patches/platform_system_core/0007-Enable-PASSCRED-incoditionnally.-This-is-needed-on-A.patch @@ -0,0 +1,32 @@ +From 69b547930f6484cd8b0117b445b4468b9035655b Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Mon, 28 Sep 2020 21:41:34 +0200 +Subject: [PATCH 7/7] Enable PASSCRED incoditionnally. This is needed on A-only + devices that cant set passcred on socket + +Change-Id: I74168aa94d91fd91e1baa321582a2b2893efa3af +--- + logd/LogListener.cpp | 7 ++----- + 1 file changed, 2 insertions(+), 5 deletions(-) + +diff --git a/logd/LogListener.cpp b/logd/LogListener.cpp +index ba610424f..4f1df46fa 100644 +--- a/logd/LogListener.cpp ++++ b/logd/LogListener.cpp +@@ -121,11 +121,8 @@ int LogListener::getLogSocket() { + if (sock < 0) { // logd started up in init.sh + sock = socket_local_server( + socketName, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_DGRAM); +- +- int on = 1; +- if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) { +- return -1; +- } + } ++ int on = 1; ++ setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); + return sock; + } +-- +2.17.1 + diff --git a/patches/platform_system_extras/0001-libfscrypt-Bail-out-if-we-can-t-open-directory.patch b/patches/platform_system_extras/0001-libfscrypt-Bail-out-if-we-can-t-open-directory.patch new file mode 100644 index 0000000..a11a8d4 --- /dev/null +++ b/patches/platform_system_extras/0001-libfscrypt-Bail-out-if-we-can-t-open-directory.patch @@ -0,0 +1,35 @@ +From 286db70cc6de5cf8f8642cdfb27ae76922f97abb Mon Sep 17 00:00:00 2001 +From: Luca Stefani +Date: Wed, 9 Sep 2020 12:43:54 +0200 +Subject: [PATCH] libfscrypt: Bail out if we can't open directory + +* On QCOM Q vendor init.qcom.rc has a 'mkdir' entry + for /data/system +* While the encryption policy matches and is applied properly in + permissive, vendor_init doesn't have enough perms to open + and run ioctl over system_data_file to check its policy +* Instead of opening the possiblity to vendors, just + bail out if we fail to open the directory + +Test: m, boot r-gsi on Zenfone 6 +Change-Id: I5ea37019221cd0887b8a5d7454f5a42ac01335c7 +--- + libfscrypt/fscrypt.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/libfscrypt/fscrypt.cpp b/libfscrypt/fscrypt.cpp +index a52ed90c..435de51d 100644 +--- a/libfscrypt/fscrypt.cpp ++++ b/libfscrypt/fscrypt.cpp +@@ -323,7 +323,7 @@ bool EnsurePolicy(const EncryptionPolicy& policy, const std::string& directory) + android::base::unique_fd fd(open(directory.c_str(), O_DIRECTORY | O_NOFOLLOW | O_CLOEXEC)); + if (fd == -1) { + PLOG(ERROR) << "Failed to open directory " << directory; +- return false; ++ return true; + } + + bool already_encrypted = fscrypt_is_encrypted(fd); +-- +2.17.1 + diff --git a/patches/platform_system_linkerconfig/0001-Disable-vndk.lite.patch b/patches/platform_system_linkerconfig/0001-Disable-vndk.lite.patch new file mode 100644 index 0000000..ef8a628 --- /dev/null +++ b/patches/platform_system_linkerconfig/0001-Disable-vndk.lite.patch @@ -0,0 +1,33 @@ +From f287850503008d796b7873e5fc0b06981c2318fe Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Sun, 20 Sep 2020 20:53:14 +0200 +Subject: [PATCH 1/2] Disable vndk.lite + +Change-Id: I129bbee49f6c9b901ca4f5cf55dae2ec36bea107 +--- + modules/environment.cc | 6 ++---- + 1 file changed, 2 insertions(+), 4 deletions(-) + +diff --git a/modules/environment.cc b/modules/environment.cc +index e63be71..bc2630b 100644 +--- a/modules/environment.cc ++++ b/modules/environment.cc +@@ -24,13 +24,11 @@ namespace android { + namespace linkerconfig { + namespace modules { + bool IsLegacyDevice() { +- return (!Variables::GetValue("ro.vndk.version").has_value() && +- !Variables::GetValue("ro.vndk.lite").has_value()) || +- Variables::GetValue("ro.treble.enabled") == "false"; ++ return false; + } + + bool IsVndkLiteDevice() { +- return Variables::GetValue("ro.vndk.lite").value_or("") == "true"; ++ return false; + } + + bool IsVndkInSystemNamespace() { +-- +2.17.1 + diff --git a/patches/platform_system_linkerconfig/0002-Add-special-handling-of-vndk-26-remove-liblog.so-fro.patch b/patches/platform_system_linkerconfig/0002-Add-special-handling-of-vndk-26-remove-liblog.so-fro.patch new file mode 100644 index 0000000..31e5a1f --- /dev/null +++ b/patches/platform_system_linkerconfig/0002-Add-special-handling-of-vndk-26-remove-liblog.so-fro.patch @@ -0,0 +1,81 @@ +From d4880eec8fa830f6c8fe68fe1ebbb05adc5bbbb1 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Mon, 28 Sep 2020 21:02:19 +0200 +Subject: [PATCH 2/2] Add special handling of vndk 26 (remove liblog.so from + llndk, it is provided by vndk, and allow linking against libnativeloader) + +Change-Id: I29b0bb6087ba58f69ee6406e003513bceb6785d8 +--- + contents/namespace/vendordefault.cc | 16 ++++++++++++++-- + contents/namespace/vndk.cc | 5 +++++ + 2 files changed, 19 insertions(+), 2 deletions(-) + +diff --git a/contents/namespace/vendordefault.cc b/contents/namespace/vendordefault.cc +index 6d56904..c93c489 100644 +--- a/contents/namespace/vendordefault.cc ++++ b/contents/namespace/vendordefault.cc +@@ -17,6 +17,7 @@ + // This is the default linker namespace for a vendor process (a process started + // from /vendor/bin/*). + ++#include + #include "linkerconfig/namespacebuilder.h" + + #include "linkerconfig/common.h" +@@ -92,7 +93,7 @@ Namespace BuildVendorDefaultNamespace([[maybe_unused]] const Context& ctx) { + AsanPath::SAME_PATH); + } + +- if (ctx.IsDefaultConfig() && GetVendorVndkVersion() == "27") { ++ if (ctx.IsDefaultConfig() && (GetVendorVndkVersion() == "27" || GetVendorVndkVersion()== "26")) { + ns.AddSearchPath("/vendor/${LIB}/hw", AsanPath::WITH_DATA_ASAN); + ns.AddSearchPath("/vendor/${LIB}/egl", AsanPath::WITH_DATA_ASAN); + } +@@ -107,9 +108,20 @@ Namespace BuildVendorDefaultNamespace([[maybe_unused]] const Context& ctx) { + ns.AddRequires(kVndkLiteVendorRequires); + ns.AddProvides(GetSystemStubLibraries()); + } else { ++ auto llndk = Var("LLNDK_LIBRARIES_VENDOR"); ++ std::cerr << "handing llndk for default vendor namespace" << std::endl; ++ if(GetVendorVndkVersion()== "26") { ++ std::cerr << "vndk 26" << std::endl; ++ std::string lookFor = ":liblog.so"; ++ std::cerr << "Before " << llndk << std::endl; ++ ++ llndk = llndk.replace(llndk.find(lookFor), lookFor.length(), ""); ++ std::cerr << "After " << llndk << std::endl; ++ } ++ + ns.GetLink(ctx.GetSystemNamespaceName()) + .AddSharedLib( +- {Var("LLNDK_LIBRARIES_VENDOR"), Var("SANITIZER_DEFAULT_VENDOR")}); ++ {llndk, Var("SANITIZER_DEFAULT_VENDOR")}); + ns.GetLink("vndk").AddSharedLib({Var("VNDK_SAMEPROCESS_LIBRARIES_VENDOR"), + Var("VNDK_CORE_LIBRARIES_VENDOR")}); + if (android::linkerconfig::modules::IsVndkInSystemNamespace()) { +diff --git a/contents/namespace/vndk.cc b/contents/namespace/vndk.cc +index a95db80..589f745 100644 +--- a/contents/namespace/vndk.cc ++++ b/contents/namespace/vndk.cc +@@ -17,6 +17,7 @@ + // This namespace is exclusively for vndk-sp libs. + + #include "linkerconfig/environment.h" ++using android::linkerconfig::modules::GetVendorVndkVersion; + #include "linkerconfig/namespacebuilder.h" + + using android::linkerconfig::modules::AsanPath; +@@ -121,6 +122,10 @@ Namespace BuildVndkNamespace([[maybe_unused]] const Context& ctx, + + ns.AddRequires(std::vector{"libneuralnetworks.so"}); + ++ if(GetVendorVndkVersion() == "26" || GetVendorVndkVersion() == "27" ) { ++ ns.GetLink("com_android_art").AddSharedLib("libnativeloader.so"); ++ } ++ + return ns; + } + } // namespace contents +-- +2.17.1 + diff --git a/patches/platform_system_memory_lmkd/0001-Enable-passcred-useful-on-A-only-when-init-cant-do-i.patch b/patches/platform_system_memory_lmkd/0001-Enable-passcred-useful-on-A-only-when-init-cant-do-i.patch new file mode 100644 index 0000000..d8fe69d --- /dev/null +++ b/patches/platform_system_memory_lmkd/0001-Enable-passcred-useful-on-A-only-when-init-cant-do-i.patch @@ -0,0 +1,28 @@ +From 791aba3f1be4cf9ba56a94d1744cc308ac439a2b Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Mon, 28 Sep 2020 22:03:00 +0200 +Subject: [PATCH] Enable passcred (useful on A-only when init cant do it for + us) + +Change-Id: I0984518531011276acf21c4f226dea35a7f9373f +--- + lmkd.cpp | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/lmkd.cpp b/lmkd.cpp +index 882ae4a..2f1c649 100644 +--- a/lmkd.cpp ++++ b/lmkd.cpp +@@ -2924,6 +2924,9 @@ static int init(void) { + return -1; + } + ++ int on = 1; ++ setsockopt(ctrl_sock.sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); ++ + ret = listen(ctrl_sock.sock, MAX_DATA_CONN); + if (ret < 0) { + ALOGE("lmkd control socket listen failed (errno=%d)", errno); +-- +2.17.1 + diff --git a/patches/platform_system_vold/0001-Allow-deletion-of-symlink.patch b/patches/platform_system_vold/0001-Allow-deletion-of-symlink.patch new file mode 100644 index 0000000..842d287 --- /dev/null +++ b/patches/platform_system_vold/0001-Allow-deletion-of-symlink.patch @@ -0,0 +1,25 @@ +From 32affddf47a6a3aba580707f349565b3655a16b7 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Sat, 17 Feb 2018 19:39:38 +0100 +Subject: [PATCH 1/6] Allow deletion of symlink + +Change-Id: I9731895f88729072297f753088583aabbe6990f4 +--- + FsCrypt.cpp | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/FsCrypt.cpp b/FsCrypt.cpp +index e21524a..ca8e6f9 100644 +--- a/FsCrypt.cpp ++++ b/FsCrypt.cpp +@@ -313,6 +313,7 @@ static bool prepare_dir(const std::string& dir, mode_t mode, uid_t uid, gid_t gi + static bool destroy_dir(const std::string& dir) { + LOG(DEBUG) << "Destroying: " << dir; + if (rmdir(dir.c_str()) != 0 && errno != ENOENT) { ++ if(unlink(dir.c_str()) == 0) return true; + PLOG(ERROR) << "Failed to destroy " << dir; + return false; + } +-- +2.17.1 + diff --git a/patches/platform_system_vold/0002-Support-Samsung-s-implementation-of-exfat-called-sdf.patch b/patches/platform_system_vold/0002-Support-Samsung-s-implementation-of-exfat-called-sdf.patch new file mode 100644 index 0000000..51370bf --- /dev/null +++ b/patches/platform_system_vold/0002-Support-Samsung-s-implementation-of-exfat-called-sdf.patch @@ -0,0 +1,44 @@ +From f19e29be30ae859c084bebd2d5c32f7ff94bc31f Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Mon, 20 Aug 2018 22:37:54 +0200 +Subject: [PATCH 2/6] Support Samsung's implementation of exfat, called sdfat + +--- + fs/Exfat.cpp | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/fs/Exfat.cpp b/fs/Exfat.cpp +index 34f1024..3aa9494 100644 +--- a/fs/Exfat.cpp ++++ b/fs/Exfat.cpp +@@ -35,7 +35,7 @@ static const char* kFsckPath = "/system/bin/fsck.exfat"; + + bool IsSupported() { + return access(kMkfsPath, X_OK) == 0 && access(kFsckPath, X_OK) == 0 && +- IsFilesystemSupported("exfat"); ++ (IsFilesystemSupported("exfat") || IsFilesystemSupported("sdfat")); + } + + status_t Check(const std::string& source) { +@@ -61,13 +61,16 @@ status_t Mount(const std::string& source, const std::string& target, int ownerUi + auto mountData = android::base::StringPrintf("uid=%d,gid=%d,fmask=%o,dmask=%o", ownerUid, + ownerGid, permMask, permMask); + +- if (mount(source.c_str(), target.c_str(), "exfat", mountFlags, mountData.c_str()) == 0) { ++ const char *fs = "exfat"; ++ if(IsFilesystemSupported("sdfat")) ++ fs = "sdfat"; ++ if (mount(source.c_str(), target.c_str(), fs, mountFlags, mountData.c_str()) == 0) { + return 0; + } + + PLOG(ERROR) << "Mount failed; attempting read-only"; + mountFlags |= MS_RDONLY; +- if (mount(source.c_str(), target.c_str(), "exfat", mountFlags, mountData.c_str()) == 0) { ++ if (mount(source.c_str(), target.c_str(), fs, mountFlags, mountData.c_str()) == 0) { + return 0; + } + +-- +2.17.1 + diff --git a/patches/platform_system_vold/0003-Check-needsCheckpoint-only-if-checkpoint-is-supporte.patch b/patches/platform_system_vold/0003-Check-needsCheckpoint-only-if-checkpoint-is-supporte.patch new file mode 100644 index 0000000..72d69fb --- /dev/null +++ b/patches/platform_system_vold/0003-Check-needsCheckpoint-only-if-checkpoint-is-supporte.patch @@ -0,0 +1,41 @@ +From d6f033bfbd7c03b30a23b287e84b1608c35d6038 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Mon, 16 Sep 2019 13:49:05 +0200 +Subject: [PATCH] Check needsCheckpoint only if checkpoint is supported + +This is needed because some devices (Xiaomi MiPad 4, uncertified) +declares a bootctrl HAL in manifest, but doesn't have it. +vold will then hang in needsCheckpoint waiting for bootctrl + +Change-Id: I2dafcbca7e994d7a3ac36ef3698590db2ab482fa +--- + cryptfs.cpp | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/cryptfs.cpp b/cryptfs.cpp +index 063c5be..df48b15 100644 +--- a/cryptfs.cpp ++++ b/cryptfs.cpp +@@ -1363,7 +1363,7 @@ static int create_crypto_blk_dev_hw(struct crypt_mnt_ftr* crypt_ftr, const unsig + load_count = load_crypto_mapping_table(crypt_ftr, master_key, real_blk_name, name, fd, + extra_params); + } +- ++ + if (load_count < 0) { + SLOGE("Cannot load dm-crypt mapping table.\n"); + goto errout; +@@ -1963,7 +1963,9 @@ static int cryptfs_restart_internal(int restart_main) { + SLOGE("Failed to setexeccon"); + return -1; + } +- bool needs_cp = android::vold::cp_needsCheckpoint(); ++ bool supportsCheckpoint = false; ++ android::vold::cp_supportsCheckpoint(supportsCheckpoint); ++ bool needs_cp = supportsCheckpoint && android::vold::cp_needsCheckpoint(); + #ifdef CONFIG_HW_DISK_ENCRYPTION + while ((mount_rc = fs_mgr_do_mount(&fstab_default, DATA_MNT_POINT, blkdev.data(), 0, + needs_cp, false)) != 0) { +-- +2.25.1 + diff --git a/patches/platform_system_vold/0004-Sony-has-texfat-exfat-fs.patch b/patches/platform_system_vold/0004-Sony-has-texfat-exfat-fs.patch new file mode 100644 index 0000000..20b2204 --- /dev/null +++ b/patches/platform_system_vold/0004-Sony-has-texfat-exfat-fs.patch @@ -0,0 +1,38 @@ +From 8ffe3b736fa6736eb12951977eabdde6cd999359 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Tue, 28 Jan 2020 00:27:17 +0100 +Subject: [PATCH 4/6] Sony has `texfat` exfat fs + +--- + fs/Exfat.cpp | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/fs/Exfat.cpp b/fs/Exfat.cpp +index 3aa9494..03ad649 100644 +--- a/fs/Exfat.cpp ++++ b/fs/Exfat.cpp +@@ -35,7 +35,11 @@ static const char* kFsckPath = "/system/bin/fsck.exfat"; + + bool IsSupported() { + return access(kMkfsPath, X_OK) == 0 && access(kFsckPath, X_OK) == 0 && +- (IsFilesystemSupported("exfat") || IsFilesystemSupported("sdfat")); ++ ( ++ IsFilesystemSupported("exfat") || ++ IsFilesystemSupported("sdfat") || ++ IsFilesystemSupported("texfat") ++ ); + } + + status_t Check(const std::string& source) { +@@ -64,6 +68,8 @@ status_t Mount(const std::string& source, const std::string& target, int ownerUi + const char *fs = "exfat"; + if(IsFilesystemSupported("sdfat")) + fs = "sdfat"; ++ if(IsFilesystemSupported("texfat")) ++ fs = "texfat"; + if (mount(source.c_str(), target.c_str(), fs, mountFlags, mountData.c_str()) == 0) { + return 0; + } +-- +2.17.1 + diff --git a/patches/platform_system_vold/0005-Failing-to-create-facedata-shouldn-t-be-fatal.patch b/patches/platform_system_vold/0005-Failing-to-create-facedata-shouldn-t-be-fatal.patch new file mode 100644 index 0000000..bbc02cd --- /dev/null +++ b/patches/platform_system_vold/0005-Failing-to-create-facedata-shouldn-t-be-fatal.patch @@ -0,0 +1,38 @@ +From f558003318cd010d46dc3fca1508cf282d4ca56b Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Sat, 7 Mar 2020 14:49:09 +0100 +Subject: [PATCH 5/6] Failing to create facedata shouldn't be fatal + +Some Pie vendors create it on their own, so SELinux would deny that +Also not all devices have face unlock anyway + +See https://github.com/phhusson/treble_experimentations/issues/1119 +--- + vold_prepare_subdirs.cpp | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/vold_prepare_subdirs.cpp b/vold_prepare_subdirs.cpp +index d624d73..60056f3 100644 +--- a/vold_prepare_subdirs.cpp ++++ b/vold_prepare_subdirs.cpp +@@ -164,7 +164,7 @@ static bool prepare_subdirs(const std::string& volume_uuid, int user_id, int fla + } + auto facedata_path = vendor_de_path + "/facedata"; + if (!prepare_dir(sehandle, 0700, AID_SYSTEM, AID_SYSTEM, facedata_path)) { +- return false; ++ LOG(ERROR) << "Failed preparing folder for de facedata"; + } + } + if (flags & android::os::IVold::STORAGE_FLAG_CE) { +@@ -187,7 +187,7 @@ static bool prepare_subdirs(const std::string& volume_uuid, int user_id, int fla + auto vendor_ce_path = android::vold::BuildDataVendorCePath(user_id); + auto facedata_path = vendor_ce_path + "/facedata"; + if (!prepare_dir(sehandle, 0700, AID_SYSTEM, AID_SYSTEM, facedata_path)) { +- return false; ++ LOG(ERROR) << "Failed preparing folder for de facedata"; + } + } + } +-- +2.17.1 + diff --git a/patches/platform_system_vold/0006-Every-voldmanaged-storage-is-adoptable.patch b/patches/platform_system_vold/0006-Every-voldmanaged-storage-is-adoptable.patch new file mode 100644 index 0000000..07b07ce --- /dev/null +++ b/patches/platform_system_vold/0006-Every-voldmanaged-storage-is-adoptable.patch @@ -0,0 +1,25 @@ +From c1ccb433badede868ab164e0b74d942fa6341559 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Wed, 11 Mar 2020 14:02:35 +0100 +Subject: [PATCH 6/6] Every voldmanaged storage is adoptable + +--- + main.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/main.cpp b/main.cpp +index ebe5510..113d9b9 100644 +--- a/main.cpp ++++ b/main.cpp +@@ -249,7 +249,7 @@ static int process_config(VolumeManager* vm, bool* has_adoptable, bool* has_quot + std::string nickname(entry.label); + int flags = 0; + +- if (entry.is_encryptable()) { ++ if (entry.is_encryptable() || true) { + flags |= android::vold::Disk::Flags::kAdoptable; + *has_adoptable = true; + } +-- +2.17.1 +