From 6bcbafcdc011f649eadb198c3c7c26d378384e24 Mon Sep 17 00:00:00 2001 From: LuK1337 Date: Fri, 10 Feb 2017 16:27:22 +0100 Subject: [PATCH] Initial commit --- ...command-to-access-OverlayManagerServ.patch | 44 + ...agging-resources-as-OK-to-overlay-1-.patch | 474 +++ ...oduce-the-OverlayManagerService-2-11.patch | 3082 +++++++++++++++++ ...-OverlayManagerService-into-framewor.patch | 1473 ++++++++ ..._REPLACING-correctly-in-ACTION_PACKA.patch | 51 + ...press-print-for-padded-resources-5-1.patch | 29 + ...mory-leak-during-idmap-creation-6-11.patch | 67 + ...-N-installd-add-command-rmidmap-7-11.patch | 38 + ...able-Zygote-preloaded-drawables-8-11.patch | 45 + ...ce-on-boot-through-OverlayManagerSer.patch | 78 + ...force-code-policy-limiting-overlay-i.patch | 29 + ...-multi-target-enable-disable-and-dis.patch | 470 +++ ...mes-Expose-resolver-hardcoded-colors.patch | 200 ++ ...ersive-cling-colors-to-be-fully-them.patch | 81 + ...-Permission-Icons-to-be-fully-themed.patch | 44 + ...low-Navbar-ripple-color-to-be-themed.patch | 73 + ...QS-edit-item-decoration-background-c.patch | 58 + ...lpha-for-notification-shade-bg-color.patch | 64 + ...se-various-QuickSettings-text-colors.patch | 55 + ...pose-a-bool-to-disable-dynamic-color.patch | 162 + ...amic-colors-bool-compatible-with-OMS.patch | 48 + ...-of-doze-notification-color-inversio.patch | 66 + ...tible-Ambient-notification-inversion.patch | 64 + ...Use-own-drawables-for-QS-expand-icon.patch | 505 +++ ...-dynamic-theme-BootAnimation-support.patch | 81 + ...tras-Add-dynamic-theme-fonts-support.patch | 600 ++++ ...rvice-Allow-system-effect-sounds-to-.patch | 79 + ...onsState-add-filter-for-Substratum-o.patch | 53 + ...onsState-add-filter-for-Substratum-i.patch | 41 + .../base/0029-Themes-Expose-QS-battery.patch | 55 + ...DIFY_OVERLAYS-permission-for-user-ap.patch | 120 + ...031-SystemUI-Expose-switch-bar-title.patch | 61 + ...es-Expose-manifest-styles-for-themes.patch | 151 + ...de-and-files-under-data-system-theme.patch | 40 + ...cale-even-if-invert-boolean-is-false.patch | 45 + ...5-Expose-external-qs-tile-tint-color.patch | 42 + ...hics-ADB-N-icon-compatible-with-OMS7.patch | 45 + ...ernal-QS-tiles-tint-mode-to-SRC_ATOP.patch | 43 + ...eyguard-affordance-circle-background.patch | 38 + ...1-OMS-N-installd-add-command-rmidmap.patch | 87 + ...e-hardcoded-layout-and-styles-colors.patch | 188 + ...e-hardcoded-contact-tile-text-colors.patch | 87 + ...Expose-hard-coded-background-in-java.patch | 53 + .../0002-Themes-Expose-all-elevations.patch | 53 + ...ke-dialpad-seperator-line-theme-able.patch | 69 + ...se-dashboard-category-and-tile-color.patch | 127 + ...ettings-Expose-condition-card-colors.patch | 118 + ...Settings-Expose-storage-summary-text.patch | 124 + ...gs-Expose-gesture-settings-switchbar.patch | 44 + ...-Settings-Expose-storage-icon-colors.patch | 121 + ...Expose-LinearColorBar-default-colors.patch | 68 + ...ps-show-hide-Substratum-overlays-2-2.patch | 159 + ...ow-hide-Substratum-icon-overlays-2-2.patch | 151 + ...xclude-overlays-from-the-app-counter.patch | 49 + ...de-overlays-when-no-overlay-installe.patch | 86 + ...se-color-for-external-settings-icons.patch | 77 + ...se-dashboard-category-padding-bottom.patch | 50 + ...13-Expose-switchbar-background-color.patch | 77 + ...bluetooth-pin-confirm-dialog-text-co.patch | 60 + ...-settings-icon-tint-mode-to-SRC_ATOP.patch | 42 + patch.sh | 20 + ...-service-overlay-to-service_contexts.patch | 59 + 62 files changed, 10763 insertions(+) create mode 100644 build/0001-OMS-N-adb-shell-command-to-access-OverlayManagerServ.patch create mode 100644 frameworks/base/0001-OMS7-N-Support-tagging-resources-as-OK-to-overlay-1-.patch create mode 100644 frameworks/base/0002-OMS7-N-Introduce-the-OverlayManagerService-2-11.patch create mode 100644 frameworks/base/0003-OMS7-N-Integrate-OverlayManagerService-into-framewor.patch create mode 100644 frameworks/base/0004-OMS7-N-Set-EXTRA_REPLACING-correctly-in-ACTION_PACKA.patch create mode 100644 frameworks/base/0005-OMS7-N-idmap-suppress-print-for-padded-resources-5-1.patch create mode 100644 frameworks/base/0006-OMS7-N-Fix-memory-leak-during-idmap-creation-6-11.patch create mode 100644 frameworks/base/0007-OMS7-N-installd-add-command-rmidmap-7-11.patch create mode 100644 frameworks/base/0008-OMS7-N-Disable-Zygote-preloaded-drawables-8-11.patch create mode 100644 frameworks/base/0009-OMS7-N-Persistence-on-boot-through-OverlayManagerSer.patch create mode 100644 frameworks/base/0010-OMS7-N-Do-not-enforce-code-policy-limiting-overlay-i.patch create mode 100644 frameworks/base/0011-OMS7-N-Implement-multi-target-enable-disable-and-dis.patch create mode 100644 frameworks/base/0012-Themes-Expose-resolver-hardcoded-colors.patch create mode 100644 frameworks/base/0013-Themes-Allow-Immersive-cling-colors-to-be-fully-them.patch create mode 100644 frameworks/base/0014-Themes-Allow-Permission-Icons-to-be-fully-themed.patch create mode 100644 frameworks/base/0015-Themes-Allow-Navbar-ripple-color-to-be-themed.patch create mode 100644 frameworks/base/0016-SystemUI-Expose-QS-edit-item-decoration-background-c.patch create mode 100644 frameworks/base/0017-Allow-custom-alpha-for-notification-shade-bg-color.patch create mode 100644 frameworks/base/0018-Themes-Expose-various-QuickSettings-text-colors.patch create mode 100644 frameworks/base/0019-Notifications-Expose-a-bool-to-disable-dynamic-color.patch create mode 100644 frameworks/base/0020-Notification-dynamic-colors-bool-compatible-with-OMS.patch create mode 100644 frameworks/base/0021-Allow-prevention-of-doze-notification-color-inversio.patch create mode 100644 frameworks/base/0022-OMS7-compatible-Ambient-notification-inversion.patch create mode 100644 frameworks/base/0023-SystemUI-Use-own-drawables-for-QS-expand-icon.patch create mode 100644 frameworks/base/0024-N-Extras-Add-dynamic-theme-BootAnimation-support.patch create mode 100644 frameworks/base/0025-N-Extras-Add-dynamic-theme-fonts-support.patch create mode 100644 frameworks/base/0026-N-Extras-AudioService-Allow-system-effect-sounds-to-.patch create mode 100644 frameworks/base/0027-OMS7-N-ApplicationsState-add-filter-for-Substratum-o.patch create mode 100644 frameworks/base/0028-OMS7-N-ApplicationsState-add-filter-for-Substratum-i.patch create mode 100644 frameworks/base/0029-Themes-Expose-QS-battery.patch create mode 100644 frameworks/base/0030-OMS-Introduce-MODIFY_OVERLAYS-permission-for-user-ap.patch create mode 100644 frameworks/base/0031-SystemUI-Expose-switch-bar-title.patch create mode 100644 frameworks/base/0032-Themes-Expose-manifest-styles-for-themes.patch create mode 100644 frameworks/base/0033-OMS-StrictMode-and-files-under-data-system-theme.patch create mode 100644 frameworks/base/0034-doze-allow-grayscale-even-if-invert-boolean-is-false.patch create mode 100644 frameworks/base/0035-Expose-external-qs-tile-tint-color.patch create mode 100644 frameworks/base/0036-graphics-ADB-N-icon-compatible-with-OMS7.patch create mode 100644 frameworks/base/0037-Set-external-QS-tiles-tint-mode-to-SRC_ATOP.patch create mode 100644 frameworks/base/0038-Themes-Expose-Keyguard-affordance-circle-background.patch create mode 100644 frameworks/native/0001-OMS-N-installd-add-command-rmidmap.patch create mode 100644 packages/apps/Contacts/0001-Themes-Expose-hardcoded-layout-and-styles-colors.patch create mode 100644 packages/apps/ContactsCommon/0001-Themes-Expose-hardcoded-contact-tile-text-colors.patch create mode 100644 packages/apps/ExactCalculator/0001-Themes-Expose-hard-coded-background-in-java.patch create mode 100644 packages/apps/ExactCalculator/0002-Themes-Expose-all-elevations.patch create mode 100644 packages/apps/PhoneCommon/0001-Themes-Make-dialpad-seperator-line-theme-able.patch create mode 100644 packages/apps/Settings/0001-Settings-Expose-dashboard-category-and-tile-color.patch create mode 100644 packages/apps/Settings/0002-Settings-Expose-condition-card-colors.patch create mode 100644 packages/apps/Settings/0003-Settings-Expose-storage-summary-text.patch create mode 100644 packages/apps/Settings/0004-Settings-Expose-gesture-settings-switchbar.patch create mode 100644 packages/apps/Settings/0005-Settings-Expose-storage-icon-colors.patch create mode 100644 packages/apps/Settings/0006-Settings-Expose-LinearColorBar-default-colors.patch create mode 100644 packages/apps/Settings/0007-OMS7-N-Apps-show-hide-Substratum-overlays-2-2.patch create mode 100644 packages/apps/Settings/0008-OMS7-N-Apps-show-hide-Substratum-icon-overlays-2-2.patch create mode 100644 packages/apps/Settings/0009-Exclude-overlays-from-the-app-counter.patch create mode 100644 packages/apps/Settings/0010-Hide-the-show-hide-overlays-when-no-overlay-installe.patch create mode 100644 packages/apps/Settings/0011-Expose-color-for-external-settings-icons.patch create mode 100644 packages/apps/Settings/0012-Expose-dashboard-category-padding-bottom.patch create mode 100644 packages/apps/Settings/0013-Expose-switchbar-background-color.patch create mode 100644 packages/apps/Settings/0014-Settings-Expose-bluetooth-pin-confirm-dialog-text-co.patch create mode 100644 packages/apps/Settings/0015-Set-external-settings-icon-tint-mode-to-SRC_ATOP.patch create mode 100755 patch.sh create mode 100644 system/sepolicy/0001-OMS-N-Add-service-overlay-to-service_contexts.patch diff --git a/build/0001-OMS-N-adb-shell-command-to-access-OverlayManagerServ.patch b/build/0001-OMS-N-adb-shell-command-to-access-OverlayManagerServ.patch new file mode 100644 index 0000000..80b0242 --- /dev/null +++ b/build/0001-OMS-N-adb-shell-command-to-access-OverlayManagerServ.patch @@ -0,0 +1,44 @@ +From 9c60c359e98e9c862411c08e72bb7a1520b736d1 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= +Date: Mon, 27 Jul 2015 12:24:40 +0200 +Subject: [PATCH] OMS-N: adb shell command to access OverlayManagerService + +Add a command to communicate with the OverlayManagerService for +debugging purposes. This mirrors the am and pm commands. + +This commit restores the functionality after the Nougat rebase from +Sony. + +Example use: + $ adb shell om list + com.android.systemui + [ ] com.test.awesome-home-button + + $ adb shell om enable com.test.awesome-home-button + + $ adb shell om list + com.android.systemui + [x] com.test.awesome-home-button + +Co-authored-by: Martin Wallgren +Signed-off-by: Zoran Jovanovic +Change-Id: If424b8ef6052e4121902b630279c0ebaf416203c +--- + target/product/base.mk | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/target/product/base.mk b/target/product/base.mk +index dcd48e7..a12f939 100644 +--- a/target/product/base.mk ++++ b/target/product/base.mk +@@ -108,6 +108,7 @@ PRODUCT_PACKAGES += \ + mtpd \ + ndc \ + netd \ ++ om \ + ping \ + ping6 \ + platform.xml \ +-- +2.9.3 + diff --git a/frameworks/base/0001-OMS7-N-Support-tagging-resources-as-OK-to-overlay-1-.patch b/frameworks/base/0001-OMS7-N-Support-tagging-resources-as-OK-to-overlay-1-.patch new file mode 100644 index 0000000..342173e --- /dev/null +++ b/frameworks/base/0001-OMS7-N-Support-tagging-resources-as-OK-to-overlay-1-.patch @@ -0,0 +1,474 @@ +From 75d5463e848764c3255c32d126415faceca62023 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= +Date: Tue, 15 Dec 2015 16:08:31 +0100 +Subject: [PATCH 01/38] OMS7-N: Support tagging resources as OK to overlay + [1/11] + +This will allow applications to have a resource xml defining what +resources that are safe to overlay by third party overlay packages. +The format of the tag is and it will +result in the FLAG_OVELAY being set on the resource entry. + +An overlay package with resources that are not tagged as OK +to overlay by the target application, is considered to be a dangerous +overlay. + +Idmaps generated for dangerous overlays will be flagged as dangerous in +the idmap header. It is still possible to use both idmap and overlays that +are dangerous, but it might not be advisable. + +The intention is to allow dangerous overlays to be used if +they are pre-installed or if the signature of the overlay package +matches the signature of the target package. + +Change-Id: I08d11de845c1679017798ea1636ef4c36f820d8e +--- + cmds/idmap/create.cpp | 2 +- + cmds/idmap/inspect.cpp | 6 +++ + include/androidfw/ResourceTypes.h | 10 ++-- + libs/androidfw/AssetManager.cpp | 2 +- + libs/androidfw/ResourceTypes.cpp | 27 ++++++++--- + tools/aapt/ResourceTable.cpp | 96 ++++++++++++++++++++++++++++++++++++++- + tools/aapt/ResourceTable.h | 36 ++++++++++++++- + 7 files changed, 164 insertions(+), 15 deletions(-) + +diff --git a/cmds/idmap/create.cpp b/cmds/idmap/create.cpp +index c13d318..8656b0e 100644 +--- a/cmds/idmap/create.cpp ++++ b/cmds/idmap/create.cpp +@@ -106,7 +106,7 @@ fail: + + uint32_t cached_target_crc, cached_overlay_crc; + String8 cached_target_path, cached_overlay_path; +- if (!ResTable::getIdmapInfo(buf, N, NULL, &cached_target_crc, &cached_overlay_crc, ++ if (!ResTable::getIdmapInfo(buf, N, NULL, NULL, &cached_target_crc, &cached_overlay_crc, + &cached_target_path, &cached_overlay_path)) { + return true; + } +diff --git a/cmds/idmap/inspect.cpp b/cmds/idmap/inspect.cpp +index f6afc85..924090f 100644 +--- a/cmds/idmap/inspect.cpp ++++ b/cmds/idmap/inspect.cpp +@@ -192,6 +192,12 @@ namespace { + if (err != NO_ERROR) { + return err; + } ++ print("", "dangerous", i, ""); ++ ++ err = buf.nextUint32(&i); ++ if (err != NO_ERROR) { ++ return err; ++ } + print("", "base crc", i, ""); + + err = buf.nextUint32(&i); +diff --git a/include/androidfw/ResourceTypes.h b/include/androidfw/ResourceTypes.h +index 12a6b0f..6094a57 100644 +--- a/include/androidfw/ResourceTypes.h ++++ b/include/androidfw/ResourceTypes.h +@@ -1382,7 +1382,11 @@ struct ResTable_entry + // If set, this is a weak resource and may be overriden by strong + // resources of the same name/type. This is only useful during + // linking with other resource tables. +- FLAG_WEAK = 0x0004 ++ FLAG_WEAK = 0x0004, ++ // If set, this resource has been declared OK to overlay, so overlay ++ // packages may be added to the resource table to provide alternative ++ // resource values. ++ FLAG_OVERLAY = 0x0008, + }; + uint16_t flags; + +@@ -1861,14 +1865,14 @@ public: + const char* targetPath, const char* overlayPath, + void** outData, size_t* outSize) const; + +- static const size_t IDMAP_HEADER_SIZE_BYTES = 4 * sizeof(uint32_t) + 2 * 256; ++ static const size_t IDMAP_HEADER_SIZE_BYTES = 5 * sizeof(uint32_t) + 2 * 256; + + // Retrieve idmap meta-data. + // + // This function only requires the idmap header (the first + // IDMAP_HEADER_SIZE_BYTES) bytes of an idmap file. + static bool getIdmapInfo(const void* idmap, size_t size, +- uint32_t* pVersion, ++ uint32_t* pVersion, uint32_t* pDangerous, + uint32_t* pTargetCrc, uint32_t* pOverlayCrc, + String8* pTargetPath, String8* pOverlayPath); + +diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp +index fdd0caf..edc625b 100644 +--- a/libs/androidfw/AssetManager.cpp ++++ b/libs/androidfw/AssetManager.cpp +@@ -252,7 +252,7 @@ bool AssetManager::addOverlayPath(const String8& packagePath, int32_t* cookie) + String8 targetPath; + String8 overlayPath; + if (!ResTable::getIdmapInfo(idmap->getBuffer(false), idmap->getLength(), +- NULL, NULL, NULL, &targetPath, &overlayPath)) { ++ NULL, NULL, NULL, NULL, &targetPath, &overlayPath)) { + ALOGW("failed to read idmap file %s\n", idmapPath.string()); + delete idmap; + return false; +diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp +index e8c6fcf..022f19e 100644 +--- a/libs/androidfw/ResourceTypes.cpp ++++ b/libs/androidfw/ResourceTypes.cpp +@@ -59,7 +59,7 @@ namespace android { + #endif + + #define IDMAP_MAGIC 0x504D4449 +-#define IDMAP_CURRENT_VERSION 0x00000001 ++#define IDMAP_CURRENT_VERSION 0x00000002 + + #define APP_PACKAGE_ID 0x7f + #define CMSDK_PACKAGE_ID 0x3f +@@ -6601,6 +6601,7 @@ status_t ResTable::createIdmap(const ResTable& overlay, + return UNKNOWN_ERROR; + } + ++ bool isDangerous = false; + KeyedVector map; + + // overlaid packages are assumed to contain only one package group +@@ -6675,6 +6676,13 @@ status_t ResTable::createIdmap(const ResTable& overlay, + } + } + typeMap.entryMap.add(Res_GETENTRY(overlayResID)); ++ ++ Entry entry; ++ if (getEntry(pg, typeIndex, entryIndex, NULL, &entry)) { ++ return UNKNOWN_ERROR; ++ } ++ isDangerous = isDangerous || ++ ((dtohs(entry.entry->flags) & ResTable_entry::FLAG_OVERLAY) == 0); + } + + if (!typeMap.entryMap.isEmpty()) { +@@ -6697,6 +6705,7 @@ status_t ResTable::createIdmap(const ResTable& overlay, + uint32_t* data = (uint32_t*)*outData; + *data++ = htodl(IDMAP_MAGIC); + *data++ = htodl(IDMAP_CURRENT_VERSION); ++ *data++ = htodl(isDangerous ? 1 : 0); + *data++ = htodl(targetCrc); + *data++ = htodl(overlayCrc); + const char* paths[] = { targetPath, overlayPath }; +@@ -6737,7 +6746,7 @@ status_t ResTable::createIdmap(const ResTable& overlay, + } + + bool ResTable::getIdmapInfo(const void* idmap, size_t sizeBytes, +- uint32_t* pVersion, ++ uint32_t* pVersion, uint32_t* pDangerous, + uint32_t* pTargetCrc, uint32_t* pOverlayCrc, + String8* pTargetPath, String8* pOverlayPath) + { +@@ -6748,17 +6757,20 @@ bool ResTable::getIdmapInfo(const void* idmap, size_t sizeBytes, + if (pVersion) { + *pVersion = dtohl(map[1]); + } ++ if (pDangerous) { ++ *pDangerous = dtohl(map[2]); ++ } + if (pTargetCrc) { +- *pTargetCrc = dtohl(map[2]); ++ *pTargetCrc = dtohl(map[3]); + } + if (pOverlayCrc) { +- *pOverlayCrc = dtohl(map[3]); ++ *pOverlayCrc = dtohl(map[4]); + } + if (pTargetPath) { +- pTargetPath->setTo(reinterpret_cast(map + 4)); ++ pTargetPath->setTo(reinterpret_cast(map + 5)); + } + if (pOverlayPath) { +- pOverlayPath->setTo(reinterpret_cast(map + 4 + 256 / sizeof(uint32_t))); ++ pOverlayPath->setTo(reinterpret_cast(map + 5 + 256 / sizeof(uint32_t))); + } + return true; + } +@@ -7085,6 +7097,9 @@ void ResTable::print(bool inclValues) const + if ((dtohs(ent->flags)&ResTable_entry::FLAG_PUBLIC) != 0) { + printf(" (PUBLIC)"); + } ++ if ((dtohs(ent->flags)&ResTable_entry::FLAG_OVERLAY) != 0) { ++ printf(" (OVERLAY)"); ++ } + printf("\n"); + + if (inclValues) { +diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp +index a1b1879..b2d0480 100644 +--- a/tools/aapt/ResourceTable.cpp ++++ b/tools/aapt/ResourceTable.cpp +@@ -800,6 +800,7 @@ status_t compileResourceFile(Bundle* bundle, + const String16 string_array16("string-array"); + const String16 integer_array16("integer-array"); + const String16 public16("public"); ++ const String16 overlay16("overlay"); + const String16 public_padding16("public-padding"); + const String16 private_symbols16("private-symbols"); + const String16 java_symbol16("java-symbol"); +@@ -1003,6 +1004,41 @@ status_t compileResourceFile(Bundle* bundle, + } + continue; + ++ } else if (strcmp16(block.getElementName(&len), overlay16.string()) == 0) { ++ SourcePos srcPos(in->getPrintableSource(), block.getLineNumber()); ++ ++ String16 type; ++ const ssize_t typeIdx = block.indexOfAttribute(NULL, "type"); ++ if (typeIdx < 0) { ++ srcPos.error("A 'type' attribute is required for \n"); ++ hasErrors = localHasErrors = true; ++ } ++ type = String16(block.getAttributeStringValue(typeIdx, &len)); ++ ++ String16 name; ++ const ssize_t nameIdx = block.indexOfAttribute(NULL, "name"); ++ if (nameIdx < 0) { ++ srcPos.error("A 'name' attribute is required for \n"); ++ hasErrors = localHasErrors = true; ++ } ++ name = String16(block.getAttributeStringValue(nameIdx, &len)); ++ ++ if (!localHasErrors) { ++ err = outTable->addOverlay(srcPos, myPackage, type, name); ++ if (err < NO_ERROR) { ++ hasErrors = localHasErrors = true; ++ } ++ } ++ ++ while ((code=block.next()) != ResXMLTree::END_DOCUMENT && code != ResXMLTree::BAD_DOCUMENT) { ++ if (code == ResXMLTree::END_TAG) { ++ if (strcmp16(block.getElementName(&len), overlay16.string()) == 0) { ++ break; ++ } ++ } ++ } ++ continue; ++ + } else if (strcmp16(block.getElementName(&len), public_padding16.string()) == 0) { + SourcePos srcPos(in->getPrintableSource(), block.getLineNumber()); + +@@ -1865,6 +1901,29 @@ status_t ResourceTable::addPublic(const SourcePos& sourcePos, + return t->addPublic(sourcePos, name, ident); + } + ++status_t ResourceTable::addOverlay(const SourcePos& sourcePos, ++ const String16& package, ++ const String16& type, ++ const String16& name) ++{ ++ uint32_t rid = mAssets->getIncludedResources() ++ .identifierForName(name.string(), name.size(), ++ type.string(), type.size(), ++ package.string(), package.size()); ++ if (rid != 0) { ++ sourcePos.error("Error declaring overlay resource %s/%s for included package %s\n", ++ String8(type).string(), String8(name).string(), ++ String8(package).string()); ++ return UNKNOWN_ERROR; ++ } ++ ++ sp t = getType(package, type, sourcePos); ++ if (t == NULL) { ++ return UNKNOWN_ERROR; ++ } ++ return t->addOverlay(sourcePos, name); ++} ++ + status_t ResourceTable::addEntry(const SourcePos& sourcePos, + const String16& package, + const String16& type, +@@ -2665,6 +2724,11 @@ status_t ResourceTable::assignResourceIds() + firstError = err; + } + ++ err = t->applyOverlay(); ++ if (err != NO_ERROR && firstError == NO_ERROR) { ++ firstError = err; ++ } ++ + const size_t N = t->getOrderedConfigs().size(); + t->setIndex(ti + 1 + typeIdOffset); + +@@ -3254,7 +3318,7 @@ status_t ResourceTable::flatten(Bundle* bundle, const sp& + index[ei] = htodl(data->getSize()-typeStart-typeSize); + + // Create the entry. +- ssize_t amt = e->flatten(bundle, data, cl->getPublic()); ++ ssize_t amt = e->flatten(bundle, data, cl->getPublic(), cl->getOverlay()); + if (amt < 0) { + return amt; + } +@@ -3759,7 +3823,8 @@ status_t ResourceTable::Entry::remapStringValue(StringPool* strings) + return NO_ERROR; + } + +-ssize_t ResourceTable::Entry::flatten(Bundle* /* bundle */, const sp& data, bool isPublic) ++ssize_t ResourceTable::Entry::flatten(Bundle* /* bundle */, const sp& data, bool isPublic, ++ bool isOverlay) + { + size_t amt = 0; + ResTable_entry header; +@@ -3772,6 +3837,9 @@ ssize_t ResourceTable::Entry::flatten(Bundle* /* bundle */, const sp& + if (isPublic) { + header.flags |= htods(header.FLAG_PUBLIC); + } ++ if (isOverlay) { ++ header.flags |= htods(header.FLAG_OVERLAY); ++ } + header.key.index = htodl(mNameIndex); + if (ty != TYPE_BAG) { + status_t err = data->writeData(&header, sizeof(header)); +@@ -3912,6 +3980,13 @@ status_t ResourceTable::Type::addPublic(const SourcePos& sourcePos, + return NO_ERROR; + } + ++status_t ResourceTable::Type::addOverlay(const SourcePos& sourcePos, ++ const String16& name) ++{ ++ mOverlay.add(name, Overlay(sourcePos, String16())); ++ return NO_ERROR; ++} ++ + void ResourceTable::Type::canAddEntry(const String16& name) + { + mCanAddEntries.add(name); +@@ -4014,6 +4089,7 @@ sp ResourceTable::Type::removeEntry(const String16& e + } + + mPublic.removeItem(entry); ++ mOverlay.removeItem(entry); + return removed; + } + +@@ -4115,6 +4191,22 @@ status_t ResourceTable::Type::applyPublicEntryOrder() + return hasError ? STATUST(UNKNOWN_ERROR) : NO_ERROR; + } + ++status_t ResourceTable::Type::applyOverlay() { ++ const size_t N = mOverlay.size(); ++ const size_t M = mOrderedConfigs.size(); ++ for (size_t i = 0; i < N; i++) { ++ const String16& name = mOverlay.keyAt(i); ++ for (size_t j = 0; j < M; j++) { ++ sp e = mOrderedConfigs.itemAt(j); ++ if (e->getName() == name) { ++ e->setOverlay(true); ++ break; ++ } ++ } ++ } ++ return NO_ERROR; ++} ++ + ResourceTable::Package::Package(const String16& name, size_t packageId) + : mName(name), mPackageId(packageId), + mTypeStringsMapping(0xffffffff), +diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h +index 54d56cf..7e9b044 100644 +--- a/tools/aapt/ResourceTable.h ++++ b/tools/aapt/ResourceTable.h +@@ -139,6 +139,11 @@ public: + const String16& name, + const uint32_t ident); + ++ status_t addOverlay(const SourcePos& pos, ++ const String16& package, ++ const String16& type, ++ const String16& name); ++ + status_t addEntry(const SourcePos& pos, + const String16& package, + const String16& type, +@@ -388,7 +393,7 @@ public: + + status_t remapStringValue(StringPool* strings); + +- ssize_t flatten(Bundle*, const sp& data, bool isPublic); ++ ssize_t flatten(Bundle*, const sp& data, bool isPublic, bool isOverlay); + + const SourcePos& getPos() const { return mPos; } + +@@ -407,7 +412,7 @@ public: + class ConfigList : public RefBase { + public: + ConfigList(const String16& name, const SourcePos& pos) +- : mName(name), mPos(pos), mPublic(false), mEntryIndex(-1) { } ++ : mName(name), mPos(pos), mPublic(false), mOverlay(false), mEntryIndex(-1) { } + virtual ~ConfigList() { } + + String16 getName() const { return mName; } +@@ -427,6 +432,9 @@ public: + bool getPublic() const { return mPublic; } + void setPublicSourcePos(const SourcePos& pos) { mPublicSourcePos = pos; } + const SourcePos& getPublicSourcePos() { return mPublicSourcePos; } ++ ++ void setOverlay(bool o) { mOverlay = o; } ++ bool getOverlay() const { return mOverlay; } + + void addEntry(const ResTable_config& config, const sp& entry) { + mEntries.add(config, entry); +@@ -440,6 +448,7 @@ public: + String16 mTypeComment; + bool mPublic; + SourcePos mPublicSourcePos; ++ bool mOverlay; + int32_t mEntryIndex; + DefaultKeyedVector > mEntries; + }; +@@ -467,6 +476,24 @@ public: + String16 comment; + uint32_t ident; + }; ++ ++ class Overlay { ++ public: ++ Overlay() : sourcePos() { } ++ Overlay(const SourcePos& pos, const String16& _comment) ++ : sourcePos(pos), comment(_comment) { } ++ Overlay(const Overlay& o) : sourcePos(o.sourcePos), comment(o.comment) { } ++ ~Overlay() { } ++ ++ Overlay& operator=(const Overlay& o) { ++ sourcePos = o.sourcePos; ++ comment = o.comment; ++ return *this; ++ } ++ ++ SourcePos sourcePos; ++ String16 comment; ++ }; + + class Type : public RefBase { + public: +@@ -478,6 +505,9 @@ public: + status_t addPublic(const SourcePos& pos, + const String16& name, + const uint32_t ident); ++ ++ status_t addOverlay(const SourcePos& pos, ++ const String16& name); + + void canAddEntry(const String16& name); + +@@ -505,6 +535,7 @@ public: + void setIndex(int32_t index) { mIndex = index; } + + status_t applyPublicEntryOrder(); ++ status_t applyOverlay(); + + const DefaultKeyedVector >& getConfigs() const { return mConfigs; } + const Vector >& getOrderedConfigs() const { return mOrderedConfigs; } +@@ -516,6 +547,7 @@ public: + String16 mName; + SourcePos* mFirstPublicSourcePos; + DefaultKeyedVector mPublic; ++ DefaultKeyedVector mOverlay; + DefaultKeyedVector > mConfigs; + Vector > mOrderedConfigs; + SortedVector mCanAddEntries; +-- +2.9.3 + diff --git a/frameworks/base/0002-OMS7-N-Introduce-the-OverlayManagerService-2-11.patch b/frameworks/base/0002-OMS7-N-Introduce-the-OverlayManagerService-2-11.patch new file mode 100644 index 0000000..919e42c --- /dev/null +++ b/frameworks/base/0002-OMS7-N-Introduce-the-OverlayManagerService-2-11.patch @@ -0,0 +1,3082 @@ +From 90815d5667efdc258011508a5599a45bd3d66756 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= +Date: Tue, 15 Dec 2015 16:40:23 +0100 +Subject: [PATCH 02/38] OMS7-N: Introduce the OverlayManagerService [2/11] + +Add a new system service to manage Runtime Resource Overlays. This will +offload the PackageManagerService and allow administration of overlay +packages while affected packages continue to execute. + +Overlays can be enabled or disabled during runtime. To the running +application the change is invisible. Technically, the overlay is added +or removed from the running application similarly to how multi-window +affects resources. + +Before an overlay can be enabled it has to be approved. This happens +during package installation or upgrade and if an error occurs, the +overlay is marked as not approved. + +The system performs the following security checks, in +the order listed, to determine if an overlay can be approved: + + 1. Is the overlay package pre-installed? If yes, OK to use. + + 2. Are the target and overlay packages signed with the same + certificate? If yes, OK to use. + + 3. Is the overlay dangerous? If no, OK to use. + +An overlay is said to be dangerous if it modifies resources not +explicitly specified by the target as OK to overlay. (This is done by +adding tags to the target's resources.) + +The decision to approve/not approve an overlay is not re-evaluated until +the next time either the overlay or its target package is changed. + +The order in which a set of overlays is loaded may also be changed +during runtime. The underlying mechanics are the same as for when an +overlay is enabled or disabled. + +When an overlay changes state, e.g. becomes enabled, the +OverlayManagerService will broadcast one of the new intents +android.intent.action.OVERLAY_ADDED, *_CHANGED, *_REMOVED or +*.OVERLAYS_REORDERED. + +Clients that wish to read information about overlays for users other +than themselves are required to hold the +android.permission.INTERACT_ACROSS_USERS_FULL permission. This mirrors +the protection level of PackageManager.getPackageInfo. + +Clients that wish to change the information are required to +hold the permission android.permission.CHANGE_CONFIGURATION. + +Each pair of overlay package and corresponding target package is +respresented by a new OverlayInfo class. This class mirrors the +existing PackageInfo class. + +Overlay packages are handled per Android user. The data is persisted in +/data/system/overlays.xml. + +Change-Id: Icc3c7daa25345d20bc5014b865024422eab72f5b +--- + Android.mk | 1 + + core/java/android/content/Context.java | 10 + + core/java/android/content/Intent.java | 34 + + core/java/android/content/om/IOverlayManager.aidl | 129 +++ + core/java/android/content/om/OverlayInfo.aidl | 19 + + core/java/android/content/om/OverlayInfo.java | 290 +++++++ + .../android/content/pm/PackageManagerInternal.java | 24 + + core/res/AndroidManifest.xml | 4 + + .../java/com/android/server/om/IdmapManager.java | 116 +++ + .../android/server/om/OverlayManagerService.java | 901 +++++++++++++++++++++ + .../server/om/OverlayManagerServiceImpl.java | 478 +++++++++++ + .../android/server/om/OverlayManagerSettings.java | 656 +++++++++++++++ + .../server/om/OverlayManagerShellCommand.java | 179 ++++ + .../android/server/pm/PackageManagerService.java | 41 + + 14 files changed, 2882 insertions(+) + create mode 100644 core/java/android/content/om/IOverlayManager.aidl + create mode 100644 core/java/android/content/om/OverlayInfo.aidl + create mode 100644 core/java/android/content/om/OverlayInfo.java + create mode 100644 services/core/java/com/android/server/om/IdmapManager.java + create mode 100644 services/core/java/com/android/server/om/OverlayManagerService.java + create mode 100644 services/core/java/com/android/server/om/OverlayManagerServiceImpl.java + create mode 100644 services/core/java/com/android/server/om/OverlayManagerSettings.java + create mode 100644 services/core/java/com/android/server/om/OverlayManagerShellCommand.java + +diff --git a/Android.mk b/Android.mk +index bdb4555..df42780 100644 +--- a/Android.mk ++++ b/Android.mk +@@ -137,6 +137,7 @@ LOCAL_SRC_FILES += \ + core/java/android/content/ISyncContext.aidl \ + core/java/android/content/ISyncServiceAdapter.aidl \ + core/java/android/content/ISyncStatusObserver.aidl \ ++ core/java/android/content/om/IOverlayManager.aidl \ + core/java/android/content/pm/ILauncherApps.aidl \ + core/java/android/content/pm/IOnAppsChangedListener.aidl \ + core/java/android/content/pm/IOtaDexopt.aidl \ +diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java +index 3a2f471..1ca44e0 100644 +--- a/core/java/android/content/Context.java ++++ b/core/java/android/content/Context.java +@@ -3656,6 +3656,16 @@ public abstract class Context { + public static final String GATEKEEPER_SERVICE = "android.service.gatekeeper.IGateKeeperService"; + + /** ++ * Use with {@link #getSystemService} to retrieve a {@link ++ * android.content.om.OverlayManager} for managing overlay packages. ++ * ++ * @see #getSystemService ++ * @see android.content.om.OverlayManager ++ * @hide ++ */ ++ public static final String OVERLAY_SERVICE = "overlay"; ++ ++ /** + * Determine whether the given permission is allowed for a particular + * process and user ID running in the system. + * +diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java +index 861aae5..1afae79 100644 +--- a/core/java/android/content/Intent.java ++++ b/core/java/android/content/Intent.java +@@ -3092,6 +3092,40 @@ public class Intent implements Parcelable, Cloneable { + "android.intent.action.MEDIA_RESOURCE_GRANTED"; + + /** ++ * Broadcast Action: An overlay package has been installed. The data ++ * contains the name of the added overlay package. ++ * @hide ++ */ ++ public static final String ACTION_OVERLAY_ADDED = "android.intent.action.OVERLAY_ADDED"; ++ ++ /** ++ * Broadcast Action: An overlay package has changed. The data contains the ++ * name of the overlay package which has changed. This is broadcast on all ++ * changes to the OverlayInfo returned by {@link ++ * android.content.om.IOverlayManager#getOverlayInfo(String, int)}. The ++ * most common change is a state change that will change whether the ++ * overlay is enabled or not. ++ * @hide ++ */ ++ public static final String ACTION_OVERLAY_CHANGED = "android.intent.action.OVERLAY_CHANGED"; ++ ++ /** ++ * Broadcast Action: An overlay package has been removed. The data contains ++ * the name of the overlay package which has been removed. ++ * @hide ++ */ ++ public static final String ACTION_OVERLAY_REMOVED = "android.intent.action.OVERLAY_REMOVED"; ++ ++ /** ++ * Broadcast Action: The order of a package's list of overlay packages has ++ * changed. The data contains the package name of the overlay package that ++ * had its position in the list adjusted. ++ * @hide ++ */ ++ public static final String ++ ACTION_OVERLAY_PRIORITY_CHANGED = "android.intent.action.OVERLAY_PRIORITY_CHANGED"; ++ ++ /** + * Activity Action: Allow the user to select and return one or more existing + * documents. When invoked, the system will display the various + * {@link DocumentsProvider} instances installed on the device, letting the +diff --git a/core/java/android/content/om/IOverlayManager.aidl b/core/java/android/content/om/IOverlayManager.aidl +new file mode 100644 +index 0000000..4f5d960 +--- /dev/null ++++ b/core/java/android/content/om/IOverlayManager.aidl +@@ -0,0 +1,129 @@ ++/* ++ * Copyright (C) 2015 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.content.om; ++ ++import android.content.om.OverlayInfo; ++ ++/** ++ * Api for getting information about overlay packages. ++ * ++ * {@hide} ++ */ ++interface IOverlayManager { ++ /** ++ * Returns information about all installed overlay packages for the ++ * specified user. If there are no installed overlay packages for this user, ++ * an empty map is returned (i.e. null is never returned). The returned map is a ++ * mapping of target package names to lists of overlays. Each list for a ++ * given target package is sorted in priority order, with the overlay with ++ * the highest priority at the end of the list. ++ * ++ * @param userId The user to get the OverlayInfos for. ++ * @return A Map> with target package names ++ * mapped to lists of overlays; if no overlays exist for the ++ * requested user, an empty map is returned. ++ */ ++ Map getAllOverlays(in int userId); ++ ++ /** ++ * Returns information about all overlays for the given target package for ++ * the specified user. The returned list is ordered according to the ++ * overlay priority with the highest priority at the end of the list. ++ * ++ * @param targetPackageName The name of the target package. ++ * @param userId The user to get the OverlayInfos for. ++ * @return A list of OverlayInfo objects; if no overlays exist for the ++ * requested package, an empty list is returned. ++ */ ++ List getOverlayInfosForTarget(in String targetPackageName, in int userId); ++ ++ /** ++ * Returns information about the overlay with the given package name for the ++ * specified user. ++ * ++ * @param packageName The name of the overlay package. ++ * @param userId The user to get the OverlayInfo for. ++ * @return The OverlayInfo for the overlay package; or null if no such ++ * overlay package exists. ++ */ ++ OverlayInfo getOverlayInfo(in String packageName, in int userId); ++ ++ /** ++ * Request that an overlay package be enabled or disabled when possible to ++ * do so. ++ * ++ * It is always possible to disable an overlay, but due to technical and ++ * security reasons it may not always be possible to enable an overlay. An ++ * example of the latter is when the related target package is not ++ * installed. If the technical obstacle is later overcome, the overlay is ++ * automatically enabled at that point in time. ++ * ++ * An enabled overlay is a part of target package's resources, i.e. it will ++ * be part of any lookups performed via {@link android.content.res.Resources} ++ * and {@link android.content.res.AssetManager}. A disabled overlay will no ++ * longer affect the resources of the target package. If the target is ++ * currently running, its outdated resources will be replaced by new ones. ++ * This happens the same way as when an application enters or exits split ++ * window mode. ++ * ++ * @param packageName The name of the overlay package. ++ * @param enable true to enable the overlay, false to disable it. ++ * @param userId The user for which to change the overlay. ++ * @return true if the system successfully registered the request, false ++ * otherwise. ++ */ ++ boolean setEnabled(in String packageName, in boolean enable, in int userId); ++ ++ /** ++ * Change the priority of the given overlay to be just higher than the ++ * overlay with package name newParentPackageName. Both overlay packages ++ * must have the same target and user. ++ * ++ * @see getOverlayInfosForTarget ++ * ++ * @param packageName The name of the overlay package whose priority should ++ * be adjusted. ++ * @param newParentPackageName The name of the overlay package the newly ++ * adjusted overlay package should just outrank. ++ * @param userId The user for which to change the overlay. ++ */ ++ boolean setPriority(in String packageName, in String newParentPackageName, in int userId); ++ ++ /** ++ * Change the priority of the given overlay to the highest priority relative to ++ * the other overlays with the same target and user. ++ * ++ * @see getOverlayInfosForTarget ++ * ++ * @param packageName The name of the overlay package whose priority should ++ * be adjusted. ++ * @param userId The user for which to change the overlay. ++ */ ++ boolean setHighestPriority(in String packageName, in int userId); ++ ++ /** ++ * Change the priority of the overlay to the lowest priority relative to ++ * the other overlays for the same target and user. ++ * ++ * @see getOverlayInfosForTarget ++ * ++ * @param packageName The name of the overlay package whose priority should ++ * be adjusted. ++ * @param userId The user for which to change the overlay. ++ */ ++ boolean setLowestPriority(in String packageName, in int userId); ++} +diff --git a/core/java/android/content/om/OverlayInfo.aidl b/core/java/android/content/om/OverlayInfo.aidl +new file mode 100644 +index 0000000..e7d413d +--- /dev/null ++++ b/core/java/android/content/om/OverlayInfo.aidl +@@ -0,0 +1,19 @@ ++/* ++ * Copyright (C) 2015 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.content.om; ++ ++parcelable OverlayInfo; +diff --git a/core/java/android/content/om/OverlayInfo.java b/core/java/android/content/om/OverlayInfo.java +new file mode 100644 +index 0000000..a25cf0c +--- /dev/null ++++ b/core/java/android/content/om/OverlayInfo.java +@@ -0,0 +1,290 @@ ++/* ++ * Copyright (C) 2015 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.content.om; ++ ++import android.annotation.NonNull; ++import android.os.Parcel; ++import android.os.Parcelable; ++ ++/** ++ * Immutable overlay information about a package. All PackageInfos that ++ * represent an overlay package will have a corresponding OverlayInfo. ++ * ++ * @hide ++ */ ++public final class OverlayInfo implements Parcelable { ++ /** ++ * An internal state used as the initial state of an overlay. OverlayInfo ++ * objects exposed outside the {@link ++ * com.android.server.om.OverlayManagerService} should never have this ++ * state. ++ */ ++ public static final int STATE_NOT_APPROVED_UNKNOWN = -1; ++ ++ /** ++ * The overlay package is disabled by the PackageManager. ++ */ ++ public static final int STATE_NOT_APPROVED_COMPONENT_DISABLED = 0; ++ ++ /** ++ * The target package of the overlay is not installed. ++ */ ++ public static final int STATE_NOT_APPROVED_MISSING_TARGET = 1; ++ ++ /** ++ * Creation of idmap file failed (e.g. no matching resources). ++ */ ++ public static final int STATE_NOT_APPROVED_NO_IDMAP = 2; ++ ++ /** ++ * The overlay package is dangerous, i.e. it touches resources not explicitly ++ * OK'd by the target package. ++ */ ++ public static final int STATE_NOT_APPROVED_DANGEROUS_OVERLAY = 3; ++ ++ /** ++ * The OverlayInfo is currently disabled but it is allowed to be enabled ++ * ({@link #STATE_APPROVED_ENABLED}) in the future. ++ */ ++ public static final int STATE_APPROVED_DISABLED = 4; ++ ++ /** ++ * The OverlayInfo is enabled but can be disabled ++ * ({@link #STATE_APPROVED_DISABLED}) in the future. ++ */ ++ public static final int STATE_APPROVED_ENABLED = 5; ++ ++ /** ++ * Package name of the overlay package ++ */ ++ public final String packageName; ++ ++ /** ++ * Package name of the target package ++ */ ++ public final String targetPackageName; ++ ++ /** ++ * Full path to the base APK for this overlay package ++ */ ++ public final String baseCodePath; ++ ++ /** ++ * The state of this OverlayInfo as defined by the STATE_* constants in this class. ++ *

++ * The state of an OverlayInfo determines if it is approved and/or enabled. An OverlayInfo with ++ * one of the STATE_NOT_APPROVED_* states cannot be enabled, and can thus never be part of the ++ * best match in the resource lookup. ++ *

++ * The only way to get an overlay package to be active and be part of the best matching in the ++ * resource lookup is if the corresponding OverlayInfo is in an STATE_*_ENABLED state. ++ * ++ * @see #STATE_NOT_APPROVED_COMPONENT_DISABLED ++ * @see #STATE_NOT_APPROVED_MISSING_TARGET ++ * @see #STATE_NOT_APPROVED_NO_IDMAP ++ * @see #STATE_NOT_APPROVED_DANGEROUS_OVERLAY ++ * @see #STATE_APPROVED_DISABLED ++ * @see #STATE_APPROVED_ENABLED ++ */ ++ public final int state; ++ ++ /** ++ * User handle for which this overlay applies ++ */ ++ public final int userId; ++ ++ /** ++ * Create a new OverlayInfo based on source with an updated state. ++ * ++ * @param source the source OverlayInfo to base the new instance on ++ * @param state the new state for the source OverlayInfo ++ */ ++ public OverlayInfo(@NonNull OverlayInfo source, int state) { ++ this(source.packageName, source.targetPackageName, source.baseCodePath, state, ++ source.userId); ++ } ++ ++ public OverlayInfo(@NonNull String packageName, @NonNull String targetPackageName, ++ @NonNull String baseCodePath, int state, int userId) { ++ this.packageName = packageName; ++ this.targetPackageName = targetPackageName; ++ this.baseCodePath = baseCodePath; ++ this.state = state; ++ this.userId = userId; ++ ensureValidState(); ++ } ++ ++ public OverlayInfo(Parcel source) { ++ packageName = source.readString(); ++ targetPackageName = source.readString(); ++ baseCodePath = source.readString(); ++ state = source.readInt(); ++ userId = source.readInt(); ++ ensureValidState(); ++ } ++ ++ private void ensureValidState() { ++ if (packageName == null) { ++ throw new IllegalArgumentException("packageName must not be null"); ++ } ++ if (targetPackageName == null) { ++ throw new IllegalArgumentException("targetPackageName must not be null"); ++ } ++ if (baseCodePath == null) { ++ throw new IllegalArgumentException("baseCodePath must not be null"); ++ } ++ switch (state) { ++ case STATE_NOT_APPROVED_UNKNOWN: ++ case STATE_NOT_APPROVED_COMPONENT_DISABLED: ++ case STATE_NOT_APPROVED_MISSING_TARGET: ++ case STATE_NOT_APPROVED_NO_IDMAP: ++ case STATE_NOT_APPROVED_DANGEROUS_OVERLAY: ++ case STATE_APPROVED_DISABLED: ++ case STATE_APPROVED_ENABLED: ++ break; ++ default: ++ throw new IllegalArgumentException("State " + state + " is not a valid state"); ++ } ++ } ++ ++ @Override ++ public int describeContents() { ++ return 0; ++ } ++ ++ @Override ++ public void writeToParcel(Parcel dest, int flags) { ++ dest.writeString(packageName); ++ dest.writeString(targetPackageName); ++ dest.writeString(baseCodePath); ++ dest.writeInt(state); ++ dest.writeInt(userId); ++ } ++ ++ public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { ++ @Override ++ public OverlayInfo createFromParcel(Parcel source) { ++ return new OverlayInfo(source); ++ } ++ ++ @Override ++ public OverlayInfo[] newArray(int size) { ++ return new OverlayInfo[size]; ++ } ++ }; ++ ++ /** ++ * Returns true if this overlay is enabled, i.e. should be used to overlay ++ * the resources in the target package. ++ * ++ * Disabled overlay packages are installed but are currently not in use. ++ * ++ * @return true if the overlay is enabled, else false. ++ */ ++ public boolean isEnabled() { ++ switch (state) { ++ case STATE_APPROVED_ENABLED: ++ return true; ++ default: ++ return false; ++ } ++ } ++ ++ /** ++ * Returns true if this overlay is approved. ++ * ++ * @return true if this overlay is approved, else false. ++ */ ++ public boolean isApproved() { ++ switch (state) { ++ case STATE_APPROVED_ENABLED: ++ case STATE_APPROVED_DISABLED: ++ return true; ++ default: ++ return false; ++ } ++ } ++ ++ public static String stateToString(int state) { ++ switch (state) { ++ case STATE_NOT_APPROVED_UNKNOWN: ++ return "STATE_NOT_APPROVED_UNKNOWN"; ++ case STATE_NOT_APPROVED_COMPONENT_DISABLED: ++ return "STATE_NOT_APPROVED_COMPONENT_DISABLED"; ++ case STATE_NOT_APPROVED_MISSING_TARGET: ++ return "STATE_NOT_APPROVED_MISSING_TARGET"; ++ case STATE_NOT_APPROVED_NO_IDMAP: ++ return "STATE_NOT_APPROVED_NO_IDMAP"; ++ case STATE_NOT_APPROVED_DANGEROUS_OVERLAY: ++ return "STATE_NOT_APPROVED_DANGEROUS_OVERLAY"; ++ case STATE_APPROVED_DISABLED: ++ return "STATE_APPROVED_DISABLED"; ++ case STATE_APPROVED_ENABLED: ++ return "STATE_APPROVED_ENABLED"; ++ default: ++ return ""; ++ } ++ } ++ ++ @Override ++ public int hashCode() { ++ final int prime = 31; ++ int result = 1; ++ result = prime * result + userId; ++ result = prime * result + state; ++ result = prime * result + ((packageName == null) ? 0 : packageName.hashCode()); ++ result = prime * result + ((targetPackageName == null) ? 0 : targetPackageName.hashCode()); ++ result = prime * result + ((baseCodePath == null) ? 0 : baseCodePath.hashCode()); ++ return result; ++ } ++ ++ @Override ++ public boolean equals(Object obj) { ++ if (this == obj) { ++ return true; ++ } ++ if (obj == null) { ++ return false; ++ } ++ if (getClass() != obj.getClass()) { ++ return false; ++ } ++ OverlayInfo other = (OverlayInfo) obj; ++ if (userId != other.userId) { ++ return false; ++ } ++ if (state != other.state) { ++ return false; ++ } ++ if (!packageName.equals(other.packageName)) { ++ return false; ++ } ++ if (!targetPackageName.equals(other.targetPackageName)) { ++ return false; ++ } ++ if (!baseCodePath.equals(other.baseCodePath)) { ++ return false; ++ } ++ return true; ++ } ++ ++ @Override ++ public String toString() { ++ return "OverlayInfo { overlay=" + packageName + ", target=" + targetPackageName + ", state=" ++ + state + " (" + stateToString(state) + "), userId=" + userId + " }"; ++ } ++} +diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java +index f5bcf64..bdbdd1e 100644 +--- a/core/java/android/content/pm/PackageManagerInternal.java ++++ b/core/java/android/content/pm/PackageManagerInternal.java +@@ -168,4 +168,28 @@ public abstract class PackageManagerInternal { + * @return Whether was launched. + */ + public abstract boolean wasPackageEverLaunched(String packageName, int userId); ++ ++ /** ++ * Get all overlay packages for a user. ++ * @param userId The user for which to get the overlays. ++ * @return A list of overlay packages. An empty list is returned if the ++ * user has no installed overlay packages. ++ */ ++ public abstract List getOverlayPackages(int userId); ++ ++ /** ++ * Get the names of all target packages for a user. ++ * @param userId The user for which to get the package names. ++ * @return A list of target package names. This list includes the "android" package. ++ */ ++ public abstract List getTargetPackageNames(int userId); ++ ++ /** ++ * Set which overlay to use for a package. ++ * @param userId The user for which to update the overlays. ++ * @param packageName The package name of the package for which to update the overlays. ++ * @param resourceDirs The paths to the overlay packages to use, ordered in the order in ++ * which to load the paths, or null if no overlays should be used. ++ */ ++ public abstract void setResourceDirs(int userId, String packageName, String[] resourceDirs); + } +diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml +index c2f12ef..986be28 100644 +--- a/core/res/AndroidManifest.xml ++++ b/core/res/AndroidManifest.xml +@@ -84,6 +84,10 @@ + + + ++ ++ ++ ++ + + + +diff --git a/services/core/java/com/android/server/om/IdmapManager.java b/services/core/java/com/android/server/om/IdmapManager.java +new file mode 100644 +index 0000000..e2a3775 +--- /dev/null ++++ b/services/core/java/com/android/server/om/IdmapManager.java +@@ -0,0 +1,116 @@ ++/* ++ * Copyright (C) 2016 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.om; ++ ++import static com.android.server.om.OverlayManagerService.DEBUG; ++import static com.android.server.om.OverlayManagerService.TAG; ++ ++import android.annotation.NonNull; ++import android.content.om.OverlayInfo; ++import android.content.pm.PackageInfo; ++import android.os.UserHandle; ++import android.util.Slog; ++import com.android.internal.os.InstallerConnection.InstallerException; ++import com.android.server.pm.Installer; ++ ++import java.io.DataInputStream; ++import java.io.File; ++import java.io.FileInputStream; ++import java.io.IOException; ++ ++/** ++ * Handle the creation and deletion of idmap files. ++ * ++ * The actual work is performed by the idmap binary, launched through Installer ++ * and installd. ++ * ++ * Note: this class is subclassed in the OMS unit tests, and hence not marked as final. ++ */ ++class IdmapManager { ++ private final Installer mInstaller; ++ ++ IdmapManager(final Installer installer) { ++ mInstaller = installer; ++ } ++ ++ boolean createIdmap(@NonNull final PackageInfo targetPackage, ++ @NonNull final PackageInfo overlayPackage, int userId) { ++ // unused userId: see comment in OverlayManagerServiceImpl.removeIdmapIfPossible ++ if (DEBUG) { ++ Slog.d(TAG, "create idmap for " + targetPackage.packageName + " and " + ++ overlayPackage.packageName); ++ } ++ final int sharedGid = UserHandle.getSharedAppGid(targetPackage.applicationInfo.uid); ++ final String targetPath = targetPackage.applicationInfo.getBaseCodePath(); ++ final String overlayPath = overlayPackage.applicationInfo.getBaseCodePath(); ++ try { ++ mInstaller.idmap(targetPath, overlayPath, sharedGid); ++ } catch (InstallerException e) { ++ Slog.w(TAG, "failed to generate idmap for " + targetPath + " and " + ++ overlayPath + ": " + e.getMessage()); ++ return false; ++ } ++ return true; ++ } ++ ++ boolean removeIdmap(@NonNull final OverlayInfo oi, final int userId) { ++ // unused userId: see comment in OverlayManagerServiceImpl.removeIdmapIfPossible ++ if (DEBUG) { ++ Slog.d(TAG, "remove idmap for " + oi.baseCodePath); ++ } ++ try { ++ mInstaller.removeIdmap(oi.baseCodePath); ++ } catch (InstallerException e) { ++ Slog.w(TAG, "failed to remove idmap for " + oi.baseCodePath + ": " + e.getMessage()); ++ return false; ++ } ++ return true; ++ } ++ ++ boolean idmapExists(@NonNull final OverlayInfo oi) { ++ // unused OverlayInfo.userId: see comment in OverlayManagerServiceImpl.removeIdmapIfPossible ++ return new File(getIdmapPath(oi.baseCodePath)).isFile(); ++ } ++ ++ boolean idmapExists(@NonNull final PackageInfo overlayPackage, final int userId) { ++ // unused userId: see comment in OverlayManagerServiceImpl.removeIdmapIfPossible ++ return new File(getIdmapPath(overlayPackage.applicationInfo.getBaseCodePath())).isFile(); ++ } ++ ++ boolean isDangerous(@NonNull final PackageInfo overlayPackage, final int userId) { ++ // unused userId: see comment in OverlayManagerServiceImpl.removeIdmapIfPossible ++ return isDangerous(getIdmapPath(overlayPackage.applicationInfo.getBaseCodePath())); ++ } ++ ++ private String getIdmapPath(@NonNull final String baseCodePath) { ++ final StringBuilder sb = new StringBuilder("/data/resource-cache/"); ++ sb.append(baseCodePath.substring(1).replace('/', '@')); ++ sb.append("@idmap"); ++ return sb.toString(); ++ } ++ ++ private boolean isDangerous(@NonNull final String idmapPath) { ++ try (DataInputStream dis = new DataInputStream(new FileInputStream(idmapPath))) { ++ final int magic = dis.readInt(); ++ final int version = dis.readInt(); ++ final int dangerous = dis.readInt(); ++ return dangerous != 0; ++ } catch (IOException e) { ++ return true; ++ } ++ } ++} +diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java +new file mode 100644 +index 0000000..ec148dd +--- /dev/null ++++ b/services/core/java/com/android/server/om/OverlayManagerService.java +@@ -0,0 +1,901 @@ ++/* ++ * Copyright (C) 2016 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.om; ++ ++import static android.app.AppGlobals.getPackageManager; ++import static android.content.Intent.ACTION_PACKAGE_ADDED; ++import static android.content.Intent.ACTION_PACKAGE_CHANGED; ++import static android.content.Intent.ACTION_PACKAGE_REMOVED; ++import static android.content.Intent.ACTION_USER_REMOVED; ++import static android.content.pm.PackageManager.SIGNATURE_MATCH; ++ ++import android.annotation.NonNull; ++import android.annotation.Nullable; ++import android.app.ActivityManager; ++import android.app.ActivityManagerNative; ++import android.app.IActivityManager; ++import android.content.BroadcastReceiver; ++import android.content.Context; ++import android.content.Intent; ++import android.content.IntentFilter; ++import android.content.om.IOverlayManager; ++import android.content.om.OverlayInfo; ++import android.content.pm.IPackageManager; ++import android.content.pm.PackageInfo; ++import android.content.pm.PackageManagerInternal; ++import android.content.pm.UserInfo; ++import android.net.Uri; ++import android.os.Binder; ++import android.os.Environment; ++import android.os.IBinder; ++import android.os.Process; ++import android.os.RemoteException; ++import android.os.ResultReceiver; ++import android.os.UserHandle; ++import android.util.ArrayMap; ++import android.util.AtomicFile; ++import android.util.Slog; ++import android.util.SparseArray; ++ ++import com.android.server.FgThread; ++import com.android.server.IoThread; ++import com.android.server.LocalServices; ++import com.android.server.SystemService; ++import com.android.server.pm.Installer; ++import com.android.server.pm.UserManagerService; ++ ++import org.xmlpull.v1.XmlPullParserException; ++ ++import java.io.File; ++import java.io.FileDescriptor; ++import java.io.FileInputStream; ++import java.io.FileOutputStream; ++import java.io.IOException; ++import java.io.PrintWriter; ++import java.util.ArrayList; ++import java.util.Collection; ++import java.util.Collections; ++import java.util.HashMap; ++import java.util.Iterator; ++import java.util.List; ++import java.util.Map; ++import java.util.concurrent.atomic.AtomicBoolean; ++ ++/** ++ * Service to manage asset overlays. ++ * ++ *

Asset overlays are additional resources that come from apks loaded ++ * alongside the system and app apks. This service, the OverlayManagerService ++ * (OMS), tracks which installed overlays to use and provides methods to change ++ * this. Changes propagate to running applications as part of the Activity ++ * lifecycle. This allows Activities to reread their resources at a well ++ * defined point.

++ * ++ *

By itself, the OMS will not change what overlays should be active. ++ * Instead, it is only responsible for making sure that overlays *can* be used ++ * from a technical and security point of view and to activate overlays in ++ * response to external requests. The responsibility to toggle overlays on and ++ * off lies within components that implement different use-cases such as themes ++ * or dynamic customization.

++ * ++ *

The OMS receives input from three sources:

++ * ++ *
    ++ *
  • Callbacks from the SystemService class, specifically when the ++ * Android framework is booting and when the end user switches Android ++ * users.
  • ++ * ++ *
  • Intents from the PackageManagerService (PMS). Overlays are regular ++ * apks, and whenever a package is installed (or removed, or has a ++ * component enabled or disabled), the PMS broadcasts this as an intent. ++ * When the OMS receives one of these intents, it updates its internal ++ * representation of the available overlays and, if there was a visible ++ * change, triggers an asset refresh in the affected apps.
  • ++ * ++ *
  • External requests via the {@link IOverlayManager AIDL interface}. ++ * The interface allows clients to read information about the currently ++ * available overlays, change whether an overlay should be used or not, and ++ * change the relative order in which overlay packages are loaded. ++ * Read-access is granted if the request targets the same Android user as ++ * the caller runs as, or if the caller holds the ++ * INTERACT_ACROSS_USERS_FULL permission. Write-access is granted if the ++ * caller is granted read-access and additionaly holds the ++ * CHANGE_CONFIGURATION permission.
  • ++ *
++ * ++ *

The AIDL interface works with String package names, int user IDs, and ++ * {@link OverlayInfo} objects. OverlayInfo instances are used to track a ++ * specific pair of target and overlay packages and include information such as ++ * the current state of the overlay. OverlayInfo objects are immutable.

++ * ++ *

Internally, OverlayInfo objects are maintained by the ++ * OverlayManagerSettings class. The OMS and its helper classes are notified of ++ * changes to the settings by the OverlayManagerSettings.ChangeListener ++ * callback interface. The file /data/system/overlays.xml is used to persist ++ * the settings.

++ * ++ *

Creation and deletion of idmap files are handled by the IdmapManager ++ * class.

++ * ++ *

The following is an overview of OMS and its related classes. Note how box ++ * (2) does the heavy lifting, box (1) interacts with the Android framework, ++ * and box (3) replaces box (1) during unit testing.

++ * ++ *
++ *         Android framework
++ *            |         ^
++ *      . . . | . . . . | . . . .
++ *     .      |         |       .
++ *     .    AIDL,   broadcasts  .
++ *     .   intents      |       .
++ *     .      |         |       . . . . . . . . . . . .
++ *     .      v         |       .                     .
++ *     .  OverlayManagerService . OverlayManagerTests .
++ *     .                  \     .     /               .
++ *     . (1)               \    .    /            (3) .
++ *      . . . . . . . . . . \ . . . / . . . . . . . . .
++ *     .                     \     /              .
++ *     . (2)                  \   /               .
++ *     .           OverlayManagerServiceImpl      .
++ *     .                  |            |          .
++ *     .                  |            |          .
++ *     . OverlayManagerSettings     IdmapManager  .
++ *     .                                          .
++ *     . . . .  . . . . . . . . . . . . . . . . . .
++ * 
++ * ++ *

Finally, here is a list of keywords used in the OMS context.

++ * ++ *
    ++ *
  • target [package] -- A regular apk that may have its resource ++ * pool extended by zero or more overlay packages.
  • ++ * ++ *
  • overlay [package] -- An apk that provides additional ++ * resources to another apk.
  • ++ * ++ *
  • OMS -- The OverlayManagerService, i.e. this class.
  • ++ * ++ *
  • approved -- An overlay is approved if the OMS has verified ++ * that it can be used technically speaking (its target package is ++ * installed, at least one resource name in both packages match, the ++ * idmap was created, etc) and that it is secure to do so. External ++ * clients can not change this state.
  • ++ * ++ *
  • not approved -- The opposite of approved.
  • ++ * ++ *
  • enabled -- An overlay currently in active use and thus part ++ * of resource lookups. This requires the overlay to be approved. Only ++ * external clients can change this state.
  • ++ * ++ *
  • disabled -- The opposite of enabled.
  • ++ * ++ *
  • idmap -- A mapping of resource IDs between target and overlay ++ * used during resource lookup. Also the name of the binary that creates ++ * the mapping.
  • ++ *
++ */ ++public final class OverlayManagerService extends SystemService { ++ ++ static final String TAG = "OverlayManager"; ++ ++ static final boolean DEBUG = false; ++ ++ private final Object mLock = new Object(); ++ ++ private final AtomicFile mSettingsFile; ++ ++ private final PackageManagerHelper mPackageManager; ++ ++ private final UserManagerService mUserManager; ++ ++ private final OverlayManagerSettings mSettings; ++ ++ private final OverlayManagerServiceImpl mImpl; ++ ++ private final AtomicBoolean mPersistSettingsScheduled = new AtomicBoolean(false); ++ ++ public OverlayManagerService(@NonNull final Context context, ++ @NonNull final Installer installer) { ++ super(context); ++ mSettingsFile = ++ new AtomicFile(new File(Environment.getDataSystemDirectory(), "overlays.xml")); ++ mPackageManager = new PackageManagerHelper(); ++ mUserManager = UserManagerService.getInstance(); ++ IdmapManager im = new IdmapManager(installer); ++ mSettings = new OverlayManagerSettings(); ++ mImpl = new OverlayManagerServiceImpl(mPackageManager, im, mSettings); ++ ++ final IntentFilter packageFilter = new IntentFilter(); ++ packageFilter.addAction(ACTION_PACKAGE_ADDED); ++ packageFilter.addAction(ACTION_PACKAGE_CHANGED); ++ packageFilter.addAction(ACTION_PACKAGE_REMOVED); ++ packageFilter.addDataScheme("package"); ++ getContext().registerReceiverAsUser(new PackageReceiver(), UserHandle.ALL, ++ packageFilter, null, null); ++ ++ final IntentFilter userFilter = new IntentFilter(); ++ userFilter.addAction(ACTION_USER_REMOVED); ++ getContext().registerReceiverAsUser(new UserReceiver(), UserHandle.ALL, ++ userFilter, null, null); ++ ++ restoreSettings(); ++ onSwitchUser(UserHandle.USER_SYSTEM); ++ schedulePersistSettings(); ++ ++ mSettings.addChangeListener(new OverlayChangeListener()); ++ ++ publishBinderService(Context.OVERLAY_SERVICE, mService); ++ publishLocalService(OverlayManagerService.class, this); ++ } ++ ++ @Override ++ public void onStart() { ++ // Intentionally left empty. ++ } ++ ++ @Override ++ public void onSwitchUser(final int newUserId) { ++ // ensure overlays in the settings are up-to-date, and propagate ++ // any asset changes to the rest of the system ++ final List targets; ++ synchronized (mLock) { ++ targets = mImpl.onSwitchUser(newUserId); ++ } ++ updateAssets(newUserId, targets); ++ } ++ ++ public List getEnabledOverlayPaths(@NonNull final String packageName, ++ final int userId) { ++ synchronized (mLock) { ++ return mImpl.onGetEnabledOverlayPaths(packageName, userId); ++ } ++ } ++ ++ private final class PackageReceiver extends BroadcastReceiver { ++ @Override ++ public void onReceive(@NonNull final Context context, @NonNull final Intent intent) { ++ final Uri data = intent.getData(); ++ if (data == null) { ++ Slog.e(TAG, "Cannot handle package broadcast with null data"); ++ return; ++ } ++ final String packageName = data.getSchemeSpecificPart(); ++ ++ final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false); ++ ++ final int[] userIds; ++ final int extraUid = intent.getIntExtra(Intent.EXTRA_UID, UserHandle.USER_NULL); ++ if (extraUid == UserHandle.USER_NULL) { ++ userIds = mUserManager.getUserIds(); ++ } else { ++ userIds = new int[] { UserHandle.getUserId(extraUid) }; ++ } ++ ++ switch (intent.getAction()) { ++ case ACTION_PACKAGE_ADDED: ++ if (replacing) { ++ onPackageUpgraded(packageName, userIds); ++ } else { ++ onPackageAdded(packageName, userIds); ++ } ++ break; ++ case ACTION_PACKAGE_CHANGED: ++ onPackageChanged(packageName, userIds); ++ break; ++ case ACTION_PACKAGE_REMOVED: ++ if (replacing) { ++ onPackageUpgrading(packageName, userIds); ++ } else { ++ onPackageRemoved(packageName, userIds); ++ } ++ break; ++ default: ++ // do nothing ++ break; ++ } ++ } ++ ++ private void onPackageAdded(@NonNull final String packageName, ++ @NonNull final int[] userIds) { ++ for (final int userId : userIds) { ++ synchronized (mLock) { ++ final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId, false); ++ if (pi != null) { ++ mPackageManager.cachePackageInfo(packageName, userId, pi); ++ if (!isOverlayPackage(pi)) { ++ mImpl.onTargetPackageAdded(packageName, userId); ++ } else { ++ mImpl.onOverlayPackageAdded(packageName, userId); ++ } ++ } ++ } ++ } ++ } ++ ++ private void onPackageChanged(@NonNull final String packageName, ++ @NonNull final int[] userIds) { ++ for (int userId : userIds) { ++ synchronized (mLock) { ++ final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId, false); ++ if (pi != null) { ++ mPackageManager.cachePackageInfo(packageName, userId, pi); ++ if (!isOverlayPackage(pi)) { ++ mImpl.onTargetPackageChanged(packageName, userId); ++ } else { ++ mImpl.onOverlayPackageChanged(packageName, userId); ++ } ++ } ++ } ++ } ++ } ++ ++ private void onPackageUpgrading(@NonNull final String packageName, ++ @NonNull final int[] userIds) { ++ for (int userId : userIds) { ++ synchronized (mLock) { ++ mPackageManager.forgetPackageInfo(packageName, userId); ++ final OverlayInfo oi = mImpl.onGetOverlayInfo(packageName, userId); ++ if (oi == null) { ++ mImpl.onTargetPackageUpgrading(packageName, userId); ++ } else { ++ mImpl.onOverlayPackageUpgrading(packageName, userId); ++ } ++ } ++ } ++ } ++ ++ private void onPackageUpgraded(@NonNull final String packageName, ++ @NonNull final int[] userIds) { ++ for (int userId : userIds) { ++ synchronized (mLock) { ++ final PackageInfo pi = mPackageManager.getPackageInfo(packageName, userId, false); ++ if (pi != null) { ++ mPackageManager.cachePackageInfo(packageName, userId, pi); ++ if (!isOverlayPackage(pi)) { ++ mImpl.onTargetPackageUpgraded(packageName, userId); ++ } else { ++ mImpl.onOverlayPackageUpgraded(packageName, userId); ++ } ++ } ++ } ++ } ++ } ++ ++ private void onPackageRemoved(@NonNull final String packageName, ++ @NonNull final int[] userIds) { ++ for (int userId : userIds) { ++ synchronized (mLock) { ++ mPackageManager.forgetPackageInfo(packageName, userId); ++ final OverlayInfo oi = mImpl.onGetOverlayInfo(packageName, userId); ++ if (oi == null) { ++ mImpl.onTargetPackageRemoved(packageName, userId); ++ } else { ++ mImpl.onOverlayPackageRemoved(packageName, userId); ++ } ++ } ++ } ++ } ++ } ++ ++ private final class UserReceiver extends BroadcastReceiver { ++ @Override ++ public void onReceive(@NonNull final Context context, @NonNull final Intent intent) { ++ switch (intent.getAction()) { ++ case ACTION_USER_REMOVED: ++ final int userId = ++ intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); ++ if (userId != UserHandle.USER_NULL) { ++ synchronized (mLock) { ++ mImpl.onUserRemoved(userId); ++ mPackageManager.forgetAllPackageInfos(userId); ++ } ++ } ++ break; ++ default: ++ // do nothing ++ break; ++ } ++ } ++ } ++ ++ private final IBinder mService = new IOverlayManager.Stub() { ++ @Override ++ public Map> getAllOverlays(int userId) ++ throws RemoteException { ++ userId = handleIncomingUser(userId, "getAllOverlays"); ++ ++ synchronized (mLock) { ++ return mImpl.onGetOverlaysForUser(userId); ++ } ++ } ++ ++ @Override ++ public List getOverlayInfosForTarget(@Nullable final String targetPackageName, ++ int userId) throws RemoteException { ++ userId = handleIncomingUser(userId, "getOverlayInfosForTarget"); ++ if (targetPackageName == null) { ++ return Collections.emptyList(); ++ } ++ ++ synchronized (mLock) { ++ return mImpl.onGetOverlayInfosForTarget(targetPackageName, userId); ++ } ++ } ++ ++ @Override ++ public OverlayInfo getOverlayInfo(@Nullable final String packageName, ++ int userId) throws RemoteException { ++ userId = handleIncomingUser(userId, "getOverlayInfo"); ++ if (packageName == null) { ++ return null; ++ } ++ ++ synchronized (mLock) { ++ return mImpl.onGetOverlayInfo(packageName, userId); ++ } ++ } ++ ++ @Override ++ public boolean setEnabled(@Nullable final String packageName, final boolean enable, ++ int userId) throws RemoteException { ++ enforceChangeConfigurationPermission("setEnabled"); ++ userId = handleIncomingUser(userId, "setEnabled"); ++ if (packageName == null) { ++ return false; ++ } ++ ++ final long ident = Binder.clearCallingIdentity(); ++ try { ++ synchronized (mLock) { ++ return mImpl.onSetEnabled(packageName, enable, userId); ++ } ++ } finally { ++ Binder.restoreCallingIdentity(ident); ++ } ++ } ++ ++ @Override ++ public boolean setPriority(@Nullable final String packageName, ++ @Nullable final String parentPackageName, int userId) throws RemoteException { ++ enforceChangeConfigurationPermission("setPriority"); ++ userId = handleIncomingUser(userId, "setPriority"); ++ if (packageName == null || parentPackageName == null) { ++ return false; ++ } ++ ++ final long ident = Binder.clearCallingIdentity(); ++ try { ++ synchronized (mLock) { ++ return mImpl.onSetPriority(packageName, parentPackageName, userId); ++ } ++ } finally { ++ Binder.restoreCallingIdentity(ident); ++ } ++ } ++ ++ @Override ++ public boolean setHighestPriority(@Nullable final String packageName, int userId) ++ throws RemoteException { ++ enforceChangeConfigurationPermission("setHighestPriority"); ++ userId = handleIncomingUser(userId, "setHighestPriority"); ++ if (packageName == null) { ++ return false; ++ } ++ ++ final long ident = Binder.clearCallingIdentity(); ++ try { ++ synchronized (mLock) { ++ return mImpl.onSetHighestPriority(packageName, userId); ++ } ++ } finally { ++ Binder.restoreCallingIdentity(ident); ++ } ++ } ++ ++ @Override ++ public boolean setLowestPriority(@Nullable final String packageName, int userId) ++ throws RemoteException { ++ enforceChangeConfigurationPermission("setLowestPriority"); ++ userId = handleIncomingUser(userId, "setLowestPriority"); ++ if (packageName == null) { ++ return false; ++ } ++ ++ final long ident = Binder.clearCallingIdentity(); ++ try { ++ synchronized (mLock) { ++ return mImpl.onSetLowestPriority(packageName, userId); ++ } ++ } finally { ++ Binder.restoreCallingIdentity(ident); ++ } ++ } ++ ++ @Override ++ public void onShellCommand(@NonNull final FileDescriptor in, ++ @NonNull final FileDescriptor out, @NonNull final FileDescriptor err, ++ @NonNull final String[] args, @NonNull final ResultReceiver resultReceiver) { ++ (new OverlayManagerShellCommand(this)).exec( ++ this, in, out, err, args, resultReceiver); ++ } ++ ++ @Override ++ protected void dump(@NonNull final FileDescriptor fd, @NonNull final PrintWriter pw, ++ @NonNull final String[] argv) { ++ enforceDumpPermission("dump"); ++ ++ final boolean verbose = argv.length > 0 && "--verbose".equals(argv[0]); ++ ++ synchronized (mLock) { ++ mImpl.onDump(pw); ++ mPackageManager.dump(pw, verbose); ++ } ++ } ++ ++ /** ++ * Ensure that the caller has permission to interact with the given userId. ++ * If the calling user is not the same as the provided user, the caller needs ++ * to hold the INTERACT_ACROSS_USERS_FULL permission (or be system uid or ++ * root). ++ * ++ * @param userId the user to interact with ++ * @param message message for any SecurityException ++ */ ++ private int handleIncomingUser(final int userId, @NonNull final String message) { ++ return ActivityManager.handleIncomingUser(Binder.getCallingPid(), ++ Binder.getCallingUid(), userId, false, true, message, null); ++ } ++ ++ /** ++ * Enforce that the caller holds the CHANGE_CONFIGURATION permission (or is ++ * system or root). ++ * ++ * @param message used as message if SecurityException is thrown ++ * @throws SecurityException if the permission check fails ++ */ ++ private void enforceChangeConfigurationPermission(@NonNull final String message) { ++ final int callingUid = Binder.getCallingUid(); ++ ++ if (callingUid != Process.SYSTEM_UID && callingUid != 0) { ++ getContext().enforceCallingOrSelfPermission( ++ android.Manifest.permission.CHANGE_CONFIGURATION, message); ++ } ++ } ++ ++ /** ++ * Enforce that the caller holds the DUMP permission (or is system or root). ++ * ++ * @param message used as message if SecurityException is thrown ++ * @throws SecurityException if the permission check fails ++ */ ++ private void enforceDumpPermission(@NonNull final String message) { ++ final int callingUid = Binder.getCallingUid(); ++ ++ if (callingUid != Process.SYSTEM_UID && callingUid != 0) { ++ getContext().enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, ++ message); ++ } ++ } ++ }; ++ ++ private boolean isOverlayPackage(@NonNull final PackageInfo pi) { ++ return pi != null && pi.overlayTarget != null; ++ } ++ ++ private final class OverlayChangeListener implements OverlayManagerSettings.ChangeListener { ++ @Override ++ public void onSettingsChanged() { ++ schedulePersistSettings(); ++ } ++ ++ @Override ++ public void onOverlayAdded(@NonNull final OverlayInfo oi) { ++ scheduleBroadcast(Intent.ACTION_OVERLAY_ADDED, oi, oi.isEnabled()); ++ } ++ ++ @Override ++ public void onOverlayRemoved(@NonNull final OverlayInfo oi) { ++ scheduleBroadcast(Intent.ACTION_OVERLAY_REMOVED, oi, oi.isEnabled()); ++ } ++ ++ @Override ++ public void onOverlayChanged(@NonNull final OverlayInfo oi, ++ @NonNull final OverlayInfo oldOi) { ++ scheduleBroadcast(Intent.ACTION_OVERLAY_CHANGED, oi, oi.isEnabled() != oldOi.isEnabled()); ++ } ++ ++ @Override ++ public void onOverlayPriorityChanged(@NonNull final OverlayInfo oi) { ++ scheduleBroadcast(Intent.ACTION_OVERLAY_PRIORITY_CHANGED, oi, oi.isEnabled()); ++ } ++ ++ private void scheduleBroadcast(@NonNull final String action, @NonNull final OverlayInfo oi, ++ final boolean doUpdate) { ++ FgThread.getHandler().post(new BroadcastRunnable(action, oi, doUpdate)); ++ } ++ ++ private final class BroadcastRunnable extends Thread { ++ private final String mAction; ++ private final OverlayInfo mOverlayInfo; ++ private final boolean mDoUpdate; ++ ++ public BroadcastRunnable(@NonNull final String action, @NonNull final OverlayInfo oi, ++ final boolean doUpdate) { ++ mAction = action; ++ mOverlayInfo = oi; ++ mDoUpdate = doUpdate; ++ } ++ ++ public void run() { ++ if (mDoUpdate) { ++ updateAssets(mOverlayInfo.userId, mOverlayInfo.targetPackageName); ++ } ++ sendBroadcast(mAction, mOverlayInfo.targetPackageName, mOverlayInfo.packageName, ++ mOverlayInfo.userId); ++ } ++ ++ private void sendBroadcast(@NonNull final String action, ++ @NonNull final String targetPackageName, @NonNull final String packageName, ++ final int userId) { ++ final Intent intent = new Intent(action, Uri.fromParts("package", ++ String.format("%s/%s", targetPackageName, packageName), null)); ++ intent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); ++ if (DEBUG) { ++ Slog.d(TAG, String.format("send broadcast %s", intent)); ++ } ++ try { ++ ActivityManagerNative.getDefault().broadcastIntent(null, intent, null, null, 0, ++ null, null, null, android.app.AppOpsManager.OP_NONE, null, false, false, ++ userId); ++ } catch (RemoteException e) { ++ // Intentionally left empty. ++ } ++ } ++ ++ } ++ } ++ ++ private void updateAssets(final int userId, final String targetPackageName) { ++ final List list = new ArrayList<>(); ++ list.add(targetPackageName); ++ updateAssets(userId, list); ++ } ++ ++ private void updateAssets(final int userId, List targetPackageNames) { ++ // TODO: uncomment when we integrate OMS properly ++ // final PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class); ++ // final boolean updateFrameworkRes = targetPackageNames.contains("android"); ++ // if (updateFrameworkRes) { ++ // targetPackageNames = pm.getTargetPackageNames(userId); ++ // } ++ ++ // final Map allPaths = new ArrayMap<>(targetPackageNames.size()); ++ // synchronized (mLock) { ++ // final List frameworkPaths = mImpl.onGetEnabledOverlayPaths("android", userId); ++ // for (final String packageName : targetPackageNames) { ++ // final List paths = new ArrayList<>(); ++ // paths.addAll(frameworkPaths); ++ // if (!"android".equals(packageName)) { ++ // paths.addAll(mImpl.onGetEnabledOverlayPaths(packageName, userId)); ++ // } ++ // allPaths.put(packageName, ++ // paths.isEmpty() ? null : paths.toArray(new String[paths.size()])); ++ // } ++ // } ++ ++ // for (String packageName : targetPackageNames) { ++ // pm.setResourceDirs(userId, packageName, allPaths.get(packageName)); ++ // } ++ ++ // final IActivityManager am = ActivityManagerNative.getDefault(); ++ // try { ++ // am.updateAssets(userId, targetPackageNames); ++ // } catch (RemoteException e) { ++ // // Intentionally left empty. ++ // } ++ } ++ ++ private void schedulePersistSettings() { ++ if (mPersistSettingsScheduled.get()) { ++ return; ++ } ++ mPersistSettingsScheduled.set(true); ++ IoThread.getHandler().post(new Runnable() { ++ @Override ++ public void run() { ++ mPersistSettingsScheduled.set(false); ++ synchronized (mLock) { ++ FileOutputStream stream = null; ++ try { ++ stream = mSettingsFile.startWrite(); ++ mSettings.persist(stream); ++ mSettingsFile.finishWrite(stream); ++ } catch (IOException | XmlPullParserException e) { ++ mSettingsFile.failWrite(stream); ++ Slog.e(TAG, "failed to persist overlay state", e); ++ } ++ } ++ } ++ }); ++ } ++ ++ private void restoreSettings() { ++ synchronized (mLock) { ++ if (!mSettingsFile.getBaseFile().exists()) { ++ return; ++ } ++ try (final FileInputStream stream = mSettingsFile.openRead()) { ++ mSettings.restore(stream); ++ ++ // We might have data for dying users if the device was ++ // restarted before we received USER_REMOVED. Remove data for ++ // users that will not exist after the system is ready. ++ ++ for (final UserInfo deadUser : getDeadUsers()) { ++ final int userId = deadUser.getUserHandle().getIdentifier(); ++ mSettings.removeUser(userId); ++ } ++ } catch (IOException | XmlPullParserException e) { ++ Slog.e(TAG, "failed to restore overlay state", e); ++ } ++ } ++ } ++ ++ private List getDeadUsers() { ++ final List users = mUserManager.getUsers(false); ++ final List onlyLiveUsers = mUserManager.getUsers(true); ++ ++ // UserInfo doesn't implement equals, so we'll roll our own ++ // Collection.removeAll implementation ++ final Iterator iter = users.iterator(); ++ while (iter.hasNext()) { ++ final UserInfo ui = iter.next(); ++ for (final UserInfo live : onlyLiveUsers) { ++ if (ui.id == live.id) { ++ iter.remove(); ++ break; ++ } ++ } ++ } ++ ++ return users; ++ } ++ ++ private static final class PackageManagerHelper implements ++ OverlayManagerServiceImpl.PackageManagerHelper { ++ ++ private final IPackageManager mPackageManager; ++ private final PackageManagerInternal mPackageManagerInternal; ++ ++ // Use a cache for performance and for consistency within OMS: because ++ // additional PACKAGE_* intents may be delivered while we process an ++ // intent, querying the PackageManagerService for the actual current ++ // state may lead to contradictions within OMS. Better then to lag ++ // behind until all pending intents have been processed. ++ private final SparseArray> mCache = new SparseArray<>(); ++ ++ public PackageManagerHelper() { ++ mPackageManager = getPackageManager(); ++ mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); ++ } ++ ++ public PackageInfo getPackageInfo(@NonNull final String packageName, final int userId, ++ final boolean useCache) { ++ if (useCache) { ++ final PackageInfo cachedPi = getCachedPackageInfo(packageName, userId); ++ if (cachedPi != null) { ++ return cachedPi; ++ } ++ } ++ try { ++ final PackageInfo pi = mPackageManager.getPackageInfo(packageName, 0, userId); ++ if (useCache && pi != null) { ++ cachePackageInfo(packageName, userId, pi); ++ } ++ return pi; ++ } catch (RemoteException e) { ++ // Intentionally left empty. ++ } ++ return null; ++ } ++ ++ @Override ++ public PackageInfo getPackageInfo(@NonNull final String packageName, final int userId) { ++ return getPackageInfo(packageName, userId, true); ++ } ++ ++ @Override ++ public boolean signaturesMatching(@NonNull final String packageName1, ++ @NonNull final String packageName2, final int userId) { ++ // The package manager does not support different versions of packages ++ // to be installed for different users: ignore userId for now. ++ try { ++ return mPackageManager.checkSignatures(packageName1, packageName2) == SIGNATURE_MATCH; ++ } catch (RemoteException e) { ++ // Intentionally left blank ++ } ++ return false; ++ } ++ ++ @Override ++ public List getOverlayPackages(final int userId) { ++ return mPackageManagerInternal.getOverlayPackages(userId); ++ } ++ ++ public PackageInfo getCachedPackageInfo(@NonNull final String packageName, ++ final int userId) { ++ final HashMap map = mCache.get(userId); ++ return map == null ? null : map.get(packageName); ++ } ++ ++ public void cachePackageInfo(@NonNull final String packageName, final int userId, ++ @NonNull final PackageInfo pi) { ++ HashMap map = mCache.get(userId); ++ if (map == null) { ++ map = new HashMap<>(); ++ mCache.put(userId, map); ++ } ++ map.put(packageName, pi); ++ } ++ ++ public void forgetPackageInfo(@NonNull final String packageName, final int userId) { ++ final HashMap map = mCache.get(userId); ++ if (map == null) { ++ return; ++ } ++ map.remove(packageName); ++ if (map.isEmpty()) { ++ mCache.delete(userId); ++ } ++ } ++ ++ public void forgetAllPackageInfos(final int userId) { ++ mCache.delete(userId); ++ } ++ ++ private static final String TAB1 = " "; ++ private static final String TAB2 = TAB1 + TAB1; ++ ++ public void dump(@NonNull final PrintWriter pw, final boolean verbose) { ++ pw.println("PackageInfo cache"); ++ ++ if (!verbose) { ++ int n = 0; ++ for (int i = 0; i < mCache.size(); i++) { ++ final int userId = mCache.keyAt(i); ++ n += mCache.get(userId).size(); ++ } ++ pw.println(TAB1 + n + " package(s)"); ++ return; ++ } ++ ++ if (mCache.size() == 0) { ++ pw.println(TAB1 + ""); ++ return; ++ } ++ ++ for (int i = 0; i < mCache.size(); i++) { ++ final int userId = mCache.keyAt(i); ++ pw.println(TAB1 + "User " + userId); ++ final HashMap map = mCache.get(userId); ++ for (Map.Entry entry : map.entrySet()) { ++ pw.println(TAB2 + entry.getKey() + ": " + entry.getValue()); ++ } ++ } ++ } ++ } ++} +diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java +new file mode 100644 +index 0000000..2a0d88b +--- /dev/null ++++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java +@@ -0,0 +1,478 @@ ++/* ++ * Copyright (C) 2016 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.om; ++ ++import static android.content.om.OverlayInfo.STATE_APPROVED_DISABLED; ++import static android.content.om.OverlayInfo.STATE_APPROVED_ENABLED; ++import static android.content.om.OverlayInfo.STATE_NOT_APPROVED_COMPONENT_DISABLED; ++import static android.content.om.OverlayInfo.STATE_NOT_APPROVED_DANGEROUS_OVERLAY; ++import static android.content.om.OverlayInfo.STATE_NOT_APPROVED_MISSING_TARGET; ++import static android.content.om.OverlayInfo.STATE_NOT_APPROVED_NO_IDMAP; ++import static com.android.server.om.OverlayManagerService.DEBUG; ++import static com.android.server.om.OverlayManagerService.TAG; ++ ++import android.annotation.NonNull; ++import android.annotation.Nullable; ++import android.content.om.OverlayInfo; ++import android.content.pm.ApplicationInfo; ++import android.content.pm.PackageInfo; ++import android.util.ArrayMap; ++import android.util.ArraySet; ++import android.util.Slog; ++ ++import java.io.PrintWriter; ++import java.util.ArrayList; ++import java.util.Iterator; ++import java.util.List; ++import java.util.Map; ++import java.util.Set; ++ ++/** ++ * Internal implementation of OverlayManagerService. ++ * ++ * Methods in this class should only be called by the OverlayManagerService. ++ * This class is not thread-safe; the caller is expected to ensure the ++ * necessary thread synchronization. ++ * ++ * @see OverlayManagerService ++ */ ++final class OverlayManagerServiceImpl { ++ private final PackageManagerHelper mPackageManager; ++ private final IdmapManager mIdmapManager; ++ private final OverlayManagerSettings mSettings; ++ ++ OverlayManagerServiceImpl(@NonNull final PackageManagerHelper packageManager, ++ @NonNull final IdmapManager idmapManager, ++ @NonNull final OverlayManagerSettings settings) { ++ mPackageManager = packageManager; ++ mIdmapManager = idmapManager; ++ mSettings = settings; ++ } ++ ++ /* ++ * Call this when switching to a new Android user. Will return a list of ++ * target packages that must refresh their overlays. This list is the union ++ * of two sets: the set of targets with currently active overlays, and the ++ * set of targets that had, but no longer have, active overlays. ++ */ ++ List onSwitchUser(final int newUserId) { ++ if (DEBUG) { ++ Slog.d(TAG, "onSwitchUser newUserId=" + newUserId); ++ } ++ ++ final Set packagesToUpdateAssets = new ArraySet<>(); ++ final Map> tmp = mSettings.getOverlaysForUser(newUserId); ++ final Map storedOverlayInfos = new ArrayMap<>(tmp.size()); ++ for (final List chunk: tmp.values()) { ++ for (final OverlayInfo oi: chunk) { ++ storedOverlayInfos.put(oi.packageName, oi); ++ } ++ } ++ ++ for (PackageInfo overlayPackage: mPackageManager.getOverlayPackages(newUserId)) { ++ final OverlayInfo oi = storedOverlayInfos.get(overlayPackage.packageName); ++ if (oi == null || !oi.targetPackageName.equals(overlayPackage.overlayTarget)) { ++ if (oi != null) { ++ packagesToUpdateAssets.add(oi.targetPackageName); ++ } ++ mSettings.init(overlayPackage.packageName, newUserId, ++ overlayPackage.overlayTarget, ++ overlayPackage.applicationInfo.getBaseCodePath()); ++ } ++ ++ try { ++ final PackageInfo targetPackage = ++ mPackageManager.getPackageInfo(overlayPackage.overlayTarget, newUserId); ++ updateState(targetPackage, overlayPackage, newUserId); ++ } catch (OverlayManagerSettings.BadKeyException e) { ++ Slog.e(TAG, "failed to update settings", e); ++ mSettings.remove(overlayPackage.packageName, newUserId); ++ } ++ ++ packagesToUpdateAssets.add(overlayPackage.overlayTarget); ++ storedOverlayInfos.remove(overlayPackage.packageName); ++ } ++ ++ // any OverlayInfo left in storedOverlayInfos is no longer ++ // installed and should be removed ++ for (final OverlayInfo oi: storedOverlayInfos.values()) { ++ mSettings.remove(oi.packageName, oi.userId); ++ removeIdmapIfPossible(oi); ++ packagesToUpdateAssets.add(oi.targetPackageName); ++ } ++ ++ // remove target packages that are not installed ++ final Iterator iter = packagesToUpdateAssets.iterator(); ++ while (iter.hasNext()) { ++ String targetPackageName = iter.next(); ++ if (mPackageManager.getPackageInfo(targetPackageName, newUserId) == null) { ++ iter.remove(); ++ } ++ } ++ ++ return new ArrayList(packagesToUpdateAssets); ++ } ++ ++ void onUserRemoved(final int userId) { ++ if (DEBUG) { ++ Slog.d(TAG, "onUserRemoved userId=" + userId); ++ } ++ mSettings.removeUser(userId); ++ } ++ ++ void onTargetPackageAdded(@NonNull final String packageName, final int userId) { ++ if (DEBUG) { ++ Slog.d(TAG, "onTargetPackageAdded packageName=" + packageName + " userId=" + userId); ++ } ++ ++ final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId); ++ updateAllOverlaysForTarget(packageName, userId, targetPackage); ++ } ++ ++ void onTargetPackageChanged(@NonNull final String packageName, final int userId) { ++ if (DEBUG) { ++ Slog.d(TAG, "onTargetPackageChanged packageName=" + packageName + " userId=" + userId); ++ } ++ ++ final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId); ++ updateAllOverlaysForTarget(packageName, userId, targetPackage); ++ } ++ ++ void onTargetPackageUpgrading(@NonNull final String packageName, final int userId) { ++ if (DEBUG) { ++ Slog.d(TAG, "onTargetPackageUpgrading packageName=" + packageName + " userId=" + userId); ++ } ++ ++ updateAllOverlaysForTarget(packageName, userId, null); ++ } ++ ++ void onTargetPackageUpgraded(@NonNull final String packageName, final int userId) { ++ if (DEBUG) { ++ Slog.d(TAG, "onTargetPackageUpgraded packageName=" + packageName + " userId=" + userId); ++ } ++ ++ final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId); ++ updateAllOverlaysForTarget(packageName, userId, targetPackage); ++ } ++ ++ void onTargetPackageRemoved(@NonNull final String packageName, final int userId) { ++ if (DEBUG) { ++ Slog.d(TAG, "onTargetPackageRemoved packageName=" + packageName + " userId=" + userId); ++ } ++ ++ updateAllOverlaysForTarget(packageName, userId, null); ++ } ++ ++ private void updateAllOverlaysForTarget(@NonNull final String packageName, final int userId, ++ @Nullable final PackageInfo targetPackage) { ++ final List ois = mSettings.getOverlaysForTarget(packageName, userId); ++ for (final OverlayInfo oi : ois) { ++ final PackageInfo overlayPackage = mPackageManager.getPackageInfo(oi.packageName, userId); ++ if (overlayPackage == null) { ++ mSettings.remove(oi.packageName, oi.userId); ++ removeIdmapIfPossible(oi); ++ } else { ++ try { ++ updateState(targetPackage, overlayPackage, userId); ++ } catch (OverlayManagerSettings.BadKeyException e) { ++ Slog.e(TAG, "failed to update settings", e); ++ mSettings.remove(oi.packageName, userId); ++ } ++ } ++ } ++ } ++ ++ void onOverlayPackageAdded(@NonNull final String packageName, final int userId) { ++ if (DEBUG) { ++ Slog.d(TAG, "onOverlayPackageAdded packageName=" + packageName + " userId=" + userId); ++ } ++ ++ final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); ++ if (overlayPackage == null) { ++ Slog.w(TAG, "overlay package " + packageName + " was added, but couldn't be found"); ++ onOverlayPackageRemoved(packageName, userId); ++ return; ++ } ++ ++ final PackageInfo targetPackage = ++ mPackageManager.getPackageInfo(overlayPackage.overlayTarget, userId); ++ ++ mSettings.init(packageName, userId, overlayPackage.overlayTarget, ++ overlayPackage.applicationInfo.getBaseCodePath()); ++ try { ++ updateState(targetPackage, overlayPackage, userId); ++ } catch (OverlayManagerSettings.BadKeyException e) { ++ Slog.e(TAG, "failed to update settings", e); ++ mSettings.remove(packageName, userId); ++ } ++ } ++ ++ void onOverlayPackageChanged(@NonNull final String packageName, final int userId) { ++ if (DEBUG) { ++ Slog.d(TAG, "onOverlayPackageChanged packageName=" + packageName + " userId=" + userId); ++ } ++ ++ final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); ++ if (overlayPackage == null) { ++ Slog.w(TAG, "overlay package " + packageName + " was changed, but couldn't be found"); ++ onOverlayPackageRemoved(packageName, userId); ++ return; ++ } ++ ++ final PackageInfo targetPackage = ++ mPackageManager.getPackageInfo(overlayPackage.overlayTarget, userId); ++ ++ try { ++ updateState(targetPackage, overlayPackage, userId); ++ } catch (OverlayManagerSettings.BadKeyException e) { ++ Slog.e(TAG, "failed to update settings", e); ++ mSettings.remove(packageName, userId); ++ } ++ } ++ ++ void onOverlayPackageUpgrading(@NonNull final String packageName, final int userId) { ++ if (DEBUG) { ++ Slog.d(TAG, "onOverlayPackageUpgrading packageName=" + packageName + " userId=" + userId); ++ } ++ ++ try { ++ final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId); ++ mSettings.setUpgrading(packageName, userId, true); ++ removeIdmapIfPossible(oi); ++ } catch (OverlayManagerSettings.BadKeyException e) { ++ Slog.e(TAG, "failed to update settings", e); ++ mSettings.remove(packageName, userId); ++ } ++ } ++ ++ void onOverlayPackageUpgraded(@NonNull final String packageName, final int userId) { ++ if (DEBUG) { ++ Slog.d(TAG, "onOverlayPackageUpgraded packageName=" + packageName + " userId=" + userId); ++ } ++ ++ final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); ++ if (overlayPackage == null) { ++ Slog.w(TAG, "overlay package " + packageName + " was upgraded, but couldn't be found"); ++ onOverlayPackageRemoved(packageName, userId); ++ return; ++ } ++ ++ try { ++ final String storedTargetPackageName = mSettings.getTargetPackageName(packageName, userId); ++ if (!overlayPackage.overlayTarget.equals(storedTargetPackageName)) { ++ // Sneaky little hobbitses, changing the overlay's target package ++ // from one version to the next! We can't use the old version's ++ // state. ++ mSettings.remove(packageName, userId); ++ onOverlayPackageAdded(packageName, userId); ++ return; ++ } ++ ++ mSettings.setUpgrading(packageName, userId, false); ++ final PackageInfo targetPackage = ++ mPackageManager.getPackageInfo(overlayPackage.overlayTarget, userId); ++ updateState(targetPackage, overlayPackage, userId); ++ } catch (OverlayManagerSettings.BadKeyException e) { ++ Slog.e(TAG, "failed to update settings", e); ++ mSettings.remove(packageName, userId); ++ } ++ } ++ ++ void onOverlayPackageRemoved(@NonNull final String packageName, final int userId) { ++ if (DEBUG) { ++ Slog.d(TAG, "onOverlayPackageRemoved packageName=" + packageName + " userId=" + userId); ++ } ++ ++ try { ++ final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId); ++ mSettings.remove(packageName, userId); ++ removeIdmapIfPossible(oi); ++ } catch (OverlayManagerSettings.BadKeyException e) { ++ Slog.e(TAG, "failed to remove overlay package", e); ++ } ++ } ++ ++ OverlayInfo onGetOverlayInfo(@NonNull final String packageName, final int userId) { ++ try { ++ return mSettings.getOverlayInfo(packageName, userId); ++ } catch (OverlayManagerSettings.BadKeyException e) { ++ return null; ++ } ++ } ++ ++ List onGetOverlayInfosForTarget(@NonNull final String targetPackageName, ++ final int userId) { ++ return mSettings.getOverlaysForTarget(targetPackageName, userId); ++ } ++ ++ Map> onGetOverlaysForUser(final int userId) { ++ return mSettings.getOverlaysForUser(userId); ++ } ++ ++ boolean onSetEnabled(@NonNull final String packageName, final boolean enable, ++ final int userId) { ++ if (DEBUG) { ++ Slog.d(TAG, String.format("onSetEnabled packageName=%s enable=%s userId=%d", ++ packageName, enable, userId)); ++ } ++ ++ final PackageInfo overlayPackage = mPackageManager.getPackageInfo(packageName, userId); ++ if (overlayPackage == null) { ++ return false; ++ } ++ ++ try { ++ final OverlayInfo oi = mSettings.getOverlayInfo(packageName, userId); ++ final PackageInfo targetPackage = ++ mPackageManager.getPackageInfo(oi.targetPackageName, userId); ++ mSettings.setEnabled(packageName, userId, enable); ++ updateState(targetPackage, overlayPackage, userId); ++ return true; ++ } catch (OverlayManagerSettings.BadKeyException e) { ++ return false; ++ } ++ } ++ ++ boolean onSetPriority(@NonNull final String packageName, ++ @NonNull final String newParentPackageName, final int userId) { ++ return mSettings.setPriority(packageName, newParentPackageName, userId); ++ } ++ ++ boolean onSetHighestPriority(@NonNull final String packageName, final int userId) { ++ return mSettings.setHighestPriority(packageName, userId); ++ } ++ ++ boolean onSetLowestPriority(@NonNull final String packageName, final int userId) { ++ return mSettings.setLowestPriority(packageName, userId); ++ } ++ ++ void onDump(@NonNull final PrintWriter pw) { ++ mSettings.dump(pw); ++ } ++ ++ List onGetEnabledOverlayPaths(@NonNull final String targetPackageName, ++ final int userId) { ++ final List overlays = mSettings.getOverlaysForTarget(targetPackageName, userId); ++ final List paths = new ArrayList<>(overlays.size()); ++ for (final OverlayInfo oi : overlays) { ++ if (oi.isEnabled()) { ++ paths.add(oi.baseCodePath); ++ } ++ } ++ return paths; ++ } ++ ++ private void updateState(@Nullable final PackageInfo targetPackage, ++ @NonNull final PackageInfo overlayPackage, final int userId) ++ throws OverlayManagerSettings.BadKeyException { ++ if (targetPackage != null) { ++ mIdmapManager.createIdmap(targetPackage, overlayPackage, userId); ++ } ++ ++ mSettings.setBaseCodePath(overlayPackage.packageName, userId, ++ overlayPackage.applicationInfo.getBaseCodePath()); ++ ++ final int currentState = mSettings.getState(overlayPackage.packageName, userId); ++ final int newState = calculateNewState(targetPackage, overlayPackage, userId); ++ if (currentState != newState) { ++ if (DEBUG) { ++ Slog.d(TAG, String.format("%s:%d: %s -> %s", ++ overlayPackage.packageName, userId, ++ OverlayInfo.stateToString(currentState), ++ OverlayInfo.stateToString(newState))); ++ } ++ mSettings.setState(overlayPackage.packageName, userId, newState); ++ } ++ } ++ ++ private int calculateNewState(@Nullable final PackageInfo targetPackage, ++ @NonNull final PackageInfo overlayPackage, final int userId) ++ throws OverlayManagerSettings.BadKeyException { ++ if (!overlayPackage.applicationInfo.enabled) { ++ return STATE_NOT_APPROVED_COMPONENT_DISABLED; ++ } ++ ++ if (targetPackage == null) { ++ return STATE_NOT_APPROVED_MISSING_TARGET; ++ } ++ ++ if (!mIdmapManager.idmapExists(overlayPackage, userId)) { ++ return STATE_NOT_APPROVED_NO_IDMAP; ++ } ++ ++ final boolean enableIfApproved = mSettings.getEnabled(overlayPackage.packageName, userId); ++ ++ if (mPackageManager.signaturesMatching(targetPackage.packageName, ++ overlayPackage.packageName, userId)) { ++ return enableIfApproved ? STATE_APPROVED_ENABLED : STATE_APPROVED_DISABLED; ++ } ++ ++ if ((overlayPackage.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { ++ return enableIfApproved ? STATE_APPROVED_ENABLED : STATE_APPROVED_DISABLED; ++ } ++ ++ if (!mIdmapManager.isDangerous(overlayPackage, userId)) { ++ return enableIfApproved ? STATE_APPROVED_ENABLED : STATE_APPROVED_DISABLED; ++ } ++ ++ return STATE_NOT_APPROVED_DANGEROUS_OVERLAY; ++ } ++ ++ private void removeIdmapIfPossible(@NonNull final OverlayInfo oi) { ++ // For a given package, all Android users share the same idmap file. ++ // This works because Android currently does not support users to ++ // install different versions of the same package. It also means we ++ // cannot remove an idmap file if any user still needs it. ++ // ++ // When/if the Android framework allows different versions of the same ++ // package to be installed for different users, idmap file handling ++ // should be revised: ++ // ++ // - an idmap file should be unique for each {user, package} pair ++ // ++ // - the path to the idmap file should be passed to the native Asset ++ // Manager layers, just like the path to the apk is passed today ++ // ++ // As part of that change, calls to this method should be replaced by ++ // direct calls to IdmapManager.removeIdmap, without looping over all ++ // users. ++ ++ if (!mIdmapManager.idmapExists(oi)) { ++ return; ++ } ++ final List userIds = mSettings.getUsers(); ++ for (final int userId : userIds) { ++ try { ++ final OverlayInfo tmp = mSettings.getOverlayInfo(oi.packageName, userId); ++ if (tmp != null && tmp.isEnabled()) { ++ // someone is still using the idmap file -> we cannot remove it ++ return; ++ } ++ } catch (OverlayManagerSettings.BadKeyException e) { ++ // intentionally left empty ++ } ++ } ++ mIdmapManager.removeIdmap(oi, oi.userId); ++ } ++ ++ interface PackageManagerHelper { ++ PackageInfo getPackageInfo(@NonNull String packageName, int userId); ++ boolean signaturesMatching(@NonNull String packageName1, @NonNull String packageName2, ++ int userId); ++ List getOverlayPackages(int userId); ++ } ++} +diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java +new file mode 100644 +index 0000000..af0bb64 +--- /dev/null ++++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java +@@ -0,0 +1,656 @@ ++/* ++ * Copyright (C) 2016 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.om; ++ ++import static android.content.om.OverlayInfo.STATE_NOT_APPROVED_UNKNOWN; ++import static com.android.server.om.OverlayManagerService.DEBUG; ++import static com.android.server.om.OverlayManagerService.TAG; ++ ++import android.annotation.NonNull; ++import android.annotation.Nullable; ++import android.content.om.OverlayInfo; ++import android.util.AndroidRuntimeException; ++import android.util.ArrayMap; ++import android.util.Slog; ++import android.util.Xml; ++ ++import com.android.internal.util.FastXmlSerializer; ++import com.android.internal.util.XmlUtils; ++ ++import org.xmlpull.v1.XmlPullParser; ++import org.xmlpull.v1.XmlPullParserException; ++ ++import java.io.IOException; ++import java.io.InputStream; ++import java.io.InputStreamReader; ++import java.io.OutputStream; ++import java.io.PrintWriter; ++import java.util.ArrayList; ++import java.util.Collections; ++import java.util.Iterator; ++import java.util.List; ++import java.util.ListIterator; ++import java.util.Map; ++ ++/** ++ * Data structure representing the current state of all overlay packages in the ++ * system. ++ * ++ * Modifications to the data are exposed through the ChangeListener interface. ++ * ++ * @see ChangeListener ++ * @see OverlayManagerService ++ */ ++final class OverlayManagerSettings { ++ private final List mListeners = new ArrayList<>(); ++ ++ private final ArrayList mItems = new ArrayList<>(); ++ ++ void init(@NonNull final String packageName, final int userId, ++ @NonNull final String targetPackageName, @NonNull final String baseCodePath) { ++ remove(packageName, userId); ++ final SettingsItem item = ++ new SettingsItem(packageName, userId, targetPackageName, baseCodePath); ++ mItems.add(item); ++ } ++ ++ void remove(@NonNull final String packageName, final int userId) { ++ final SettingsItem item = select(packageName, userId); ++ if (item == null) { ++ return; ++ } ++ final OverlayInfo oi = item.getOverlayInfo(); ++ mItems.remove(item); ++ if (oi != null) { ++ notifyOverlayRemoved(oi); ++ } ++ } ++ ++ boolean contains(@NonNull final String packageName, final int userId) { ++ return select(packageName, userId) != null; ++ } ++ ++ OverlayInfo getOverlayInfo(@NonNull final String packageName, final int userId) ++ throws BadKeyException { ++ final SettingsItem item = select(packageName, userId); ++ if (item == null) { ++ throw new BadKeyException(packageName, userId); ++ } ++ return item.getOverlayInfo(); ++ } ++ ++ String getTargetPackageName(@NonNull final String packageName, final int userId) ++ throws BadKeyException { ++ final SettingsItem item = select(packageName, userId); ++ if (item == null) { ++ throw new BadKeyException(packageName, userId); ++ } ++ return item.getTargetPackageName(); ++ } ++ ++ void setBaseCodePath(@NonNull final String packageName, final int userId, ++ @NonNull final String path) throws BadKeyException { ++ final SettingsItem item = select(packageName, userId); ++ if (item == null) { ++ throw new BadKeyException(packageName, userId); ++ } ++ item.setBaseCodePath(path); ++ notifySettingsChanged(); ++ } ++ ++ boolean getUpgrading(@NonNull final String packageName, final int userId) ++ throws BadKeyException { ++ final SettingsItem item = select(packageName, userId); ++ if (item == null) { ++ throw new BadKeyException(packageName, userId); ++ } ++ return item.isUpgrading(); ++ } ++ ++ void setUpgrading(@NonNull final String packageName, final int userId, final boolean newValue) ++ throws BadKeyException { ++ final SettingsItem item = select(packageName, userId); ++ if (item == null) { ++ throw new BadKeyException(packageName, userId); ++ } ++ if (newValue == item.isUpgrading()) { ++ return; // nothing to do ++ } ++ ++ if (newValue) { ++ final OverlayInfo oi = item.getOverlayInfo(); ++ item.setUpgrading(true); ++ item.setState(STATE_NOT_APPROVED_UNKNOWN); ++ notifyOverlayRemoved(oi); ++ } else { ++ item.setUpgrading(false); ++ } ++ notifySettingsChanged(); ++ } ++ ++ boolean getEnabled(@NonNull final String packageName, final int userId) throws BadKeyException { ++ final SettingsItem item = select(packageName, userId); ++ if (item == null) { ++ throw new BadKeyException(packageName, userId); ++ } ++ return item.isEnabled(); ++ } ++ ++ void setEnabled(@NonNull final String packageName, final int userId, final boolean enable) ++ throws BadKeyException { ++ final SettingsItem item = select(packageName, userId); ++ if (item == null) { ++ throw new BadKeyException(packageName, userId); ++ } ++ if (enable == item.isEnabled()) { ++ return; // nothing to do ++ } ++ ++ item.setEnabled(enable); ++ notifySettingsChanged(); ++ } ++ ++ int getState(@NonNull final String packageName, final int userId) throws BadKeyException { ++ final SettingsItem item = select(packageName, userId); ++ if (item == null) { ++ throw new BadKeyException(packageName, userId); ++ } ++ return item.getState(); ++ } ++ ++ void setState(@NonNull final String packageName, final int userId, final int state) ++ throws BadKeyException { ++ final SettingsItem item = select(packageName, userId); ++ if (item == null) { ++ throw new BadKeyException(packageName, userId); ++ } ++ final OverlayInfo previous = item.getOverlayInfo(); ++ item.setState(state); ++ final OverlayInfo current = item.getOverlayInfo(); ++ if (previous.state == STATE_NOT_APPROVED_UNKNOWN) { ++ notifyOverlayAdded(current); ++ notifySettingsChanged(); ++ } else if (current.state != previous.state) { ++ notifyOverlayChanged(current, previous); ++ notifySettingsChanged(); ++ } ++ } ++ ++ List getOverlaysForTarget(@NonNull final String targetPackageName, ++ final int userId) { ++ final List items = selectWhereTarget(targetPackageName, userId); ++ if (items.isEmpty()) { ++ return Collections.emptyList(); ++ } ++ final List out = new ArrayList<>(items.size()); ++ for (final SettingsItem item : items) { ++ if (item.isUpgrading()) { ++ continue; ++ } ++ out.add(item.getOverlayInfo()); ++ } ++ return out; ++ } ++ ++ Map> getOverlaysForUser(final int userId) { ++ final List items = selectWhereUser(userId); ++ if (items.isEmpty()) { ++ return Collections.emptyMap(); ++ } ++ final Map> out = new ArrayMap<>(items.size()); ++ for (final SettingsItem item : items) { ++ if (item.isUpgrading()) { ++ continue; ++ } ++ final String targetPackageName = item.getTargetPackageName(); ++ if (!out.containsKey(targetPackageName)) { ++ out.put(targetPackageName, new ArrayList()); ++ } ++ final List overlays = out.get(targetPackageName); ++ overlays.add(item.getOverlayInfo()); ++ } ++ return out; ++ } ++ ++ List getTargetPackageNamesForUser(final int userId) { ++ final List items = selectWhereUser(userId); ++ if (items.isEmpty()) { ++ return Collections.emptyList(); ++ } ++ final List out = new ArrayList<>(); ++ for (final SettingsItem item : items) { ++ if (item.isUpgrading()) { ++ continue; ++ } ++ final String targetPackageName = item.getTargetPackageName(); ++ if (!out.contains(targetPackageName)) { ++ out.add(targetPackageName); ++ } ++ } ++ return out; ++ } ++ ++ List getUsers() { ++ final ArrayList users = new ArrayList<>(); ++ for (final SettingsItem item : mItems) { ++ if (!users.contains(item.userId)) { ++ users.add(item.userId); ++ } ++ } ++ return users; ++ } ++ ++ void removeUser(final int userId) { ++ final Iterator iter = mItems.iterator(); ++ while (iter.hasNext()) { ++ final SettingsItem item = iter.next(); ++ if (item.userId == userId) { ++ iter.remove(); ++ } ++ } ++ } ++ ++ boolean setPriority(@NonNull final String packageName, ++ @NonNull final String newParentPackageName, final int userId) { ++ if (packageName.equals(newParentPackageName)) { ++ return false; ++ } ++ final SettingsItem rowToMove = select(packageName, userId); ++ if (rowToMove == null || rowToMove.isUpgrading()) { ++ return false; ++ } ++ final SettingsItem newParentRow = select(newParentPackageName, userId); ++ if (newParentRow == null || newParentRow.isUpgrading()) { ++ return false; ++ } ++ if (!rowToMove.getTargetPackageName().equals(newParentRow.getTargetPackageName())) { ++ return false; ++ } ++ ++ mItems.remove(rowToMove); ++ final ListIterator iter = mItems.listIterator(); ++ while (iter.hasNext()) { ++ final SettingsItem item = iter.next(); ++ if (item.userId == userId && item.packageName.equals(newParentPackageName)) { ++ iter.add(rowToMove); ++ notifyOverlayPriorityChanged(rowToMove.getOverlayInfo()); ++ notifySettingsChanged(); ++ return true; ++ } ++ } ++ ++ Slog.wtf(TAG, "failed to find the parent item a second time"); ++ return false; ++ } ++ ++ boolean setLowestPriority(@NonNull final String packageName, final int userId) { ++ final SettingsItem item = select(packageName, userId); ++ if (item == null || item.isUpgrading()) { ++ return false; ++ } ++ mItems.remove(item); ++ mItems.add(0, item); ++ notifyOverlayPriorityChanged(item.getOverlayInfo()); ++ notifySettingsChanged(); ++ return true; ++ } ++ ++ boolean setHighestPriority(@NonNull final String packageName, final int userId) { ++ final SettingsItem item = select(packageName, userId); ++ if (item == null || item.isUpgrading()) { ++ return false; ++ } ++ mItems.remove(item); ++ mItems.add(item); ++ notifyOverlayPriorityChanged(item.getOverlayInfo()); ++ notifySettingsChanged(); ++ return true; ++ } ++ ++ private static final String TAB1 = " "; ++ private static final String TAB2 = TAB1 + TAB1; ++ private static final String TAB3 = TAB2 + TAB1; ++ ++ void dump(@NonNull final PrintWriter pw) { ++ pw.println("Settings"); ++ dumpItems(pw); ++ dumpListeners(pw); ++ } ++ ++ private void dumpItems(@NonNull final PrintWriter pw) { ++ pw.println(TAB1 + "Items"); ++ ++ if (mItems.isEmpty()) { ++ pw.println(TAB2 + ""); ++ return; ++ } ++ ++ for (final SettingsItem item : mItems) { ++ final StringBuilder sb = new StringBuilder(); ++ sb.append(TAB2 + item.packageName + ":" + item.userId + " {\n"); ++ sb.append(TAB3 + "packageName.......: " + item.packageName + "\n"); ++ sb.append(TAB3 + "userId............: " + item.userId + "\n"); ++ sb.append(TAB3 + "targetPackageName.: " + item.getTargetPackageName() + "\n"); ++ sb.append(TAB3 + "baseCodePath......: " + item.getBaseCodePath() + "\n"); ++ sb.append(TAB3 + "state.............: " + OverlayInfo.stateToString(item.getState()) + "\n"); ++ sb.append(TAB3 + "isEnabled.........: " + item.isEnabled() + "\n"); ++ sb.append(TAB3 + "isUpgrading.......: " + item.isUpgrading() + "\n"); ++ sb.append(TAB2 + "}"); ++ pw.println(sb.toString()); ++ } ++ } ++ ++ private void dumpListeners(@NonNull final PrintWriter pw) { ++ pw.println(TAB1 + "Change listeners"); ++ ++ if (mListeners.isEmpty()) { ++ pw.println(TAB2 + ""); ++ return; ++ } ++ ++ for (ChangeListener ch : mListeners) { ++ pw.println(TAB2 + ch); ++ } ++ ++ } ++ ++ void restore(@NonNull final InputStream is) throws IOException, XmlPullParserException { ++ Serializer.restore(mItems, is); ++ } ++ ++ void persist(@NonNull final OutputStream os) throws IOException, XmlPullParserException { ++ Serializer.persist(mItems, os); ++ } ++ ++ private static final class Serializer { ++ private static final String TAG_OVERLAYS = "overlays"; ++ private static final String TAG_ITEM = "item"; ++ ++ private static final String ATTR_BASE_CODE_PATH = "baseCodePath"; ++ private static final String ATTR_IS_ENABLED = "isEnabled"; ++ private static final String ATTR_IS_UPGRADING = "isUpgrading"; ++ private static final String ATTR_PACKAGE_NAME = "packageName"; ++ private static final String ATTR_STATE = "state"; ++ private static final String ATTR_TARGET_PACKAGE_NAME = "targetPackageName"; ++ private static final String ATTR_USER_ID = "userId"; ++ private static final String ATTR_VERSION = "version"; ++ ++ private static final int CURRENT_VERSION = 1; ++ ++ public static void restore(@NonNull final ArrayList table, ++ @NonNull final InputStream is) throws IOException, XmlPullParserException { ++ ++ table.clear(); ++ final XmlPullParser parser = Xml.newPullParser(); ++ parser.setInput(new InputStreamReader(is)); ++ XmlUtils.beginDocument(parser, TAG_OVERLAYS); ++ int version = XmlUtils.readIntAttribute(parser, ATTR_VERSION); ++ if (version != CURRENT_VERSION) { ++ throw new XmlPullParserException("unrecognized version " + version); ++ } ++ int depth = parser.getDepth(); ++ ++ while (XmlUtils.nextElementWithin(parser, depth)) { ++ switch (parser.getName()) { ++ case TAG_ITEM: ++ final SettingsItem item = restoreRow(parser, depth + 1); ++ table.add(item); ++ break; ++ } ++ } ++ } ++ ++ private static SettingsItem restoreRow(@NonNull final XmlPullParser parser, final int depth) ++ throws IOException { ++ final String packageName = XmlUtils.readStringAttribute(parser, ATTR_PACKAGE_NAME); ++ final int userId = XmlUtils.readIntAttribute(parser, ATTR_USER_ID); ++ final String targetPackageName = XmlUtils.readStringAttribute(parser, ++ ATTR_TARGET_PACKAGE_NAME); ++ final String baseCodePath = XmlUtils.readStringAttribute(parser, ATTR_BASE_CODE_PATH); ++ final int state = XmlUtils.readIntAttribute(parser, ATTR_STATE); ++ final boolean isEnabled = XmlUtils.readBooleanAttribute(parser, ATTR_IS_ENABLED); ++ final boolean isUpgrading = XmlUtils.readBooleanAttribute(parser, ATTR_IS_UPGRADING); ++ ++ return new SettingsItem(packageName, userId, targetPackageName, baseCodePath, state, ++ isEnabled, isUpgrading); ++ } ++ ++ public static void persist(@NonNull final ArrayList table, ++ @NonNull final OutputStream os) throws IOException, XmlPullParserException { ++ final FastXmlSerializer xml = new FastXmlSerializer(); ++ xml.setOutput(os, "utf-8"); ++ xml.startDocument(null, true); ++ xml.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); ++ xml.startTag(null, TAG_OVERLAYS); ++ XmlUtils.writeIntAttribute(xml, ATTR_VERSION, CURRENT_VERSION); ++ ++ for (final SettingsItem item : table) { ++ persistRow(xml, item); ++ } ++ xml.endTag(null, TAG_OVERLAYS); ++ xml.endDocument(); ++ } ++ ++ private static void persistRow(@NonNull final FastXmlSerializer xml, ++ @NonNull final SettingsItem item) throws IOException { ++ xml.startTag(null, TAG_ITEM); ++ XmlUtils.writeStringAttribute(xml, ATTR_PACKAGE_NAME, item.packageName); ++ XmlUtils.writeIntAttribute(xml, ATTR_USER_ID, item.userId); ++ XmlUtils.writeStringAttribute(xml, ATTR_TARGET_PACKAGE_NAME, item.targetPackageName); ++ XmlUtils.writeStringAttribute(xml, ATTR_BASE_CODE_PATH, item.baseCodePath); ++ XmlUtils.writeIntAttribute(xml, ATTR_STATE, item.state); ++ XmlUtils.writeBooleanAttribute(xml, ATTR_IS_ENABLED, item.isEnabled); ++ XmlUtils.writeBooleanAttribute(xml, ATTR_IS_UPGRADING, item.isUpgrading); ++ xml.endTag(null, TAG_ITEM); ++ } ++ } ++ ++ private static final class SettingsItem { ++ private final int userId; ++ private final String packageName; ++ private final String targetPackageName; ++ private String baseCodePath; ++ private int state; ++ private boolean isEnabled; ++ private boolean isUpgrading; ++ private OverlayInfo cache; ++ ++ SettingsItem(@NonNull final String packageName, final int userId, ++ @NonNull final String targetPackageName, @NonNull final String baseCodePath, ++ final int state, final boolean isEnabled, final boolean isUpgrading) { ++ this.packageName = packageName; ++ this.userId = userId; ++ this.targetPackageName = targetPackageName; ++ this.baseCodePath = baseCodePath; ++ this.state = state; ++ this.isEnabled = isEnabled; ++ this.isUpgrading = isUpgrading; ++ cache = null; ++ } ++ ++ SettingsItem(@NonNull final String packageName, final int userId, ++ @NonNull final String targetPackageName, @NonNull final String baseCodePath) { ++ this(packageName, userId, targetPackageName, baseCodePath, STATE_NOT_APPROVED_UNKNOWN, ++ false, false); ++ } ++ ++ private String getTargetPackageName() { ++ return targetPackageName; ++ } ++ ++ private String getBaseCodePath() { ++ return baseCodePath; ++ } ++ ++ private void setBaseCodePath(@NonNull final String path) { ++ if (!baseCodePath.equals(path)) { ++ baseCodePath = path; ++ invalidateCache(); ++ } ++ } ++ ++ private int getState() { ++ return state; ++ } ++ ++ private void setState(final int state) { ++ if (this.state != state) { ++ this.state = state; ++ invalidateCache(); ++ } ++ } ++ ++ private boolean isEnabled() { ++ return isEnabled; ++ } ++ ++ private void setEnabled(final boolean enable) { ++ if (isEnabled != enable) { ++ isEnabled = enable; ++ invalidateCache(); ++ } ++ } ++ ++ private boolean isUpgrading() { ++ return isUpgrading; ++ } ++ ++ private void setUpgrading(final boolean upgrading) { ++ if (isUpgrading != upgrading) { ++ isUpgrading = upgrading; ++ invalidateCache(); ++ } ++ } ++ ++ private OverlayInfo getOverlayInfo() { ++ if (isUpgrading) { ++ return null; ++ } ++ if (cache == null) { ++ cache = new OverlayInfo(packageName, targetPackageName, baseCodePath, ++ state, userId); ++ } ++ return cache; ++ } ++ ++ private void invalidateCache() { ++ cache = null; ++ } ++ } ++ ++ private SettingsItem select(@NonNull final String packageName, final int userId) { ++ for (final SettingsItem item : mItems) { ++ if (item.userId == userId && item.packageName.equals(packageName)) { ++ return item; ++ } ++ } ++ return null; ++ } ++ ++ private List selectWhereUser(final int userId) { ++ final ArrayList items = new ArrayList<>(); ++ for (final SettingsItem item : mItems) { ++ if (item.userId == userId) { ++ items.add(item); ++ } ++ } ++ return items; ++ } ++ ++ private List selectWhereTarget(@NonNull final String targetPackageName, ++ final int userId) { ++ final ArrayList items = new ArrayList<>(); ++ for (final SettingsItem item : mItems) { ++ if (item.userId == userId && item.getTargetPackageName().equals(targetPackageName)) { ++ items.add(item); ++ } ++ } ++ return items; ++ } ++ ++ private void assertNotNull(@Nullable final Object o) { ++ if (o == null) { ++ throw new AndroidRuntimeException("object must not be null"); ++ } ++ } ++ ++ void addChangeListener(@NonNull final ChangeListener listener) { ++ mListeners.add(listener); ++ } ++ ++ void removeChangeListener(@NonNull final ChangeListener listener) { ++ mListeners.remove(listener); ++ } ++ ++ private void notifySettingsChanged() { ++ for (final ChangeListener listener : mListeners) { ++ listener.onSettingsChanged(); ++ } ++ } ++ ++ private void notifyOverlayAdded(@NonNull final OverlayInfo oi) { ++ if (DEBUG) { ++ assertNotNull(oi); ++ } ++ for (final ChangeListener listener : mListeners) { ++ listener.onOverlayAdded(oi); ++ } ++ } ++ ++ private void notifyOverlayRemoved(@NonNull final OverlayInfo oi) { ++ if (DEBUG) { ++ assertNotNull(oi); ++ } ++ for (final ChangeListener listener : mListeners) { ++ listener.onOverlayRemoved(oi); ++ } ++ } ++ ++ private void notifyOverlayChanged(@NonNull final OverlayInfo oi, ++ @NonNull final OverlayInfo oldOi) { ++ if (DEBUG) { ++ assertNotNull(oi); ++ assertNotNull(oldOi); ++ } ++ for (final ChangeListener listener : mListeners) { ++ listener.onOverlayChanged(oi, oldOi); ++ } ++ } ++ ++ private void notifyOverlayPriorityChanged(@NonNull final OverlayInfo oi) { ++ if (DEBUG) { ++ assertNotNull(oi); ++ } ++ for (final ChangeListener listener : mListeners) { ++ listener.onOverlayPriorityChanged(oi); ++ } ++ } ++ ++ interface ChangeListener { ++ void onSettingsChanged(); ++ void onOverlayAdded(@NonNull OverlayInfo oi); ++ void onOverlayRemoved(@NonNull OverlayInfo oi); ++ void onOverlayChanged(@NonNull OverlayInfo oi, @NonNull OverlayInfo oldOi); ++ void onOverlayPriorityChanged(@NonNull OverlayInfo oi); ++ } ++ ++ static final class BadKeyException extends RuntimeException { ++ public BadKeyException(@NonNull final String packageName, final int userId) { ++ super("Bad key packageName=" + packageName + " userId=" + userId); ++ } ++ } ++} +diff --git a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java +new file mode 100644 +index 0000000..d6f5373 +--- /dev/null ++++ b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java +@@ -0,0 +1,179 @@ ++/* ++ * Copyright (C) 2016 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.om; ++ ++import android.annotation.NonNull; ++import android.annotation.Nullable; ++import android.content.om.IOverlayManager; ++import android.content.om.OverlayInfo; ++import android.os.RemoteException; ++import android.os.ShellCommand; ++import android.os.UserHandle; ++ ++import java.io.PrintWriter; ++import java.util.List; ++import java.util.Map; ++ ++/** ++ * Implementation of 'cmd overlay' commands. ++ * ++ * This class provides an interface to the OverlayManagerService via adb. ++ * Intended only for manual debugging. Execute 'adb exec-out cmd overlay help' ++ * for a list of available commands. ++ */ ++final class OverlayManagerShellCommand extends ShellCommand { ++ private final IOverlayManager mInterface; ++ ++ OverlayManagerShellCommand(@NonNull final IOverlayManager iom) { ++ mInterface = iom; ++ } ++ ++ @Override ++ public int onCommand(@Nullable final String cmd) { ++ if (cmd == null) { ++ return handleDefaultCommands(cmd); ++ } ++ final PrintWriter err = getErrPrintWriter(); ++ try { ++ switch (cmd) { ++ case "list": ++ return runList(); ++ case "enable": ++ return runEnableDisable(true); ++ case "disable": ++ return runEnableDisable(false); ++ case "set-priority": ++ return runSetPriority(); ++ default: ++ return handleDefaultCommands(cmd); ++ } ++ } catch (IllegalArgumentException e) { ++ err.println("Error: " + e.getMessage()); ++ } catch (RemoteException e) { ++ err.println("Remote exception: " + e); ++ } ++ return -1; ++ } ++ ++ @Override ++ public void onHelp() { ++ final PrintWriter out = getOutPrintWriter(); ++ out.println("Overlay manager (overlay) commands:"); ++ out.println(" help"); ++ out.println(" Print this help text."); ++ out.println(" dump [--verbose] [--user USER_ID] [PACKAGE [PACKAGE [...]]]"); ++ out.println(" Print debugging information about the overlay manager."); ++ out.println(" list [--user USER_ID] [PACKAGE [PACKAGE [...]]]"); ++ out.println(" Print information about target and overlay packages."); ++ out.println(" Overlay packages are printed in priority order. With optional"); ++ out.println(" parameters PACKAGEs, limit output to the specified packages"); ++ out.println(" but include more information about each package."); ++ out.println(" enable [--user USER_ID] PACKAGE"); ++ out.println(" Enable overlay package PACKAGE."); ++ out.println(" disable [--user USER_ID] PACKAGE"); ++ out.println(" Disable overlay package PACKAGE."); ++ out.println(" set-priority [--user USER_ID] PACKAGE PARENT|lowest|highest"); ++ out.println(" Change the priority of the overlay PACKAGE to be just higher than"); ++ out.println(" the priority of PACKAGE_PARENT If PARENT is the special keyword"); ++ out.println(" 'lowest', change priority of PACKAGE to the lowest priority."); ++ out.println(" If PARENT is the special keyword 'highest', change priority of"); ++ out.println(" PACKAGE to the highest priority."); ++ } ++ ++ private int runList() throws RemoteException { ++ final PrintWriter out = getOutPrintWriter(); ++ final PrintWriter err = getErrPrintWriter(); ++ ++ int userId = UserHandle.USER_SYSTEM; ++ String opt; ++ while ((opt = getNextOption()) != null) { ++ switch (opt) { ++ case "--user": ++ userId = UserHandle.parseUserArg(getNextArgRequired()); ++ break; ++ default: ++ err.println("Error: Unknown option: " + opt); ++ return 1; ++ } ++ } ++ ++ final Map> allOverlays = mInterface.getAllOverlays(userId); ++ for (final String targetPackageName : allOverlays.keySet()) { ++ out.println(targetPackageName); ++ for (final OverlayInfo oi : allOverlays.get(targetPackageName)) { ++ String status = "---"; ++ if (oi.isApproved()) { ++ status = "[ ]"; ++ } ++ if (oi.isEnabled()) { ++ status = "[x]"; ++ } ++ out.println(String.format("%s %s", status, oi.packageName)); ++ } ++ out.println(); ++ } ++ return 0; ++ } ++ ++ private int runEnableDisable(final boolean enable) throws RemoteException { ++ final PrintWriter err = getErrPrintWriter(); ++ ++ int userId = UserHandle.USER_SYSTEM; ++ String opt; ++ while ((opt = getNextOption()) != null) { ++ switch (opt) { ++ case "--user": ++ userId = UserHandle.parseUserArg(getNextArgRequired()); ++ break; ++ default: ++ err.println("Error: Unknown option: " + opt); ++ return 1; ++ } ++ } ++ ++ final String packageName = getNextArgRequired(); ++ return mInterface.setEnabled(packageName, enable, userId) ? 0 : 1; ++ } ++ ++ private int runSetPriority() throws RemoteException { ++ final PrintWriter err = getErrPrintWriter(); ++ ++ int userId = UserHandle.USER_SYSTEM; ++ String opt; ++ while ((opt = getNextOption()) != null) { ++ switch (opt) { ++ case "--user": ++ userId = UserHandle.parseUserArg(getNextArgRequired()); ++ break; ++ default: ++ err.println("Error: Unknown option: " + opt); ++ return 1; ++ } ++ } ++ ++ final String packageName = getNextArgRequired(); ++ final String newParentPackageName = getNextArgRequired(); ++ ++ if ("highest".equals(newParentPackageName)) { ++ return mInterface.setHighestPriority(packageName, userId) ? 0 : 1; ++ } else if ("lowest".equals(newParentPackageName)) { ++ return mInterface.setLowestPriority(packageName, userId) ? 0 : 1; ++ } else { ++ return mInterface.setPriority(packageName, newParentPackageName, userId) ? 0 : 1; ++ } ++ } ++} +diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java +index 583cb23..3892f8f 100644 +--- a/services/core/java/com/android/server/pm/PackageManagerService.java ++++ b/services/core/java/com/android/server/pm/PackageManagerService.java +@@ -21444,6 +21444,47 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); + return mSettings.wasPackageEverLaunchedLPr(packageName, userId); + } + } ++ ++ @Override ++ public List getOverlayPackages(int userId) { ++ final ArrayList overlayPackages = new ArrayList(); ++ synchronized (mPackages) { ++ for (PackageParser.Package p : mPackages.values()) { ++ if (p.mOverlayTarget != null) { ++ PackageInfo pkg = generatePackageInfo((PackageSetting)p.mExtras, 0, userId); ++ if (pkg != null) { ++ overlayPackages.add(pkg); ++ } ++ } ++ } ++ } ++ return overlayPackages; ++ } ++ ++ @Override ++ public List getTargetPackageNames(int userId) { ++ List targetPackages = new ArrayList<>(); ++ synchronized (mPackages) { ++ for (PackageParser.Package p : mPackages.values()) { ++ if (p.mOverlayTarget == null) { ++ targetPackages.add(p.packageName); ++ } ++ } ++ } ++ return targetPackages; ++ } ++ ++ @Override ++ public void setResourceDirs(int userId, String packageName, String[] resourceDirs) { ++ // TODO: uncomment when we integrate OMS properly ++ // synchronized (mPackages) { ++ // PackageSetting ps = mSettings.mPackages.get(packageName); ++ // if (ps == null) { ++ // return; ++ // } ++ // ps.setResourceDirs(resourceDirs, userId); ++ // } ++ } + } + + @Override +-- +2.9.3 + diff --git a/frameworks/base/0003-OMS7-N-Integrate-OverlayManagerService-into-framewor.patch b/frameworks/base/0003-OMS7-N-Integrate-OverlayManagerService-into-framewor.patch new file mode 100644 index 0000000..5104306 --- /dev/null +++ b/frameworks/base/0003-OMS7-N-Integrate-OverlayManagerService-into-framewor.patch @@ -0,0 +1,1473 @@ +From 2a5bd30d939fd04580f162115ddba6c359290b6f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= +Date: Thu, 2 Jun 2016 09:35:31 +0200 +Subject: [PATCH 03/38] OMS7-N: Integrate OverlayManagerService into framework + [3/11] + +Hand over ownership of overlays to OverlayManagerService. + +Changes to a package's overlays are propagated using the activity life +cycle. Affected activities will be recreated as needed. This provides a +well-defined point to modify an application's assets while the +application is paused. + +Consolidate how overlays targeting the system and overlays targeting +regular applications are handled. Previously, system overlays were +handled as a special case. Now, everything is handled identically. As a +side effect, the call to idmap --scan during Zygote boot has become +obsolete and is removed. + +Deprecate and remove use of SCAN_TRUSTED_OVERLAY from +PackageManagerService. Previously, the flag was used to restrict what +paths overlays were allowed to be installed in. Now, overlay packages +are first class packages and it is up to the OverlayManagerService to +decide whether to use the overlay or not. + +Information on what overlays to use is recorded in +ApplicationInfo.resourceDirs. The PackageManagerService is responsible +for the creation of ApplicationInfo objects. The OverlayManagerService +is responsible for informing the PackageManagerService in advance about +what resourceDirs to use. + +When launching an application, the ApplicationInfo is already populated +with up-to-date information about overlays. + +When enabling or disabling an overlay for a running application, the +OverlayManagerService first notifies the OverlayManagerService about the +updated resourceDirs. It then tells the ActivityManagerService to push +the new ApplicationInfo object to the application's ActivityThread. +Finally the application requests its ResourcesManager to create new +ResourcesImpl objects based on the updated paths. + +Change-Id: If0b1eaa690c38f9c33f7c8dc981314205a73fa9c +--- + cmds/idmap/Android.mk | 2 +- + cmds/idmap/idmap.cpp | 55 --------- + cmds/idmap/idmap.h | 6 - + core/java/android/app/ActivityManagerNative.java | 30 +++++ + core/java/android/app/ActivityThread.java | 45 +++++++ + core/java/android/app/ApplicationThreadNative.java | 21 ++++ + core/java/android/app/IActivityManager.java | 2 + + core/java/android/app/IApplicationThread.java | 2 + + core/java/android/app/ResourcesManager.java | 119 ++++++++++++++---- + core/java/android/content/pm/PackageParser.java | 20 +-- + core/java/android/content/pm/PackageUserState.java | 6 + + core/jni/android_util_AssetManager.cpp | 95 --------------- + include/androidfw/AssetManager.h | 15 +-- + libs/androidfw/AssetManager.cpp | 104 ---------------- + .../com/android/server/SystemServiceManager.java | 24 ++-- + .../android/server/am/ActivityManagerService.java | 51 ++++++++ + .../android/server/om/OverlayManagerService.java | 61 +++++----- + .../android/server/pm/PackageManagerService.java | 134 ++++----------------- + .../com/android/server/pm/PackageSettingBase.java | 8 +- + .../core/java/com/android/server/pm/Settings.java | 4 +- + services/java/com/android/server/SystemServer.java | 4 + + 21 files changed, 335 insertions(+), 473 deletions(-) + +diff --git a/cmds/idmap/Android.mk b/cmds/idmap/Android.mk +index 50ccb07..eb6da18 100644 +--- a/cmds/idmap/Android.mk ++++ b/cmds/idmap/Android.mk +@@ -15,7 +15,7 @@ + LOCAL_PATH:= $(call my-dir) + include $(CLEAR_VARS) + +-LOCAL_SRC_FILES := idmap.cpp create.cpp scan.cpp inspect.cpp ++LOCAL_SRC_FILES := idmap.cpp create.cpp inspect.cpp + + LOCAL_SHARED_LIBRARIES := liblog libutils libandroidfw + +diff --git a/cmds/idmap/idmap.cpp b/cmds/idmap/idmap.cpp +index 3ab1915..d388977 100644 +--- a/cmds/idmap/idmap.cpp ++++ b/cmds/idmap/idmap.cpp +@@ -13,8 +13,6 @@ SYNOPSIS \n\ + idmap --help \n\ + idmap --fd target overlay fd \n\ + idmap --path target overlay idmap \n\ +- idmap --scan target-package-name-to-look-for path-to-target-apk dir-to-hold-idmaps \\\ +- dir-to-scan [additional-dir-to-scan [additional-dir-to-scan [...]]]\n\ + idmap --inspect idmap \n\ + \n\ + DESCRIPTION \n\ +@@ -49,11 +47,6 @@ OPTIONS \n\ + --path: create idmap for target package 'target' (path to apk) and overlay package \n\ + 'overlay' (path to apk); write results to 'idmap' (path). \n\ + \n\ +- --scan: non-recursively search directory 'dir-to-scan' (path) for overlay packages with \n\ +- target package 'target-package-name-to-look-for' (package name) present at\n\ +- 'path-to-target-apk' (path to apk). For each overlay package found, create an\n\ +- idmap file in 'dir-to-hold-idmaps' (path). \n\ +-\n\ + --inspect: decode the binary format of 'idmap' (path) and display the contents in a \n\ + debug-friendly format. \n\ + \n\ +@@ -97,16 +90,6 @@ EXAMPLES \n\ + NOTES \n\ + This tool and its expected invocation from installd is modelled on dexopt."; + +- bool verify_directory_readable(const char *path) +- { +- return access(path, R_OK | X_OK) == 0; +- } +- +- bool verify_directory_writable(const char *path) +- { +- return access(path, W_OK) == 0; +- } +- + bool verify_file_readable(const char *path) + { + return access(path, R_OK) == 0; +@@ -167,36 +150,6 @@ NOTES \n\ + return idmap_create_path(target_apk_path, overlay_apk_path, idmap_path); + } + +- int maybe_scan(const char *target_package_name, const char *target_apk_path, +- const char *idmap_dir, const android::Vector *overlay_dirs) +- { +- if (!verify_root_or_system()) { +- fprintf(stderr, "error: permission denied: not user root or user system\n"); +- return -1; +- } +- +- if (!verify_file_readable(target_apk_path)) { +- ALOGD("error: failed to read apk %s: %s\n", target_apk_path, strerror(errno)); +- return -1; +- } +- +- if (!verify_directory_writable(idmap_dir)) { +- ALOGD("error: no write access to %s: %s\n", idmap_dir, strerror(errno)); +- return -1; +- } +- +- const size_t N = overlay_dirs->size(); +- for (size_t i = 0; i < N; i++) { +- const char *dir = overlay_dirs->itemAt(i); +- if (!verify_directory_readable(dir)) { +- ALOGD("error: no read access to %s: %s\n", dir, strerror(errno)); +- return -1; +- } +- } +- +- return idmap_scan(target_package_name, target_apk_path, idmap_dir, overlay_dirs); +- } +- + int maybe_inspect(const char *idmap_path) + { + // anyone (not just root or system) may do --inspect +@@ -235,14 +188,6 @@ int main(int argc, char **argv) + return maybe_create_path(argv[2], argv[3], argv[4]); + } + +- if (argc >= 6 && !strcmp(argv[1], "--scan")) { +- android::Vector v; +- for (int i = 5; i < argc; i++) { +- v.push(argv[i]); +- } +- return maybe_scan(argv[2], argv[3], argv[4], &v); +- } +- + if (argc == 3 && !strcmp(argv[1], "--inspect")) { + return maybe_inspect(argv[2]); + } +diff --git a/cmds/idmap/idmap.h b/cmds/idmap/idmap.h +index 8d4210b..5914de9 100644 +--- a/cmds/idmap/idmap.h ++++ b/cmds/idmap/idmap.h +@@ -25,12 +25,6 @@ int idmap_create_path(const char *target_apk_path, const char *overlay_apk_path, + + int idmap_create_fd(const char *target_apk_path, const char *overlay_apk_path, int fd); + +-// Regarding target_package_name: the idmap_scan implementation should +-// be able to extract this from the manifest in target_apk_path, +-// simplifying the external API. +-int idmap_scan(const char *target_package_name, const char *target_apk_path, +- const char *idmap_dir, const android::Vector *overlay_dirs); +- + int idmap_inspect(const char *idmap_path); + + #endif // _IDMAP_H_ +diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java +index 50479c8..389f342 100644 +--- a/core/java/android/app/ActivityManagerNative.java ++++ b/core/java/android/app/ActivityManagerNative.java +@@ -16,6 +16,7 @@ + + package android.app; + ++import android.annotation.NonNull; + import android.annotation.UserIdInt; + import android.app.ActivityManager.StackInfo; + import android.app.assist.AssistContent; +@@ -1226,6 +1227,19 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM + return true; + } + ++ case UPDATE_ASSETS_TRANSACTION: { ++ data.enforceInterface(IActivityManager.descriptor); ++ final int userId = data.readInt(); ++ final int N = data.readInt(); ++ final List packageNames = new ArrayList<>(); ++ for (int i = 0; i < N; i++) { ++ packageNames.add(data.readString()); ++ } ++ updateAssets(userId, packageNames); ++ reply.writeNoException(); ++ return true; ++ } ++ + case SET_REQUESTED_ORIENTATION_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IBinder token = data.readStrongBinder(); +@@ -4586,6 +4600,22 @@ class ActivityManagerProxy implements IActivityManager + data.recycle(); + reply.recycle(); + } ++ public void updateAssets(final int userId, @NonNull final List packageNames) ++ throws RemoteException ++ { ++ final Parcel data = Parcel.obtain(); ++ final Parcel reply = Parcel.obtain(); ++ data.writeInterfaceToken(IActivityManager.descriptor); ++ data.writeInt(userId); ++ data.writeInt(packageNames.size()); ++ for (int i = 0; i < packageNames.size(); i++) { ++ data.writeString(packageNames.get(i)); ++ } ++ mRemote.transact(UPDATE_ASSETS_TRANSACTION, data, reply, 0); ++ reply.readException(); ++ data.recycle(); ++ reply.recycle(); ++ } + public void setRequestedOrientation(IBinder token, int requestedOrientation) + throws RemoteException { + Parcel data = Parcel.obtain(); +diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java +index 2d22f26..55fc25d 100644 +--- a/core/java/android/app/ActivityThread.java ++++ b/core/java/android/app/ActivityThread.java +@@ -897,6 +897,14 @@ public final class ActivityThread { + sendMessage(H.CONFIGURATION_CHANGED, config); + } + ++ public void scheduleAssetsChanged(@NonNull final String packageName, ++ @NonNull final ApplicationInfo ai) { ++ final SomeArgs args = SomeArgs.obtain(); ++ args.arg1 = packageName; ++ args.arg2 = ai; ++ sendMessage(H.ASSETS_CHANGED, args); ++ } ++ + public void updateTimeZone() { + TimeZone.setDefault(null); + } +@@ -1405,6 +1413,7 @@ public final class ActivityThread { + public static final int MULTI_WINDOW_MODE_CHANGED = 152; + public static final int PICTURE_IN_PICTURE_MODE_CHANGED = 153; + public static final int LOCAL_VOICE_INTERACTION_STARTED = 154; ++ public static final int ASSETS_CHANGED = 155; + + String codeToString(int code) { + if (DEBUG_MESSAGES) { +@@ -1461,6 +1470,7 @@ public final class ActivityThread { + case MULTI_WINDOW_MODE_CHANGED: return "MULTI_WINDOW_MODE_CHANGED"; + case PICTURE_IN_PICTURE_MODE_CHANGED: return "PICTURE_IN_PICTURE_MODE_CHANGED"; + case LOCAL_VOICE_INTERACTION_STARTED: return "LOCAL_VOICE_INTERACTION_STARTED"; ++ case ASSETS_CHANGED: return "ASSETS_CHANGED"; + } + } + return Integer.toString(code); +@@ -1716,6 +1726,10 @@ public final class ActivityThread { + handleLocalVoiceInteractionStarted((IBinder) ((SomeArgs) msg.obj).arg1, + (IVoiceInteractor) ((SomeArgs) msg.obj).arg2); + break; ++ case ASSETS_CHANGED: ++ handleAssetsChanged((String)((SomeArgs)msg.obj).arg1, ++ (ApplicationInfo)((SomeArgs)msg.obj).arg2); ++ break; + } + Object obj = msg.obj; + if (obj instanceof SomeArgs) { +@@ -4803,6 +4817,37 @@ public final class ActivityThread { + } + } + ++ final void handleAssetsChanged(@NonNull final String packageToUpdate, ++ @NonNull final ApplicationInfo ai) { ++ synchronized (mResourcesManager) { ++ // Update all affected loaded packages with new overlay package information ++ final ArrayList> loadedPackages = new ArrayList<>(); ++ loadedPackages.addAll(mPackages.values()); ++ loadedPackages.addAll(mResourcePackages.values()); ++ for (final WeakReference ref : loadedPackages) { ++ final LoadedApk apk = ref.get(); ++ if (apk != null) { ++ final String packageName = apk.getPackageName(); ++ if (packageToUpdate.equals(packageName)) { ++ apk.updateApplicationInfo(ai, null); ++ } ++ } ++ } ++ ++ // Update all affected Resources objects to use new ResourcesImpl ++ mResourcesManager.applyNewResourceDirsLocked(ai.sourceDir, ai.resourceDirs); ++ } ++ ++ // Schedule all activities to reload ++ for (final Map.Entry entry : mActivities.entrySet()) { ++ final Activity activity = entry.getValue().activity; ++ if (!activity.mFinished) { ++ requestRelaunchActivity(entry.getKey(), null, null, 0, false, null, null, false, ++ false); ++ } ++ } ++ } ++ + static void freeTextLayoutCachesIfNeeded(int configDiff) { + if (configDiff != 0) { + // Ask text layout engine to free its caches if there is a locale change +diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java +index 05d9d7e..47b05ee 100644 +--- a/core/java/android/app/ApplicationThreadNative.java ++++ b/core/java/android/app/ApplicationThreadNative.java +@@ -16,6 +16,7 @@ + + package android.app; + ++import android.annotation.NonNull; + import android.content.ComponentName; + import android.content.Intent; + import android.content.IIntentReceiver; +@@ -331,6 +332,15 @@ public abstract class ApplicationThreadNative extends Binder + return true; + } + ++ case SCHEDULE_ASSETS_CHANGED_TRANSACTION: ++ { ++ data.enforceInterface(IApplicationThread.descriptor); ++ final String packageName = data.readString(); ++ final ApplicationInfo ai = ApplicationInfo.CREATOR.createFromParcel(data); ++ scheduleAssetsChanged(packageName, ai); ++ return true; ++ } ++ + case UPDATE_TIME_ZONE_TRANSACTION: { + data.enforceInterface(IApplicationThread.descriptor); + updateTimeZone(); +@@ -1126,6 +1136,17 @@ class ApplicationThreadProxy implements IApplicationThread { + data.recycle(); + } + ++ public final void scheduleAssetsChanged(@NonNull final String packageName, ++ @NonNull final ApplicationInfo ai) throws RemoteException { ++ final Parcel data = Parcel.obtain(); ++ data.writeInterfaceToken(IApplicationThread.descriptor); ++ data.writeString(packageName); ++ ai.writeToParcel(data, 0); ++ mRemote.transact(SCHEDULE_ASSETS_CHANGED_TRANSACTION, data, null, ++ IBinder.FLAG_ONEWAY); ++ data.recycle(); ++ } ++ + public void updateTimeZone() throws RemoteException { + Parcel data = Parcel.obtain(); + data.writeInterfaceToken(IApplicationThread.descriptor); +diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java +index 5a4470b..c7522b9 100644 +--- a/core/java/android/app/IActivityManager.java ++++ b/core/java/android/app/IActivityManager.java +@@ -266,6 +266,7 @@ public interface IActivityManager extends IInterface { + + public Configuration getConfiguration() throws RemoteException; + public void updateConfiguration(Configuration values) throws RemoteException; ++ public void updateAssets(int userId, List packageNames) throws RemoteException; + public void setRequestedOrientation(IBinder token, + int requestedOrientation) throws RemoteException; + public int getRequestedOrientation(IBinder token) throws RemoteException; +@@ -1075,4 +1076,5 @@ public interface IActivityManager extends IInterface { + int SET_RENDER_THREAD_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 378; + int SET_HAS_TOP_UI = IBinder.FIRST_CALL_TRANSACTION + 379; + int CAN_BYPASS_WORK_CHALLENGE = IBinder.FIRST_CALL_TRANSACTION + 380; ++ int UPDATE_ASSETS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 381; + } +diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java +index 3fa88ae..fcc97e3 100644 +--- a/core/java/android/app/IApplicationThread.java ++++ b/core/java/android/app/IApplicationThread.java +@@ -104,6 +104,7 @@ public interface IApplicationThread extends IInterface { + void scheduleExit() throws RemoteException; + void scheduleSuicide() throws RemoteException; + void scheduleConfigurationChanged(Configuration config) throws RemoteException; ++ void scheduleAssetsChanged(String packageName, ApplicationInfo ai) throws RemoteException; + void updateTimeZone() throws RemoteException; + void clearDnsCache() throws RemoteException; + void setHttpProxy(String proxy, String port, String exclList, +@@ -225,4 +226,5 @@ public interface IApplicationThread extends IInterface { + int SCHEDULE_MULTI_WINDOW_CHANGED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+58; + int SCHEDULE_PICTURE_IN_PICTURE_CHANGED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+59; + int SCHEDULE_LOCAL_VOICE_INTERACTION_STARTED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+60; ++ int SCHEDULE_ASSETS_CHANGED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+61; + } +diff --git a/core/java/android/app/ResourcesManager.java b/core/java/android/app/ResourcesManager.java +index 4916c1c..8cbca31 100644 +--- a/core/java/android/app/ResourcesManager.java ++++ b/core/java/android/app/ResourcesManager.java +@@ -29,6 +29,7 @@ import android.content.res.ResourcesImpl; + import android.content.res.ResourcesKey; + import android.hardware.display.DisplayManagerGlobal; + import android.os.IBinder; ++import android.os.Process; + import android.os.Trace; + import android.util.ArrayMap; + import android.util.DisplayMetrics; +@@ -52,6 +53,8 @@ public class ResourcesManager { + static final String TAG = "ResourcesManager"; + private static final boolean DEBUG = false; + ++ private static final String FRAMEWORK_RESOURCES_PATH = "/system/framework/framework-res.apk"; ++ + private static ResourcesManager sResourcesManager; + + /** +@@ -916,44 +919,108 @@ public class ResourcesManager { + } + } + +- // Bail early if there is no work to do. +- if (updatedResourceKeys.isEmpty()) { +- return; ++ redirectResourcesToNewImplLocked(updatedResourceKeys); ++ } ++ } ++ ++ final void applyNewResourceDirsLocked(@NonNull final String baseCodePath, ++ @NonNull final String[] newResourceDirs) { ++ try { ++ Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, ++ "ResourcesManager#applyNewResourceDirsLocked"); ++ ++ ApplicationPackageManager.configurationChanged(); ++ ++ if (Process.myUid() == Process.SYSTEM_UID) { ++ // Resources.getSystem Resources are created on request and aren't tracked by ++ // mResourceReferences. ++ // ++ // If overlays targeting "android" are to be used, we must create the system ++ // resources regardless of whether they already exist, since otherwise the ++ // information on what overlays to use would be lost. This is wasteful for most ++ // applications, so limit this operation to the system user only. (This means ++ // Resources.getSystem() will *not* use overlays for applications.) ++ if (FRAMEWORK_RESOURCES_PATH.equals(baseCodePath)) { ++ final ResourcesKey key = new ResourcesKey( ++ FRAMEWORK_RESOURCES_PATH, ++ null, ++ newResourceDirs, ++ null, ++ Display.DEFAULT_DISPLAY, ++ null, ++ null); ++ final ResourcesImpl impl = createResourcesImpl(key); ++ Resources.getSystem().setImpl(impl); ++ } + } + +- // Update any references to ResourcesImpl that require reloading. +- final int resourcesCount = mResourceReferences.size(); +- for (int i = 0; i < resourcesCount; i++) { +- final Resources r = mResourceReferences.get(i).get(); ++ ++ final ArrayMap updatedResourceKeys = new ArrayMap<>(); ++ final int implCount = mResourceImpls.size(); ++ for (int i = 0; i < implCount; i++) { ++ final ResourcesImpl impl = mResourceImpls.valueAt(i).get(); ++ final ResourcesKey key = mResourceImpls.keyAt(i); ++ if (impl != null && key.mResDir != null && key.mResDir.equals(baseCodePath)) { ++ ++ updatedResourceKeys.put(impl, new ResourcesKey( ++ key.mResDir, ++ key.mSplitResDirs, ++ newResourceDirs, ++ key.mLibDirs, ++ key.mDisplayId, ++ key.mOverrideConfiguration, ++ key.mCompatInfo)); ++ } ++ } ++ ++ invalidatePath("/"); ++ ++ redirectResourcesToNewImplLocked(updatedResourceKeys); ++ } finally { ++ Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); ++ } ++ } ++ ++ private void redirectResourcesToNewImplLocked( ++ @NonNull final ArrayMap updatedResourceKeys) { ++ ++ // Bail early if there is no work to do. ++ if (updatedResourceKeys.isEmpty()) { ++ return; ++ } ++ ++ // Update any references to ResourcesImpl that require reloading. ++ final int resourcesCount = mResourceReferences.size(); ++ for (int i = 0; i < resourcesCount; i++) { ++ final Resources r = mResourceReferences.get(i).get(); ++ if (r != null) { ++ final ResourcesKey key = updatedResourceKeys.get(r.getImpl()); ++ if (key != null) { ++ final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key); ++ if (impl == null) { ++ throw new Resources.NotFoundException("failed to load"); ++ } ++ r.setImpl(impl); ++ } ++ } ++ } ++ ++ // Update any references to ResourcesImpl that require reloading for each Activity. ++ for (final ActivityResources activityResources : mActivityResourceReferences.values()) { ++ final int resCount = activityResources.activityResources.size(); ++ for (int i = 0; i < resCount; i++) { ++ final Resources r = activityResources.activityResources.get(i).get(); + if (r != null) { + final ResourcesKey key = updatedResourceKeys.get(r.getImpl()); + if (key != null) { + final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key); + if (impl == null) { +- throw new Resources.NotFoundException("failed to load " + libAsset); ++ throw new Resources.NotFoundException("failed to load"); + } + r.setImpl(impl); + } + } + } +- +- // Update any references to ResourcesImpl that require reloading for each Activity. +- for (ActivityResources activityResources : mActivityResourceReferences.values()) { +- final int resCount = activityResources.activityResources.size(); +- for (int i = 0; i < resCount; i++) { +- final Resources r = activityResources.activityResources.get(i).get(); +- if (r != null) { +- final ResourcesKey key = updatedResourceKeys.get(r.getImpl()); +- if (key != null) { +- final ResourcesImpl impl = findOrCreateResourcesImplForKeyLocked(key); +- if (impl == null) { +- throw new Resources.NotFoundException("failed to load " + libAsset); +- } +- r.setImpl(impl); +- } +- } +- } +- } + } + } + } +diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java +index f2e3333..8d42d2c 100644 +--- a/core/java/android/content/pm/PackageParser.java ++++ b/core/java/android/content/pm/PackageParser.java +@@ -668,10 +668,9 @@ public class PackageParser { + public final static int PARSE_IS_SYSTEM_DIR = 1<<6; + public final static int PARSE_IS_PRIVILEGED = 1<<7; + public final static int PARSE_COLLECT_CERTIFICATES = 1<<8; +- public final static int PARSE_TRUSTED_OVERLAY = 1<<9; +- public final static int PARSE_ENFORCE_CODE = 1<<10; +- public final static int PARSE_IS_EPHEMERAL = 1<<11; +- public final static int PARSE_FORCE_SDK = 1<<12; ++ public final static int PARSE_ENFORCE_CODE = 1<<9; ++ public final static int PARSE_IS_EPHEMERAL = 1<<10; ++ public final static int PARSE_FORCE_SDK = 1<<11; + + private static final Comparator sSplitNameComparator = new SplitNameComparator(); + +@@ -1806,9 +1805,6 @@ public class PackageParser { + com.android.internal.R.styleable.AndroidManifestResourceOverlay); + pkg.mOverlayTarget = sa.getString( + com.android.internal.R.styleable.AndroidManifestResourceOverlay_targetPackage); +- pkg.mOverlayPriority = sa.getInt( +- com.android.internal.R.styleable.AndroidManifestResourceOverlay_priority, +- -1); + sa.recycle(); + + if (pkg.mOverlayTarget == null) { +@@ -1816,14 +1812,7 @@ public class PackageParser { + mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; + return null; + } +- if (pkg.mOverlayPriority < 0 || pkg.mOverlayPriority > 9999) { +- outError[0] = " priority must be between 0 and 9999"; +- mParseError = +- PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED; +- return null; +- } + XmlUtils.skipCurrentTag(parser); +- + } else if (tagName.equals(TAG_KEY_SETS)) { + if (!parseKeySets(pkg, res, parser, outError)) { + return null; +@@ -4913,8 +4902,6 @@ public class PackageParser { + public String mRequiredAccountType; + + public String mOverlayTarget; +- public int mOverlayPriority; +- public boolean mTrustedOverlay; + + /** + * Data used to feed the KeySetManagerService +@@ -5455,6 +5442,7 @@ public class PackageParser { + ai.enabled = false; + } + ai.enabledSetting = state.enabled; ++ ai.resourceDirs = state.resourceDirs; + if (state.protectedComponents != null) { + ai.protect = state.protectedComponents.size() > 0; + } +diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java +index e64e4c4..4b276fb 100644 +--- a/core/java/android/content/pm/PackageUserState.java ++++ b/core/java/android/content/pm/PackageUserState.java +@@ -31,6 +31,8 @@ import android.util.ArraySet; + + import com.android.internal.util.ArrayUtils; + ++import java.util.Arrays; ++ + /** + * Per-user state information about a package. + * @hide +@@ -53,6 +55,8 @@ public class PackageUserState { + public ArraySet protectedComponents; + public ArraySet visibleComponents; + ++ public String[] resourceDirs; ++ + public PackageUserState() { + installed = true; + hidden = false; +@@ -76,6 +80,8 @@ public class PackageUserState { + appLinkGeneration = o.appLinkGeneration; + disabledComponents = ArrayUtils.cloneOrNull(o.disabledComponents); + enabledComponents = ArrayUtils.cloneOrNull(o.enabledComponents); ++ resourceDirs = ++ o.resourceDirs == null ? null : Arrays.copyOf(o.resourceDirs, o.resourceDirs.length); + protectedComponents = o.protectedComponents != null + ? new ArraySet(o.protectedComponents) : null; + visibleComponents = o.visibleComponents != null +diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp +index 1a7294f..6c5a88f 100644 +--- a/core/jni/android_util_AssetManager.cpp ++++ b/core/jni/android_util_AssetManager.cpp +@@ -129,85 +129,6 @@ jint copyValue(JNIEnv* env, jobject outValue, const ResTable* table, + return block; + } + +-// This is called by zygote (running as user root) as part of preloadResources. +-static void verifySystemIdmaps(const char* overlay_dir) +-{ +- pid_t pid; +- char system_id[10]; +- +- snprintf(system_id, sizeof(system_id), "%d", AID_SYSTEM); +- +- switch (pid = fork()) { +- case -1: +- ALOGE("failed to fork for idmap: %s", strerror(errno)); +- break; +- case 0: // child +- { +- struct __user_cap_header_struct capheader; +- struct __user_cap_data_struct capdata; +- +- memset(&capheader, 0, sizeof(capheader)); +- memset(&capdata, 0, sizeof(capdata)); +- +- capheader.version = _LINUX_CAPABILITY_VERSION; +- capheader.pid = 0; +- +- if (capget(&capheader, &capdata) != 0) { +- ALOGE("capget: %s\n", strerror(errno)); +- exit(1); +- } +- +- capdata.effective = capdata.permitted; +- if (capset(&capheader, &capdata) != 0) { +- ALOGE("capset: %s\n", strerror(errno)); +- exit(1); +- } +- +- if (setgid(AID_SYSTEM) != 0) { +- ALOGE("setgid: %s\n", strerror(errno)); +- exit(1); +- } +- +- if (setuid(AID_SYSTEM) != 0) { +- ALOGE("setuid: %s\n", strerror(errno)); +- exit(1); +- } +- +- // Generic idmap parameters +- const char* argv[7]; +- int argc = 0; +- struct stat st; +- +- memset(argv, NULL, sizeof(argv)); +- argv[argc++] = AssetManager::IDMAP_BIN; +- argv[argc++] = "--scan"; +- argv[argc++] = AssetManager::TARGET_PACKAGE_NAME; +- argv[argc++] = AssetManager::TARGET_APK_PATH; +- argv[argc++] = AssetManager::IDMAP_DIR; +- +- // Directories to scan for overlays +- // /vendor/overlay +- +- if (stat(overlay_dir, &st) == 0) { +- argv[argc++] = overlay_dir; +- } +- +- // Finally, invoke idmap (if any overlay directory exists) +- if (argc > 5) { +- execv(AssetManager::IDMAP_BIN, (char* const*)argv); +- ALOGE("failed to execl for idmap: %s", strerror(errno)); +- exit(1); // should never get here +- } else { +- exit(0); +- } +- } +- break; +- default: // parent +- waitpid(pid, NULL, 0); +- break; +- } +-} +- + // ---------------------------------------------------------------------------- + + // this guy is exported to other jni routines +@@ -2078,22 +1999,6 @@ static jintArray android_content_AssetManager_getStyleAttributes(JNIEnv* env, jo + + static void android_content_AssetManager_init(JNIEnv* env, jobject clazz, jboolean isSystem) + { +- if (isSystem) { +- // Load frameworks-res.apk's overlay through regionalization environment +- if (Environment::isSupported()) { +- Environment* environment = new Environment(); +- if (environment != NULL) { +- const char* overlay_dir = environment->getOverlayDir(); +- if (overlay_dir != NULL && strcmp(overlay_dir, "") != 0) { +- ALOGD("Regionalization - getOverlayDir:%s", overlay_dir); +- verifySystemIdmaps(overlay_dir); +- } +- delete environment; +- } +- } +- +- verifySystemIdmaps(AssetManager::OVERLAY_DIR); +- } + AssetManager* am = new AssetManager(); + if (am == NULL) { + jniThrowException(env, "java/lang/OutOfMemoryError", ""); +diff --git a/include/androidfw/AssetManager.h b/include/androidfw/AssetManager.h +index 914ac3d..651765b 100644 +--- a/include/androidfw/AssetManager.h ++++ b/include/androidfw/AssetManager.h +@@ -238,12 +238,10 @@ public: + private: + struct asset_path + { +- asset_path() : path(""), type(kFileTypeRegular), idmap(""), +- isSystemOverlay(false), isSystemAsset(false) {} ++ asset_path() : path(""), type(kFileTypeRegular), idmap(""), isSystemAsset(false) {} + String8 path; + FileType type; + String8 idmap; +- bool isSystemOverlay; + bool isSystemAsset; + }; + +@@ -288,9 +286,6 @@ private: + + Asset* openIdmapLocked(const struct asset_path& ap) const; + +- void addSystemOverlays(const char* pathOverlaysList, const String8& targetPackagePath, +- ResTable* sharedRes, size_t offset) const; +- + class SharedZip : public RefBase { + public: + static sp get(const String8& path, bool createIfNotPresent = true); +@@ -305,9 +300,6 @@ private: + + bool isUpToDate(); + +- void addOverlay(const asset_path& ap); +- bool getOverlay(size_t idx, asset_path* out) const; +- + protected: + ~SharedZip(); + +@@ -322,8 +314,6 @@ private: + Asset* mResourceTableAsset; + ResTable* mResourceTable; + +- Vector mOverlays; +- + static Mutex gLock; + static DefaultKeyedVector > gOpen; + }; +@@ -356,9 +346,6 @@ private: + static String8 getPathName(const char* path); + + bool isUpToDate(); +- +- void addOverlay(const String8& path, const asset_path& overlay); +- bool getOverlay(const String8& path, size_t idx, asset_path* out) const; + + private: + void closeZip(int idx); +diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp +index edc625b..924b230 100644 +--- a/libs/androidfw/AssetManager.cpp ++++ b/libs/androidfw/AssetManager.cpp +@@ -214,15 +214,6 @@ bool AssetManager::addAssetPath( + *cookie = static_cast(mAssetPaths.size()); + } + +-#ifdef __ANDROID__ +- // Load overlays, if any +- asset_path oap; +- for (size_t idx = 0; mZipSet.getOverlay(ap.path, idx, &oap); idx++) { +- oap.isSystemAsset = isSystemAsset; +- mAssetPaths.add(oap); +- } +-#endif +- + if (mResources != NULL) { + appendPathToResTable(ap, appAsLib); + } +@@ -606,11 +597,6 @@ FileType AssetManager::getFileType(const char* fileName) + } + + bool AssetManager::appendPathToResTable(const asset_path& ap, bool appAsLib) const { +- // skip those ap's that correspond to system overlays +- if (ap.isSystemOverlay) { +- return true; +- } +- + Asset* ass = NULL; + ResTable* sharedRes = NULL; + bool shared = true; +@@ -652,14 +638,6 @@ bool AssetManager::appendPathToResTable(const asset_path& ap, bool appAsLib) con + ALOGV("Creating shared resources for %s", ap.path.string()); + sharedRes = new ResTable(); + sharedRes->add(ass, idmap, nextEntryIdx + 1, false); +-#ifdef __ANDROID__ +- const char* data = getenv("ANDROID_DATA"); +- LOG_ALWAYS_FATAL_IF(data == NULL, "ANDROID_DATA not set"); +- String8 overlaysListPath(data); +- overlaysListPath.appendPath(kResourceCache); +- overlaysListPath.appendPath("overlays.list"); +- addSystemOverlays(overlaysListPath.string(), ap.path, sharedRes, nextEntryIdx); +-#endif + sharedRes = const_cast(this)-> + mZipSet.setZipResourceTable(ap.path, sharedRes); + } +@@ -772,58 +750,6 @@ Asset* AssetManager::openIdmapLocked(const struct asset_path& ap) const + return ass; + } + +-void AssetManager::addSystemOverlays(const char* pathOverlaysList, +- const String8& targetPackagePath, ResTable* sharedRes, size_t offset) const +-{ +- FILE* fin = fopen(pathOverlaysList, "r"); +- if (fin == NULL) { +- return; +- } +- +-#ifndef _WIN32 +- if (TEMP_FAILURE_RETRY(flock(fileno(fin), LOCK_SH)) != 0) { +- fclose(fin); +- return; +- } +-#endif +- char buf[1024]; +- while (fgets(buf, sizeof(buf), fin)) { +- // format of each line: +- // +- char* space = strchr(buf, ' '); +- char* newline = strchr(buf, '\n'); +- asset_path oap; +- +- if (space == NULL || newline == NULL || newline < space) { +- continue; +- } +- +- oap.path = String8(buf, space - buf); +- oap.type = kFileTypeRegular; +- oap.idmap = String8(space + 1, newline - space - 1); +- oap.isSystemOverlay = true; +- +- Asset* oass = const_cast(this)-> +- openNonAssetInPathLocked("resources.arsc", +- Asset::ACCESS_BUFFER, +- oap); +- +- if (oass != NULL) { +- Asset* oidmap = openIdmapLocked(oap); +- offset++; +- sharedRes->add(oass, oidmap, offset + 1, false); +- const_cast(this)->mAssetPaths.add(oap); +- const_cast(this)->mZipSet.addOverlay(targetPackagePath, oap); +- delete oidmap; +- } +- } +- +-#ifndef _WIN32 +- TEMP_FAILURE_RETRY(flock(fileno(fin), LOCK_UN)); +-#endif +- fclose(fin); +-} +- + const ResTable& AssetManager::getResources(bool required) const + { + const ResTable* rt = getResTable(required); +@@ -1962,20 +1888,6 @@ bool AssetManager::SharedZip::isUpToDate() + return mModWhen == modWhen; + } + +-void AssetManager::SharedZip::addOverlay(const asset_path& ap) +-{ +- mOverlays.add(ap); +-} +- +-bool AssetManager::SharedZip::getOverlay(size_t idx, asset_path* out) const +-{ +- if (idx >= mOverlays.size()) { +- return false; +- } +- *out = mOverlays[idx]; +- return true; +-} +- + AssetManager::SharedZip::~SharedZip() + { + if (kIsDebug) { +@@ -2101,22 +2013,6 @@ bool AssetManager::ZipSet::isUpToDate() + return true; + } + +-void AssetManager::ZipSet::addOverlay(const String8& path, const asset_path& overlay) +-{ +- int idx = getIndex(path); +- sp zip = mZipFile[idx]; +- zip->addOverlay(overlay); +-} +- +-bool AssetManager::ZipSet::getOverlay(const String8& path, size_t idx, asset_path* out) const +-{ +- sp zip = SharedZip::get(path, false); +- if (zip == NULL) { +- return false; +- } +- return zip->getOverlay(idx, out); +-} +- + /* + * Compute the zip file's index. + * +diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java +index 90f507c..904c967 100644 +--- a/services/core/java/com/android/server/SystemServiceManager.java ++++ b/services/core/java/com/android/server/SystemServiceManager.java +@@ -16,6 +16,7 @@ + + package com.android.server; + ++import android.annotation.NonNull; + import android.content.Context; + import android.os.Trace; + import android.util.Slog; +@@ -104,22 +105,25 @@ public class SystemServiceManager { + + ": service constructor threw an exception", ex); + } + +- // Register it. +- mServices.add(service); +- +- // Start it. +- try { +- service.onStart(); +- } catch (RuntimeException ex) { +- throw new RuntimeException("Failed to start service " + name +- + ": onStart threw an exception", ex); +- } ++ startService(service); + return service; + } finally { + Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); + } + } + ++ public void startService(@NonNull final SystemService service) { ++ // Register it. ++ mServices.add(service); ++ // Start it. ++ try { ++ service.onStart(); ++ } catch (RuntimeException ex) { ++ throw new RuntimeException("Failed to start service " + service.getClass().getName() ++ + ": onStart threw an exception", ex); ++ } ++ } ++ + /** + * Starts the specified boot phase for all system services that have been started up to + * this point. +diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java +index e31b409..2822dc9 100644 +--- a/services/core/java/com/android/server/am/ActivityManagerService.java ++++ b/services/core/java/com/android/server/am/ActivityManagerService.java +@@ -19297,6 +19297,57 @@ public final class ActivityManagerService extends ActivityManagerNative + } + + /** ++ * @hide ++ */ ++ @Override ++ public void updateAssets(final int userId, @NonNull final List packageNames) { ++ enforceCallingPermission(android.Manifest.permission.CHANGE_CONFIGURATION, "updateAssets()"); ++ ++ synchronized(this) { ++ final long origId = Binder.clearCallingIdentity(); ++ try { ++ updateAssetsLocked(userId, packageNames); ++ } finally { ++ Binder.restoreCallingIdentity(origId); ++ } ++ } ++ } ++ ++ void updateAssetsLocked(final int userId, @NonNull final List packagesToUpdate) { ++ final IPackageManager pm = AppGlobals.getPackageManager(); ++ final Map cache = new ArrayMap<>(); ++ ++ final boolean updateFrameworkRes = packagesToUpdate.contains("android"); ++ for (int i = mLruProcesses.size() - 1; i >= 0; i--) { ++ final ProcessRecord app = mLruProcesses.get(i); ++ if (app.userId != userId || app.thread == null) { ++ continue; ++ } ++ ++ for (final String packageName : app.pkgList.keySet()) { ++ if (updateFrameworkRes || packagesToUpdate.contains(packageName)) { ++ try { ++ final ApplicationInfo ai; ++ if (cache.containsKey(packageName)) { ++ ai = cache.get(packageName); ++ } else { ++ ai = pm.getApplicationInfo(packageName, 0, userId); ++ cache.put(packageName, ai); ++ } ++ ++ if (ai != null) { ++ app.thread.scheduleAssetsChanged(packageName, ai); ++ } ++ } catch (RemoteException e) { ++ Slog.w(TAG, String.format("Failed to update %s assets for %s", ++ packageName, app)); ++ } ++ } ++ } ++ } ++ } ++ ++ /** + * Decide based on the configuration whether we should shouw the ANR, + * crash, etc dialogs. The idea is that if there is no affordence to + * press the on-screen buttons, or the user experience would be more +diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java +index ec148dd..761ef52 100644 +--- a/services/core/java/com/android/server/om/OverlayManagerService.java ++++ b/services/core/java/com/android/server/om/OverlayManagerService.java +@@ -676,37 +676,36 @@ public final class OverlayManagerService extends SystemService { + } + + private void updateAssets(final int userId, List targetPackageNames) { +- // TODO: uncomment when we integrate OMS properly +- // final PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class); +- // final boolean updateFrameworkRes = targetPackageNames.contains("android"); +- // if (updateFrameworkRes) { +- // targetPackageNames = pm.getTargetPackageNames(userId); +- // } +- +- // final Map allPaths = new ArrayMap<>(targetPackageNames.size()); +- // synchronized (mLock) { +- // final List frameworkPaths = mImpl.onGetEnabledOverlayPaths("android", userId); +- // for (final String packageName : targetPackageNames) { +- // final List paths = new ArrayList<>(); +- // paths.addAll(frameworkPaths); +- // if (!"android".equals(packageName)) { +- // paths.addAll(mImpl.onGetEnabledOverlayPaths(packageName, userId)); +- // } +- // allPaths.put(packageName, +- // paths.isEmpty() ? null : paths.toArray(new String[paths.size()])); +- // } +- // } +- +- // for (String packageName : targetPackageNames) { +- // pm.setResourceDirs(userId, packageName, allPaths.get(packageName)); +- // } +- +- // final IActivityManager am = ActivityManagerNative.getDefault(); +- // try { +- // am.updateAssets(userId, targetPackageNames); +- // } catch (RemoteException e) { +- // // Intentionally left empty. +- // } ++ final PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class); ++ final boolean updateFrameworkRes = targetPackageNames.contains("android"); ++ if (updateFrameworkRes) { ++ targetPackageNames = pm.getTargetPackageNames(userId); ++ } ++ ++ final Map allPaths = new ArrayMap<>(targetPackageNames.size()); ++ synchronized (mLock) { ++ final List frameworkPaths = mImpl.onGetEnabledOverlayPaths("android", userId); ++ for (final String packageName : targetPackageNames) { ++ final List paths = new ArrayList<>(); ++ paths.addAll(frameworkPaths); ++ if (!"android".equals(packageName)) { ++ paths.addAll(mImpl.onGetEnabledOverlayPaths(packageName, userId)); ++ } ++ allPaths.put(packageName, ++ paths.isEmpty() ? null : paths.toArray(new String[paths.size()])); ++ } ++ } ++ ++ for (String packageName : targetPackageNames) { ++ pm.setResourceDirs(userId, packageName, allPaths.get(packageName)); ++ } ++ ++ final IActivityManager am = ActivityManagerNative.getDefault(); ++ try { ++ am.updateAssets(userId, targetPackageNames); ++ } catch (RemoteException e) { ++ // Intentionally left empty. ++ } + } + + private void schedulePersistSettings() { +diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java +index 3892f8f..f700522 100644 +--- a/services/core/java/com/android/server/pm/PackageManagerService.java ++++ b/services/core/java/com/android/server/pm/PackageManagerService.java +@@ -399,17 +399,16 @@ public class PackageManagerService extends IPackageManager.Stub { + static final int SCAN_UPDATE_TIME = 1<<6; + static final int SCAN_DEFER_DEX = 1<<7; + static final int SCAN_BOOTING = 1<<8; +- static final int SCAN_TRUSTED_OVERLAY = 1<<9; +- static final int SCAN_DELETE_DATA_ON_FAILURES = 1<<10; +- static final int SCAN_REPLACING = 1<<11; +- static final int SCAN_REQUIRE_KNOWN = 1<<12; +- static final int SCAN_MOVE = 1<<13; +- static final int SCAN_INITIAL = 1<<14; +- static final int SCAN_CHECK_ONLY = 1<<15; +- static final int SCAN_DONT_KILL_APP = 1<<17; +- static final int SCAN_IGNORE_FROZEN = 1<<18; +- +- static final int REMOVE_CHATTY = 1<<16; ++ static final int SCAN_DELETE_DATA_ON_FAILURES = 1<<9; ++ static final int SCAN_REPLACING = 1<<10; ++ static final int SCAN_REQUIRE_KNOWN = 1<<11; ++ static final int SCAN_MOVE = 1<<12; ++ static final int SCAN_INITIAL = 1<<13; ++ static final int SCAN_CHECK_ONLY = 1<<14; ++ static final int SCAN_DONT_KILL_APP = 1<<15; ++ static final int SCAN_IGNORE_FROZEN = 1<<16; ++ ++ static final int REMOVE_CHATTY = 1<<17; + + private static final int[] EMPTY_INT_ARRAY = new int[0]; + +@@ -600,10 +599,6 @@ public class PackageManagerService extends IPackageManager.Stub { + final ArrayMap> mKnownCodebase = + new ArrayMap>(); + +- // Tracks available target package names -> overlay package paths. +- final ArrayMap> mOverlays = +- new ArrayMap>(); +- + /** + * Tracks new system packages [received in an OTA] that we expect to + * find updated user-installed versions. Keys are package name, values +@@ -2342,8 +2337,8 @@ public class PackageManagerService extends IPackageManager.Stub { + File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR); + scanDirTracedLI(vendorOverlayDir, mDefParseFlags + | PackageParser.PARSE_IS_SYSTEM +- | PackageParser.PARSE_IS_SYSTEM_DIR +- | PackageParser.PARSE_TRUSTED_OVERLAY, scanFlags | SCAN_TRUSTED_OVERLAY, 0); ++ | PackageParser.PARSE_IS_SYSTEM_DIR, ++ scanFlags, 0); + + // Find base frameworks (resource packages without code). + scanDirTracedLI(frameworkDir, mDefParseFlags +@@ -2400,7 +2395,7 @@ public class PackageManagerService extends IPackageManager.Stub { + // Collect overlay in /system/vendor + scanDirLI(new File(RegionalizationSystemDir, "vendor/overlay"), + PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, +- scanFlags | SCAN_TRUSTED_OVERLAY, 0); ++ scanFlags, 0); + } + } + +@@ -6723,60 +6718,6 @@ public class PackageManagerService extends IPackageManager.Stub { + return finalList; + } + +- private void createIdmapsForPackageLI(PackageParser.Package pkg) { +- ArrayMap overlays = mOverlays.get(pkg.packageName); +- if (overlays == null) { +- Slog.w(TAG, "Unable to create idmap for " + pkg.packageName + ": no overlay packages"); +- return; +- } +- for (PackageParser.Package opkg : overlays.values()) { +- // Not much to do if idmap fails: we already logged the error +- // and we certainly don't want to abort installation of pkg simply +- // because an overlay didn't fit properly. For these reasons, +- // ignore the return value of createIdmapForPackagePairLI. +- createIdmapForPackagePairLI(pkg, opkg); +- } +- } +- +- private boolean createIdmapForPackagePairLI(PackageParser.Package pkg, +- PackageParser.Package opkg) { +- if (!opkg.mTrustedOverlay) { +- Slog.w(TAG, "Skipping target and overlay pair " + pkg.baseCodePath + " and " + +- opkg.baseCodePath + ": overlay not trusted"); +- return false; +- } +- ArrayMap overlaySet = mOverlays.get(pkg.packageName); +- if (overlaySet == null) { +- Slog.e(TAG, "was about to create idmap for " + pkg.baseCodePath + " and " + +- opkg.baseCodePath + " but target package has no known overlays"); +- return false; +- } +- final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid); +- // TODO: generate idmap for split APKs +- try { +- mInstaller.idmap(pkg.baseCodePath, opkg.baseCodePath, sharedGid); +- } catch (InstallerException e) { +- Slog.e(TAG, "Failed to generate idmap for " + pkg.baseCodePath + " and " +- + opkg.baseCodePath); +- return false; +- } +- PackageParser.Package[] overlayArray = +- overlaySet.values().toArray(new PackageParser.Package[0]); +- Comparator cmp = new Comparator() { +- public int compare(PackageParser.Package p1, PackageParser.Package p2) { +- return p1.mOverlayPriority - p2.mOverlayPriority; +- } +- }; +- Arrays.sort(overlayArray, cmp); +- +- pkg.applicationInfo.resourceDirs = new String[overlayArray.length]; +- int i = 0; +- for (PackageParser.Package p : overlayArray) { +- pkg.applicationInfo.resourceDirs[i++] = p.baseCodePath; +- } +- return true; +- } +- + private void scanDirTracedLI(File dir, final int parseFlags, int scanFlags, long currentTime) { + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir"); + try { +@@ -6964,10 +6905,6 @@ public class PackageManagerService extends IPackageManager.Stub { + pp.setOnlyPowerOffAlarmApps(mOnlyPowerOffAlarm); + pp.setDisplayMetrics(mMetrics); + +- if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) { +- parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY; +- } +- + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage"); + final PackageParser.Package pkg; + try { +@@ -8200,7 +8137,6 @@ public class PackageManagerService extends IPackageManager.Stub { + pkg.applicationInfo.privateFlags &= + ~ApplicationInfo.PRIVATE_FLAG_DIRECT_BOOT_AWARE; + } +- pkg.mTrustedOverlay = (policyFlags&PackageParser.PARSE_TRUSTED_OVERLAY) != 0; + + if ((policyFlags&PackageParser.PARSE_IS_PRIVILEGED) != 0) { + pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED; +@@ -8795,7 +8731,6 @@ public class PackageManagerService extends IPackageManager.Stub { + // writer + Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "updateSettings"); + +- boolean createIdmapFailed = false; + synchronized (mPackages) { + // We don't expect installation to fail beyond this point + +@@ -9142,36 +9077,10 @@ public class PackageManagerService extends IPackageManager.Stub { + } + + pkgSetting.setTimeStamp(scanFileTime); +- +- // Create idmap files for pairs of (packages, overlay packages). +- // Note: "android", ie framework-res.apk, is handled by native layers. +- if (pkg.mOverlayTarget != null) { +- // This is an overlay package. +- if (pkg.mOverlayTarget != null && !pkg.mOverlayTarget.equals("android")) { +- if (!mOverlays.containsKey(pkg.mOverlayTarget)) { +- mOverlays.put(pkg.mOverlayTarget, +- new ArrayMap()); +- } +- ArrayMap map = mOverlays.get(pkg.mOverlayTarget); +- map.put(pkg.packageName, pkg); +- PackageParser.Package orig = mPackages.get(pkg.mOverlayTarget); +- if (orig != null && !createIdmapForPackagePairLI(orig, pkg)) { +- createIdmapFailed = true; +- } +- } +- } else if (mOverlays.containsKey(pkg.packageName) && +- !pkg.packageName.equals("android")) { +- // This is a regular package, with one or more known overlay packages. +- createIdmapsForPackageLI(pkg); +- } + } + + Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); + +- if (createIdmapFailed) { +- throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE, +- "scanPackageLI failed to createIdmap"); +- } + return pkg; + } + +@@ -16618,7 +16527,7 @@ public class PackageManagerService extends IPackageManager.Stub { + false /*hidden*/, false /*suspended*/, null, null, null, + false /*blockUninstall*/, + ps.readUserState(nextUserId).domainVerificationStatus, 0, +- null, null); ++ null, null, null); + } + } + +@@ -21476,14 +21385,13 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); + + @Override + public void setResourceDirs(int userId, String packageName, String[] resourceDirs) { +- // TODO: uncomment when we integrate OMS properly +- // synchronized (mPackages) { +- // PackageSetting ps = mSettings.mPackages.get(packageName); +- // if (ps == null) { +- // return; +- // } +- // ps.setResourceDirs(resourceDirs, userId); +- // } ++ synchronized (mPackages) { ++ final PackageSetting ps = mSettings.mPackages.get(packageName); ++ if (ps == null) { ++ return; ++ } ++ ps.setResourceDirs(resourceDirs, userId); ++ } + } + } + +diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java +index 75427a8..18c79cd 100644 +--- a/services/core/java/com/android/server/pm/PackageSettingBase.java ++++ b/services/core/java/com/android/server/pm/PackageSettingBase.java +@@ -21,6 +21,7 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED + import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; + import static android.content.pm.PackageManager.COMPONENT_VISIBLE_STATUS; + ++import android.annotation.NonNull; + import android.content.pm.IntentFilterVerificationInfo; + import android.content.pm.PackageManager; + import android.content.pm.PackageUserState; +@@ -379,7 +380,7 @@ abstract class PackageSettingBase extends SettingBase { + boolean notLaunched, boolean hidden, boolean suspended, + String lastDisableAppCaller, ArraySet enabledComponents, + ArraySet disabledComponents, boolean blockUninstall, int domainVerifState, +- int linkGeneration, ++ int linkGeneration, String[] resourceDirs, + ArraySet protectedComponents, ArraySet visibleComponents) { + PackageUserState state = modifyUserState(userId); + state.ceDataInode = ceDataInode; +@@ -395,6 +396,7 @@ abstract class PackageSettingBase extends SettingBase { + state.blockUninstall = blockUninstall; + state.domainVerificationStatus = domainVerifState; + state.appLinkGeneration = linkGeneration; ++ state.resourceDirs = resourceDirs; + state.protectedComponents = protectedComponents; + state.visibleComponents = visibleComponents; + } +@@ -455,6 +457,10 @@ abstract class PackageSettingBase extends SettingBase { + modifyUserStateComponents(userId, false, true).enabledComponents.add(componentClassName); + } + ++ void setResourceDirs(@NonNull final String[] resourceDirs, final int userId) { ++ modifyUserState(userId).resourceDirs = resourceDirs; ++ } ++ + boolean enableComponentLPw(String componentClassName, int userId) { + PackageUserState state = modifyUserStateComponents(userId, false, true); + boolean changed = state.disabledComponents != null +diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java +index 285b5bb..2b36e0e 100755 +--- a/services/core/java/com/android/server/pm/Settings.java ++++ b/services/core/java/com/android/server/pm/Settings.java +@@ -813,6 +813,7 @@ final class Settings { + false, // blockUninstall + INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, 0, + null, ++ null, + null + ); + writePackageRestrictionsLPr(user.id); +@@ -1617,6 +1618,7 @@ final class Settings { + false, // blockUninstall + INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, 0, + null, ++ null, + null + ); + } +@@ -1726,7 +1728,7 @@ final class Settings { + + ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched, + hidden, suspended, enabledCaller, enabledComponents, disabledComponents, +- blockUninstall, verifState, linkGeneration, ++ blockUninstall, verifState, linkGeneration, null, + protectedComponents, visibleComponents); + } else if (tagName.equals("preferred-activities")) { + readPreferredActivitiesLPw(parser, userId); +diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java +index 3e16338..010aaa1 100644 +--- a/services/java/com/android/server/SystemServer.java ++++ b/services/java/com/android/server/SystemServer.java +@@ -84,6 +84,7 @@ import com.android.server.media.projection.MediaProjectionManagerService; + import com.android.server.net.NetworkPolicyManagerService; + import com.android.server.net.NetworkStatsService; + import com.android.server.notification.NotificationManagerService; ++import com.android.server.om.OverlayManagerService; + import com.android.server.os.RegionalizationService; + import com.android.server.os.SchedulingPolicyService; + import com.android.server.pm.BackgroundDexOptService; +@@ -530,6 +531,9 @@ public final class SystemServer { + // Set up the Application instance for the system process and get started. + mActivityManagerService.setSystemProcess(); + ++ // Manages Overlay packages ++ mSystemServiceManager.startService(new OverlayManagerService(mSystemContext, installer)); ++ + // The sensor service needs access to package manager service, app ops + // service, and permissions service, therefore we start it after them. + startSensorService(); +-- +2.9.3 + diff --git a/frameworks/base/0004-OMS7-N-Set-EXTRA_REPLACING-correctly-in-ACTION_PACKA.patch b/frameworks/base/0004-OMS7-N-Set-EXTRA_REPLACING-correctly-in-ACTION_PACKA.patch new file mode 100644 index 0000000..5ce4094 --- /dev/null +++ b/frameworks/base/0004-OMS7-N-Set-EXTRA_REPLACING-correctly-in-ACTION_PACKA.patch @@ -0,0 +1,51 @@ +From 06912868733feadee85507d9f40ede2468a9a496 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= +Date: Mon, 25 Apr 2016 16:29:22 +0200 +Subject: [PATCH 04/38] OMS7-N: Set EXTRA_REPLACING correctly in + ACTION_PACKAGE_ADDED [4/11] + +When broadcasting ACTION_PACKAGE_ADDED the recipients of the Intent are +split into two groups: the first group hasn't seen the new package +before and the Intent should have EXTRA_REPLACING set to false; and vice +versa for the second group. + +The package manager schedules these Intent broadcasts on a background +thread by posting Runnable objects to a handler. Each Runnable holds +references to objects used to construct the Intents, one of which is a +Bundle used to create the Intent extras. + +If the same Bundle object is used for both recipient groups, any +modification to the object made for one group will unintentionally +propagate to the other. To prevent this a separate Bundle is now created +for each group. + +Prior to this patch, the following scenario would fail: + + 1. Install a package P for user owner + 2. Create and switch to a secondary user + 3. Install a new version of package P (for all users) + +In step 3, the secondary user was expected to receive +ACTION_PACKAGE_ADDED with EXTRA_REPLACING set to false, but instead it +was set to true. The bug was initially introduced in commit bd0e9e49. + +Change-Id: Icf869013d5d652de4bf0f6df4529b7a68d35a25c +--- + services/core/java/com/android/server/pm/PackageManagerService.java | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java +index f700522..7277f6a 100644 +--- a/services/core/java/com/android/server/pm/PackageManagerService.java ++++ b/services/core/java/com/android/server/pm/PackageManagerService.java +@@ -1714,6 +1714,7 @@ public class PackageManagerService extends IPackageManager.Stub { + + // Send added for users that don't see the package for the first time + if (update) { ++ extras = new Bundle(extras); + extras.putBoolean(Intent.EXTRA_REPLACING, true); + } + sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, +-- +2.9.3 + diff --git a/frameworks/base/0005-OMS7-N-idmap-suppress-print-for-padded-resources-5-1.patch b/frameworks/base/0005-OMS7-N-idmap-suppress-print-for-padded-resources-5-1.patch new file mode 100644 index 0000000..31ebb89 --- /dev/null +++ b/frameworks/base/0005-OMS7-N-idmap-suppress-print-for-padded-resources-5-1.patch @@ -0,0 +1,29 @@ +From ee6f42041d3c77be1151d82898ee5626633cf9fa Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= +Date: Mon, 29 Feb 2016 14:12:35 +0100 +Subject: [PATCH 05/38] OMS7-N: idmap: suppress print for padded resources + [5/11] + +Change-Id: I565ccf515068b96927e4317cc9c06543415bb324 +--- + cmds/idmap/inspect.cpp | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/cmds/idmap/inspect.cpp b/cmds/idmap/inspect.cpp +index 924090f..cb52a39 100644 +--- a/cmds/idmap/inspect.cpp ++++ b/cmds/idmap/inspect.cpp +@@ -289,7 +289,9 @@ namespace { + if (err != NO_ERROR) { + return err; + } +- print("", "entry", data32, "%s/%s", type.string(), name.string()); ++ if (data32 != ResTable_type::NO_ENTRY) { ++ print("", "entry", data32, "%s/%s", type.string(), name.string()); ++ } + } + } + +-- +2.9.3 + diff --git a/frameworks/base/0006-OMS7-N-Fix-memory-leak-during-idmap-creation-6-11.patch b/frameworks/base/0006-OMS7-N-Fix-memory-leak-during-idmap-creation-6-11.patch new file mode 100644 index 0000000..9eb37d8 --- /dev/null +++ b/frameworks/base/0006-OMS7-N-Fix-memory-leak-during-idmap-creation-6-11.patch @@ -0,0 +1,67 @@ +From a2ce720b5bfca010dc28ab9cd79e9a8d94f981b3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= +Date: Thu, 2 Jun 2016 09:34:36 +0200 +Subject: [PATCH 06/38] OMS7-N: Fix memory leak during idmap creation [6/11] + +Plug a memory leak in AssetManager::createIdmap. + +Change-Id: Ieed805c596df931e2167ebb47c1b2907d6bf67f4 +--- + libs/androidfw/AssetManager.cpp | 38 +++++++++++++++++++++++++------------- + 1 file changed, 25 insertions(+), 13 deletions(-) + +diff --git a/libs/androidfw/AssetManager.cpp b/libs/androidfw/AssetManager.cpp +index 924b230..c501e8b 100644 +--- a/libs/androidfw/AssetManager.cpp ++++ b/libs/androidfw/AssetManager.cpp +@@ -291,22 +291,34 @@ bool AssetManager::createIdmap(const char* targetApkPath, const char* overlayApk + { + AutoMutex _l(mLock); + const String8 paths[2] = { String8(targetApkPath), String8(overlayApkPath) }; +- ResTable tables[2]; +- +- for (int i = 0; i < 2; ++i) { +- asset_path ap; +- ap.type = kFileTypeRegular; +- ap.path = paths[i]; +- Asset* ass = openNonAssetInPathLocked("resources.arsc", Asset::ACCESS_BUFFER, ap); +- if (ass == NULL) { +- ALOGW("failed to find resources.arsc in %s\n", ap.path.string()); +- return false; ++ Asset* assets[2] = {NULL, NULL}; ++ bool ret = false; ++ { ++ ResTable tables[2]; ++ ++ for (int i = 0; i < 2; ++i) { ++ asset_path ap; ++ ap.type = kFileTypeRegular; ++ ap.path = paths[i]; ++ assets[i] = openNonAssetInPathLocked("resources.arsc", ++ Asset::ACCESS_BUFFER, ap); ++ if (assets[i] == NULL) { ++ ALOGW("failed to find resources.arsc in %s\n", ap.path.string()); ++ goto exit; ++ } ++ if (tables[i].add(assets[i]) != NO_ERROR) { ++ ALOGW("failed to add %s to resource table", paths[i].string()); ++ goto exit; ++ } + } +- tables[i].add(ass); ++ ret = tables[0].createIdmap(tables[1], targetCrc, overlayCrc, ++ targetApkPath, overlayApkPath, (void**)outData, outSize) == NO_ERROR; + } + +- return tables[0].createIdmap(tables[1], targetCrc, overlayCrc, +- targetApkPath, overlayApkPath, (void**)outData, outSize) == NO_ERROR; ++exit: ++ delete assets[0]; ++ delete assets[1]; ++ return ret; + } + + bool AssetManager::addDefaultAssets() +-- +2.9.3 + diff --git a/frameworks/base/0007-OMS7-N-installd-add-command-rmidmap-7-11.patch b/frameworks/base/0007-OMS7-N-installd-add-command-rmidmap-7-11.patch new file mode 100644 index 0000000..209f9e3 --- /dev/null +++ b/frameworks/base/0007-OMS7-N-installd-add-command-rmidmap-7-11.patch @@ -0,0 +1,38 @@ +From b14c245ca8e1b2fa9665ef8cde29eeafaeba282f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= +Date: Thu, 2 Jun 2016 09:35:09 +0200 +Subject: [PATCH 07/38] OMS7-N: installd: add command 'rmidmap' [7/11] + +Add an installd command to remove an idmap file. This is the inverse of +the 'idmap' command and is intended for clean-up once an idmap file is +no longer needed because an APK was removed, etc. + +This commit depends on a corresponding commit in frameworks/native (with +the same Change-Id). + +Change-Id: I58f55f643da99c0bd69136ee43c1c8c70c352797 +--- + services/core/java/com/android/server/pm/Installer.java | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java +index 2e18b1c..7f618b8 100644 +--- a/services/core/java/com/android/server/pm/Installer.java ++++ b/services/core/java/com/android/server/pm/Installer.java +@@ -170,6 +170,13 @@ public final class Installer extends SystemService { + mInstaller.execute("idmap", targetApkPath, overlayApkPath, uid); + } + ++ public void removeIdmap(String overlayApkPath) throws InstallerException { ++ StringBuilder builder = new StringBuilder("rmidmap"); ++ builder.append(' '); ++ builder.append(overlayApkPath); ++ mInstaller.execute(builder.toString()); ++ } ++ + public void rmdex(String codePath, String instructionSet) throws InstallerException { + assertValidInstructionSet(instructionSet); + mInstaller.execute("rmdex", codePath, instructionSet); +-- +2.9.3 + diff --git a/frameworks/base/0008-OMS7-N-Disable-Zygote-preloaded-drawables-8-11.patch b/frameworks/base/0008-OMS7-N-Disable-Zygote-preloaded-drawables-8-11.patch new file mode 100644 index 0000000..fd22568 --- /dev/null +++ b/frameworks/base/0008-OMS7-N-Disable-Zygote-preloaded-drawables-8-11.patch @@ -0,0 +1,45 @@ +From 5e8fc3854ee67ed15983c460b390d0cff1dde775 Mon Sep 17 00:00:00 2001 +From: Josh Guilfoyle +Date: Wed, 26 Jan 2011 23:28:43 -0800 +Subject: [PATCH 08/38] OMS7-N: Disable Zygote preloaded drawables [8/11] + +With a theme applied, most of these preloaded drawables go unused. Any +assets the theme has redirected will need to be loaded with each app +process regardless. Worse, preloads make it impossible to do asset +redirection for constituent parts of a preloaded drawable (for instance, +individual states of a StateListDrawable cannot be redirected). + +Some day it might be nice to revisit this and see if there's a way to +reintroduce the drawable cache in a way that can be altered at runtime +without significant complexity or runtime penalty. + +Change-Id: I253b1a22482ac664c196533a4c2fcd88ae84b996 +--- + core/java/com/android/internal/os/ZygoteInit.java | 4 +++- + 1 file changed, 3 insertions(+), 1 deletion(-) + +diff --git a/core/java/com/android/internal/os/ZygoteInit.java b/core/java/com/android/internal/os/ZygoteInit.java +index 20f84b5..455849e 100644 +--- a/core/java/com/android/internal/os/ZygoteInit.java ++++ b/core/java/com/android/internal/os/ZygoteInit.java +@@ -108,7 +108,7 @@ public class ZygoteInit { + private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes"; + + /** Controls whether we should preload resources during zygote init. */ +- public static final boolean PRELOAD_RESOURCES = true; ++ public static final boolean PRELOAD_RESOURCES = false; + + /** + * Registers a server socket for zygote command connections +@@ -437,6 +437,8 @@ public class ZygoteInit { + Log.i(TAG, "...preloaded " + N + " resource in " + + (SystemClock.uptimeMillis() - startTime) + "ms."); + } ++ } else { ++ Log.i(TAG, "Preload resources disabled, skipped."); + } + mResources.finishPreloading(); + } catch (RuntimeException e) { +-- +2.9.3 + diff --git a/frameworks/base/0009-OMS7-N-Persistence-on-boot-through-OverlayManagerSer.patch b/frameworks/base/0009-OMS7-N-Persistence-on-boot-through-OverlayManagerSer.patch new file mode 100644 index 0000000..16c58d4 --- /dev/null +++ b/frameworks/base/0009-OMS7-N-Persistence-on-boot-through-OverlayManagerSer.patch @@ -0,0 +1,78 @@ +From fc7d694af82449a4d4a3456ffd486a5afa30a2ef Mon Sep 17 00:00:00 2001 +From: Nicholas Chum +Date: Sun, 19 Jun 2016 10:37:13 -0400 +Subject: [PATCH 09/38] OMS7-N: Persistence on boot through + OverlayManagerServiceImpl [9/11] + +Overlays should not be enforced by the traditional OverlayManagerService +by Sony, but instead, it shouldn't be enforced at all to allow third +party overlays from the community to boot up with the device. + +Change-Id: Ic6eeb38b5e7bcec4211405d4504ba37a75738227 +--- + .../server/om/OverlayManagerServiceImpl.java | 31 +++++++++++++++------- + 1 file changed, 21 insertions(+), 10 deletions(-) + +diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java +index 2a0d88b..4c61968 100644 +--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java ++++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java +@@ -402,34 +402,45 @@ final class OverlayManagerServiceImpl { + private int calculateNewState(@Nullable final PackageInfo targetPackage, + @NonNull final PackageInfo overlayPackage, final int userId) + throws OverlayManagerSettings.BadKeyException { ++ ++ // STATE 0 CHECK: Check if the overlay package is disabled by PackageManager + if (!overlayPackage.applicationInfo.enabled) { + return STATE_NOT_APPROVED_COMPONENT_DISABLED; + } + ++ // OVERLAY STATE CHECK: Check the current overlay's activation ++ boolean stateCheck = mSettings.getEnabled(overlayPackage.packageName, userId); ++ ++ // STATE 1 CHECK: Check if the overlay's target package is missing from the device + if (targetPackage == null) { + return STATE_NOT_APPROVED_MISSING_TARGET; + } + ++ // STATE 2 CHECK: Check if the overlay has an existing idmap file created. Perhaps ++ // there were no matching resources between the two packages? (Overlay & Target) + if (!mIdmapManager.idmapExists(overlayPackage, userId)) { + return STATE_NOT_APPROVED_NO_IDMAP; + } + +- final boolean enableIfApproved = mSettings.getEnabled(overlayPackage.packageName, userId); +- +- if (mPackageManager.signaturesMatching(targetPackage.packageName, +- overlayPackage.packageName, userId)) { +- return enableIfApproved ? STATE_APPROVED_ENABLED : STATE_APPROVED_DISABLED; +- } +- ++ // STATE 6 CHECK: System Overlays, also known as RRO overlay files, work the same ++ // as OMS, but with enable/disable limitations. A system overlay resides in the ++ // directory "/vendor/overlay" depending on your device. ++ // ++ // Team Substratum: Disable this as this is a security vulnerability and a ++ // memory-limited partition. + if ((overlayPackage.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { +- return enableIfApproved ? STATE_APPROVED_ENABLED : STATE_APPROVED_DISABLED; ++ return STATE_NOT_APPROVED_COMPONENT_DISABLED; + } + ++ // STATE 3 CHECK: If the overlay only modifies resources explicitly granted by the ++ // target, we approve it. ++ // ++ // Team Substratum: Always approve dangerous packages but disabled state + if (!mIdmapManager.isDangerous(overlayPackage, userId)) { +- return enableIfApproved ? STATE_APPROVED_ENABLED : STATE_APPROVED_DISABLED; ++ return STATE_APPROVED_DISABLED; + } + +- return STATE_NOT_APPROVED_DANGEROUS_OVERLAY; ++ return stateCheck ? STATE_APPROVED_ENABLED : STATE_APPROVED_DISABLED; + } + + private void removeIdmapIfPossible(@NonNull final OverlayInfo oi) { +-- +2.9.3 + diff --git a/frameworks/base/0010-OMS7-N-Do-not-enforce-code-policy-limiting-overlay-i.patch b/frameworks/base/0010-OMS7-N-Do-not-enforce-code-policy-limiting-overlay-i.patch new file mode 100644 index 0000000..abf34be --- /dev/null +++ b/frameworks/base/0010-OMS7-N-Do-not-enforce-code-policy-limiting-overlay-i.patch @@ -0,0 +1,29 @@ +From c41b5f7552e5ef1f221d1f755b37501037463c66 Mon Sep 17 00:00:00 2001 +From: Nicholas Chum +Date: Thu, 27 Oct 2016 07:08:00 +0200 +Subject: [PATCH 10/38] OMS7-N: Do not enforce code policy limiting overlay + installation [10/11] + +Change-Id: Iea317f3771f25dbfcbf4938e88cace12fd97d7eb +--- + services/core/java/com/android/server/pm/PackageManagerService.java | 4 ---- + 1 file changed, 4 deletions(-) + +diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java +index 7277f6a..46679e5 100644 +--- a/services/core/java/com/android/server/pm/PackageManagerService.java ++++ b/services/core/java/com/android/server/pm/PackageManagerService.java +@@ -8143,10 +8143,6 @@ public class PackageManagerService extends IPackageManager.Stub { + pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED; + } + +- if ((policyFlags & PackageParser.PARSE_ENFORCE_CODE) != 0) { +- enforceCodePolicy(pkg); +- } +- + if (mCustomResolverComponentName != null && + mCustomResolverComponentName.getPackageName().equals(pkg.packageName)) { + setUpCustomResolverActivity(pkg); +-- +2.9.3 + diff --git a/frameworks/base/0011-OMS7-N-Implement-multi-target-enable-disable-and-dis.patch b/frameworks/base/0011-OMS7-N-Implement-multi-target-enable-disable-and-dis.patch new file mode 100644 index 0000000..c155b92 --- /dev/null +++ b/frameworks/base/0011-OMS7-N-Implement-multi-target-enable-disable-and-dis.patch @@ -0,0 +1,470 @@ +From 61a04ac0c542521026b925d35faf4244e6ea2810 Mon Sep 17 00:00:00 2001 +From: Jacob McSwain +Date: Sun, 26 Jun 2016 15:21:52 -0500 +Subject: [PATCH 11/38] OMS7-N: Implement multi-target enable/disable and + disable-all [11/11] + +Just use the enable option like normal, but you can add more arguments +for more packages. Also add a feature that allows the client to disable +all of the current user's overlays. + +Multiple targets example: + om enable android.AkZent com.android.systemui.AkZent + +Works the same as: + om enable android.AkZent && om enable com.android.systemui.AkZent + +Original implementation for M by @USA-RedDragon +Current and further development by @nicholaschum + +Change-Id: I04a595084a87b8260b5c534c4f5f111adbe154d7 +--- + core/java/android/content/om/IOverlayManager.aidl | 10 +- + .../android/server/om/OverlayManagerService.java | 48 ++++++---- + .../server/om/OverlayManagerServiceImpl.java | 12 ++- + .../android/server/om/OverlayManagerSettings.java | 31 +++--- + .../server/om/OverlayManagerShellCommand.java | 104 +++++++++++++++++++-- + 5 files changed, 161 insertions(+), 44 deletions(-) + +diff --git a/core/java/android/content/om/IOverlayManager.aidl b/core/java/android/content/om/IOverlayManager.aidl +index 4f5d960..8e349dc 100644 +--- a/core/java/android/content/om/IOverlayManager.aidl ++++ b/core/java/android/content/om/IOverlayManager.aidl +@@ -83,10 +83,12 @@ interface IOverlayManager { + * @param packageName The name of the overlay package. + * @param enable true to enable the overlay, false to disable it. + * @param userId The user for which to change the overlay. ++ * @param shouldWait true to wait to reload resources until refresh is called + * @return true if the system successfully registered the request, false + * otherwise. + */ +- boolean setEnabled(in String packageName, in boolean enable, in int userId); ++ boolean setEnabled(in String packageName, in boolean enable, in int userId, ++ in boolean shouldWait); + + /** + * Change the priority of the given overlay to be just higher than the +@@ -126,4 +128,10 @@ interface IOverlayManager { + * @param userId The user for which to change the overlay. + */ + boolean setLowestPriority(in String packageName, in int userId); ++ ++ /** ++ * Refresh assets ++ * @param uid the user to refresh assets for ++ */ ++ void refresh(in int uid); + } +diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java +index 761ef52..deb9046 100644 +--- a/services/core/java/com/android/server/om/OverlayManagerService.java ++++ b/services/core/java/com/android/server/om/OverlayManagerService.java +@@ -255,7 +255,7 @@ public final class OverlayManagerService extends SystemService { + synchronized (mLock) { + targets = mImpl.onSwitchUser(newUserId); + } +- updateAssets(newUserId, targets); ++ updateSelectedAssets(newUserId, targets); + } + + public List getEnabledOverlayPaths(@NonNull final String packageName, +@@ -451,7 +451,7 @@ public final class OverlayManagerService extends SystemService { + + @Override + public boolean setEnabled(@Nullable final String packageName, final boolean enable, +- int userId) throws RemoteException { ++ int userId, final boolean shouldWait) throws RemoteException { + enforceChangeConfigurationPermission("setEnabled"); + userId = handleIncomingUser(userId, "setEnabled"); + if (packageName == null) { +@@ -461,7 +461,7 @@ public final class OverlayManagerService extends SystemService { + final long ident = Binder.clearCallingIdentity(); + try { + synchronized (mLock) { +- return mImpl.onSetEnabled(packageName, enable, userId); ++ return mImpl.onSetEnabled(packageName, enable, userId, shouldWait); + } + } finally { + Binder.restoreCallingIdentity(ident); +@@ -590,6 +590,15 @@ public final class OverlayManagerService extends SystemService { + message); + } + } ++ ++ public void refresh(int uid) { ++ Collection targets; ++ synchronized (mLock) { ++ targets = mImpl.onSwitchUser(uid); ++ } ++ List targeted = new ArrayList(targets); ++ updateSelectedAssets(uid, targeted); ++ } + }; + + private boolean isOverlayPackage(@NonNull final PackageInfo pi) { +@@ -603,45 +612,48 @@ public final class OverlayManagerService extends SystemService { + } + + @Override +- public void onOverlayAdded(@NonNull final OverlayInfo oi) { +- scheduleBroadcast(Intent.ACTION_OVERLAY_ADDED, oi, oi.isEnabled()); ++ public void onOverlayAdded(@NonNull final OverlayInfo oi, final boolean shouldWait) { ++ scheduleBroadcast(Intent.ACTION_OVERLAY_ADDED, oi, oi.isEnabled(), shouldWait); + } + + @Override +- public void onOverlayRemoved(@NonNull final OverlayInfo oi) { +- scheduleBroadcast(Intent.ACTION_OVERLAY_REMOVED, oi, oi.isEnabled()); ++ public void onOverlayRemoved(@NonNull final OverlayInfo oi, final boolean shouldWait) { ++ scheduleBroadcast(Intent.ACTION_OVERLAY_REMOVED, oi, oi.isEnabled(), shouldWait); + } + + @Override +- public void onOverlayChanged(@NonNull final OverlayInfo oi, +- @NonNull final OverlayInfo oldOi) { +- scheduleBroadcast(Intent.ACTION_OVERLAY_CHANGED, oi, oi.isEnabled() != oldOi.isEnabled()); ++ public void onOverlayChanged(@NonNull final OverlayInfo oi, @NonNull OverlayInfo oldOi, ++ final boolean shouldWait) { ++ scheduleBroadcast(Intent.ACTION_OVERLAY_CHANGED, oi, ++ oi.isEnabled() != oldOi.isEnabled(), shouldWait); + } + + @Override + public void onOverlayPriorityChanged(@NonNull final OverlayInfo oi) { +- scheduleBroadcast(Intent.ACTION_OVERLAY_PRIORITY_CHANGED, oi, oi.isEnabled()); ++ scheduleBroadcast(Intent.ACTION_OVERLAY_PRIORITY_CHANGED, oi, oi.isEnabled(), false); + } + + private void scheduleBroadcast(@NonNull final String action, @NonNull final OverlayInfo oi, +- final boolean doUpdate) { +- FgThread.getHandler().post(new BroadcastRunnable(action, oi, doUpdate)); ++ final boolean doUpdate, final boolean shouldWait) { ++ FgThread.getHandler().post(new BroadcastRunnable(action, oi, doUpdate, shouldWait)); + } + + private final class BroadcastRunnable extends Thread { + private final String mAction; + private final OverlayInfo mOverlayInfo; + private final boolean mDoUpdate; ++ private final boolean shouldWait; + +- public BroadcastRunnable(@NonNull final String action, @NonNull final OverlayInfo oi, +- final boolean doUpdate) { ++ public BroadcastRunnable(@NonNull final String action, @NonNull final OverlayInfo oi, ++ final boolean doUpdate, final boolean shouldWait) { + mAction = action; + mOverlayInfo = oi; + mDoUpdate = doUpdate; ++ this.shouldWait = shouldWait; + } + + public void run() { +- if (mDoUpdate) { ++ if (mDoUpdate && !shouldWait) { + updateAssets(mOverlayInfo.userId, mOverlayInfo.targetPackageName); + } + sendBroadcast(mAction, mOverlayInfo.targetPackageName, mOverlayInfo.packageName, +@@ -672,10 +684,10 @@ public final class OverlayManagerService extends SystemService { + private void updateAssets(final int userId, final String targetPackageName) { + final List list = new ArrayList<>(); + list.add(targetPackageName); +- updateAssets(userId, list); ++ updateSelectedAssets(userId, list); + } + +- private void updateAssets(final int userId, List targetPackageNames) { ++ private void updateSelectedAssets(final int userId, List targetPackageNames) { + final PackageManagerInternal pm = LocalServices.getService(PackageManagerInternal.class); + final boolean updateFrameworkRes = targetPackageNames.contains("android"); + if (updateFrameworkRes) { +diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java +index 4c61968..c515640 100644 +--- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java ++++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java +@@ -324,7 +324,7 @@ final class OverlayManagerServiceImpl { + } + + boolean onSetEnabled(@NonNull final String packageName, final boolean enable, +- final int userId) { ++ final int userId, final boolean shouldWait) { + if (DEBUG) { + Slog.d(TAG, String.format("onSetEnabled packageName=%s enable=%s userId=%d", + packageName, enable, userId)); +@@ -340,7 +340,7 @@ final class OverlayManagerServiceImpl { + final PackageInfo targetPackage = + mPackageManager.getPackageInfo(oi.targetPackageName, userId); + mSettings.setEnabled(packageName, userId, enable); +- updateState(targetPackage, overlayPackage, userId); ++ updateState(targetPackage, overlayPackage, userId, shouldWait); + return true; + } catch (OverlayManagerSettings.BadKeyException e) { + return false; +@@ -379,6 +379,12 @@ final class OverlayManagerServiceImpl { + private void updateState(@Nullable final PackageInfo targetPackage, + @NonNull final PackageInfo overlayPackage, final int userId) + throws OverlayManagerSettings.BadKeyException { ++ updateState(targetPackage, overlayPackage, userId, false); ++ } ++ ++ private void updateState(@Nullable final PackageInfo targetPackage, ++ @NonNull final PackageInfo overlayPackage, final int userId, ++ final boolean shouldWait) throws OverlayManagerSettings.BadKeyException { + if (targetPackage != null) { + mIdmapManager.createIdmap(targetPackage, overlayPackage, userId); + } +@@ -395,7 +401,7 @@ final class OverlayManagerServiceImpl { + OverlayInfo.stateToString(currentState), + OverlayInfo.stateToString(newState))); + } +- mSettings.setState(overlayPackage.packageName, userId, newState); ++ mSettings.setState(overlayPackage.packageName, userId, newState, shouldWait); + } + } + +diff --git a/services/core/java/com/android/server/om/OverlayManagerSettings.java b/services/core/java/com/android/server/om/OverlayManagerSettings.java +index af0bb64..935ea02 100644 +--- a/services/core/java/com/android/server/om/OverlayManagerSettings.java ++++ b/services/core/java/com/android/server/om/OverlayManagerSettings.java +@@ -76,7 +76,7 @@ final class OverlayManagerSettings { + final OverlayInfo oi = item.getOverlayInfo(); + mItems.remove(item); + if (oi != null) { +- notifyOverlayRemoved(oi); ++ notifyOverlayRemoved(oi, false); + } + } + +@@ -135,7 +135,7 @@ final class OverlayManagerSettings { + final OverlayInfo oi = item.getOverlayInfo(); + item.setUpgrading(true); + item.setState(STATE_NOT_APPROVED_UNKNOWN); +- notifyOverlayRemoved(oi); ++ notifyOverlayRemoved(oi, false); + } else { + item.setUpgrading(false); + } +@@ -172,8 +172,8 @@ final class OverlayManagerSettings { + return item.getState(); + } + +- void setState(@NonNull final String packageName, final int userId, final int state) +- throws BadKeyException { ++ void setState(@NonNull final String packageName, final int userId, final int state, ++ final boolean shouldWait) throws BadKeyException { + final SettingsItem item = select(packageName, userId); + if (item == null) { + throw new BadKeyException(packageName, userId); +@@ -182,10 +182,10 @@ final class OverlayManagerSettings { + item.setState(state); + final OverlayInfo current = item.getOverlayInfo(); + if (previous.state == STATE_NOT_APPROVED_UNKNOWN) { +- notifyOverlayAdded(current); ++ notifyOverlayAdded(current, shouldWait); + notifySettingsChanged(); + } else if (current.state != previous.state) { +- notifyOverlayChanged(current, previous); ++ notifyOverlayChanged(current, previous, shouldWait); + notifySettingsChanged(); + } + } +@@ -602,32 +602,32 @@ final class OverlayManagerSettings { + } + } + +- private void notifyOverlayAdded(@NonNull final OverlayInfo oi) { ++ private void notifyOverlayAdded(@NonNull final OverlayInfo oi, final boolean shouldWait) { + if (DEBUG) { + assertNotNull(oi); + } + for (final ChangeListener listener : mListeners) { +- listener.onOverlayAdded(oi); ++ listener.onOverlayAdded(oi, shouldWait); + } + } + +- private void notifyOverlayRemoved(@NonNull final OverlayInfo oi) { ++ private void notifyOverlayRemoved(@NonNull final OverlayInfo oi, final boolean shouldWait) { + if (DEBUG) { + assertNotNull(oi); + } + for (final ChangeListener listener : mListeners) { +- listener.onOverlayRemoved(oi); ++ listener.onOverlayRemoved(oi, shouldWait); + } + } + + private void notifyOverlayChanged(@NonNull final OverlayInfo oi, +- @NonNull final OverlayInfo oldOi) { ++ @NonNull final OverlayInfo oldOi, final boolean shouldWait) { + if (DEBUG) { + assertNotNull(oi); + assertNotNull(oldOi); + } + for (final ChangeListener listener : mListeners) { +- listener.onOverlayChanged(oi, oldOi); ++ listener.onOverlayChanged(oi, oldOi, shouldWait); + } + } + +@@ -642,9 +642,10 @@ final class OverlayManagerSettings { + + interface ChangeListener { + void onSettingsChanged(); +- void onOverlayAdded(@NonNull OverlayInfo oi); +- void onOverlayRemoved(@NonNull OverlayInfo oi); +- void onOverlayChanged(@NonNull OverlayInfo oi, @NonNull OverlayInfo oldOi); ++ void onOverlayAdded(@NonNull OverlayInfo oi, boolean shouldWait); ++ void onOverlayRemoved(@NonNull OverlayInfo oi, boolean shouldWait); ++ void onOverlayChanged(@NonNull OverlayInfo oi, @NonNull OverlayInfo oldOi, ++ boolean shouldWait); + void onOverlayPriorityChanged(@NonNull OverlayInfo oi); + } + +diff --git a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java +index d6f5373..44004c1 100644 +--- a/services/core/java/com/android/server/om/OverlayManagerShellCommand.java ++++ b/services/core/java/com/android/server/om/OverlayManagerShellCommand.java +@@ -25,8 +25,10 @@ import android.os.ShellCommand; + import android.os.UserHandle; + + import java.io.PrintWriter; ++import java.util.ArrayList; + import java.util.List; + import java.util.Map; ++import java.util.Map.Entry; + + /** + * Implementation of 'cmd overlay' commands. +@@ -45,7 +47,9 @@ final class OverlayManagerShellCommand extends ShellCommand { + @Override + public int onCommand(@Nullable final String cmd) { + if (cmd == null) { +- return handleDefaultCommands(cmd); ++ final PrintWriter out = getOutPrintWriter(); ++ out.println("The overlay manager has already been initialized."); ++ return -1; + } + final PrintWriter err = getErrPrintWriter(); + try { +@@ -56,6 +60,8 @@ final class OverlayManagerShellCommand extends ShellCommand { + return runEnableDisable(true); + case "disable": + return runEnableDisable(false); ++ case "disable-all": ++ return runDisableAll(); + case "set-priority": + return runSetPriority(); + default: +@@ -82,10 +88,12 @@ final class OverlayManagerShellCommand extends ShellCommand { + out.println(" Overlay packages are printed in priority order. With optional"); + out.println(" parameters PACKAGEs, limit output to the specified packages"); + out.println(" but include more information about each package."); +- out.println(" enable [--user USER_ID] PACKAGE"); +- out.println(" Enable overlay package PACKAGE."); +- out.println(" disable [--user USER_ID] PACKAGE"); +- out.println(" Disable overlay package PACKAGE."); ++ out.println(" enable [--user USER_ID] [PACKAGE [PACKAGE [...]]]"); ++ out.println(" Enable overlay package PACKAGE or subsequent counts of PACKAGE."); ++ out.println(" disable [--user USER_ID] [PACKAGE [PACKAGE [...]]]"); ++ out.println(" Disable overlay package PACKAGE or subsequent counts of PACKAGE."); ++ out.println(" disable-all [--user USER_ID]"); ++ out.println(" Disable all overlay packages."); + out.println(" set-priority [--user USER_ID] PACKAGE PARENT|lowest|highest"); + out.println(" Change the priority of the overlay PACKAGE to be just higher than"); + out.println(" the priority of PACKAGE_PARENT If PARENT is the special keyword"); +@@ -145,8 +153,90 @@ final class OverlayManagerShellCommand extends ShellCommand { + } + } + +- final String packageName = getNextArgRequired(); +- return mInterface.setEnabled(packageName, enable, userId) ? 0 : 1; ++ int argc = 0; ++ String packageName = getNextArgRequired(); ++ ArrayList packages = new ArrayList<>(); ++ if (packageName == null) { ++ System.err.println("Error: no packages specified"); ++ return 1; ++ } ++ while (packageName != null) { ++ argc++; ++ packages.add(packageName); ++ packageName = getNextArg(); ++ } ++ if (argc > 1) { ++ for (String pkg : packages) { ++ boolean ret = mInterface.setEnabled(pkg, enable, userId, false); ++ if (!ret) { ++ System.err.println("Error: Failed to " + ((enable) ? "enable ": "disable ") + pkg); ++ } ++ } ++ return 0; ++ } else if (argc == 1) { ++ return mInterface.setEnabled(packages.get(0), enable, userId, false) ? 0 : 1; ++ } else { ++ System.err.println("Error: A fatal exception has occurred."); ++ return 1; ++ } ++ } ++ ++ private int runDisableAll() { ++ int userId = UserHandle.USER_OWNER; ++ String opt; ++ while ((opt = getNextOption()) != null) { ++ switch (opt) { ++ case "--user": ++ userId = UserHandle.parseUserArg(getNextArgRequired()); ++ break; ++ default: ++ System.err.println("Error: Unknown option: " + opt); ++ return 1; ++ } ++ } ++ ++ try { ++ Map> targetsAndOverlays = mInterface.getAllOverlays(userId); ++ int iterator = 0; ++ int overlaySize = targetsAndOverlays.entrySet().size(); ++ for (Entry> targetEntry : targetsAndOverlays.entrySet()) { ++ int iterator_nested = 0; ++ int targetSize_nested = targetEntry.getValue().size(); ++ iterator++; ++ for (OverlayInfo oi : targetEntry.getValue()) { ++ if (iterator_nested < targetSize_nested) { ++ if (oi.isEnabled()) { ++ boolean worked = mInterface.setEnabled(oi.packageName, false, userId, true); ++ if (!worked) { ++ System.err.println("Failed to disable " + oi.packageName); ++ } ++ } ++ } else { ++ if (iterator == overlaySize) { ++ if (oi.isEnabled()) { ++ boolean worked = mInterface.setEnabled(oi.packageName, false, userId, false); ++ if (!worked) { ++ System.err.println("Failed to disable " + oi.packageName); ++ } ++ } ++ } else { ++ if (oi.isEnabled()) { ++ boolean worked = mInterface.setEnabled(oi.packageName, false, userId, true); ++ if (!worked) { ++ System.err.println("Failed to disable " + oi.packageName); ++ } ++ } ++ } ++ } ++ iterator_nested++; ++ } ++ } ++ mInterface.refresh(userId); ++ } catch (RemoteException re) { ++ System.err.println(re.toString()); ++ System.err.println("Error: A fatal exception has occurred."); ++ } ++ return 0; + } + + private int runSetPriority() throws RemoteException { +-- +2.9.3 + diff --git a/frameworks/base/0012-Themes-Expose-resolver-hardcoded-colors.patch b/frameworks/base/0012-Themes-Expose-resolver-hardcoded-colors.patch new file mode 100644 index 0000000..618f62d --- /dev/null +++ b/frameworks/base/0012-Themes-Expose-resolver-hardcoded-colors.patch @@ -0,0 +1,200 @@ +From 68105cc059781ce4d0948cfe6d34511da24079d2 Mon Sep 17 00:00:00 2001 +From: Dave Kover +Date: Fri, 9 Dec 2016 10:47:17 -0700 +Subject: [PATCH 12/38] Themes: Expose resolver hardcoded colors + +commit dbbd5e70cc65002df41561474b03362022dd6716 +Author: Dave Kover +Date: Wed Feb 18 16:11:14 2015 -0800 + + Themes: Expose resolver hardcoded colors + + Expose background colors of the resolver list. + + Change-Id: I3a0a460c5ffe0f5057b3b9ec92faa7a3e09c9e01 + +commit 0343eb126f3901a3857791137f74fa805bb9d75c +Author: Thyrus11 +Date: Sat Feb 21 07:19:42 2015 +0100 + + Themes: Make resolver list fully themeable + + Follow-up on commit cc9e3b8fcba95b911d1cda36f7770c410058aa8b. + + Change-Id: I3f006a1157db9d0b151a4fe8edf50e7edc7a0b9f + +commit c7d973809488b801e8c708d740009f1233bb762e +Author: Nicholas Chum +Date: Sun Nov 8 05:27:28 2015 -0500 + + Themes: Allow Resolver List BG to be fully themed + + We are able to trace the activity of the new resolver/chooser through + different + methods, thus leading us to the Java file: + \com\android\internal\app\ChooserActivity.java + Here we see that the exposed "chooser_service_row_background_color" is + available, but not the rest of the activity, so we look into R.layout's, + and we + find chooser_grid to be the only one containing hardcoded + "@color/white" values (as this is framework, we assume this is also + known as + "@android:color/white" to themers). + + Expose all "@color/white" values from this file to resolver_list_bg. + + Change-Id: I286d92b5d1f672c8adb3c0af1951793521536d90 + +Change-Id: Iec7951147bbbc99aee6b06ae50c1acc7b9c01a7f +--- + core/res/res/layout/chooser_grid.xml | 6 +++--- + .../res/layout/resolver_different_item_header.xml | 2 +- + core/res/res/layout/resolver_list.xml | 8 ++++---- + core/res/res/layout/resolver_list_with_default.xml | 6 +++--- + core/res/res/values/projekt_colors.xml | 20 ++++++++++++++++++++ + 5 files changed, 31 insertions(+), 11 deletions(-) + create mode 100644 core/res/res/values/projekt_colors.xml + +diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml +index d8dd447..78c2e05 100644 +--- a/core/res/res/layout/chooser_grid.xml ++++ b/core/res/res/layout/chooser_grid.xml +@@ -31,7 +31,7 @@ + android:layout_alwaysShow="true" + android:elevation="8dp" + android:paddingStart="16dp" +- android:background="@color/white" > ++ android:background="@color/resolver_list_bg" > + +diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml +index c4e8e9c..1b6230a 100644 +--- a/core/res/res/layout/resolver_list.xml ++++ b/core/res/res/layout/resolver_list.xml +@@ -30,7 +30,7 @@ + android:layout_height="wrap_content" + android:layout_alwaysShow="true" + android:elevation="8dp" +- android:background="@color/white"> ++ android:background="@color/resolver_list_bg"> + + + + + +