Merge "Add tests for layout overlaying"
diff --git a/core/tests/overlaytests/device/res/layout/layout.xml b/core/tests/overlaytests/device/res/layout/layout.xml
new file mode 100644
index 0000000..e12c715
--- /dev/null
+++ b/core/tests/overlaytests/device/res/layout/layout.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ 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.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:app="http://schemas.android.com/apk/res-auto"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+    <LinearLayout android:id="@id/view_1"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+        <com.android.overlaytest.view.TestTextView
+            android:id="@id/view_2"
+            android:layout_width="match_parent"
+            android:layout_height="100dp"
+            app:customAttribute="none"/>
+
+        <com.android.overlaytest.view.TestTextView
+            android:id="@id/view_3"
+            android:layout_width="match_parent"
+            android:layout_height="100dp"
+            app:customAttribute="none" />
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/core/tests/overlaytests/device/res/values/config.xml b/core/tests/overlaytests/device/res/values/config.xml
index c692a26..e918268 100644
--- a/core/tests/overlaytests/device/res/values/config.xml
+++ b/core/tests/overlaytests/device/res/values/config.xml
@@ -56,4 +56,16 @@
         <item>17</item>
         <item>19</item>
     </integer-array>
+
+    <attr name="customAttribute" />
+    <id name="view_1" />
+    <id name="view_2" />
+    <id name="view_3" />
+
+    <!-- Stabilize the ids of attributes and ids used in test layouts so that they differ from the
+     overlay resource ids -->
+    <public type="attr" name="customAttribute" id="0x7f200000"/>
+    <public type="id" name="view_1" id="0x7f210000"/>
+    <public type="id" name="view_2" id="0x7f210001"/>
+    <public type="id" name="view_3" id="0x7f210002"/>
 </resources>
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
index fdb6bbb..636f4c8 100644
--- a/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/OverlayBaseTest.java
@@ -18,20 +18,26 @@
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import android.content.Context;
 import android.content.res.AssetManager;
 import android.content.res.Configuration;
 import android.content.res.Resources;
 import android.content.res.XmlResourceParser;
 import android.os.LocaleList;
 import android.util.AttributeSet;
+import android.util.TypedValue;
 import android.util.Xml;
+import android.view.LayoutInflater;
+import android.view.View;
 
 import androidx.test.InstrumentationRegistry;
 
 import com.android.internal.util.ArrayUtils;
+import com.android.overlaytest.view.TestTextView;
 
 import org.junit.Before;
 import org.junit.Ignore;
@@ -45,6 +51,7 @@
 
 @Ignore
 public abstract class OverlayBaseTest {
+    private Context mContext;
     private Resources mResources;
     private final int mMode;
     static final int MODE_NO_OVERLAY = 0;
@@ -61,7 +68,8 @@
 
     @Before
     public void setUp() {
-        mResources = InstrumentationRegistry.getContext().getResources();
+        mContext = InstrumentationRegistry.getContext();
+        mResources = mContext.getResources();
     }
 
     private int calculateRawResourceChecksum(int resId) throws Throwable {
@@ -321,6 +329,50 @@
         assertEquals("com.android.overlaytest", contents);
     }
 
+    @Test
+    public void testRewrite() throws Throwable {
+        final TypedValue result = new TypedValue();
+        mResources.getValue(R.string.str, result, true);
+        assertEquals(result.resourceId & 0xff000000, 0x7f000000);
+    }
+
+    @Test
+    public void testOverlayLayout() throws Throwable {
+        final LayoutInflater inflater = LayoutInflater.from(mContext);
+        final View layout = inflater.inflate(R.layout.layout, null);
+        assertNotNull(layout.findViewById(R.id.view_1));
+
+        final TestTextView view2 = layout.findViewById(R.id.view_2);
+        assertNotNull(view2);
+        switch (mMode) {
+            case MODE_NO_OVERLAY:
+                assertEquals("none", view2.getCustomAttributeValue());
+                break;
+            case MODE_SINGLE_OVERLAY:
+                assertEquals("single", view2.getCustomAttributeValue());
+                break;
+            case MODE_MULTIPLE_OVERLAYS:
+                assertEquals("multiple", view2.getCustomAttributeValue());
+                break;
+            default:
+                fail("Unknown mode " + mMode);
+        }
+
+        final TestTextView view3 = layout.findViewById(R.id.view_3);
+        assertNotNull(view3);
+        switch (mMode) {
+            case MODE_NO_OVERLAY:
+                assertEquals("none", view3.getCustomAttributeValue());
+                break;
+            case MODE_SINGLE_OVERLAY:
+            case MODE_MULTIPLE_OVERLAYS:
+                assertEquals("overlaid", view3.getCustomAttributeValue());
+                break;
+            default:
+                fail("Unknown mode " + mMode);
+        }
+    }
+
     /*
      * testMatrix* tests
      *
diff --git a/core/tests/overlaytests/device/src/com/android/overlaytest/view/TestTextView.java b/core/tests/overlaytests/device/src/com/android/overlaytest/view/TestTextView.java
new file mode 100644
index 0000000..2245e2b
--- /dev/null
+++ b/core/tests/overlaytests/device/src/com/android/overlaytest/view/TestTextView.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * 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.overlaytest.view;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+public class TestTextView extends TextView {
+
+    private final String mCustomAttributeValue;
+
+    public TestTextView(Context context, AttributeSet attrs) {
+        this(context, attrs, com.android.internal.R.attr.textViewStyle, 0);
+    }
+
+    public TestTextView(Context context, AttributeSet attrs,
+            int defStyleAttr,
+            int defStyleRes) {
+        super(context, attrs, defStyleAttr, defStyleRes);
+
+        int[] testResources = new int[]{com.android.overlaytest.R.attr.customAttribute};
+        final Resources.Theme theme = context.getTheme();
+        TypedArray typedArray = theme.obtainStyledAttributes(attrs, testResources, defStyleAttr,
+                defStyleRes);
+        mCustomAttributeValue = typedArray.getString(0);
+    }
+
+    public String getCustomAttributeValue() {
+        return mCustomAttributeValue;
+    }
+}
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/AndroidManifest.xml b/core/tests/overlaytests/device/test-apps/AppOverlayOne/AndroidManifest.xml
index 7d28408..873ca3c 100644
--- a/core/tests/overlaytests/device/test-apps/AppOverlayOne/AndroidManifest.xml
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/AndroidManifest.xml
@@ -19,5 +19,6 @@
         android:versionCode="1"
         android:versionName="1.0">
         <application android:hasCode="false" />
-        <overlay android:targetPackage="com.android.overlaytest" />
+        <overlay android:targetPackage="com.android.overlaytest"
+                 android:resourcesMap="@xml/overlays"/>
 </manifest>
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/layout/layout.xml b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/layout/layout.xml
new file mode 100644
index 0000000..7b63605
--- /dev/null
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/layout/layout.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ 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.
+  -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              xmlns:app="http://schemas.android.com/apk/res-auto"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+    <LinearLayout android:id="@+id/view_1"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+        <com.android.overlaytest.view.TestTextView
+            android:id="@+id/view_2"
+            android:layout_width="match_parent"
+            android:layout_height="100dp"
+            app:customAttribute="@string/str" />
+
+        <com.android.overlaytest.view.TestTextView
+            android:id="@+id/view_3"
+            android:layout_width="match_parent"
+            android:layout_height="100dp"
+            app:customAttribute="overlaid" />
+    </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/values/config.xml b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/values/config.xml
index 972137a..74c4963 100644
--- a/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/values/config.xml
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/values/config.xml
@@ -1,7 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
     <string name="str">single</string>
-    <string name="str2">single</string>
     <integer name="matrix_101000">300</integer>
     <integer name="matrix_101001">300</integer>
     <integer name="matrix_101010">300</integer>
@@ -18,7 +17,6 @@
     <integer name="matrix_111101">300</integer>
     <integer name="matrix_111110">300</integer>
     <integer name="matrix_111111">300</integer>
-    <bool name="usually_false">true</bool>
     <integer-array name="fibonacci">
         <item>21</item>
         <item>13</item>
@@ -29,7 +27,10 @@
         <item>1</item>
         <item>1</item>
     </integer-array>
+
     <!-- The following integer does not exist in the original package. Idmap
          generation should therefore ignore it. -->
     <integer name="integer_not_in_original_package">0</integer>
+
+    <attr name="customAttribute" />
 </resources>
diff --git a/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/xml/overlays.xml b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/xml/overlays.xml
new file mode 100644
index 0000000..38e5fa1
--- /dev/null
+++ b/core/tests/overlaytests/device/test-apps/AppOverlayOne/res/xml/overlays.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2019 The Android Open Source Project
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ 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.
+  -->
+<overlay>
+    <item target="drawable/drawable" value="@drawable/drawable"/>
+    <item target="layout/layout" value="@layout/layout"/>
+    <item target="raw/lorem_ipsum" value="@raw/lorem_ipsum"/>
+    <item target="xml/integer" value="@xml/integer"/>
+    <item target="string/str" value="@string/str"/>
+    <item target="string/str2" value="single"/>
+
+    <item target="integer/matrix_100100" value="@integer/matrix_100100"/>
+    <item target="integer/matrix_100101" value="@integer/matrix_100101"/>
+    <item target="integer/matrix_100110" value="@integer/matrix_100110"/>
+    <item target="integer/matrix_100110" value="@integer/matrix_100110"/>
+    <item target="integer/matrix_100111" value="@integer/matrix_100111"/>
+    <item target="integer/matrix_101000" value="@integer/matrix_101000"/>
+    <item target="integer/matrix_101001" value="@integer/matrix_101001"/>
+    <item target="integer/matrix_101010" value="@integer/matrix_101010"/>
+    <item target="integer/matrix_101011" value="@integer/matrix_101011"/>
+    <item target="integer/matrix_101100" value="@integer/matrix_101100"/>
+    <item target="integer/matrix_101101" value="@integer/matrix_101101"/>
+    <item target="integer/matrix_101110" value="@integer/matrix_101110"/>
+    <item target="integer/matrix_101111" value="@integer/matrix_101111"/>
+    <item target="integer/matrix_110100" value="@integer/matrix_110100"/>
+    <item target="integer/matrix_110101" value="@integer/matrix_110101"/>
+    <item target="integer/matrix_110110" value="@integer/matrix_110110"/>
+    <item target="integer/matrix_110111" value="@integer/matrix_110111"/>
+    <item target="integer/matrix_111000" value="@integer/matrix_111000"/>
+    <item target="integer/matrix_111001" value="@integer/matrix_111001"/>
+    <item target="integer/matrix_111010" value="@integer/matrix_111010"/>
+    <item target="integer/matrix_111011" value="@integer/matrix_111011"/>
+    <item target="integer/matrix_111100" value="@integer/matrix_111100"/>
+    <item target="integer/matrix_111101" value="@integer/matrix_111101"/>
+    <item target="integer/matrix_111110" value="@integer/matrix_111110"/>
+    <item target="integer/matrix_111111" value="@integer/matrix_111111"/>
+
+    <item target="bool/usually_false" value="true"/>
+
+    <item target="array/fibonacci" value="@array/fibonacci"/>
+
+    <item target="attr/customAttribute" value="@attr/customAttribute"/>
+    <item target="id/view_1" value="@id/view_1"/>
+    <item target="id/view_2" value="@id/view_2"/>
+    <item target="id/view_3" value="@id/view_3"/>
+</overlay>
+
+
diff --git a/libs/androidfw/Idmap.cpp b/libs/androidfw/Idmap.cpp
index ce3bfff..0b2fd9e 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -57,7 +57,7 @@
 
 const char16_t* OverlayStringPool::stringAt(size_t idx, size_t* outLen) const {
   const size_t offset = dtohl(data_header_->string_pool_index_offset);
-  if (idmap_string_pool_ != nullptr && idx >= size() && idx >= offset) {
+  if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
     return idmap_string_pool_->stringAt(idx - offset, outLen);
   }
 
@@ -66,13 +66,17 @@
 
 const char* OverlayStringPool::string8At(size_t idx, size_t* outLen) const {
   const size_t offset = dtohl(data_header_->string_pool_index_offset);
-  if (idmap_string_pool_ != nullptr && idx >= size() && idx >= offset) {
+  if (idmap_string_pool_ != nullptr && idx >= ResStringPool::size() && idx >= offset) {
     return idmap_string_pool_->string8At(idx - offset, outLen);
   }
 
   return ResStringPool::string8At(idx, outLen);
 }
 
+size_t OverlayStringPool::size() const {
+  return ResStringPool::size() + (idmap_string_pool_ != nullptr ? idmap_string_pool_->size() : 0U);
+}
+
 OverlayDynamicRefTable::OverlayDynamicRefTable(const Idmap_data_header* data_header,
                                                const Idmap_overlay_entry* entries,
                                                uint8_t target_assigned_package_id)
diff --git a/libs/androidfw/include/androidfw/Idmap.h b/libs/androidfw/include/androidfw/Idmap.h
index ab4c9c2..ccb57f3 100644
--- a/libs/androidfw/include/androidfw/Idmap.h
+++ b/libs/androidfw/include/androidfw/Idmap.h
@@ -40,8 +40,9 @@
 class OverlayStringPool : public ResStringPool {
  public:
   virtual ~OverlayStringPool();
-  virtual const char16_t* stringAt(size_t idx, size_t* outLen) const;
-  virtual const char* string8At(size_t idx, size_t* outLen) const;
+  const char16_t* stringAt(size_t idx, size_t* outLen) const override;
+  const char* string8At(size_t idx, size_t* outLen) const override;
+  size_t size() const override;
 
   explicit OverlayStringPool(const LoadedIdmap* loaded_idmap);
  private:
@@ -53,8 +54,8 @@
 // resources to the resource id of corresponding target resources.
 class OverlayDynamicRefTable : public DynamicRefTable {
  public:
-  virtual ~OverlayDynamicRefTable() = default;
-  virtual status_t lookupResourceId(uint32_t* resId) const;
+  ~OverlayDynamicRefTable() override = default;
+  status_t lookupResourceId(uint32_t* resId) const override;
 
  private:
   explicit OverlayDynamicRefTable(const Idmap_data_header* data_header,
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index b603326..35cebd4 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -520,7 +520,7 @@
 
     ssize_t indexOfString(const char16_t* str, size_t strLen) const;
 
-    size_t size() const;
+    virtual size_t size() const;
     size_t styleCount() const;
     size_t bytes() const;
     const void* data() const;