summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt1
-rw-r--r--api/system-current.txt1
-rw-r--r--api/test-current.txt1
-rw-r--r--core/java/android/app/Activity.java4
-rw-r--r--core/java/android/provider/AlarmClock.java9
-rw-r--r--core/java/android/view/View.java7
-rw-r--r--core/java/android/view/ViewGroup.java125
-rw-r--r--core/java/android/view/ViewOverlay.java3
-rw-r--r--core/java/android/view/ViewRootImpl.java4
-rw-r--r--core/tests/coretests/Android.mk4
-rw-r--r--core/tests/coretests/src/android/view/ViewInvalidateTest.java265
-rw-r--r--services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java59
12 files changed, 451 insertions, 32 deletions
diff --git a/api/current.txt b/api/current.txt
index 2b6deaef2053..a4402bb7987e 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -30933,6 +30933,7 @@ package android.provider {
field public static final java.lang.String ACTION_SET_ALARM = "android.intent.action.SET_ALARM";
field public static final java.lang.String ACTION_SET_TIMER = "android.intent.action.SET_TIMER";
field public static final java.lang.String ACTION_SHOW_ALARMS = "android.intent.action.SHOW_ALARMS";
+ field public static final java.lang.String ACTION_SHOW_TIMERS = "android.intent.action.SHOW_TIMERS";
field public static final java.lang.String ACTION_SNOOZE_ALARM = "android.intent.action.SNOOZE_ALARM";
field public static final java.lang.String ALARM_SEARCH_MODE_ALL = "android.all";
field public static final java.lang.String ALARM_SEARCH_MODE_LABEL = "android.label";
diff --git a/api/system-current.txt b/api/system-current.txt
index ad9590435d10..2ec564302d8c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -33653,6 +33653,7 @@ package android.provider {
field public static final java.lang.String ACTION_SET_ALARM = "android.intent.action.SET_ALARM";
field public static final java.lang.String ACTION_SET_TIMER = "android.intent.action.SET_TIMER";
field public static final java.lang.String ACTION_SHOW_ALARMS = "android.intent.action.SHOW_ALARMS";
+ field public static final java.lang.String ACTION_SHOW_TIMERS = "android.intent.action.SHOW_TIMERS";
field public static final java.lang.String ACTION_SNOOZE_ALARM = "android.intent.action.SNOOZE_ALARM";
field public static final java.lang.String ALARM_SEARCH_MODE_ALL = "android.all";
field public static final java.lang.String ALARM_SEARCH_MODE_LABEL = "android.label";
diff --git a/api/test-current.txt b/api/test-current.txt
index 7bb471eaf701..7e05b785524e 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -31046,6 +31046,7 @@ package android.provider {
field public static final java.lang.String ACTION_SET_ALARM = "android.intent.action.SET_ALARM";
field public static final java.lang.String ACTION_SET_TIMER = "android.intent.action.SET_TIMER";
field public static final java.lang.String ACTION_SHOW_ALARMS = "android.intent.action.SHOW_ALARMS";
+ field public static final java.lang.String ACTION_SHOW_TIMERS = "android.intent.action.SHOW_TIMERS";
field public static final java.lang.String ACTION_SNOOZE_ALARM = "android.intent.action.SNOOZE_ALARM";
field public static final java.lang.String ALARM_SEARCH_MODE_ALL = "android.all";
field public static final java.lang.String ALARM_SEARCH_MODE_LABEL = "android.label";
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 2ccfe0e3b72d..0d9e8a0afad2 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4341,8 +4341,8 @@ public class Activity extends ContextThemeWrapper
* that are defined to return a result. In other protocols (such as
* {@link Intent#ACTION_MAIN} or {@link Intent#ACTION_VIEW}), you may
* not get the result when you expect. For example, if the activity you
- * are launching uses the singleTask launch mode, it will not run in your
- * task and thus you will immediately receive a cancel result.
+ * are launching uses {@link Intent#FLAG_ACTIVITY_NEW_TASK}, it will not
+ * run in your task and thus you will immediately receive a cancel result.
*
* <p>As a special case, if you call startActivityForResult() with a requestCode
* >= 0 during the initial onCreate(Bundle savedInstanceState)/onResume() of your
diff --git a/core/java/android/provider/AlarmClock.java b/core/java/android/provider/AlarmClock.java
index 63ae9a96c8a6..23134cd0fef7 100644
--- a/core/java/android/provider/AlarmClock.java
+++ b/core/java/android/provider/AlarmClock.java
@@ -153,6 +153,15 @@ public final class AlarmClock {
public static final String ACTION_SET_TIMER = "android.intent.action.SET_TIMER";
/**
+ * Activity Action: Show the timers.
+ * <p>
+ * This action opens the timers page.
+ * </p>
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_SHOW_TIMERS = "android.intent.action.SHOW_TIMERS";
+
+ /**
* Activity Action: Show the alarms.
* <p>
* This action opens the alarms page.
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index ecc0f2d8f03c..0b1dfa2a321f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -3313,7 +3313,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
@ViewDebug.FlagToString(mask = PFLAG_DIRTY_MASK, equals = PFLAG_DIRTY_OPAQUE, name = "DIRTY_OPAQUE"),
@ViewDebug.FlagToString(mask = PFLAG_DIRTY_MASK, equals = PFLAG_DIRTY, name = "DIRTY")
}, formatToHexString = true)
- int mPrivateFlags;
+
+ /* @hide */
+ public int mPrivateFlags;
int mPrivateFlags2;
int mPrivateFlags3;
@@ -14110,8 +14112,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* invalidated as well. This is usually true for a full
* invalidate, but may be set to false if the View's contents or
* dimensions have not changed.
+ * @hide
*/
- void invalidate(boolean invalidateCache) {
+ public void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index 9ab368d05a57..56501ecd9f4a 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -5340,11 +5340,96 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
}
/**
+ * HW-only, Rect-ignoring invalidation path.
+ *
+ * Returns false if this path was unable to complete successfully. This means
+ * it hit a ViewParent it doesn't recognize and needs to fall back to calculating
+ * damage area.
+ *
+ * Hardware acceleration ignores damage rectangles, since native computes damage for everything
+ * drawn by HWUI (and SW layer / drawing cache doesn't keep track of damage area).
+ *
+ * Ignores opaque dirty optimizations, always using the full PFLAG_DIRTY flag.
+ *
+ * Ignores FLAG_OPTIMIZE_INVALIDATE, since we're not computing a rect,
+ * so no point in optimizing that.
+ * @hide
+ */
+ public boolean tryInvalidateChildHardware(View child) {
+ final AttachInfo attachInfo = mAttachInfo;
+ if (attachInfo == null || !attachInfo.mHardwareAccelerated) {
+ return false;
+ }
+
+ // verify it's ViewGroups up to a ViewRootImpl
+ ViewRootImpl viewRoot = null;
+ ViewParent parent = getParent();
+ while (parent != null) {
+ if (parent instanceof ViewGroup) {
+ parent = parent.getParent();
+ } else if (parent instanceof ViewRootImpl) {
+ viewRoot = (ViewRootImpl) parent;
+ break;
+ } else {
+ // unknown parent type, abort
+ return false;
+ }
+ }
+ if (viewRoot == null) {
+ // unable to find ViewRoot
+ return false;
+ }
+
+ final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0;
+
+ if (child.mLayerType != LAYER_TYPE_NONE) {
+ mPrivateFlags |= PFLAG_INVALIDATED;
+ }
+
+ parent = this;
+ do {
+ if (parent != viewRoot) {
+ // Note: we cast here without checking isinstance, to avoid cost of isinstance again
+ ViewGroup viewGroup = (ViewGroup) parent;
+ if (drawAnimation) {
+ viewGroup.mPrivateFlags |= PFLAG_DRAW_ANIMATION;
+ }
+
+ // We lazily use PFLAG_DIRTY, since computing opaque isn't worth the potential
+ // optimization in provides in a DisplayList world.
+ viewGroup.mPrivateFlags =
+ (viewGroup.mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DIRTY;
+
+ // simplified invalidateChildInParent behavior: clear cache validity to be safe,
+ // and mark inval if in layer
+ viewGroup.mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
+ if (viewGroup.mLayerType != LAYER_TYPE_NONE) {
+ viewGroup.mPrivateFlags |= PFLAG_INVALIDATED;
+ }
+ } else {
+ if (drawAnimation) {
+ viewRoot.mIsAnimating = true;
+ }
+ ((ViewRootImpl) parent).invalidate();
+ return true;
+ }
+
+ parent = parent.getParent();
+ } while (parent != null);
+ return true;
+ }
+
+
+ /**
* Don't call or override this method. It is used for the implementation of
* the view hierarchy.
*/
@Override
public final void invalidateChild(View child, final Rect dirty) {
+ if (tryInvalidateChildHardware(child)) {
+ return;
+ }
+
ViewParent parent = this;
final AttachInfo attachInfo = mAttachInfo;
@@ -5352,8 +5437,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
// If the child is drawing an animation, we want to copy this flag onto
// ourselves and the parent to make sure the invalidate request goes
// through
- final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION)
- == PFLAG_DRAW_ANIMATION;
+ final boolean drawAnimation = (child.mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0;
// Check whether the child that requests the invalidate is fully opaque
// Views being animated or transformed are not considered opaque because we may
@@ -5454,10 +5538,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
*/
@Override
public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
- if ((mPrivateFlags & PFLAG_DRAWN) == PFLAG_DRAWN ||
- (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == PFLAG_DRAWING_CACHE_VALID) {
- if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) !=
- FLAG_OPTIMIZE_INVALIDATE) {
+ if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_DRAWING_CACHE_VALID)) != 0) {
+ // either DRAWN, or DRAWING_CACHE_VALID
+ if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE))
+ != FLAG_OPTIMIZE_INVALIDATE) {
dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,
location[CHILD_TOP_INDEX] - mScrollY);
if ((mGroupFlags & FLAG_CLIP_CHILDREN) == 0) {
@@ -5472,35 +5556,28 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
dirty.setEmpty();
}
}
- mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
location[CHILD_LEFT_INDEX] = left;
location[CHILD_TOP_INDEX] = top;
-
- if (mLayerType != LAYER_TYPE_NONE) {
- mPrivateFlags |= PFLAG_INVALIDATED;
- }
-
- return mParent;
-
} else {
- mPrivateFlags &= ~PFLAG_DRAWN & ~PFLAG_DRAWING_CACHE_VALID;
- location[CHILD_LEFT_INDEX] = mLeft;
- location[CHILD_TOP_INDEX] = mTop;
if ((mGroupFlags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
dirty.set(0, 0, mRight - mLeft, mBottom - mTop);
} else {
// in case the dirty rect extends outside the bounds of this container
dirty.union(0, 0, mRight - mLeft, mBottom - mTop);
}
+ location[CHILD_LEFT_INDEX] = mLeft;
+ location[CHILD_TOP_INDEX] = mTop;
- if (mLayerType != LAYER_TYPE_NONE) {
- mPrivateFlags |= PFLAG_INVALIDATED;
- }
-
- return mParent;
+ mPrivateFlags &= ~PFLAG_DRAWN;
+ }
+ mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
+ if (mLayerType != LAYER_TYPE_NONE) {
+ mPrivateFlags |= PFLAG_INVALIDATED;
}
+
+ return mParent;
}
return null;
@@ -5513,7 +5590,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* damage area
* @hide
*/
- public boolean damageChildDeferred(View child) {
+ public boolean damageChildDeferred() {
ViewParent parent = getParent();
while (parent != null) {
if (parent instanceof ViewGroup) {
@@ -5536,7 +5613,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
* @hide
*/
public void damageChild(View child, final Rect dirty) {
- if (damageChildDeferred(child)) {
+ if (damageChildDeferred()) {
return;
}
diff --git a/core/java/android/view/ViewOverlay.java b/core/java/android/view/ViewOverlay.java
index b770bd50c277..61cf0c7555c2 100644
--- a/core/java/android/view/ViewOverlay.java
+++ b/core/java/android/view/ViewOverlay.java
@@ -283,8 +283,9 @@ public class ViewOverlay {
}
}
+ /** @hide */
@Override
- void invalidate(boolean invalidateCache) {
+ public void invalidate(boolean invalidateCache) {
super.invalidate(invalidateCache);
if (mHostView != null) {
mHostView.invalidate(invalidateCache);
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 68cd10cf262d..d6db634e6c64 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -240,7 +240,7 @@ public final class ViewRootImpl implements ViewParent,
int mWidth;
int mHeight;
Rect mDirty;
- boolean mIsAnimating;
+ public boolean mIsAnimating;
private boolean mDragResizing;
private boolean mInvalidateRootRequested;
@@ -261,7 +261,7 @@ public final class ViewRootImpl implements ViewParent,
final Rect mTempRect; // used in the transaction to not thrash the heap.
final Rect mVisRect; // used to retrieve visible rect of focused view.
- boolean mTraversalScheduled;
+ public boolean mTraversalScheduled;
int mTraversalBarrier;
boolean mWillDrawSoon;
/** Set to true while in performTraversals for detecting when die(true) is called from internal
diff --git a/core/tests/coretests/Android.mk b/core/tests/coretests/Android.mk
index 4699fd5f16e0..a0a9e0129152 100644
--- a/core/tests/coretests/Android.mk
+++ b/core/tests/coretests/Android.mk
@@ -33,7 +33,9 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
mockito-target-minus-junit4 \
espresso-core \
ub-uiautomator \
- platform-test-annotations
+ platform-test-annotations \
+ compatibility-device-util
+
LOCAL_JAVA_LIBRARIES := android.test.runner conscrypt telephony-common org.apache.http.legacy
LOCAL_PACKAGE_NAME := FrameworksCoreTests
diff --git a/core/tests/coretests/src/android/view/ViewInvalidateTest.java b/core/tests/coretests/src/android/view/ViewInvalidateTest.java
new file mode 100644
index 000000000000..4db70ec4a1c4
--- /dev/null
+++ b/core/tests/coretests/src/android/view/ViewInvalidateTest.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2016 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 android.view;
+
+import static junit.framework.Assert.assertFalse;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Rect;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.LargeTest;
+import android.support.test.rule.ActivityTestRule;
+import android.support.test.runner.AndroidJUnit4;
+import android.widget.FrameLayout;
+
+import com.android.compatibility.common.util.WidgetTestUtils;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test of invalidates, drawing, and the flags that support them
+ */
+@LargeTest
+@RunWith(AndroidJUnit4.class)
+public class ViewInvalidateTest {
+ @Rule
+ public ActivityTestRule<Activity> mActivityRule = new ActivityTestRule<>(Activity.class);
+
+ private static final int INVAL_TEST_FLAG_MASK = View.PFLAG_DIRTY
+ | View.PFLAG_DIRTY_OPAQUE
+ | View.PFLAG_DRAWN
+ | View.PFLAG_DRAWING_CACHE_VALID
+ | View.PFLAG_INVALIDATED
+ | View.PFLAG_DRAW_ANIMATION;
+
+ @Before
+ public void setup() throws Throwable {
+ // separate runnable to initialize, so ref is safe to pass to runOnMainAndDrawSync
+ mActivityRule.runOnUiThread(() -> {
+ mParent = new FrameLayout(getContext());
+ mChild = new View(getContext());
+ });
+
+ // attached view is drawn once
+ WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mParent, () -> {
+ mParent.addView(mChild);
+ getActivity().setContentView(mParent);
+
+ // 'invalidated', but not yet drawn
+ validateInvalFlags(mChild, View.PFLAG_INVALIDATED);
+ });
+ }
+
+ @After
+ public void teardown() {
+ // ensure we don't share views between tests
+ mParent = null;
+ mChild = null;
+ }
+
+ Context getContext() {
+ return InstrumentationRegistry.getTargetContext();
+ }
+
+ Activity getActivity() {
+ return mActivityRule.getActivity();
+ }
+
+ private ViewGroup mParent;
+ private View mChild;
+
+ private static void validateInvalFlags(View view, int... expectedFlagArray) {
+ int expectedFlags = 0;
+ for (int expectedFlag : expectedFlagArray) {
+ expectedFlags |= expectedFlag;
+ }
+
+ final int observedFlags = view.mPrivateFlags & INVAL_TEST_FLAG_MASK;
+ assertEquals(String.format("expect %x, observed %x", expectedFlags, observedFlags),
+ expectedFlags, observedFlags);
+ }
+
+ private static ViewRootImpl getViewRoot(View view) {
+ ViewParent parent = view.getParent();
+ while (parent != null) {
+ if (parent instanceof ViewRootImpl) {
+ return (ViewRootImpl) parent;
+ }
+ parent = parent.getParent();
+ }
+ return null;
+ }
+
+ @UiThreadTest
+ @Test
+ public void testInvalidate_behavior() throws Throwable {
+ validateInvalFlags(mChild,
+ View.PFLAG_DRAWING_CACHE_VALID,
+ View.PFLAG_DRAWN);
+ validateInvalFlags(mParent,
+ View.PFLAG_DRAWING_CACHE_VALID,
+ View.PFLAG_DRAWN);
+ assertFalse(getViewRoot(mParent).mTraversalScheduled);
+
+ mChild.invalidate();
+
+ // no longer drawn, is now invalidated
+ validateInvalFlags(mChild,
+ View.PFLAG_DIRTY,
+ View.PFLAG_INVALIDATED);
+
+ // parent drawing cache no longer valid, marked dirty
+ validateInvalFlags(mParent,
+ View.PFLAG_DRAWN,
+ View.PFLAG_DIRTY);
+ assertTrue(getViewRoot(mParent).mTraversalScheduled);
+ }
+
+ @UiThreadTest
+ @Test
+ public void testInvalidate_false() {
+ // Invalidate makes it invalid
+ validateInvalFlags(mChild,
+ View.PFLAG_DRAWING_CACHE_VALID,
+ View.PFLAG_DRAWN);
+
+ mChild.invalidate(/*don't invalidate cache*/ false);
+
+ // drawn is cleared, dirty set, nothing else changed
+ validateInvalFlags(mChild,
+ View.PFLAG_DRAWING_CACHE_VALID,
+ View.PFLAG_DIRTY);
+ }
+
+ @Test
+ public void testInvalidate_simple() throws Throwable {
+ // simple invalidate, which marks the view invalid
+ WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mParent, () -> {
+ validateInvalFlags(mChild,
+ View.PFLAG_DRAWING_CACHE_VALID,
+ View.PFLAG_DRAWN);
+
+ mChild.invalidate();
+
+ validateInvalFlags(mChild,
+ View.PFLAG_DIRTY,
+ View.PFLAG_INVALIDATED);
+ });
+
+ // after draw pass, view has drawn, no longer invalid
+ mActivityRule.runOnUiThread(() -> {
+ validateInvalFlags(mChild,
+ View.PFLAG_DRAWING_CACHE_VALID,
+ View.PFLAG_DRAWN);
+ });
+ }
+
+ @UiThreadTest
+ @Test
+ public void testInvalidate_manualUpdateDisplayList() {
+ // Invalidate makes it invalid
+ validateInvalFlags(mChild,
+ View.PFLAG_DRAWING_CACHE_VALID,
+ View.PFLAG_DRAWN);
+
+ mChild.invalidate();
+ validateInvalFlags(mChild,
+ View.PFLAG_DIRTY,
+ View.PFLAG_INVALIDATED);
+
+ // updateDisplayListIfDirty makes it valid again, but invalidate still set,
+ // since it's cleared by View#draw(canvas, parent, drawtime)
+ mChild.updateDisplayListIfDirty();
+ validateInvalFlags(mChild,
+ View.PFLAG_DRAWING_CACHE_VALID,
+ View.PFLAG_DRAWN,
+ View.PFLAG_INVALIDATED);
+ }
+
+ @UiThreadTest
+ @Test
+ public void testInvalidateChild_simple() {
+ validateInvalFlags(mParent,
+ View.PFLAG_DRAWING_CACHE_VALID,
+ View.PFLAG_DRAWN);
+ assertFalse(getViewRoot(mParent).mTraversalScheduled);
+
+ mParent.invalidateChild(mChild, new Rect(0, 0, 1, 1));
+
+ validateInvalFlags(mParent,
+ View.PFLAG_DIRTY,
+ View.PFLAG_DRAWN);
+ assertTrue(getViewRoot(mParent).mTraversalScheduled);
+ }
+
+ @Test
+ public void testInvalidateChild_childHardwareLayer() throws Throwable {
+ WidgetTestUtils.runOnMainAndDrawSync(mActivityRule, mParent, () -> {
+ // do in runnable, so tree won't be dirty
+ mChild.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ });
+
+ mActivityRule.runOnUiThread(() -> {
+ validateInvalFlags(mParent,
+ View.PFLAG_DRAWING_CACHE_VALID,
+ View.PFLAG_DRAWN);
+
+ mParent.invalidateChild(mChild, new Rect(0, 0, 1, 1));
+
+ validateInvalFlags(mParent,
+ View.PFLAG_DIRTY,
+ View.PFLAG_DRAWN,
+ View.PFLAG_INVALIDATED);
+ });
+ }
+
+ @UiThreadTest
+ @Test
+ public void testInvalidateChild_legacyAnimation() throws Throwable {
+ mChild.mPrivateFlags |= View.PFLAG_DRAW_ANIMATION;
+
+ validateInvalFlags(mChild,
+ View.PFLAG_DRAW_ANIMATION,
+ View.PFLAG_DRAWING_CACHE_VALID,
+ View.PFLAG_DRAWN);
+ validateInvalFlags(mParent,
+ View.PFLAG_DRAWING_CACHE_VALID,
+ View.PFLAG_DRAWN);
+ assertFalse(getViewRoot(mParent).mIsAnimating);
+
+ mParent.invalidateChild(mChild, new Rect(0, 0, 1, 1));
+
+ validateInvalFlags(mChild,
+ View.PFLAG_DRAW_ANIMATION,
+ View.PFLAG_DRAWING_CACHE_VALID,
+ View.PFLAG_DRAWN);
+ validateInvalFlags(mParent,
+ View.PFLAG_DIRTY,
+ View.PFLAG_DRAW_ANIMATION, // carried up to parent
+ View.PFLAG_DRAWN);
+ assertTrue(getViewRoot(mParent).mIsAnimating);
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index d39245992d49..182f0457de3d 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -2289,6 +2289,11 @@ public class DevicePolicyManagerTest extends DpmTestBase {
setUpPackageManagerForAdmin(admin1, mContext.binder.callingUid);
mContext.packageName = admin1.getPackageName();
+ final ComponentName adminDifferentPackage =
+ new ComponentName("another.package", "whatever.random.class");
+ final int ANOTHER_UID = UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE, 948);
+ setUpPackageManagerForFakeAdmin(adminDifferentPackage, ANOTHER_UID, admin2);
+
// COMP mode is allowed.
assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
@@ -2304,12 +2309,66 @@ public class DevicePolicyManagerTest extends DpmTestBase {
.thenReturn(UserManager.RESTRICTION_SOURCE_DEVICE_OWNER);
assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
+ // But another app should not
+ mContext.binder.callingUid = ANOTHER_UID;
+ mContext.packageName = adminDifferentPackage.getPackageName();
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
+
// The DO should not be allowed to initiate provisioning if the restriction is set by
// another entity.
when(mContext.userManager.getUserRestrictionSource(
eq(UserManager.DISALLOW_ADD_MANAGED_PROFILE),
eq(UserHandle.getUserHandleForUid(mContext.binder.callingUid))))
.thenReturn(UserManager.RESTRICTION_SOURCE_SYSTEM);
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ mContext.packageName = admin1.getPackageName();
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
+
+ mContext.binder.callingUid = ANOTHER_UID;
+ mContext.packageName = adminDifferentPackage.getPackageName();
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
+ }
+
+ public void testIsProvisioningAllowed_nonSplitUser_comp() throws Exception {
+ setDeviceOwner();
+ setup_nonSplitUser_afterDeviceSetup_primaryUser();
+ setUpPackageManagerForAdmin(admin1, DpmMockContext.CALLER_UID);
+
+ final ComponentName adminDifferentPackage =
+ new ComponentName("another.package", "whatever.class");
+ final int ANOTHER_UID = UserHandle.getUid(DpmMockContext.CALLER_USER_HANDLE, 948);
+ setUpPackageManagerForFakeAdmin(adminDifferentPackage, ANOTHER_UID, admin2);
+
+ final int MANAGED_PROFILE_USER_ID = 18;
+ final int MANAGED_PROFILE_ADMIN_UID = UserHandle.getUid(MANAGED_PROFILE_USER_ID, 1308);
+ addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
+
+ when(mContext.userManager.canAddMoreManagedProfiles(DpmMockContext.CALLER_USER_HANDLE,
+ false /* we can't remove a managed profile */)).thenReturn(false);
+ when(mContext.userManager.canAddMoreManagedProfiles(DpmMockContext.CALLER_USER_HANDLE,
+ true)).thenReturn(true);
+
+ // We can delete the managed profile to create a new one, so provisioning is allowed.
+ mContext.packageName = admin1.getPackageName();
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
+
+ mContext.packageName = adminDifferentPackage.getPackageName();
+ mContext.binder.callingUid = ANOTHER_UID;
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, true);
+
+ when(mContext.userManager.hasUserRestriction(
+ eq(UserManager.DISALLOW_REMOVE_MANAGED_PROFILE),
+ eq(UserHandle.of(DpmMockContext.CALLER_USER_HANDLE))))
+ .thenReturn(true);
+
+ // Now, we can't remove the profile any more to create a new one.
+ mContext.packageName = admin1.getPackageName();
+ mContext.binder.callingUid = DpmMockContext.CALLER_UID;
+ assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
+
+ mContext.packageName = adminDifferentPackage.getPackageName();
+ mContext.binder.callingUid = ANOTHER_UID;
assertProvisioningAllowed(DevicePolicyManager.ACTION_PROVISION_MANAGED_PROFILE, false);
}