Fixes #1846038. The dirty region can sometimes be modified by SurfaceFlinger. When this happens, force the view hierarchy to ignore the dirty flags.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 9e709cf..16b70ed 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1449,7 +1449,7 @@
         @ViewDebug.FlagToString(mask = LAYOUT_REQUIRED, equals = LAYOUT_REQUIRED,
                 name = "LAYOUT_REQUIRED"),
         @ViewDebug.FlagToString(mask = DRAWING_CACHE_VALID, equals = DRAWING_CACHE_VALID,
-            name = "DRAWING_CACHE_VALID", outputIf = false),
+            name = "DRAWING_CACHE_INVALID", outputIf = false),
         @ViewDebug.FlagToString(mask = DRAWN, equals = DRAWN, name = "DRAWN", outputIf = true),
         @ViewDebug.FlagToString(mask = DRAWN, equals = DRAWN, name = "NOT_DRAWN", outputIf = false),
         @ViewDebug.FlagToString(mask = DIRTY_MASK, equals = DIRTY_OPAQUE, name = "DIRTY_OPAQUE"),
@@ -5739,13 +5739,14 @@
             final int restoreCount = canvas.save();
             canvas.translate(-mScrollX, -mScrollY);
 
-            mPrivateFlags = (mPrivateFlags & ~DIRTY_MASK) | DRAWN; 
+            mPrivateFlags |= DRAWN;
 
             // Fast path for layouts with no backgrounds
             if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
                 if (ViewDebug.TRACE_HIERARCHY) {
                     ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
                 }
+                mPrivateFlags &= ~DIRTY_MASK;
                 dispatchDraw(canvas);
             } else {
                 draw(canvas);
@@ -5792,7 +5793,7 @@
             canvas = new Canvas(bitmap);
         }
 
-        if ((backgroundColor&0xff000000) != 0) {
+        if ((backgroundColor & 0xff000000) != 0) {
             bitmap.eraseColor(backgroundColor);
         }
 
@@ -5800,6 +5801,10 @@
         final int restoreCount = canvas.save();
         canvas.translate(-mScrollX, -mScrollY);
 
+        // Temporarily remove the dirty mask
+        int flags = mPrivateFlags;
+        mPrivateFlags &= ~DIRTY_MASK;
+
         // Fast path for layouts with no backgrounds
         if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
             dispatchDraw(canvas);
@@ -5807,6 +5812,8 @@
             draw(canvas);
         }
 
+        mPrivateFlags = flags;
+
         canvas.restoreToCount(restoreCount);
 
         if (attachInfo != null) {
@@ -5927,8 +5934,10 @@
             ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
         }
 
-        final boolean dirtyOpaque = (mPrivateFlags & DIRTY_MASK) == DIRTY_OPAQUE;
-        mPrivateFlags = (mPrivateFlags & ~DIRTY_MASK) | DRAWN;
+        final int privateFlags = mPrivateFlags;
+        final boolean dirtyOpaque = (privateFlags & DIRTY_MASK) == DIRTY_OPAQUE &&
+                (mAttachInfo == null || !mAttachInfo.mIgnoreDirtyState);
+        mPrivateFlags = (privateFlags & ~DIRTY_MASK) | DRAWN;
 
         /*
          * Draw traversal performs several drawing steps which must be executed
@@ -8158,7 +8167,6 @@
      * window.
      */
     static class AttachInfo {
-
         interface Callbacks {
             void playSoundEffect(int effectId);
             boolean performHapticFeedback(int effectId, boolean always);
@@ -8288,6 +8296,11 @@
         long mDrawingTime;
 
         /**
+         * Indicates whether or not ignoring the DIRTY_MASK flags.
+         */
+        boolean mIgnoreDirtyState;
+
+        /**
          * Indicates whether the view's window is currently in touch mode.
          */
         boolean mInTouchMode;
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
index 74a248f..aaaadef 100644
--- a/core/java/android/view/ViewDebug.java
+++ b/core/java/android/view/ViewDebug.java
@@ -1235,10 +1235,11 @@
         for (int j = 0; j < count; j++) {
             final FlagToString flagMapping = mapping[j];
             final boolean ifTrue = flagMapping.outputIf();
-            final boolean test = (intValue & flagMapping.mask()) == flagMapping.equals();
+            final int maskResult = intValue & flagMapping.mask();
+            final boolean test = maskResult == flagMapping.equals();
             if ((test && ifTrue) || (!test && !ifTrue)) {
                 final String name = flagMapping.name();
-                final String value = ifTrue ? "true" : "false";
+                final String value = "0x" + Integer.toHexString(maskResult);
                 writeEntry(out, prefix, name, "", value);
             }
         }
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 26fe776..db5177f 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -1403,9 +1403,9 @@
             }
         }
 
-        // Clear the flag as early as possible to allow draw() implementations
+        // Sets the flag as early as possible to allow draw() implementations
         // to call invalidate() successfully when doing animations
-        child.mPrivateFlags = (child.mPrivateFlags & ~DIRTY_MASK) | DRAWN;
+        child.mPrivateFlags |= DRAWN;
 
         if (!concatMatrix && canvas.quickReject(cl, ct, cr, cb, Canvas.EdgeType.BW) &&
                 (child.mPrivateFlags & DRAW_ANIMATION) == 0) {
@@ -1482,6 +1482,7 @@
                 if (ViewDebug.TRACE_HIERARCHY) {
                     ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
                 }
+                child.mPrivateFlags &= ~DIRTY_MASK;                
                 child.dispatchDraw(canvas);
             } else {
                 child.draw(canvas);
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 5090c56..dbfb194 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -537,7 +537,6 @@
             }
             dirty = mTempRect;
         }
-        // TODO: When doing a union with mDirty != empty, we must cancel all the DIRTY_OPAQUE flags
         mDirty.union(dirty);
         if (!mWillDrawSoon) {
             scheduleTraversals();
@@ -1142,8 +1141,7 @@
         }
         
         int yoff;
-        final boolean scrolling = mScroller != null
-                && mScroller.computeScrollOffset();
+        final boolean scrolling = mScroller != null && mScroller.computeScrollOffset();
         if (scrolling) {
             yoff = mScroller.getCurrY();
         } else {
@@ -1158,13 +1156,14 @@
         if (mUseGL) {
             if (!dirty.isEmpty()) {
                 Canvas canvas = mGlCanvas;
-                if (mGL!=null && canvas != null) {
+                if (mGL != null && canvas != null) {
                     mGL.glDisable(GL_SCISSOR_TEST);
                     mGL.glClearColor(0, 0, 0, 0);
                     mGL.glClear(GL_COLOR_BUFFER_BIT);
                     mGL.glEnable(GL_SCISSOR_TEST);
 
                     mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();
+                    mAttachInfo.mIgnoreDirtyState = true;
                     mView.mPrivateFlags |= View.DRAWN;
 
                     float scale = mAppScale;
@@ -1182,6 +1181,8 @@
                         canvas.restoreToCount(saveCount);
                     }
 
+                    mAttachInfo.mIgnoreDirtyState = false;
+
                     mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
                     checkEglErrors();
 
@@ -1201,8 +1202,10 @@
             return;
         }
 
-        if (fullRedrawNeeded)
+        if (fullRedrawNeeded) {
+            mAttachInfo.mIgnoreDirtyState = true;            
             dirty.union(0, 0, (int) (mWidth * mAppScale), (int) (mHeight * mAppScale));
+        }
 
         if (DEBUG_ORIENTATION || DEBUG_DRAW) {
             Log.v("ViewRoot", "Draw " + mView + "/"
@@ -1214,7 +1217,18 @@
 
         Canvas canvas;
         try {
+            int left = dirty.left;
+            int top = dirty.top;
+            int right = dirty.right;
+            int bottom = dirty.bottom;
+
             canvas = surface.lockCanvas(dirty);
+
+            if (left != dirty.left || top != dirty.top || right != dirty.right ||
+                    bottom != dirty.bottom) {
+                mAttachInfo.mIgnoreDirtyState = true;
+            }
+
             // TODO: Do this in native
             canvas.setDensityScale(mDensity);
         } catch (Surface.OutOfResourcesException e) {
@@ -1242,12 +1256,11 @@
                 // need to clear it before drawing so that the child will
                 // properly re-composite its drawing on a transparent
                 // background. This automatically respects the clip/dirty region
-                if (!canvas.isOpaque()) {
-                    canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR);
-                } else if (yoff != 0) {
-                    // If we are applying an offset, we need to clear the area
-                    // where the offset doesn't appear to avoid having garbage
-                    // left in the blank areas.
+                // or
+                // If we are applying an offset, we need to clear the area
+                // where the offset doesn't appear to avoid having garbage
+                // left in the blank areas.
+                if (!canvas.isOpaque() || yoff != 0) {
                     canvas.drawColor(0, PorterDuff.Mode.CLEAR);
                 }
 
@@ -1257,9 +1270,10 @@
                 mView.mPrivateFlags |= View.DRAWN;
 
                 float scale = mAppScale;
-                Context cxt = mView.getContext();
                 if (DEBUG_DRAW) {
-                    Log.i(TAG, "Drawing: package:" + cxt.getPackageName() + ", appScale=" + mAppScale);
+                    Context cxt = mView.getContext();
+                    Log.i(TAG, "Drawing: package:" + cxt.getPackageName() +
+                            ", appScale=" + mAppScale);
                 }
                 int saveCount =  canvas.save(Canvas.MATRIX_SAVE_FLAG);
                 try {
@@ -1269,14 +1283,15 @@
                         canvas.scale(scale, scale);
                     }
                     mView.draw(canvas);
-
-                    if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
-                        mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
-                    }
                 } finally {
+                    mAttachInfo.mIgnoreDirtyState = false;
                     canvas.restoreToCount(saveCount);
                 }
 
+                if (Config.DEBUG && ViewDebug.consistencyCheckEnabled) {
+                    mView.dispatchConsistencyCheck(ViewDebug.CONSISTENCY_DRAWING);
+                }
+
                 if (Config.DEBUG && ViewDebug.showFps) {
                     int now = (int)SystemClock.elapsedRealtime();
                     if (sDrawTime != 0) {