Snap for 4431872 from 250714c67ba9d2029bc88284672949a87b243b9a to oc-m2-release

Change-Id: I959e83ca9924df486c7e27876a5a354a8d46df9e
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 26f5ba13..2c04f8f 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 @@
     @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 08821be..0c80dea 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -135,6 +135,7 @@
     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 93338bb..e0c3f75 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -20,7 +20,6 @@
 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 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 @@
      * 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 @@
         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 386239c..3239212 100644
--- a/core/java/android/content/res/ResourcesImpl.java
+++ b/core/java/android/content/res/ResourcesImpl.java
@@ -49,6 +49,8 @@
 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 @@
     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 @@
         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 @@
             }
         }
     }
+
+    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 d581777..e0e15c8 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.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 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 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 4d98f19..be281df 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_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 @@
 
         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 @@
     }
 
     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 842ee91..566e5f5 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 @@
         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 @@
         @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 @@
                             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 @@
                             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 0a7d1c1..7b240c2 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_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.util.ArrayDeque;
 import java.util.ArrayList;
 
-import static android.os.Build.VERSION_CODES.O;
-
 class AppTokenList extends ArrayList<AppWindowToken> {
 }
 
@@ -1309,15 +1306,6 @@
      */
     @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 7ea42da..0c3f2d5 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 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.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.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 @@
     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 @@
         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 @@
 
         mBinderService.createNotificationChannels(
                 PKG, new ParceledListSlice(Arrays.asList(mTestNotificationChannel)));
+        assertNotNull(mBinderService.getNotificationChannel(PKG, TEST_CHANNEL_ID));
     }
 
     @After
@@ -1311,6 +1317,72 @@
     }
 
     @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 9ad7add..25d700f 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 @@
         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 857cdd5..73a90da 100644
--- a/tools/aapt2/compile/InlineXmlFormatParser.cpp
+++ b/tools/aapt2/compile/InlineXmlFormatParser.cpp
@@ -146,6 +146,10 @@
       } 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 de7739a..a4c602c 100644
--- a/tools/aapt2/compile/InlineXmlFormatParser_test.cpp
+++ b/tools/aapt2/compile/InlineXmlFormatParser_test.cpp
@@ -137,4 +137,47 @@
   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