--- /dev/null
+include $(call all-subdir-makefiles)
+++ /dev/null
-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
-
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<manifest>
+ <project name="luk1337/android_vendor_extra" path="vendor/extra" remote="github" />
+ <project name="TeamSubstratum/masquerade" path="packages/apps/masquerade" remote="github" revision="n" />
+</manifest>
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
+++ /dev/null
-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
-
for repository in "${REPOSITORIES[@]}"; do
cd "${ROOT}/${repository}"
- git am "${ROOT}/oms-patches/${repository}"/*
+ git am "${ROOT}/vendor/extra/patches/${repository}"/*
cd "${ROOT}"
done
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+# Masquerade
+PRODUCT_PACKAGES += \
+ masquerade
+++ /dev/null
-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
-