Initial commit
authorLuK1337 <priv.luk@gmail.com>
Fri, 10 Feb 2017 15:27:22 +0000 (16:27 +0100)
committerLuK1337 <priv.luk@gmail.com>
Fri, 10 Feb 2017 15:27:22 +0000 (16:27 +0100)
62 files changed:
build/0001-OMS-N-adb-shell-command-to-access-OverlayManagerServ.patch [new file with mode: 0644]
frameworks/base/0001-OMS7-N-Support-tagging-resources-as-OK-to-overlay-1-.patch [new file with mode: 0644]
frameworks/base/0002-OMS7-N-Introduce-the-OverlayManagerService-2-11.patch [new file with mode: 0644]
frameworks/base/0003-OMS7-N-Integrate-OverlayManagerService-into-framewor.patch [new file with mode: 0644]
frameworks/base/0004-OMS7-N-Set-EXTRA_REPLACING-correctly-in-ACTION_PACKA.patch [new file with mode: 0644]
frameworks/base/0005-OMS7-N-idmap-suppress-print-for-padded-resources-5-1.patch [new file with mode: 0644]
frameworks/base/0006-OMS7-N-Fix-memory-leak-during-idmap-creation-6-11.patch [new file with mode: 0644]
frameworks/base/0007-OMS7-N-installd-add-command-rmidmap-7-11.patch [new file with mode: 0644]
frameworks/base/0008-OMS7-N-Disable-Zygote-preloaded-drawables-8-11.patch [new file with mode: 0644]
frameworks/base/0009-OMS7-N-Persistence-on-boot-through-OverlayManagerSer.patch [new file with mode: 0644]
frameworks/base/0010-OMS7-N-Do-not-enforce-code-policy-limiting-overlay-i.patch [new file with mode: 0644]
frameworks/base/0011-OMS7-N-Implement-multi-target-enable-disable-and-dis.patch [new file with mode: 0644]
frameworks/base/0012-Themes-Expose-resolver-hardcoded-colors.patch [new file with mode: 0644]
frameworks/base/0013-Themes-Allow-Immersive-cling-colors-to-be-fully-them.patch [new file with mode: 0644]
frameworks/base/0014-Themes-Allow-Permission-Icons-to-be-fully-themed.patch [new file with mode: 0644]
frameworks/base/0015-Themes-Allow-Navbar-ripple-color-to-be-themed.patch [new file with mode: 0644]
frameworks/base/0016-SystemUI-Expose-QS-edit-item-decoration-background-c.patch [new file with mode: 0644]
frameworks/base/0017-Allow-custom-alpha-for-notification-shade-bg-color.patch [new file with mode: 0644]
frameworks/base/0018-Themes-Expose-various-QuickSettings-text-colors.patch [new file with mode: 0644]
frameworks/base/0019-Notifications-Expose-a-bool-to-disable-dynamic-color.patch [new file with mode: 0644]
frameworks/base/0020-Notification-dynamic-colors-bool-compatible-with-OMS.patch [new file with mode: 0644]
frameworks/base/0021-Allow-prevention-of-doze-notification-color-inversio.patch [new file with mode: 0644]
frameworks/base/0022-OMS7-compatible-Ambient-notification-inversion.patch [new file with mode: 0644]
frameworks/base/0023-SystemUI-Use-own-drawables-for-QS-expand-icon.patch [new file with mode: 0644]
frameworks/base/0024-N-Extras-Add-dynamic-theme-BootAnimation-support.patch [new file with mode: 0644]
frameworks/base/0025-N-Extras-Add-dynamic-theme-fonts-support.patch [new file with mode: 0644]
frameworks/base/0026-N-Extras-AudioService-Allow-system-effect-sounds-to-.patch [new file with mode: 0644]
frameworks/base/0027-OMS7-N-ApplicationsState-add-filter-for-Substratum-o.patch [new file with mode: 0644]
frameworks/base/0028-OMS7-N-ApplicationsState-add-filter-for-Substratum-i.patch [new file with mode: 0644]
frameworks/base/0029-Themes-Expose-QS-battery.patch [new file with mode: 0644]
frameworks/base/0030-OMS-Introduce-MODIFY_OVERLAYS-permission-for-user-ap.patch [new file with mode: 0644]
frameworks/base/0031-SystemUI-Expose-switch-bar-title.patch [new file with mode: 0644]
frameworks/base/0032-Themes-Expose-manifest-styles-for-themes.patch [new file with mode: 0644]
frameworks/base/0033-OMS-StrictMode-and-files-under-data-system-theme.patch [new file with mode: 0644]
frameworks/base/0034-doze-allow-grayscale-even-if-invert-boolean-is-false.patch [new file with mode: 0644]
frameworks/base/0035-Expose-external-qs-tile-tint-color.patch [new file with mode: 0644]
frameworks/base/0036-graphics-ADB-N-icon-compatible-with-OMS7.patch [new file with mode: 0644]
frameworks/base/0037-Set-external-QS-tiles-tint-mode-to-SRC_ATOP.patch [new file with mode: 0644]
frameworks/base/0038-Themes-Expose-Keyguard-affordance-circle-background.patch [new file with mode: 0644]
frameworks/native/0001-OMS-N-installd-add-command-rmidmap.patch [new file with mode: 0644]
packages/apps/Contacts/0001-Themes-Expose-hardcoded-layout-and-styles-colors.patch [new file with mode: 0644]
packages/apps/ContactsCommon/0001-Themes-Expose-hardcoded-contact-tile-text-colors.patch [new file with mode: 0644]
packages/apps/ExactCalculator/0001-Themes-Expose-hard-coded-background-in-java.patch [new file with mode: 0644]
packages/apps/ExactCalculator/0002-Themes-Expose-all-elevations.patch [new file with mode: 0644]
packages/apps/PhoneCommon/0001-Themes-Make-dialpad-seperator-line-theme-able.patch [new file with mode: 0644]
packages/apps/Settings/0001-Settings-Expose-dashboard-category-and-tile-color.patch [new file with mode: 0644]
packages/apps/Settings/0002-Settings-Expose-condition-card-colors.patch [new file with mode: 0644]
packages/apps/Settings/0003-Settings-Expose-storage-summary-text.patch [new file with mode: 0644]
packages/apps/Settings/0004-Settings-Expose-gesture-settings-switchbar.patch [new file with mode: 0644]
packages/apps/Settings/0005-Settings-Expose-storage-icon-colors.patch [new file with mode: 0644]
packages/apps/Settings/0006-Settings-Expose-LinearColorBar-default-colors.patch [new file with mode: 0644]
packages/apps/Settings/0007-OMS7-N-Apps-show-hide-Substratum-overlays-2-2.patch [new file with mode: 0644]
packages/apps/Settings/0008-OMS7-N-Apps-show-hide-Substratum-icon-overlays-2-2.patch [new file with mode: 0644]
packages/apps/Settings/0009-Exclude-overlays-from-the-app-counter.patch [new file with mode: 0644]
packages/apps/Settings/0010-Hide-the-show-hide-overlays-when-no-overlay-installe.patch [new file with mode: 0644]
packages/apps/Settings/0011-Expose-color-for-external-settings-icons.patch [new file with mode: 0644]
packages/apps/Settings/0012-Expose-dashboard-category-padding-bottom.patch [new file with mode: 0644]
packages/apps/Settings/0013-Expose-switchbar-background-color.patch [new file with mode: 0644]
packages/apps/Settings/0014-Settings-Expose-bluetooth-pin-confirm-dialog-text-co.patch [new file with mode: 0644]
packages/apps/Settings/0015-Set-external-settings-icon-tint-mode-to-SRC_ATOP.patch [new file with mode: 0644]
patch.sh [new file with mode: 0755]
system/sepolicy/0001-OMS-N-Add-service-overlay-to-service_contexts.patch [new file with mode: 0644]

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 (file)
index 0000000..80b0242
--- /dev/null
@@ -0,0 +1,44 @@
+From 9c60c359e98e9c862411c08e72bb7a1520b736d1 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= <marten.kongstad@sonymobile.com>
+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 <martin.wallgren@sonymobile.com>
+Signed-off-by: Zoran Jovanovic <zoran.jovanovic@sonymobile.com>
+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 (file)
index 0000000..342173e
--- /dev/null
@@ -0,0 +1,474 @@
+From 75d5463e848764c3255c32d126415faceca62023 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= <marten.kongstad@sonymobile.com>
+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 <overlay type="..." name="..."/> 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<uint8_t, IdmapTypeMap> 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<const char*>(map + 4));
++        pTargetPath->setTo(reinterpret_cast<const char*>(map + 5));
+     }
+     if (pOverlayPath) {
+-        pOverlayPath->setTo(reinterpret_cast<const char*>(map + 4 + 256 / sizeof(uint32_t)));
++        pOverlayPath->setTo(reinterpret_cast<const char*>(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 <overlay>\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 <overlay>\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<Type> 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<const ResourceFilter>&
+                         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<AaptFile>& data, bool isPublic)
++ssize_t ResourceTable::Entry::flatten(Bundle* /* bundle */, const sp<AaptFile>& 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<AaptFile>&
+     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::ConfigList> 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<ConfigList> 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<AaptFile>& data, bool isPublic);
++        ssize_t flatten(Bundle*, const sp<AaptFile>& 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>& entry) {
+             mEntries.add(config, entry);
+@@ -440,6 +448,7 @@ public:
+         String16 mTypeComment;
+         bool mPublic;
+         SourcePos mPublicSourcePos;
++        bool mOverlay;
+         int32_t mEntryIndex;
+         DefaultKeyedVector<ConfigDescription, sp<Entry> > 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<String16, sp<ConfigList> >& getConfigs() const { return mConfigs; }
+         const Vector<sp<ConfigList> >& getOrderedConfigs() const { return mOrderedConfigs; }
+@@ -516,6 +547,7 @@ public:
+         String16 mName;
+         SourcePos* mFirstPublicSourcePos;
+         DefaultKeyedVector<String16, Public> mPublic;
++        DefaultKeyedVector<String16, Overlay> mOverlay;
+         DefaultKeyedVector<String16, sp<ConfigList> > mConfigs;
+         Vector<sp<ConfigList> > mOrderedConfigs;
+         SortedVector<String16> 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 (file)
index 0000000..919e42c
--- /dev/null
@@ -0,0 +1,3082 @@
+From 90815d5667efdc258011508a5599a45bd3d66756 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= <marten.kongstad@sonymobile.com>
+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 <overlay type="..." name="..."/> 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<String, List<OverlayInfo>> 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.
++     * <p/>
++     * 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.
++     * <p/>
++     * 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<OverlayInfo> CREATOR = new Parcelable.Creator<OverlayInfo>() {
++        @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 "<unknown state>";
++        }
++    }
++
++    @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<PackageInfo> 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<String> 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 @@
+     <protected-broadcast android:name="android.intent.action.USER_INITIALIZE" />
+     <protected-broadcast android:name="android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION" />
+     <protected-broadcast android:name="android.intent.action.SU_SESSION_CHANGED" />
++    <protected-broadcast android:name="android.intent.action.OVERLAY_ADDED" />
++    <protected-broadcast android:name="android.intent.action.OVERLAY_CHANGED" />
++    <protected-broadcast android:name="android.intent.action.OVERLAY_REMOVED" />
++    <protected-broadcast android:name="android.intent.action.OVERLAY_PRIORITY_CHANGED" />
+     <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGED" />
+     <protected-broadcast android:name="android.os.action.POWER_SAVE_MODE_CHANGING" />
+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.
++ *
++ * <p>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.</p>
++ *
++ * <p>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.</p>
++ *
++ * <p>The OMS receives input from three sources:</p>
++ *
++ * <ul>
++ *     <li>Callbacks from the SystemService class, specifically when the
++ *     Android framework is booting and when the end user switches Android
++ *     users.</li>
++ *
++ *     <li>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.</li>
++ *
++ *     <li>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.</li>
++ * </ul>
++ *
++ * <p>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.</p>
++ *
++ * <p>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.</p>
++ *
++ * <p>Creation and deletion of idmap files are handled by the IdmapManager
++ * class.</p>
++ *
++ * <p>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.</p>
++ *
++ * <pre>
++ *         Android framework
++ *            |         ^
++ *      . . . | . . . . | . . . .
++ *     .      |         |       .
++ *     .    AIDL,   broadcasts  .
++ *     .   intents      |       .
++ *     .      |         |       . . . . . . . . . . . .
++ *     .      v         |       .                     .
++ *     .  OverlayManagerService . OverlayManagerTests .
++ *     .                  \     .     /               .
++ *     . (1)               \    .    /            (3) .
++ *      . . . . . . . . . . \ . . . / . . . . . . . . .
++ *     .                     \     /              .
++ *     . (2)                  \   /               .
++ *     .           OverlayManagerServiceImpl      .
++ *     .                  |            |          .
++ *     .                  |            |          .
++ *     . OverlayManagerSettings     IdmapManager  .
++ *     .                                          .
++ *     . . . .  . . . . . . . . . . . . . . . . . .
++ * </pre>
++ *
++ * <p>Finally, here is a list of keywords used in the OMS context.</p>
++ *
++ * <ul>
++ *     <li><b>target [package]</b> -- A regular apk that may have its resource
++ *     pool extended  by zero or more overlay packages.</li>
++ *
++ *     <li><b>overlay [package]</b> -- An apk that provides additional
++ *     resources to another apk.</li>
++ *
++ *     <li><b>OMS</b> -- The OverlayManagerService, i.e. this class.</li>
++ *
++ *     <li><b>approved</b> -- 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.</li>
++ *
++ *     <li><b>not approved</b> -- The opposite of approved.</li>
++ *
++ *     <li><b>enabled</b> -- 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.</li>
++ *
++ *     <li><b>disabled</b> -- The opposite of enabled.</li>
++ *
++ *     <li><b>idmap</b> -- A mapping of resource IDs between target and overlay
++ *     used during resource lookup. Also the name of the binary that creates
++ *     the mapping.</li>
++ * </ul>
++ */
++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<String> targets;
++        synchronized (mLock) {
++            targets = mImpl.onSwitchUser(newUserId);
++        }
++        updateAssets(newUserId, targets);
++    }
++
++    public List<String> 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<String, List<OverlayInfo>> getAllOverlays(int userId)
++                throws RemoteException {
++            userId = handleIncomingUser(userId, "getAllOverlays");
++
++            synchronized (mLock) {
++                return mImpl.onGetOverlaysForUser(userId);
++            }
++        }
++
++        @Override
++        public List<OverlayInfo> 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<String> list = new ArrayList<>();
++        list.add(targetPackageName);
++        updateAssets(userId, list);
++    }
++
++    private void updateAssets(final int userId, List<String> 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<String, String[]> allPaths = new ArrayMap<>(targetPackageNames.size());
++        // synchronized (mLock) {
++        //     final List<String> frameworkPaths = mImpl.onGetEnabledOverlayPaths("android", userId);
++        //     for (final String packageName : targetPackageNames) {
++        //         final List<String> 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<UserInfo> getDeadUsers() {
++        final List<UserInfo> users = mUserManager.getUsers(false);
++        final List<UserInfo> onlyLiveUsers = mUserManager.getUsers(true);
++
++        // UserInfo doesn't implement equals, so we'll roll our own
++        // Collection.removeAll implementation
++        final Iterator<UserInfo> 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<HashMap<String, PackageInfo>> 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<PackageInfo> getOverlayPackages(final int userId) {
++            return mPackageManagerInternal.getOverlayPackages(userId);
++        }
++
++        public PackageInfo getCachedPackageInfo(@NonNull final String packageName,
++                final int userId) {
++            final HashMap<String, PackageInfo> 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<String, PackageInfo> 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<String, PackageInfo> 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 + "<empty>");
++                return;
++            }
++
++            for (int i = 0; i < mCache.size(); i++) {
++                final int userId = mCache.keyAt(i);
++                pw.println(TAB1 + "User " + userId);
++                final HashMap<String, PackageInfo> map = mCache.get(userId);
++                for (Map.Entry<String, PackageInfo> 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<String> onSwitchUser(final int newUserId) {
++        if (DEBUG) {
++            Slog.d(TAG, "onSwitchUser newUserId=" + newUserId);
++        }
++
++        final Set<String> packagesToUpdateAssets = new ArraySet<>();
++        final Map<String, List<OverlayInfo>> tmp = mSettings.getOverlaysForUser(newUserId);
++        final Map<String, OverlayInfo> storedOverlayInfos = new ArrayMap<>(tmp.size());
++        for (final List<OverlayInfo> 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<String> iter = packagesToUpdateAssets.iterator();
++        while (iter.hasNext()) {
++            String targetPackageName = iter.next();
++            if (mPackageManager.getPackageInfo(targetPackageName, newUserId) == null) {
++                iter.remove();
++            }
++        }
++
++        return new ArrayList<String>(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<OverlayInfo> 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<OverlayInfo> onGetOverlayInfosForTarget(@NonNull final String targetPackageName,
++            final int userId) {
++        return mSettings.getOverlaysForTarget(targetPackageName, userId);
++    }
++
++    Map<String, List<OverlayInfo>> 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<String> onGetEnabledOverlayPaths(@NonNull final String targetPackageName,
++            final int userId) {
++        final List<OverlayInfo> overlays = mSettings.getOverlaysForTarget(targetPackageName, userId);
++        final List<String> 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<Integer> 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<PackageInfo> 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<ChangeListener> mListeners = new ArrayList<>();
++
++    private final ArrayList<SettingsItem> 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<OverlayInfo> getOverlaysForTarget(@NonNull final String targetPackageName,
++            final int userId) {
++        final List<SettingsItem> items = selectWhereTarget(targetPackageName, userId);
++        if (items.isEmpty()) {
++            return Collections.emptyList();
++        }
++        final List<OverlayInfo> out = new ArrayList<>(items.size());
++        for (final SettingsItem item : items) {
++            if (item.isUpgrading()) {
++                continue;
++            }
++            out.add(item.getOverlayInfo());
++        }
++        return out;
++    }
++
++    Map<String, List<OverlayInfo>> getOverlaysForUser(final int userId) {
++        final List<SettingsItem> items = selectWhereUser(userId);
++        if (items.isEmpty()) {
++            return Collections.emptyMap();
++        }
++        final Map<String, List<OverlayInfo>> 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<OverlayInfo>());
++            }
++            final List<OverlayInfo> overlays = out.get(targetPackageName);
++            overlays.add(item.getOverlayInfo());
++        }
++        return out;
++    }
++
++    List<String> getTargetPackageNamesForUser(final int userId) {
++        final List<SettingsItem> items = selectWhereUser(userId);
++        if (items.isEmpty()) {
++            return Collections.emptyList();
++        }
++        final List<String> 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<Integer> getUsers() {
++        final ArrayList<Integer> 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<SettingsItem> 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<SettingsItem> 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 + "<none>");
++            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 + "<none>");
++            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<SettingsItem> 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<SettingsItem> 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<SettingsItem> selectWhereUser(final int userId) {
++        final ArrayList<SettingsItem> items = new ArrayList<>();
++        for (final SettingsItem item : mItems) {
++            if (item.userId == userId) {
++                items.add(item);
++            }
++        }
++        return items;
++    }
++
++    private List<SettingsItem> selectWhereTarget(@NonNull final String targetPackageName,
++            final int userId) {
++        final ArrayList<SettingsItem> 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<String, List<OverlayInfo>> 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<PackageInfo> getOverlayPackages(int userId) {
++            final ArrayList<PackageInfo> overlayPackages = new ArrayList<PackageInfo>();
++            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<String> getTargetPackageNames(int userId) {
++            List<String> 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 (file)
index 0000000..5104306
--- /dev/null
@@ -0,0 +1,1473 @@
+From 2a5bd30d939fd04580f162115ddba6c359290b6f Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= <marten.kongstad@sonymobile.com>
+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<const char *> *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<const char *> 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<const char *> *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<String> 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<String> 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<WeakReference<LoadedApk>> loadedPackages = new ArrayList<>();
++            loadedPackages.addAll(mPackages.values());
++            loadedPackages.addAll(mResourcePackages.values());
++            for (final WeakReference<LoadedApk> 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<IBinder, ActivityClientRecord> 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<String> 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<ResourcesImpl, ResourcesKey> 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<ResourcesImpl, ResourcesKey> 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<String> 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] = "<overlay> 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<String> protectedComponents;
+     public ArraySet<String> 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<String>(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<SharedZip> 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<asset_path> mOverlays;
+-
+         static Mutex gLock;
+         static DefaultKeyedVector<String8, wp<SharedZip> > 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<int32_t>(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<AssetManager*>(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:
+-        //   <path to apk><space><path to idmap><newline>
+-        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<AssetManager*>(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<AssetManager*>(this)->mAssetPaths.add(oap);
+-            const_cast<AssetManager*>(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<SharedZip> zip = mZipFile[idx];
+-    zip->addOverlay(overlay);
+-}
+-
+-bool AssetManager::ZipSet::getOverlay(const String8& path, size_t idx, asset_path* out) const
+-{
+-    sp<SharedZip> 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<String> 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<String> packagesToUpdate) {
++        final IPackageManager pm = AppGlobals.getPackageManager();
++        final Map<String, ApplicationInfo> 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<String> 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<String, String[]> allPaths = new ArrayMap<>(targetPackageNames.size());
+-        // synchronized (mLock) {
+-        //     final List<String> frameworkPaths = mImpl.onGetEnabledOverlayPaths("android", userId);
+-        //     for (final String packageName : targetPackageNames) {
+-        //         final List<String> 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<String, String[]> allPaths = new ArrayMap<>(targetPackageNames.size());
++        synchronized (mLock) {
++            final List<String> frameworkPaths = mImpl.onGetEnabledOverlayPaths("android", userId);
++            for (final String packageName : targetPackageNames) {
++                final List<String> 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<String, Set<String>> mKnownCodebase =
+             new ArrayMap<String, Set<String>>();
+-    // Tracks available target package names -> overlay package paths.
+-    final ArrayMap<String, ArrayMap<String, PackageParser.Package>> mOverlays =
+-        new ArrayMap<String, ArrayMap<String, PackageParser.Package>>();
+-
+     /**
+      * 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 <Package>/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<String, PackageParser.Package> 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<String, PackageParser.Package> 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<PackageParser.Package> cmp = new Comparator<PackageParser.Package>() {
+-            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<String, PackageParser.Package>());
+-                    }
+-                    ArrayMap<String, PackageParser.Package> 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<String> enabledComponents,
+             ArraySet<String> disabledComponents, boolean blockUninstall, int domainVerifState,
+-            int linkGeneration,
++            int linkGeneration, String[] resourceDirs,
+             ArraySet<String> protectedComponents, ArraySet<String> 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 (file)
index 0000000..5ce4094
--- /dev/null
@@ -0,0 +1,51 @@
+From 06912868733feadee85507d9f40ede2468a9a496 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= <marten.kongstad@sonymobile.com>
+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 (file)
index 0000000..31ebb89
--- /dev/null
@@ -0,0 +1,29 @@
+From ee6f42041d3c77be1151d82898ee5626633cf9fa Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= <marten.kongstad@sonymobile.com>
+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 (file)
index 0000000..9eb37d8
--- /dev/null
@@ -0,0 +1,67 @@
+From a2ce720b5bfca010dc28ab9cd79e9a8d94f981b3 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= <marten.kongstad@sonymobile.com>
+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 (file)
index 0000000..209f9e3
--- /dev/null
@@ -0,0 +1,38 @@
+From b14c245ca8e1b2fa9665ef8cde29eeafaeba282f Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= <marten.kongstad@sonymobile.com>
+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 (file)
index 0000000..fd22568
--- /dev/null
@@ -0,0 +1,45 @@
+From 5e8fc3854ee67ed15983c460b390d0cff1dde775 Mon Sep 17 00:00:00 2001
+From: Josh Guilfoyle <Josh.Guilfoyle@T-Mobile.com>
+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 (file)
index 0000000..16c58d4
--- /dev/null
@@ -0,0 +1,78 @@
+From fc7d694af82449a4d4a3456ffd486a5afa30a2ef Mon Sep 17 00:00:00 2001
+From: Nicholas Chum <nicholaschum@gmail.com>
+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 (file)
index 0000000..abf34be
--- /dev/null
@@ -0,0 +1,29 @@
+From c41b5f7552e5ef1f221d1f755b37501037463c66 Mon Sep 17 00:00:00 2001
+From: Nicholas Chum <nicholaschum@gmail.com>
+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 (file)
index 0000000..c155b92
--- /dev/null
@@ -0,0 +1,470 @@
+From 61a04ac0c542521026b925d35faf4244e6ea2810 Mon Sep 17 00:00:00 2001
+From: Jacob McSwain <jacob.a.mcswain@gmail.com>
+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<String> 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<String> 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<String> list = new ArrayList<>();
+         list.add(targetPackageName);
+-        updateAssets(userId, list);
++        updateSelectedAssets(userId, list);
+     }
+-    private void updateAssets(final int userId, List<String> targetPackageNames) {
++    private void updateSelectedAssets(final int userId, List<String> 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<String> 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<String, List<OverlayInfo>> targetsAndOverlays = mInterface.getAllOverlays(userId);
++            int iterator = 0;
++            int overlaySize = targetsAndOverlays.entrySet().size();
++            for (Entry<String, List<OverlayInfo>> 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 (file)
index 0000000..618f62d
--- /dev/null
@@ -0,0 +1,200 @@
+From 68105cc059781ce4d0948cfe6d34511da24079d2 Mon Sep 17 00:00:00 2001
+From: Dave Kover <dkover@cyngn.com>
+Date: Fri, 9 Dec 2016 10:47:17 -0700
+Subject: [PATCH 12/38] Themes: Expose resolver hardcoded colors
+
+commit dbbd5e70cc65002df41561474b03362022dd6716
+Author: Dave Kover <dkover@cyngn.com>
+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 <thyrus11@gmail.com>
+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 <nicholaschum@gmail.com>
+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" >
+         <TextView android:id="@+id/profile_button"
+                   android:layout_width="wrap_content"
+                   android:layout_height="48dp"
+@@ -74,7 +74,7 @@
+             android:id="@+id/resolver_list"
+             android:clipToPadding="false"
+             android:scrollbarStyle="outsideOverlay"
+-            android:background="@color/white"
++            android:background="@color/resolver_list_bg"
+             android:elevation="8dp"
+             android:listSelector="@color/transparent"
+             android:divider="@null"
+@@ -85,7 +85,7 @@
+               android:layout_width="match_parent"
+               android:layout_height="wrap_content"
+               android:layout_alwaysShow="true"
+-              android:background="@color/white"
++              android:background="@color/resolver_list_bg"
+               android:text="@string/noApplications"
+               android:padding="32dp"
+               android:gravity="center"
+diff --git a/core/res/res/layout/resolver_different_item_header.xml b/core/res/res/layout/resolver_different_item_header.xml
+index 5889136..201c8c6 100644
+--- a/core/res/res/layout/resolver_different_item_header.xml
++++ b/core/res/res/layout/resolver_different_item_header.xml
+@@ -29,6 +29,6 @@
+     android:paddingEnd="16dp"
+     android:paddingTop="8dp"
+     android:paddingBottom="8dp"
+-    android:background="@color/white"
++    android:background="@color/resolver_list_bg"
+     android:elevation="8dp"
+     />
+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">
+         <TextView
+             android:id="@+id/profile_button"
+@@ -69,7 +69,7 @@
+         android:id="@+id/resolver_list"
+         android:clipToPadding="false"
+         android:scrollbarStyle="outsideOverlay"
+-        android:background="@color/white"
++        android:background="@color/resolver_list_bg"
+         android:elevation="8dp"
+         android:nestedScrollingEnabled="true"
+         android:scrollIndicators="top|bottom"
+@@ -78,7 +78,7 @@
+     <TextView android:id="@+id/empty"
+               android:layout_width="match_parent"
+               android:layout_height="wrap_content"
+-              android:background="@color/white"
++              android:background="@color/resolver_list_bg"
+               android:elevation="8dp"
+               android:layout_alwaysShow="true"
+               android:text="@string/noApplications"
+@@ -99,7 +99,7 @@
+         android:orientation="horizontal"
+         android:layoutDirection="locale"
+         android:measureWithLargestChild="true"
+-        android:background="@color/white"
++        android:background="@color/resolver_list_bg"
+         android:paddingTop="8dp"
+         android:paddingBottom="8dp"
+         android:paddingStart="12dp"
+diff --git a/core/res/res/layout/resolver_list_with_default.xml b/core/res/res/layout/resolver_list_with_default.xml
+index 02dc2ed..d3baf68 100644
+--- a/core/res/res/layout/resolver_list_with_default.xml
++++ b/core/res/res/layout/resolver_list_with_default.xml
+@@ -29,7 +29,7 @@
+         android:layout_height="wrap_content"
+         android:layout_alwaysShow="true"
+         android:orientation="vertical"
+-        android:background="@color/white"
++        android:background="@color/resolver_list_bg"
+         android:elevation="8dp">
+         <LinearLayout
+@@ -110,7 +110,7 @@
+             android:paddingBottom="8dp"
+             android:paddingStart="12dp"
+             android:paddingEnd="12dp"
+-            android:background="@color/white"
++            android:background="@color/resolver_list_bg"
+             android:elevation="8dp">
+             <Button
+@@ -150,7 +150,7 @@
+         android:id="@+id/resolver_list"
+         android:clipToPadding="false"
+         android:scrollbarStyle="outsideOverlay"
+-        android:background="@color/white"
++        android:background="@color/resolver_list_bg"
+         android:elevation="8dp"
+         android:nestedScrollingEnabled="true"
+         android:divider="@null" />
+diff --git a/core/res/res/values/projekt_colors.xml b/core/res/res/values/projekt_colors.xml
+new file mode 100644
+index 0000000..c6dbc1c
+--- /dev/null
++++ b/core/res/res/values/projekt_colors.xml
+@@ -0,0 +1,20 @@
++<?xml version="1.0" encoding="utf-8"?>
++<!--
++    Copyright (c) 2016 Projekt Substratum
++
++    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.
++-->
++
++<resources>
++    <color name="resolver_list_bg">@color/white</color>
++</resources>
+-- 
+2.9.3
+
diff --git a/frameworks/base/0013-Themes-Allow-Immersive-cling-colors-to-be-fully-them.patch b/frameworks/base/0013-Themes-Allow-Immersive-cling-colors-to-be-fully-them.patch
new file mode 100644 (file)
index 0000000..a5b2fc9
--- /dev/null
@@ -0,0 +1,81 @@
+From 0e81796b338e5009df8049d0ad4c4b2095f4c807 Mon Sep 17 00:00:00 2001
+From: Nicholas Chum <nicholaschum@gmail.com>
+Date: Tue, 17 Nov 2015 18:57:11 -0500
+Subject: [PATCH 13/38] Themes: Allow Immersive cling colors to be fully themed
+
+This allows the immersive mode help tooltip to be themed completely by
+removing hardcoded framework calls. Let the themer decide what they want
+the colors to be.
+
+Change-Id: Ia0927fda5e44a3ce8ef699cb018bea9b9e7ace62
+---
+ core/res/res/layout/immersive_mode_cling.xml | 12 ++++++------
+ core/res/res/values/projekt_colors.xml       |  3 +++
+ 2 files changed, 9 insertions(+), 6 deletions(-)
+
+diff --git a/core/res/res/layout/immersive_mode_cling.xml b/core/res/res/layout/immersive_mode_cling.xml
+index b08b0f4..6dbde20 100644
+--- a/core/res/res/layout/immersive_mode_cling.xml
++++ b/core/res/res/layout/immersive_mode_cling.xml
+@@ -16,7 +16,7 @@
+ <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+         android:layout_width="match_parent"
+         android:layout_height="wrap_content"
+-        android:background="?android:attr/colorAccent"
++        android:background="@color/immersive_cling_bg_color"
+         android:gravity="center_vertical"
+         android:paddingBottom="24dp">
+@@ -47,7 +47,7 @@
+                 android:paddingTop="8dp"
+                 android:scaleType="center"
+                 android:src="@drawable/ic_expand_more_48dp"
+-                android:tint="?android:attr/colorAccent"/>
++                android:tint="@color/immersive_cling_bg_color"/>
+     </FrameLayout>
+     <TextView
+@@ -59,7 +59,7 @@
+             android:paddingStart="48dp"
+             android:paddingTop="40dp"
+             android:text="@string/immersive_cling_title"
+-            android:textColor="@color/primary_text_default_material_light"
++            android:textColor="@color/immersive_cling_text_color"
+             android:textSize="24sp" />
+     <TextView
+@@ -71,7 +71,7 @@
+             android:paddingStart="48dp"
+             android:paddingTop="12.6dp"
+             android:text="@string/immersive_cling_description"
+-            android:textColor="@color/primary_text_default_material_light"
++            android:textColor="@color/immersive_cling_text_color"
+             android:textSize="16sp" />
+     <Button
+@@ -86,7 +86,7 @@
+             android:paddingEnd="8dp"
+             android:paddingStart="8dp"
+             android:text="@string/immersive_cling_positive"
+-            android:textColor="@android:color/white"
++            android:textColor="@color/immersive_cling_button_text_color"
+             android:textSize="14sp" />
+-</RelativeLayout>
+\ No newline at end of file
++</RelativeLayout>
+diff --git a/core/res/res/values/projekt_colors.xml b/core/res/res/values/projekt_colors.xml
+index c6dbc1c..7408ae9 100644
+--- a/core/res/res/values/projekt_colors.xml
++++ b/core/res/res/values/projekt_colors.xml
+@@ -17,4 +17,7 @@
+ <resources>
+     <color name="resolver_list_bg">@color/white</color>
++    <color name="immersive_cling_bg_color">@color/accent_device_default_light</color>
++    <color name="immersive_cling_text_color">@color/primary_text_default_material_light</color>
++    <color name="immersive_cling_button_text_color">@android:color/white</color>
+ </resources>
+-- 
+2.9.3
+
diff --git a/frameworks/base/0014-Themes-Allow-Permission-Icons-to-be-fully-themed.patch b/frameworks/base/0014-Themes-Allow-Permission-Icons-to-be-fully-themed.patch
new file mode 100644 (file)
index 0000000..64aa7f6
--- /dev/null
@@ -0,0 +1,44 @@
+From 16db866f5fdb07a80c87e733af4952ff2484a4f3 Mon Sep 17 00:00:00 2001
+From: Nicholas Chum <nicholaschum@gmail.com>
+Date: Mon, 23 Nov 2015 23:49:15 -0500
+Subject: [PATCH 14/38] Themes: Allow Permission Icons to be fully themed
+
+This removes the forced @android:color/black tint on the permission
+icons during app sideload through PackageInstaller.
+
+These icons are able to be changed through framework XMLs, but this line
+forces a black tint (invisible on dark themes) on the icons. Let's
+remove this.
+
+Change-Id: I31eb5021a6d297997dbba156f98cbf47f2102b6c
+---
+ core/res/res/layout/app_permission_item.xml | 2 +-
+ core/res/res/values/projekt_colors.xml      | 1 +
+ 2 files changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/core/res/res/layout/app_permission_item.xml b/core/res/res/layout/app_permission_item.xml
+index 383d771..a80d40e 100644
+--- a/core/res/res/layout/app_permission_item.xml
++++ b/core/res/res/layout/app_permission_item.xml
+@@ -32,7 +32,7 @@
+         android:layout_marginStart="16dp"
+         android:layout_marginEnd="8dp"
+         android:scaleType="fitCenter"
+-        android:tint="@android:color/black"/>
++        android:tint="@color/app_permission_icon_tint"/>
+     <ImageView
+         android:layout_width="wrap_content"
+diff --git a/core/res/res/values/projekt_colors.xml b/core/res/res/values/projekt_colors.xml
+index 7408ae9..a7316ab 100644
+--- a/core/res/res/values/projekt_colors.xml
++++ b/core/res/res/values/projekt_colors.xml
+@@ -20,4 +20,5 @@
+     <color name="immersive_cling_bg_color">@color/accent_device_default_light</color>
+     <color name="immersive_cling_text_color">@color/primary_text_default_material_light</color>
+     <color name="immersive_cling_button_text_color">@android:color/white</color>
++    <color name="app_permission_icon_tint">@android:color/black</color>
+ </resources>
+-- 
+2.9.3
+
diff --git a/frameworks/base/0015-Themes-Allow-Navbar-ripple-color-to-be-themed.patch b/frameworks/base/0015-Themes-Allow-Navbar-ripple-color-to-be-themed.patch
new file mode 100644 (file)
index 0000000..52cfce0
--- /dev/null
@@ -0,0 +1,73 @@
+From 91025c7450fc618d35d40d6581172a262d73bd22 Mon Sep 17 00:00:00 2001
+From: Dave Kover <dkover@cyngn.com>
+Date: Thu, 14 Apr 2016 10:19:13 +0700
+Subject: [PATCH 15/38] Themes: Allow Navbar ripple color to be themed
+
+PS1:
+Layers Commit by @setiawanjimmy
+Rewrite of commit by: KreAch3R
+Original commit by: Dave Kover
+Distilled from: https://github.com/CyanogenMod/android_frameworks_base/commit/05ce0a6f5651743add398556d557a5f4c40c2503
+
+Change-Id: I7969e952d7e08f1d12e89291512312421585b70f
+---
+ packages/SystemUI/res/values/projekt_colors.xml     | 21 +++++++++++++++++++++
+ .../systemui/statusbar/policy/KeyButtonRipple.java  |  5 ++++-
+ 2 files changed, 25 insertions(+), 1 deletion(-)
+ create mode 100644 packages/SystemUI/res/values/projekt_colors.xml
+
+diff --git a/packages/SystemUI/res/values/projekt_colors.xml b/packages/SystemUI/res/values/projekt_colors.xml
+new file mode 100644
+index 0000000..a0f1df0
+--- /dev/null
++++ b/packages/SystemUI/res/values/projekt_colors.xml
+@@ -0,0 +1,21 @@
++<?xml version="1.0" encoding="utf-8"?>
++<!--
++    Copyright (c) 2016 Projekt Substratum
++
++    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.
++-->
++
++<resources>
++    <!-- Navigation button ripple color -->
++    <color name="navbutton_ripple_color">#FFFFFFFF</color>
++</resources>
+diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
+index 57e092a..2579579 100644
+--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
++++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonRipple.java
+@@ -65,16 +65,19 @@ public class KeyButtonRipple extends Drawable {
+     private final HashSet<Animator> mRunningAnimations = new HashSet<>();
+     private final ArrayList<Animator> mTmpArray = new ArrayList<>();
++    private int mRippleColor;
++
+     public KeyButtonRipple(Context ctx, View targetView) {
+         mMaxWidth =  ctx.getResources().getDimensionPixelSize(R.dimen.key_button_ripple_max_width);
+         mTargetView = targetView;
++        mRippleColor = ctx.getResources().getColor(R.color.navbutton_ripple_color);
+     }
+     private Paint getRipplePaint() {
+         if (mRipplePaint == null) {
+             mRipplePaint = new Paint();
+             mRipplePaint.setAntiAlias(true);
+-            mRipplePaint.setColor(0xffffffff);
++            mRipplePaint.setColor(mRippleColor);
+         }
+         return mRipplePaint;
+     }
+-- 
+2.9.3
+
diff --git a/frameworks/base/0016-SystemUI-Expose-QS-edit-item-decoration-background-c.patch b/frameworks/base/0016-SystemUI-Expose-QS-edit-item-decoration-background-c.patch
new file mode 100644 (file)
index 0000000..c3ad488
--- /dev/null
@@ -0,0 +1,58 @@
+From 2220bc8b7603b3223e73cd4303151d56eda5948b Mon Sep 17 00:00:00 2001
+From: Ivan Iskandar <iiiiskandar14@gmail.com>
+Date: Sun, 18 Sep 2016 21:33:18 +0700
+Subject: [PATCH 16/38] SystemUI: Expose QS edit item decoration background
+ color
+
+PS2:
+More descriptive color name
+
+@nathanchance edit: updated for 7.1 Nougat
+
+Change-Id: I9867ca26e7d9dacad37d2b70180a98fede0fb0e8
+---
+ packages/SystemUI/res/values/projekt_colors.xml                     | 2 ++
+ .../SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java | 6 ++++--
+ 2 files changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/packages/SystemUI/res/values/projekt_colors.xml b/packages/SystemUI/res/values/projekt_colors.xml
+index a0f1df0..42f4a15 100644
+--- a/packages/SystemUI/res/values/projekt_colors.xml
++++ b/packages/SystemUI/res/values/projekt_colors.xml
+@@ -18,4 +18,6 @@
+ <resources>
+     <!-- Navigation button ripple color -->
+     <color name="navbutton_ripple_color">#FFFFFFFF</color>
++    <!-- QS edit page background color -->
++    <color name="qs_edit_item_decoration_bg">@*android:color/secondary_device_default_settings</color>
+ </resources>
+diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+index 8d7f6ee..c7a1441 100644
+--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
++++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+@@ -471,11 +471,10 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
+         private TileItemDecoration(Context context) {
+             TypedArray ta =
+                     context.obtainStyledAttributes(new int[]{android.R.attr.colorSecondary});
+-            mDrawable = new ColorDrawable(ta.getColor(0, 0));
++            mDrawable = new ColorDrawable();
+             ta.recycle();
+         }
+-
+         @Override
+         public void onDraw(Canvas c, RecyclerView parent, State state) {
+             super.onDraw(c, parent, state);
+@@ -494,6 +493,9 @@ public class TileAdapter extends RecyclerView.Adapter<Holder> implements TileSta
+                         .getLayoutParams();
+                 final int top = child.getTop() + params.topMargin +
+                         Math.round(ViewCompat.getTranslationY(child));
++                // Set drawable color
++                mDrawable.setColor(mContext.getResources().getColor(
++                        R.color.qs_edit_item_decoration_bg));
+                 // Draw full width, in case there aren't tiles all the way across.
+                 mDrawable.setBounds(0, top, width, bottom);
+                 mDrawable.draw(c);
+-- 
+2.9.3
+
diff --git a/frameworks/base/0017-Allow-custom-alpha-for-notification-shade-bg-color.patch b/frameworks/base/0017-Allow-custom-alpha-for-notification-shade-bg-color.patch
new file mode 100644 (file)
index 0000000..2f79692
--- /dev/null
@@ -0,0 +1,64 @@
+From 78783e0ed0a5eb8c3676d152892bf5eebf659317 Mon Sep 17 00:00:00 2001
+From: Simao Gomes Viana <xdevs23@outlook.com>
+Date: Fri, 25 Nov 2016 20:50:29 +0100
+Subject: [PATCH 17/38] Allow custom alpha for notification shade bg color
+
+Change-Id: If621df83d994feae0448a734408ba85ac8329325
+---
+ .../stack/NotificationStackScrollLayout.java       | 40 +++++++++++++++-------
+ 1 file changed, 27 insertions(+), 13 deletions(-)
+
+diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+index a6fe438..5cf0e0a 100644
+--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
++++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+@@ -430,19 +430,33 @@ public class NotificationStackScrollLayout extends ViewGroup
+     }
+     private void updateBackgroundDimming() {
+-        float alpha = BACKGROUND_ALPHA_DIMMED + (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
+-        alpha *= mBackgroundFadeAmount;
+-        // We need to manually blend in the background color
+-        int scrimColor = mScrimController.getScrimBehindColor();
+-        // SRC_OVER blending Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc
+-        float alphaInv = 1 - alpha;
+-        int color = Color.argb((int) (alpha * 255 + alphaInv * Color.alpha(scrimColor)),
+-                (int) (mBackgroundFadeAmount * Color.red(mBgColor)
+-                        + alphaInv * Color.red(scrimColor)),
+-                (int) (mBackgroundFadeAmount * Color.green(mBgColor)
+-                        + alphaInv * Color.green(scrimColor)),
+-                (int) (mBackgroundFadeAmount * Color.blue(mBgColor)
+-                        + alphaInv * Color.blue(scrimColor)));
++        int color;
++        if(Color.alpha(mBgColor) == 255) {
++            float alpha = BACKGROUND_ALPHA_DIMMED +
++                    (1 - BACKGROUND_ALPHA_DIMMED) * (1.0f - mDimAmount);
++            alpha *= mBackgroundFadeAmount;
++            // We need to manually blend in the background color
++            int scrimColor = mScrimController.getScrimBehindColor();
++            // SRC_OVER blending Sa + (1 - Sa)*Da, Rc = Sc + (1 - Sa)*Dc
++            float alphaInv = 1 - alpha;
++            color = Color.argb(
++                    (int) (alpha * 255 + alphaInv * Color.alpha(scrimColor)),
++                    (int) (mBackgroundFadeAmount * Color.red(mBgColor)
++                            + alphaInv * Color.red(scrimColor)),
++                    (int) (mBackgroundFadeAmount * Color.green(mBgColor)
++                            + alphaInv * Color.green(scrimColor)),
++                    (int) (mBackgroundFadeAmount * Color.blue(mBgColor)
++                            + alphaInv * Color.blue(scrimColor)));
++        } else {
++            int a = Color.alpha(mBgColor) +
++                Color.alpha(mScrimController.getScrimBehindColor());
++            if(a > 255) a = 255;
++            else if(a < 0) /* shouldn't happen */ a = 0;
++            color = Color.argb(
++                a,
++                Color.red(mBgColor), Color.green(mBgColor), Color.blue(mBgColor)
++            );
++        }
+         mBackgroundPaint.setColor(color);
+         invalidate();
+     }
+-- 
+2.9.3
+
diff --git a/frameworks/base/0018-Themes-Expose-various-QuickSettings-text-colors.patch b/frameworks/base/0018-Themes-Expose-various-QuickSettings-text-colors.patch
new file mode 100644 (file)
index 0000000..3854ba4
--- /dev/null
@@ -0,0 +1,55 @@
+From c6782421d455174ac3b03e994505947471726194 Mon Sep 17 00:00:00 2001
+From: "Niklas Schnettler (Sh4dowSoul)" <niklas.schnettler@gmail.com>
+Date: Wed, 5 Oct 2016 18:07:43 +0200
+Subject: [PATCH 18/38] Themes: Expose various QuickSettings text colors
+
+Change-Id: Iaea71ca83afbc3d8cc6faea6afac16cabb46cfff
+---
+ packages/SystemUI/res/layout/qs_customize_panel_content.xml | 3 ++-
+ packages/SystemUI/res/layout/status_bar_alarm_group.xml     | 2 +-
+ packages/SystemUI/res/values/projekt_colors.xml             | 4 ++++
+ 3 files changed, 7 insertions(+), 2 deletions(-)
+
+diff --git a/packages/SystemUI/res/layout/qs_customize_panel_content.xml b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
+index 04d0e65..9c23250 100644
+--- a/packages/SystemUI/res/layout/qs_customize_panel_content.xml
++++ b/packages/SystemUI/res/layout/qs_customize_panel_content.xml
+@@ -23,7 +23,8 @@
+         android:layout_height="wrap_content"
+         android:layout_marginTop="28dp"
+         android:navigationContentDescription="@*android:string/action_bar_up_description"
+-        style="?android:attr/toolbarStyle" />
++        style="?android:attr/toolbarStyle"
++        android:titleTextColor="@color/qs_edit_toolbar_text_color"/>
+     <android.support.v7.widget.RecyclerView
+         android:id="@android:id/list"
+diff --git a/packages/SystemUI/res/layout/status_bar_alarm_group.xml b/packages/SystemUI/res/layout/status_bar_alarm_group.xml
+index 1b47d4b..dca8afb 100644
+--- a/packages/SystemUI/res/layout/status_bar_alarm_group.xml
++++ b/packages/SystemUI/res/layout/status_bar_alarm_group.xml
+@@ -73,7 +73,7 @@
+         android:paddingTop="3dp"
+         android:drawablePadding="8dp"
+         android:drawableStart="@drawable/ic_access_alarms_small"
+-        android:textColor="#64ffffff"
++        android:textColor="@color/qs_alarm_status_text_color"
+         android:textAppearance="@style/TextAppearance.StatusBar.Expanded.Date"
+         android:gravity="top"
+         android:background="?android:attr/selectableItemBackgroundBorderless"
+diff --git a/packages/SystemUI/res/values/projekt_colors.xml b/packages/SystemUI/res/values/projekt_colors.xml
+index 42f4a15..0f89199 100644
+--- a/packages/SystemUI/res/values/projekt_colors.xml
++++ b/packages/SystemUI/res/values/projekt_colors.xml
+@@ -20,4 +20,8 @@
+     <color name="navbutton_ripple_color">#FFFFFFFF</color>
+     <!-- QS edit page background color -->
+     <color name="qs_edit_item_decoration_bg">@*android:color/secondary_device_default_settings</color>
++    <!-- QS alarm status text color -->
++    <color name="qs_alarm_status_text_color">#64ffffff</color>
++    <!-- QS edit page toolbar text color -->
++    <color name="qs_edit_toolbar_text_color">#FFFFFF</color>
+ </resources>
+-- 
+2.9.3
+
diff --git a/frameworks/base/0019-Notifications-Expose-a-bool-to-disable-dynamic-color.patch b/frameworks/base/0019-Notifications-Expose-a-bool-to-disable-dynamic-color.patch
new file mode 100644 (file)
index 0000000..68fa9b3
--- /dev/null
@@ -0,0 +1,162 @@
+From 665ebd4404843764b9a98c3fef7a2e6007695d40 Mon Sep 17 00:00:00 2001
+From: Nicholas Chum <nicholaschum@gmail.com>
+Date: Sat, 27 Aug 2016 10:56:46 -0400
+Subject: [PATCH 19/38] Notifications: Expose a bool to disable dynamic colors
+
+This commit allows a themer to overlay a boolean value in config.xml to
+disable dynamic colors applied to the app title and app icon of each
+notification.
+
+PS6:
+Separate app title and small icon colors for more flexibility.
+Expose hardcoded sender text name.
+
+Change-Id: I3c7828118991ec4fc616011caf073c81f75428b4
+---
+ core/java/android/app/Notification.java | 23 ++++++++++++++++++++---
+ core/res/res/values/projekt_colors.xml  |  2 ++
+ core/res/res/values/projekt_config.xml  | 25 +++++++++++++++++++++++++
+ core/res/res/values/projekt_symbols.xml | 25 +++++++++++++++++++++++++
+ 4 files changed, 72 insertions(+), 3 deletions(-)
+ create mode 100644 core/res/res/values/projekt_config.xml
+ create mode 100644 core/res/res/values/projekt_symbols.xml
+
+diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
+index 9f217d4..55029aa 100644
+--- a/core/java/android/app/Notification.java
++++ b/core/java/android/app/Notification.java
+@@ -29,6 +29,7 @@ import android.content.pm.ApplicationInfo;
+ import android.content.pm.PackageManager;
+ import android.content.pm.PackageManager.NameNotFoundException;
+ import android.content.res.ColorStateList;
++import android.content.res.Resources;
+ import android.graphics.Bitmap;
+ import android.graphics.Canvas;
+ import android.graphics.Color;
+@@ -3884,7 +3885,7 @@ public class Notification implements Parcelable
+         private void processSmallIconColor(Icon smallIcon, RemoteViews contentView) {
+             boolean colorable = !isLegacy() || getColorUtil().isGrayscaleIcon(mContext, smallIcon);
+             if (colorable) {
+-                contentView.setDrawableParameters(R.id.icon, false, -1, resolveContrastColor(),
++                contentView.setDrawableParameters(R.id.icon, false, -1, resolveIconContrastColor(),
+                         PorterDuff.Mode.SRC_ATOP, -1);
+             }
+@@ -3901,7 +3902,7 @@ public class Notification implements Parcelable
+             if (largeIcon != null && isLegacy()
+                     && getColorUtil().isGrayscaleIcon(mContext, largeIcon)) {
+                 // resolve color will fall back to the default when legacy
+-                contentView.setDrawableParameters(R.id.icon, false, -1, resolveContrastColor(),
++                contentView.setDrawableParameters(R.id.icon, false, -1, resolveIconContrastColor(),
+                         PorterDuff.Mode.SRC_ATOP, -1);
+             }
+         }
+@@ -3912,7 +3913,23 @@ public class Notification implements Parcelable
+             }
+         }
++        int getSenderTextColor() {
++            return mContext.getColor(R.color.sender_text_color);
++        }
++
++        int resolveIconContrastColor() {
++            if (!Resources.getSystem().getBoolean(R.bool.config_allowNotificationIconTextTinting)) {
++                return mContext.getColor(R.color.notification_icon_default_color);
++            } else {
++                return resolveContrastColor();
++            }
++        }
++
+         int resolveContrastColor() {
++            if (!Resources.getSystem().getBoolean(R.bool.config_allowNotificationIconTextTinting)) {
++                return mContext.getColor(R.color.notification_text_default_color);
++            }
++
+             if (mCachedContrastColorIsFor == mN.color && mCachedContrastColor != COLOR_INVALID) {
+                 return mCachedContrastColor;
+             }
+@@ -4915,7 +4932,7 @@ public class Notification implements Parcelable
+                         0 /* flags */);
+             } else {
+                 sb.append(bidi.unicodeWrap(m.mSender),
+-                        makeFontColorSpan(Color.BLACK),
++                        makeFontColorSpan(mBuilder.getSenderTextColor()),
+                         0 /* flags */);
+             }
+             CharSequence text = m.mText == null ? "" : m.mText;
+diff --git a/core/res/res/values/projekt_colors.xml b/core/res/res/values/projekt_colors.xml
+index a7316ab..d17fbba 100644
+--- a/core/res/res/values/projekt_colors.xml
++++ b/core/res/res/values/projekt_colors.xml
+@@ -21,4 +21,6 @@
+     <color name="immersive_cling_text_color">@color/primary_text_default_material_light</color>
+     <color name="immersive_cling_button_text_color">@android:color/white</color>
+     <color name="app_permission_icon_tint">@android:color/black</color>
++    <color name="notification_text_default_color">@android:color/notification_default_color</color>
++    <color name="sender_text_color">@android:color/black</color>
+ </resources>
+diff --git a/core/res/res/values/projekt_config.xml b/core/res/res/values/projekt_config.xml
+new file mode 100644
+index 0000000..f6eb90a
+--- /dev/null
++++ b/core/res/res/values/projekt_config.xml
+@@ -0,0 +1,25 @@
++<?xml version="1.0" encoding="utf-8"?>
++<!--
++    Copyright (c) 2016 Project Substratum
++
++    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.
++-->
++
++<!-- These resources are around just to allow their values to be customized
++     for different hardware and product builds.  Do not translate. -->
++<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
++
++    <!-- True if the notifications should dynamically tint the app icon and app title -->
++    <bool name="config_allowNotificationIconTextTinting">true</bool>
++
++</resources>
+diff --git a/core/res/res/values/projekt_symbols.xml b/core/res/res/values/projekt_symbols.xml
+new file mode 100644
+index 0000000..f597a5f
+--- /dev/null
++++ b/core/res/res/values/projekt_symbols.xml
+@@ -0,0 +1,25 @@
++<?xml version="1.0" encoding="utf-8"?>
++<!--
++    Copyright (c) 2016 Project Substratum
++
++    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.
++-->
++
++<resources>
++
++  <!-- Notification icon/text dynamic tint -->
++  <java-symbol type="bool" name="config_allowNotificationIconTextTinting" />
++  <java-symbol type="color" name="notification_text_default_color" />
++  <java-symbol type="color" name="sender_text_color" />
++
++</resources>
+-- 
+2.9.3
+
diff --git a/frameworks/base/0020-Notification-dynamic-colors-bool-compatible-with-OMS.patch b/frameworks/base/0020-Notification-dynamic-colors-bool-compatible-with-OMS.patch
new file mode 100644 (file)
index 0000000..6df4637
--- /dev/null
@@ -0,0 +1,48 @@
+From 85f0110784ed2b55e062ca3c35d05480dc78ab8a Mon Sep 17 00:00:00 2001
+From: George G <kreach3r@users.noreply.github.com>
+Date: Mon, 14 Nov 2016 14:49:47 +0200
+Subject: [PATCH 20/38] Notification dynamic colors bool compatible with OMS7
+
+OMS7 introduced this fine piece of code: https://github.com/SubstratumResources/platform_frameworks_base/blob/n-oms7/core/java/android/app/ResourcesManager.java#L897..#L904
+
+// 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.)
+
+Replaced deprecated Resources.getSystem() with compatible method.
+
+Change-Id: I02efe27de3cc7067552964ffbaf079f9e9b5bc3e
+---
+ core/java/android/app/Notification.java | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
+index 55029aa..67c2132 100644
+--- a/core/java/android/app/Notification.java
++++ b/core/java/android/app/Notification.java
+@@ -3918,7 +3918,7 @@ public class Notification implements Parcelable
+         }
+         int resolveIconContrastColor() {
+-            if (!Resources.getSystem().getBoolean(R.bool.config_allowNotificationIconTextTinting)) {
++            if (!mContext.getResources().getBoolean(R.bool.config_allowNotificationIconTextTinting)) {
+                 return mContext.getColor(R.color.notification_icon_default_color);
+             } else {
+                 return resolveContrastColor();
+@@ -3926,7 +3926,7 @@ public class Notification implements Parcelable
+         }
+         int resolveContrastColor() {
+-            if (!Resources.getSystem().getBoolean(R.bool.config_allowNotificationIconTextTinting)) {
++            if (!mContext.getResources().getBoolean(R.bool.config_allowNotificationIconTextTinting)) {
+                 return mContext.getColor(R.color.notification_text_default_color);
+             }
+-- 
+2.9.3
+
diff --git a/frameworks/base/0021-Allow-prevention-of-doze-notification-color-inversio.patch b/frameworks/base/0021-Allow-prevention-of-doze-notification-color-inversio.patch
new file mode 100644 (file)
index 0000000..0014548
--- /dev/null
@@ -0,0 +1,66 @@
+From a5c8355f8999d9c4868bb22018ed8d6d9e25ad48 Mon Sep 17 00:00:00 2001
+From: Daniel Koman <dankoman30@gmail.com>
+Date: Fri, 17 Apr 2015 11:56:28 -0600
+Subject: [PATCH 21/38] Allow prevention of doze notification color inversion
+
+Removed empty newline at the end -- KreAch3R
+Removed slims files for aosp roms -- Bgill55
+
+Change-Id: I2d361c34904f3d168894b8b1741456319fd68456
+---
+ core/res/res/values/projekt_config.xml                           | 5 +++++
+ core/res/res/values/projekt_symbols.xml                          | 3 +++
+ packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java | 4 +++-
+ 3 files changed, 11 insertions(+), 1 deletion(-)
+
+diff --git a/core/res/res/values/projekt_config.xml b/core/res/res/values/projekt_config.xml
+index f6eb90a..5234959 100644
+--- a/core/res/res/values/projekt_config.xml
++++ b/core/res/res/values/projekt_config.xml
+@@ -22,4 +22,9 @@
+     <!-- True if the notifications should dynamically tint the app icon and app title -->
+     <bool name="config_allowNotificationIconTextTinting">true</bool>
++    <!-- Whether doze should invert colors for notifications. If the RRO theme causes
++    the NORMAL notification background to be dark and the text to be light, this boolean
++    needs to be set to false, to prevent the doze notifications from being light -->
++    <bool name="config_invert_colors_on_doze">true</bool>
++
+ </resources>
+diff --git a/core/res/res/values/projekt_symbols.xml b/core/res/res/values/projekt_symbols.xml
+index f597a5f..16a5728 100644
+--- a/core/res/res/values/projekt_symbols.xml
++++ b/core/res/res/values/projekt_symbols.xml
+@@ -22,4 +22,7 @@
+   <java-symbol type="color" name="notification_text_default_color" />
+   <java-symbol type="color" name="sender_text_color" />
++  <!-- Doze invert -->
++  <java-symbol type="bool" name="config_invert_colors_on_doze" />
++
+ </resources>
+diff --git a/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java b/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
+index 2c96e31..65fd115 100644
+--- a/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
++++ b/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
+@@ -20,6 +20,7 @@ import android.animation.Animator;
+ import android.animation.AnimatorListenerAdapter;
+ import android.animation.ValueAnimator;
+ import android.content.Context;
++import android.content.res.Resources;
+ import android.graphics.ColorMatrix;
+ import android.graphics.ColorMatrixColorFilter;
+ import android.graphics.Paint;
+@@ -90,7 +91,8 @@ public class ViewInvertHelper {
+     }
+     public void update(boolean invert) {
+-        if (invert) {
++        if (invert && Resources.getSystem().getBoolean(
++                com.android.internal.R.bool.config_invert_colors_on_doze)) {
+             updateInvertPaint(1f);
+             for (int i = 0; i < mTargets.size(); i++) {
+                 mTargets.get(i).setLayerType(View.LAYER_TYPE_HARDWARE, mDarkPaint);
+-- 
+2.9.3
+
diff --git a/frameworks/base/0022-OMS7-compatible-Ambient-notification-inversion.patch b/frameworks/base/0022-OMS7-compatible-Ambient-notification-inversion.patch
new file mode 100644 (file)
index 0000000..3768431
--- /dev/null
@@ -0,0 +1,64 @@
+From 367e7476f68b80c265bedb92a019cb5fd7c8dfc7 Mon Sep 17 00:00:00 2001
+From: George G <kreach3r@users.noreply.github.com>
+Date: Mon, 14 Nov 2016 14:44:17 +0200
+Subject: [PATCH 22/38] OMS7 compatible 'Ambient notification inversion'
+
+OMS7 introduced this fine piece of code: https://github.com/SubstratumResources/platform_frameworks_base/blob/n-oms7/core/java/android/app/ResourcesManager.java#L897..#L904
+
+// 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.)
+
+Replaced deprecated Resources.getSystem() with compatible method.
+
+Change-Id: I80ad5d037004f0dc63d9eb746c3af05e59a8834e
+---
+ packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java | 6 +++---
+ 1 file changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java b/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
+index 65fd115..605f381 100644
+--- a/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
++++ b/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
+@@ -20,7 +20,6 @@ import android.animation.Animator;
+ import android.animation.AnimatorListenerAdapter;
+ import android.animation.ValueAnimator;
+ import android.content.Context;
+-import android.content.res.Resources;
+ import android.graphics.ColorMatrix;
+ import android.graphics.ColorMatrixColorFilter;
+ import android.graphics.Paint;
+@@ -37,6 +36,7 @@ public class ViewInvertHelper {
+     private final ColorMatrix mMatrix = new ColorMatrix();
+     private final ColorMatrix mGrayscaleMatrix = new ColorMatrix();
+     private final long mFadeDuration;
++    private final boolean mThemeInvert;
+     private final ArrayList<View> mTargets = new ArrayList<>();
+     public ViewInvertHelper(View v, long fadeDuration) {
+@@ -45,6 +45,7 @@ public class ViewInvertHelper {
+     }
+     public ViewInvertHelper(Context context, long fadeDuration) {
+         mFadeDuration = fadeDuration;
++        mThemeInvert = context.getResources().getBoolean(com.android.internal.R.bool.config_invert_colors_on_doze);
+     }
+     private static ArrayList<View> constructArray(View target) {
+@@ -91,8 +92,7 @@ public class ViewInvertHelper {
+     }
+     public void update(boolean invert) {
+-        if (invert && Resources.getSystem().getBoolean(
+-                com.android.internal.R.bool.config_invert_colors_on_doze)) {
++        if (invert && mThemeInvert) {
+             updateInvertPaint(1f);
+             for (int i = 0; i < mTargets.size(); i++) {
+                 mTargets.get(i).setLayerType(View.LAYER_TYPE_HARDWARE, mDarkPaint);
+-- 
+2.9.3
+
diff --git a/frameworks/base/0023-SystemUI-Use-own-drawables-for-QS-expand-icon.patch b/frameworks/base/0023-SystemUI-Use-own-drawables-for-QS-expand-icon.patch
new file mode 100644 (file)
index 0000000..4550eb4
--- /dev/null
@@ -0,0 +1,505 @@
+From 415172d6cb86b8608948ebd8d26d5ac8911cb07c Mon Sep 17 00:00:00 2001
+From: Ivan Iskandar <iiiiskandar14@gmail.com>
+Date: Mon, 5 Dec 2016 19:00:04 +0700
+Subject: [PATCH 23/38] SystemUI: Use own drawables for QS expand icon
+
+This was using the volume panel drawables used also on volume panel.
+So with this commit themers can give different icon for either QS
+and volume panel expand icon.
+
+Change-Id: Ice8d8a520b9b22ba773cceb885e11c8a4bbf6d5f
+---
+ .../anim/ic_qs_collapse_chevron_02_animation.xml   | 25 +++++++++
+ .../anim/ic_qs_collapse_rectangle_1_animation.xml  | 26 +++++++++
+ .../anim/ic_qs_collapse_rectangle_2_animation.xml  | 26 +++++++++
+ .../res/anim/ic_qs_expand_chevron_01_animation.xml | 25 +++++++++
+ .../anim/ic_qs_expand_rectangle_3_animation.xml    | 26 +++++++++
+ .../anim/ic_qs_expand_rectangle_4_animation.xml    | 26 +++++++++
+ packages/SystemUI/res/drawable/ic_qs_collapse.xml  | 62 ++++++++++++++++++++++
+ .../res/drawable/ic_qs_collapse_animation.xml      | 29 ++++++++++
+ packages/SystemUI/res/drawable/ic_qs_expand.xml    | 62 ++++++++++++++++++++++
+ .../res/drawable/ic_qs_expand_animation.xml        | 29 ++++++++++
+ .../ic_qs_collapse_animation_interpolator_0.xml    | 17 ++++++
+ .../ic_qs_expand_animation_interpolator_0.xml      | 17 ++++++
+ .../statusbar/phone/ExpandableIndicator.java       |  8 +--
+ 13 files changed, 374 insertions(+), 4 deletions(-)
+ create mode 100644 packages/SystemUI/res/anim/ic_qs_collapse_chevron_02_animation.xml
+ create mode 100644 packages/SystemUI/res/anim/ic_qs_collapse_rectangle_1_animation.xml
+ create mode 100644 packages/SystemUI/res/anim/ic_qs_collapse_rectangle_2_animation.xml
+ create mode 100644 packages/SystemUI/res/anim/ic_qs_expand_chevron_01_animation.xml
+ create mode 100644 packages/SystemUI/res/anim/ic_qs_expand_rectangle_3_animation.xml
+ create mode 100644 packages/SystemUI/res/anim/ic_qs_expand_rectangle_4_animation.xml
+ create mode 100644 packages/SystemUI/res/drawable/ic_qs_collapse.xml
+ create mode 100644 packages/SystemUI/res/drawable/ic_qs_collapse_animation.xml
+ create mode 100644 packages/SystemUI/res/drawable/ic_qs_expand.xml
+ create mode 100644 packages/SystemUI/res/drawable/ic_qs_expand_animation.xml
+ create mode 100644 packages/SystemUI/res/interpolator/ic_qs_collapse_animation_interpolator_0.xml
+ create mode 100644 packages/SystemUI/res/interpolator/ic_qs_expand_animation_interpolator_0.xml
+
+diff --git a/packages/SystemUI/res/anim/ic_qs_collapse_chevron_02_animation.xml b/packages/SystemUI/res/anim/ic_qs_collapse_chevron_02_animation.xml
+new file mode 100644
+index 0000000..443f2a6
+--- /dev/null
++++ b/packages/SystemUI/res/anim/ic_qs_collapse_chevron_02_animation.xml
+@@ -0,0 +1,25 @@
++<!--
++     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.
++-->
++<set xmlns:android="http://schemas.android.com/apk/res/android" >
++
++    <objectAnimator
++        android:duration="250"
++        android:interpolator="@android:interpolator/fast_out_slow_in"
++        android:pathData="M 12.0,9.0 c 0.0,0.66667 0.0,5.0 0.0,6.0"
++        android:propertyXName="translateX"
++        android:propertyYName="translateY" />
++
++</set>
+\ No newline at end of file
+diff --git a/packages/SystemUI/res/anim/ic_qs_collapse_rectangle_1_animation.xml b/packages/SystemUI/res/anim/ic_qs_collapse_rectangle_1_animation.xml
+new file mode 100644
+index 0000000..b73cdca
+--- /dev/null
++++ b/packages/SystemUI/res/anim/ic_qs_collapse_rectangle_1_animation.xml
+@@ -0,0 +1,26 @@
++<!--
++     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.
++-->
++<set xmlns:android="http://schemas.android.com/apk/res/android" >
++
++    <objectAnimator
++        android:duration="200"
++        android:interpolator="@interpolator/ic_qs_collapse_animation_interpolator_0"
++        android:propertyName="rotation"
++        android:valueFrom="45.0"
++        android:valueTo="-45.0"
++        android:valueType="floatType" />
++
++</set>
+diff --git a/packages/SystemUI/res/anim/ic_qs_collapse_rectangle_2_animation.xml b/packages/SystemUI/res/anim/ic_qs_collapse_rectangle_2_animation.xml
+new file mode 100644
+index 0000000..91c83fc
+--- /dev/null
++++ b/packages/SystemUI/res/anim/ic_qs_collapse_rectangle_2_animation.xml
+@@ -0,0 +1,26 @@
++<!--
++     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.
++-->
++<set xmlns:android="http://schemas.android.com/apk/res/android" >
++
++    <objectAnimator
++        android:duration="200"
++        android:interpolator="@interpolator/ic_qs_collapse_animation_interpolator_0"
++        android:propertyName="rotation"
++        android:valueFrom="-45.0"
++        android:valueTo="45.0"
++        android:valueType="floatType" />
++
++</set>
+diff --git a/packages/SystemUI/res/anim/ic_qs_expand_chevron_01_animation.xml b/packages/SystemUI/res/anim/ic_qs_expand_chevron_01_animation.xml
+new file mode 100644
+index 0000000..e43e645
+--- /dev/null
++++ b/packages/SystemUI/res/anim/ic_qs_expand_chevron_01_animation.xml
+@@ -0,0 +1,25 @@
++<!--
++     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.
++-->
++<set xmlns:android="http://schemas.android.com/apk/res/android" >
++
++    <objectAnimator
++        android:duration="250"
++        android:interpolator="@android:interpolator/fast_out_slow_in"
++        android:pathData="M 12.0,15.0 c 0.0,-1.0 0.0,-5.33333 0.0,-6.0"
++        android:propertyXName="translateX"
++        android:propertyYName="translateY" />
++
++</set>
+\ No newline at end of file
+diff --git a/packages/SystemUI/res/anim/ic_qs_expand_rectangle_3_animation.xml b/packages/SystemUI/res/anim/ic_qs_expand_rectangle_3_animation.xml
+new file mode 100644
+index 0000000..493bdae
+--- /dev/null
++++ b/packages/SystemUI/res/anim/ic_qs_expand_rectangle_3_animation.xml
+@@ -0,0 +1,26 @@
++<!--
++     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.
++-->
++<set xmlns:android="http://schemas.android.com/apk/res/android" >
++
++    <objectAnimator
++        android:duration="200"
++        android:interpolator="@interpolator/ic_qs_expand_animation_interpolator_0"
++        android:propertyName="rotation"
++        android:valueFrom="45.0"
++        android:valueTo="-45.0"
++        android:valueType="floatType" />
++
++</set>
+diff --git a/packages/SystemUI/res/anim/ic_qs_expand_rectangle_4_animation.xml b/packages/SystemUI/res/anim/ic_qs_expand_rectangle_4_animation.xml
+new file mode 100644
+index 0000000..58e485c
+--- /dev/null
++++ b/packages/SystemUI/res/anim/ic_qs_expand_rectangle_4_animation.xml
+@@ -0,0 +1,26 @@
++<!--
++     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.
++-->
++<set xmlns:android="http://schemas.android.com/apk/res/android" >
++
++    <objectAnimator
++        android:duration="200"
++        android:interpolator="@interpolator/ic_qs_expand_animation_interpolator_0"
++        android:propertyName="rotation"
++        android:valueFrom="-45.0"
++        android:valueTo="45.0"
++        android:valueType="floatType" />
++
++</set>
+diff --git a/packages/SystemUI/res/drawable/ic_qs_collapse.xml b/packages/SystemUI/res/drawable/ic_qs_collapse.xml
+new file mode 100644
+index 0000000..bba6b7f
+--- /dev/null
++++ b/packages/SystemUI/res/drawable/ic_qs_collapse.xml
+@@ -0,0 +1,62 @@
++<!--
++     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.
++-->
++<vector xmlns:android="http://schemas.android.com/apk/res/android"
++    android:name="ic_qs_collapse"
++    android:height="24dp"
++    android:viewportHeight="24"
++    android:viewportWidth="24"
++    android:width="24dp" >
++
++    <group
++        android:name="chevron_02"
++        android:rotation="90"
++        android:translateX="12"
++        android:translateY="9" >
++        <group
++            android:name="rectangle_2"
++            android:rotation="-45" >
++            <group
++                android:name="rectangle_2_pivot"
++                android:translateY="4" >
++                <group
++                    android:name="rectangle_path_2_position"
++                    android:translateY="-1" >
++                    <path
++                        android:name="rectangle_path_2"
++                        android:fillColor="#FFFFFFFF"
++                        android:pathData="M -1.0,-4.0 l 2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
++                </group>
++            </group>
++        </group>
++        <group
++            android:name="rectangle_1"
++            android:rotation="45" >
++            <group
++                android:name="rectangle_1_pivot"
++                android:translateY="-4" >
++                <group
++                    android:name="rectangle_path_1_position"
++                    android:translateY="1" >
++                    <path
++                        android:name="rectangle_path_1"
++                        android:fillColor="#FFFFFFFF"
++                        android:pathData="M -1.0,-4.0 l 2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
++                </group>
++            </group>
++        </group>
++    </group>
++
++</vector>
+diff --git a/packages/SystemUI/res/drawable/ic_qs_collapse_animation.xml b/packages/SystemUI/res/drawable/ic_qs_collapse_animation.xml
+new file mode 100644
+index 0000000..8138b2e
+--- /dev/null
++++ b/packages/SystemUI/res/drawable/ic_qs_collapse_animation.xml
+@@ -0,0 +1,29 @@
++<!--
++     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.
++-->
++<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
++    android:drawable="@drawable/ic_qs_collapse" >
++
++    <target
++        android:name="chevron_02"
++        android:animation="@anim/ic_qs_collapse_chevron_02_animation" />
++    <target
++        android:name="rectangle_2"
++        android:animation="@anim/ic_qs_collapse_rectangle_2_animation" />
++    <target
++        android:name="rectangle_1"
++        android:animation="@anim/ic_qs_collapse_rectangle_1_animation" />
++
++</animated-vector>
+diff --git a/packages/SystemUI/res/drawable/ic_qs_expand.xml b/packages/SystemUI/res/drawable/ic_qs_expand.xml
+new file mode 100644
+index 0000000..bb22064
+--- /dev/null
++++ b/packages/SystemUI/res/drawable/ic_qs_expand.xml
+@@ -0,0 +1,62 @@
++<!--
++     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.
++-->
++<vector xmlns:android="http://schemas.android.com/apk/res/android"
++    android:name="ic_qs_expand"
++    android:height="24dp"
++    android:viewportHeight="24"
++    android:viewportWidth="24"
++    android:width="24dp" >
++
++    <group
++        android:name="chevron_01"
++        android:rotation="90"
++        android:translateX="12"
++        android:translateY="15" >
++        <group
++            android:name="rectangle_3"
++            android:rotation="45" >
++            <group
++                android:name="rectangle_2_pivot_0"
++                android:translateY="4" >
++                <group
++                    android:name="rectangle_path_3_position"
++                    android:translateY="-1" >
++                    <path
++                        android:name="rectangle_path_3"
++                        android:fillColor="#FFFFFFFF"
++                        android:pathData="M -1.0,-4.0 l 2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
++                </group>
++            </group>
++        </group>
++        <group
++            android:name="rectangle_4"
++            android:rotation="-45" >
++            <group
++                android:name="rectangle_1_pivot_0"
++                android:translateY="-4" >
++                <group
++                    android:name="rectangle_path_4_position"
++                    android:translateY="1" >
++                    <path
++                        android:name="rectangle_path_4"
++                        android:fillColor="#FFFFFFFF"
++                        android:pathData="M -1.0,-4.0 l 2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l -2.0,0.0 c 0.0,0.0 0.0,0.0 0.0,0.0 l 0.0,-8.0 c 0.0,0.0 0.0,0.0 0.0,0.0 Z" />
++                </group>
++            </group>
++        </group>
++    </group>
++
++</vector>
+diff --git a/packages/SystemUI/res/drawable/ic_qs_expand_animation.xml b/packages/SystemUI/res/drawable/ic_qs_expand_animation.xml
+new file mode 100644
+index 0000000..b561ee0
+--- /dev/null
++++ b/packages/SystemUI/res/drawable/ic_qs_expand_animation.xml
+@@ -0,0 +1,29 @@
++<!--
++     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.
++-->
++<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
++    android:drawable="@drawable/ic_qs_expand" >
++
++    <target
++        android:name="chevron_01"
++        android:animation="@anim/ic_qs_expand_chevron_01_animation" />
++    <target
++        android:name="rectangle_3"
++        android:animation="@anim/ic_qs_expand_rectangle_3_animation" />
++    <target
++        android:name="rectangle_4"
++        android:animation="@anim/ic_qs_expand_rectangle_4_animation" />
++
++</animated-vector>
+diff --git a/packages/SystemUI/res/interpolator/ic_qs_collapse_animation_interpolator_0.xml b/packages/SystemUI/res/interpolator/ic_qs_collapse_animation_interpolator_0.xml
+new file mode 100644
+index 0000000..c3930e4
+--- /dev/null
++++ b/packages/SystemUI/res/interpolator/ic_qs_collapse_animation_interpolator_0.xml
+@@ -0,0 +1,17 @@
++<!--
++     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.
++-->
++<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
++    android:pathData="M 0.0,0.0 c 0.0001,0.0 0.0,1.0 1.0,1.0" />
+diff --git a/packages/SystemUI/res/interpolator/ic_qs_expand_animation_interpolator_0.xml b/packages/SystemUI/res/interpolator/ic_qs_expand_animation_interpolator_0.xml
+new file mode 100644
+index 0000000..c3930e4
+--- /dev/null
++++ b/packages/SystemUI/res/interpolator/ic_qs_expand_animation_interpolator_0.xml
+@@ -0,0 +1,17 @@
++<!--
++     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.
++-->
++<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
++    android:pathData="M 0.0,0.0 c 0.0001,0.0 0.0,1.0 1.0,1.0" />
+diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandableIndicator.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandableIndicator.java
+index a295cfa..0f04c28 100644
+--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandableIndicator.java
++++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ExpandableIndicator.java
+@@ -57,11 +57,11 @@ public class ExpandableIndicator extends ImageView {
+     private int getDrawableResourceId(boolean expanded) {
+         if (mIsDefaultDirection) {
+-            return expanded ? R.drawable.ic_volume_collapse_animation
+-                    : R.drawable.ic_volume_expand_animation;
++            return expanded ? R.drawable.ic_qs_collapse_animation
++                    : R.drawable.ic_qs_expand_animation;
+         } else {
+-            return expanded ? R.drawable.ic_volume_expand_animation
+-                    : R.drawable.ic_volume_collapse_animation;
++            return expanded ? R.drawable.ic_qs_expand_animation
++                    : R.drawable.ic_qs_collapse_animation;
+         }
+     }
+-- 
+2.9.3
+
diff --git a/frameworks/base/0024-N-Extras-Add-dynamic-theme-BootAnimation-support.patch b/frameworks/base/0024-N-Extras-Add-dynamic-theme-BootAnimation-support.patch
new file mode 100644 (file)
index 0000000..88df6a0
--- /dev/null
@@ -0,0 +1,81 @@
+From 429a491bac8c8e3966f9afa5cc80becdb4677f9e Mon Sep 17 00:00:00 2001
+From: 0xD34D <clark@scheffsblend.com>
+Date: Mon, 9 Jan 2017 07:19:41 +0530
+Subject: [PATCH 24/38] N-Extras: Add dynamic theme BootAnimation support
+
+Extracted from "Themes: Port to CM13 [1/3]"
+http://review.cyanogenmod.org/#/c/113273/14
+
+Change-Id: I394897c10f02695f0416e87e9bf960e840bcb3b7
+---
+ cmds/bootanimation/BootAnimation.cpp | 13 ++++++++++---
+ cmds/bootanimation/BootAnimation.h   |  3 ++-
+ 2 files changed, 12 insertions(+), 4 deletions(-)
+
+diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp
+index c6b2f63..f374ece 100644
+--- a/cmds/bootanimation/BootAnimation.cpp
++++ b/cmds/bootanimation/BootAnimation.cpp
+@@ -68,6 +68,7 @@ namespace android {
+ static const char OEM_BOOTANIMATION_FILE[] = "/oem/media/bootanimation.zip";
+ static const char SYSTEM_BOOTANIMATION_FILE[] = "/system/media/bootanimation.zip";
+ static const char SYSTEM_ENCRYPTED_BOOTANIMATION_FILE[] = "/system/media/bootanimation-encrypted.zip";
++static const char THEME_BOOTANIMATION_FILE[] = "/data/system/theme/bootanimation.zip";
+ static const char SYSTEM_DATA_DIR_PATH[] = "/data/system";
+ static const char SYSTEM_TIME_DIR_NAME[] = "time";
+ static const char SYSTEM_TIME_DIR_PATH[] = "/data/system/time";
+@@ -317,13 +318,14 @@ status_t BootAnimation::initTexture(SkBitmap *bitmap)
+ // Get bootup Animation File
+-// Parameter: ImageID: IMG_OEM IMG_SYS IMG_ENC
++// Parameter: ImageID: IMG_OEM IMG_SYS IMG_ENC IMG_THM
+ // Return Value : File path
+ const char *BootAnimation::getAnimationFileName(ImageID image)
+ {
+-    const char *fileName[3] = { OEM_BOOTANIMATION_FILE,
++    const char *fileName[4] = { OEM_BOOTANIMATION_FILE,
+             SYSTEM_BOOTANIMATION_FILE,
+-            SYSTEM_ENCRYPTED_BOOTANIMATION_FILE };
++            SYSTEM_ENCRYPTED_BOOTANIMATION_FILE,
++            THEME_BOOTANIMATION_FILE };
+     // Load animations of Carrier through regionalization environment
+     if (Environment::isSupported()) {
+@@ -408,6 +410,9 @@ status_t BootAnimation::readyToRun() {
+     if (encryptedAnimation && (access(getAnimationFileName(IMG_ENC), R_OK) == 0)) {
+         mZipFileName = getAnimationFileName(IMG_ENC);
+     }
++    else if (access(getAnimationFileName(IMG_THM), R_OK) == 0) {
++        mZipFileName = getAnimationFileName(IMG_THM);
++    }
+     else if (access(getAnimationFileName(IMG_OEM), R_OK) == 0) {
+         mZipFileName = getAnimationFileName(IMG_OEM);
+     }
+@@ -421,6 +426,8 @@ status_t BootAnimation::readyToRun() {
+     FILE* fd;
+     if (encryptedAnimation && access(getAnimationFileName(IMG_ENC), R_OK) == 0)
+         fd = fopen(getAnimationFileName(IMG_ENC), "r");
++    else if (access(getAnimationFileName(IMG_THM), R_OK) == 0)
++        fd = fopen(getAnimationFileName(IMG_THM), "r");
+     else if (access(getAnimationFileName(IMG_OEM), R_OK) == 0)
+         fd = fopen(getAnimationFileName(IMG_OEM), "r");
+     else if (access(getAnimationFileName(IMG_SYS), R_OK) == 0)
+diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h
+index b70cc0e..958a022 100644
+--- a/cmds/bootanimation/BootAnimation.h
++++ b/cmds/bootanimation/BootAnimation.h
+@@ -122,8 +122,9 @@ private:
+      *IMG_OEM: bootanimation file from oem/media
+      *IMG_SYS: bootanimation file from system/media
+      *IMG_ENC: encrypted bootanimation file from system/media
++     *IMG_THM: bootanimation file from data/system/theme
+      */
+-    enum ImageID { IMG_OEM = 0, IMG_SYS = 1, IMG_ENC = 2 };
++    enum ImageID { IMG_OEM = 0, IMG_SYS = 1, IMG_ENC = 2, IMG_THM = 3 };
+     const char *getAnimationFileName(ImageID image);
+     status_t initTexture(Texture* texture, AssetManager& asset, const char* name);
+     status_t initTexture(const Animation::Frame& frame);
+-- 
+2.9.3
+
diff --git a/frameworks/base/0025-N-Extras-Add-dynamic-theme-fonts-support.patch b/frameworks/base/0025-N-Extras-Add-dynamic-theme-fonts-support.patch
new file mode 100644 (file)
index 0000000..9eb6494
--- /dev/null
@@ -0,0 +1,600 @@
+From fd877629e3a2d8bfdafb1ba1371237396ba176e1 Mon Sep 17 00:00:00 2001
+From: 0xD34D <clark@scheffsblend.com>
+Date: Wed, 22 Jun 2016 23:54:23 +0300
+Subject: [PATCH 25/38] N-Extras: Add dynamic theme fonts support
+
+Due to the nature of the removal of assetSeq in OMS7+, we now use the
+more controllable font scale updating code to update the fonts on
+demand.
+
+Extracted from Themes: Port to CM13 [1/3]
+http://review.cyanogenmod.org/#/c/113273/14
+
+Squashed:
+
+Small adjustment to Font commit
+Author camcory
+https://github.com/SubstratumResources/platform_frameworks_base/commit/a13f088dff70bc52f2053f32acff47a7a377a807
+
+Themes: Ensure themed fonts always have fallbacks
+Author 0xD34D
+https://github.com/CyanogenMod/android_frameworks_base/commit/18b301874e2a658eb01f97defd70da038521f450
+
+Themes: Let garbage collector free up native instances
+Author 0xD34D
+https://github.com/CyanogenMod/android_frameworks_base/commit/b7108ea9ce7ad2226aa6340046d24e069c6e8e21
+
+Themes: Make parse() method in FontListParser public
+Author 0xD34D
+https://github.com/CyanogenMod/android_frameworks_base/commit/b3ae4609f2754fd156e34dfbf39551041e976031
+
+Fonts: add sans-serif fallback fonts first
+Author 0xD34D
+https://github.com/CyanogenMod/android_frameworks_base/commit/f1d7b86dd267ed5b59e51339edc4553d37561a39
+
+Themes: Add config change flag for font change
+Author 0xD34D
+https://github.com/CyanogenMod/android_frameworks_base/commit/2ec1a33b70d3c013daa956696b68167a5eeef70d
+
+Themes: don't recreateDefaults on typeface when locale changes
+Author romanbb
+https://github.com/CyanogenMod/android_frameworks_base/commit/e05ffea4ea55a4eb6b40436a864a570509eb33ac
+
+Change-Id: I1f61bd269b42ab6145482a51d25fe5b1b5308f94
+---
+ core/java/android/app/ActivityThread.java          |   7 +-
+ core/java/android/content/pm/ActivityInfo.java     |   5 +
+ core/java/android/content/res/Configuration.java   |   5 +-
+ core/java/android/os/Process.java                  |   7 +-
+ .../com/android/internal/os/ZygoteConnection.java  |  10 ++
+ graphics/java/android/graphics/FontListParser.java |  48 ++++--
+ graphics/java/android/graphics/Typeface.java       | 174 +++++++++++++++++++--
+ .../android/server/am/ActivityManagerService.java  |  11 +-
+ 8 files changed, 240 insertions(+), 27 deletions(-)
+
+diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
+index 55fc25d..44a900b 100644
+--- a/core/java/android/app/ActivityThread.java
++++ b/core/java/android/app/ActivityThread.java
+@@ -48,6 +48,7 @@ import android.database.sqlite.SQLiteDebug;
+ import android.database.sqlite.SQLiteDebug.DbStats;
+ import android.graphics.Bitmap;
+ import android.graphics.Canvas;
++import android.graphics.Typeface;
+ import android.hardware.display.DisplayManagerGlobal;
+ import android.net.ConnectivityManager;
+ import android.net.IConnectivityManager;
+@@ -4852,8 +4853,12 @@ public final class ActivityThread {
+         if (configDiff != 0) {
+             // Ask text layout engine to free its caches if there is a locale change
+             boolean hasLocaleConfigChange = ((configDiff & ActivityInfo.CONFIG_LOCALE) != 0);
+-            if (hasLocaleConfigChange) {
++            boolean hasFontConfigChange = ((configDiff & ActivityInfo.CONFIG_THEME_FONT) != 0);
++            if (hasLocaleConfigChange || hasFontConfigChange) {
+                 Canvas.freeTextLayoutCaches();
++                if (hasFontConfigChange) {
++                    Typeface.recreateDefaults();
++                }
+                 if (DEBUG_CONFIGURATION) Slog.v(TAG, "Cleared TextLayout Caches");
+             }
+         }
+diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
+index 5a09c00..42febcf 100644
+--- a/core/java/android/content/pm/ActivityInfo.java
++++ b/core/java/android/content/pm/ActivityInfo.java
+@@ -624,6 +624,11 @@ public class ActivityInfo extends ComponentInfo
+      */
+     public static final int CONFIG_LAYOUT_DIRECTION = 0x2000;
+     /**
++     * Bit in {@link #configChanges} that indicates a font change occurred
++     * @hide
++     */
++    public static final int CONFIG_THEME_FONT = 0x200000;
++    /**
+      * Bit in {@link #configChanges} that indicates that the activity
+      * can itself handle changes to the font scaling factor.  Set from the
+      * {@link android.R.attr#configChanges} attribute.  This is
+diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
+index b2d518c..2f7c3ec 100644
+--- a/core/java/android/content/res/Configuration.java
++++ b/core/java/android/content/res/Configuration.java
+@@ -955,6 +955,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
+         int changed = 0;
+         if (delta.fontScale > 0 && fontScale != delta.fontScale) {
+             changed |= ActivityInfo.CONFIG_FONT_SCALE;
++            changed |= ActivityInfo.CONFIG_THEME_FONT;
+             fontScale = delta.fontScale;
+         }
+         if (delta.mcc != 0 && mcc != delta.mcc) {
+@@ -1121,6 +1122,7 @@ public final class Configuration implements Parcelable, Comparable<Configuration
+         int changed = 0;
+         if (delta.fontScale > 0 && fontScale != delta.fontScale) {
+             changed |= ActivityInfo.CONFIG_FONT_SCALE;
++            changed |= ActivityInfo.CONFIG_THEME_FONT;
+         }
+         if (delta.mcc != 0 && mcc != delta.mcc) {
+             changed |= ActivityInfo.CONFIG_MCC;
+@@ -1211,7 +1213,8 @@ public final class Configuration implements Parcelable, Comparable<Configuration
+      */
+     public static boolean needNewResources(@Config int configChanges,
+             @Config int interestingChanges) {
+-        return (configChanges & (interestingChanges|ActivityInfo.CONFIG_FONT_SCALE)) != 0;
++        return (configChanges & (interestingChanges|ActivityInfo.CONFIG_FONT_SCALE|
++                    ActivityInfo.CONFIG_THEME_FONT)) != 0;
+     }
+     /**
+diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
+index e1b7fda..6dcd045 100644
+--- a/core/java/android/os/Process.java
++++ b/core/java/android/os/Process.java
+@@ -516,11 +516,12 @@ public class Process {
+                                   String abi,
+                                   String instructionSet,
+                                   String appDataDir,
++                                  boolean refreshTheme,
+                                   String[] zygoteArgs) {
+         try {
+             return startViaZygote(processClass, niceName, uid, gid, gids,
+                     debugFlags, mountExternal, targetSdkVersion, seInfo,
+-                    abi, instructionSet, appDataDir, zygoteArgs);
++                    abi, instructionSet, appDataDir, refreshTheme, zygoteArgs);
+         } catch (ZygoteStartFailedEx ex) {
+             Log.e(LOG_TAG,
+                     "Starting VM process through Zygote failed");
+@@ -648,6 +649,7 @@ public class Process {
+                                   String abi,
+                                   String instructionSet,
+                                   String appDataDir,
++                                  boolean refreshTheme,
+                                   String[] extraArgs)
+                                   throws ZygoteStartFailedEx {
+         synchronized(Process.class) {
+@@ -689,6 +691,9 @@ public class Process {
+             } else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
+                 argsForZygote.add("--mount-external-write");
+             }
++            if (refreshTheme) {
++                argsForZygote.add("--refresh_theme");
++            }
+             argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
+             //TODO optionally enable debuger
+diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
+index 85d84bb..81257f0 100644
+--- a/core/java/com/android/internal/os/ZygoteConnection.java
++++ b/core/java/com/android/internal/os/ZygoteConnection.java
+@@ -22,6 +22,7 @@ import static android.system.OsConstants.STDERR_FILENO;
+ import static android.system.OsConstants.STDIN_FILENO;
+ import static android.system.OsConstants.STDOUT_FILENO;
++import android.graphics.Typeface;
+ import android.net.Credentials;
+ import android.net.LocalSocket;
+ import android.os.Process;
+@@ -194,6 +195,10 @@ class ZygoteConnection {
+                 Os.fcntlInt(childPipeFd, F_SETFD, 0);
+             }
++            if (parsedArgs.refreshTheme) {
++                Typeface.recreateDefaults();
++            }
++
+             /**
+              * In order to avoid leaking descriptors to the Zygote child,
+              * the native code must close the two Zygote socket descriptors
+@@ -373,6 +378,9 @@ class ZygoteConnection {
+          */
+         String appDataDir;
++        /** from --refresh_theme */
++        boolean refreshTheme;
++
+         /**
+          * Constructs instance and parses args
+          * @param args zygote command-line args
+@@ -531,6 +539,8 @@ class ZygoteConnection {
+                     instructionSet = arg.substring(arg.indexOf('=') + 1);
+                 } else if (arg.startsWith("--app-data-dir=")) {
+                     appDataDir = arg.substring(arg.indexOf('=') + 1);
++                } else if (arg.equals("--refresh_theme")) {
++                    refreshTheme = true;
+                 } else {
+                     break;
+                 }
+diff --git a/graphics/java/android/graphics/FontListParser.java b/graphics/java/android/graphics/FontListParser.java
+index 7871aa8..f4590c9 100644
+--- a/graphics/java/android/graphics/FontListParser.java
++++ b/graphics/java/android/graphics/FontListParser.java
+@@ -21,6 +21,9 @@ import android.util.Xml;
+ import org.xmlpull.v1.XmlPullParser;
+ import org.xmlpull.v1.XmlPullParserException;
++import java.io.BufferedInputStream;
++import java.io.File;
++import java.io.FileInputStream;
+ import java.io.IOException;
+ import java.io.InputStream;
+ import java.util.ArrayList;
+@@ -88,18 +91,41 @@ public class FontListParser {
+     }
+     /* Parse fallback list (no names) */
+-    public static Config parse(InputStream in) throws XmlPullParserException, IOException {
++    public static Config parse(File configFilename, String fontDir)
++            throws XmlPullParserException, IOException {
++        FileInputStream in = null;
++        in = new FileInputStream(configFilename);
++        return FontListParser.parse(in, fontDir);
++    }
++
++    /* Parse fallback list (no names) */
++    public static Config parse(InputStream in, String fontDir)
++            throws XmlPullParserException, IOException {
++        BufferedInputStream bis = null;
+         try {
++            // wrap input stream in a BufferedInputStream, if it's not already, for mark support
++            if (!(in instanceof BufferedInputStream)) {
++                bis = new BufferedInputStream(in);
++            } else {
++                bis = (BufferedInputStream) in;
++            }
++            // mark the beginning so we can reset to this position after checking format
++            bis.mark(in.available());
++            return parseNormalFormat(bis, fontDir);
++        } finally {
++            if (bis != null) bis.close();
++        }
++    }
++
++    public static Config parseNormalFormat(InputStream in, String dirName)
++            throws XmlPullParserException, IOException {
+             XmlPullParser parser = Xml.newPullParser();
+             parser.setInput(in, null);
+             parser.nextTag();
+-            return readFamilies(parser);
+-        } finally {
+-            in.close();
+-        }
++            return readFamilies(parser, dirName);
+     }
+-    private static Config readFamilies(XmlPullParser parser)
++    private static Config readFamilies(XmlPullParser parser, String dirPath)
+             throws XmlPullParserException, IOException {
+         Config config = new Config();
+         parser.require(XmlPullParser.START_TAG, null, "familyset");
+@@ -107,7 +133,7 @@ public class FontListParser {
+             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
+             String tag = parser.getName();
+             if (tag.equals("family")) {
+-                config.families.add(readFamily(parser));
++                config.families.add(readFamily(parser, dirPath));
+             } else if (tag.equals("alias")) {
+                 config.aliases.add(readAlias(parser));
+             } else {
+@@ -117,7 +143,7 @@ public class FontListParser {
+         return config;
+     }
+-    private static Family readFamily(XmlPullParser parser)
++    private static Family readFamily(XmlPullParser parser, String dirPath)
+             throws XmlPullParserException, IOException {
+         String name = parser.getAttributeValue(null, "name");
+         String lang = parser.getAttributeValue(null, "lang");
+@@ -127,7 +153,7 @@ public class FontListParser {
+             if (parser.getEventType() != XmlPullParser.START_TAG) continue;
+             String tag = parser.getName();
+             if (tag.equals("font")) {
+-                fonts.add(readFont(parser));
++                fonts.add(readFont(parser, dirPath));
+             } else {
+                 skip(parser);
+             }
+@@ -139,7 +165,7 @@ public class FontListParser {
+     private static final Pattern FILENAME_WHITESPACE_PATTERN =
+             Pattern.compile("^[ \\n\\r\\t]+|[ \\n\\r\\t]+$");
+-    private static Font readFont(XmlPullParser parser)
++    private static Font readFont(XmlPullParser parser, String dirPath)
+             throws XmlPullParserException, IOException {
+         String indexStr = parser.getAttributeValue(null, "index");
+         int index = indexStr == null ? 0 : Integer.parseInt(indexStr);
+@@ -160,7 +186,7 @@ public class FontListParser {
+                 skip(parser);
+             }
+         }
+-        String fullFilename = "/system/fonts/" +
++        String fullFilename = dirPath + File.separatorChar +
+                 FILENAME_WHITESPACE_PATTERN.matcher(filename).replaceAll("");
+         return new Font(fullFilename, index, axes, weight, isItalic);
+     }
+diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
+index 2886f0d..990c9bd 100644
+--- a/graphics/java/android/graphics/Typeface.java
++++ b/graphics/java/android/graphics/Typeface.java
+@@ -17,6 +17,7 @@
+ package android.graphics;
+ import android.content.res.AssetManager;
++import android.graphics.FontListParser.Family;
+ import android.util.Log;
+ import android.util.LongSparseArray;
+ import android.util.LruCache;
+@@ -75,6 +76,8 @@ public class Typeface {
+     static final String FONTS_CONFIG = "fonts.xml";
++    static final String SANS_SERIF_FAMILY_NAME = "sans-serif";
++
+     /**
+      * @hide
+      */
+@@ -88,6 +91,13 @@ public class Typeface {
+     private int mStyle = 0;
++    // Typefaces that we can garbage collect when changing fonts, and so we don't break public APIs
++    private static Typeface DEFAULT_INTERNAL;
++    private static Typeface DEFAULT_BOLD_INTERNAL;
++    private static Typeface SANS_SERIF_INTERNAL;
++    private static Typeface SERIF_INTERNAL;
++    private static Typeface MONOSPACE_INTERNAL;
++
+     private static void setDefault(Typeface t) {
+         sDefaultTypeface = t;
+         nativeSetDefault(t.native_instance);
+@@ -263,7 +273,10 @@ public class Typeface {
+         for (int i = 0; i < families.length; i++) {
+             ptrArray[i] = families[i].mNativePtr;
+         }
+-        return new Typeface(nativeCreateFromArray(ptrArray));
++
++
++        Typeface typeface = new Typeface(nativeCreateFromArray(ptrArray));
++        return typeface;
+     }
+     /**
+@@ -318,6 +331,73 @@ public class Typeface {
+         return fontFamily;
+     }
++    /**
++     * Adds the family from src with the name familyName as a fallback font in dst
++     * @param src Source font config
++     * @param dst Destination font config
++     * @param familyName Name of family to add as a fallback
++     */
++    private static void addFallbackFontsForFamilyName(FontListParser.Config src,
++            FontListParser.Config dst, String familyName) {
++        for (Family srcFamily : src.families) {
++            if (familyName.equals(srcFamily.name)) {
++                // set the name to null so that it will be added as a fallback
++                srcFamily.name = null;
++                dst.families.add(srcFamily);
++                return;
++            }
++        }
++    }
++
++    /**
++     * Adds any font families in src that do not exist in dst
++     * @param src Source font config
++     * @param dst Destination font config
++     */
++    private static void addMissingFontFamilies(FontListParser.Config src,
++            FontListParser.Config dst) {
++        final int N = dst.families.size();
++        // add missing families
++        for (Family srcFamily : src.families) {
++            boolean addFamily = true;
++            for (int i = 0; i < N && addFamily; i++) {
++                final Family dstFamily = dst.families.get(i);
++                final String dstFamilyName = dstFamily.name;
++                if (dstFamilyName != null && dstFamilyName.equals(srcFamily.name)) {
++                    addFamily = false;
++                    break;
++                }
++            }
++            if (addFamily) {
++                dst.families.add(srcFamily);
++            }
++        }
++    }
++
++    /**
++     * Adds any aliases in src that do not exist in dst
++     * @param src Source font config
++     * @param dst Destination font config
++     */
++    private static void addMissingFontAliases(FontListParser.Config src,
++            FontListParser.Config dst) {
++        final int N = dst.aliases.size();
++        // add missing aliases
++        for (FontListParser.Alias alias : src.aliases) {
++            boolean addAlias = true;
++            for (int i = 0; i < N && addAlias; i++) {
++                final String dstAliasName = dst.aliases.get(i).name;
++                if (dstAliasName != null && dstAliasName.equals(alias.name)) {
++                    addAlias = false;
++                    break;
++                }
++            }
++            if (addAlias) {
++                dst.aliases.add(alias);
++            }
++        }
++    }
++
+     /*
+      * (non-Javadoc)
+      *
+@@ -326,10 +406,36 @@ public class Typeface {
+     private static void init() {
+         // Load font config and initialize Minikin state
+         File systemFontConfigLocation = getSystemFontConfigLocation();
+-        File configFilename = new File(systemFontConfigLocation, FONTS_CONFIG);
++        File themeFontConfigLocation = getThemeFontConfigLocation();
++
++        File systemConfigFile = new File(systemFontConfigLocation, FONTS_CONFIG);
++        File themeConfigFile = new File(themeFontConfigLocation, FONTS_CONFIG);
++        File configFile = null;
++        File fontDir;
++
++        if (themeConfigFile.exists()) {
++            configFile = themeConfigFile;
++            fontDir = getThemeFontDirLocation();
++        } else {
++            configFile = systemConfigFile;
++            fontDir = getSystemFontDirLocation();
++        }
++
+         try {
+-            FileInputStream fontsIn = new FileInputStream(configFilename);
+-            FontListParser.Config fontConfig = FontListParser.parse(fontsIn);
++            FontListParser.Config fontConfig = FontListParser.parse(configFile,
++                    fontDir.getAbsolutePath());
++            FontListParser.Config systemFontConfig = null;
++
++            // If the fonts are coming from a theme, we will need to make sure that we include
++            // any font families from the system fonts that the theme did not include.
++            // NOTE: All the system font families without names ALWAYS get added.
++            if (configFile == themeConfigFile) {
++                systemFontConfig = FontListParser.parse(systemConfigFile,
++                        getSystemFontDirLocation().getAbsolutePath());
++                addFallbackFontsForFamilyName(systemFontConfig, fontConfig, SANS_SERIF_FAMILY_NAME);
++                addMissingFontFamilies(systemFontConfig, fontConfig);
++                addMissingFontAliases(systemFontConfig, fontConfig);
++            }
+             Map<String, ByteBuffer> bufferForPath = new HashMap<String, ByteBuffer>();
+@@ -342,6 +448,7 @@ public class Typeface {
+                     familyList.add(makeFamilyFromParsed(f, bufferForPath));
+                 }
+             }
++
+             sFallbackFonts = familyList.toArray(new FontFamily[familyList.size()]);
+             setDefault(Typeface.createFromFamilies(sFallbackFonts));
+@@ -377,22 +484,53 @@ public class Typeface {
+             Log.w(TAG, "Didn't create default family (most likely, non-Minikin build)", e);
+             // TODO: normal in non-Minikin case, remove or make error when Minikin-only
+         } catch (FileNotFoundException e) {
+-            Log.e(TAG, "Error opening " + configFilename, e);
++            Log.e(TAG, "Error opening " + configFile, e);
+         } catch (IOException e) {
+-            Log.e(TAG, "Error reading " + configFilename, e);
++            Log.e(TAG, "Error reading " + configFile, e);
+         } catch (XmlPullParserException e) {
+-            Log.e(TAG, "XML parse exception for " + configFilename, e);
++            Log.e(TAG, "XML parse exception for " + configFile, e);
+         }
+     }
++    /**
++     * Clears caches in java and skia.
++     * Skia will then reparse font config
++     * @hide
++     */
++    public static void recreateDefaults() {
++        sTypefaceCache.clear();
++        sSystemFontMap.clear();
++        init();
++
++        DEFAULT_INTERNAL = create((String) null, 0);
++        DEFAULT_BOLD_INTERNAL = create((String) null, Typeface.BOLD);
++        SANS_SERIF_INTERNAL = create("sans-serif", 0);
++        SERIF_INTERNAL = create("serif", 0);
++        MONOSPACE_INTERNAL = create("monospace", 0);
++
++        DEFAULT.native_instance = DEFAULT_INTERNAL.native_instance;
++        DEFAULT_BOLD.native_instance = DEFAULT_BOLD_INTERNAL.native_instance;
++        SANS_SERIF.native_instance = SANS_SERIF_INTERNAL.native_instance;
++        SERIF.native_instance = SERIF_INTERNAL.native_instance;
++        MONOSPACE.native_instance = MONOSPACE_INTERNAL.native_instance;
++        sDefaults[2] = create((String) null, Typeface.ITALIC);
++        sDefaults[3] = create((String) null, Typeface.BOLD_ITALIC);
++    }
++
+     static {
+         init();
+         // Set up defaults and typefaces exposed in public API
+-        DEFAULT         = create((String) null, 0);
+-        DEFAULT_BOLD    = create((String) null, Typeface.BOLD);
+-        SANS_SERIF      = create("sans-serif", 0);
+-        SERIF           = create("serif", 0);
+-        MONOSPACE       = create("monospace", 0);
++        DEFAULT_INTERNAL         = create((String) null, 0);
++        DEFAULT_BOLD_INTERNAL    = create((String) null, Typeface.BOLD);
++        SANS_SERIF_INTERNAL      = create("sans-serif", 0);
++        SERIF_INTERNAL           = create("serif", 0);
++        MONOSPACE_INTERNAL       = create("monospace", 0);
++
++        DEFAULT         = new Typeface(DEFAULT_INTERNAL.native_instance);
++        DEFAULT_BOLD    = new Typeface(DEFAULT_BOLD_INTERNAL.native_instance);
++        SANS_SERIF      = new Typeface(SANS_SERIF_INTERNAL.native_instance);
++        SERIF           = new Typeface(SERIF_INTERNAL.native_instance);
++        MONOSPACE       = new Typeface(MONOSPACE_INTERNAL.native_instance);
+         sDefaults = new Typeface[] {
+             DEFAULT,
+@@ -407,6 +545,18 @@ public class Typeface {
+         return new File("/system/etc/");
+     }
++    private static File getSystemFontDirLocation() {
++        return new File("/system/fonts/");
++    }
++
++    private static File getThemeFontConfigLocation() {
++        return new File("/data/system/theme/fonts/");
++    }
++
++    private static File getThemeFontDirLocation() {
++        return new File("/data/system/theme/fonts/");
++    }
++
+     @Override
+     protected void finalize() throws Throwable {
+         try {
+diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
+index 2822dc9..ab010d4 100644
+--- a/services/core/java/com/android/server/am/ActivityManagerService.java
++++ b/services/core/java/com/android/server/am/ActivityManagerService.java
+@@ -512,6 +512,8 @@ public final class ActivityManagerService extends ActivityManagerNative
+     // as one line, but close enough for now.
+     static final int RESERVED_BYTES_PER_LOGCAT_LINE = 100;
++    static final String PROP_REFRESH_THEME = "sys.refresh_theme";
++
+     // Access modes for handleIncomingUser.
+     static final int ALLOW_NON_FULL = 0;
+     static final int ALLOW_NON_FULL_IN_PROFILE = 1;
+@@ -3947,6 +3949,13 @@ public final class ActivityManagerService extends ActivityManagerNative
+                 mNativeDebuggingApp = null;
+             }
++            //Check if zygote should refresh its fonts
++            boolean refreshTheme = false;
++            if (SystemProperties.getBoolean(PROP_REFRESH_THEME, false)) {
++                SystemProperties.set(PROP_REFRESH_THEME, "false");
++                refreshTheme = true;
++            }
++
+             String requiredAbi = (abiOverride != null) ? abiOverride : app.info.primaryCpuAbi;
+             if (requiredAbi == null) {
+                 requiredAbi = Build.SUPPORTED_ABIS[0];
+@@ -3971,7 +3980,7 @@ public final class ActivityManagerService extends ActivityManagerNative
+             Process.ProcessStartResult startResult = Process.start(entryPoint,
+                     app.processName, uid, uid, gids, debugFlags, mountExternal,
+                     app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
+-                    app.info.dataDir, entryPointArgs);
++                    app.info.dataDir, refreshTheme, entryPointArgs);
+             checkTime(startTime, "startProcess: returned from zygote!");
+             Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
+-- 
+2.9.3
+
diff --git a/frameworks/base/0026-N-Extras-AudioService-Allow-system-effect-sounds-to-.patch b/frameworks/base/0026-N-Extras-AudioService-Allow-system-effect-sounds-to-.patch
new file mode 100644 (file)
index 0000000..d0bc5f5
--- /dev/null
@@ -0,0 +1,79 @@
+From f3f83a64481e2abdf561f7c42d7f2e7d5f32012b Mon Sep 17 00:00:00 2001
+From: Nicholas Chum <nicholaschum@gmail.com>
+Date: Sun, 17 Jul 2016 17:56:40 -0400
+Subject: [PATCH 26/38] N-Extras: AudioService: Allow system effect sounds to
+ be themed
+
+This commit checks whether there is a preexisting file in the themed
+directory "/data/system/theme/audio/ui/" and if so, change the base
+file paths for the sound. If the file does not exist in the theme
+directory, then use the default sounds.
+
+At the current moment, this will require a soft reboot to work.
+
+Change-Id: I7666c2bd259443ccec442bf6059786bea3dc069e
+---
+ .../com/android/server/audio/AudioService.java     | 26 +++++++++++++++++-----
+ 1 file changed, 21 insertions(+), 5 deletions(-)
+
+diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
+index cd766af..2e3d5a6 100644
+--- a/services/core/java/com/android/server/audio/AudioService.java
++++ b/services/core/java/com/android/server/audio/AudioService.java
+@@ -123,6 +123,7 @@ import com.android.server.pm.UserManagerService;
+ import org.xmlpull.v1.XmlPullParserException;
++import java.io.File;
+ import java.io.FileDescriptor;
+ import java.io.IOException;
+ import java.io.PrintWriter;
+@@ -281,6 +282,7 @@ public class AudioService extends IAudioService.Stub {
+     /* Sound effect file names  */
+     private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/";
++    private static final String SOUND_EFFECTS_THEMED_PATH = "/data/system/theme/audio/ui/";
+     private static final List<String> SOUND_EFFECT_FILES = new ArrayList<String>();
+     /* Sound effect file name mapping sound effect id (AudioManager.FX_xxx) to
+@@ -4679,9 +4681,16 @@ public class AudioService extends IAudioService.Stub {
+                         continue;
+                     }
+                     if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
+-                        String filePath = Environment.getRootDirectory()
+-                                + SOUND_EFFECTS_PATH
+-                                + SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effect][0]);
++                        String filePath = "";
++                        File theme_file = new File(SOUND_EFFECTS_THEMED_PATH +
++                            SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effect][0]));
++                        if (theme_file.exists()) {
++                            filePath = theme_file.getAbsolutePath();
++                        } else {
++                            filePath = Environment.getRootDirectory()
++                                    + SOUND_EFFECTS_PATH
++                                    + SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effect][0]);
++                        }
+                         int sampleId = mSoundPool.load(filePath, 0);
+                         if (sampleId <= 0) {
+                             Log.w(TAG, "Soundpool could not load file: "+filePath);
+@@ -4787,8 +4796,15 @@ public class AudioService extends IAudioService.Stub {
+                 } else {
+                     MediaPlayer mediaPlayer = new MediaPlayer();
+                     try {
+-                        String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH +
+-                                    SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effectType][0]);
++                        String filePath = "";
++                        File theme_file = new File(SOUND_EFFECTS_THEMED_PATH +
++                                    SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effectType][0]));
++                        if (theme_file.exists()) {
++                            filePath = theme_file.getAbsolutePath();
++                        } else {
++                            filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH +
++                                        SOUND_EFFECT_FILES.get(SOUND_EFFECT_FILES_MAP[effectType][0]);
++                        }
+                         mediaPlayer.setDataSource(filePath);
+                         mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM);
+                         mediaPlayer.prepare();
+-- 
+2.9.3
+
diff --git a/frameworks/base/0027-OMS7-N-ApplicationsState-add-filter-for-Substratum-o.patch b/frameworks/base/0027-OMS7-N-ApplicationsState-add-filter-for-Substratum-o.patch
new file mode 100644 (file)
index 0000000..8e1dad6
--- /dev/null
@@ -0,0 +1,53 @@
+From 0b5d2c23c6f07c09a0181c76cc83cbb61da4d495 Mon Sep 17 00:00:00 2001
+From: George G <kreach3r@users.noreply.github.com>
+Date: Mon, 4 Jul 2016 06:25:15 +0300
+Subject: [PATCH 27/38] OMS7-N: ApplicationsState: add filter for Substratum
+ overlays [1/2]
+
+This commit allows the framework to handle the filtering of the
+overlays found for OMS.
+
+Change-Id: I7646115e8f73494d726728fac58cc47aafd69d5d
+---
+ .../settingslib/applications/ApplicationsState.java     | 17 +++++++++++++++--
+ 1 file changed, 15 insertions(+), 2 deletions(-)
+
+diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+index f0ec107..6fecd40 100644
+--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
++++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+@@ -137,9 +137,11 @@ public class ApplicationsState {
+         // Only the owner can see all apps.
+         mAdminRetrieveFlags = PackageManager.GET_UNINSTALLED_PACKAGES |
+                 PackageManager.GET_DISABLED_COMPONENTS |
+-                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS;
++                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS |
++                PackageManager.GET_META_DATA;
+         mRetrieveFlags = PackageManager.GET_DISABLED_COMPONENTS |
+-                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS;
++                PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS |
++                PackageManager.GET_META_DATA;
+         /**
+          * This is a trick to prevent the foreground thread from being delayed.
+@@ -1312,6 +1314,17 @@ public class ApplicationsState {
+         }
+     };
++    public static final AppFilter FILTER_SUBSTRATUM = new AppFilter() {
++        public void init() {
++        }
++
++        @Override
++        public boolean filterApp(AppEntry entry) {
++            return !((entry.info.metaData != null) &&
++                    (entry.info.metaData.getString("Substratum_Parent") != null));
++        }
++    };
++
+     public static final AppFilter FILTER_WORK = new AppFilter() {
+         private int mCurrentUser;
+-- 
+2.9.3
+
diff --git a/frameworks/base/0028-OMS7-N-ApplicationsState-add-filter-for-Substratum-i.patch b/frameworks/base/0028-OMS7-N-ApplicationsState-add-filter-for-Substratum-i.patch
new file mode 100644 (file)
index 0000000..8734985
--- /dev/null
@@ -0,0 +1,41 @@
+From db55a4020f8942389b3862ddb7442c5f50d62f15 Mon Sep 17 00:00:00 2001
+From: Kuba Schenk <abukcz@gmail.com>
+Date: Thu, 1 Dec 2016 21:48:26 +0100
+Subject: [PATCH 28/38] OMS7-N: ApplicationsState: add filter for Substratum
+ icon overlays [1/2]
+
+This commit allows the framework to handle the filtering of the icon overlays found for OMS.
+
+Base this off the work from @KreAch3R
+
+Change-Id: I594c993977733e67f566ac65df50ad2e1bbdbdd3
+(cherry picked from commit 4d682464550f71e72e491934c78b8a42fdfc0348)
+---
+ .../android/settingslib/applications/ApplicationsState.java   | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+index 6fecd40..d3a6e21 100644
+--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
++++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+@@ -1325,6 +1325,17 @@ public class ApplicationsState {
+         }
+     };
++    public static final AppFilter FILTER_SUBSTRATUM_ICONS = new AppFilter() {
++        public void init() {
++        }
++
++        @Override
++        public boolean filterApp(AppEntry entry) {
++            return !((entry.info.metaData != null) &&
++                    (entry.info.metaData.getString("Substratum_IconPack") != null));
++        }
++    };
++
+     public static final AppFilter FILTER_WORK = new AppFilter() {
+         private int mCurrentUser;
+-- 
+2.9.3
+
diff --git a/frameworks/base/0029-Themes-Expose-QS-battery.patch b/frameworks/base/0029-Themes-Expose-QS-battery.patch
new file mode 100644 (file)
index 0000000..fe79dc3
--- /dev/null
@@ -0,0 +1,55 @@
+From da38273a4dc66c1f259b89430ffcb3c58db0e39c Mon Sep 17 00:00:00 2001
+From: Abdulwahab Isam <abdoi94.iq@gmail.com>
+Date: Fri, 7 Oct 2016 08:30:11 +0300
+Subject: [PATCH 29/38] Themes: Expose QS battery
+
+This is needed for white themes like Belo. Should function the same with dark themes as well.
+
+Change-Id: I65e3f777a2a2605a06260705f92f8617407d9005
+---
+ packages/SystemUI/res/layout/battery_detail.xml | 6 +++---
+ packages/SystemUI/res/values/projekt_colors.xml | 6 ++++++
+ 2 files changed, 9 insertions(+), 3 deletions(-)
+
+diff --git a/packages/SystemUI/res/layout/battery_detail.xml b/packages/SystemUI/res/layout/battery_detail.xml
+index 8abfcf6..af3acdc 100644
+--- a/packages/SystemUI/res/layout/battery_detail.xml
++++ b/packages/SystemUI/res/layout/battery_detail.xml
+@@ -27,7 +27,7 @@
+         android:layout_height="wrap_content"
+         android:paddingStart="16dp"
+         android:textAppearance="?android:attr/textAppearanceSmall"
+-        android:textColor="?android:attr/colorAccent" />
++        android:textColor="@color/qs_battery_text_color" />
+     <com.android.systemui.ResizingSpace
+         android:layout_width="match_parent"
+@@ -40,8 +40,8 @@
+         android:layout_marginStart="16dp"
+         android:layout_marginEnd="24dp"
+         systemui:sideLabels="@array/battery_labels"
+-        android:colorAccent="?android:attr/colorAccent"
+-        systemui:textColor="#66FFFFFF" />
++        android:colorAccent="@color/qs_battery_accent"
++        systemui:textColor="@color/qs_battery_graph_text_color" />
+     <com.android.systemui.ResizingSpace
+         android:layout_width="match_parent"
+diff --git a/packages/SystemUI/res/values/projekt_colors.xml b/packages/SystemUI/res/values/projekt_colors.xml
+index 0f89199..83b8524 100644
+--- a/packages/SystemUI/res/values/projekt_colors.xml
++++ b/packages/SystemUI/res/values/projekt_colors.xml
+@@ -24,4 +24,10 @@
+     <color name="qs_alarm_status_text_color">#64ffffff</color>
+     <!-- QS edit page toolbar text color -->
+     <color name="qs_edit_toolbar_text_color">#FFFFFF</color>
++    <!-- QS battery % text color -->
++    <color name="qs_battery_text_color">@*android:color/accent_device_default_light</color>
++    <!-- QS battery graph text color -->
++    <color name="qs_battery_graph_text_color">#66FFFFFF</color>
++    <!-- QS battery accent color -->
++    <color name="qs_battery_accent">@*android:color/accent_device_default_light</color>
+ </resources>
+-- 
+2.9.3
+
diff --git a/frameworks/base/0030-OMS-Introduce-MODIFY_OVERLAYS-permission-for-user-ap.patch b/frameworks/base/0030-OMS-Introduce-MODIFY_OVERLAYS-permission-for-user-ap.patch
new file mode 100644 (file)
index 0000000..6756897
--- /dev/null
@@ -0,0 +1,120 @@
+From 4045dd2c0cbb895e8d430a9388d33b5596a9fd2b Mon Sep 17 00:00:00 2001
+From: bigrushdog <randall.rushing@gmail.com>
+Date: Mon, 19 Dec 2016 04:33:31 -0800
+Subject: [PATCH 30/38] OMS: Introduce MODIFY_OVERLAYS permission for user apps
+
+This permission will grant the app read and write permissions
+to access OverlayManagerService. If caller does not posess
+this permissions, OMS falls back to default permission checks
+
+Change-Id: Ib6b10b516577f338aee31e759bfd16278f902c20
+(cherry picked from commit 8e59c96513c573a4912492c005b076cb2a972332)
+---
+ core/res/AndroidManifest.xml                       |  6 +++++
+ core/res/res/values/strings.xml                    |  5 +++++
+ .../android/server/om/OverlayManagerService.java   | 26 +++++++++++++++-------
+ 3 files changed, 29 insertions(+), 8 deletions(-)
+
+diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
+index 986be28..abed0df 100644
+--- a/core/res/AndroidManifest.xml
++++ b/core/res/AndroidManifest.xml
+@@ -3160,6 +3160,12 @@
+     <permission android:name="android.permission.SUBSTITUTE_NOTIFICATION_APP_NAME"
+                 android:protectionLevel="signature|privileged" />
++    <!-- Allows an application to make calls to OverlayManagerService.-->
++    <permission android:name="android.permission.MODIFY_OVERLAYS"
++        android:label="@string/permlab_modifyOverlays"
++        android:description="@string/permdesc_modifyOverlays"
++        android:protectionLevel="normal" />
++
+     <application android:process="system"
+                  android:persistent="true"
+                  android:hasCode="false"
+diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
+index 8f5f8cf..8313d5d 100644
+--- a/core/res/res/values/strings.xml
++++ b/core/res/res/values/strings.xml
+@@ -1411,6 +1411,11 @@
+     <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+     <string name="permdesc_access_notification_policy">Allows the app to read and write Do Not Disturb configuration.</string>
++    <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
++    <string name="permlab_modifyOverlays">modify theme overlays</string>
++    <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
++    <string name="permdesc_modifyOverlays">Allows the app to make modifications to theme overlays using OverlayManagerService</string>
++
+     <!-- Policy administration -->
+     <!-- Title of policy access to limiting the user's password choices -->
+diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java
+index deb9046..e205ce1 100644
+--- a/services/core/java/com/android/server/om/OverlayManagerService.java
++++ b/services/core/java/com/android/server/om/OverlayManagerService.java
+@@ -36,6 +36,7 @@ import android.content.om.IOverlayManager;
+ import android.content.om.OverlayInfo;
+ import android.content.pm.IPackageManager;
+ import android.content.pm.PackageInfo;
++import android.content.pm.PackageManager;
+ import android.content.pm.PackageManagerInternal;
+ import android.content.pm.UserInfo;
+ import android.net.Uri;
+@@ -114,7 +115,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
+  *     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.</li>
++ *     CHANGE_CONFIGURATION permission. Additionally, read and write access
++ *     is granted by the MODIFY_OVERLAYS permission.</li>
+  * </ul>
+  *
+  * <p>The AIDL interface works with String package names, int user IDs, and
+@@ -549,19 +551,24 @@ public final class OverlayManagerService extends SystemService {
+         /**
+          * 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
++         * to hold the INTERACT_ACROSS_USERS_FULL permission or MODIFY_OVERLAYS 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);
++            if (getContext().checkCallingOrSelfPermission(
++                    android.Manifest.permission.MODIFY_OVERLAYS) == PackageManager.PERMISSION_GRANTED) {
++                return userId;
++            } else {
++                return ActivityManager.handleIncomingUser(Binder.getCallingPid(),
++                        Binder.getCallingUid(), userId, false, true, message, null);
++            }
+         }
+         /**
+-         * Enforce that the caller holds the CHANGE_CONFIGURATION permission (or is
++         * Enforce that the caller holds the CHANGE_CONFIGURATION permission or MODIFY_OVERLAYS permission (or is
+          * system or root).
+          *
+          * @param message used as message if SecurityException is thrown
+@@ -570,9 +577,12 @@ public final class OverlayManagerService extends SystemService {
+         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);
++            if (getContext().checkCallingOrSelfPermission(
++                    android.Manifest.permission.MODIFY_OVERLAYS) != PackageManager.PERMISSION_GRANTED) {
++                if (callingUid != Process.SYSTEM_UID && callingUid != 0) {
++                    getContext().enforceCallingOrSelfPermission(
++                            android.Manifest.permission.CHANGE_CONFIGURATION, message);
++                }
+             }
+         }
+-- 
+2.9.3
+
diff --git a/frameworks/base/0031-SystemUI-Expose-switch-bar-title.patch b/frameworks/base/0031-SystemUI-Expose-switch-bar-title.patch
new file mode 100644 (file)
index 0000000..91c2989
--- /dev/null
@@ -0,0 +1,61 @@
+From ee6b990018d29a124adc51e273d1fe0cf5fa66bc Mon Sep 17 00:00:00 2001
+From: daveyannihilation <daveyannihilation@hotmail.com>
+Date: Sun, 1 Jan 2017 01:47:53 -0700
+Subject: [PATCH 31/38] SystemUI: Expose switch bar title
+
+This is needed for the power notifications switchbar in SystemUI Tuner, amongst other things.
+
+Change-Id: I86f04840c2be46519509556b8d0061cefe26f631
+---
+ packages/SystemUI/res/layout/switch_bar.xml     |  3 +--
+ packages/SystemUI/res/values/projekt_styles.xml | 24 ++++++++++++++++++++++++
+ 2 files changed, 25 insertions(+), 2 deletions(-)
+ create mode 100644 packages/SystemUI/res/values/projekt_styles.xml
+
+diff --git a/packages/SystemUI/res/layout/switch_bar.xml b/packages/SystemUI/res/layout/switch_bar.xml
+index 41cdb78..344c5aa 100644
+--- a/packages/SystemUI/res/layout/switch_bar.xml
++++ b/packages/SystemUI/res/layout/switch_bar.xml
+@@ -33,8 +33,7 @@
+         android:paddingStart="48dp"
+         android:maxLines="2"
+         android:ellipsize="end"
+-        android:textAppearance="@android:style/TextAppearance.Material.Title"
+-        android:textColor="?android:attr/textColorPrimaryInverse"
++        android:textAppearance="@style/TextAppearance.SwitchBar"
+         android:textAlignment="viewStart"
+         android:text="@string/switch_bar_on" />
+diff --git a/packages/SystemUI/res/values/projekt_styles.xml b/packages/SystemUI/res/values/projekt_styles.xml
+new file mode 100644
+index 0000000..f49834b
+--- /dev/null
++++ b/packages/SystemUI/res/values/projekt_styles.xml
+@@ -0,0 +1,24 @@
++<?xml version="1.0" encoding="utf-8"?>
++<!--
++    Copyright (c) 2016 Projekt Substratum
++
++    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.
++-->
++
++<resources>
++    <!-- Switch bar text appearance -->
++    <style name="TextAppearance.SwitchBar" parent="@android:style/TextAppearance.Material.Title">
++        <item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
++    </style>
++</resources>
++
+-- 
+2.9.3
+
diff --git a/frameworks/base/0032-Themes-Expose-manifest-styles-for-themes.patch b/frameworks/base/0032-Themes-Expose-manifest-styles-for-themes.patch
new file mode 100644 (file)
index 0000000..a682146
--- /dev/null
@@ -0,0 +1,151 @@
+From b865d57f51ef8545a434dfdfee32055954ce952b Mon Sep 17 00:00:00 2001
+From: Bryan Owens <djbryan3540@gmail.com>
+Date: Fri, 6 Jan 2017 21:12:15 +0800
+Subject: [PATCH 32/38] Themes: Expose manifest styles for themes
+
+Change-Id: Ie3a4fdead4f4fa1c121018b38de1c86a05bbcff2
+---
+ core/res/AndroidManifest.xml           | 20 ++++++++---------
+ core/res/res/values/projekt_styles.xml | 41 ++++++++++++++++++++++++++++++++++
+ 2 files changed, 51 insertions(+), 10 deletions(-)
+ create mode 100644 core/res/res/values/projekt_styles.xml
+
+diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
+index abed0df..6ac87ca 100644
+--- a/core/res/AndroidManifest.xml
++++ b/core/res/AndroidManifest.xml
+@@ -3212,7 +3212,7 @@
+                 android:label="@string/managed_profile_label">
+         </activity-alias>
+         <activity android:name="com.android.internal.app.HeavyWeightSwitcherActivity"
+-                android:theme="@style/Theme.DeviceDefault.Light.Dialog"
++                android:theme="@style/HeavyWeightSwitcherActivityTheme"
+                 android:label="@string/heavy_weight_switcher_title"
+                 android:finishOnCloseSystemDialogs="true"
+                 android:excludeFromRecents="true"
+@@ -3245,7 +3245,7 @@
+         <activity android:name="android.accounts.ChooseAccountActivity"
+                 android:excludeFromRecents="true"
+                 android:exported="true"
+-                android:theme="@style/Theme.DeviceDefault.Light.Dialog"
++                android:theme="@style/ChooseAccountActivityTheme"
+                 android:label="@string/choose_account_label"
+                 android:process=":ui">
+         </activity>
+@@ -3253,14 +3253,14 @@
+         <activity android:name="android.accounts.ChooseTypeAndAccountActivity"
+                 android:excludeFromRecents="true"
+                 android:exported="true"
+-                android:theme="@style/Theme.DeviceDefault.Light.Dialog"
++                android:theme="@style/ChooseTypeAndAccountActivityTheme"
+                 android:label="@string/choose_account_label"
+                 android:process=":ui">
+         </activity>
+         <activity android:name="android.accounts.ChooseAccountTypeActivity"
+                 android:excludeFromRecents="true"
+-                android:theme="@style/Theme.DeviceDefault.Light.Dialog"
++                android:theme="@style/ChooseAccountTypeActivityTheme"
+                 android:label="@string/choose_account_label"
+                 android:process=":ui">
+         </activity>
+@@ -3268,19 +3268,19 @@
+         <activity android:name="android.accounts.CantAddAccountActivity"
+                 android:excludeFromRecents="true"
+                 android:exported="true"
+-                android:theme="@style/Theme.DeviceDefault.Light.Dialog.NoActionBar"
++                android:theme="@style/CantAddAccountActivityTheme"
+                 android:process=":ui">
+         </activity>
+         <activity android:name="android.accounts.GrantCredentialsPermissionActivity"
+                 android:excludeFromRecents="true"
+                 android:exported="true"
+-                android:theme="@style/Theme.DeviceDefault.Light.DialogWhenLarge"
++                android:theme="@style/GrantCredentialsPermissionActivityTheme"
+                 android:process=":ui">
+         </activity>
+         <activity android:name="android.content.SyncActivityTooManyDeletes"
+-               android:theme="@style/Theme.DeviceDefault.Light.Dialog"
++               android:theme="@style/SyncActivityTooManyDeletesTheme"
+                android:label="@string/sync_too_many_deletes"
+                android:process=":ui">
+         </activity>
+@@ -3300,7 +3300,7 @@
+         </activity>
+         <activity android:name="com.android.internal.app.NetInitiatedActivity"
+-                android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert"
++                android:theme="@style/NetInitiatedActivityTheme"
+                 android:excludeFromRecents="true"
+                 android:process=":ui">
+         </activity>
+@@ -3321,7 +3321,7 @@
+         <activity android:name="com.android.internal.app.ConfirmUserCreationActivity"
+                 android:excludeFromRecents="true"
+                 android:process=":ui"
+-                android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert">
++                android:theme="@style/ConfirmUserCreationActivityTheme">
+             <intent-filter android:priority="1000">
+                 <action android:name="android.os.action.CREATE_USER" />
+                 <category android:name="android.intent.category.DEFAULT" />
+@@ -3329,7 +3329,7 @@
+         </activity>
+         <activity android:name="com.android.internal.app.UnlaunchableAppActivity"
+-                android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert"
++                android:theme="@style/UnlaunchableAppActivityTheme"
+                 android:excludeFromRecents="true"
+                 android:process=":ui">
+         </activity>
+diff --git a/core/res/res/values/projekt_styles.xml b/core/res/res/values/projekt_styles.xml
+new file mode 100644
+index 0000000..e5fe635
+--- /dev/null
++++ b/core/res/res/values/projekt_styles.xml
+@@ -0,0 +1,41 @@
++<?xml version="1.0" encoding="utf-8"?>
++<!-- Copyright (C) 2017 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.
++-->
++
++<resources>
++
++    <!-- Exposed manifest styles -->
++
++    <style name="HeavyWeightSwitcherActivityTheme" parent="Theme.DeviceDefault.Light.Dialog" />
++
++    <style name="ChooseAccountActivityTheme" parent="Theme.DeviceDefault.Light.Dialog" />
++
++    <style name="ChooseTypeAndAccountActivityTheme" parent="Theme.DeviceDefault.Light.Dialog" />
++
++    <style name="ChooseAccountTypeActivityTheme" parent="Theme.DeviceDefault.Light.Dialog" />
++
++    <style name="CantAddAccountActivityTheme" parent="Theme.DeviceDefault.Light.Dialog.NoActionBar" />
++
++    <style name="GrantCredentialsPermissionActivityTheme" parent="Theme.DeviceDefault.Light.DialogWhenLarge" />
++
++    <style name="SyncActivityTooManyDeletesTheme" parent="Theme.DeviceDefault.Light.Dialog" />
++
++    <style name="NetInitiatedActivityTheme" parent="Theme.DeviceDefault.Light.Dialog.Alert" />
++
++    <style name="ConfirmUserCreationActivityTheme" parent="Theme.DeviceDefault.Light.Dialog.Alert" />
++
++    <style name="UnlaunchableAppActivityTheme" parent="Theme.DeviceDefault.Light.Dialog.Alert" />
++
++</resources>
+-- 
+2.9.3
+
diff --git a/frameworks/base/0033-OMS-StrictMode-and-files-under-data-system-theme.patch b/frameworks/base/0033-OMS-StrictMode-and-files-under-data-system-theme.patch
new file mode 100644 (file)
index 0000000..8be7278
--- /dev/null
@@ -0,0 +1,40 @@
+From a33286e7a15b13bf4d8ac2aa9a779b8b888c9d2a Mon Sep 17 00:00:00 2001
+From: mickybart <mickybart@pygoscelis.org>
+Date: Sat, 19 Nov 2016 19:05:05 -0500
+Subject: [PATCH 33/38] OMS: StrictMode and files under /data/system/theme/
+
+Themes are using /data/system/theme/ to push some files like LowBattery.ogg (audio notification)
+When the device battery trigger the low battery state, the sound is not played due
+to StrictMode and SystemUI is crashing.
+
+So we need that StrictMode authorize files under /system OR /data/system/theme
+
+Logcat of the issue:
+
+E AndroidRuntime: Caused by: android.os.FileUriExposedException: file:///data/system/theme/audio/ui/LowBattery.ogg exposed beyond app through Notification.sound
+E AndroidRuntime:        at android.os.StrictMode.onFileUriExposed(StrictMode.java:1799)
+E AndroidRuntime:        at android.net.Uri.checkFileUriExposed(Uri.java:2346)
+E AndroidRuntime:        at android.app.NotificationManager.notifyAsUser(NotificationManager.java:300)
+
+Change-Id: I154dc4280de8eaf891772a9632283e9f547f5718
+---
+ core/java/android/net/Uri.java | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
+index 67378bd..4fb189e 100644
+--- a/core/java/android/net/Uri.java
++++ b/core/java/android/net/Uri.java
+@@ -2342,7 +2342,8 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
+      * @hide
+      */
+     public void checkFileUriExposed(String location) {
+-        if ("file".equals(getScheme()) && !getPath().startsWith("/system/")) {
++        if ("file".equals(getScheme()) && !(getPath().startsWith("/system/")
++                || getPath().startsWith("/data/system/theme/"))) {
+             StrictMode.onFileUriExposed(this, location);
+         }
+     }
+-- 
+2.9.3
+
diff --git a/frameworks/base/0034-doze-allow-grayscale-even-if-invert-boolean-is-false.patch b/frameworks/base/0034-doze-allow-grayscale-even-if-invert-boolean-is-false.patch
new file mode 100644 (file)
index 0000000..30f3445
--- /dev/null
@@ -0,0 +1,45 @@
+From 82244aeb8e03cec7e5cbbcd89fef918ea3d151d2 Mon Sep 17 00:00:00 2001
+From: Daniel Koman <dankoman30@gmail.com>
+Date: Wed, 28 Sep 2016 15:28:26 +0200
+Subject: [PATCH 34/38] doze: allow grayscale even if invert boolean is false
+
+for dark themes, we are setting the config boolean for inverting
+    doze notifications to false.  in addition to preventing
+    color inversion, this was also preventing the notification
+    from grayscaling.  as a result, on dark themes (boolean false), we
+    were seeing color icons on doze notifications.  this commit fixes
+    the grayscaling, and brings back the very aesthetically pleasing
+    (imo) grayscale-to-color fade-in for the notifications' app icons.
+
+Change-Id: Ifc5efbccbeb02910684b76793721b10f1d64f870
+---
+ packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+diff --git a/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java b/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
+index 605f381..16d46c0 100644
+--- a/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
++++ b/packages/SystemUI/src/com/android/systemui/ViewInvertHelper.java
+@@ -92,7 +92,7 @@ public class ViewInvertHelper {
+     }
+     public void update(boolean invert) {
+-        if (invert && mThemeInvert) {
++        if (invert) {
+             updateInvertPaint(1f);
+             for (int i = 0; i < mTargets.size(); i++) {
+                 mTargets.get(i).setLayerType(View.LAYER_TYPE_HARDWARE, mDarkPaint);
+@@ -115,7 +115,9 @@ public class ViewInvertHelper {
+         mMatrix.set(invert);
+         mGrayscaleMatrix.setSaturation(1 - intensity);
+         mMatrix.preConcat(mGrayscaleMatrix);
+-        mDarkPaint.setColorFilter(new ColorMatrixColorFilter(mMatrix));
++        mDarkPaint.setColorFilter(new ColorMatrixColorFilter(
++                mThemeInvert ? mMatrix : mGrayscaleMatrix));
++
+     }
+     public void setInverted(boolean invert, boolean fade, long delay) {
+-- 
+2.9.3
+
diff --git a/frameworks/base/0035-Expose-external-qs-tile-tint-color.patch b/frameworks/base/0035-Expose-external-qs-tile-tint-color.patch
new file mode 100644 (file)
index 0000000..7bc7c43
--- /dev/null
@@ -0,0 +1,42 @@
+From a71d0e0ea46b7d910dea2f9c0eafbab2b1e3ef24 Mon Sep 17 00:00:00 2001
+From: Alex Cruz <mazdarider23@gmail.com>
+Date: Tue, 24 Jan 2017 11:14:46 +0100
+Subject: [PATCH 35/38] Expose external qs tile tint color
+
+This should allow themers to get around issues like this (see pic below)
+
+https://i.imgur.com/cG2OzRT.jpg
+
+Change-Id: If6dbf9ab29f8007d85a3c45524b1cf4ba1b032fb
+---
+ packages/SystemUI/res/values/projekt_colors.xml                         | 2 ++
+ .../SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java | 2 +-
+ 2 files changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/packages/SystemUI/res/values/projekt_colors.xml b/packages/SystemUI/res/values/projekt_colors.xml
+index 83b8524..e31d124 100644
+--- a/packages/SystemUI/res/values/projekt_colors.xml
++++ b/packages/SystemUI/res/values/projekt_colors.xml
+@@ -30,4 +30,6 @@
+     <color name="qs_battery_graph_text_color">#66FFFFFF</color>
+     <!-- QS battery accent color -->
+     <color name="qs_battery_accent">@*android:color/accent_device_default_light</color>
++    <!-- External QS tile tint color -->
++    <color name="external_qs_tile_tint_color">@android:color/white</color>
+ </resources>
+diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+index 0cd6490..6fabc61 100644
+--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
++++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+@@ -177,7 +177,7 @@ public class TileQueryHelper {
+                     continue;
+                 }
+                 icon.mutate();
+-                icon.setTint(mContext.getColor(android.R.color.white));
++                icon.setTint(mContext.getColor(R.color.external_qs_tile_tint_color));
+                 CharSequence label = info.serviceInfo.loadLabel(pm);
+                 addTile(spec, icon, label != null ? label.toString() : "null", appLabel, mContext);
+             }
+-- 
+2.9.3
+
diff --git a/frameworks/base/0036-graphics-ADB-N-icon-compatible-with-OMS7.patch b/frameworks/base/0036-graphics-ADB-N-icon-compatible-with-OMS7.patch
new file mode 100644 (file)
index 0000000..c3b926f
--- /dev/null
@@ -0,0 +1,45 @@
+From cd5423a1a675a6ac3986ac9960a6c9db8a386b0b Mon Sep 17 00:00:00 2001
+From: George G <kreach3r@users.noreply.github.com>
+Date: Thu, 2 Feb 2017 01:52:27 +0200
+Subject: [PATCH 36/38] graphics: ADB "N" icon compatible with OMS7
+
+It's the same problem as the booleans again. This time, it affected the adb "N" icon in the statusbar.
+This commit should fix this.
+
+After: http://i.imgur.com/RPh6WKK.jpg
+
+Previous commits on the same matter:
+OMS7 introduced this fine piece of code: https://github.com/SubstratumResources/platform_frameworks_base/blob/n-oms7/core/java/android/app/ResourcesManager.java#L897..#L904
+
+// 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.)
+
+Replaced deprecated Resources.getSystem() with compatible method.
+
+Change-Id: Ibab2ce1571360a9e03043d1bf3144c89e54e1947
+---
+ graphics/java/android/graphics/drawable/Icon.java | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
+index 2b950d3..fb26bee 100644
+--- a/graphics/java/android/graphics/drawable/Icon.java
++++ b/graphics/java/android/graphics/drawable/Icon.java
+@@ -294,7 +294,7 @@ public final class Icon implements Parcelable {
+                         resPackage = context.getPackageName();
+                     }
+                     if ("android".equals(resPackage)) {
+-                        mObj1 = Resources.getSystem();
++                        mObj1 = context.getResources();
+                     } else {
+                         final PackageManager pm = context.getPackageManager();
+                         try {
+-- 
+2.9.3
+
diff --git a/frameworks/base/0037-Set-external-QS-tiles-tint-mode-to-SRC_ATOP.patch b/frameworks/base/0037-Set-external-QS-tiles-tint-mode-to-SRC_ATOP.patch
new file mode 100644 (file)
index 0000000..347da96
--- /dev/null
@@ -0,0 +1,43 @@
+From 1a4d5be7f78b87480be19243ae88e543a1b9db58 Mon Sep 17 00:00:00 2001
+From: Alex Cruz <mazdarider23@gmail.com>
+Date: Sat, 4 Feb 2017 14:13:26 +0100
+Subject: [PATCH 37/38] Set external QS tiles tint mode to SRC_ATOP
+
+While the external qs tile tint color was exposed, we had the same problem
+we had with the external icons in Settings which is if a themer set the color
+to transparent, they were SOL.
+
+This mirrors what @iskandar1023 did in the commit below
+http://review.projektsubstratum.com/#/c/286/
+
+Before - https://i.imgur.com/trpefmZ.png
+After - https://i.imgur.com/ugAqrju.png
+
+Change-Id: I6d577573dd494d61a3e87abebd919b02a481db56
+---
+ .../SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+index 6fabc61..dbdb614 100644
+--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
++++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileQueryHelper.java
+@@ -23,6 +23,7 @@ import android.content.Context;
+ import android.content.Intent;
+ import android.content.pm.PackageManager;
+ import android.content.pm.ResolveInfo;
++import android.graphics.PorterDuff.Mode;
+ import android.graphics.drawable.Drawable;
+ import android.os.AsyncTask;
+ import android.os.Handler;
+@@ -178,6 +179,7 @@ public class TileQueryHelper {
+                 }
+                 icon.mutate();
+                 icon.setTint(mContext.getColor(R.color.external_qs_tile_tint_color));
++                icon.setTintMode(Mode.SRC_ATOP);
+                 CharSequence label = info.serviceInfo.loadLabel(pm);
+                 addTile(spec, icon, label != null ? label.toString() : "null", appLabel, mContext);
+             }
+-- 
+2.9.3
+
diff --git a/frameworks/base/0038-Themes-Expose-Keyguard-affordance-circle-background.patch b/frameworks/base/0038-Themes-Expose-Keyguard-affordance-circle-background.patch
new file mode 100644 (file)
index 0000000..9abb843
--- /dev/null
@@ -0,0 +1,38 @@
+From c06669d9a78ed88300a59d9cb1e6f9555086e569 Mon Sep 17 00:00:00 2001
+From: Branden M <wasabi.dev@gmail.com>
+Date: Wed, 1 Feb 2017 22:22:45 -0600
+Subject: [PATCH 38/38] Themes: Expose Keyguard affordance circle background
+
+Change-Id: Id4a078cdbc944fa0c0736103045a0382d49ecb80
+---
+ packages/SystemUI/res/values/projekt_colors.xml                         | 2 ++
+ .../src/com/android/systemui/statusbar/KeyguardAffordanceView.java      | 2 +-
+ 2 files changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/packages/SystemUI/res/values/projekt_colors.xml b/packages/SystemUI/res/values/projekt_colors.xml
+index e31d124..e8f8e50 100644
+--- a/packages/SystemUI/res/values/projekt_colors.xml
++++ b/packages/SystemUI/res/values/projekt_colors.xml
+@@ -32,4 +32,6 @@
+     <color name="qs_battery_accent">@*android:color/accent_device_default_light</color>
+     <!-- External QS tile tint color -->
+     <color name="external_qs_tile_tint_color">@android:color/white</color>
++    <!-- Keyguard affordance circle background -->
++    <color name="keyguard_affordance_circle_background">@android:color/white</color>
+ </resources>
+diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
+index b94df1d..3d661b7 100644
+--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
++++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
+@@ -131,7 +131,7 @@ public class KeyguardAffordanceView extends ImageView implements Palette.Palette
+         super(context, attrs, defStyleAttr, defStyleRes);
+         mCirclePaint = new Paint();
+         mCirclePaint.setAntiAlias(true);
+-        mCircleColor = 0xffffffff;
++        mCircleColor = mContext.getResources().getColor(R.color.keyguard_affordance_circle_background);
+         mCirclePaint.setColor(mCircleColor);
+         mNormalColor = 0xffffffff;
+-- 
+2.9.3
+
diff --git a/frameworks/native/0001-OMS-N-installd-add-command-rmidmap.patch b/frameworks/native/0001-OMS-N-installd-add-command-rmidmap.patch
new file mode 100644 (file)
index 0000000..ec1ad89
--- /dev/null
@@ -0,0 +1,87 @@
+From 70dc365c2877d443aebb7da11f061fa3d69dd2a4 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= <marten.kongstad@sonymobile.com>
+Date: Tue, 15 Dec 2015 14:02:30 +0100
+Subject: [PATCH] OMS-N: installd: add command 'rmidmap'
+
+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/base
+(with the same Change-Id).
+
+Bug: 31052947
+
+Change-Id: Iae19a519803f0c172b02a32faa283ef36f43863c
+---
+ cmds/installd/commands.cpp | 16 ++++++++++++++++
+ cmds/installd/commands.h   |  1 +
+ cmds/installd/installd.cpp |  6 ++++++
+ 3 files changed, 23 insertions(+)
+
+diff --git a/cmds/installd/commands.cpp b/cmds/installd/commands.cpp
+index cadcd1e..0b25da1 100644
+--- a/cmds/installd/commands.cpp
++++ b/cmds/installd/commands.cpp
+@@ -2047,6 +2047,22 @@ fail:
+     return -1;
+ }
++int rm_idmap(const char *overlay_apk)
++{
++    char idmap_path[PATH_MAX];
++
++    if (flatten_path(IDMAP_PREFIX, IDMAP_SUFFIX, overlay_apk,
++                idmap_path, sizeof(idmap_path)) == -1) {
++        ALOGE("idmap cannot generate idmap path for overlay %s\n", overlay_apk);
++        return -1;
++    }
++    if (unlink(idmap_path) < 0) {
++        ALOGE("couldn't unlink idmap file %s\n", idmap_path);
++        return -1;
++    }
++    return 0;
++}
++
+ int restorecon_app_data(const char* uuid, const char* pkgName, userid_t userid, int flags,
+         appid_t appid, const char* seinfo) {
+     int res = 0;
+diff --git a/cmds/installd/commands.h b/cmds/installd/commands.h
+index ba27517..2da80dd 100644
+--- a/cmds/installd/commands.h
++++ b/cmds/installd/commands.h
+@@ -76,6 +76,7 @@ int dexopt(const char* const params[DEXOPT_PARAM_COUNT]);
+ int mark_boot_complete(const char *instruction_set);
+ int linklib(const char* uuid, const char* pkgname, const char* asecLibDir, int userId);
+ int idmap(const char *target_path, const char *overlay_path, uid_t uid);
++int rm_idmap(const char *overlay_path);
+ int create_oat_dir(const char* oat_dir, const char *instruction_set);
+ int rm_package_dir(const char* apk_path);
+ int clear_app_profiles(const char* pkgname);
+diff --git a/cmds/installd/installd.cpp b/cmds/installd/installd.cpp
+index 8f883db..93388ce 100644
+--- a/cmds/installd/installd.cpp
++++ b/cmds/installd/installd.cpp
+@@ -383,6 +383,11 @@ static int do_idmap(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
+     return idmap(arg[0], arg[1], atoi(arg[2]));
+ }
++static int do_rm_idmap(char **arg, char reply[REPLY_MAX] __unused)
++{
++    return rm_idmap(arg[0]);
++}
++
+ static int do_create_oat_dir(char **arg, char reply[REPLY_MAX] ATTRIBUTE_UNUSED)
+ {
+     /* oat_dir, instruction_set */
+@@ -450,6 +455,7 @@ struct cmdinfo cmds[] = {
+     { "freecache",            2, do_free_cache },
+     { "linklib",              4, do_linklib },
+     { "idmap",                3, do_idmap },
++    { "rmidmap",              1, do_rm_idmap },
+     { "createoatdir",         2, do_create_oat_dir },
+     { "rmpackagedir",         1, do_rm_package_dir },
+     { "clear_app_profiles",   1, do_clear_app_profiles },
+-- 
+2.9.3
+
diff --git a/packages/apps/Contacts/0001-Themes-Expose-hardcoded-layout-and-styles-colors.patch b/packages/apps/Contacts/0001-Themes-Expose-hardcoded-layout-and-styles-colors.patch
new file mode 100644 (file)
index 0000000..ac7993f
--- /dev/null
@@ -0,0 +1,188 @@
+From 136516428b86786251449bf2e008d126d9690691 Mon Sep 17 00:00:00 2001
+From: beanstown106 <nbenis106@gmail.com>
+Date: Fri, 20 Nov 2015 17:27:43 -0500
+Subject: [PATCH] Themes: Expose hardcoded layout and styles colors
+
+Exposed text and background colors for themes to be compatible with themeable
+google dialer. Split PeopleTheme in styles.
+
+Change-Id: I4ca0347470333508e399bfae7ed5291a1a704410
+---
+ res/layout/confirm_add_detail_activity.xml |  6 +++---
+ res/layout/editor_account_header.xml       |  2 +-
+ res/values/projekt_colors.xml              | 29 +++++++++++++++++++++++++++++
+ res/values/styles.xml                      | 27 ++++++++++++++++-----------
+ 4 files changed, 49 insertions(+), 15 deletions(-)
+ create mode 100644 res/values/projekt_colors.xml
+
+diff --git a/res/layout/confirm_add_detail_activity.xml b/res/layout/confirm_add_detail_activity.xml
+index 1fa6236..eb54b8a 100644
+--- a/res/layout/confirm_add_detail_activity.xml
++++ b/res/layout/confirm_add_detail_activity.xml
+@@ -55,7 +55,7 @@
+             android:layout_alignRight="@id/photo"
+             android:layout_alignStart="@id/photo"
+             android:layout_alignEnd="@id/photo"
+-            android:background="#7F000000" />
++            android:background="@color/photo_text_bar_bg" />
+         <ImageButton
+             android:id="@+id/open_details_button"
+@@ -92,7 +92,7 @@
+                 android:paddingLeft="8dip"
+                 android:paddingStart="8dip"
+                 android:gravity="center_vertical"
+-                android:textColor="@android:color/white"
++                android:textColor="@color/text_color_white"
+                 android:textSize="16sp"
+                 android:singleLine="true" />
+@@ -105,7 +105,7 @@
+                 android:paddingStart="8dip"
+                 android:gravity="center_vertical"
+                 android:textAppearance="?android:attr/textAppearanceSmall"
+-                android:textColor="@android:color/white"
++                android:textColor="@color/text_color_white"
+                 android:singleLine="true"
+                 android:paddingBottom="4dip"
+                 android:visibility="gone" />
+diff --git a/res/layout/editor_account_header.xml b/res/layout/editor_account_header.xml
+index 59ae3b1..a7d0c62 100644
+--- a/res/layout/editor_account_header.xml
++++ b/res/layout/editor_account_header.xml
+@@ -20,7 +20,7 @@
+     android:layout_height="wrap_content"
+     android:layout_width="match_parent"
+     android:minHeight="48dip"
+-    android:background="#EEEEEE"
++    android:background="@color/editor_account_header_bg"
+     android:orientation="horizontal"
+     android:paddingTop="8dip"
+     android:paddingBottom="8dip"
+diff --git a/res/values/projekt_colors.xml b/res/values/projekt_colors.xml
+new file mode 100644
+index 0000000..d258c86
+--- /dev/null
++++ b/res/values/projekt_colors.xml
+@@ -0,0 +1,29 @@
++<?xml version="1.0" encoding="utf-8"?>
++<!--
++    Copyright (c) 2016 Project Substratum
++
++    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.
++-->
++
++<resources>
++    <color name="photo_text_bar_bg">#7F000000</color>
++    <color name="text_color_white">@android:color/white</color>
++    <color name="editor_account_header_bg">#EEEEEE</color>
++    <color name="list_item_name_text_color">#ff212121</color>
++    <color name="white">@android:color/white</color>
++    <color name="contacts_action_bar_text_color_hint">#CCCCCC</color>
++    <color name="contacts_action_bar_text_color">@android:color/black</color>
++    <color name="section_divider_background_color">#7e7e87</color>
++    <color name="edit_kind_text_appearance_text_color">#363636</color>
++    <color name="account_type_name_text_color">#363636</color>
++</resources>
+diff --git a/res/values/styles.xml b/res/values/styles.xml
+index 95641e3..69d79ea 100644
+--- a/res/values/styles.xml
++++ b/res/values/styles.xml
+@@ -44,7 +44,13 @@
+         <item name="android:actionBarItemBackground">@drawable/item_background_material_borderless_dark</item>
+     </style>
+-    <style name="PeopleTheme" parent="@android:style/Theme.Material.Light">
++    <style name="PeopleTheme1" parent="@android:style/Theme.Material.Light">
++        <item name="android:colorPrimary">@color/primary_color</item>
++        <item name="android:colorPrimaryDark">@color/primary_color_dark</item>
++        <item name="android:colorAccent">@color/primary_color</item>
++    </style>
++
++    <style name="PeopleTheme" parent="@style/PeopleTheme1">
+         <item name="android:actionBarStyle">@style/ContactsActionBarStyle</item>
+         <!-- Style for the tab bar (for the divider between tabs) -->
+         <item name="android:actionBarTabBarStyle">@style/ContactsActionBarTabBarStyle</item>
+@@ -62,9 +68,6 @@
+         <item name="android:icon">@android:color/transparent</item>
+         <item name="android:listViewStyle">@style/ListViewStyle</item>
+         <item name="android:windowBackground">@color/background_primary</item>
+-        <item name="android:colorPrimaryDark">@color/primary_color_dark</item>
+-        <item name="android:colorPrimary">@color/primary_color</item>
+-        <item name="android:colorAccent">@color/primary_color</item>
+         <item name="android:alertDialogTheme">@style/ContactsAlertDialogTheme</item>
+         <item name="list_item_height">?android:attr/listPreferredItemHeight</item>
+         <item name="activated_background">@drawable/list_item_activated_background</item>
+@@ -98,6 +101,8 @@
+         <item name="contact_browser_list_padding_right">0dip</item>
+         <item name="contact_browser_background">@color/background_primary</item>
+         <item name="list_item_text_indent">@dimen/contact_browser_list_item_text_indent</item>
++        <item name="list_item_name_text_color">@color/list_item_name_text_color</item>
++        <item name="list_item_name_text_size">16.0sp</item>
+         <!-- Favorites -->
+         <item name="favorites_padding_bottom">0dip</item>
+     </style>
+@@ -129,10 +134,10 @@
+     </style>
+     <style name="ContactPickerSearchTheme" parent="@style/PeopleTheme">
+-        <item name="android:textColorPrimary">@android:color/white</item>
++        <item name="android:textColorPrimary">@color/text_color_white</item>
+         <item name="android:textColorHint">?android:textColorHintInverse</item>
+         <item name="android:colorControlActivated">?android:textColorHintInverse</item>
+-        <item name="android:colorControlNormal">@android:color/white</item>
++        <item name="android:colorControlNormal">@color/white</item>
+     </style>
+     <!-- Text in the action bar at the top of the screen -->
+@@ -174,8 +179,8 @@
+     </style>
+     <style name="ContactsActionBarTheme" parent="@android:style/Theme.Material.Light">
+-        <item name="android:textColorHint">#CCCCCC</item>
+-        <item name="android:textColor">@android:color/black</item>
++        <item name="android:textColorHint">@color/contacts_action_bar_text_color_hint</item>
++        <item name="android:textColor">@color/contacts_action_bar_text_color</item>
+         <item name="android:popupMenuStyle">@android:style/Widget.Holo.Light.PopupMenu</item>
+         <item name="android:dropDownListViewStyle">@style/ListViewDropdownStyle</item>
+     </style>
+@@ -220,7 +225,7 @@
+     </style>
+     <style name="SectionDivider">
+-        <item name="android:background">#7e7e87</item>
++        <item name="android:background">@color/section_divider_background_color</item>
+         <item name="android:layout_height">1dip</item>
+         <item name="android:layout_width">match_parent</item>
+     </style>
+@@ -302,7 +307,7 @@
+         <item name="android:textSize">14sp</item>
+         <item name="android:textStyle">bold</item>
+         <item name="android:textAllCaps">true</item>
+-        <item name="android:textColor">#363636</item>
++        <item name="android:textColor">@color/edit_kind_text_appearance_text_color</item>
+         <item name="android:fontFamily">sans-serif</item>
+     </style>
+@@ -342,7 +347,7 @@
+     <style name="AccountTypeNameStyle">
+         <item name="android:textSize">10sp</item>
+-        <item name="android:textColor">#363636</item>
++        <item name="android:textColor">@color/account_type_name_text_color</item>
+         <item name="android:fontFamily">sans-serif</item>
+     </style>
+ </resources>
+-- 
+2.9.3
+
diff --git a/packages/apps/ContactsCommon/0001-Themes-Expose-hardcoded-contact-tile-text-colors.patch b/packages/apps/ContactsCommon/0001-Themes-Expose-hardcoded-contact-tile-text-colors.patch
new file mode 100644 (file)
index 0000000..f1febe4
--- /dev/null
@@ -0,0 +1,87 @@
+From 1f53add2077b2573b7d6b4d761b48f8505bb3701 Mon Sep 17 00:00:00 2001
+From: bgill55 <bricam55@gmail.com>
+Date: Sat, 24 Jan 2015 18:12:39 -0700
+Subject: [PATCH] Themes: Expose hardcoded contact tile text colors
+
+Naming conventions in line with @daveyannihilation's commit
+https://github.com/daveyannihilation/Theme-Ready-Google-Contacts/commit/dedf7925f52bec3896b138fa3d9f3754218abb2d
+
+Change-Id: I85aa4349ece8a9821d9443523b13f2234edd4af7
+---
+ res/layout/contact_tile_frequent.xml              |  2 +-
+ res/layout/contact_tile_starred.xml               |  2 +-
+ res/layout/contact_tile_starred_quick_contact.xml |  2 +-
+ res/values/projekt_colors.xml                     | 22 ++++++++++++++++++++++
+ 4 files changed, 25 insertions(+), 3 deletions(-)
+ create mode 100644 res/values/projekt_colors.xml
+
+diff --git a/res/layout/contact_tile_frequent.xml b/res/layout/contact_tile_frequent.xml
+index b1e83ce..8c718fb 100644
+--- a/res/layout/contact_tile_frequent.xml
++++ b/res/layout/contact_tile_frequent.xml
+@@ -38,7 +38,7 @@
+             android:id="@+id/contact_tile_name"
+             android:layout_width="match_parent"
+             android:layout_height="wrap_content"
+-            android:textColor="@android:color/black"
++            android:textColor="@color/frequent_contact_text_color"
+             android:textSize="@dimen/contact_browser_list_item_text_size"
+             android:singleLine="true"
+             android:fadingEdge="horizontal"
+diff --git a/res/layout/contact_tile_starred.xml b/res/layout/contact_tile_starred.xml
+index 777cc05..88aa1e8 100644
+--- a/res/layout/contact_tile_starred.xml
++++ b/res/layout/contact_tile_starred.xml
+@@ -43,7 +43,7 @@
+             android:layout_width="match_parent"
+             android:layout_height="wrap_content"
+             android:paddingTop="7dp"
+-            android:textColor="#202020"
++            android:textColor="@color/contact_tile_text_color"
+             android:textSize="@dimen/contact_browser_list_item_text_size"
+             android:singleLine="true"
+             android:fadingEdge="horizontal"
+diff --git a/res/layout/contact_tile_starred_quick_contact.xml b/res/layout/contact_tile_starred_quick_contact.xml
+index ecbe583..2134ffc 100644
+--- a/res/layout/contact_tile_starred_quick_contact.xml
++++ b/res/layout/contact_tile_starred_quick_contact.xml
+@@ -47,7 +47,7 @@
+                 android:id="@+id/contact_tile_name"
+                 android:layout_width="wrap_content"
+                 android:layout_height="wrap_content"
+-                android:textColor="@android:color/white"
++                android:textColor="@color/starred_quick_contact_name_text_color"
+                 android:textSize="16sp"
+                 android:singleLine="true"
+                 android:fadingEdge="horizontal"
+diff --git a/res/values/projekt_colors.xml b/res/values/projekt_colors.xml
+new file mode 100644
+index 0000000..a5d6bd3
+--- /dev/null
++++ b/res/values/projekt_colors.xml
+@@ -0,0 +1,22 @@
++<?xml version="1.0" encoding="utf-8"?>
++<!--
++    Copyright (c) 2016 Project Substratum
++
++    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.
++-->
++
++<resources>
++    <color name="frequent_contact_text_color">@android:color/black</color>
++    <color name="contact_tile_text_color">#ff202020</color>
++    <color name="starred_quick_contact_name_text_color">@android:color/white</color>
++</resources>
+-- 
+2.9.3
+
diff --git a/packages/apps/ExactCalculator/0001-Themes-Expose-hard-coded-background-in-java.patch b/packages/apps/ExactCalculator/0001-Themes-Expose-hard-coded-background-in-java.patch
new file mode 100644 (file)
index 0000000..4418b9a
--- /dev/null
@@ -0,0 +1,53 @@
+From 1ff4ff6484428cd4b80aacaf1a619c3e3ea78667 Mon Sep 17 00:00:00 2001
+From: Bryan Owens <djbryan3540@gmail.com>
+Date: Tue, 29 Dec 2015 22:25:10 -0600
+Subject: [PATCH 1/2] Themes: Expose hard coded background in java
+
+Change-Id: I30bade7c82f29767c6659c8a75bb402d245e49f1
+---
+ res/values/projekt_colors.xml                         | 19 +++++++++++++++++++
+ .../android/calculator2/CalculatorPadViewPager.java   |  2 +-
+ 2 files changed, 20 insertions(+), 1 deletion(-)
+ create mode 100644 res/values/projekt_colors.xml
+
+diff --git a/res/values/projekt_colors.xml b/res/values/projekt_colors.xml
+new file mode 100644
+index 0000000..9e15ffc
+--- /dev/null
++++ b/res/values/projekt_colors.xml
+@@ -0,0 +1,19 @@
++<?xml version="1.0" encoding="utf-8"?>
++<!--
++     Copyright (C) 2016 Projekt Substratum
++
++     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.
++-->
++<resources>
++    <color name="pad_view_pager_background">#ff000000</color>
++</resources>
+diff --git a/src/com/android/calculator2/CalculatorPadViewPager.java b/src/com/android/calculator2/CalculatorPadViewPager.java
+index d4520c5..5b02e4b 100644
+--- a/src/com/android/calculator2/CalculatorPadViewPager.java
++++ b/src/com/android/calculator2/CalculatorPadViewPager.java
+@@ -92,7 +92,7 @@ public class CalculatorPadViewPager extends ViewPager {
+         super(context, attrs);
+         setAdapter(mStaticPagerAdapter);
+-        setBackgroundColor(Color.BLACK);
++        setBackgroundColor(context.getColor(R.color.pad_view_pager_background));
+         setPageMargin(getResources().getDimensionPixelSize(R.dimen.pad_page_margin));
+         setPageTransformer(false, mPageTransformer);
+         addOnPageChangeListener(mOnPageChangeListener);
+-- 
+2.9.3
+
diff --git a/packages/apps/ExactCalculator/0002-Themes-Expose-all-elevations.patch b/packages/apps/ExactCalculator/0002-Themes-Expose-all-elevations.patch
new file mode 100644 (file)
index 0000000..b2a3277
--- /dev/null
@@ -0,0 +1,53 @@
+From b6b4eaf6df434964f0c3147d4363664096a5c947 Mon Sep 17 00:00:00 2001
+From: Abdulwahab Isam <abdoi94.iq@gmail.com>
+Date: Mon, 19 Sep 2016 18:42:06 +0300
+Subject: [PATCH 2/2] Themes: Expose all elevations
+
+Change-Id: I7d161c1fce17ac3b8a468e1597a3a1632433224b
+---
+ res/layout/display.xml     | 2 +-
+ res/values-port/styles.xml | 2 +-
+ res/values/dimens.xml      | 5 +++++
+ 3 files changed, 7 insertions(+), 2 deletions(-)
+ create mode 100644 res/values/dimens.xml
+
+diff --git a/res/layout/display.xml b/res/layout/display.xml
+index 638f0d2..043b531 100644
+--- a/res/layout/display.xml
++++ b/res/layout/display.xml
+@@ -21,7 +21,7 @@
+     android:layout_width="match_parent"
+     android:layout_height="wrap_content"
+     android:background="@color/display_background_color"
+-    android:elevation="4dip">
++    android:elevation="@dimen/display_elevation">
+     <Toolbar
+         android:id="@+id/toolbar"
+diff --git a/res/values-port/styles.xml b/res/values-port/styles.xml
+index a6d2013..9e7e1df 100644
+--- a/res/values-port/styles.xml
++++ b/res/values-port/styles.xml
+@@ -65,7 +65,7 @@
+     </style>
+     <style name="PadLayoutStyle.Advanced">
+-        <item name="android:elevation">4dip</item>
++        <item name="android:elevation">@dimen/pad_elevation</item>
+         <item name="android:paddingTop">12dip</item>
+         <item name="android:paddingBottom">20dip</item>
+         <item name="android:paddingStart">20dip</item>
+diff --git a/res/values/dimens.xml b/res/values/dimens.xml
+new file mode 100644
+index 0000000..065de72
+--- /dev/null
++++ b/res/values/dimens.xml
+@@ -0,0 +1,5 @@
++<?xml version="1.0" encoding="utf-8"?>
++<resources>
++    <dimen name="display_elevation">4.0dip</dimen>
++    <dimen name="pad_elevation">4.0dip</dimen>
++</resources>
+-- 
+2.9.3
+
diff --git a/packages/apps/PhoneCommon/0001-Themes-Make-dialpad-seperator-line-theme-able.patch b/packages/apps/PhoneCommon/0001-Themes-Make-dialpad-seperator-line-theme-able.patch
new file mode 100644 (file)
index 0000000..5896c2d
--- /dev/null
@@ -0,0 +1,69 @@
+From e1c858c77863d951ceab42213cb0df56d36b2ef6 Mon Sep 17 00:00:00 2001
+From: Thyrus11 <thyrus11@gmail.com>
+Date: Sat, 21 Feb 2015 10:41:01 +0100
+Subject: [PATCH] Themes: Make dialpad seperator line theme-able
+
+Using an existing value in colors is defined
+(dialpad_seperator_line_color) in AOSP, but overwritten in layout
+
+Changed the original commit's color name due to layout was #e3e3e3 and
+dialpad_seperator_line_color is #dadada
+
+Change-Id: Ia4b610185bac6c34e0f6bd1b00a6f0c0480d5ee0
+---
+ res/layout/dialpad_view_unthemed.xml |  4 ++--
+ res/values/projekt_colors.xml        | 20 ++++++++++++++++++++
+ 2 files changed, 22 insertions(+), 2 deletions(-)
+ create mode 100644 res/values/projekt_colors.xml
+
+diff --git a/res/layout/dialpad_view_unthemed.xml b/res/layout/dialpad_view_unthemed.xml
+index 7bc4536..3fcf0da 100644
+--- a/res/layout/dialpad_view_unthemed.xml
++++ b/res/layout/dialpad_view_unthemed.xml
+@@ -56,7 +56,7 @@
+         <View
+             android:layout_width="match_parent"
+             android:layout_height="1dp"
+-            android:background="#e3e3e3" />
++            android:background="@color/dialpad_separator_color" />
+     </LinearLayout>
+@@ -148,7 +148,7 @@
+     <View
+         android:layout_width="match_parent"
+         android:layout_height="1dp"
+-        android:background="#e3e3e3" />
++        android:background="@color/dialpad_separator_color" />
+     <Space
+         android:layout_width="match_parent"
+diff --git a/res/values/projekt_colors.xml b/res/values/projekt_colors.xml
+new file mode 100644
+index 0000000..468ca5c
+--- /dev/null
++++ b/res/values/projekt_colors.xml
+@@ -0,0 +1,20 @@
++<?xml version="1.0" encoding="utf-8"?>
++<!--
++    Copyright (c) 2016 Project Substratum
++
++    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.
++-->
++
++<resources>
++    <color name="dialpad_separator_color">#e3e3e3</color>
++</resources>
+-- 
+2.9.3
+
diff --git a/packages/apps/Settings/0001-Settings-Expose-dashboard-category-and-tile-color.patch b/packages/apps/Settings/0001-Settings-Expose-dashboard-category-and-tile-color.patch
new file mode 100644 (file)
index 0000000..282a0e7
--- /dev/null
@@ -0,0 +1,127 @@
+From 121e37c0c3fe7cc8ac35793777e247ccb812de6b Mon Sep 17 00:00:00 2001
+From: Ivan Iskandar <iiiiskandar14@gmail.com>
+Date: Sun, 18 Sep 2016 10:12:05 +0700
+Subject: [PATCH 01/15] Settings: Expose dashboard category and tile color
+
+Change-Id: Ia9c83eb9b8360923ed52e03641b4179681a80330
+---
+ res/drawable/dashboard_category_title_bg.xml | 21 +++++++++++++++++++++
+ res/drawable/dashboard_tile_bg.xml           | 21 +++++++++++++++++++++
+ res/drawable/selectable_card.xml             |  4 ++--
+ res/layout/dashboard_category.xml            |  2 +-
+ res/values/projekt_colors.xml                | 21 +++++++++++++++++++++
+ 5 files changed, 66 insertions(+), 3 deletions(-)
+ create mode 100644 res/drawable/dashboard_category_title_bg.xml
+ create mode 100644 res/drawable/dashboard_tile_bg.xml
+ create mode 100644 res/values/projekt_colors.xml
+
+diff --git a/res/drawable/dashboard_category_title_bg.xml b/res/drawable/dashboard_category_title_bg.xml
+new file mode 100644
+index 0000000..0e9604d
+--- /dev/null
++++ b/res/drawable/dashboard_category_title_bg.xml
+@@ -0,0 +1,21 @@
++<?xml version="1.0" encoding="utf-8"?>
++<!--
++    Copyright (c) 2016 Project Substratum
++
++    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.
++-->
++
++<shape xmlns:android="http://schemas.android.com/apk/res/android"
++       android:shape="rectangle">
++   <solid android:color="@color/dashboard_category_title_background" />
++</shape>
+diff --git a/res/drawable/dashboard_tile_bg.xml b/res/drawable/dashboard_tile_bg.xml
+new file mode 100644
+index 0000000..61d113d
+--- /dev/null
++++ b/res/drawable/dashboard_tile_bg.xml
+@@ -0,0 +1,21 @@
++<?xml version="1.0" encoding="utf-8"?>
++<!--
++    Copyright (c) 2016 Project Substratum
++
++    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.
++-->
++
++<shape xmlns:android="http://schemas.android.com/apk/res/android"
++       android:shape="rectangle">
++   <solid android:color="@color/selectable_card_background" />
++</shape>
+diff --git a/res/drawable/selectable_card.xml b/res/drawable/selectable_card.xml
+index df9ddb1..74018b2 100644
+--- a/res/drawable/selectable_card.xml
++++ b/res/drawable/selectable_card.xml
+@@ -18,5 +18,5 @@
+ <ripple
+     xmlns:android="http://schemas.android.com/apk/res/android"
+     android:color="?android:attr/colorControlHighlight">
+-    <item android:drawable="@color/card_background"/>
+-</ripple>
+\ No newline at end of file
++    <item android:drawable="@drawable/dashboard_tile_bg"/>
++</ripple>
+diff --git a/res/layout/dashboard_category.xml b/res/layout/dashboard_category.xml
+index 2e5dd5c..7836644 100644
+--- a/res/layout/dashboard_category.xml
++++ b/res/layout/dashboard_category.xml
+@@ -20,7 +20,7 @@
+     android:layout_height="@dimen/dashboard_category_height"
+     android:orientation="vertical"
+     android:paddingBottom="8dip"
+-    android:background="@color/card_background">
++    android:background="@drawable/dashboard_category_title_bg">
+     <TextView android:id="@android:id/title"
+         android:layout_width="match_parent"
+diff --git a/res/values/projekt_colors.xml b/res/values/projekt_colors.xml
+new file mode 100644
+index 0000000..60419bd
+--- /dev/null
++++ b/res/values/projekt_colors.xml
+@@ -0,0 +1,21 @@
++<?xml version="1.0" encoding="utf-8"?>
++<!--
++    Copyright (c) 2016 Projekt Substratum
++
++    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.
++-->
++<resources>
++    <!-- Dashboard category and tile background -->
++    <color name="dashboard_category_title_background">@color/card_background</color>
++    <color name="selectable_card_background">@color/card_background</color>
++</resources>
+-- 
+2.9.3
+
diff --git a/packages/apps/Settings/0002-Settings-Expose-condition-card-colors.patch b/packages/apps/Settings/0002-Settings-Expose-condition-card-colors.patch
new file mode 100644 (file)
index 0000000..8a0c90c
--- /dev/null
@@ -0,0 +1,118 @@
+From fde00a322179ec03d9232001da19a3284e65b807 Mon Sep 17 00:00:00 2001
+From: daveyannihilation <daveyannihilation@hotmail.com>
+Date: Tue, 20 Sep 2016 23:35:04 +0800
+Subject: [PATCH 02/15] Settings: Expose condition card colors
+
+PS2 Change to the actual colors in fwb that these attrs call to.
+
+@nathanchance edit: updated for Nougat 7.1
+
+Change-Id: I8de2e2a4f79a28c0fe1f025b4d23937931fe293a
+---
+ res/layout/condition_card.xml | 18 +++++++++---------
+ res/values/projekt_colors.xml |  9 +++++++++
+ 2 files changed, 18 insertions(+), 9 deletions(-)
+
+diff --git a/res/layout/condition_card.xml b/res/layout/condition_card.xml
+index 7c9e46d..88277c2 100644
+--- a/res/layout/condition_card.xml
++++ b/res/layout/condition_card.xml
+@@ -1,6 +1,6 @@
+ <?xml version="1.0" encoding="utf-8"?>
+ <!--
+-     Copyright (C) 2015 The Android Open Source Project
++     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.
+@@ -25,7 +25,7 @@
+         android:layout_width="match_parent"
+         android:layout_height="wrap_content"
+         android:orientation="vertical"
+-        android:background="?android:attr/colorAccent"
++        android:background="@color/condition_card_background_color"
+         android:elevation="2dp"
+         android:clickable="true"
+         android:focusable="true">
+@@ -44,7 +44,7 @@
+                 android:layout_height="wrap_content"
+                 android:layout_marginStart="16dp"
+                 android:layout_marginEnd="32dp"
+-                android:tint="?android:attr/textColorPrimaryInverse" />
++                android:tint="@color/condition_card_icon_color" />
+             <TextView
+                 android:id="@android:id/title"
+@@ -52,14 +52,14 @@
+                 android:layout_height="wrap_content"
+                 android:layout_weight="1"
+                 android:textAppearance="?android:attr/textAppearanceMedium"
+-                android:textColor="?android:attr/textColorPrimaryInverse" />
++                android:textColor="@color/condition_card_title_text_color" />
+             <ImageView
+                 android:id="@+id/expand_indicator"
+                 android:layout_width="wrap_content"
+                 android:layout_height="match_parent"
+                 android:padding="16dp"
+-                android:tint="?android:attr/textColorPrimaryInverse"/>
++                android:tint="@color/condition_card_expander_color" />
+         </LinearLayout>
+@@ -81,14 +81,14 @@
+                 android:paddingBottom="16dp"
+                 android:textAppearance="?android:attr/textAppearanceListItemSecondary"
+                 android:alpha=".7"
+-                android:textColor="?android:attr/textColorPrimaryInverse" />
++                android:textColor="@color/condition_card_summary_text_color" />
+             <!-- TODO: Better background -->
+             <View
+                 android:id="@+id/divider"
+                 android:layout_width="match_parent"
+                 android:layout_height=".25dp"
+-                android:background="@android:color/white" />
++                android:background="@color/condition_card_divider_color" />
+             <com.android.internal.widget.ButtonBarLayout
+                 android:id="@+id/buttonBar"
+@@ -107,7 +107,7 @@
+                     android:paddingStart="0dp"
+                     android:alpha=".8"
+                     android:textAlignment="viewStart"
+-                    android:textColor="?android:attr/textColorPrimaryInverse"
++                    android:textColor="@color/condition_card_first_button_text_color"
+                     style="?android:attr/buttonBarButtonStyle" />
+                 <Button
+@@ -117,7 +117,7 @@
+                     android:layout_weight="1"
+                     android:alpha=".8"
+                     android:textAlignment="viewStart"
+-                    android:textColor="?android:attr/textColorPrimaryInverse"
++                    android:textColor="@color/condition_card_second_button_text_color"
+                     style="?android:attr/buttonBarButtonStyle" />
+             </com.android.internal.widget.ButtonBarLayout>
+diff --git a/res/values/projekt_colors.xml b/res/values/projekt_colors.xml
+index 60419bd..9340830 100644
+--- a/res/values/projekt_colors.xml
++++ b/res/values/projekt_colors.xml
+@@ -18,4 +18,13 @@
+     <!-- Dashboard category and tile background -->
+     <color name="dashboard_category_title_background">@color/card_background</color>
+     <color name="selectable_card_background">@color/card_background</color>
++    <!-- Condition card colors -->
++    <color name="condition_card_background_color">@*android:color/accent_device_default_light</color>
++    <color name="condition_card_icon_color">@*android:color/primary_text_default_material_dark</color>
++    <color name="condition_card_title_text_color">@*android:color/primary_text_default_material_dark</color>
++    <color name="condition_card_expander_color">@*android:color/primary_text_default_material_dark</color>
++    <color name="condition_card_summary_text_color">@*android:color/primary_text_default_material_dark</color>
++    <color name="condition_card_divider_color">@android:color/white</color>
++    <color name="condition_card_first_button_text_color">@*android:color/primary_text_default_material_dark</color>
++    <color name="condition_card_second_button_text_color">@*android:color/primary_text_default_material_dark</color>
+ </resources>
+-- 
+2.9.3
+
diff --git a/packages/apps/Settings/0003-Settings-Expose-storage-summary-text.patch b/packages/apps/Settings/0003-Settings-Expose-storage-summary-text.patch
new file mode 100644 (file)
index 0000000..52d06de
--- /dev/null
@@ -0,0 +1,124 @@
+From 81f82e54029690d530fc32b31a2a2e77d80bbf18 Mon Sep 17 00:00:00 2001
+From: George G <kreach3r@users.noreply.github.com>
+Date: Tue, 3 Nov 2015 23:57:22 +0200
+Subject: [PATCH 03/15] Settings: Expose storage summary text
+
+This commit simply exposes the storage summary text hardcodes to allow
+themers to theme them without issue.
+
+@nathanchance edit: updated for Nougat 7.1
+
+Change-Id: I10c124b6a1516a1be7ac234c1dd7dbe2cbe05bb2
+---
+ res/layout/storage_summary.xml                                    | 2 +-
+ res/values/projekt_colors.xml                                     | 2 ++
+ src/com/android/settings/deviceinfo/StorageSummaryPreference.java | 8 +++++++-
+ src/com/android/settings/deviceinfo/StorageVolumePreference.java  | 8 +++++++-
+ 4 files changed, 17 insertions(+), 3 deletions(-)
+
+diff --git a/res/layout/storage_summary.xml b/res/layout/storage_summary.xml
+index 7bc51df..c01d2da 100644
+--- a/res/layout/storage_summary.xml
++++ b/res/layout/storage_summary.xml
+@@ -33,7 +33,7 @@
+         android:singleLine="true"
+         android:textAlignment="viewStart"
+         android:textAppearance="@android:style/TextAppearance.Material.Subhead"
+-        android:textColor="?android:attr/colorAccent"
++        android:textColor="@color/storage_summary_title_color"
+         android:textSize="36sp"
+         android:ellipsize="marquee"
+         android:fadingEdge="horizontal" />
+diff --git a/res/values/projekt_colors.xml b/res/values/projekt_colors.xml
+index 9340830..fb0d99e 100644
+--- a/res/values/projekt_colors.xml
++++ b/res/values/projekt_colors.xml
+@@ -27,4 +27,6 @@
+     <color name="condition_card_divider_color">@android:color/white</color>
+     <color name="condition_card_first_button_text_color">@*android:color/primary_text_default_material_dark</color>
+     <color name="condition_card_second_button_text_color">@*android:color/primary_text_default_material_dark</color>
++    <!-- Storage Summary Title -->
++    <color name="storage_summary_title_color">@*android:color/accent_device_default_light</color>
+ </resources>
+diff --git a/src/com/android/settings/deviceinfo/StorageSummaryPreference.java b/src/com/android/settings/deviceinfo/StorageSummaryPreference.java
+index e1cf774..2741798 100644
+--- a/src/com/android/settings/deviceinfo/StorageSummaryPreference.java
++++ b/src/com/android/settings/deviceinfo/StorageSummaryPreference.java
+@@ -21,6 +21,7 @@ import android.graphics.Color;
+ import android.support.v7.preference.Preference;
+ import android.support.v7.preference.PreferenceViewHolder;
+ import android.view.View;
++import android.util.TypedValue;
+ import android.widget.ProgressBar;
+ import android.widget.TextView;
+@@ -28,12 +29,17 @@ import com.android.settings.R;
+ public class StorageSummaryPreference extends Preference {
+     private int mPercent = -1;
++    private int mSecondaryColor;
+     public StorageSummaryPreference(Context context) {
+         super(context);
+         setLayoutResource(R.layout.storage_summary);
+         setEnabled(false);
++
++        TypedValue typedValue = new TypedValue();
++        context.getTheme().resolveAttribute(android.R.attr.textColorSecondary, typedValue, true);
++        mSecondaryColor = context.getResources().getColor(typedValue.resourceId);
+     }
+     public void setPercent(int percent) {
+@@ -52,7 +58,7 @@ public class StorageSummaryPreference extends Preference {
+         }
+         final TextView summary = (TextView) view.findViewById(android.R.id.summary);
+-        summary.setTextColor(Color.parseColor("#8a000000"));
++        summary.setTextColor(mSecondaryColor);
+         super.onBindViewHolder(view);
+     }
+diff --git a/src/com/android/settings/deviceinfo/StorageVolumePreference.java b/src/com/android/settings/deviceinfo/StorageVolumePreference.java
+index 6389075..9d3c7dc 100644
+--- a/src/com/android/settings/deviceinfo/StorageVolumePreference.java
++++ b/src/com/android/settings/deviceinfo/StorageVolumePreference.java
+@@ -27,6 +27,7 @@ import android.support.v7.preference.PreferenceViewHolder;
+ import android.text.format.Formatter;
+ import android.view.View;
+ import android.view.View.OnClickListener;
++import android.util.TypedValue;
+ import android.widget.ImageView;
+ import android.widget.ProgressBar;
+@@ -44,6 +45,7 @@ public class StorageVolumePreference extends Preference {
+     private final VolumeInfo mVolume;
+     private int mColor;
++    private int mSecondaryColor;
+     private int mUsedPercent = -1;
+     // TODO: ideally, VolumeInfo should have a total physical size.
+@@ -54,6 +56,10 @@ public class StorageVolumePreference extends Preference {
+         mVolume = volume;
+         mColor = color;
++        TypedValue typedValue = new TypedValue();
++        context.getTheme().resolveAttribute(android.R.attr.textColorSecondary, typedValue, true);
++        mSecondaryColor = context.getResources().getColor(typedValue.resourceId);
++
+         setLayoutResource(R.layout.storage_volume);
+         setKey(volume.getId());
+@@ -107,7 +113,7 @@ public class StorageVolumePreference extends Preference {
+     public void onBindViewHolder(PreferenceViewHolder view) {
+         final ImageView unmount = (ImageView) view.findViewById(R.id.unmount);
+         if (unmount != null) {
+-            unmount.setImageTintList(ColorStateList.valueOf(Color.parseColor("#8a000000")));
++            unmount.setImageTintList(ColorStateList.valueOf(mSecondaryColor));
+             unmount.setOnClickListener(mUnmountListener);
+         }
+-- 
+2.9.3
+
diff --git a/packages/apps/Settings/0004-Settings-Expose-gesture-settings-switchbar.patch b/packages/apps/Settings/0004-Settings-Expose-gesture-settings-switchbar.patch
new file mode 100644 (file)
index 0000000..c742a8f
--- /dev/null
@@ -0,0 +1,44 @@
+From f4e4000d09321bf4425201b039d5c2054d2fadad Mon Sep 17 00:00:00 2001
+From: daveyannihilation <daveyannihilation@hotmail.com>
+Date: Fri, 25 Nov 2016 15:20:04 -0700
+Subject: [PATCH 04/15] Settings: Expose gesture settings switchbar
+
+In Settings > Gestures, there is a hardcoded white background above each switch. Link to a colors.xml value instead.
+
+Commit written by @nathanchance, idea by @daveyannihilation
+
+Signed-off-by: Nathan Chancellor <natechancellor@gmail.com>
+Change-Id: I9857c10ee4e54d27ca1287c80a7d8e420adc9a61
+---
+ res/layout/gesture_preference.xml | 2 +-
+ res/values/projekt_colors.xml     | 3 +++
+ 2 files changed, 4 insertions(+), 1 deletion(-)
+
+diff --git a/res/layout/gesture_preference.xml b/res/layout/gesture_preference.xml
+index ccbc42a..9fcd14f 100644
+--- a/res/layout/gesture_preference.xml
++++ b/res/layout/gesture_preference.xml
+@@ -31,7 +31,7 @@
+         android:paddingBottom="16dp"
+         android:paddingStart="?android:attr/listPreferredItemPaddingStart"
+         android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
+-        android:background="@android:color/white">
++        android:background="@color/gestures_switchbar_color">
+         <TextView
+             android:id="@android:id/title"
+diff --git a/res/values/projekt_colors.xml b/res/values/projekt_colors.xml
+index fb0d99e..00252332 100644
+--- a/res/values/projekt_colors.xml
++++ b/res/values/projekt_colors.xml
+@@ -29,4 +29,7 @@
+     <color name="condition_card_second_button_text_color">@*android:color/primary_text_default_material_dark</color>
+     <!-- Storage Summary Title -->
+     <color name="storage_summary_title_color">@*android:color/accent_device_default_light</color>
++    <!-- Gesture settings switchbar background -->
++    <color name="gestures_switchbar_color">@android:color/white</color>
++
+ </resources>
+-- 
+2.9.3
+
diff --git a/packages/apps/Settings/0005-Settings-Expose-storage-icon-colors.patch b/packages/apps/Settings/0005-Settings-Expose-storage-icon-colors.patch
new file mode 100644 (file)
index 0000000..360da46
--- /dev/null
@@ -0,0 +1,121 @@
+From d4beb2fe4bbf5105446f23735674a48a0bff5b59 Mon Sep 17 00:00:00 2001
+From: SpiritCroc <spiritcroc@gmail.com>
+Date: Thu, 8 Dec 2016 20:40:16 +0100
+Subject: [PATCH 05/15] Settings: Expose storage icon colors
+
+Change-Id: I97ce427f0683791670f1bbf82eaef66152fe4961
+---
+ res/values/projekt_colors.xml                      |  9 +++++-
+ .../settings/deviceinfo/StorageSettings.java       | 32 ++++++++++++++--------
+ .../deviceinfo/StorageVolumePreference.java        |  2 +-
+ 3 files changed, 29 insertions(+), 14 deletions(-)
+
+diff --git a/res/values/projekt_colors.xml b/res/values/projekt_colors.xml
+index 00252332..bb6099b 100644
+--- a/res/values/projekt_colors.xml
++++ b/res/values/projekt_colors.xml
+@@ -31,5 +31,12 @@
+     <color name="storage_summary_title_color">@*android:color/accent_device_default_light</color>
+     <!-- Gesture settings switchbar background -->
+     <color name="gestures_switchbar_color">@android:color/white</color>
+-
++    <!-- Storage icons -->
++    <color name="storage_public">#ff9e9e9e</color>
++    <color name="storage_warning">#fff4511e</color>
++    <color name="storage_private_1">#ff26a69a</color>
++    <color name="storage_private_2">#ffab47bc</color>
++    <color name="storage_private_3">#fff2a600</color>
++    <color name="storage_private_4">#ffec407a</color>
++    <color name="storage_private_5">#ffc0ca33</color>
+ </resources>
+diff --git a/src/com/android/settings/deviceinfo/StorageSettings.java b/src/com/android/settings/deviceinfo/StorageSettings.java
+index 7757efc..b01013b 100644
+--- a/src/com/android/settings/deviceinfo/StorageSettings.java
++++ b/src/com/android/settings/deviceinfo/StorageSettings.java
+@@ -24,6 +24,7 @@ import android.app.Fragment;
+ import android.content.Context;
+ import android.content.DialogInterface;
+ import android.content.Intent;
++import android.content.res.Resources ;
+ import android.graphics.Color;
+ import android.graphics.drawable.Drawable;
+ import android.os.AsyncTask;
+@@ -73,16 +74,22 @@ public class StorageSettings extends SettingsPreferenceFragment implements Index
+     private static final String TAG_VOLUME_UNMOUNTED = "volume_unmounted";
+     private static final String TAG_DISK_INIT = "disk_init";
+-    static final int COLOR_PUBLIC = Color.parseColor("#ff9e9e9e");
+-    static final int COLOR_WARNING = Color.parseColor("#fff4511e");
++    static int getColorPublic(Resources resources) {
++        return resources.getColor(R.color.storage_public);
++    }
++    static int getColorWarning(Resources resources) {
++        return resources.getColor(R.color.storage_warning);
++    }
+-    static final int[] COLOR_PRIVATE = new int[] {
+-            Color.parseColor("#ff26a69a"),
+-            Color.parseColor("#ffab47bc"),
+-            Color.parseColor("#fff2a600"),
+-            Color.parseColor("#ffec407a"),
+-            Color.parseColor("#ffc0ca33"),
+-    };
++    static int[] getColorPrivate(Resources resources) {
++        return new int[] {
++                resources.getColor(R.color.storage_private_1),
++                resources.getColor(R.color.storage_private_2),
++                resources.getColor(R.color.storage_private_3),
++                resources.getColor(R.color.storage_private_4),
++                resources.getColor(R.color.storage_private_5),
++        };
++    }
+     private StorageManager mStorageManager;
+@@ -165,10 +172,11 @@ public class StorageSettings extends SettingsPreferenceFragment implements Index
+         final List<VolumeInfo> volumes = mStorageManager.getVolumes();
+         Collections.sort(volumes, VolumeInfo.getDescriptionComparator());
++        int[] colorPrivate = getColorPrivate(getResources());
+         for (VolumeInfo vol : volumes) {
+             if (vol.getType() == VolumeInfo.TYPE_PRIVATE) {
+                 final long volumeTotalBytes = getTotalSize(vol);
+-                final int color = COLOR_PRIVATE[privateCount++ % COLOR_PRIVATE.length];
++                final int color = colorPrivate[privateCount++ % colorPrivate.length];
+                 mInternalCategory.addPreference(
+                         new StorageVolumePreference(context, vol, color, volumeTotalBytes));
+                 if (vol.isMountedReadable()) {
+@@ -178,7 +186,7 @@ public class StorageSettings extends SettingsPreferenceFragment implements Index
+                 }
+             } else if (vol.getType() == VolumeInfo.TYPE_PUBLIC) {
+                 mExternalCategory.addPreference(
+-                        new StorageVolumePreference(context, vol, COLOR_PUBLIC, 0));
++                        new StorageVolumePreference(context, vol, getColorPublic(getResources()), 0));
+             }
+         }
+@@ -190,7 +198,7 @@ public class StorageSettings extends SettingsPreferenceFragment implements Index
+                 // TODO: add actual storage type to record
+                 final Drawable icon = context.getDrawable(R.drawable.ic_sim_sd);
+                 icon.mutate();
+-                icon.setTint(COLOR_PUBLIC);
++                icon.setTint(getColorPublic(getResources()));
+                 final Preference pref = new Preference(context);
+                 pref.setKey(rec.getFsUuid());
+diff --git a/src/com/android/settings/deviceinfo/StorageVolumePreference.java b/src/com/android/settings/deviceinfo/StorageVolumePreference.java
+index 9d3c7dc..8a2780a 100644
+--- a/src/com/android/settings/deviceinfo/StorageVolumePreference.java
++++ b/src/com/android/settings/deviceinfo/StorageVolumePreference.java
+@@ -89,7 +89,7 @@ public class StorageVolumePreference extends Preference {
+             }
+             if (freeBytes < mStorageManager.getStorageLowBytes(path)) {
+-                mColor = StorageSettings.COLOR_WARNING;
++                mColor = StorageSettings.getColorWarning(context.getResources());
+                 icon = context.getDrawable(R.drawable.ic_warning_24dp);
+             }
+-- 
+2.9.3
+
diff --git a/packages/apps/Settings/0006-Settings-Expose-LinearColorBar-default-colors.patch b/packages/apps/Settings/0006-Settings-Expose-LinearColorBar-default-colors.patch
new file mode 100644 (file)
index 0000000..93b9c32
--- /dev/null
@@ -0,0 +1,68 @@
+From c52f89f21eade3f277963374f3c5cf34cad3bdd7 Mon Sep 17 00:00:00 2001
+From: SpiritCroc <spiritcroc@gmail.com>
+Date: Fri, 9 Dec 2016 12:47:54 +0100
+Subject: [PATCH 06/15] Settings: Expose LinearColorBar default colors
+
+Change-Id: I8e63f10f9e4079d1a03eaece18e8343102f4a755
+---
+ res/values/projekt_colors.xml                             |  5 +++++
+ src/com/android/settings/applications/LinearColorBar.java | 10 +++++++---
+ 2 files changed, 12 insertions(+), 3 deletions(-)
+
+diff --git a/res/values/projekt_colors.xml b/res/values/projekt_colors.xml
+index bb6099b..e1977f9 100644
+--- a/res/values/projekt_colors.xml
++++ b/res/values/projekt_colors.xml
+@@ -39,4 +39,9 @@
+     <color name="storage_private_3">#fff2a600</color>
+     <color name="storage_private_4">#ffec407a</color>
+     <color name="storage_private_5">#ffc0ca33</color>
++
++    <!-- Linear color bar -->
++    <color name="linear_color_bar_left">@*android:color/accent_device_default_light</color>
++    <color name="linear_color_bar_middle">@*android:color/accent_device_default_light</color>
++    <color name="linear_color_bar_right">#ffced7db</color>
+ </resources>
+diff --git a/src/com/android/settings/applications/LinearColorBar.java b/src/com/android/settings/applications/LinearColorBar.java
+index b637671..b0b8b82 100644
+--- a/src/com/android/settings/applications/LinearColorBar.java
++++ b/src/com/android/settings/applications/LinearColorBar.java
+@@ -15,6 +15,7 @@ import android.util.DisplayMetrics;
+ import android.view.MotionEvent;
+ import android.widget.LinearLayout;
+ import com.android.settings.Utils;
++import com.android.settings.R;
+ public class LinearColorBar extends LinearLayout {
+@@ -28,7 +29,7 @@ public class LinearColorBar extends LinearLayout {
+     private int mLeftColor;
+     private int mMiddleColor;
+-    private int mRightColor = RIGHT_COLOR;
++    private int mRightColor;
+     private boolean mShowIndicator = true;
+     private boolean mShowingGreen;
+@@ -70,7 +71,10 @@ public class LinearColorBar extends LinearLayout {
+                 ? 2 : 1;
+         mEdgeGradientPaint.setStrokeWidth(mLineWidth);
+         mEdgeGradientPaint.setAntiAlias(true);
+-        mLeftColor = mMiddleColor = Utils.getColorAccent(context);
++
++        mLeftColor = context.getResources().getColor(R.color.linear_color_bar_left);
++        mMiddleColor = context.getResources().getColor(R.color.linear_color_bar_middle);
++        mRightColor = context.getResources().getColor(R.color.linear_color_bar_right);
+     }
+     public void setOnRegionTappedListener(OnRegionTappedListener listener) {
+@@ -271,4 +275,4 @@ public class LinearColorBar extends LinearLayout {
+             canvas.drawRect(mRect, mPaint);
+         }
+     }
+-}
+\ No newline at end of file
++}
+-- 
+2.9.3
+
diff --git a/packages/apps/Settings/0007-OMS7-N-Apps-show-hide-Substratum-overlays-2-2.patch b/packages/apps/Settings/0007-OMS7-N-Apps-show-hide-Substratum-overlays-2-2.patch
new file mode 100644 (file)
index 0000000..8a03910
--- /dev/null
@@ -0,0 +1,159 @@
+From 952537ca209dd20a4bc98ea192aa78032a665b10 Mon Sep 17 00:00:00 2001
+From: George G <kreach3r@users.noreply.github.com>
+Date: Mon, 4 Jul 2016 12:07:11 +0300
+Subject: [PATCH 07/15] OMS7-N: Apps: show/hide Substratum overlays [2/2]
+
+Default behavior is "hidden" substratum overlays,
+as they pollute the screen, and you can't notice the
+actual apps.
+So we follow the current trend set by Android with
+"show system" and make it "show overlays" on default.
+
+Change-Id: I1cd4d4d3acd767a87fe402c325a872a4c04e0438
+---
+ res/menu/manage_apps.xml                           |  8 +++++++
+ res/values/projekt_strings.xml                     | 23 ++++++++++++++++++
+ .../settings/applications/ManageApplications.java  | 27 +++++++++++++++++++++-
+ 3 files changed, 57 insertions(+), 1 deletion(-)
+ create mode 100644 res/values/projekt_strings.xml
+
+diff --git a/res/menu/manage_apps.xml b/res/menu/manage_apps.xml
+index 02ee2bb..67f09e5 100644
+--- a/res/menu/manage_apps.xml
++++ b/res/menu/manage_apps.xml
+@@ -30,6 +30,14 @@
+         android:title="@string/menu_hide_system"
+         android:showAsAction="never" />
+     <item
++        android:id="@+id/show_substratum"
++        android:title="@string/menu_show_substratum"
++        android:showAsAction="never" />
++    <item
++        android:id="@+id/hide_substratum"
++        android:title="@string/menu_hide_substratum"
++        android:showAsAction="never" />
++    <item
+         android:id="@+id/sort_order_alpha"
+         android:title="@string/sort_order_alpha"
+         android:showAsAction="never" />
+diff --git a/res/values/projekt_strings.xml b/res/values/projekt_strings.xml
+new file mode 100644
+index 0000000..177e78c
+--- /dev/null
++++ b/res/values/projekt_strings.xml
+@@ -0,0 +1,23 @@
++<?xml version="1.0" encoding="utf-8"?>
++<!-- Copyright (c) 2016 Project Substratum
++
++     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.
++-->
++<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
++
++   <!-- Manage applications: show substratum overlays -->
++   <string name="filter_substratum_apps">Substratum overlays</string>
++   <string name="menu_show_substratum">Show overlays</string>
++   <string name="menu_hide_substratum">Hide overlays</string>
++
++</resources>
+diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java
+index 8b1dd27..070754a 100644
+--- a/src/com/android/settings/applications/ManageApplications.java
++++ b/src/com/android/settings/applications/ManageApplications.java
+@@ -107,6 +107,7 @@ public class ManageApplications extends InstrumentedFragment
+     private static final String EXTRA_SORT_ORDER = "sortOrder";
+     private static final String EXTRA_SHOW_SYSTEM = "showSystem";
++    private static final String EXTRA_SHOW_SUBSTRATUM = "showSubstratum";
+     private static final String EXTRA_HAS_ENTRIES = "hasEntries";
+     private static final String EXTRA_HAS_BRIDGE = "hasBridge";
+@@ -138,6 +139,7 @@ public class ManageApplications extends InstrumentedFragment
+     public static final int FILTER_APPS_USAGE_ACCESS = 13;
+     public static final int FILTER_APPS_WITH_OVERLAY = 14;
+     public static final int FILTER_APPS_WRITE_SETTINGS = 15;
++    public static final int FILTER_APPS_SUBSTRATUM = 16;
+     // This is the string labels for the filter modes above, the order must be kept in sync.
+     public static final int[] FILTER_LABELS = new int[]{
+@@ -187,6 +189,9 @@ public class ManageApplications extends InstrumentedFragment
+     // whether showing system apps.
+     private boolean mShowSystem;
++    // whether showing substratum overlays.
++    private boolean mShowSubstratum;
++
+     private ApplicationsState mApplicationsState;
+     public int mListType;
+@@ -276,6 +281,7 @@ public class ManageApplications extends InstrumentedFragment
+         if (savedInstanceState != null) {
+             mSortOrder = savedInstanceState.getInt(EXTRA_SORT_ORDER, mSortOrder);
+             mShowSystem = savedInstanceState.getBoolean(EXTRA_SHOW_SYSTEM, mShowSystem);
++            mShowSubstratum = savedInstanceState.getBoolean(EXTRA_SHOW_SUBSTRATUM, mShowSubstratum);
+         }
+         mInvalidSizeStr = getActivity().getText(R.string.invalid_size_value);
+@@ -440,6 +446,7 @@ public class ManageApplications extends InstrumentedFragment
+         mResetAppsHelper.onSaveInstanceState(outState);
+         outState.putInt(EXTRA_SORT_ORDER, mSortOrder);
+         outState.putBoolean(EXTRA_SHOW_SYSTEM, mShowSystem);
++        outState.putBoolean(EXTRA_SHOW_SUBSTRATUM, mShowSubstratum);
+         outState.putBoolean(EXTRA_HAS_ENTRIES, mApplications.mHasReceivedLoadEntries);
+         outState.putBoolean(EXTRA_HAS_BRIDGE, mApplications.mHasReceivedBridgeCallback);
+     }
+@@ -558,6 +565,11 @@ public class ManageApplications extends InstrumentedFragment
+                 && mListType != LIST_TYPE_HIGH_POWER);
+         mOptionsMenu.findItem(R.id.hide_system).setVisible(mShowSystem
+                 && mListType != LIST_TYPE_HIGH_POWER);
++
++        mOptionsMenu.findItem(R.id.show_substratum).setVisible(!mShowSubstratum
++                && mListType != LIST_TYPE_HIGH_POWER);
++        mOptionsMenu.findItem(R.id.hide_substratum).setVisible(mShowSubstratum
++                && mListType != LIST_TYPE_HIGH_POWER);
+     }
+     @Override
+@@ -577,6 +589,11 @@ public class ManageApplications extends InstrumentedFragment
+                 mShowSystem = !mShowSystem;
+                 mApplications.rebuild(false);
+                 break;
++            case R.id.show_substratum:
++            case R.id.hide_substratum:
++                mShowSubstratum = !mShowSubstratum;
++                mApplications.rebuild(false);
++                break;
+             case R.id.reset_app_preferences:
+                 mResetAppsHelper.buildResetDialog();
+                 return true;
+@@ -865,9 +882,17 @@ public class ManageApplications extends InstrumentedFragment
+             if (mOverrideFilter != null) {
+                 filterObj = mOverrideFilter;
+             }
+-            if (!mManageApplications.mShowSystem) {
++            if (!mManageApplications.mShowSystem && !mManageApplications.mShowSubstratum) {
+                 filterObj = new CompoundFilter(filterObj,
+                         ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER);
++                filterObj = new CompoundFilter(filterObj,
++                        ApplicationsState.FILTER_SUBSTRATUM);
++            } else if (!mManageApplications.mShowSystem) {
++                filterObj = new CompoundFilter(filterObj,
++                        ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER);
++            } else if (!mManageApplications.mShowSubstratum) {
++                filterObj = new CompoundFilter(filterObj,
++                        ApplicationsState.FILTER_SUBSTRATUM);
+             }
+             switch (mLastSortMode) {
+                 case R.id.sort_order_size:
+-- 
+2.9.3
+
diff --git a/packages/apps/Settings/0008-OMS7-N-Apps-show-hide-Substratum-icon-overlays-2-2.patch b/packages/apps/Settings/0008-OMS7-N-Apps-show-hide-Substratum-icon-overlays-2-2.patch
new file mode 100644 (file)
index 0000000..d6f0870
--- /dev/null
@@ -0,0 +1,151 @@
+From 3fcc8bc49ffb6dc7d35e4ba460e932857bff721b Mon Sep 17 00:00:00 2001
+From: Kuba Schenk <abukcz@gmail.com>
+Date: Thu, 1 Dec 2016 22:00:31 +0100
+Subject: [PATCH 08/15] OMS7-N: Apps: show/hide Substratum icon overlays [2/2]
+
+Default behavior is "hidden" substratum icon overlays,
+as they pollute the screen, and you can't notice the
+actual apps.
+So we follow the current trend set by Android with
+"show system" and make it "show icon overlays" on default.
+
+Change-Id: Ib6dd011f821f21bee6da1979615f2eb4658c9f52
+(cherry picked from commit a01b70568db202255fbf7f62d87c3e89e91f933d)
+---
+ res/menu/manage_apps.xml                           |  8 ++++++
+ res/values/projekt_strings.xml                     |  5 ++++
+ .../settings/applications/ManageApplications.java  | 33 ++++++++++++++++------
+ 3 files changed, 37 insertions(+), 9 deletions(-)
+
+diff --git a/res/menu/manage_apps.xml b/res/menu/manage_apps.xml
+index 67f09e5..267cff8 100644
+--- a/res/menu/manage_apps.xml
++++ b/res/menu/manage_apps.xml
+@@ -38,6 +38,14 @@
+         android:title="@string/menu_hide_substratum"
+         android:showAsAction="never" />
+     <item
++        android:id="@+id/show_substratum_icons"
++        android:title="@string/menu_show_substratum_icons"
++        android:showAsAction="never" />
++    <item
++        android:id="@+id/hide_substratum_icons"
++        android:title="@string/menu_hide_substratum_icons"
++        android:showAsAction="never" />
++    <item
+         android:id="@+id/sort_order_alpha"
+         android:title="@string/sort_order_alpha"
+         android:showAsAction="never" />
+diff --git a/res/values/projekt_strings.xml b/res/values/projekt_strings.xml
+index 177e78c..4c45544 100644
+--- a/res/values/projekt_strings.xml
++++ b/res/values/projekt_strings.xml
+@@ -20,4 +20,9 @@
+    <string name="menu_show_substratum">Show overlays</string>
+    <string name="menu_hide_substratum">Hide overlays</string>
++   <!-- Manage applications: show substratum icon overlays -->
++   <string name="filter_substratum_icons">Substratum icon overlays</string>
++   <string name="menu_show_substratum_icons">Show icon overlays</string>
++   <string name="menu_hide_substratum_icons">Hide icon overlays</string>
++
+ </resources>
+diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java
+index 070754a..5d3b353 100644
+--- a/src/com/android/settings/applications/ManageApplications.java
++++ b/src/com/android/settings/applications/ManageApplications.java
+@@ -108,6 +108,7 @@ public class ManageApplications extends InstrumentedFragment
+     private static final String EXTRA_SORT_ORDER = "sortOrder";
+     private static final String EXTRA_SHOW_SYSTEM = "showSystem";
+     private static final String EXTRA_SHOW_SUBSTRATUM = "showSubstratum";
++    private static final String EXTRA_SHOW_SUBSTRATUM_ICONS = "showSubstratumIcons";
+     private static final String EXTRA_HAS_ENTRIES = "hasEntries";
+     private static final String EXTRA_HAS_BRIDGE = "hasBridge";
+@@ -139,7 +140,8 @@ public class ManageApplications extends InstrumentedFragment
+     public static final int FILTER_APPS_USAGE_ACCESS = 13;
+     public static final int FILTER_APPS_WITH_OVERLAY = 14;
+     public static final int FILTER_APPS_WRITE_SETTINGS = 15;
+-    public static final int FILTER_APPS_SUBSTRATUM = 16;
++    public static final int FILTER_APPS_SUBSTRATUM_ICONS = 16;
++    public static final int FILTER_APPS_SUBSTRATUM = 17;
+     // This is the string labels for the filter modes above, the order must be kept in sync.
+     public static final int[] FILTER_LABELS = new int[]{
+@@ -191,6 +193,7 @@ public class ManageApplications extends InstrumentedFragment
+     // whether showing substratum overlays.
+     private boolean mShowSubstratum;
++    private boolean mShowSubstratumIcons;
+     private ApplicationsState mApplicationsState;
+@@ -282,6 +285,8 @@ public class ManageApplications extends InstrumentedFragment
+             mSortOrder = savedInstanceState.getInt(EXTRA_SORT_ORDER, mSortOrder);
+             mShowSystem = savedInstanceState.getBoolean(EXTRA_SHOW_SYSTEM, mShowSystem);
+             mShowSubstratum = savedInstanceState.getBoolean(EXTRA_SHOW_SUBSTRATUM, mShowSubstratum);
++            mShowSubstratumIcons = savedInstanceState.getBoolean(EXTRA_SHOW_SUBSTRATUM_ICONS,
++                                                                 mShowSubstratumIcons);
+         }
+         mInvalidSizeStr = getActivity().getText(R.string.invalid_size_value);
+@@ -447,6 +452,7 @@ public class ManageApplications extends InstrumentedFragment
+         outState.putInt(EXTRA_SORT_ORDER, mSortOrder);
+         outState.putBoolean(EXTRA_SHOW_SYSTEM, mShowSystem);
+         outState.putBoolean(EXTRA_SHOW_SUBSTRATUM, mShowSubstratum);
++        outState.putBoolean(EXTRA_SHOW_SUBSTRATUM_ICONS, mShowSubstratumIcons);
+         outState.putBoolean(EXTRA_HAS_ENTRIES, mApplications.mHasReceivedLoadEntries);
+         outState.putBoolean(EXTRA_HAS_BRIDGE, mApplications.mHasReceivedBridgeCallback);
+     }
+@@ -570,6 +576,10 @@ public class ManageApplications extends InstrumentedFragment
+                 && mListType != LIST_TYPE_HIGH_POWER);
+         mOptionsMenu.findItem(R.id.hide_substratum).setVisible(mShowSubstratum
+                 && mListType != LIST_TYPE_HIGH_POWER);
++        mOptionsMenu.findItem(R.id.show_substratum_icons).setVisible(!mShowSubstratumIcons
++                && mListType != LIST_TYPE_HIGH_POWER);
++        mOptionsMenu.findItem(R.id.hide_substratum_icons).setVisible(mShowSubstratumIcons
++                && mListType != LIST_TYPE_HIGH_POWER);
+     }
+     @Override
+@@ -594,6 +604,11 @@ public class ManageApplications extends InstrumentedFragment
+                 mShowSubstratum = !mShowSubstratum;
+                 mApplications.rebuild(false);
+                 break;
++            case R.id.show_substratum_icons:
++            case R.id.hide_substratum_icons:
++                mShowSubstratumIcons = !mShowSubstratumIcons;
++                mApplications.rebuild(false);
++                break;
+             case R.id.reset_app_preferences:
+                 mResetAppsHelper.buildResetDialog();
+                 return true;
+@@ -882,17 +897,17 @@ public class ManageApplications extends InstrumentedFragment
+             if (mOverrideFilter != null) {
+                 filterObj = mOverrideFilter;
+             }
+-            if (!mManageApplications.mShowSystem && !mManageApplications.mShowSubstratum) {
+-                filterObj = new CompoundFilter(filterObj,
+-                        ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER);
++            if (!mManageApplications.mShowSystem) {
+                 filterObj = new CompoundFilter(filterObj,
+-                        ApplicationsState.FILTER_SUBSTRATUM);
+-            } else if (!mManageApplications.mShowSystem) {
++                                               ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER);
++            }
++            if (!mManageApplications.mShowSubstratum) {
+                 filterObj = new CompoundFilter(filterObj,
+-                        ApplicationsState.FILTER_DOWNLOADED_AND_LAUNCHER);
+-            } else if (!mManageApplications.mShowSubstratum) {
++                                               ApplicationsState.FILTER_SUBSTRATUM);
++            }
++            if (!mManageApplications.mShowSubstratumIcons) {
+                 filterObj = new CompoundFilter(filterObj,
+-                        ApplicationsState.FILTER_SUBSTRATUM);
++                                               ApplicationsState.FILTER_SUBSTRATUM_ICONS);
+             }
+             switch (mLastSortMode) {
+                 case R.id.sort_order_size:
+-- 
+2.9.3
+
diff --git a/packages/apps/Settings/0009-Exclude-overlays-from-the-app-counter.patch b/packages/apps/Settings/0009-Exclude-overlays-from-the-app-counter.patch
new file mode 100644 (file)
index 0000000..f140374
--- /dev/null
@@ -0,0 +1,49 @@
+From 9e3533dad0365c72196f773688d54061572943ca Mon Sep 17 00:00:00 2001
+From: Ivan Iskandar <iiiiskandar14@gmail.com>
+Date: Fri, 9 Dec 2016 02:19:20 +0100
+Subject: [PATCH 09/15] Exclude overlays from the app counter
+
+This exclude overlays from the app counter that shows
+in manage app summary.
+
+idea from @KreAch3R
+
+Change-Id: I8cd591dc89eb87057e8c2ae3b2cca2c76c34534b
+(cherry picked from commit 990eeb53387bbd52e76ce7e7b45c171d1e01e7a1)
+---
+ src/com/android/settings/applications/AppCounter.java         | 1 +
+ src/com/android/settings/applications/ManageApplications.java | 6 ++++++
+ 2 files changed, 7 insertions(+)
+
+diff --git a/src/com/android/settings/applications/AppCounter.java b/src/com/android/settings/applications/AppCounter.java
+index fb8d580..852358d 100644
+--- a/src/com/android/settings/applications/AppCounter.java
++++ b/src/com/android/settings/applications/AppCounter.java
+@@ -47,6 +47,7 @@ public abstract class AppCounter extends AsyncTask<Void, Void, Integer> {
+                 ParceledListSlice<ApplicationInfo> list =
+                         mIpm.getInstalledApplications(PackageManager.GET_DISABLED_COMPONENTS
+                                 | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS
++                                | PackageManager.GET_META_DATA
+                                 | (user.isAdmin() ? PackageManager.GET_UNINSTALLED_PACKAGES : 0),
+                                 user.id);
+                 for (ApplicationInfo info : list.getList()) {
+diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java
+index 5d3b353..ed61781 100644
+--- a/src/com/android/settings/applications/ManageApplications.java
++++ b/src/com/android/settings/applications/ManageApplications.java
+@@ -1297,6 +1297,12 @@ public class ManageApplications extends InstrumentedFragment
+                         if ((info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0) {
+                             return true;
+                         } else if ((info.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
++                            if (info.metaData != null) {
++                                if (info.metaData.getString("Substratum_Parent") != null
++                                        || info.metaData.getString("Substratum_IconPack") != null) {
++                                    return false;
++                                }
++                            }
+                             return true;
+                         }
+                         Intent launchIntent = new Intent(Intent.ACTION_MAIN, null)
+-- 
+2.9.3
+
diff --git a/packages/apps/Settings/0010-Hide-the-show-hide-overlays-when-no-overlay-installe.patch b/packages/apps/Settings/0010-Hide-the-show-hide-overlays-when-no-overlay-installe.patch
new file mode 100644 (file)
index 0000000..6c5bcb3
--- /dev/null
@@ -0,0 +1,86 @@
+From 6e982674f97c80b325d739c417c95734991f1eaa Mon Sep 17 00:00:00 2001
+From: Ivan Iskandar <iiiiskandar14@gmail.com>
+Date: Wed, 14 Dec 2016 14:58:54 +0100
+Subject: [PATCH 10/15] Hide the "show/hide overlays" when no overlay installed
+
+Change-Id: I54088d27c9492e0992992de59bf340fe2465260a
+---
+ .../settings/applications/ManageApplications.java  | 38 +++++++++++++++++++---
+ 1 file changed, 34 insertions(+), 4 deletions(-)
+
+diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java
+index ed61781..c3bc4d4 100644
+--- a/src/com/android/settings/applications/ManageApplications.java
++++ b/src/com/android/settings/applications/ManageApplications.java
+@@ -195,6 +195,10 @@ public class ManageApplications extends InstrumentedFragment
+     private boolean mShowSubstratum;
+     private boolean mShowSubstratumIcons;
++    // if app and icon overlay installed
++    private boolean mAppOverlayInstalled;
++    private boolean mIconOverlayInstalled;
++
+     private ApplicationsState mApplicationsState;
+     public int mListType;
+@@ -556,6 +560,9 @@ public class ManageApplications extends InstrumentedFragment
+     }
+     void updateOptionsMenu() {
++        mAppOverlayInstalled = isOverlayInstalled("app");
++        mIconOverlayInstalled = isOverlayInstalled("icon");
++
+         if (mOptionsMenu == null) {
+             return;
+         }
+@@ -573,13 +580,13 @@ public class ManageApplications extends InstrumentedFragment
+                 && mListType != LIST_TYPE_HIGH_POWER);
+         mOptionsMenu.findItem(R.id.show_substratum).setVisible(!mShowSubstratum
+-                && mListType != LIST_TYPE_HIGH_POWER);
++                && mListType != LIST_TYPE_HIGH_POWER && mAppOverlayInstalled);
+         mOptionsMenu.findItem(R.id.hide_substratum).setVisible(mShowSubstratum
+-                && mListType != LIST_TYPE_HIGH_POWER);
++                && mListType != LIST_TYPE_HIGH_POWER && mAppOverlayInstalled);
+         mOptionsMenu.findItem(R.id.show_substratum_icons).setVisible(!mShowSubstratumIcons
+-                && mListType != LIST_TYPE_HIGH_POWER);
++                && mListType != LIST_TYPE_HIGH_POWER && mIconOverlayInstalled);
+         mOptionsMenu.findItem(R.id.hide_substratum_icons).setVisible(mShowSubstratumIcons
+-                && mListType != LIST_TYPE_HIGH_POWER);
++                && mListType != LIST_TYPE_HIGH_POWER && mIconOverlayInstalled);
+     }
+     @Override
+@@ -668,6 +675,29 @@ public class ManageApplications extends InstrumentedFragment
+         mFilterAdapter.setFilterEnabled(FILTER_APPS_DISABLED, hasDisabledApps);
+     }
++    boolean isOverlayInstalled(String type) {
++        List<ApplicationInfo> packages = getActivity().getPackageManager()
++                .getInstalledApplications(PackageManager.GET_META_DATA);
++
++        for (ApplicationInfo packageInfo : packages) {
++            if (packageInfo.metaData != null) {
++                if (type.equals("app")) {
++                    if (packageInfo.metaData
++                                    .getString("Substratum_Parent") != null) {
++                        return true;
++                    }
++                }
++                if (type.equals("icon")) {
++                    if (packageInfo.metaData
++                                    .getString("Substratum_IconPack") != null) {
++                        return true;
++                    }
++                }
++            }
++        }
++        return false;
++    }
++
+     static class FilterSpinnerAdapter extends ArrayAdapter<CharSequence> {
+         private final ManageApplications mManageApplications;
+-- 
+2.9.3
+
diff --git a/packages/apps/Settings/0011-Expose-color-for-external-settings-icons.patch b/packages/apps/Settings/0011-Expose-color-for-external-settings-icons.patch
new file mode 100644 (file)
index 0000000..67df099
--- /dev/null
@@ -0,0 +1,77 @@
+From f50ef9c89b3cd4ae9d66e885b2d406fdad3931be Mon Sep 17 00:00:00 2001
+From: Clark Scheff <clark@scheffsblend.com>
+Date: Fri, 23 Dec 2016 09:52:57 -0500
+Subject: [PATCH 11/15] Expose color for external settings icons
+
+Marhsmallow now allows for system apps to add settings.  An icon is
+loaded from the external package and a tint is applied to the icon
+so that it matches other icons in the dashboard.  The tint color
+uses the colorAccent attribute from the currently applied style.
+Themes can overlay the colorAccent but this color is also used for
+other elements of the UI which may not be ideal.
+
+This patch adds a new color external_tile_icon_tint_color which
+points to the attribute ?android:attr/colorAccent when not themed.
+This retains the original look but offers better flexibility for
+theme designers.
+
+- Ported up to 7.1.1
+
+Change-Id: Ifb4a99a315271cdcf978a796e3dea0eaef8d7750
+---
+ res/values/projekt_colors.xml                            |  4 +++-
+ src/com/android/settings/dashboard/DashboardAdapter.java | 14 +++++++++-----
+ 2 files changed, 12 insertions(+), 6 deletions(-)
+
+diff --git a/res/values/projekt_colors.xml b/res/values/projekt_colors.xml
+index e1977f9..4ecd1b2 100644
+--- a/res/values/projekt_colors.xml
++++ b/res/values/projekt_colors.xml
+@@ -39,9 +39,11 @@
+     <color name="storage_private_3">#fff2a600</color>
+     <color name="storage_private_4">#ffec407a</color>
+     <color name="storage_private_5">#ffc0ca33</color>
+-
+     <!-- Linear color bar -->
+     <color name="linear_color_bar_left">@*android:color/accent_device_default_light</color>
+     <color name="linear_color_bar_middle">@*android:color/accent_device_default_light</color>
+     <color name="linear_color_bar_right">#ffced7db</color>
++
++    <!-- External Icon Tint -->
++    <color name="external_tile_icon_tint_color">?android:attr/colorAccent</color>
+ </resources>
+diff --git a/src/com/android/settings/dashboard/DashboardAdapter.java b/src/com/android/settings/dashboard/DashboardAdapter.java
+index 6b2e145..c29da43 100644
+--- a/src/com/android/settings/dashboard/DashboardAdapter.java
++++ b/src/com/android/settings/dashboard/DashboardAdapter.java
+@@ -114,10 +114,10 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
+         mSuggestions = suggestions;
+         mCategories = categories;
+-        // TODO: Better place for tinting?
+-        TypedValue tintColor = new TypedValue();
+-        mContext.getTheme().resolveAttribute(com.android.internal.R.attr.colorAccent,
+-                tintColor, true);
++        TypedValue tintColorValue = new TypedValue();
++        mContext.getResources().getValue(R.color.external_tile_icon_tint_color,
++                tintColorValue, true);
++
+         for (int i = 0; i < categories.size(); i++) {
+             for (int j = 0; j < categories.get(i).tiles.size(); j++) {
+                 Tile tile = categories.get(i).tiles.get(j);
+@@ -126,7 +126,11 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
+                         tile.intent.getComponent().getPackageName())) {
+                     // If this drawable is coming from outside Settings, tint it to match the
+                     // color.
+-                    tile.icon.setTint(tintColor.data);
++                    if (tintColorValue.type == TypedValue.TYPE_ATTRIBUTE) {
++                        mContext.getTheme().resolveAttribute(tintColorValue.data,
++                                tintColorValue, true);
++                    }
++                    tile.icon.setTint(tintColorValue.data);
+                 }
+             }
+         }
+-- 
+2.9.3
+
diff --git a/packages/apps/Settings/0012-Expose-dashboard-category-padding-bottom.patch b/packages/apps/Settings/0012-Expose-dashboard-category-padding-bottom.patch
new file mode 100644 (file)
index 0000000..192868c
--- /dev/null
@@ -0,0 +1,50 @@
+From 0d488d0698cb80ee8236743a8be9300c7c807917 Mon Sep 17 00:00:00 2001
+From: Ivan Iskandar <iiiiskandar14@gmail.com>
+Date: Sun, 8 Jan 2017 00:18:47 +0100
+Subject: [PATCH 12/15] Expose dashboard category padding bottom
+
+Change-Id: I5a0d3266bbe96dbe6a136f78ed84cb1ee0a6a19b
+---
+ res/layout/dashboard_category.xml |  2 +-
+ res/values/projekt_dimens.xml     | 16 ++++++++++++++++
+ 2 files changed, 17 insertions(+), 1 deletion(-)
+ create mode 100644 res/values/projekt_dimens.xml
+
+diff --git a/res/layout/dashboard_category.xml b/res/layout/dashboard_category.xml
+index 7836644..86dac35 100644
+--- a/res/layout/dashboard_category.xml
++++ b/res/layout/dashboard_category.xml
+@@ -19,7 +19,7 @@
+     android:layout_width="match_parent"
+     android:layout_height="@dimen/dashboard_category_height"
+     android:orientation="vertical"
+-    android:paddingBottom="8dip"
++    android:paddingBottom="@dimen/dashboard_category_padding_bottom"
+     android:background="@drawable/dashboard_category_title_bg">
+     <TextView android:id="@android:id/title"
+diff --git a/res/values/projekt_dimens.xml b/res/values/projekt_dimens.xml
+new file mode 100644
+index 0000000..a5aae4e
+--- /dev/null
++++ b/res/values/projekt_dimens.xml
+@@ -0,0 +1,16 @@
++<?xml version="1.0" encoding="utf-8"?>
++<!--
++    Copyright (c) 2017 Projekt Substratum
++    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.
++-->
++<resources>
++    <dimen name="dashboard_category_padding_bottom">8dip</dimen>
++</resources>
+-- 
+2.9.3
+
diff --git a/packages/apps/Settings/0013-Expose-switchbar-background-color.patch b/packages/apps/Settings/0013-Expose-switchbar-background-color.patch
new file mode 100644 (file)
index 0000000..4609b94
--- /dev/null
@@ -0,0 +1,77 @@
+From 1fd8e4207626877ebf22d1c6b258fade0790a704 Mon Sep 17 00:00:00 2001
+From: Ivan Iskandar <iiiiskandar14@gmail.com>
+Date: Sun, 8 Jan 2017 01:22:47 +0100
+Subject: [PATCH 13/15] Expose switchbar background color
+
+Change-Id: Idc8b09c921bb57c9464d6ea956cfdd9b2b95e6a8
+---
+ res/values/projekt_colors.xml                  |  2 ++
+ res/values/themes.xml                          |  4 ++--
+ src/com/android/settings/widget/SwitchBar.java | 11 ++++++++++-
+ 3 files changed, 14 insertions(+), 3 deletions(-)
+
+diff --git a/res/values/projekt_colors.xml b/res/values/projekt_colors.xml
+index 4ecd1b2..f37cc54 100644
+--- a/res/values/projekt_colors.xml
++++ b/res/values/projekt_colors.xml
+@@ -46,4 +46,6 @@
+     <!-- External Icon Tint -->
+     <color name="external_tile_icon_tint_color">?android:attr/colorAccent</color>
++    <!-- Settings switchbar background -->
++    <color name="switchbar_background_color">?android:attr/colorSecondary</color>
+ </resources>
+diff --git a/res/values/themes.xml b/res/values/themes.xml
+index ffe802e..acabe21 100644
+--- a/res/values/themes.xml
++++ b/res/values/themes.xml
+@@ -196,13 +196,13 @@
+     <style name="ThemeOverlay.SwitchBar.Settings" parent="@*android:style/ThemeOverlay.DeviceDefault.Dark.ActionBar.Accent">
+         <item name="switchBarMarginStart">@dimen/switchbar_subsettings_margin_start</item>
+         <item name="switchBarMarginEnd">@dimen/switchbar_subsettings_margin_end</item>
+-        <item name="switchBarBackgroundColor">?android:attr/colorSecondary</item>
++        <item name="switchBarBackgroundColor">@color/switchbar_background_color</item>
+     </style>
+     <style name="ThemeOverlay.SwitchBar.SubSettings" parent="@android:style/ThemeOverlay.Material.Dark.ActionBar">
+         <item name="switchBarMarginStart">@dimen/switchbar_subsettings_margin_start</item>
+         <item name="switchBarMarginEnd">@dimen/switchbar_subsettings_margin_end</item>
+-        <item name="switchBarBackgroundColor">?android:attr/colorSecondary</item>
++        <item name="switchBarBackgroundColor">@color/switchbar_background_color</item>
+     </style>
+     <style name="Theme.DialogWhenLarge" parent="@*android:style/Theme.DeviceDefault.Settings.DialogWhenLarge">
+diff --git a/src/com/android/settings/widget/SwitchBar.java b/src/com/android/settings/widget/SwitchBar.java
+index 240c062..ce73a39 100644
+--- a/src/com/android/settings/widget/SwitchBar.java
++++ b/src/com/android/settings/widget/SwitchBar.java
+@@ -24,6 +24,7 @@ import android.text.SpannableStringBuilder;
+ import android.text.TextUtils;
+ import android.text.style.TextAppearanceSpan;
+ import android.util.AttributeSet;
++import android.util.TypedValue;
+ import android.view.LayoutInflater;
+ import android.view.View;
+ import android.view.ViewGroup;
+@@ -96,9 +97,17 @@ public class SwitchBar extends LinearLayout implements CompoundButton.OnCheckedC
+         final TypedArray a = context.obtainStyledAttributes(attrs, XML_ATTRIBUTES);
+         int switchBarMarginStart = (int) a.getDimension(0, 0);
+         int switchBarMarginEnd = (int) a.getDimension(1, 0);
+-        int switchBarBackgroundColor = (int) a.getColor(2, 0);
+         a.recycle();
++        TypedValue backgroundColorValue = new TypedValue();
++        mContext.getResources().getValue(R.color.switchbar_background_color,
++                backgroundColorValue, true);
++        if (backgroundColorValue.type == TypedValue.TYPE_ATTRIBUTE) {
++            context.getTheme().resolveAttribute(backgroundColorValue.data,
++                    backgroundColorValue, true);
++        }
++        int switchBarBackgroundColor = backgroundColorValue.data;
++
+         mTextView = (TextView) findViewById(R.id.switch_text);
+         mTextView.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_NO);
+         mLabel = getResources().getString(R.string.switch_off_text);
+-- 
+2.9.3
+
diff --git a/packages/apps/Settings/0014-Settings-Expose-bluetooth-pin-confirm-dialog-text-co.patch b/packages/apps/Settings/0014-Settings-Expose-bluetooth-pin-confirm-dialog-text-co.patch
new file mode 100644 (file)
index 0000000..b17f145
--- /dev/null
@@ -0,0 +1,60 @@
+From 1edcfb911d77e894086bf88ac47f966a8b88fb22 Mon Sep 17 00:00:00 2001
+From: SpiritCroc <spiritcroc@gmail.com>
+Date: Sun, 22 Jan 2017 20:51:47 +0100
+Subject: [PATCH 14/15] Settings: Expose bluetooth pin confirm dialog text
+ colors
+
+Change-Id: I1e1eb2da23f2470a82574da7ef326d56f770eb95
+---
+ res/layout/bluetooth_pin_confirm.xml | 6 +++---
+ res/values/projekt_colors.xml        | 5 +++++
+ 2 files changed, 8 insertions(+), 3 deletions(-)
+
+diff --git a/res/layout/bluetooth_pin_confirm.xml b/res/layout/bluetooth_pin_confirm.xml
+index ebdf65d..6609a1b 100644
+--- a/res/layout/bluetooth_pin_confirm.xml
++++ b/res/layout/bluetooth_pin_confirm.xml
+@@ -40,7 +40,7 @@
+             android:text="@string/bluetooth_pairing_key_msg"
+             android:visibility="gone"
+             android:textAppearance="@android:style/TextAppearance.Material.Body1"
+-            android:textColor="@*android:color/secondary_text_material_light"  />
++            android:textColor="@color/bluetooth_pairing_caption"  />
+         <TextView
+             android:id="@+id/pairing_subhead"
+@@ -63,7 +63,7 @@
+             android:gravity="center_vertical"
+             android:text="@string/bluetooth_enter_passkey_msg"
+             android:textAppearance="@android:style/TextAppearance.Material.Subhead"
+-            android:textColor="@*android:color/secondary_text_material_light"
++            android:textColor="@color/bluetooth_pairing_code_message"
+             android:visibility="gone" />
+         <CheckBox
+@@ -73,7 +73,7 @@
+             android:layout_marginStart="@dimen/bluetooth_dialog_padding"
+             android:layout_marginEnd="@dimen/bluetooth_dialog_padding"
+             android:textAppearance="@android:style/TextAppearance.Material.Body1"
+-            android:textColor="@*android:color/secondary_text_material_light"  />
++            android:textColor="@color/bluetooth_phonebook_sharing_message_confirm_pin"  />
+     </LinearLayout>
+diff --git a/res/values/projekt_colors.xml b/res/values/projekt_colors.xml
+index f37cc54..e0d3ab1 100644
+--- a/res/values/projekt_colors.xml
++++ b/res/values/projekt_colors.xml
+@@ -48,4 +48,9 @@
+     <color name="external_tile_icon_tint_color">?android:attr/colorAccent</color>
+     <!-- Settings switchbar background -->
+     <color name="switchbar_background_color">?android:attr/colorSecondary</color>
++
++    <!-- Bluetooth pairing pin confirm dialog -->
++    <color name="bluetooth_pairing_caption">@*android:color/secondary_text_material_light</color>
++    <color name="bluetooth_pairing_code_message">@*android:color/secondary_text_material_light</color>
++    <color name="bluetooth_phonebook_sharing_message_confirm_pin">@*android:color/secondary_text_material_light</color>
+ </resources>
+-- 
+2.9.3
+
diff --git a/packages/apps/Settings/0015-Set-external-settings-icon-tint-mode-to-SRC_ATOP.patch b/packages/apps/Settings/0015-Set-external-settings-icon-tint-mode-to-SRC_ATOP.patch
new file mode 100644 (file)
index 0000000..5cdcb63
--- /dev/null
@@ -0,0 +1,42 @@
+From 9bb5dca50887734bbc12e46254464f1d0734ac6f Mon Sep 17 00:00:00 2001
+From: Ivan Iskandar <iiiiskandar14@gmail.com>
+Date: Sat, 28 Jan 2017 13:40:55 +0100
+Subject: [PATCH 15/15] Set external settings icon tint mode to SRC_ATOP
+
+With "Expose color for external settings icons" commit, themers can change
+the color of the external settings icon to whatever they want except
+transparent. When they set the tint color to transparent the icon will be
+dissapeared, this commits fixed the problem by changing the tint mode.
+
+Before: http://i.imgur.com/ghDPs27.jpg
+After: http://i.imgur.com/AUEFgNO.jpg
+
+Change-Id: I65b710e2abefd1052b3af154a98247de9b4fe98d
+---
+ src/com/android/settings/dashboard/DashboardAdapter.java | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/src/com/android/settings/dashboard/DashboardAdapter.java b/src/com/android/settings/dashboard/DashboardAdapter.java
+index c29da43..8259d39 100644
+--- a/src/com/android/settings/dashboard/DashboardAdapter.java
++++ b/src/com/android/settings/dashboard/DashboardAdapter.java
+@@ -19,6 +19,7 @@ import android.content.Context;
+ import android.content.pm.PackageManager;
+ import android.graphics.drawable.Drawable;
+ import android.graphics.drawable.Icon;
++import android.graphics.PorterDuff.Mode;
+ import android.os.Bundle;
+ import android.support.v7.widget.PopupMenu;
+ import android.support.v7.widget.RecyclerView;
+@@ -130,7 +131,7 @@ public class DashboardAdapter extends RecyclerView.Adapter<DashboardAdapter.Dash
+                         mContext.getTheme().resolveAttribute(tintColorValue.data,
+                                 tintColorValue, true);
+                     }
+-                    tile.icon.setTint(tintColorValue.data);
++                    tile.icon.setTint(tintColorValue.data).setTintMode(Mode.SRC_ATOP);
+                 }
+             }
+         }
+-- 
+2.9.3
+
diff --git a/patch.sh b/patch.sh
new file mode 100755 (executable)
index 0000000..f3c18ac
--- /dev/null
+++ b/patch.sh
@@ -0,0 +1,20 @@
+ROOT='/home/luk/android/lineage-14.1'
+REPOSITORIES=(
+    'packages/apps/ContactsCommon'
+    'packages/apps/Contacts'
+    'packages/apps/Settings'
+    'packages/apps/ExactCalculator'
+    'packages/apps/PhoneCommon'
+    'build'
+    'system/sepolicy'
+    'frameworks/native'
+    'frameworks/base'
+)
+
+for repository in "${REPOSITORIES[@]}"; do
+    cd "${ROOT}/${repository}"
+
+    git am "${ROOT}/oms-patches/${repository}"/*
+
+    cd "${ROOT}"
+done
diff --git a/system/sepolicy/0001-OMS-N-Add-service-overlay-to-service_contexts.patch b/system/sepolicy/0001-OMS-N-Add-service-overlay-to-service_contexts.patch
new file mode 100644 (file)
index 0000000..de0c721
--- /dev/null
@@ -0,0 +1,59 @@
+From 1563cb90a0c8268564552bdd068410cd56079599 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?M=C3=A5rten=20Kongstad?= <marten.kongstad@sonymobile.com>
+Date: Mon, 22 Jun 2015 09:31:25 +0200
+Subject: [PATCH] OMS-N: Add service 'overlay' to service_contexts
+
+The 'overlay' service is the Overlay Manager Service, which tracks
+packages and their Runtime Resource Overlay overlay packages.
+
+Bug: 31052947
+
+Co-authored-by: Martin Wallgren <martin.wallgren@sonymobile.com>
+Signed-off-by: Zoran Jovanovic <zoran.jovanovic@sonymobile.com>
+
+Change-Id: Ie996707dd02166325271bee49163ac263e560a1d
+---
+ service.te       | 1 +
+ service_contexts | 1 +
+ system_server.te | 1 +
+ 3 files changed, 3 insertions(+)
+
+diff --git a/service.te b/service.te
+index e7a30f9..6f9ab3f 100644
+--- a/service.te
++++ b/service.te
+@@ -81,6 +81,7 @@ type network_score_service, system_api_service, system_server_service, service_m
+ type network_time_update_service, system_server_service, service_manager_type;
+ type notification_service, app_api_service, system_server_service, service_manager_type;
+ type otadexopt_service, system_server_service, service_manager_type;
++type overlay_service, app_api_service, system_server_service, service_manager_type;
+ type package_service, app_api_service, system_server_service, service_manager_type;
+ type permission_service, app_api_service, system_server_service, service_manager_type;
+ type persistent_data_block_service, system_api_service, system_server_service, service_manager_type;
+diff --git a/service_contexts b/service_contexts
+index dd7e49f..b7d7473 100644
+--- a/service_contexts
++++ b/service_contexts
+@@ -93,6 +93,7 @@ network_time_update_service               u:object_r:network_time_update_service
+ nfc                                       u:object_r:nfc_service:s0
+ notification                              u:object_r:notification_service:s0
+ otadexopt                                 u:object_r:otadexopt_service:s0
++overlay                                   u:object_r:overlay_service:s0
+ package                                   u:object_r:package_service:s0
+ permission                                u:object_r:permission_service:s0
+ persistent_data_block                     u:object_r:persistent_data_block_service:s0
+diff --git a/system_server.te b/system_server.te
+index 03a7ef3..3ca8182 100644
+--- a/system_server.te
++++ b/system_server.te
+@@ -441,6 +441,7 @@ allow system_server mediacodec_service:service_manager find;
+ allow system_server mediadrmserver_service:service_manager find;
+ allow system_server netd_service:service_manager find;
+ allow system_server nfc_service:service_manager find;
++allow system_server overlay_service:service_manager find;
+ allow system_server radio_service:service_manager find;
+ allow system_server system_server_service:service_manager { add find };
+ allow system_server surfaceflinger_service:service_manager find;
+-- 
+2.9.3
+