diff options
-rw-r--r-- | api/current.txt | 1 | ||||
-rw-r--r-- | api/system-current.txt | 1 | ||||
-rw-r--r-- | api/test-current.txt | 1 | ||||
-rw-r--r-- | core/java/android/app/Activity.java | 4 | ||||
-rw-r--r-- | core/java/android/provider/AlarmClock.java | 9 | ||||
-rw-r--r-- | core/java/android/view/View.java | 7 | ||||
-rw-r--r-- | core/java/android/view/ViewGroup.java | 125 | ||||
-rw-r--r-- | core/java/android/view/ViewOverlay.java | 3 | ||||
-rw-r--r-- | core/java/android/view/ViewRootImpl.java | 4 | ||||
-rw-r--r-- | core/tests/coretests/Android.mk | 4 | ||||
-rw-r--r-- | core/tests/coretests/src/android/view/ViewInvalidateTest.java | 265 | ||||
-rw-r--r-- | services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java | 59 |
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); } |