diff options
author | android-build-team Robot <android-build-team-robot@google.com> | 2017-11-02 04:50:11 +0000 |
---|---|---|
committer | android-build-team Robot <android-build-team-robot@google.com> | 2017-11-02 04:50:11 +0000 |
commit | 250714c67ba9d2029bc88284672949a87b243b9a (patch) | |
tree | dc3a4c67867af7745558125afd006f168956066f | |
parent | 37e2e248430976004df02588e9ff489bb2cd5ae8 (diff) | |
parent | d52b215f82e464705373d794748325298f0a1f9a (diff) | |
download | base-250714c67ba9d2029bc88284672949a87b243b9a.tar.gz |
Merge cherrypicks of [3156476, 3155698, 3156194, 3156639, 3156018, 3156477, 3156098, 3156099, 3156100, 3156101, 3156102, 3158393, 3155699, 3155700, 3156195, 3156196, 3156019, 3156020, 3158394] into oc-mr1-releaseandroid-cts-8.1_r1android-8.1.0_r1
Change-Id: I5b1e8ce0b0d4c129cf502ab21733dd7bc65aed67
12 files changed, 214 insertions, 86 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 26f5ba130b7b..2c04f8ff9548 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -16,8 +16,6 @@ package android.app; -import static android.os.Build.VERSION_CODES.O_MR1; - import static java.lang.Character.MIN_VALUE; import android.annotation.CallSuper; @@ -990,18 +988,6 @@ public class Activity extends ContextThemeWrapper @CallSuper protected void onCreate(@Nullable Bundle savedInstanceState) { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onCreate " + this + ": " + savedInstanceState); - - if (getApplicationInfo().targetSdkVersion >= O_MR1 && mActivityInfo.isFixedOrientation()) { - final TypedArray ta = obtainStyledAttributes(com.android.internal.R.styleable.Window); - final boolean isTranslucentOrFloating = ActivityInfo.isTranslucentOrFloating(ta); - ta.recycle(); - - if (isTranslucentOrFloating) { - throw new IllegalStateException( - "Only fullscreen opaque activities can request orientation"); - } - } - if (mLastNonConfigurationInstances != null) { mFragments.restoreLoaderNonConfig(mLastNonConfigurationInstances.loaders); } diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 08821bebd57e..0c80deaba910 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -135,6 +135,7 @@ interface INotificationManager void setNotificationPolicy(String pkg, in NotificationManager.Policy policy); boolean isNotificationPolicyAccessGrantedForPackage(String pkg); void setNotificationPolicyAccessGranted(String pkg, boolean granted); + void setNotificationPolicyAccessGrantedForUser(String pkg, int userId, boolean granted); AutomaticZenRule getAutomaticZenRule(String id); List<ZenModeConfig.ZenRule> getZenRules(); String addAutomaticZenRule(in AutomaticZenRule automaticZenRule); diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 93338bb5b025..e0c3f75e223e 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -20,7 +20,6 @@ import android.annotation.IntDef; import android.content.Intent; import android.content.res.Configuration; import android.content.res.Configuration.NativeConfig; -import android.content.res.TypedArray; import android.os.Parcel; import android.os.Parcelable; import android.util.Printer; @@ -454,7 +453,6 @@ public class ActivityInfo extends ComponentInfo */ public static final int FLAG_TURN_SCREEN_ON = 0x1000000; - /** * @hide Bit in {@link #flags}: If set, this component will only be seen * by the system user. Only works with broadcast receivers. Set from the @@ -993,20 +991,12 @@ public class ActivityInfo extends ComponentInfo * Returns true if the activity's orientation is fixed. * @hide */ - public boolean isFixedOrientation() { + boolean isFixedOrientation() { return isFixedOrientationLandscape() || isFixedOrientationPortrait() || screenOrientation == SCREEN_ORIENTATION_LOCKED; } /** - * Returns true if the specified orientation is considered fixed. - * @hide - */ - static public boolean isFixedOrientation(int orientation) { - return isFixedOrientationLandscape(orientation) || isFixedOrientationPortrait(orientation); - } - - /** * Returns true if the activity's orientation is fixed to landscape. * @hide */ @@ -1185,25 +1175,6 @@ public class ActivityInfo extends ComponentInfo dest.writeFloat(maxAspectRatio); } - /** - * Determines whether the {@link Activity} is considered translucent or floating. - * @hide - */ - public static boolean isTranslucentOrFloating(TypedArray attributes) { - final boolean isTranslucent = - attributes.getBoolean(com.android.internal.R.styleable.Window_windowIsTranslucent, - false); - final boolean isSwipeToDismiss = !attributes.hasValue( - com.android.internal.R.styleable.Window_windowIsTranslucent) - && attributes.getBoolean( - com.android.internal.R.styleable.Window_windowSwipeToDismiss, false); - final boolean isFloating = - attributes.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating, - false); - - return isFloating || isTranslucent || isSwipeToDismiss; - } - public static final Parcelable.Creator<ActivityInfo> CREATOR = new Parcelable.Creator<ActivityInfo>() { public ActivityInfo createFromParcel(Parcel source) { diff --git a/core/java/android/content/res/ResourcesImpl.java b/core/java/android/content/res/ResourcesImpl.java index 386239cf4f93..3239212adf66 100644 --- a/core/java/android/content/res/ResourcesImpl.java +++ b/core/java/android/content/res/ResourcesImpl.java @@ -49,6 +49,8 @@ import android.util.TypedValue; import android.util.Xml; import android.view.DisplayAdjustments; +import com.android.internal.util.GrowingArrayUtils; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -117,6 +119,13 @@ public class ResourcesImpl { private final ConfigurationBoundResourceCache<StateListAnimator> mStateListAnimatorCache = new ConfigurationBoundResourceCache<>(); + // A stack of all the resourceIds already referenced when parsing a resource. This is used to + // detect circular references in the xml. + // Using a ThreadLocal variable ensures that we have different stacks for multiple parallel + // calls to ResourcesImpl + private final ThreadLocal<LookupStack> mLookupStack = + ThreadLocal.withInitial(() -> new LookupStack()); + /** Size of the cyclical cache used to map XML files to blocks. */ private static final int XML_BLOCK_CACHE_SIZE = 4; @@ -784,19 +793,29 @@ public class ResourcesImpl { final Drawable dr; Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file); + LookupStack stack = mLookupStack.get(); try { - if (file.endsWith(".xml")) { - final XmlResourceParser rp = loadXmlResourceParser( - file, id, value.assetCookie, "drawable"); - dr = Drawable.createFromXmlForDensity(wrapper, rp, density, theme); - rp.close(); - } else { - final InputStream is = mAssets.openNonAsset( - value.assetCookie, file, AssetManager.ACCESS_STREAMING); - dr = Drawable.createFromResourceStream(wrapper, value, is, file, null); - is.close(); + // Perform a linear search to check if we have already referenced this resource before. + if (stack.contains(id)) { + throw new Exception("Recursive reference in drawable"); } - } catch (Exception | StackOverflowError e) { + stack.push(id); + try { + if (file.endsWith(".xml")) { + final XmlResourceParser rp = loadXmlResourceParser( + file, id, value.assetCookie, "drawable"); + dr = Drawable.createFromXmlForDensity(wrapper, rp, density, theme); + rp.close(); + } else { + final InputStream is = mAssets.openNonAsset( + value.assetCookie, file, AssetManager.ACCESS_STREAMING); + dr = Drawable.createFromResourceStream(wrapper, value, is, file, null); + is.close(); + } + } finally { + stack.pop(); + } + } catch (Exception e) { Trace.traceEnd(Trace.TRACE_TAG_RESOURCES); final NotFoundException rnf = new NotFoundException( "File " + file + " from drawable resource ID #0x" + Integer.toHexString(id)); @@ -1377,4 +1396,29 @@ public class ResourcesImpl { } } } + + private static class LookupStack { + + // Pick a reasonable default size for the array, it is grown as needed. + private int[] mIds = new int[4]; + private int mSize = 0; + + public void push(int id) { + mIds = GrowingArrayUtils.append(mIds, mSize, id); + mSize++; + } + + public boolean contains(int id) { + for (int i = 0; i < mSize; i++) { + if (mIds[i] == id) { + return true; + } + } + return false; + } + + public void pop() { + mSize--; + } + } } diff --git a/core/java/com/android/internal/app/NightDisplayController.java b/core/java/com/android/internal/app/NightDisplayController.java index d581777ba5bf..e0e15c818a02 100644 --- a/core/java/com/android/internal/app/NightDisplayController.java +++ b/core/java/com/android/internal/app/NightDisplayController.java @@ -25,6 +25,7 @@ import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; import android.os.Looper; +import android.os.SystemProperties; import android.provider.Settings.Secure; import android.provider.Settings.System; import android.util.Slog; @@ -100,6 +101,12 @@ public final class NightDisplayController { */ public static final int COLOR_MODE_SATURATED = 2; + /** + * See com.android.server.display.DisplayTransformManager. + */ + private static final String PERSISTENT_PROPERTY_SATURATION = "persist.sys.sf.color_saturation"; + private static final String PERSISTENT_PROPERTY_NATIVE_MODE = "persist.sys.sf.native_mode"; + private final Context mContext; private final int mUserId; @@ -334,9 +341,15 @@ public final class NightDisplayController { */ public int getColorMode() { final int colorMode = System.getIntForUser(mContext.getContentResolver(), - System.DISPLAY_COLOR_MODE, COLOR_MODE_BOOSTED, mUserId); + System.DISPLAY_COLOR_MODE, -1, mUserId); if (colorMode < COLOR_MODE_NATURAL || colorMode > COLOR_MODE_SATURATED) { - return COLOR_MODE_BOOSTED; + // There still might be a legacy system property controlling color mode that we need to + // respect. + if ("1".equals(SystemProperties.get(PERSISTENT_PROPERTY_NATIVE_MODE))) { + return COLOR_MODE_SATURATED; + } + return "1.0".equals(SystemProperties.get(PERSISTENT_PROPERTY_SATURATION)) + ? COLOR_MODE_NATURAL : COLOR_MODE_BOOSTED; } return colorMode; } diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index 4d98f19f79b9..be281df3a445 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -74,7 +74,6 @@ import static android.content.res.Configuration.UI_MODE_TYPE_MASK; import static android.content.res.Configuration.UI_MODE_TYPE_VR_HEADSET; import static android.os.Build.VERSION_CODES.HONEYCOMB; import static android.os.Build.VERSION_CODES.O; -import static android.os.Build.VERSION_CODES.O_MR1; import static android.os.Process.SYSTEM_UID; import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER; import static android.view.WindowManagerPolicy.NAV_BAR_LEFT; @@ -897,7 +896,15 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo Entry ent = AttributeCache.instance().get(packageName, realTheme, com.android.internal.R.styleable.Window, userId); - fullscreen = ent != null && !ActivityInfo.isTranslucentOrFloating(ent.array); + final boolean translucent = ent != null && (ent.array.getBoolean( + com.android.internal.R.styleable.Window_windowIsTranslucent, false) + || (!ent.array.hasValue( + com.android.internal.R.styleable.Window_windowIsTranslucent) + && ent.array.getBoolean( + com.android.internal.R.styleable.Window_windowSwipeToDismiss, + false))); + fullscreen = ent != null && !ent.array.getBoolean( + com.android.internal.R.styleable.Window_windowIsFloating, false) && !translucent; noDisplay = ent != null && ent.array.getBoolean( com.android.internal.R.styleable.Window_windowNoDisplay, false); @@ -2184,11 +2191,6 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } void setRequestedOrientation(int requestedOrientation) { - if (ActivityInfo.isFixedOrientation(requestedOrientation) && !fullscreen - && appInfo.targetSdkVersion >= O_MR1) { - throw new IllegalStateException("Only fullscreen activities can request orientation"); - } - final int displayId = getDisplayId(); final Configuration displayConfig = mStackSupervisor.getDisplayOverrideConfiguration(displayId); diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 842ee9135e33..566e5f52625e 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -1196,7 +1196,6 @@ public class NotificationManagerService extends SystemService { mAccessibilityManager = am; } - // TODO: All tests should use this init instead of the one-off setters above. @VisibleForTesting void init(Looper looper, IPackageManager packageManager, @@ -2764,19 +2763,25 @@ public class NotificationManagerService extends SystemService { @Override public void setNotificationPolicyAccessGranted(String pkg, boolean granted) throws RemoteException { + setNotificationPolicyAccessGrantedForUser( + pkg, getCallingUserHandle().getIdentifier(), granted); + } + + @Override + public void setNotificationPolicyAccessGrantedForUser( + String pkg, int userId, boolean granted) { checkCallerIsSystemOrShell(); final long identity = Binder.clearCallingIdentity(); try { if (!mActivityManager.isLowRamDevice()) { mConditionProviders.setPackageOrComponentEnabled( - pkg, getCallingUserHandle().getIdentifier(), true, granted); + pkg, userId, true, granted); getContext().sendBroadcastAsUser(new Intent( NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED) .setPackage(pkg) .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), - getCallingUserHandle(), null); - + UserHandle.of(userId), null); savePolicyFile(); } } finally { @@ -2871,11 +2876,10 @@ public class NotificationManagerService extends SystemService { userId, true, granted); getContext().sendBroadcastAsUser(new Intent( - NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED) - + NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED) .setPackage(listener.getPackageName()) .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), - getCallingUserHandle(), null); + UserHandle.of(userId), null); savePolicyFile(); } @@ -2901,7 +2905,7 @@ public class NotificationManagerService extends SystemService { NotificationManager.ACTION_NOTIFICATION_POLICY_ACCESS_GRANTED_CHANGED) .setPackage(assistant.getPackageName()) .addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY), - getCallingUserHandle(), null); + UserHandle.of(userId), null); savePolicyFile(); } diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 0a7d1c1d3460..7b240c21a3a3 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -22,7 +22,6 @@ import static android.content.pm.ActivityInfo.CONFIG_ORIENTATION; import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_BEHIND; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET; -import static android.os.Build.VERSION_CODES.O_MR1; import static android.view.Display.DEFAULT_DISPLAY; import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD; import static android.view.WindowManager.LayoutParams.FLAG_SECURE; @@ -74,8 +73,6 @@ import java.io.PrintWriter; import java.util.ArrayDeque; import java.util.ArrayList; -import static android.os.Build.VERSION_CODES.O; - class AppTokenList extends ArrayList<AppWindowToken> { } @@ -1309,15 +1306,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree */ @Override int getOrientation(int candidate) { - // We do not allow non-fullscreen apps to influence orientation starting in O-MR1. While we - // do throw an exception in {@link Activity#onCreate} and - // {@link Activity#setRequestedOrientation}, we also ignore the orientation here so that - // other calculations aren't affected. - if (!fillsParent() && mTargetSdk >= O_MR1) { - // Can't specify orientation if app doesn't fill parent. - return SCREEN_ORIENTATION_UNSET; - } - if (candidate == SCREEN_ORIENTATION_BEHIND) { // Allow app to specify orientation regardless of its visibility state if the current // candidate want us to use orientation behind. I.e. the visible app on-top of this one diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java index 7ea42daa617b..0c3f2d508e07 100644 --- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -24,6 +24,7 @@ import static android.content.pm.PackageManager.PERMISSION_DENIED; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; +import static junit.framework.Assert.assertNotNull; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; import static junit.framework.Assert.fail; @@ -38,6 +39,7 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -67,6 +69,7 @@ import android.service.notification.NotificationListenerService; import android.service.notification.StatusBarNotification; import android.test.suitebuilder.annotation.SmallTest; import android.testing.AndroidTestingRunner; +import android.testing.TestableContext; import android.testing.TestableLooper; import android.testing.TestableLooper.RunWithLooper; import android.util.ArrayMap; @@ -108,7 +111,7 @@ public class NotificationManagerServiceTest extends NotificationTestCase { private IPackageManager mPackageManager; @Mock private PackageManager mPackageManagerClient; - private Context mContext = getContext(); + private TestableContext mContext = spy(getContext()); private final String PKG = mContext.getPackageName(); private TestableLooper mTestableLooper; @Mock @@ -169,12 +172,14 @@ public class NotificationManagerServiceTest extends NotificationTestCase { mTestableLooper = TestableLooper.get(this); mHandler = mNotificationManagerService.new WorkerHandler(mTestableLooper.getLooper()); // MockPackageManager - default returns ApplicationInfo with matching calling UID + mContext.setMockPackageManager(mPackageManagerClient); final ApplicationInfo applicationInfo = new ApplicationInfo(); applicationInfo.uid = mUid; when(mPackageManager.getApplicationInfo(anyString(), anyInt(), anyInt())) .thenReturn(applicationInfo); when(mPackageManagerClient.getApplicationInfoAsUser(anyString(), anyInt(), anyInt())) .thenReturn(applicationInfo); + when(mPackageManagerClient.getPackageUidAsUser(any(), anyInt())).thenReturn(mUid); final LightsManager mockLightsManager = mock(LightsManager.class); when(mockLightsManager.getLight(anyInt())).thenReturn(mock(Light.class)); when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL); @@ -222,6 +227,7 @@ public class NotificationManagerServiceTest extends NotificationTestCase { mBinderService.createNotificationChannels( PKG, new ParceledListSlice(Arrays.asList(mTestNotificationChannel))); + assertNotNull(mBinderService.getNotificationChannel(PKG, TEST_CHANNEL_ID)); } @After @@ -1311,6 +1317,72 @@ public class NotificationManagerServiceTest extends NotificationTestCase { } @Test + public void testSetListenerAccessForUser() throws Exception { + UserHandle user = UserHandle.of(10); + ComponentName c = ComponentName.unflattenFromString("package/Component"); + try { + mBinderService.setNotificationListenerAccessGrantedForUser( + c, user.getIdentifier(), true); + } catch (SecurityException e) { + if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { + throw e; + } + } + + verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any()); + verify(mListeners, times(1)).setPackageOrComponentEnabled( + c.flattenToString(), user.getIdentifier(), true, true); + verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( + c.flattenToString(), user.getIdentifier(), false, true); + verify(mAssistants, never()).setPackageOrComponentEnabled( + any(), anyInt(), anyBoolean(), anyBoolean()); + } + + @Test + public void testSetAssistantAccessForUser() throws Exception { + UserHandle user = UserHandle.of(10); + ComponentName c = ComponentName.unflattenFromString("package/Component"); + try { + mBinderService.setNotificationAssistantAccessGrantedForUser( + c, user.getIdentifier(), true); + } catch (SecurityException e) { + if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { + throw e; + } + } + + verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any()); + verify(mAssistants, times(1)).setPackageOrComponentEnabled( + c.flattenToString(), user.getIdentifier(), true, true); + verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( + c.flattenToString(), user.getIdentifier(), false, true); + verify(mListeners, never()).setPackageOrComponentEnabled( + any(), anyInt(), anyBoolean(), anyBoolean()); + } + + @Test + public void testSetDndAccessForUser() throws Exception { + UserHandle user = UserHandle.of(10); + ComponentName c = ComponentName.unflattenFromString("package/Component"); + try { + mBinderService.setNotificationPolicyAccessGrantedForUser( + c.getPackageName(), user.getIdentifier(), true); + } catch (SecurityException e) { + if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { + throw e; + } + } + + verify(mContext, times(1)).sendBroadcastAsUser(any(), eq(user), any()); + verify(mConditionProviders, times(1)).setPackageOrComponentEnabled( + c.getPackageName(), user.getIdentifier(), true, true); + verify(mAssistants, never()).setPackageOrComponentEnabled( + any(), anyInt(), anyBoolean(), anyBoolean()); + verify(mListeners, never()).setPackageOrComponentEnabled( + any(), anyInt(), anyBoolean(), anyBoolean()); + } + + @Test public void testSetListenerAccess() throws Exception { ComponentName c = ComponentName.unflattenFromString("package/Component"); try { diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java index 9ad7addab858..25d700f97805 100644 --- a/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowTokenTests.java @@ -193,7 +193,7 @@ public class AppWindowTokenTests extends WindowTestsBase { token.setOrientation(SCREEN_ORIENTATION_LANDSCAPE); token.setFillsParent(false); - // Can specify orientation if app doesn't fill parent. Allowed for SDK <= 25. + // Can specify orientation if doesn't fill parent. assertEquals(SCREEN_ORIENTATION_LANDSCAPE, token.getOrientation()); token.setFillsParent(true); diff --git a/tools/aapt2/compile/InlineXmlFormatParser.cpp b/tools/aapt2/compile/InlineXmlFormatParser.cpp index 857cdd5365a0..73a90da6baf0 100644 --- a/tools/aapt2/compile/InlineXmlFormatParser.cpp +++ b/tools/aapt2/compile/InlineXmlFormatParser.cpp @@ -146,6 +146,10 @@ bool InlineXmlFormatParser::Consume(IAaptContext* context, xml::XmlResource* doc } else { new_doc->root.reset(static_cast<xml::Element*>(child.release())); new_doc->root->parent = nullptr; + // Copy down the namespace declarations + new_doc->root->namespace_decls = doc->root->namespace_decls; + // Recurse for nested inlines + Consume(context, new_doc.get()); } } diff --git a/tools/aapt2/compile/InlineXmlFormatParser_test.cpp b/tools/aapt2/compile/InlineXmlFormatParser_test.cpp index de7739ada407..a4c602c29b86 100644 --- a/tools/aapt2/compile/InlineXmlFormatParser_test.cpp +++ b/tools/aapt2/compile/InlineXmlFormatParser_test.cpp @@ -137,4 +137,47 @@ TEST(InlineXmlFormatParserTest, ExtractTwoXmlResources) { EXPECT_THAT(extracted_doc_drawable->root->name, StrEq("vector")); } +TEST(InlineXmlFormatParserTest, ExtractNestedXmlResources) { + std::unique_ptr<IAaptContext> context = test::ContextBuilder().Build(); + std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(R"( + <base_root xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt"> + <aapt:attr name="inline_xml"> + <inline_root> + <aapt:attr name="nested_inline_xml"> + <nested_inline_root/> + </aapt:attr> + <aapt:attr name="another_nested_inline_xml"> + <root/> + </aapt:attr> + </inline_root> + </aapt:attr> + <aapt:attr name="turtles"> + <root1> + <aapt:attr name="all"> + <root2> + <aapt:attr name="the"> + <root3> + <aapt:attr name="way"> + <root4> + <aapt:attr name="down"> + <root5/> + </aapt:attr> + </root4> + </aapt:attr> + </root3> + </aapt:attr> + </root2> + </aapt:attr> + </root1> + </aapt:attr> + </base_root>)"); + + doc->file.name = test::ParseNameOrDie("layout/main"); + + InlineXmlFormatParser parser; + ASSERT_TRUE(parser.Consume(context.get(), doc.get())); + // Confirm that all of the nested inline xmls are parsed out. + ASSERT_THAT(parser.GetExtractedInlineXmlDocuments(), SizeIs(8u)); +} } // namespace aapt |