Add tests for layout overlaying
Add tests to ensure that findViewbyId retrieves the correct view when
an id is overlaid in a RRO.
Also fixes a bug that made strings hardcoded in the overlays.xml
file unable to be retrieved from the string pool.
Bug: 135943783
Test: atest OverlayDeviceTests
Change-Id: I2bf03f151cb696d28f6bb9018eb319af29ba48f4
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 c692a262..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 773353d..6aac447 100644
--- a/libs/androidfw/Idmap.cpp
+++ b/libs/androidfw/Idmap.cpp
@@ -53,7 +53,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);
}
@@ -62,13 +62,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 b20e657..6d82d88 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;