From 93d0ea8243a3a318ee5fe0e1c6542dc17e91ee0c Mon Sep 17 00:00:00 2001 From: Andy CrossGate Yan Date: Mon, 12 Apr 2021 14:55:10 +0000 Subject: [PATCH] Sync up to v304 --- ...e-phh-treble-conflict-with-SELinux-p.patch | 182 + ...fferSourceWrapper-fix-to-apply-nBuff.patch | 44 - .../0033-Support-Samsung-R-multi-cams.patch | 66 + ...Don-t-crash-on-unknown-audio-devices.patch | 30 + ...er-retry-with-SW-bridge-if-hardware-.patch | 99 + ...Y_ROUTING.-This-is-a-leftover-of-tri.patch | 26 + ...lume-policy-when-none-has-been-found.patch | 40 + ...in-a-route-are-valid.-Dont-ignore-th.patch | 31 + ...t-Fix-backlight-control-on-Galaxy-S9.patch | 7990 +---------------- ...ung-Pie-and-Q-light-hal-and-Samsung-.patch | 35 +- ...ners-padding-overridable-with-persis.patch | 659 +- ...r-HAL-switch-case-was-filled-with-mi.patch | 16 +- ...dor-there-is-still-the-ISehMiscPower.patch | 46 + ...ype-of-Samsung-proximity-sensor-Hove.patch | 45 + ...tect-pick-up-sensor-so-that-an-overl.patch | 49 + ...nBuiltInDisplayCutoutRectApproximati.patch | 43 + ...Samsung-system-permission-on-sensors.patch | 37 + ...nged-HID_DEV_MTU_SIZE-from-64-to-512.patch | 29 + 18 files changed, 761 insertions(+), 8706 deletions(-) create mode 100644 patches/platform_external_selinux/0011-Workaround-device-phh-treble-conflict-with-SELinux-p.patch delete mode 100644 patches/platform_frameworks_av/0030-CCodec-GraphicBufferSourceWrapper-fix-to-apply-nBuff.patch create mode 100644 patches/platform_frameworks_av/0033-Support-Samsung-R-multi-cams.patch create mode 100644 patches/platform_frameworks_av/0034-audiopolicy-Don-t-crash-on-unknown-audio-devices.patch create mode 100644 patches/platform_frameworks_av/0035-AudioPolicyManager-retry-with-SW-bridge-if-hardware-.patch create mode 100644 patches/platform_frameworks_av/0036-Reenable-STRATEGY_ROUTING.-This-is-a-leftover-of-tri.patch create mode 100644 patches/platform_frameworks_av/0037-Use-a-fake-volume-policy-when-none-has-been-found.patch create mode 100644 patches/platform_frameworks_av/0038-Not-all-sources-in-a-route-are-valid.-Dont-ignore-th.patch create mode 100644 patches/platform_frameworks_base/0042-On-Samsung-R-vendor-there-is-still-the-ISehMiscPower.patch create mode 100644 patches/platform_frameworks_base/0043-Handle-another-type-of-Samsung-proximity-sensor-Hove.patch create mode 100644 patches/platform_frameworks_base/0044-Automatically-detect-pick-up-sensor-so-that-an-overl.patch create mode 100644 patches/platform_frameworks_base/0045-Catch-broken-mainBuiltInDisplayCutoutRectApproximati.patch create mode 100644 patches/platform_frameworks_native/0014-Remove-Samsung-system-permission-on-sensors.patch create mode 100644 patches/platform_system_bt/0004-Changed-HID_DEV_MTU_SIZE-from-64-to-512.patch diff --git a/patches/platform_external_selinux/0011-Workaround-device-phh-treble-conflict-with-SELinux-p.patch b/patches/platform_external_selinux/0011-Workaround-device-phh-treble-conflict-with-SELinux-p.patch new file mode 100644 index 0000000..f7abba1 --- /dev/null +++ b/patches/platform_external_selinux/0011-Workaround-device-phh-treble-conflict-with-SELinux-p.patch @@ -0,0 +1,182 @@ +From 9f851c801719b45c79cd911d67ceb7e8f4cf63ab Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Wed, 31 Mar 2021 23:32:37 +0200 +Subject: [PATCH] Workaround device/phh/treble conflict with SELinux policy + +device/phh/treble defines the following three types (hostapd, +sysfs_usb_supply, rpmb_device) +However, Qualcomm Samsung Android 11 devices export those symbols as +typealias. +Type and typealias are fundamentally not mergeable. +Luckily, Samsung doesn't do anything with those typealias, so we can +simply ignore them. + +Change-Id: I98db7e6eb55854887f90d0fd0f313fb0a19a488f +--- + libsepol/cil/src/cil_binary.c | 8 ++++++-- + libsepol/cil/src/cil_build_ast.c | 31 ++++++++++++++++++------------ + libsepol/cil/src/cil_resolve_ast.c | 15 +++++++++++++-- + 3 files changed, 38 insertions(+), 16 deletions(-) + +diff --git a/libsepol/cil/src/cil_binary.c b/libsepol/cil/src/cil_binary.c +index 03d53e1f..160fd0e0 100644 +--- a/libsepol/cil/src/cil_binary.c ++++ b/libsepol/cil/src/cil_binary.c +@@ -515,13 +515,17 @@ int cil_typealias_to_policydb(policydb_t *pdb, struct cil_alias *cil_alias) + type_datum_init(sepol_alias); + + rc = __cil_get_sepol_type_datum(pdb, DATUM(cil_alias->actual), &sepol_type); +- if (rc != SEPOL_OK) goto exit; ++ if (rc != SEPOL_OK) { ++ cil_log(CIL_ERR, "Failed at %s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__); ++ goto exit; ++ } + + sepol_alias->flavor = TYPE_TYPE; + + key = cil_strdup(cil_alias->datum.fqn); + rc = symtab_insert(pdb, SYM_TYPES, key, sepol_alias, SCOPE_DECL, 0, NULL); + if (rc != SEPOL_OK) { ++ cil_log(CIL_ERR, "Failed at %s:%s:%d:%d\n", __FILE__, __FUNCTION__, __LINE__, rc); + goto exit; + } + sepol_alias->s.value = sepol_type->s.value; +@@ -3776,7 +3780,7 @@ int __cil_node_to_policydb(struct cil_tree_node *node, void *extra_args) + + exit: + if (rc != SEPOL_OK) { +- cil_tree_log(node, CIL_ERR, "Binary policy creation failed"); ++ cil_tree_log(node, CIL_ERR, "Binary policy creation failed, for pass = %d, flavor = %d", pass, node->flavor); + } + return rc; + } +diff --git a/libsepol/cil/src/cil_build_ast.c b/libsepol/cil/src/cil_build_ast.c +index 02cdcc65..2ba08bc6 100644 +--- a/libsepol/cil/src/cil_build_ast.c ++++ b/libsepol/cil/src/cil_build_ast.c +@@ -1,16 +1,16 @@ + /* + * Copyright 2011 Tresys Technology, LLC. All rights reserved. +- * ++ * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: +- * ++ * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. +- * ++ * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. +- * ++ * + * THIS SOFTWARE IS PROVIDED BY TRESYS TECHNOLOGY, LLC ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +@@ -21,7 +21,7 @@ + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +- * ++ * + * The views and conclusions contained in the software and documentation are those + * of the authors and should not be interpreted as representing official policies, + * either expressed or implied, of Tresys Technology, LLC. +@@ -64,7 +64,7 @@ int cil_fill_list(struct cil_tree_node *current, enum cil_flavor flavor, struct + CIL_SYN_END + }; + int syntax_len = sizeof(syntax)/sizeof(*syntax); +- ++ + rc = __cil_verify_syntax(current, syntax, syntax_len); + if (rc != SEPOL_OK) { + goto exit; +@@ -108,7 +108,7 @@ int cil_gen_node(struct cil_db *db, struct cil_tree_node *ast_node, struct cil_s + { + int rc = SEPOL_ERR; + symtab_t *symtab = NULL; +- struct cil_symtab_datum *prev; ++ struct cil_symtab_datum *prev = NULL; + + rc = __cil_verify_name((const char*)key); + if (rc != SEPOL_OK) { +@@ -133,13 +133,20 @@ int cil_gen_node(struct cil_db *db, struct cil_tree_node *ast_node, struct cil_s + /* multiple_decls not ok, ret error */ + cil_log(CIL_ERR, "Re-declaration of %s %s\n", + cil_node_to_string(ast_node), key); +- if (cil_symtab_get_datum(symtab, key, &datum) == SEPOL_OK) { ++ if (cil_symtab_get_datum(symtab, key, &prev) == SEPOL_OK) { + if (sflavor == CIL_SYM_BLOCKS) { +- struct cil_tree_node *node = datum->nodes->head->data; ++ struct cil_tree_node *node = prev->nodes->head->data; + cil_tree_log(node, CIL_ERR, "Previous declaration"); + } + } +- goto exit; ++ if( ++ strcmp(key, "sysfs_usb_supply") == 0 || ++ strcmp(key, "hostapd") == 0 || ++ strcmp(key, "rpmb_device") == 0) { ++ cil_log(CIL_ERR, "Ignoring..."); ++ } else { ++ goto exit; ++ } + } + /* multiple_decls is enabled and works for this datum type, add node */ + cil_list_append(prev->nodes, CIL_NODE, ast_node); +@@ -572,7 +579,7 @@ int cil_gen_perm_nodes(struct cil_db *db, struct cil_tree_node *current_perm, st + + while(current_perm != NULL) { + if (current_perm->cl_head != NULL) { +- ++ + rc = SEPOL_ERR; + goto exit; + } +@@ -5717,7 +5724,7 @@ int cil_fill_ipaddr(struct cil_tree_node *addr_node, struct cil_ipaddr *addr) + return SEPOL_OK; + + exit: +- cil_log(CIL_ERR, "Bad ip address or netmask\n"); ++ cil_log(CIL_ERR, "Bad ip address or netmask\n"); + return rc; + } + +diff --git a/libsepol/cil/src/cil_resolve_ast.c b/libsepol/cil/src/cil_resolve_ast.c +index e72abdeb..db3f24f4 100644 +--- a/libsepol/cil/src/cil_resolve_ast.c ++++ b/libsepol/cil/src/cil_resolve_ast.c +@@ -512,7 +512,13 @@ int cil_resolve_aliasactual(struct cil_tree_node *current, void *extra_args, enu + } + if (NODE(alias_datum)->flavor != alias_flavor) { + cil_log(CIL_ERR, "%s is not an alias\n",alias_datum->name); +- rc = SEPOL_ERR; ++ if( ++ strcmp(alias_datum->name, "hostapd") == 0 || ++ strcmp(alias_datum->name, "sysfs_usb_supply") == 0 || ++ strcmp(alias_datum->name, "rpmb_device") == 0) ++ rc = 0; ++ else ++ rc = SEPOL_ERR; + goto exit; + } + +@@ -553,7 +559,12 @@ int cil_resolve_alias_to_actual(struct cil_tree_node *current, enum cil_flavor f + int limit = 2; + + if (alias->actual == NULL) { +- cil_tree_log(current, CIL_ERR, "Alias declared but not used"); ++ cil_tree_log(current, CIL_ERR, "Alias %s declared but not used", a1->datum.name); ++ if( ++ strcmp(a1->datum.name, "hostapd") == 0 || ++ strcmp(a1->datum.name, "sysfs_usb_supply") == 0 || ++ strcmp(a1->datum.name, "rpmb_device") == 0) ++ return SEPOL_OK; + return SEPOL_ERR; + } + +-- +2.25.1 + diff --git a/patches/platform_frameworks_av/0030-CCodec-GraphicBufferSourceWrapper-fix-to-apply-nBuff.patch b/patches/platform_frameworks_av/0030-CCodec-GraphicBufferSourceWrapper-fix-to-apply-nBuff.patch deleted file mode 100644 index a947686..0000000 --- a/patches/platform_frameworks_av/0030-CCodec-GraphicBufferSourceWrapper-fix-to-apply-nBuff.patch +++ /dev/null @@ -1,44 +0,0 @@ -From 9a240b125c01838dcd7be395354cd8d740b0923b Mon Sep 17 00:00:00 2001 -From: Taehwan Kim -Date: Thu, 26 Nov 2020 22:40:40 +0900 -Subject: [PATCH 30/30] CCodec: GraphicBufferSourceWrapper: fix to apply - nBufferCountActual for deciding number of inputs - -Bug: 169398817 - -Change-Id: I58cd7da35a3ddc4abdb58df954307acf329c7ee7 -Signed-off-by: Taehwan Kim -(cherry picked from commit 8b3bcddbc98af38d64a1ffefd5932b498f9d4c36) ---- - media/codec2/sfplugin/CCodec.cpp | 15 +++++++++++++-- - 1 file changed, 13 insertions(+), 2 deletions(-) - -diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp -index 54107bd852..2f16ca1104 100644 ---- a/media/codec2/sfplugin/CCodec.cpp -+++ b/media/codec2/sfplugin/CCodec.cpp -@@ -246,8 +246,19 @@ public: - if (source == nullptr) { - return NO_INIT; - } -- constexpr size_t kNumSlots = 16; -- for (size_t i = 0; i < kNumSlots; ++i) { -+ -+ size_t numSlots = 4; -+ constexpr OMX_U32 kPortIndexInput = 0; -+ -+ OMX_PARAM_PORTDEFINITIONTYPE param; -+ param.nPortIndex = kPortIndexInput; -+ status_t err = mNode->getParameter(OMX_IndexParamPortDefinition, -+ ¶m, sizeof(param)); -+ if (err == OK) { -+ numSlots = param.nBufferCountActual; -+ } -+ -+ for (size_t i = 0; i < numSlots; ++i) { - source->onInputBufferAdded(i); - } - --- -2.25.1 - diff --git a/patches/platform_frameworks_av/0033-Support-Samsung-R-multi-cams.patch b/patches/platform_frameworks_av/0033-Support-Samsung-R-multi-cams.patch new file mode 100644 index 0000000..49c67e0 --- /dev/null +++ b/patches/platform_frameworks_av/0033-Support-Samsung-R-multi-cams.patch @@ -0,0 +1,66 @@ +From ecab6fe48819adeafaceec1a33e7e1d380caec75 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Sat, 13 Mar 2021 14:20:03 -0500 +Subject: [PATCH 33/38] Support Samsung R multi-cams + +--- + services/camera/libcameraservice/Android.bp | 1 + + .../libcameraservice/common/CameraProviderManager.cpp | 8 +++++++- + 2 files changed, 8 insertions(+), 1 deletion(-) + +diff --git a/services/camera/libcameraservice/Android.bp b/services/camera/libcameraservice/Android.bp +index be275e9346..bede98bfdc 100644 +--- a/services/camera/libcameraservice/Android.bp ++++ b/services/camera/libcameraservice/Android.bp +@@ -129,6 +129,7 @@ cc_library_shared { + "android.hardware.camera.device@3.5", + "android.hardware.camera.device@3.6", + "vendor.samsung.hardware.camera.provider@3.0", ++ "vendor.samsung.hardware.camera.provider@4.0", + ], + + static_libs: [ +diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp +index 3e6ad1ef52..b7d1a57567 100644 +--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp ++++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp +@@ -22,6 +22,7 @@ + + #include + #include ++#include + + #include + #include +@@ -1363,7 +1364,9 @@ status_t CameraProviderManager::ProviderInfo::initialize( + } + + auto samsungCast = vendor::samsung::hardware::camera::provider::V3_0::ISehCameraProvider::castFrom(interface); ++ auto samsung40Cast = vendor::samsung::hardware::camera::provider::V4_0::ISehCameraProvider::castFrom(interface); + auto samsungProvider = samsungCast.isOk() ? static_cast>(samsungCast) : nullptr; ++ auto samsung40Provider = samsung40Cast.isOk() ? static_cast>(samsung40Cast) : nullptr; + + hardware::Return linked = interface->linkToDeath(this, /*cookie*/ mId); + if (!linked.isOk()) { +@@ -1401,6 +1404,7 @@ status_t CameraProviderManager::ProviderInfo::initialize( + status = idStatus; + if (status == Status::OK) { + for (auto& name : cameraDeviceNames) { ++ ALOGE("Listing camera ID %s", name.c_str()); + uint16_t major, minor; + std::string type, id; + status_t res = parseDeviceName(name, &major, &minor, &type, &id); +@@ -1414,7 +1418,9 @@ status_t CameraProviderManager::ProviderInfo::initialize( + } + } }; + hardware::Return ret; +- if(samsungProvider != nullptr && property_get_bool("persist.sys.phh.samsung.camera_ids", false)) ++ if(samsung40Provider != nullptr && property_get_bool("persist.sys.phh.samsung.camera_ids", false)) ++ ret = samsung40Provider->sehGetCameraIdList(cb); ++ else if(samsungProvider != nullptr && property_get_bool("persist.sys.phh.samsung.camera_ids", false)) + ret = samsungProvider->sehGetCameraIdList(cb); + else + ret = interface->getCameraIdList(cb); +-- +2.25.1 + diff --git a/patches/platform_frameworks_av/0034-audiopolicy-Don-t-crash-on-unknown-audio-devices.patch b/patches/platform_frameworks_av/0034-audiopolicy-Don-t-crash-on-unknown-audio-devices.patch new file mode 100644 index 0000000..4ecc0e0 --- /dev/null +++ b/patches/platform_frameworks_av/0034-audiopolicy-Don-t-crash-on-unknown-audio-devices.patch @@ -0,0 +1,30 @@ +From 347755b3b0d26b557ac0775433ccdbabe23ac6ed Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Tue, 23 Mar 2021 00:16:42 +0100 +Subject: [PATCH 34/38] [audiopolicy] Don't crash on unknown audio devices + +--- + .../common/managerdefinitions/src/Serializer.cpp | 7 ++++++- + 1 file changed, 6 insertions(+), 1 deletion(-) + +diff --git a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp +index 73e4a3e096..1f200ad850 100644 +--- a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp ++++ b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp +@@ -951,7 +951,12 @@ Return ModuleTraits::deserialize(const xmlNode *cur, PtrS + sp device = module->getDeclaredDevices(). + getDeviceFromTagName(std::string(reinterpret_cast( + attachedDevice.get()))); +- ctx->addDevice(device); ++ if(device != nullptr) { ++ ctx->addDevice(device); ++ } else { ++ ALOGE("NULL DEVICE %s: %s %s=%s", __func__, tag, childAttachedDeviceTag, ++ reinterpret_cast(attachedDevice.get())); ++ } + } + } + } +-- +2.25.1 + diff --git a/patches/platform_frameworks_av/0035-AudioPolicyManager-retry-with-SW-bridge-if-hardware-.patch b/patches/platform_frameworks_av/0035-AudioPolicyManager-retry-with-SW-bridge-if-hardware-.patch new file mode 100644 index 0000000..d1b7e63 --- /dev/null +++ b/patches/platform_frameworks_av/0035-AudioPolicyManager-retry-with-SW-bridge-if-hardware-.patch @@ -0,0 +1,99 @@ +From 0b8723c62a0d5b63f8afd46fddc3c38123b49d04 Mon Sep 17 00:00:00 2001 +From: Peter Cai +Date: Sun, 28 Mar 2021 16:17:36 +0800 +Subject: [PATCH 35/38] AudioPolicyManager: retry with SW bridge if hardware + audio patch fails + +* On two of my MT6771 Q vendor devices, in-call audio is broken due to + the audio HAL claiming to support HW audio patch between the Rx / Tx + devices but failing when called with `createAudioPatch`. + +> 03-28 11:56:42.300 1345 1345 W AudioALSAHardware: sinks[0].type == +AUDIO_PORT_TYPE_DEVICE +> 03-28 11:56:42.300 1345 1345 W AudioALSAHardware: [createAudioPatch] +[5082] +> 03-28 11:56:42.300 1345 1345 E AudioALSAHardware: Fail status -38 +> 03-28 11:56:42.300 1345 1345 W DeviceHAL: Error from HAL Device in +function create_audio_patch: Function not implemented +> 03-28 11:56:42.301 1358 1374 W APM_AudioPolicyManager: +createAudioPatchInternal patch panel could not connect device patch, +error -38 +> 03-28 11:56:42.301 1358 1374 W APM_AudioPolicyManager: +createTelephonyPatch() error -38 creating RX audio patch + +* This was not broken on Q because + `AudioPolicyManager::updateCallRouting` bypasses `createAudioPatch` by + directly calling the legacy `setOutputDevice` when `supportsPatch` is + true, i.e. `createAudioPatch` was *only* used for SW bridge for + in-call audio before R. + +* As a workaround, re-try by forcing the creation of a SW bridge after + `createAudioPatch` fails. We could also restore the old behavior of + `updateCallRouting`, but that would probably break in-call audio on + newer HALs that may or may not work properly with `setOutputDevice`. +--- + .../managerdefault/AudioPolicyManager.cpp | 13 ++++++++++--- + .../audiopolicy/managerdefault/AudioPolicyManager.h | 4 +++- + 2 files changed, 13 insertions(+), 4 deletions(-) + +diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +index 63dcd538b1..6d2c38811e 100644 +--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp ++++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp +@@ -3490,7 +3490,8 @@ status_t AudioPolicyManager::getAudioPort(struct audio_port *port) + status_t AudioPolicyManager::createAudioPatchInternal(const struct audio_patch *patch, + audio_patch_handle_t *handle, + uid_t uid, uint32_t delayMs, +- const sp& sourceDesc) ++ const sp& sourceDesc, ++ bool forceSwBridge) + { + ALOGV("%s", __func__); + if (handle == NULL || patch == NULL) { +@@ -3694,7 +3695,8 @@ status_t AudioPolicyManager::createAudioPatchInternal(const struct audio_patch * + // - audio HAL version is >= 3.0 but no route has been declared between devices + // - called from startAudioSource (aka sourceDesc != nullptr) and source device does + // not have a gain controller +- if (!srcDevice->hasSameHwModuleAs(sinkDevice) || ++ // - a previous attempt at using HW bridge failed (forceSwBridge) ++ if (forceSwBridge || !srcDevice->hasSameHwModuleAs(sinkDevice) || + (srcDevice->getModuleVersionMajor() < 3) || + !srcDevice->getModule()->supportsPatch(srcDevice, sinkDevice) || + (sourceDesc != nullptr && +@@ -3759,7 +3761,12 @@ status_t AudioPolicyManager::createAudioPatchInternal(const struct audio_patch * + __func__, index, handle, patchBuilder.patch(), delayMs, uid, &patchDesc); + if (status != NO_ERROR) { + ALOGW("%s patch panel could not connect device patch, error %d", __func__, status); +- return INVALID_OPERATION; ++ if (forceSwBridge || patch->sinks[0].type != AUDIO_PORT_TYPE_DEVICE) { ++ return INVALID_OPERATION; ++ } else { ++ ALOGW("Retrying with software bridging."); ++ return createAudioPatchInternal(patch, handle, uid, delayMs, sourceDesc, true); ++ } + } + } else { + return BAD_VALUE; +diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h +index b588f898d4..60cbd7175e 100644 +--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h ++++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h +@@ -910,12 +910,14 @@ private: + * @param[in] delayMs if required + * @param[in] sourceDesc [optional] in case of external source, source client to be + * configured by the patch, i.e. assigning an Output (HW or SW) ++ * @param[in] forceSwBridge [optional] force the creation of a SW bridge (internal use only) + * @return NO_ERROR if patch installed correctly, error code otherwise. + */ + status_t createAudioPatchInternal(const struct audio_patch *patch, + audio_patch_handle_t *handle, + uid_t uid, uint32_t delayMs = 0, +- const sp& sourceDesc = nullptr); ++ const sp& sourceDesc = nullptr, ++ bool forceSwBridge = false); + /** + * @brief releaseAudioPatchInternal internal function to remove an audio patch + * @param[in] handle of the patch to be removed +-- +2.25.1 + diff --git a/patches/platform_frameworks_av/0036-Reenable-STRATEGY_ROUTING.-This-is-a-leftover-of-tri.patch b/patches/platform_frameworks_av/0036-Reenable-STRATEGY_ROUTING.-This-is-a-leftover-of-tri.patch new file mode 100644 index 0000000..955ab0f --- /dev/null +++ b/patches/platform_frameworks_av/0036-Reenable-STRATEGY_ROUTING.-This-is-a-leftover-of-tri.patch @@ -0,0 +1,26 @@ +From c8a6af6fd0f3134b96fc291910cfc0380bc0dc31 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Sun, 28 Mar 2021 14:47:52 +0200 +Subject: [PATCH 36/38] Reenable STRATEGY_ROUTING. This is a leftover of tries + of 587b198, wrongly commited in 20f869 + +--- + services/audiopolicy/enginedefault/src/Engine.cpp | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/services/audiopolicy/enginedefault/src/Engine.cpp b/services/audiopolicy/enginedefault/src/Engine.cpp +index 16a7a4bbac..b14d2bbb0b 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 }, + }; +-- +2.25.1 + diff --git a/patches/platform_frameworks_av/0037-Use-a-fake-volume-policy-when-none-has-been-found.patch b/patches/platform_frameworks_av/0037-Use-a-fake-volume-policy-when-none-has-been-found.patch new file mode 100644 index 0000000..4e52786 --- /dev/null +++ b/patches/platform_frameworks_av/0037-Use-a-fake-volume-policy-when-none-has-been-found.patch @@ -0,0 +1,40 @@ +From c3bd3dc63c34de7c688b24999769f7eb504784a3 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Sun, 28 Mar 2021 14:48:49 +0200 +Subject: [PATCH 37/38] Use a fake volume policy when none has been found + +This is useful, because on Samsung devices, the "real" +(=non-gsi-cheating) audio policy doesn't have any volume policy. + +This requires actually adding the fake audio policy xml file (done in +device/phh/treble) +--- + services/audiopolicy/engine/config/src/EngineConfig.cpp | 8 +++++++- + 1 file changed, 7 insertions(+), 1 deletion(-) + +diff --git a/services/audiopolicy/engine/config/src/EngineConfig.cpp b/services/audiopolicy/engine/config/src/EngineConfig.cpp +index 4842cb282f..e8d6917754 100644 +--- a/services/audiopolicy/engine/config/src/EngineConfig.cpp ++++ b/services/audiopolicy/engine/config/src/EngineConfig.cpp +@@ -717,11 +717,17 @@ android::status_t parseLegacyVolumes(VolumeGroups &volumeGroups) { + snprintf(audioPolicyXmlConfigFile, sizeof(audioPolicyXmlConfigFile), + "%s/%s", path.c_str(), fileName); + ret = parseLegacyVolumeFile(audioPolicyXmlConfigFile, volumeGroups); +- if (ret == NO_ERROR) { ++ ALOGE("Parsing volume for %s gave %zu", audioPolicyXmlConfigFile, volumeGroups.size()); ++ if (ret == NO_ERROR && volumeGroups.size() > 0) { + return ret; + } + } + } ++ ret = parseLegacyVolumeFile("/system/etc/fake_audio_policy_volume.xml", volumeGroups); ++ ALOGE("Parsing volume for /system/etc/fake_audio_policy_volume.xml gave %zu", volumeGroups.size()); ++ if (ret == NO_ERROR && volumeGroups.size() == 0) { ++ return ret; ++ } + return BAD_VALUE; + } + +-- +2.25.1 + diff --git a/patches/platform_frameworks_av/0038-Not-all-sources-in-a-route-are-valid.-Dont-ignore-th.patch b/patches/platform_frameworks_av/0038-Not-all-sources-in-a-route-are-valid.-Dont-ignore-th.patch new file mode 100644 index 0000000..64d7dea --- /dev/null +++ b/patches/platform_frameworks_av/0038-Not-all-sources-in-a-route-are-valid.-Dont-ignore-th.patch @@ -0,0 +1,31 @@ +From 23f277e7a6afee9d256582204099c4c645606b98 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Sun, 28 Mar 2021 18:54:47 +0200 +Subject: [PATCH 38/38] Not all sources in a route are valid. Dont ignore the + whole route because of one broken source + +--- + .../audiopolicy/common/managerdefinitions/src/Serializer.cpp | 5 +++-- + 1 file changed, 3 insertions(+), 2 deletions(-) + +diff --git a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp +index 1f200ad850..e57bd24724 100644 +--- a/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp ++++ b/services/audiopolicy/common/managerdefinitions/src/Serializer.cpp +@@ -609,10 +609,11 @@ Return RouteTraits::deserialize(const xmlNode *cur, PtrSer + 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); ++ if(source != nullptr) { ++ sources.add(source); ++ } + } + devTag = strtok(NULL, ","); + } +-- +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 index c095b7a..84a2123 100644 --- 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 @@ -1,4 +1,4 @@ -From ed832370c42587b4d880ff3991492b67e1d97452 Mon Sep 17 00:00:00 2001 +From db0376e279f7aa4744934376157b844f51d334ce 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(+) @@ -49,7990 +49,14 @@ Change-Id: I35c35fabff8b275f35671dcb8578b96dcad526f1 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 + .../android/server/lights/LightsService.java | 49 ++++++++++++++++++- + 1 file changed, 48 insertions(+), 1 deletion(-) -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 +index f712e8de353..43264cb32d2 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; +@@ -33,6 +33,7 @@ import android.os.Looper; import android.os.PowerManager; import android.os.RemoteException; import android.os.ServiceManager; @@ -8040,7 +64,7 @@ index 16e42d21224..f0d7aa6abe7 100644 import android.os.Trace; import android.provider.Settings; import android.util.Slog; -@@ -301,12 +302,13 @@ public class LightsService extends SystemService { +@@ -302,12 +303,13 @@ public class LightsService extends SystemService { + ": brightness=" + brightness); return; } @@ -8055,7 +79,7 @@ index 16e42d21224..f0d7aa6abe7 100644 // 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 { +@@ -322,6 +324,51 @@ public class LightsService extends SystemService { // Old system int brightnessInt = BrightnessSynchronizer.brightnessFloatToInt( getContext(), brightness); 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 index b49178f..ae0b994 100644 --- 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 @@ -1,9 +1,10 @@ -From 04ada322124eca94d2e354f10c45d80d1ad522e3 Mon Sep 17 00:00:00 2001 +From bff763fb4b54c97fbb3535ea68a093acf81969bc 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 +Change-Id: I37dc7af3fbc6f9bfa2a4822d4dfba817e803945e --- services/core/jni/Android.bp | 3 ++ ...om_android_server_lights_LightsService.cpp | 51 +++++++++++++++++++ @@ -11,13 +12,13 @@ Subject: [PATCH] Support new samsung Pie and Q light hal and Samsung Power 3 files changed, 76 insertions(+) diff --git a/services/core/jni/Android.bp b/services/core/jni/Android.bp -index eaa3c66fe0d..74f5b8667c4 100644 +index 0b085d65e4c..36315e951e3 100644 --- a/services/core/jni/Android.bp +++ b/services/core/jni/Android.bp -@@ -167,6 +167,9 @@ cc_defaults { - "service.incremental", +@@ -169,6 +169,9 @@ cc_defaults { "suspend_control_aidl_interface-cpp", "vendor.lineage.power@1.0", + "vendor.lineage.power-cpp", + "vendor.samsung.hardware.miscpower@2.0", + "vendor.samsung.hardware.light@2.0", + "vendor.samsung.hardware.light@3.0", @@ -103,7 +104,7 @@ index 35d8219651d..b45b2137d0e 100644 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 +index 61d53351af5..dcd33366a35 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 @@ @@ -113,25 +114,25 @@ index 33b3bad2b33..47fdcc9a87b 100644 +#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; + #include +@@ -69,6 +70,7 @@ using LineageBoostAidl = vendor::lineage::power::Boost; + using LineageFeatureV1_0 = vendor::lineage::power::V1_0::LineageFeature; + using LineageFeatureAidl = vendor::lineage::power::Feature; + using LineagePowerHint1_0 = vendor::lineage::power::V1_0::LineagePowerHint; +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; +@@ -86,6 +88,8 @@ static sp gPowerHalHidlV1_1_ = nullptr; static sp gPowerHalAidl_ = nullptr; static sp gLineagePowerHalV1_0_ = nullptr; + static sp gLineagePowerHalAidl_ = nullptr; +static sp gSehMiscPower = nullptr; +static bool gPowerHalExists = true; static std::mutex gPowerHalMutex; + static std::mutex gLineagePowerHalMutex; - enum class HalVersion { -@@ -125,6 +129,10 @@ static HalVersion connectPowerHalLocked() { +@@ -132,6 +136,10 @@ static HalVersion connectPowerHalLocked() { gPowerHalAidlExists = false; } } @@ -142,8 +143,8 @@ index 33b3bad2b33..47fdcc9a87b 100644 if (gPowerHalHidlExists && gPowerHalHidlV1_0_ == nullptr) { gPowerHalHidlV1_0_ = IPowerV1_0::getService(); if (gPowerHalHidlV1_0_) { -@@ -163,6 +171,12 @@ void connectLineagePowerHalLocked() { - } +@@ -190,6 +198,12 @@ static HalVersion connectLineagePowerHalLocked() { + return HalVersion::NONE; } +sp getSehMiscPower() { @@ -155,7 +156,7 @@ index 33b3bad2b33..47fdcc9a87b 100644 // 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 +@@ -559,6 +573,14 @@ static void nativeSetInteractive(JNIEnv* /* env */, jclass /* clazz */, jboolean return; } } diff --git a/patches/platform_frameworks_base/0028-Make-rounded-corners-padding-overridable-with-persis.patch b/patches/platform_frameworks_base/0028-Make-rounded-corners-padding-overridable-with-persis.patch index 2dbbfaa..9ddc40b 100644 --- a/patches/platform_frameworks_base/0028-Make-rounded-corners-padding-overridable-with-persis.patch +++ b/patches/platform_frameworks_base/0028-Make-rounded-corners-padding-overridable-with-persis.patch @@ -1,4 +1,4 @@ -From 8bd0f8e1905ed0ecf275d383e816afb480feae30 Mon Sep 17 00:00:00 2001 +From 014ba558076692fb0009b0100c4f18a6800e30ac Mon Sep 17 00:00:00 2001 From: Pierre-Hugues Husson Date: Sun, 6 Dec 2020 12:20:08 +0100 Subject: [PATCH] Make rounded corners padding overridable with @@ -6,12 +6,10 @@ Subject: [PATCH] Make rounded corners padding overridable with Change-Id: Id5d73b06b9a2cb2da95ff31f204c1984555872ff --- - .../systemui/qs/QuickStatusBarHeader.java | 6 +- - .../phone/KeyguardStatusBarView.java | 6 +- - .../statusbar/phone/PhoneStatusBarView.java | 6 +- - ..._server_power_PowerManagerService.cpp.orig | 641 ++++++++++++++++++ - 4 files changed, 656 insertions(+), 3 deletions(-) - create mode 100644 services/core/jni/com_android_server_power_PowerManagerService.cpp.orig + .../src/com/android/systemui/qs/QuickStatusBarHeader.java | 6 +++++- + .../systemui/statusbar/phone/KeyguardStatusBarView.java | 6 +++++- + .../systemui/statusbar/phone/PhoneStatusBarView.java | 6 +++++- + 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java index 7533f2ac1db..94fd996d085 100644 @@ -88,653 +86,6 @@ index 1c6c7db6a69..f0e5c7bb3d2 100644 updateStatusBarHeight(); } -diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp.orig b/services/core/jni/com_android_server_power_PowerManagerService.cpp.orig -new file mode 100644 -index 00000000000..47fdcc9a87b ---- /dev/null -+++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp.orig -@@ -0,0 +1,641 @@ -+/* -+ * Copyright (C) 2010 The Android Open Source Project -+ * -+ * Licensed under the Apache License, Version 2.0 (the "License"); -+ * you may not use this file except in compliance with the License. -+ * You may obtain a copy of the License at -+ * -+ * http://www.apache.org/licenses/LICENSE-2.0 -+ * -+ * Unless required by applicable law or agreed to in writing, software -+ * distributed under the License is distributed on an "AS IS" BASIS, -+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+ * See the License for the specific language governing permissions and -+ * limitations under the License. -+ */ -+ -+#define LOG_TAG "PowerManagerService-JNI" -+ -+//#define LOG_NDEBUG 0 -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include "jni.h" -+ -+#include -+ -+#include -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "com_android_server_power_PowerManagerService.h" -+ -+using android::hardware::Return; -+using android::hardware::Void; -+using android::hardware::power::Boost; -+using android::hardware::power::Mode; -+using android::hardware::power::V1_0::PowerHint; -+using android::hardware::power::V1_0::Feature; -+using android::String8; -+using android::system::suspend::V1_0::ISystemSuspend; -+using android::system::suspend::V1_0::IWakeLock; -+using android::system::suspend::V1_0::WakeLockType; -+using android::system::suspend::ISuspendControlService; -+using IPowerV1_1 = android::hardware::power::V1_1::IPower; -+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 { -+ -+// ---------------------------------------------------------------------------- -+ -+static struct { -+ jmethodID userActivityFromNative; -+} gPowerManagerServiceClassInfo; -+ -+// ---------------------------------------------------------------------------- -+ -+static jobject gPowerManagerServiceObj; -+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 { -+ NONE, -+ HIDL_1_0, -+ HIDL_1_1, -+ AIDL, -+}; -+ -+static nsecs_t gLastEventTime[USER_ACTIVITY_EVENT_LAST + 1]; -+ -+// Throttling interval for user activity calls. -+static const nsecs_t MIN_TIME_BETWEEN_USERACTIVITIES = 100 * 1000000L; // 100ms -+ -+// ---------------------------------------------------------------------------- -+ -+static bool checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) { -+ if (env->ExceptionCheck()) { -+ ALOGE("An exception was thrown by callback '%s'.", methodName); -+ LOGE_EX(env); -+ env->ExceptionClear(); -+ return true; -+ } -+ return false; -+} -+ -+// Check validity of current handle to the power HAL service, and connect to it if necessary. -+// The caller must be holding gPowerHalMutex. -+static HalVersion connectPowerHalLocked() { -+ static bool gPowerHalHidlExists = true; -+ static bool gPowerHalAidlExists = true; -+ if (!gPowerHalHidlExists && !gPowerHalAidlExists) { -+ return HalVersion::NONE; -+ } -+ if (gPowerHalAidlExists) { -+ if (!gPowerHalAidl_) { -+ gPowerHalAidl_ = waitForVintfService(); -+ } -+ if (gPowerHalAidl_) { -+ ALOGV("Successfully connected to Power HAL AIDL service."); -+ return HalVersion::AIDL; -+ } else { -+ 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_) { -+ ALOGV("Successfully connected to Power HAL HIDL 1.0 service."); -+ // Try cast to powerHAL HIDL V1_1 -+ gPowerHalHidlV1_1_ = IPowerV1_1::castFrom(gPowerHalHidlV1_0_); -+ if (gPowerHalHidlV1_1_) { -+ ALOGV("Successfully connected to Power HAL HIDL 1.1 service."); -+ } -+ } else { -+ ALOGV("Couldn't load power HAL HIDL service"); -+ gPowerHalHidlExists = false; -+ return HalVersion::NONE; -+ } -+ } -+ if (gPowerHalHidlV1_1_) { -+ return HalVersion::HIDL_1_1; -+ } else if (gPowerHalHidlV1_0_) { -+ return HalVersion::HIDL_1_0; -+ } -+ return HalVersion::NONE; -+} -+ -+// Check validity of current handle to the Lineage power HAL service, and call getService() if necessary. -+// The caller must be holding gPowerHalMutex. -+void connectLineagePowerHalLocked() { -+ static bool gLineagePowerHalExists = true; -+ if (gLineagePowerHalExists && gLineagePowerHalV1_0_ == nullptr) { -+ gLineagePowerHalV1_0_ = ILineagePowerV1_0::getService(); -+ if (gLineagePowerHalV1_0_ != nullptr) { -+ ALOGI("Loaded power HAL service"); -+ } else { -+ ALOGI("Couldn't load power HAL service"); -+ gLineagePowerHalExists = false; -+ } -+ } -+} -+ -+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); -+ HalVersion halVersion = connectPowerHalLocked(); -+ if (halVersion == HalVersion::HIDL_1_0 || halVersion == HalVersion::HIDL_1_1) { -+ return gPowerHalHidlV1_0_; -+ } -+ -+ return nullptr; -+} -+ -+// Retrieve a copy of PowerHAL HIDL V1_1 -+sp getPowerHalHidlV1_1() { -+ std::lock_guard lock(gPowerHalMutex); -+ if (connectPowerHalLocked() == HalVersion::HIDL_1_1) { -+ return gPowerHalHidlV1_1_; -+ } -+ -+ return nullptr; -+} -+ -+// Retrieve a copy of LineagePowerHAL V1_0 -+sp getLineagePowerHalV1_0() { -+ std::lock_guard lock(gPowerHalMutex); -+ connectLineagePowerHalLocked(); -+ return gLineagePowerHalV1_0_; -+} -+ -+// Check if a call to a power HAL function failed; if so, log the failure and invalidate the -+// current handle to the power HAL service. -+bool processPowerHalReturn(bool isOk, const char* functionName) { -+ if (!isOk) { -+ ALOGE("%s() failed: power HAL service not available.", functionName); -+ gPowerHalMutex.lock(); -+ gPowerHalHidlV1_0_ = nullptr; -+ gPowerHalHidlV1_1_ = nullptr; -+ gPowerHalAidl_ = nullptr; -+ gPowerHalMutex.unlock(); -+ } -+ return isOk; -+} -+ -+enum class HalSupport { -+ UNKNOWN = 0, -+ ON, -+ OFF, -+}; -+ -+static void setPowerBoostWithHandle(sp handle, Boost boost, int32_t durationMs) { -+ // Android framework only sends boost upto DISPLAY_UPDATE_IMMINENT. -+ // Need to increase the array size if more boost supported. -+ static std::array, -+ static_cast(Boost::DISPLAY_UPDATE_IMMINENT) + 1> -+ boostSupportedArray = {HalSupport::UNKNOWN}; -+ -+ // Quick return if boost is not supported by HAL -+ if (boost > Boost::DISPLAY_UPDATE_IMMINENT || -+ boostSupportedArray[static_cast(boost)] == HalSupport::OFF) { -+ ALOGV("Skipped setPowerBoost %s because HAL doesn't support it", toString(boost).c_str()); -+ return; -+ } -+ -+ if (boostSupportedArray[static_cast(boost)] == HalSupport::UNKNOWN) { -+ bool isSupported = false; -+ handle->isBoostSupported(boost, &isSupported); -+ boostSupportedArray[static_cast(boost)] = -+ isSupported ? HalSupport::ON : HalSupport::OFF; -+ if (!isSupported) { -+ ALOGV("Skipped setPowerBoost %s because HAL doesn't support it", -+ toString(boost).c_str()); -+ return; -+ } -+ } -+ -+ auto ret = handle->setBoost(boost, durationMs); -+ processPowerHalReturn(ret.isOk(), "setPowerBoost"); -+} -+ -+static void setPowerBoost(Boost boost, int32_t durationMs) { -+ std::unique_lock lock(gPowerHalMutex); -+ if (connectPowerHalLocked() != HalVersion::AIDL) { -+ ALOGV("Power HAL AIDL not available"); -+ return; -+ } -+ sp handle = gPowerHalAidl_; -+ lock.unlock(); -+ setPowerBoostWithHandle(handle, boost, durationMs); -+} -+ -+static bool setPowerModeWithHandle(sp handle, Mode mode, bool enabled) { -+ // Android framework only sends mode upto DISPLAY_INACTIVE. -+ // Need to increase the array if more mode supported. -+ static std::array, static_cast(Mode::DISPLAY_INACTIVE) + 1> -+ modeSupportedArray = {HalSupport::UNKNOWN}; -+ -+ // Quick return if mode is not supported by HAL -+ if (mode > Mode::DISPLAY_INACTIVE || -+ modeSupportedArray[static_cast(mode)] == HalSupport::OFF) { -+ ALOGV("Skipped setPowerMode %s because HAL doesn't support it", toString(mode).c_str()); -+ return false; -+ } -+ -+ if (modeSupportedArray[static_cast(mode)] == HalSupport::UNKNOWN) { -+ bool isSupported = false; -+ handle->isModeSupported(mode, &isSupported); -+ modeSupportedArray[static_cast(mode)] = -+ isSupported ? HalSupport::ON : HalSupport::OFF; -+ if (!isSupported) { -+ ALOGV("Skipped setPowerMode %s because HAL doesn't support it", toString(mode).c_str()); -+ return false; -+ } -+ } -+ -+ auto ret = handle->setMode(mode, enabled); -+ processPowerHalReturn(ret.isOk(), "setPowerMode"); -+ return ret.isOk(); -+} -+ -+static bool setPowerMode(Mode mode, bool enabled) { -+ std::unique_lock lock(gPowerHalMutex); -+ if (connectPowerHalLocked() != HalVersion::AIDL) { -+ ALOGV("Power HAL AIDL not available"); -+ return false; -+ } -+ sp handle = gPowerHalAidl_; -+ lock.unlock(); -+ return setPowerModeWithHandle(handle, mode, enabled); -+} -+ -+static void sendPowerHint(PowerHint hintId, uint32_t data) { -+ std::unique_lock lock(gPowerHalMutex); -+ switch (connectPowerHalLocked()) { -+ case HalVersion::NONE: -+ return; -+ case HalVersion::HIDL_1_0: { -+ sp handle = gPowerHalHidlV1_0_; -+ lock.unlock(); -+ auto ret = handle->powerHint(hintId, data); -+ processPowerHalReturn(ret.isOk(), "powerHint"); -+ break; -+ } -+ case HalVersion::HIDL_1_1: { -+ sp handle = gPowerHalHidlV1_1_; -+ lock.unlock(); -+ auto ret = handle->powerHintAsync(hintId, data); -+ processPowerHalReturn(ret.isOk(), "powerHintAsync"); -+ break; -+ } -+ case HalVersion::AIDL: { -+ if (hintId == PowerHint::INTERACTION) { -+ sp handle = gPowerHalAidl_; -+ lock.unlock(); -+ setPowerBoostWithHandle(handle, Boost::INTERACTION, data); -+ break; -+ } else if (hintId == PowerHint::LAUNCH) { -+ sp handle = gPowerHalAidl_; -+ lock.unlock(); -+ setPowerModeWithHandle(handle, Mode::LAUNCH, static_cast(data)); -+ break; -+ } else if (hintId == PowerHint::LOW_POWER) { -+ sp handle = gPowerHalAidl_; -+ lock.unlock(); -+ setPowerModeWithHandle(handle, Mode::LOW_POWER, static_cast(data)); -+ break; -+ } else if (hintId == PowerHint::SUSTAINED_PERFORMANCE) { -+ sp handle = gPowerHalAidl_; -+ lock.unlock(); -+ setPowerModeWithHandle(handle, Mode::SUSTAINED_PERFORMANCE, -+ static_cast(data)); -+ break; -+ } else if (hintId == PowerHint::VR_MODE) { -+ sp handle = gPowerHalAidl_; -+ lock.unlock(); -+ setPowerModeWithHandle(handle, Mode::VR, static_cast(data)); -+ break; -+ } else { -+ ALOGE("Unsupported power hint: %s.", toString(hintId).c_str()); -+ return; -+ } -+ } -+ default: { -+ ALOGE("Unknown power HAL state"); -+ return; -+ } -+ } -+ SurfaceComposerClient::notifyPowerHint(static_cast(hintId)); -+} -+ -+void android_server_PowerManagerService_userActivity(nsecs_t eventTime, int32_t eventType, -+ int32_t keyCode) { -+ if (gPowerManagerServiceObj) { -+ // Throttle calls into user activity by event type. -+ // We're a little conservative about argument checking here in case the caller -+ // passes in bad data which could corrupt system state. -+ if (eventType >= 0 && eventType <= USER_ACTIVITY_EVENT_LAST) { -+ nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); -+ if (eventTime > now) { -+ eventTime = now; -+ } -+ -+ if (gLastEventTime[eventType] + MIN_TIME_BETWEEN_USERACTIVITIES > eventTime) { -+ return; -+ } -+ gLastEventTime[eventType] = eventTime; -+ -+ // Tell the power HAL when user activity occurs. -+ sendPowerHint(PowerHint::INTERACTION, 0); -+ } -+ -+ JNIEnv* env = AndroidRuntime::getJNIEnv(); -+ -+ int flags = 0; -+ if (keyCode == AKEYCODE_VOLUME_UP || keyCode == AKEYCODE_VOLUME_DOWN) { -+ flags |= USER_ACTIVITY_FLAG_NO_BUTTON_LIGHTS; -+ } -+ -+ env->CallVoidMethod(gPowerManagerServiceObj, -+ gPowerManagerServiceClassInfo.userActivityFromNative, -+ nanoseconds_to_milliseconds(eventTime), eventType, flags); -+ checkAndClearExceptionFromCallback(env, "userActivityFromNative"); -+ } -+} -+ -+static sp gSuspendHal = nullptr; -+static sp gSuspendControl = nullptr; -+static sp gSuspendBlocker = nullptr; -+static std::mutex gSuspendMutex; -+ -+// Assume SystemSuspend HAL is always alive. -+// TODO: Force device to restart if SystemSuspend HAL dies. -+sp getSuspendHal() { -+ static std::once_flag suspendHalFlag; -+ std::call_once(suspendHalFlag, [](){ -+ ::android::hardware::details::waitForHwService(ISystemSuspend::descriptor, "default"); -+ gSuspendHal = ISystemSuspend::getService(); -+ assert(gSuspendHal != nullptr); -+ }); -+ return gSuspendHal; -+} -+ -+sp getSuspendControl() { -+ static std::once_flag suspendControlFlag; -+ std::call_once(suspendControlFlag, [](){ -+ gSuspendControl = waitForService(String16("suspend_control")); -+ LOG_ALWAYS_FATAL_IF(gSuspendControl == nullptr); -+ }); -+ return gSuspendControl; -+} -+ -+void enableAutoSuspend() { -+ static bool enabled = false; -+ if (!enabled) { -+ sp suspendControl = getSuspendControl(); -+ suspendControl->enableAutosuspend(&enabled); -+ } -+ -+ { -+ std::lock_guard lock(gSuspendMutex); -+ if (gSuspendBlocker) { -+ gSuspendBlocker->release(); -+ gSuspendBlocker.clear(); -+ } -+ } -+} -+ -+void disableAutoSuspend() { -+ std::lock_guard lock(gSuspendMutex); -+ if (!gSuspendBlocker) { -+ sp suspendHal = getSuspendHal(); -+ gSuspendBlocker = suspendHal->acquireWakeLock(WakeLockType::PARTIAL, -+ "PowerManager.SuspendLockout"); -+ } -+} -+ -+static jint nativeGetFeature(JNIEnv* /* env */, jclass /* clazz */, jint featureId) { -+ int value = -1; -+ -+ sp lineagePowerHalV1_0 = getLineagePowerHalV1_0(); -+ if (lineagePowerHalV1_0 != nullptr) { -+ value = lineagePowerHalV1_0->getFeature(static_cast(featureId)); -+ } -+ -+ return static_cast(value); -+} -+ -+// ---------------------------------------------------------------------------- -+ -+static void nativeInit(JNIEnv* env, jobject obj) { -+ gPowerManagerServiceObj = env->NewGlobalRef(obj); -+ -+ gPowerHalMutex.lock(); -+ connectPowerHalLocked(); -+ gPowerHalMutex.unlock(); -+} -+ -+static void nativeAcquireSuspendBlocker(JNIEnv *env, jclass /* clazz */, jstring nameStr) { -+ ScopedUtfChars name(env, nameStr); -+ acquire_wake_lock(PARTIAL_WAKE_LOCK, name.c_str()); -+} -+ -+static void nativeReleaseSuspendBlocker(JNIEnv *env, jclass /* clazz */, jstring nameStr) { -+ ScopedUtfChars name(env, nameStr); -+ release_wake_lock(name.c_str()); -+} -+ -+static void nativeSetInteractive(JNIEnv* /* env */, jclass /* clazz */, jboolean enable) { -+ std::unique_lock lock(gPowerHalMutex); -+ switch (connectPowerHalLocked()) { -+ case HalVersion::NONE: -+ return; -+ case HalVersion::HIDL_1_0: -+ FALLTHROUGH_INTENDED; -+ case HalVersion::HIDL_1_1: { -+ android::base::Timer t; -+ sp handle = gPowerHalHidlV1_0_; -+ lock.unlock(); -+ auto ret = handle->setInteractive(enable); -+ processPowerHalReturn(ret.isOk(), "setInteractive"); -+ if (t.duration() > 20ms) { -+ ALOGD("Excessive delay in setInteractive(%s) while turning screen %s", -+ enable ? "true" : "false", enable ? "on" : "off"); -+ } -+ return; -+ } -+ case HalVersion::AIDL: { -+ sp handle = gPowerHalAidl_; -+ lock.unlock(); -+ setPowerModeWithHandle(handle, Mode::INTERACTIVE, enable); -+ return; -+ } -+ default: { -+ ALOGE("Unknown power HAL state"); -+ 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) { -+ if (enable) { -+ android::base::Timer t; -+ enableAutoSuspend(); -+ if (t.duration() > 100ms) { -+ ALOGD("Excessive delay in autosuspend_enable() while turning screen off"); -+ } -+ } else { -+ android::base::Timer t; -+ disableAutoSuspend(); -+ if (t.duration() > 100ms) { -+ ALOGD("Excessive delay in autosuspend_disable() while turning screen on"); -+ } -+ } -+} -+ -+static void nativeSendPowerHint(JNIEnv* /* env */, jclass /* clazz */, jint hintId, jint data) { -+ sendPowerHint(static_cast(hintId), data); -+} -+ -+static void nativeSetPowerBoost(JNIEnv* /* env */, jclass /* clazz */, jint boost, -+ jint durationMs) { -+ setPowerBoost(static_cast(boost), durationMs); -+} -+ -+static jboolean nativeSetPowerMode(JNIEnv* /* env */, jclass /* clazz */, jint mode, -+ jboolean enabled) { -+ return setPowerMode(static_cast(mode), enabled); -+} -+ -+static void nativeSetFeature(JNIEnv* /* env */, jclass /* clazz */, jint featureId, jint data) { -+ std::unique_lock lock(gPowerHalMutex); -+ switch (connectPowerHalLocked()) { -+ case HalVersion::NONE: -+ return; -+ case HalVersion::HIDL_1_0: -+ FALLTHROUGH_INTENDED; -+ case HalVersion::HIDL_1_1: { -+ sp handle = gPowerHalHidlV1_0_; -+ lock.unlock(); -+ auto ret = handle->setFeature(static_cast(featureId), static_cast(data)); -+ processPowerHalReturn(ret.isOk(), "setFeature"); -+ return; -+ } -+ case HalVersion::AIDL: { -+ sp handle = gPowerHalAidl_; -+ lock.unlock(); -+ setPowerModeWithHandle(handle, Mode::DOUBLE_TAP_TO_WAKE, static_cast(data)); -+ return; -+ } -+ default: { -+ ALOGE("Unknown power HAL state"); -+ return; -+ } -+ } -+} -+ -+static bool nativeForceSuspend(JNIEnv* /* env */, jclass /* clazz */) { -+ bool retval = false; -+ getSuspendControl()->forceSuspend(&retval); -+ return retval; -+} -+ -+// ---------------------------------------------------------------------------- -+ -+static const JNINativeMethod gPowerManagerServiceMethods[] = { -+ /* name, signature, funcPtr */ -+ {"nativeInit", "()V", (void*)nativeInit}, -+ {"nativeAcquireSuspendBlocker", "(Ljava/lang/String;)V", -+ (void*)nativeAcquireSuspendBlocker}, -+ {"nativeForceSuspend", "()Z", (void*)nativeForceSuspend}, -+ {"nativeReleaseSuspendBlocker", "(Ljava/lang/String;)V", -+ (void*)nativeReleaseSuspendBlocker}, -+ {"nativeSetInteractive", "(Z)V", (void*)nativeSetInteractive}, -+ {"nativeSetAutoSuspend", "(Z)V", (void*)nativeSetAutoSuspend}, -+ {"nativeSendPowerHint", "(II)V", (void*)nativeSendPowerHint}, -+ {"nativeSetPowerBoost", "(II)V", (void*)nativeSetPowerBoost}, -+ {"nativeSetPowerMode", "(IZ)Z", (void*)nativeSetPowerMode}, -+ {"nativeSetFeature", "(II)V", (void*)nativeSetFeature}, -+ {"nativeGetFeature", "(I)I", (void*)nativeGetFeature}, -+}; -+ -+#define FIND_CLASS(var, className) \ -+ var = env->FindClass(className); \ -+ LOG_FATAL_IF(! (var), "Unable to find class " className); -+ -+#define GET_METHOD_ID(var, clazz, methodName, methodDescriptor) \ -+ var = env->GetMethodID(clazz, methodName, methodDescriptor); \ -+ LOG_FATAL_IF(! (var), "Unable to find method " methodName); -+ -+#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ -+ var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ -+ LOG_FATAL_IF(! (var), "Unable to find field " fieldName); -+ -+int register_android_server_PowerManagerService(JNIEnv* env) { -+ int res = jniRegisterNativeMethods(env, "com/android/server/power/PowerManagerService", -+ gPowerManagerServiceMethods, NELEM(gPowerManagerServiceMethods)); -+ (void) res; // Faked use when LOG_NDEBUG. -+ LOG_FATAL_IF(res < 0, "Unable to register native methods."); -+ -+ // Callbacks -+ -+ jclass clazz; -+ FIND_CLASS(clazz, "com/android/server/power/PowerManagerService"); -+ -+ GET_METHOD_ID(gPowerManagerServiceClassInfo.userActivityFromNative, clazz, -+ "userActivityFromNative", "(JII)V"); -+ -+ // Initialize -+ for (int i = 0; i <= USER_ACTIVITY_EVENT_LAST; i++) { -+ gLastEventTime[i] = LLONG_MIN; -+ } -+ gPowerManagerServiceObj = NULL; -+ return 0; -+} -+ -+} /* namespace android */ -- 2.25.1 diff --git a/patches/platform_frameworks_base/0035-Fix-Samsung-Power-HAL-switch-case-was-filled-with-mi.patch b/patches/platform_frameworks_base/0035-Fix-Samsung-Power-HAL-switch-case-was-filled-with-mi.patch index aff9ef5..2483251 100644 --- a/patches/platform_frameworks_base/0035-Fix-Samsung-Power-HAL-switch-case-was-filled-with-mi.patch +++ b/patches/platform_frameworks_base/0035-Fix-Samsung-Power-HAL-switch-case-was-filled-with-mi.patch @@ -1,4 +1,4 @@ -From 792b863af87a7bd3d063da76ecefa7e229e1aded Mon Sep 17 00:00:00 2001 +From 368af07a0fc4fbed6f6946c8bf8237702841d914 Mon Sep 17 00:00:00 2001 From: Pierre-Hugues Husson Date: Fri, 18 Dec 2020 16:38:58 -0500 Subject: [PATCH] Fix Samsung Power HAL (switch/case was filled with @@ -9,18 +9,18 @@ Subject: [PATCH] Fix Samsung Power HAL (switch/case was filled with 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp -index 47fdcc9a87b..ed7d311ab9f 100644 +index dcd33366a35..298cd8c7171 100644 --- a/services/core/jni/com_android_server_power_PowerManagerService.cpp +++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp -@@ -83,7 +83,6 @@ static sp gPowerHalHidlV1_1_ = nullptr; - static sp gPowerHalAidl_ = nullptr; +@@ -89,7 +89,6 @@ static sp gPowerHalAidl_ = nullptr; static sp gLineagePowerHalV1_0_ = nullptr; + static sp gLineagePowerHalAidl_ = nullptr; static sp gSehMiscPower = nullptr; -static bool gPowerHalExists = true; static std::mutex gPowerHalMutex; + static std::mutex gLineagePowerHalMutex; - enum class HalVersion { -@@ -129,9 +128,11 @@ static HalVersion connectPowerHalLocked() { +@@ -136,9 +135,11 @@ static HalVersion connectPowerHalLocked() { gPowerHalAidlExists = false; } } @@ -33,7 +33,7 @@ index 47fdcc9a87b..ed7d311ab9f 100644 } if (gPowerHalHidlExists && gPowerHalHidlV1_0_ == nullptr) { gPowerHalHidlV1_0_ = IPowerV1_0::getService(); -@@ -484,7 +485,17 @@ static void nativeReleaseSuspendBlocker(JNIEnv *env, jclass /* clazz */, jstring +@@ -545,7 +546,17 @@ static void nativeReleaseSuspendBlocker(JNIEnv *env, jclass /* clazz */, jstring static void nativeSetInteractive(JNIEnv* /* env */, jclass /* clazz */, jboolean enable) { std::unique_lock lock(gPowerHalMutex); @@ -52,7 +52,7 @@ index 47fdcc9a87b..ed7d311ab9f 100644 case HalVersion::NONE: return; case HalVersion::HIDL_1_0: -@@ -512,14 +523,6 @@ static void nativeSetInteractive(JNIEnv* /* env */, jclass /* clazz */, jboolean +@@ -573,14 +584,6 @@ static void nativeSetInteractive(JNIEnv* /* env */, jclass /* clazz */, jboolean return; } } diff --git a/patches/platform_frameworks_base/0042-On-Samsung-R-vendor-there-is-still-the-ISehMiscPower.patch b/patches/platform_frameworks_base/0042-On-Samsung-R-vendor-there-is-still-the-ISehMiscPower.patch new file mode 100644 index 0000000..a8b33a9 --- /dev/null +++ b/patches/platform_frameworks_base/0042-On-Samsung-R-vendor-there-is-still-the-ISehMiscPower.patch @@ -0,0 +1,46 @@ +From e202b51e3522636dade7a0ede8688ccd38306468 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Sat, 13 Mar 2021 13:21:46 -0500 +Subject: [PATCH 42/45] On Samsung R vendor, there is still the ISehMiscPower + HIDL HAL, but there is also the AOSP's AIDL HAL. It was previously assumed + this wouldn't happen, fix this assumption. + +--- + ...om_android_server_power_PowerManagerService.cpp | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +diff --git a/services/core/jni/com_android_server_power_PowerManagerService.cpp b/services/core/jni/com_android_server_power_PowerManagerService.cpp +index 2ebff05d6c5c..c5d9aa94687e 100644 +--- a/services/core/jni/com_android_server_power_PowerManagerService.cpp ++++ b/services/core/jni/com_android_server_power_PowerManagerService.cpp +@@ -112,6 +112,14 @@ static HalVersion connectPowerHalLocked() { + if (!gPowerHalHidlExists && !gPowerHalAidlExists) { + return HalVersion::NONE; + } ++ if ( ++ gPowerHalAidl_ == nullptr && ++ gPowerHalHidlV1_0_ == nullptr) { ++ ALOGE("Trying to connect to Samsung Power HAL"); ++ gSehMiscPower = ISehMiscPower::getService(); ++ gPowerHalHidlV1_0_ = IPowerV1_0::getService("miscpower"); ++ ALOGE("Got miscpower = %d, SehMiscPower = %d", gPowerHalHidlV1_0_ != nullptr ? 1 : 0, gSehMiscPower ? 1 : 0); ++ } + if (gPowerHalAidlExists) { + if (!gPowerHalAidl_) { + gPowerHalAidl_ = waitForVintfService(); +@@ -123,12 +131,6 @@ static HalVersion connectPowerHalLocked() { + gPowerHalAidlExists = false; + } + } +- if (gPowerHalHidlExists && gPowerHalHidlV1_0_ == nullptr) { +- ALOGE("Trying to connect to Samsung Power HAL"); +- gSehMiscPower = ISehMiscPower::getService(); +- gPowerHalHidlV1_0_ = IPowerV1_0::getService("miscpower"); +- ALOGE("Got miscpower = %d, SehMiscPower = %d", gPowerHalHidlV1_0_ != nullptr ? 1 : 0, gSehMiscPower ? 1 : 0); +- } + if (gPowerHalHidlExists && gPowerHalHidlV1_0_ == nullptr) { + gPowerHalHidlV1_0_ = IPowerV1_0::getService(); + if (gPowerHalHidlV1_0_) { +-- +2.25.1 + diff --git a/patches/platform_frameworks_base/0043-Handle-another-type-of-Samsung-proximity-sensor-Hove.patch b/patches/platform_frameworks_base/0043-Handle-another-type-of-Samsung-proximity-sensor-Hove.patch new file mode 100644 index 0000000..99d9673 --- /dev/null +++ b/patches/platform_frameworks_base/0043-Handle-another-type-of-Samsung-proximity-sensor-Hove.patch @@ -0,0 +1,45 @@ +From a4d9278457f0035b44a4b18e07a224d0a827e446 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Sat, 20 Mar 2021 14:30:17 +0100 +Subject: [PATCH 43/45] Handle another type of Samsung proximity sensor: Hover + Proximity Sensor, with yet another behavior (seen on S20 FE) + +--- + .../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 bd41d38ad34c..0d160c37f6ea 100644 +--- a/services/core/java/com/android/server/display/DisplayPowerController.java ++++ b/services/core/java/com/android/server/display/DisplayPowerController.java +@@ -540,6 +540,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call + mProximitySensor = sensor; + } + } ++ if(mProximitySensor == null) { ++ List sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL); ++ for(Sensor sensor: sensors) { ++ if("com.samsung.sensor.hover_proximity".equals(sensor.getStringType())) ++ mProximitySensor = sensor; ++ } ++ } + if(mProximitySensor == null) { + List sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL); + for(Sensor sensor: sensors) { +@@ -2055,6 +2062,13 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call + handleProximitySensorEvent(time, positive); + return; + } ++ if("com.samsung.sensor.hover_proximity".equals(mProximitySensor.getStringType())) { ++ float v = event.values[0]; ++ boolean positive = (v >= 0.5f && v <= 4.5); ++ android.util.Log.d("PHH", "Samsung hover 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.25.1 + diff --git a/patches/platform_frameworks_base/0044-Automatically-detect-pick-up-sensor-so-that-an-overl.patch b/patches/platform_frameworks_base/0044-Automatically-detect-pick-up-sensor-so-that-an-overl.patch new file mode 100644 index 0000000..243b5d5 --- /dev/null +++ b/patches/platform_frameworks_base/0044-Automatically-detect-pick-up-sensor-so-that-an-overl.patch @@ -0,0 +1,49 @@ +From b560c2bba2559c95a97ca614fa46d542be6d2858 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Sat, 20 Mar 2021 14:31:01 +0100 +Subject: [PATCH 44/45] Automatically detect pick up sensor, so that an overlay + is required for the sole purpose of enabling pulse doze on pick up sensor + +--- + .../display/AmbientDisplayConfiguration.java | 18 +++++++++++++++++- + 1 file changed, 17 insertions(+), 1 deletion(-) + +diff --git a/core/java/android/hardware/display/AmbientDisplayConfiguration.java b/core/java/android/hardware/display/AmbientDisplayConfiguration.java +index ece5c28884fa..010fce24057d 100644 +--- a/core/java/android/hardware/display/AmbientDisplayConfiguration.java ++++ b/core/java/android/hardware/display/AmbientDisplayConfiguration.java +@@ -23,6 +23,9 @@ import android.os.SystemProperties; + import android.provider.Settings; + import android.text.TextUtils; + ++import android.hardware.SensorManager; ++import android.hardware.Sensor; ++ + import com.android.internal.R; + + /** +@@ -74,7 +77,20 @@ public class AmbientDisplayConfiguration { + + /** {@hide} */ + public boolean dozePickupSensorAvailable() { +- return mContext.getResources().getBoolean(R.bool.config_dozePulsePickup); ++ SensorManager sm = mContext.getSystemService(SensorManager.class); ++ boolean found = false; ++ if(sm == null) { ++ android.util.Log.d("PHH", "Failed getting sensor manager, can't detect pickup sensor"); ++ } else { ++ java.util.List sensors = sm.getSensorList(Sensor.TYPE_ALL); ++ for(Sensor s : sensors) { ++ if(Sensor.STRING_TYPE_PICK_UP_GESTURE.equals(s.getStringType())) { ++ found = true; ++ break; ++ } ++ } ++ } ++ return mContext.getResources().getBoolean(R.bool.config_dozePulsePickup) || found; + } + + /** {@hide} */ +-- +2.25.1 + diff --git a/patches/platform_frameworks_base/0045-Catch-broken-mainBuiltInDisplayCutoutRectApproximati.patch b/patches/platform_frameworks_base/0045-Catch-broken-mainBuiltInDisplayCutoutRectApproximati.patch new file mode 100644 index 0000000..1c541ec --- /dev/null +++ b/patches/platform_frameworks_base/0045-Catch-broken-mainBuiltInDisplayCutoutRectApproximati.patch @@ -0,0 +1,43 @@ +From 575db4c47ba68a6355774abc815f2f4c8de7a996 Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Tue, 23 Mar 2021 19:43:26 +0100 +Subject: [PATCH 45/45] Catch broken mainBuiltInDisplayCutoutRectApproximation + +Some devices (Redmi Note 9T) have: +mainBuiltInDisplayCutoutRectApproximation = @android:mainBuiltInDisplayCutout +Since mainBuiltInDisplayCutout is private, its ID is dynamic and can't +be relied upon. +This means that we'll get garbage in mainBuiltInDisplayCutoutRectApproximation +The SVG Path parser will fail, triggering an exception. + +Fix it by catching it, and parsing mainBuiltInDisplayCutout instead +--- + core/java/android/view/DisplayCutout.java | 12 +++++++++--- + 1 file changed, 9 insertions(+), 3 deletions(-) + +diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java +index b4863f9c6dd0..e1d57e6869aa 100644 +--- a/core/java/android/view/DisplayCutout.java ++++ b/core/java/android/view/DisplayCutout.java +@@ -681,9 +681,15 @@ public final class DisplayCutout { + * @hide + */ + public static DisplayCutout fromResourcesRectApproximation(Resources res, int displayWidth, int displayHeight) { +- return fromSpec(res.getString(R.string.config_mainBuiltInDisplayCutoutRectApproximation), +- displayWidth, displayHeight, DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT, +- loadWaterfallInset(res)); ++ try { ++ return fromSpec(res.getString(R.string.config_mainBuiltInDisplayCutoutRectApproximation), ++ displayWidth, displayHeight, DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT, ++ loadWaterfallInset(res)); ++ } catch(java.lang.IllegalArgumentException e) { ++ return fromSpec(res.getString(R.string.config_mainBuiltInDisplayCutout), ++ displayWidth, displayHeight, DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT, ++ loadWaterfallInset(res)); ++ } + } + + /** +-- +2.25.1 + diff --git a/patches/platform_frameworks_native/0014-Remove-Samsung-system-permission-on-sensors.patch b/patches/platform_frameworks_native/0014-Remove-Samsung-system-permission-on-sensors.patch new file mode 100644 index 0000000..5a66cfd --- /dev/null +++ b/patches/platform_frameworks_native/0014-Remove-Samsung-system-permission-on-sensors.patch @@ -0,0 +1,37 @@ +From 862798495297d8cb0d02951d533644929570ae1f Mon Sep 17 00:00:00 2001 +From: Pierre-Hugues Husson +Date: Wed, 31 Mar 2021 23:36:03 +0200 +Subject: [PATCH 14/14] Remove Samsung system permission on sensors + +--- + libs/sensor/Sensor.cpp | 1 + + libs/sensor/include/sensor/Sensor.h | 1 + + 2 files changed, 2 insertions(+) + +diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp +index 9d817ae0b..aeae0589d 100644 +--- a/libs/sensor/Sensor.cpp ++++ b/libs/sensor/Sensor.cpp +@@ -405,6 +405,7 @@ const String8& Sensor::getStringType() const { + } + + const String8& Sensor::getRequiredPermission() const { ++ if (mRequiredPermission == "com.samsung.permission.SSENSOR") return mFakeRequiredPermission; + return mRequiredPermission; + } + +diff --git a/libs/sensor/include/sensor/Sensor.h b/libs/sensor/include/sensor/Sensor.h +index 324d44310..2d4446767 100644 +--- a/libs/sensor/include/sensor/Sensor.h ++++ b/libs/sensor/include/sensor/Sensor.h +@@ -125,6 +125,7 @@ private: + uint32_t mFifoMaxEventCount; + String8 mStringType; + String8 mRequiredPermission; ++ String8 mFakeRequiredPermission; + bool mRequiredPermissionRuntime = false; + int32_t mRequiredAppOp; + int32_t mMaxDelay; +-- +2.25.1 + diff --git a/patches/platform_system_bt/0004-Changed-HID_DEV_MTU_SIZE-from-64-to-512.patch b/patches/platform_system_bt/0004-Changed-HID_DEV_MTU_SIZE-from-64-to-512.patch new file mode 100644 index 0000000..b8180ad --- /dev/null +++ b/patches/platform_system_bt/0004-Changed-HID_DEV_MTU_SIZE-from-64-to-512.patch @@ -0,0 +1,29 @@ +From aa8436c122f42c043bfd709f5aedbe7dcc3f7e9c Mon Sep 17 00:00:00 2001 +From: Danny Trunk +Date: Mon, 7 Sep 2020 16:44:24 +0200 +Subject: [PATCH 4/4] Changed HID_DEV_MTU_SIZE from 64 to 512 + +Changed HID_DEV_MTU_SIZE for Amiibo Backups over Bluetooth support + +Signed-off-by: Danny Trunk +Change-Id: Ie4226b477d040177108729cb25e711fd35bfe0f2 +--- + internal_include/bt_target.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/internal_include/bt_target.h b/internal_include/bt_target.h +index 57bd69e9f..389acda68 100644 +--- a/internal_include/bt_target.h ++++ b/internal_include/bt_target.h +@@ -1143,7 +1143,7 @@ + #endif + + #ifndef HID_DEV_MTU_SIZE +-#define HID_DEV_MTU_SIZE 64 ++#define HID_DEV_MTU_SIZE 512 + #endif + + #ifndef HID_DEV_FLUSH_TO +-- +2.25.1 +