summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/current.txt10
-rw-r--r--api/system-current.txt10
-rw-r--r--api/test-current.txt10
-rw-r--r--core/java/android/content/Intent.java8
-rw-r--r--core/java/android/content/pm/IShortcutService.aidl4
-rw-r--r--core/java/android/widget/Toolbar.java191
-rw-r--r--core/java/com/android/internal/view/menu/MenuBuilder.java8
-rw-r--r--core/java/com/android/internal/view/menu/StandardMenuPopup.java12
-rw-r--r--core/res/res/values-sw600dp/dimens_material.xml2
-rw-r--r--core/res/res/values/attrs.xml8
-rw-r--r--core/res/res/values/config.xml4
-rw-r--r--core/res/res/values/dimens_material.xml2
-rw-r--r--core/res/res/values/public.xml2
-rw-r--r--core/res/res/values/styles.xml1
-rw-r--r--core/res/res/values/styles_material.xml1
-rw-r--r--core/res/res/values/symbols.xml1
-rw-r--r--libs/hwui/BakedOpState.cpp58
-rw-r--r--libs/hwui/BakedOpState.h49
-rw-r--r--libs/hwui/FrameBuilder.cpp8
-rw-r--r--libs/hwui/FrameBuilder.h2
-rw-r--r--libs/hwui/LayerBuilder.cpp34
-rw-r--r--libs/hwui/LayerBuilder.h5
-rw-r--r--libs/hwui/tests/unit/FrameBuilderTests.cpp74
-rw-r--r--libs/hwui/utils/PaintUtils.h15
-rw-r--r--services/core/java/com/android/server/pm/ShortcutLauncher.java113
-rw-r--r--services/core/java/com/android/server/pm/ShortcutPackage.java81
-rw-r--r--services/core/java/com/android/server/pm/ShortcutPackageInfo.java170
-rw-r--r--services/core/java/com/android/server/pm/ShortcutPackageItem.java60
-rw-r--r--services/core/java/com/android/server/pm/ShortcutService.java254
-rw-r--r--services/core/java/com/android/server/pm/ShortcutUser.java144
-rw-r--r--services/java/com/android/server/SystemServer.java3
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java387
-rw-r--r--tests/VectorDrawableTest/res/drawable/vector_drawable_linear_progress_bar.xml2
-rw-r--r--tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java38
34 files changed, 1003 insertions, 768 deletions
diff --git a/api/current.txt b/api/current.txt
index c4160443380f..dd70a3298aeb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -416,9 +416,11 @@ package android {
field public static final int contentAuthority = 16843408; // 0x1010290
field public static final int contentDescription = 16843379; // 0x1010273
field public static final int contentInsetEnd = 16843860; // 0x1010454
+ field public static final int contentInsetEndWithActions = 16844070; // 0x1010526
field public static final int contentInsetLeft = 16843861; // 0x1010455
field public static final int contentInsetRight = 16843862; // 0x1010456
field public static final int contentInsetStart = 16843859; // 0x1010453
+ field public static final int contentInsetStartWithNavigation = 16844069; // 0x1010525
field public static final int contextClickable = 16844007; // 0x10104e7
field public static final int contextPopupMenuStyle = 16844034; // 0x1010502
field public static final int controlX1 = 16843772; // 0x10103fc
@@ -48290,9 +48292,15 @@ package android.widget {
method public void collapseActionView();
method public void dismissPopupMenus();
method public int getContentInsetEnd();
+ method public int getContentInsetEndWithActions();
method public int getContentInsetLeft();
method public int getContentInsetRight();
method public int getContentInsetStart();
+ method public int getContentInsetStartWithNavigation();
+ method public int getCurrentContentInsetEnd();
+ method public int getCurrentContentInsetLeft();
+ method public int getCurrentContentInsetRight();
+ method public int getCurrentContentInsetStart();
method public android.graphics.drawable.Drawable getLogo();
method public java.lang.CharSequence getLogoDescription();
method public android.view.Menu getMenu();
@@ -48311,6 +48319,8 @@ package android.widget {
method public void inflateMenu(int);
method public boolean isOverflowMenuShowing();
method protected void onLayout(boolean, int, int, int, int);
+ method public void setContentInsetEndWithActions(int);
+ method public void setContentInsetStartWithNavigation(int);
method public void setContentInsetsAbsolute(int, int);
method public void setContentInsetsRelative(int, int);
method public void setLogo(int);
diff --git a/api/system-current.txt b/api/system-current.txt
index e372d98a92a1..0fe632d27d88 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -511,9 +511,11 @@ package android {
field public static final int contentAuthority = 16843408; // 0x1010290
field public static final int contentDescription = 16843379; // 0x1010273
field public static final int contentInsetEnd = 16843860; // 0x1010454
+ field public static final int contentInsetEndWithActions = 16844070; // 0x1010526
field public static final int contentInsetLeft = 16843861; // 0x1010455
field public static final int contentInsetRight = 16843862; // 0x1010456
field public static final int contentInsetStart = 16843859; // 0x1010453
+ field public static final int contentInsetStartWithNavigation = 16844069; // 0x1010525
field public static final int contextClickable = 16844007; // 0x10104e7
field public static final int contextPopupMenuStyle = 16844034; // 0x1010502
field public static final int controlX1 = 16843772; // 0x10103fc
@@ -51354,9 +51356,15 @@ package android.widget {
method public void collapseActionView();
method public void dismissPopupMenus();
method public int getContentInsetEnd();
+ method public int getContentInsetEndWithActions();
method public int getContentInsetLeft();
method public int getContentInsetRight();
method public int getContentInsetStart();
+ method public int getContentInsetStartWithNavigation();
+ method public int getCurrentContentInsetEnd();
+ method public int getCurrentContentInsetLeft();
+ method public int getCurrentContentInsetRight();
+ method public int getCurrentContentInsetStart();
method public android.graphics.drawable.Drawable getLogo();
method public java.lang.CharSequence getLogoDescription();
method public android.view.Menu getMenu();
@@ -51375,6 +51383,8 @@ package android.widget {
method public void inflateMenu(int);
method public boolean isOverflowMenuShowing();
method protected void onLayout(boolean, int, int, int, int);
+ method public void setContentInsetEndWithActions(int);
+ method public void setContentInsetStartWithNavigation(int);
method public void setContentInsetsAbsolute(int, int);
method public void setContentInsetsRelative(int, int);
method public void setLogo(int);
diff --git a/api/test-current.txt b/api/test-current.txt
index e4153f126cb8..d59fa272088d 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -416,9 +416,11 @@ package android {
field public static final int contentAuthority = 16843408; // 0x1010290
field public static final int contentDescription = 16843379; // 0x1010273
field public static final int contentInsetEnd = 16843860; // 0x1010454
+ field public static final int contentInsetEndWithActions = 16844070; // 0x1010526
field public static final int contentInsetLeft = 16843861; // 0x1010455
field public static final int contentInsetRight = 16843862; // 0x1010456
field public static final int contentInsetStart = 16843859; // 0x1010453
+ field public static final int contentInsetStartWithNavigation = 16844069; // 0x1010525
field public static final int contextClickable = 16844007; // 0x10104e7
field public static final int contextPopupMenuStyle = 16844034; // 0x1010502
field public static final int controlX1 = 16843772; // 0x10103fc
@@ -48364,9 +48366,15 @@ package android.widget {
method public void collapseActionView();
method public void dismissPopupMenus();
method public int getContentInsetEnd();
+ method public int getContentInsetEndWithActions();
method public int getContentInsetLeft();
method public int getContentInsetRight();
method public int getContentInsetStart();
+ method public int getContentInsetStartWithNavigation();
+ method public int getCurrentContentInsetEnd();
+ method public int getCurrentContentInsetLeft();
+ method public int getCurrentContentInsetRight();
+ method public int getCurrentContentInsetStart();
method public android.graphics.drawable.Drawable getLogo();
method public java.lang.CharSequence getLogoDescription();
method public android.view.Menu getMenu();
@@ -48385,6 +48393,8 @@ package android.widget {
method public void inflateMenu(int);
method public boolean isOverflowMenuShowing();
method protected void onLayout(boolean, int, int, int, int);
+ method public void setContentInsetEndWithActions(int);
+ method public void setContentInsetStartWithNavigation(int);
method public void setContentInsetsAbsolute(int, int);
method public void setContentInsetsRelative(int, int);
method public void setLogo(int);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 66e0ada27b52..10259be8d430 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1581,14 +1581,6 @@ public class Intent implements Parcelable, Cloneable {
= "android.intent.extra.UNINSTALL_ALL_USERS";
/**
- * Specified when the uninstall confirmation dialog is not required to be shown.
- * Use with {@link #ACTION_UNINSTALL_PACKAGE}
- * @hide
- */
- public static final String EXTRA_SKIP_UNINSTALL_CONFIRMATION =
- "android.intent.extra.SKIP_UNINSTALL_CONFIRMATION";
-
- /**
* A string associated with a {@link #ACTION_UPGRADE_SETUP} activity
* describing the last run version of the platform that was setup.
* @hide
diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl
index 8f9dcfc15d63..31d377b554f8 100644
--- a/core/java/android/content/pm/IShortcutService.aidl
+++ b/core/java/android/content/pm/IShortcutService.aidl
@@ -47,4 +47,8 @@ interface IShortcutService {
int getIconMaxDimensions(String packageName, int userId);
void resetThrottling(); // system only API for developer opsions
+
+ byte[] getBackupPayload(int user);
+
+ void applyRestore(in byte[] payload, int user);
} \ No newline at end of file
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index 06daf61937c1..5b0a90adde33 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -106,6 +106,8 @@ import java.util.List;
* @attr ref android.R.styleable#Toolbar_contentInsetLeft
* @attr ref android.R.styleable#Toolbar_contentInsetRight
* @attr ref android.R.styleable#Toolbar_contentInsetStart
+ * @attr ref android.R.styleable#Toolbar_contentInsetStartWithNavigation
+ * @attr ref android.R.styleable#Toolbar_contentInsetEndWithActions
* @attr ref android.R.styleable#Toolbar_gravity
* @attr ref android.R.styleable#Toolbar_logo
* @attr ref android.R.styleable#Toolbar_logoDescription
@@ -159,6 +161,8 @@ public class Toolbar extends ViewGroup {
private int mTitleMarginBottom;
private final RtlSpacingHelper mContentInsets = new RtlSpacingHelper();
+ private int mContentInsetStartWithNavigation;
+ private int mContentInsetEndWithActions;
private int mGravity = Gravity.START | Gravity.CENTER_VERTICAL;
@@ -272,6 +276,11 @@ public class Toolbar extends ViewGroup {
mContentInsets.setRelative(contentInsetStart, contentInsetEnd);
}
+ mContentInsetStartWithNavigation = a.getDimensionPixelOffset(
+ R.styleable.Toolbar_contentInsetStartWithNavigation, RtlSpacingHelper.UNDEFINED);
+ mContentInsetEndWithActions = a.getDimensionPixelOffset(
+ R.styleable.Toolbar_contentInsetEndWithActions, RtlSpacingHelper.UNDEFINED);
+
mCollapseIcon = a.getDrawable(R.styleable.Toolbar_collapseIcon);
mCollapseDescription = a.getText(R.styleable.Toolbar_collapseContentDescription);
@@ -1055,7 +1064,7 @@ public class Toolbar extends ViewGroup {
}
/**
- * Set the content insets for this toolbar relative to layout direction.
+ * Sets the content insets for this toolbar relative to layout direction.
*
* <p>The content inset affects the valid area for Toolbar content other than
* the navigation button and menu. Insets define the minimum margin for these components
@@ -1069,13 +1078,15 @@ public class Toolbar extends ViewGroup {
* @see #getContentInsetEnd()
* @see #getContentInsetLeft()
* @see #getContentInsetRight()
+ * @attr ref android.R.styleable#Toolbar_contentInsetEnd
+ * @attr ref android.R.styleable#Toolbar_contentInsetStart
*/
public void setContentInsetsRelative(int contentInsetStart, int contentInsetEnd) {
mContentInsets.setRelative(contentInsetStart, contentInsetEnd);
}
/**
- * Get the starting content inset for this toolbar.
+ * Gets the starting content inset for this toolbar.
*
* <p>The content inset affects the valid area for Toolbar content other than
* the navigation button and menu. Insets define the minimum margin for these components
@@ -1088,13 +1099,14 @@ public class Toolbar extends ViewGroup {
* @see #getContentInsetEnd()
* @see #getContentInsetLeft()
* @see #getContentInsetRight()
+ * @attr ref android.R.styleable#Toolbar_contentInsetStart
*/
public int getContentInsetStart() {
return mContentInsets.getStart();
}
/**
- * Get the ending content inset for this toolbar.
+ * Gets the ending content inset for this toolbar.
*
* <p>The content inset affects the valid area for Toolbar content other than
* the navigation button and menu. Insets define the minimum margin for these components
@@ -1107,13 +1119,14 @@ public class Toolbar extends ViewGroup {
* @see #getContentInsetStart()
* @see #getContentInsetLeft()
* @see #getContentInsetRight()
+ * @attr ref android.R.styleable#Toolbar_contentInsetEnd
*/
public int getContentInsetEnd() {
return mContentInsets.getEnd();
}
/**
- * Set the content insets for this toolbar.
+ * Sets the content insets for this toolbar.
*
* <p>The content inset affects the valid area for Toolbar content other than
* the navigation button and menu. Insets define the minimum margin for these components
@@ -1127,13 +1140,15 @@ public class Toolbar extends ViewGroup {
* @see #getContentInsetEnd()
* @see #getContentInsetLeft()
* @see #getContentInsetRight()
+ * @attr ref android.R.styleable#Toolbar_contentInsetLeft
+ * @attr ref android.R.styleable#Toolbar_contentInsetRight
*/
public void setContentInsetsAbsolute(int contentInsetLeft, int contentInsetRight) {
mContentInsets.setAbsolute(contentInsetLeft, contentInsetRight);
}
/**
- * Get the left content inset for this toolbar.
+ * Gets the left content inset for this toolbar.
*
* <p>The content inset affects the valid area for Toolbar content other than
* the navigation button and menu. Insets define the minimum margin for these components
@@ -1146,13 +1161,14 @@ public class Toolbar extends ViewGroup {
* @see #getContentInsetStart()
* @see #getContentInsetEnd()
* @see #getContentInsetRight()
+ * @attr ref android.R.styleable#Toolbar_contentInsetLeft
*/
public int getContentInsetLeft() {
return mContentInsets.getLeft();
}
/**
- * Get the right content inset for this toolbar.
+ * Gets the right content inset for this toolbar.
*
* <p>The content inset affects the valid area for Toolbar content other than
* the navigation button and menu. Insets define the minimum margin for these components
@@ -1165,11 +1181,160 @@ public class Toolbar extends ViewGroup {
* @see #getContentInsetStart()
* @see #getContentInsetEnd()
* @see #getContentInsetLeft()
+ * @attr ref android.R.styleable#Toolbar_contentInsetRight
*/
public int getContentInsetRight() {
return mContentInsets.getRight();
}
+ /**
+ * Gets the start content inset to use when a navigation button is present.
+ *
+ * <p>Different content insets are often called for when additional buttons are present
+ * in the toolbar, as well as at different toolbar sizes. The larger value of
+ * {@link #getContentInsetStart()} and this value will be used during layout.</p>
+ *
+ * @return the start content inset used when a navigation icon has been set in pixels
+ *
+ * @see #setContentInsetStartWithNavigation(int)
+ * @attr ref android.R.styleable#Toolbar_contentInsetStartWithNavigation
+ */
+ public int getContentInsetStartWithNavigation() {
+ return mContentInsetStartWithNavigation != RtlSpacingHelper.UNDEFINED
+ ? mContentInsetStartWithNavigation
+ : getContentInsetStart();
+ }
+
+ /**
+ * Sets the start content inset to use when a navigation button is present.
+ *
+ * <p>Different content insets are often called for when additional buttons are present
+ * in the toolbar, as well as at different toolbar sizes. The larger value of
+ * {@link #getContentInsetStart()} and this value will be used during layout.</p>
+ *
+ * @param insetStartWithNavigation the inset to use when a navigation icon has been set
+ * in pixels
+ *
+ * @see #getContentInsetStartWithNavigation()
+ * @attr ref android.R.styleable#Toolbar_contentInsetStartWithNavigation
+ */
+ public void setContentInsetStartWithNavigation(int insetStartWithNavigation) {
+ if (insetStartWithNavigation < 0) {
+ insetStartWithNavigation = RtlSpacingHelper.UNDEFINED;
+ }
+ if (insetStartWithNavigation != mContentInsetStartWithNavigation) {
+ mContentInsetStartWithNavigation = insetStartWithNavigation;
+ if (getNavigationIcon() != null) {
+ requestLayout();
+ }
+ }
+ }
+
+ /**
+ * Gets the end content inset to use when action buttons are present.
+ *
+ * <p>Different content insets are often called for when additional buttons are present
+ * in the toolbar, as well as at different toolbar sizes. The larger value of
+ * {@link #getContentInsetEnd()} and this value will be used during layout.</p>
+ *
+ * @return the end content inset used when a menu has been set in pixels
+ *
+ * @see #setContentInsetEndWithActions(int)
+ * @attr ref android.R.styleable#Toolbar_contentInsetEndWithActions
+ */
+ public int getContentInsetEndWithActions() {
+ return mContentInsetEndWithActions != RtlSpacingHelper.UNDEFINED
+ ? mContentInsetEndWithActions
+ : getContentInsetEnd();
+ }
+
+ /**
+ * Sets the start content inset to use when action buttons are present.
+ *
+ * <p>Different content insets are often called for when additional buttons are present
+ * in the toolbar, as well as at different toolbar sizes. The larger value of
+ * {@link #getContentInsetEnd()} and this value will be used during layout.</p>
+ *
+ * @param insetEndWithActions the inset to use when a menu has been set in pixels
+ *
+ * @see #setContentInsetEndWithActions(int)
+ * @attr ref android.R.styleable#Toolbar_contentInsetEndWithActions
+ */
+ public void setContentInsetEndWithActions(int insetEndWithActions) {
+ if (insetEndWithActions < 0) {
+ insetEndWithActions = RtlSpacingHelper.UNDEFINED;
+ }
+ if (insetEndWithActions != mContentInsetEndWithActions) {
+ mContentInsetEndWithActions = insetEndWithActions;
+ if (getNavigationIcon() != null) {
+ requestLayout();
+ }
+ }
+ }
+
+ /**
+ * Gets the content inset that will be used on the starting side of the bar in the current
+ * toolbar configuration.
+ *
+ * @return the current content inset start in pixels
+ *
+ * @see #getContentInsetStartWithNavigation()
+ */
+ public int getCurrentContentInsetStart() {
+ return getNavigationIcon() != null
+ ? Math.max(getContentInsetStart(), Math.max(mContentInsetStartWithNavigation, 0))
+ : getContentInsetStart();
+ }
+
+ /**
+ * Gets the content inset that will be used on the ending side of the bar in the current
+ * toolbar configuration.
+ *
+ * @return the current content inset end in pixels
+ *
+ * @see #getContentInsetEndWithActions()
+ */
+ public int getCurrentContentInsetEnd() {
+ boolean hasActions = false;
+ if (mMenuView != null) {
+ final MenuBuilder mb = mMenuView.peekMenu();
+ hasActions = mb != null && mb.hasVisibleItems();
+ }
+ return hasActions
+ ? Math.max(getContentInsetEnd(), Math.max(mContentInsetEndWithActions, 0))
+ : getContentInsetEnd();
+ }
+
+ /**
+ * Gets the content inset that will be used on the left side of the bar in the current
+ * toolbar configuration.
+ *
+ * @return the current content inset left in pixels
+ *
+ * @see #getContentInsetStartWithNavigation()
+ * @see #getContentInsetEndWithActions()
+ */
+ public int getCurrentContentInsetLeft() {
+ return isLayoutRtl()
+ ? getCurrentContentInsetEnd()
+ : getCurrentContentInsetStart();
+ }
+
+ /**
+ * Gets the content inset that will be used on the right side of the bar in the current
+ * toolbar configuration.
+ *
+ * @return the current content inset right in pixels
+ *
+ * @see #getContentInsetStartWithNavigation()
+ * @see #getContentInsetEndWithActions()
+ */
+ public int getCurrentContentInsetRight() {
+ return isLayoutRtl()
+ ? getCurrentContentInsetStart()
+ : getCurrentContentInsetEnd();
+ }
+
private void ensureNavButtonView() {
if (mNavButtonView == null) {
mNavButtonView = new ImageButton(getContext(), null, 0, mNavButtonStyle);
@@ -1406,7 +1571,7 @@ public class Toolbar extends ViewGroup {
childState = combineMeasuredStates(childState, mCollapseButtonView.getMeasuredState());
}
- final int contentInsetStart = getContentInsetStart();
+ final int contentInsetStart = getCurrentContentInsetStart();
width += Math.max(contentInsetStart, navWidth);
collapsingMargins[marginStartIndex] = Math.max(0, contentInsetStart - navWidth);
@@ -1420,7 +1585,7 @@ public class Toolbar extends ViewGroup {
childState = combineMeasuredStates(childState, mMenuView.getMeasuredState());
}
- final int contentInsetEnd = getContentInsetEnd();
+ final int contentInsetEnd = getCurrentContentInsetEnd();
width += Math.max(contentInsetEnd, menuWidth);
collapsingMargins[marginEndIndex] = Math.max(0, contentInsetEnd - menuWidth);
@@ -1543,10 +1708,12 @@ public class Toolbar extends ViewGroup {
}
}
- collapsingMargins[0] = Math.max(0, getContentInsetLeft() - left);
- collapsingMargins[1] = Math.max(0, getContentInsetRight() - (width - paddingRight - right));
- left = Math.max(left, getContentInsetLeft());
- right = Math.min(right, width - paddingRight - getContentInsetRight());
+ final int contentInsetLeft = getCurrentContentInsetLeft();
+ final int contentInsetRight = getCurrentContentInsetRight();
+ collapsingMargins[0] = Math.max(0, contentInsetLeft - left);
+ collapsingMargins[1] = Math.max(0, contentInsetRight - (width - paddingRight - right));
+ left = Math.max(left, contentInsetLeft);
+ right = Math.min(right, width - paddingRight - contentInsetRight);
if (shouldLayout(mExpandedActionView)) {
if (isRtl) {
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index 31b2f9619ed6..df57639a819e 100644
--- a/core/java/com/android/internal/view/menu/MenuBuilder.java
+++ b/core/java/com/android/internal/view/menu/MenuBuilder.java
@@ -65,7 +65,6 @@ public class MenuBuilder implements Menu {
private final Context mContext;
private final Resources mResources;
- private final boolean mShowCascadingMenus;
/**
* Whether the shortcuts should be qwerty-accessible. Use isQwertyMode()
@@ -188,9 +187,6 @@ public class MenuBuilder implements Menu {
public MenuBuilder(Context context) {
mContext = context;
mResources = context.getResources();
- mShowCascadingMenus = context.getResources().getBoolean(
- com.android.internal.R.bool.config_enableCascadingSubmenus);
-
mItems = new ArrayList<MenuItemImpl>();
mVisibleItems = new ArrayList<MenuItemImpl>();
@@ -915,10 +911,6 @@ public class MenuBuilder implements Menu {
close(true /* closeAllMenus */);
}
} else if (itemImpl.hasSubMenu() || providerHasSubMenu) {
- if (!mShowCascadingMenus) {
- close(false /* closeAllMenus */);
- }
-
if (!itemImpl.hasSubMenu()) {
itemImpl.setSubMenu(new SubMenuBuilder(getContext(), this, itemImpl));
}
diff --git a/core/java/com/android/internal/view/menu/StandardMenuPopup.java b/core/java/com/android/internal/view/menu/StandardMenuPopup.java
index 2cb224edf1d5..8ced36f78d75 100644
--- a/core/java/com/android/internal/view/menu/StandardMenuPopup.java
+++ b/core/java/com/android/internal/view/menu/StandardMenuPopup.java
@@ -240,7 +240,10 @@ final class StandardMenuPopup extends MenuPopup implements OnDismissListener, On
mTreeObserver = null;
}
mShownAnchorView.removeOnAttachStateChangeListener(mAttachStateChangeListener);
- mOnDismissListener.onDismiss();
+
+ if (mOnDismissListener != null) {
+ mOnDismissListener.onDismiss();
+ }
}
@Override
@@ -265,6 +268,13 @@ final class StandardMenuPopup extends MenuPopup implements OnDismissListener, On
subPopup.setPresenterCallback(mPresenterCallback);
subPopup.setForceShowIcon(mAdapter.getForceShowIcon());
+ // Pass responsibility for handling onDismiss to the submenu.
+ subPopup.setOnDismissListener(mOnDismissListener);
+ mOnDismissListener = null;
+
+ // Close this menu popup to make room for the submenu popup.
+ dismiss();
+
// Show the new sub-menu popup at the same location as this popup.
if (subPopup.tryShow(mXOffset, mYOffset)) {
if (mPresenterCallback != null) {
diff --git a/core/res/res/values-sw600dp/dimens_material.xml b/core/res/res/values-sw600dp/dimens_material.xml
index 3bbb352bca8e..1ec5c0fa5cb8 100644
--- a/core/res/res/values-sw600dp/dimens_material.xml
+++ b/core/res/res/values-sw600dp/dimens_material.xml
@@ -23,6 +23,8 @@
<dimen name="action_bar_default_height_material">64dp</dimen>
<!-- Default content inset of an action bar. -->
<dimen name="action_bar_content_inset_material">24dp</dimen>
+ <!-- Default content inset of an action bar with navigation present. -->
+ <dimen name="action_bar_content_inset_with_nav">80dp</dimen>
<!-- Default start padding of an action bar. -->
<dimen name="action_bar_default_padding_start_material">8dp</dimen>
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 00eb81a8ed98..d7381abed1fb 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -7644,6 +7644,12 @@ i
<!-- Minimum inset for content views within a bar. Navigation buttons and
menu views are excepted. Only valid for some themes and configurations. -->
<attr name="contentInsetRight" format="dimension" />
+ <!-- Minimum inset for content views within a bar when a navigation button
+ is present, such as the Up button. Only valid for some themes and configurations. -->
+ <attr name="contentInsetStartWithNavigation" format="dimension" />
+ <!-- Minimum inset for content views within a bar when actions from a menu
+ are present. Only valid for some themes and configurations. -->
+ <attr name="contentInsetEndWithActions" format="dimension" />
<!-- Elevation for the action bar itself -->
<attr name="elevation" />
<!-- Reference to a theme that should be used to inflate popups
@@ -8011,6 +8017,8 @@ i
<attr name="contentInsetEnd" />
<attr name="contentInsetLeft" />
<attr name="contentInsetRight" />
+ <attr name="contentInsetStartWithNavigation" />
+ <attr name="contentInsetEndWithActions" />
<attr name="maxButtonHeight" format="dimension" />
<attr name="navigationButtonStyle" format="reference" />
<attr name="buttonGravity">
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 6ecaa1faac55..892b3d58b388 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2497,4 +2497,8 @@
<!-- True if the device supports at least one form of multi-window.
E.g. freeform, split-screen, picture-in-picture. -->
<bool name="config_supportsMultiWindow">true</bool>
+
+ <!-- True if the device requires AppWidgetService even if it does not have
+ the PackageManager.FEATURE_APP_WIDGETS feature -->
+ <bool name="config_enableAppWidgetService">false</bool>
</resources>
diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml
index 2fe4f6652a87..ad2b335de5bb 100644
--- a/core/res/res/values/dimens_material.xml
+++ b/core/res/res/values/dimens_material.xml
@@ -41,6 +41,8 @@
<dimen name="action_bar_default_padding_end_material">0dp</dimen>
<!-- Default content inset of an action bar. -->
<dimen name="action_bar_content_inset_material">16dp</dimen>
+ <!-- Default content inset of an action bar when a navigation button is present. -->
+ <dimen name="action_bar_content_inset_with_nav">72dp</dimen>
<!-- Vertical padding around action bar icons. -->
<dimen name="action_bar_icon_vertical_padding_material">16dp</dimen>
<!-- Top margin for action bar subtitles -->
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index ac29f92fe5d3..0839187c48d1 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2711,6 +2711,8 @@
<public type="attr" name="popupExitTransition" />
<public type="attr" name="minimalHeight" />
<public type="attr" name="forceHasOverlappingRendering" />
+ <public type="attr" name="contentInsetStartWithNavigation" />
+ <public type="attr" name="contentInsetEndWithActions" />
<public type="style" name="Theme.Material.Light.DialogWhenLarge.DarkActionBar" />
<public type="style" name="Widget.Material.SeekBar.Discrete" />
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 86b9f1d1b7d5..790dcfa3f127 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1234,6 +1234,7 @@ please see styles_device_defaults.xml.
<item name="collapseIcon">?attr/homeAsUpIndicator</item>
<item name="collapseContentDescription">@string/toolbar_collapse_description</item>
<item name="contentInsetStart">16dp</item>
+ <item name="contentInsetStartWithNavigation">@dimen/action_bar_content_inset_with_nav</item>
<item name="touchscreenBlocksFocus">true</item>
</style>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index 6d4936c14f95..2420c1a96a6c 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -944,6 +944,7 @@ please see styles_device_defaults.xml.
<item name="homeLayout">@layout/action_bar_home_material</item>
<item name="gravity">center_vertical</item>
<item name="contentInsetStart">@dimen/action_bar_content_inset_material</item>
+ <item name="contentInsetStartWithNavigation">@dimen/action_bar_content_inset_with_nav</item>
<item name="contentInsetEnd">@dimen/action_bar_content_inset_material</item>
<item name="elevation">@dimen/action_bar_elevation_material</item>
<item name="popupTheme">?attr/actionBarPopupTheme</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6526571e3a11..694e9342000d 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -307,6 +307,7 @@
<java-symbol type="bool" name="config_supportsMultiWindow" />
<java-symbol type="bool" name="config_guestUserEphemeral" />
<java-symbol type="bool" name="config_localDisplaysMirrorContent" />
+ <java-symbol type="bool" name="config_enableAppWidgetService" />
<java-symbol type="string" name="config_defaultPictureInPictureBounds" />
<java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_threshold" />
<java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_factor" />
diff --git a/libs/hwui/BakedOpState.cpp b/libs/hwui/BakedOpState.cpp
index 859036543b4a..b70d5868c718 100644
--- a/libs/hwui/BakedOpState.cpp
+++ b/libs/hwui/BakedOpState.cpp
@@ -108,5 +108,63 @@ ResolvedRenderState::ResolvedRenderState(const ClipRect* clipRect, const Rect& d
clippedBounds.doIntersect(clipRect->rect);
}
+BakedOpState* BakedOpState::tryConstruct(LinearAllocator& allocator,
+ Snapshot& snapshot, const RecordedOp& recordedOp) {
+ if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
+ BakedOpState* bakedState = allocator.create_trivial<BakedOpState>(
+ allocator, snapshot, recordedOp, false);
+ if (bakedState->computedState.clippedBounds.isEmpty()) {
+ // bounds are empty, so op is rejected
+ allocator.rewindIfLastAlloc(bakedState);
+ return nullptr;
+ }
+ return bakedState;
+}
+
+BakedOpState* BakedOpState::tryConstructUnbounded(LinearAllocator& allocator,
+ Snapshot& snapshot, const RecordedOp& recordedOp) {
+ if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
+ return allocator.create_trivial<BakedOpState>(allocator, snapshot, recordedOp);
+}
+
+BakedOpState* BakedOpState::tryStrokeableOpConstruct(LinearAllocator& allocator,
+ Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior) {
+ if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
+ bool expandForStroke = (strokeBehavior == StrokeBehavior::StyleDefined)
+ ? (recordedOp.paint && recordedOp.paint->getStyle() != SkPaint::kFill_Style)
+ : true;
+
+ BakedOpState* bakedState = allocator.create_trivial<BakedOpState>(
+ allocator, snapshot, recordedOp, expandForStroke);
+ if (bakedState->computedState.clippedBounds.isEmpty()) {
+ // bounds are empty, so op is rejected
+ // NOTE: this won't succeed if a clip was allocated
+ allocator.rewindIfLastAlloc(bakedState);
+ return nullptr;
+ }
+ return bakedState;
+}
+
+BakedOpState* BakedOpState::tryShadowOpConstruct(LinearAllocator& allocator,
+ Snapshot& snapshot, const ShadowOp* shadowOpPtr) {
+ if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
+
+ // clip isn't empty, so construct the op
+ return allocator.create_trivial<BakedOpState>(allocator, snapshot, shadowOpPtr);
+}
+
+BakedOpState* BakedOpState::directConstruct(LinearAllocator& allocator,
+ const ClipRect* clip, const Rect& dstRect, const RecordedOp& recordedOp) {
+ return allocator.create_trivial<BakedOpState>(clip, dstRect, recordedOp);
+}
+
+void BakedOpState::setupOpacity(const SkPaint* paint) {
+ computedState.opaqueOverClippedBounds = computedState.transform.isSimple()
+ && computedState.clipState->mode == ClipMode::Rectangle
+ && MathUtils::areEqual(alpha, 1.0f)
+ && !roundRectClipState
+ && PaintUtils::isOpaquePaint(paint);
+}
+
} // namespace uirenderer
} // namespace android
diff --git a/libs/hwui/BakedOpState.h b/libs/hwui/BakedOpState.h
index 4e3cb8a15e24..e1441fca5ee2 100644
--- a/libs/hwui/BakedOpState.h
+++ b/libs/hwui/BakedOpState.h
@@ -93,6 +93,7 @@ public:
Rect clippedBounds;
int clipSideFlags = 0;
const SkPath* localProjectionPathMask = nullptr;
+ bool opaqueOverClippedBounds = false;
};
/**
@@ -103,23 +104,10 @@ public:
class BakedOpState {
public:
static BakedOpState* tryConstruct(LinearAllocator& allocator,
- Snapshot& snapshot, const RecordedOp& recordedOp) {
- if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
- BakedOpState* bakedState = allocator.create_trivial<BakedOpState>(
- allocator, snapshot, recordedOp, false);
- if (bakedState->computedState.clippedBounds.isEmpty()) {
- // bounds are empty, so op is rejected
- allocator.rewindIfLastAlloc(bakedState);
- return nullptr;
- }
- return bakedState;
- }
+ Snapshot& snapshot, const RecordedOp& recordedOp);
static BakedOpState* tryConstructUnbounded(LinearAllocator& allocator,
- Snapshot& snapshot, const RecordedOp& recordedOp) {
- if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
- return allocator.create_trivial<BakedOpState>(allocator, snapshot, recordedOp);
- }
+ Snapshot& snapshot, const RecordedOp& recordedOp);
enum class StrokeBehavior {
// stroking is forced, regardless of style on paint (such as for lines)
@@ -129,35 +117,16 @@ public:
};
static BakedOpState* tryStrokeableOpConstruct(LinearAllocator& allocator,
- Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior) {
- if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
- bool expandForStroke = (strokeBehavior == StrokeBehavior::StyleDefined)
- ? (recordedOp.paint && recordedOp.paint->getStyle() != SkPaint::kFill_Style)
- : true;
-
- BakedOpState* bakedState = allocator.create_trivial<BakedOpState>(
- allocator, snapshot, recordedOp, expandForStroke);
- if (bakedState->computedState.clippedBounds.isEmpty()) {
- // bounds are empty, so op is rejected
- // NOTE: this won't succeed if a clip was allocated
- allocator.rewindIfLastAlloc(bakedState);
- return nullptr;
- }
- return bakedState;
- }
+ Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior);
static BakedOpState* tryShadowOpConstruct(LinearAllocator& allocator,
- Snapshot& snapshot, const ShadowOp* shadowOpPtr) {
- if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr;
-
- // clip isn't empty, so construct the op
- return allocator.create_trivial<BakedOpState>(allocator, snapshot, shadowOpPtr);
- }
+ Snapshot& snapshot, const ShadowOp* shadowOpPtr);
static BakedOpState* directConstruct(LinearAllocator& allocator,
- const ClipRect* clip, const Rect& dstRect, const RecordedOp& recordedOp) {
- return allocator.create_trivial<BakedOpState>(clip, dstRect, recordedOp);
- }
+ const ClipRect* clip, const Rect& dstRect, const RecordedOp& recordedOp);
+
+ // Set opaqueOverClippedBounds. If this method isn't called, the op is assumed translucent.
+ void setupOpacity(const SkPaint* paint);
// computed state:
ResolvedRenderState computedState;
diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp
index b1314feebf34..b18836f175ea 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -481,12 +481,17 @@ void FrameBuilder::deferRenderNodeOp(const RenderNodeOp& op) {
* Defers an unmergeable, strokeable op, accounting correctly
* for paint's style on the bounds being computed.
*/
-const BakedOpState* FrameBuilder::deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
+BakedOpState* FrameBuilder::deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
BakedOpState::StrokeBehavior strokeBehavior) {
// Note: here we account for stroke when baking the op
BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct(
mAllocator, *mCanvasState.writableSnapshot(), op, strokeBehavior);
if (!bakedState) return nullptr; // quick rejected
+
+ if (op.opId == RecordedOpId::RectOp && op.paint->getStyle() != SkPaint::kStroke_Style) {
+ bakedState->setupOpacity(op.paint);
+ }
+
currentLayer().deferUnmergeableOp(mAllocator, bakedState, batchId);
return bakedState;
}
@@ -516,6 +521,7 @@ static bool hasMergeableClip(const BakedOpState& state) {
void FrameBuilder::deferBitmapOp(const BitmapOp& op) {
BakedOpState* bakedState = tryBakeOpState(op);
if (!bakedState) return; // quick rejected
+ bakedState->setupOpacity(op.paint);
// Don't merge non-simply transformed or neg scale ops, SET_TEXTURE doesn't handle rotation
// Don't merge A8 bitmaps - the paint's color isn't compared by mergeId, or in
diff --git a/libs/hwui/FrameBuilder.h b/libs/hwui/FrameBuilder.h
index 0b7a6062456a..02c05cb1bbbe 100644
--- a/libs/hwui/FrameBuilder.h
+++ b/libs/hwui/FrameBuilder.h
@@ -201,7 +201,7 @@ private:
return mAllocator.create<SkPath>();
}
- const BakedOpState* deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
+ BakedOpState* deferStrokeableOp(const RecordedOp& op, batchid_t batchId,
BakedOpState::StrokeBehavior strokeBehavior = BakedOpState::StrokeBehavior::StyleDefined);
/**
diff --git a/libs/hwui/LayerBuilder.cpp b/libs/hwui/LayerBuilder.cpp
index e6a95ff177a4..eea11bff7d8a 100644
--- a/libs/hwui/LayerBuilder.cpp
+++ b/libs/hwui/LayerBuilder.cpp
@@ -236,6 +236,21 @@ void LayerBuilder::deferLayerClear(const Rect& rect) {
mClearRects.push_back(rect);
}
+void LayerBuilder::onDeferOp(LinearAllocator& allocator, const BakedOpState* bakedState) {
+ if (bakedState->op->opId != RecordedOpId::CopyToLayerOp) {
+ // First non-CopyToLayer, so stop stashing up layer clears for unclipped save layers,
+ // and issue them together in one draw.
+ flushLayerClears(allocator);
+
+ if (CC_UNLIKELY(activeUnclippedSaveLayers.empty()
+ && bakedState->computedState.opaqueOverClippedBounds
+ && bakedState->computedState.clippedBounds.contains(repaintRect))) {
+ // discard all deferred drawing ops, since new one will occlude them
+ clear();
+ }
+ }
+}
+
void LayerBuilder::flushLayerClears(LinearAllocator& allocator) {
if (CC_UNLIKELY(!mClearRects.empty())) {
const int vertCount = mClearRects.size() * 4;
@@ -270,11 +285,7 @@ void LayerBuilder::flushLayerClears(LinearAllocator& allocator) {
void LayerBuilder::deferUnmergeableOp(LinearAllocator& allocator,
BakedOpState* op, batchid_t batchId) {
- if (batchId != OpBatchType::CopyToLayer) {
- // if first op after one or more unclipped saveLayers, flush the layer clears
- flushLayerClears(allocator);
- }
-
+ onDeferOp(allocator, op);
OpBatch* targetBatch = mBatchLookup[batchId];
size_t insertBatchIndex = mBatches.size();
@@ -295,10 +306,7 @@ void LayerBuilder::deferUnmergeableOp(LinearAllocator& allocator,
void LayerBuilder::deferMergeableOp(LinearAllocator& allocator,
BakedOpState* op, batchid_t batchId, mergeid_t mergeId) {
- if (batchId != OpBatchType::CopyToLayer) {
- // if first op after one or more unclipped saveLayers, flush the layer clears
- flushLayerClears(allocator);
- }
+ onDeferOp(allocator, op);
MergingOpBatch* targetBatch = nullptr;
// Try to merge with any existing batch with same mergeId
@@ -348,6 +356,14 @@ void LayerBuilder::replayBakedOpsImpl(void* arg,
}
}
+void LayerBuilder::clear() {
+ mBatches.clear();
+ for (int i = 0; i < OpBatchType::Count; i++) {
+ mBatchLookup[i] = nullptr;
+ mMergingBatchLookup[i].clear();
+ }
+}
+
void LayerBuilder::dump() const {
ALOGD("LayerBuilder %p, %ux%u buffer %p, blo %p, rn %p (%s)",
this, width, height, offscreenBuffer, beginLayerOp,
diff --git a/libs/hwui/LayerBuilder.h b/libs/hwui/LayerBuilder.h
index 4a7ca2de9b1b..4de432c5e7be 100644
--- a/libs/hwui/LayerBuilder.h
+++ b/libs/hwui/LayerBuilder.h
@@ -100,9 +100,7 @@ public:
return mBatches.empty();
}
- void clear() {
- mBatches.clear();
- }
+ void clear();
void dump() const;
@@ -117,6 +115,7 @@ public:
// list of deferred CopyFromLayer ops, to be deferred upon encountering EndUnclippedLayerOps
std::vector<BakedOpState*> activeUnclippedSaveLayers;
private:
+ void onDeferOp(LinearAllocator& allocator, const BakedOpState* bakedState);
void flushLayerClears(LinearAllocator& allocator);
std::vector<BatchBase*> mBatches;
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index e97aaa6ac688..ba22f91cde9d 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -216,6 +216,80 @@ RENDERTHREAD_TEST(FrameBuilder, simpleBatching) {
<< "Expect number of ops = 2 * loop count";
}
+RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) {
+ class AvoidOverdrawRectsTestRenderer : public TestRendererBase {
+ public:
+ void onRectOp(const RectOp& op, const BakedOpState& state) override {
+ EXPECT_EQ(mIndex++, 0) << "Should be one rect";
+ EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds)
+ << "Last rect should occlude others.";
+ }
+ };
+ auto node = TestUtils::createNode(0, 0, 200, 200,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ canvas.drawRect(0, 0, 200, 200, SkPaint());
+ canvas.drawRect(0, 0, 200, 200, SkPaint());
+ canvas.drawRect(10, 10, 190, 190, SkPaint());
+ });
+
+ // Damage (and therefore clip) is same as last draw, subset of renderable area.
+ // This means last op occludes other contents, and they'll be rejected to avoid overdraw.
+ SkRect damageRect = SkRect::MakeLTRB(10, 10, 190, 190);
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, damageRect, 200, 200,
+ TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+
+ EXPECT_EQ(3u, node->getDisplayList()->getOps().size())
+ << "Recording must not have rejected ops, in order for this test to be valid";
+
+ AvoidOverdrawRectsTestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(1, renderer.getIndex()) << "Expect exactly one op";
+}
+
+RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_bitmaps) {
+ static SkBitmap opaqueBitmap = TestUtils::createSkBitmap(50, 50,
+ SkColorType::kRGB_565_SkColorType);
+ static SkBitmap transpBitmap = TestUtils::createSkBitmap(50, 50,
+ SkColorType::kAlpha_8_SkColorType);
+ class AvoidOverdrawBitmapsTestRenderer : public TestRendererBase {
+ public:
+ void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override {
+ EXPECT_LT(mIndex++, 2) << "Should be two bitmaps";
+ switch(mIndex++) {
+ case 0:
+ EXPECT_EQ(opaqueBitmap.pixelRef(), op.bitmap->pixelRef());
+ break;
+ case 1:
+ EXPECT_EQ(transpBitmap.pixelRef(), op.bitmap->pixelRef());
+ break;
+ default:
+ ADD_FAILURE() << "Only two ops expected.";
+ }
+ }
+ };
+
+ auto node = TestUtils::createNode(0, 0, 50, 50,
+ [](RenderProperties& props, RecordingCanvas& canvas) {
+ canvas.drawRect(0, 0, 50, 50, SkPaint());
+ canvas.drawRect(0, 0, 50, 50, SkPaint());
+ canvas.drawBitmap(transpBitmap, 0, 0, nullptr);
+
+ // only the below draws should remain, since they're
+ canvas.drawBitmap(opaqueBitmap, 0, 0, nullptr);
+ canvas.drawBitmap(transpBitmap, 0, 0, nullptr);
+ });
+
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(50, 50), 50, 50,
+ TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance());
+
+ EXPECT_EQ(5u, node->getDisplayList()->getOps().size())
+ << "Recording must not have rejected ops, in order for this test to be valid";
+
+ AvoidOverdrawBitmapsTestRenderer renderer;
+ frameBuilder.replayBakedOps<TestDispatcher>(renderer);
+ EXPECT_EQ(2, renderer.getIndex()) << "Expect exactly one op";
+}
+
RENDERTHREAD_TEST(FrameBuilder, clippedMerging) {
class ClippedMergingTestRenderer : public TestRendererBase {
public:
diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h
index db537130e12e..4faab9a5f648 100644
--- a/libs/hwui/utils/PaintUtils.h
+++ b/libs/hwui/utils/PaintUtils.h
@@ -67,6 +67,21 @@ public:
&& getXfermode(paint.getXfermode()) == SkXfermode::kSrcOver_Mode;
}
+ static bool isOpaquePaint(const SkPaint* paint) {
+ if (!paint) return true; // default (paintless) behavior is SrcOver, black
+
+ if (paint->getAlpha() != 0xFF
+ || PaintUtils::isBlendedShader(paint->getShader())
+ || PaintUtils::isBlendedColorFilter(paint->getColorFilter())) {
+ return false;
+ }
+
+ // Only let simple srcOver / src blending modes declare opaque, since behavior is clear.
+ SkXfermode::Mode mode = getXfermode(paint->getXfermode());
+ return mode == SkXfermode::Mode::kSrcOver_Mode
+ || mode == SkXfermode::Mode::kSrc_Mode;
+ }
+
static bool isBlendedShader(const SkShader* shader) {
if (shader == nullptr) {
return false;
diff --git a/services/core/java/com/android/server/pm/ShortcutLauncher.java b/services/core/java/com/android/server/pm/ShortcutLauncher.java
index b759e1682940..7699f305fb17 100644
--- a/services/core/java/com/android/server/pm/ShortcutLauncher.java
+++ b/services/core/java/com/android/server/pm/ShortcutLauncher.java
@@ -32,7 +32,7 @@ import java.util.List;
/**
* Launcher information used by {@link ShortcutService}.
*/
-class ShortcutLauncher implements ShortcutPackageItem {
+class ShortcutLauncher extends ShortcutPackageItem {
private static final String TAG = ShortcutService.TAG;
static final String TAG_ROOT = "launcher-pins";
@@ -44,44 +44,34 @@ class ShortcutLauncher implements ShortcutPackageItem {
private static final String ATTR_VALUE = "value";
private static final String ATTR_PACKAGE_NAME = "package-name";
- @UserIdInt
- private final int mUserId;
-
- @NonNull
- private final String mPackageName;
-
- @UserIdInt
- private final int mLauncherUserId;
+ private final int mOwnerUserId;
/**
* Package name -> IDs.
*/
final private ArrayMap<String, ArraySet<String>> mPinnedShortcuts = new ArrayMap<>();
- ShortcutLauncher(@UserIdInt int userId, @NonNull String packageName,
- @UserIdInt int launcherUserId) {
- mUserId = userId;
- mPackageName = packageName;
- mLauncherUserId = launcherUserId;
+ public ShortcutLauncher(@UserIdInt int ownerUserId, @NonNull String packageName,
+ @UserIdInt int launcherUserId, ShortcutPackageInfo spi) {
+ super(launcherUserId, packageName, spi != null ? spi : ShortcutPackageInfo.newEmpty());
+ mOwnerUserId = ownerUserId;
}
- @UserIdInt
- public int getUserId() {
- return mUserId;
+ public ShortcutLauncher(@UserIdInt int ownerUserId, @NonNull String packageName,
+ @UserIdInt int launcherUserId) {
+ this(launcherUserId, packageName, launcherUserId, null);
}
- @UserIdInt
- public int getLauncherUserId() {
- return mLauncherUserId;
+ @Override
+ public int getOwnerUserId() {
+ return mOwnerUserId;
}
- @NonNull
- public String getPackageName() {
- return mPackageName;
- }
+ public void pinShortcuts(@NonNull ShortcutService s, @UserIdInt int packageUserId,
+ @NonNull String packageName, @NonNull List<String> ids) {
+ final ShortcutPackage packageShortcuts =
+ s.getPackageShortcutsLocked(packageName, packageUserId);
- public void pinShortcuts(@NonNull ShortcutService s, @NonNull String packageName,
- @NonNull List<String> ids) {
final int idSize = ids.size();
if (idSize == 0) {
mPinnedShortcuts.remove(packageName);
@@ -91,8 +81,6 @@ class ShortcutLauncher implements ShortcutPackageItem {
// Pin shortcuts. Make sure only pin the ones that were visible to the caller.
// i.e. a non-dynamic, pinned shortcut by *other launchers* shouldn't be pinned here.
- final ShortcutPackage packageShortcuts =
- s.getPackageShortcutsLocked(packageName, mUserId);
final ArraySet<String> newSet = new ArraySet<>();
for (int i = 0; i < idSize; i++) {
@@ -107,7 +95,7 @@ class ShortcutLauncher implements ShortcutPackageItem {
}
mPinnedShortcuts.put(packageName, newSet);
}
- s.getPackageShortcutsLocked(packageName, mUserId).refreshPinnedFlags(s);
+ packageShortcuts.refreshPinnedFlags(s);
}
/**
@@ -124,15 +112,18 @@ class ShortcutLauncher implements ShortcutPackageItem {
/**
* Persist.
*/
- public void saveToXml(XmlSerializer out, boolean forBackup) throws IOException {
+ @Override
+ public void saveToXml(XmlSerializer out, boolean forBackup)
+ throws IOException {
final int size = mPinnedShortcuts.size();
if (size == 0) {
return; // Nothing to write.
}
out.startTag(null, TAG_ROOT);
- ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME, mPackageName);
- ShortcutService.writeAttr(out, ATTR_LAUNCHER_USER_ID, mLauncherUserId);
+ ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME, getPackageName());
+ ShortcutService.writeAttr(out, ATTR_LAUNCHER_USER_ID, getPackageUserId());
+ getPackageInfo().saveToXml(out);
for (int i = 0; i < size; i++) {
out.startTag(null, TAG_PACKAGE);
@@ -153,16 +144,21 @@ class ShortcutLauncher implements ShortcutPackageItem {
/**
* Load.
*/
- public static ShortcutLauncher loadFromXml(XmlPullParser parser, int ownerUserId)
- throws IOException, XmlPullParserException {
+ public static ShortcutLauncher loadFromXml(XmlPullParser parser, int ownerUserId,
+ boolean fromBackup) throws IOException, XmlPullParserException {
final String launcherPackageName = ShortcutService.parseStringAttribute(parser,
ATTR_PACKAGE_NAME);
- final int launcherUserId = ShortcutService.parseIntAttribute(parser,
- ATTR_LAUNCHER_USER_ID, ownerUserId);
+
+ // If restoring, just use the real user ID.
+ final int launcherUserId =
+ fromBackup ? ownerUserId
+ : ShortcutService.parseIntAttribute(parser, ATTR_LAUNCHER_USER_ID, ownerUserId);
final ShortcutLauncher ret = new ShortcutLauncher(launcherUserId, launcherPackageName,
launcherUserId);
+ ShortcutPackageInfo spi = null;
+
ArraySet<String> ids = null;
final int outerDepth = parser.getDepth();
int type;
@@ -173,21 +169,33 @@ class ShortcutLauncher implements ShortcutPackageItem {
}
final int depth = parser.getDepth();
final String tag = parser.getName();
- switch (tag) {
- case TAG_PACKAGE: {
- final String packageName = ShortcutService.parseStringAttribute(parser,
- ATTR_PACKAGE_NAME);
- ids = new ArraySet<>();
- ret.mPinnedShortcuts.put(packageName, ids);
- continue;
+ if (depth == outerDepth + 1) {
+ switch (tag) {
+ case ShortcutPackageInfo.TAG_ROOT:
+ spi = ShortcutPackageInfo.loadFromXml(parser);
+ continue;
+ case TAG_PACKAGE: {
+ final String packageName = ShortcutService.parseStringAttribute(parser,
+ ATTR_PACKAGE_NAME);
+ ids = new ArraySet<>();
+ ret.mPinnedShortcuts.put(packageName, ids);
+ continue;
+ }
}
- case TAG_PIN: {
- ids.add(ShortcutService.parseStringAttribute(parser,
- ATTR_VALUE));
- continue;
+ }
+ if (depth == outerDepth + 2) {
+ switch (tag) {
+ case TAG_PIN: {
+ ids.add(ShortcutService.parseStringAttribute(parser,
+ ATTR_VALUE));
+ continue;
+ }
}
}
- throw ShortcutService.throwForInvalidTag(depth, tag);
+ ShortcutService.warnForInvalidTag(depth, tag);
+ }
+ if (spi != null) {
+ ret.replacePackageInfo(spi);
}
return ret;
}
@@ -197,9 +205,12 @@ class ShortcutLauncher implements ShortcutPackageItem {
pw.print(prefix);
pw.print("Launcher: ");
- pw.print(mPackageName);
- pw.print(" UserId: ");
- pw.print(mLauncherUserId);
+ pw.print(getPackageName());
+ pw.print(" Package user: ");
+ pw.print(getPackageUserId());
+ pw.println();
+
+ getPackageInfo().dump(s, pw, prefix + " ");
pw.println();
final int size = mPinnedShortcuts.size();
diff --git a/services/core/java/com/android/server/pm/ShortcutPackage.java b/services/core/java/com/android/server/pm/ShortcutPackage.java
index e4d578744159..f9414328885c 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackage.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackage.java
@@ -17,7 +17,6 @@ package com.android.server.pm;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.annotation.UserIdInt;
import android.content.ComponentName;
import android.content.Intent;
import android.content.pm.ShortcutInfo;
@@ -41,7 +40,7 @@ import java.util.function.Predicate;
/**
* Package information used by {@link ShortcutService}.
*/
-class ShortcutPackage implements ShortcutPackageItem {
+class ShortcutPackage extends ShortcutPackageItem {
private static final String TAG = ShortcutService.TAG;
static final String TAG_ROOT = "package";
@@ -63,12 +62,6 @@ class ShortcutPackage implements ShortcutPackageItem {
private static final String ATTR_ICON_RES = "icon-res";
private static final String ATTR_BITMAP_PATH = "bitmap-path";
- @UserIdInt
- private final int mUserId;
-
- @NonNull
- private final String mPackageName;
-
/**
* All the shortcuts from the package, keyed on IDs.
*/
@@ -89,19 +82,18 @@ class ShortcutPackage implements ShortcutPackageItem {
*/
private long mLastResetTime;
- ShortcutPackage(int userId, String packageName) {
- mUserId = userId;
- mPackageName = packageName;
+ public ShortcutPackage(int packageUserId, String packageName, ShortcutPackageInfo spi) {
+ super(packageUserId, packageName, spi != null ? spi : ShortcutPackageInfo.newEmpty());
}
- @UserIdInt
- public int getUserId() {
- return mUserId;
+ public ShortcutPackage(int packageUserId, String packageName) {
+ this(packageUserId, packageName, null);
}
- @NonNull
- public String getPackageName() {
- return mPackageName;
+ @Override
+ public int getOwnerUserId() {
+ // For packages, always owner user == package user.
+ return getPackageUserId();
}
/**
@@ -116,7 +108,7 @@ class ShortcutPackage implements ShortcutPackageItem {
@NonNull String id) {
final ShortcutInfo shortcut = mShortcuts.remove(id);
if (shortcut != null) {
- s.removeIcon(mUserId, shortcut);
+ s.removeIcon(getPackageUserId(), shortcut);
shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED);
}
return shortcut;
@@ -124,7 +116,7 @@ class ShortcutPackage implements ShortcutPackageItem {
void addShortcut(@NonNull ShortcutService s, @NonNull ShortcutInfo newShortcut) {
deleteShortcut(s, newShortcut.getId());
- s.saveIconAndFixUpShortcut(mUserId, newShortcut);
+ s.saveIconAndFixUpShortcut(getPackageUserId(), newShortcut);
mShortcuts.put(newShortcut.getId(), newShortcut);
}
@@ -233,11 +225,12 @@ class ShortcutPackage implements ShortcutPackageItem {
// Then, for the pinned set for each launcher, set the pin flag one by one.
final ArrayMap<ShortcutUser.PackageWithUser, ShortcutLauncher> launchers =
- s.getUserShortcutsLocked(mUserId).getAllLaunchers();
+ s.getUserShortcutsLocked(getPackageUserId()).getAllLaunchers();
for (int l = launchers.size() - 1; l >= 0; l--) {
final ShortcutLauncher launcherShortcuts = launchers.valueAt(l);
- final ArraySet<String> pinned = launcherShortcuts.getPinnedShortcutIds(mPackageName);
+ final ArraySet<String> pinned = launcherShortcuts.getPinnedShortcutIds(
+ getPackageName());
if (pinned == null || pinned.size() == 0) {
continue;
@@ -321,8 +314,8 @@ class ShortcutPackage implements ShortcutPackageItem {
// Set of pinned shortcuts by the calling launcher.
final ArraySet<String> pinnedByCallerSet = (callingLauncher == null) ? null
- : s.getLauncherShortcuts(callingLauncher, mUserId, launcherUserId)
- .getPinnedShortcutIds(mPackageName);
+ : s.getLauncherShortcuts(callingLauncher, getPackageUserId(), launcherUserId)
+ .getPinnedShortcutIds(getPackageName());
for (int i = 0; i < mShortcuts.size(); i++) {
final ShortcutInfo si = mShortcuts.valueAt(i);
@@ -362,7 +355,7 @@ class ShortcutPackage implements ShortcutPackageItem {
pw.print(prefix);
pw.print("Package: ");
- pw.print(mPackageName);
+ pw.print(getPackageName());
pw.println();
pw.print(prefix);
@@ -380,6 +373,9 @@ class ShortcutPackage implements ShortcutPackageItem {
pw.print(s.formatTime(mLastResetTime));
pw.println();
+ getPackageInfo().dump(s, pw, prefix + " ");
+ pw.println();
+
pw.println(" Shortcuts:");
long totalBitmapSize = 0;
final ArrayMap<String, ShortcutInfo> shortcuts = mShortcuts;
@@ -406,6 +402,7 @@ class ShortcutPackage implements ShortcutPackageItem {
pw.println(")");
}
+ @Override
public void saveToXml(@NonNull XmlSerializer out, boolean forBackup)
throws IOException, XmlPullParserException {
final int size = mShortcuts.size();
@@ -416,10 +413,11 @@ class ShortcutPackage implements ShortcutPackageItem {
out.startTag(null, TAG_ROOT);
- ShortcutService.writeAttr(out, ATTR_NAME, mPackageName);
+ ShortcutService.writeAttr(out, ATTR_NAME, getPackageName());
ShortcutService.writeAttr(out, ATTR_DYNAMIC_COUNT, mDynamicShortcutCount);
ShortcutService.writeAttr(out, ATTR_CALL_COUNT, mApiCallCount);
ShortcutService.writeAttr(out, ATTR_LAST_RESET, mLastResetTime);
+ getPackageInfo().saveToXml(out);
for (int j = 0; j < size; j++) {
saveShortcut(out, mShortcuts.valueAt(j), forBackup);
@@ -464,13 +462,14 @@ class ShortcutPackage implements ShortcutPackageItem {
out.endTag(null, TAG_SHORTCUT);
}
- public static ShortcutPackage loadFromXml(XmlPullParser parser, int userId)
+ public static ShortcutPackage loadFromXml(ShortcutService s, XmlPullParser parser,
+ int ownerUserId, boolean fromBackup)
throws IOException, XmlPullParserException {
final String packageName = ShortcutService.parseStringAttribute(parser,
ATTR_NAME);
- final ShortcutPackage ret = new ShortcutPackage(userId, packageName);
+ final ShortcutPackage ret = new ShortcutPackage(ownerUserId, packageName);
ret.mDynamicShortcutCount =
ShortcutService.parseIntAttribute(parser, ATTR_DYNAMIC_COUNT);
@@ -478,6 +477,7 @@ class ShortcutPackage implements ShortcutPackageItem {
ShortcutService.parseIntAttribute(parser, ATTR_CALL_COUNT);
ret.mLastResetTime =
ShortcutService.parseLongAttribute(parser, ATTR_LAST_RESET);
+ ShortcutPackageInfo spi = null;
final int outerDepth = parser.getDepth();
int type;
@@ -488,15 +488,23 @@ class ShortcutPackage implements ShortcutPackageItem {
}
final int depth = parser.getDepth();
final String tag = parser.getName();
- switch (tag) {
- case TAG_SHORTCUT:
- final ShortcutInfo si = parseShortcut(parser, packageName);
-
- // Don't use addShortcut(), we don't need to save the icon.
- ret.mShortcuts.put(si.getId(), si);
- continue;
+ if (depth == outerDepth + 1) {
+ switch (tag) {
+ case ShortcutPackageInfo.TAG_ROOT:
+ spi = ShortcutPackageInfo.loadFromXml(parser);
+ continue;
+ case TAG_SHORTCUT:
+ final ShortcutInfo si = parseShortcut(parser, packageName);
+
+ // Don't use addShortcut(), we don't need to save the icon.
+ ret.mShortcuts.put(si.getId(), si);
+ continue;
+ }
}
- throw ShortcutService.throwForInvalidTag(depth, tag);
+ ShortcutService.warnForInvalidTag(depth, tag);
+ }
+ if (spi != null) {
+ ret.replacePackageInfo(spi);
}
return ret;
}
@@ -522,8 +530,7 @@ class ShortcutPackage implements ShortcutPackageItem {
title = ShortcutService.parseStringAttribute(parser, ATTR_TITLE);
intent = ShortcutService.parseIntentAttribute(parser, ATTR_INTENT);
weight = (int) ShortcutService.parseLongAttribute(parser, ATTR_WEIGHT);
- lastChangedTimestamp = (int) ShortcutService.parseLongAttribute(parser,
- ATTR_TIMESTAMP);
+ lastChangedTimestamp = ShortcutService.parseLongAttribute(parser, ATTR_TIMESTAMP);
flags = (int) ShortcutService.parseLongAttribute(parser, ATTR_FLAGS);
iconRes = (int) ShortcutService.parseLongAttribute(parser, ATTR_ICON_RES);
bitmapPath = ShortcutService.parseStringAttribute(parser, ATTR_BITMAP_PATH);
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
index ab456893f244..5f706b83271e 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java
@@ -15,14 +15,11 @@
*/
package com.android.server.pm;
-import android.annotation.NonNull;
import android.annotation.UserIdInt;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
-import android.content.pm.Signature;
import android.util.Slog;
-import com.android.internal.util.Preconditions;
+import com.android.server.backup.BackupUtils;
import libcore.io.Base64;
import libcore.util.HexEncoding;
@@ -33,32 +30,21 @@ import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.io.PrintWriter;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
-import java.util.Arrays;
/**
* Package information used by {@link android.content.pm.ShortcutManager} for backup / restore.
- *
- * TODO: The methods about signature hashes are copied from BackupManagerService, which is not
- * visible here. Unify the code.
*/
-class ShortcutPackageInfo implements ShortcutPackageItem {
+class ShortcutPackageInfo {
private static final String TAG = ShortcutService.TAG;
static final String TAG_ROOT = "package-info";
- private static final String ATTR_USER_ID = "user";
- private static final String ATTR_NAME = "name";
private static final String ATTR_VERSION = "version";
private static final String ATTR_SHADOW = "shadow";
private static final String TAG_SIGNATURE = "signature";
private static final String ATTR_SIGNATURE_HASH = "hash";
- private final String mPackageName;
- private final int mUserId;
-
/**
* When true, this package information was restored from the previous device, and the app hasn't
* been installed yet.
@@ -67,22 +53,14 @@ class ShortcutPackageInfo implements ShortcutPackageItem {
private int mVersionCode;
private ArrayList<byte[]> mSigHashes;
- private ShortcutPackageInfo(String packageName, int userId,
- int versionCode, ArrayList<byte[]> sigHashes, boolean isShadow) {
- mPackageName = Preconditions.checkNotNull(packageName);
- mUserId = userId;
+ private ShortcutPackageInfo(int versionCode, ArrayList<byte[]> sigHashes, boolean isShadow) {
mVersionCode = versionCode;
mIsShadow = isShadow;
mSigHashes = sigHashes;
}
- @NonNull
- public String getPackageName() {
- return mPackageName;
- }
-
- public int getUserId() {
- return mUserId;
+ public static ShortcutPackageInfo newEmpty() {
+ return new ShortcutPackageInfo(0, new ArrayList<>(0), /* isShadow */ false);
}
public boolean isShadow() {
@@ -101,92 +79,13 @@ class ShortcutPackageInfo implements ShortcutPackageItem {
return mVersionCode;
}
- private static byte[] hashSignature(Signature sig) {
- try {
- MessageDigest digest = MessageDigest.getInstance("SHA-256");
- digest.update(sig.toByteArray());
- return digest.digest();
- } catch (NoSuchAlgorithmException e) {
- Slog.w(TAG, "No SHA-256 algorithm found!");
- }
- return null;
- }
-
- private static ArrayList<byte[]> hashSignatureArray(Signature[] sigs) {
- if (sigs == null) {
- return null;
- }
-
- ArrayList<byte[]> hashes = new ArrayList<byte[]>(sigs.length);
- for (Signature s : sigs) {
- hashes.add(hashSignature(s));
- }
- return hashes;
- }
-
- private static boolean signaturesMatch(ArrayList<byte[]> storedSigHashes, PackageInfo target) {
- if (target == null) {
- return false;
- }
-
- // If the target resides on the system partition, we allow it to restore
- // data from the like-named package in a restore set even if the signatures
- // do not match. (Unlike general applications, those flashed to the system
- // partition will be signed with the device's platform certificate, so on
- // different phones the same system app will have different signatures.)
- if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
- return true;
- }
-
- // Allow unsigned apps, but not signed on one device and unsigned on the other
- // !!! TODO: is this the right policy?
- Signature[] deviceSigs = target.signatures;
- if ((storedSigHashes == null || storedSigHashes.size() == 0)
- && (deviceSigs == null || deviceSigs.length == 0)) {
- return true;
- }
- if (storedSigHashes == null || deviceSigs == null) {
- return false;
- }
-
- // !!! TODO: this demands that every stored signature match one
- // that is present on device, and does not demand the converse.
- // Is this this right policy?
- final int nStored = storedSigHashes.size();
- final int nDevice = deviceSigs.length;
-
- // hash each on-device signature
- ArrayList<byte[]> deviceHashes = new ArrayList<byte[]>(nDevice);
- for (int i = 0; i < nDevice; i++) {
- deviceHashes.add(hashSignature(deviceSigs[i]));
- }
-
- // now ensure that each stored sig (hash) matches an on-device sig (hash)
- for (int n = 0; n < nStored; n++) {
- boolean match = false;
- final byte[] storedHash = storedSigHashes.get(n);
- for (int i = 0; i < nDevice; i++) {
- if (Arrays.equals(storedHash, deviceHashes.get(i))) {
- match = true;
- break;
- }
- }
- // match is false when no on-device sig matched one of the stored ones
- if (!match) {
- return false;
- }
- }
-
- return true;
- }
-
public boolean canRestoreTo(PackageInfo target) {
if (target.versionCode < mVersionCode) {
Slog.w(TAG, String.format("Package current version %d < backed up version %d",
target.versionCode, mVersionCode));
return false;
}
- if (!signaturesMatch(mSigHashes, target)) {
+ if (!BackupUtils.signaturesMatch(mSigHashes, target)) {
Slog.w(TAG, "Package signature mismtach");
return false;
}
@@ -194,37 +93,34 @@ class ShortcutPackageInfo implements ShortcutPackageItem {
}
public static ShortcutPackageInfo generateForInstalledPackage(
- ShortcutService s, String packageName, @UserIdInt int userId) {
- final PackageInfo pi = s.getPackageInfoWithSignatures(packageName, userId);
+ ShortcutService s, String packageName, @UserIdInt int packageUserId) {
+ final PackageInfo pi = s.getPackageInfoWithSignatures(packageName, packageUserId);
if (pi.signatures == null || pi.signatures.length == 0) {
Slog.e(TAG, "Can't get signatures: package=" + packageName);
return null;
}
- final ShortcutPackageInfo ret = new ShortcutPackageInfo(packageName, userId, pi.versionCode,
- hashSignatureArray(pi.signatures), /* shadow=*/ false);
+ final ShortcutPackageInfo ret = new ShortcutPackageInfo(pi.versionCode,
+ BackupUtils.hashSignatureArray(pi.signatures), /* shadow=*/ false);
return ret;
}
- public void refreshAndSave(ShortcutService s, @UserIdInt int userId) {
- final PackageInfo pi = s.getPackageInfoWithSignatures(mPackageName, userId);
+ public void refresh(ShortcutService s, ShortcutPackageItem pkg) {
+ // Note use mUserId here, rather than userId.
+ final PackageInfo pi = s.getPackageInfoWithSignatures(
+ pkg.getPackageName(), pkg.getPackageUserId());
if (pi == null) {
- Slog.w(TAG, "Package not found: " + mPackageName);
+ Slog.w(TAG, "Package not found: " + pkg.getPackageName());
return;
}
mVersionCode = pi.versionCode;
- mSigHashes = hashSignatureArray(pi.signatures);
-
- s.scheduleSaveUser(userId);
+ mSigHashes = BackupUtils.hashSignatureArray(pi.signatures);
}
- public void saveToXml(XmlSerializer out, boolean forBackup)
- throws IOException, XmlPullParserException {
+ public void saveToXml(XmlSerializer out) throws IOException {
out.startTag(null, TAG_ROOT);
- ShortcutService.writeAttr(out, ATTR_NAME, mPackageName);
- ShortcutService.writeAttr(out, ATTR_USER_ID, mUserId);
ShortcutService.writeAttr(out, ATTR_VERSION, mVersionCode);
ShortcutService.writeAttr(out, ATTR_SHADOW, mIsShadow);
@@ -236,11 +132,9 @@ class ShortcutPackageInfo implements ShortcutPackageItem {
out.endTag(null, TAG_ROOT);
}
- public static ShortcutPackageInfo loadFromXml(XmlPullParser parser, int ownerUserId)
+ public static ShortcutPackageInfo loadFromXml(XmlPullParser parser)
throws IOException, XmlPullParserException {
- final String packageName = ShortcutService.parseStringAttribute(parser, ATTR_NAME);
- final int userId = ShortcutService.parseIntAttribute(parser, ATTR_USER_ID, ownerUserId);
final int versionCode = ShortcutService.parseIntAttribute(parser, ATTR_VERSION);
final boolean shadow = ShortcutService.parseBooleanAttribute(parser, ATTR_SHADOW);
@@ -256,31 +150,27 @@ class ShortcutPackageInfo implements ShortcutPackageItem {
}
final int depth = parser.getDepth();
final String tag = parser.getName();
- switch (tag) {
- case TAG_SIGNATURE: {
- final String hash = ShortcutService.parseStringAttribute(
- parser, ATTR_SIGNATURE_HASH);
- hashes.add(Base64.decode(hash.getBytes()));
- continue;
+
+ if (depth == outerDepth + 1) {
+ switch (tag) {
+ case TAG_SIGNATURE: {
+ final String hash = ShortcutService.parseStringAttribute(
+ parser, ATTR_SIGNATURE_HASH);
+ hashes.add(Base64.decode(hash.getBytes()));
+ continue;
+ }
}
}
- throw ShortcutService.throwForInvalidTag(depth, tag);
+ ShortcutService.warnForInvalidTag(depth, tag);
}
- return new ShortcutPackageInfo(packageName, userId, versionCode, hashes, shadow);
+ return new ShortcutPackageInfo(versionCode, hashes, shadow);
}
public void dump(ShortcutService s, PrintWriter pw, String prefix) {
pw.println();
pw.print(prefix);
- pw.print("PackageInfo: ");
- pw.print(mPackageName);
- pw.println();
-
- pw.print(prefix);
- pw.print(" User: ");
- pw.print(mUserId);
- pw.println();
+ pw.println("PackageInfo:");
pw.print(prefix);
pw.print(" IsShadow: ");
diff --git a/services/core/java/com/android/server/pm/ShortcutPackageItem.java b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
index 526c84db6963..de2709d3d7ac 100644
--- a/services/core/java/com/android/server/pm/ShortcutPackageItem.java
+++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java
@@ -17,15 +17,69 @@ package com.android.server.pm;
import android.annotation.NonNull;
+import com.android.internal.util.Preconditions;
+
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
-public interface ShortcutPackageItem {
+abstract class ShortcutPackageItem {
+ private final int mPackageUserId;
+ private final String mPackageName;
+
+ private ShortcutPackageInfo mPackageInfo;
+
+ protected ShortcutPackageItem(int packageUserId, @NonNull String packageName,
+ @NonNull ShortcutPackageInfo packageInfo) {
+ mPackageUserId = packageUserId;
+ mPackageName = Preconditions.checkStringNotEmpty(packageName);
+ mPackageInfo = Preconditions.checkNotNull(packageInfo);
+ }
+
+ /**
+ * ID of the user who actually has this package running on. For {@link ShortcutPackage},
+ * this is the same thing as {@link #getOwnerUserId}, but if it's a {@link ShortcutLauncher} and
+ * {@link #getOwnerUserId} is of a work profile, then this ID could be the user who owns the
+ * profile.
+ */
+ public int getPackageUserId() {
+ return mPackageUserId;
+ }
+
+ /**
+ * ID of the user who sees the shortcuts from this instance.
+ */
+ public abstract int getOwnerUserId();
+
@NonNull
- String getPackageName();
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public ShortcutPackageInfo getPackageInfo() {
+ return mPackageInfo;
+ }
+
+ /**
+ * Should be only used when loading from a file.o
+ */
+ protected void replacePackageInfo(@NonNull ShortcutPackageInfo packageInfo) {
+ mPackageInfo = Preconditions.checkNotNull(packageInfo);
+ }
+
+ public void refreshPackageInfoAndSave(ShortcutService s) {
+ mPackageInfo.refresh(s, this);
+ s.scheduleSaveUser(getOwnerUserId());
+ }
+
+ public void ensureNotShadowAndSave(ShortcutService s) {
+ if (mPackageInfo.isShadow()) {
+ mPackageInfo.setShadow(false);
+ s.scheduleSaveUser(getOwnerUserId());
+ }
+ }
- void saveToXml(@NonNull XmlSerializer out, boolean forBackup)
+ public abstract void saveToXml(@NonNull XmlSerializer out, boolean forBackup)
throws IOException, XmlPullParserException;
}
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index cf1102595d84..76a2dfadc264 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -72,7 +72,6 @@ import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.content.PackageMonitor;
import com.android.internal.os.BackgroundThread;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.Preconditions;
import com.android.server.LocalServices;
@@ -85,6 +84,10 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -92,6 +95,7 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
+import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
@@ -104,8 +108,6 @@ import java.util.function.Predicate;
*
* - Default launcher check does take a few ms. Worth caching.
*
- * - Don't backup launcher from different profile.
- *
* - Clear data -> remove all dynamic? but not the pinned?
*
* - Scan and remove orphan bitmaps (just in case).
@@ -633,31 +635,45 @@ public class ShortcutService extends IShortcutService.Stub {
}
path.mkdirs();
final AtomicFile file = new AtomicFile(path);
- FileOutputStream outs = null;
+ FileOutputStream os = null;
try {
- outs = file.startWrite();
-
- // Write to XML
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(outs, StandardCharsets.UTF_8.name());
- out.startDocument(null, true);
+ os = file.startWrite();
- getUserShortcutsLocked(userId).saveToXml(this, out, /* forBackup= */ false);
+ saveUserInternalLocked(userId, os, /* forBackup= */ false);
- out.endDocument();
-
- // Close.
- file.finishWrite(outs);
- } catch (IOException|XmlPullParserException e) {
+ file.finishWrite(os);
+ } catch (XmlPullParserException|IOException e) {
Slog.e(TAG, "Failed to write to file " + file.getBaseFile(), e);
- file.failWrite(outs);
+ file.failWrite(os);
}
}
+ private void saveUserInternalLocked(@UserIdInt int userId, OutputStream os,
+ boolean forBackup) throws IOException, XmlPullParserException {
+
+ final BufferedOutputStream bos = new BufferedOutputStream(os);
+
+ // Write to XML
+ XmlSerializer out = new FastXmlSerializer();
+ out.setOutput(bos, StandardCharsets.UTF_8.name());
+ out.startDocument(null, true);
+
+ getUserShortcutsLocked(userId).saveToXml(this, out, forBackup);
+
+ out.endDocument();
+
+ bos.flush();
+ os.flush();
+ }
+
static IOException throwForInvalidTag(int depth, String tag) throws IOException {
throw new IOException(String.format("Invalid tag '%s' found at depth %d", tag, depth));
}
+ static void warnForInvalidTag(int depth, String tag) throws IOException {
+ Slog.w(TAG, String.format("Invalid tag '%s' found at depth %d", tag, depth));
+ }
+
@Nullable
private ShortcutUser loadUserLocked(@UserIdInt int userId) {
final File path = new File(injectUserDataPath(userId), FILENAME_USER_PACKAGES);
@@ -675,30 +691,8 @@ public class ShortcutService extends IShortcutService.Stub {
}
return null;
}
- ShortcutUser ret = null;
try {
- XmlPullParser parser = Xml.newPullParser();
- parser.setInput(in, StandardCharsets.UTF_8.name());
-
- int type;
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
- if (type != XmlPullParser.START_TAG) {
- continue;
- }
- final int depth = parser.getDepth();
-
- final String tag = parser.getName();
- if (DEBUG_LOAD) {
- Slog.d(TAG, String.format("depth=%d type=%d name=%s",
- depth, type, tag));
- }
- if ((depth == 1) && ShortcutUser.TAG_ROOT.equals(tag)) {
- ret = ShortcutUser.loadFromXml(parser, userId);
- continue;
- }
- throwForInvalidTag(depth, tag);
- }
- return ret;
+ return loadUserInternal(userId, in, /* forBackup= */ false);
} catch (IOException|XmlPullParserException e) {
Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e);
return null;
@@ -707,6 +701,36 @@ public class ShortcutService extends IShortcutService.Stub {
}
}
+ private ShortcutUser loadUserInternal(@UserIdInt int userId, InputStream is,
+ boolean fromBackup) throws XmlPullParserException, IOException {
+
+ final BufferedInputStream bis = new BufferedInputStream(is);
+
+ ShortcutUser ret = null;
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(bis, StandardCharsets.UTF_8.name());
+
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+ final int depth = parser.getDepth();
+
+ final String tag = parser.getName();
+ if (DEBUG_LOAD) {
+ Slog.d(TAG, String.format("depth=%d type=%d name=%s",
+ depth, type, tag));
+ }
+ if ((depth == 1) && ShortcutUser.TAG_ROOT.equals(tag)) {
+ ret = ShortcutUser.loadFromXml(this, parser, userId, fromBackup);
+ continue;
+ }
+ throwForInvalidTag(depth, tag);
+ }
+ return ret;
+ }
+
private void scheduleSaveBaseState() {
scheduleSaveInner(UserHandle.USER_NULL); // Special case -- use USER_NULL for base state.
}
@@ -1042,6 +1066,10 @@ public class ShortcutService extends IShortcutService.Stub {
Preconditions.checkState(isCallerShell(), "Caller must be shell");
}
+ private void enforceSystem() {
+ Preconditions.checkState(isCallerSystem(), "Caller must be system");
+ }
+
private void verifyCaller(@NonNull String packageName, @UserIdInt int userId) {
Preconditions.checkStringNotEmpty(packageName, "packageName");
@@ -1182,10 +1210,10 @@ public class ShortcutService extends IShortcutService.Stub {
final int size = newShortcuts.size();
synchronized (mLock) {
- getUserShortcutsLocked(userId).ensurePackageInfo(this, packageName, userId);
-
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
+ ps.ensureNotShadowAndSave(this);
+
// Throttling.
if (!ps.tryApiCall(this)) {
return false;
@@ -1219,10 +1247,10 @@ public class ShortcutService extends IShortcutService.Stub {
final int size = newShortcuts.size();
synchronized (mLock) {
- getUserShortcutsLocked(userId).ensurePackageInfo(this, packageName, userId);
-
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
+ ps.ensureNotShadowAndSave(this);
+
// Throttling.
if (!ps.tryApiCall(this)) {
return false;
@@ -1258,10 +1286,10 @@ public class ShortcutService extends IShortcutService.Stub {
verifyCaller(packageName, userId);
synchronized (mLock) {
- getUserShortcutsLocked(userId).ensurePackageInfo(this, packageName, userId);
-
final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId);
+ ps.ensureNotShadowAndSave(this);
+
// Throttling.
if (!ps.tryApiCall(this)) {
return false;
@@ -1466,6 +1494,9 @@ public class ShortcutService extends IShortcutService.Stub {
@VisibleForTesting
void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId) {
+
+ // TODO Don't remove shadow packages' information.
+
final boolean wasUserLoaded = isUserLoadedLocked(owningUserId);
final ShortcutUser mUser = getUserShortcutsLocked(owningUserId);
@@ -1492,9 +1523,6 @@ public class ShortcutService extends IShortcutService.Stub {
mUser.getPackages().valueAt(i).refreshPinnedFlags(this);
}
- // Remove the package info too.
- mUser.removePackageInfo(packageUserId, packageName);
-
scheduleSaveUser(owningUserId);
if (doNotify) {
@@ -1617,11 +1645,13 @@ public class ShortcutService extends IShortcutService.Stub {
Preconditions.checkNotNull(shortcutIds, "shortcutIds");
synchronized (mLock) {
- getUserShortcutsLocked(userId).ensurePackageInfo(
- ShortcutService.this, callingPackage, launcherUserId);
+ final ShortcutLauncher launcher =
+ getLauncherShortcuts(callingPackage, userId, launcherUserId);
- getLauncherShortcuts(callingPackage, userId, launcherUserId).pinShortcuts(
- ShortcutService.this, packageName, shortcutIds);
+ launcher.ensureNotShadowAndSave(ShortcutService.this);
+
+ launcher.pinShortcuts(
+ ShortcutService.this, userId, packageName, shortcutIds);
}
userPackageChanged(packageName, userId);
}
@@ -1731,23 +1761,21 @@ public class ShortcutService extends IShortcutService.Stub {
if (DEBUG) {
Slog.d(TAG, "cleanupGonePackages() userId=" + userId);
}
- ArrayList<PackageWithUser> gonePackages = null;
+ final ArrayList<PackageWithUser> gonePackages = new ArrayList<>();
synchronized (mLock) {
final ShortcutUser user = getUserShortcutsLocked(userId);
- final ArrayMap<PackageWithUser, ShortcutPackageInfo> infos = user.getAllPackageInfos();
- for (int i = infos.size() -1; i >= 0; i--) {
- final ShortcutPackageInfo info = infos.valueAt(i);
- if (info.isShadow()) {
- continue;
+
+ user.forAllPackageItems(spi -> {
+ if (spi.getPackageInfo().isShadow()) {
+ return; // Don't delete shadow information.
}
- if (isPackageInstalled(info.getPackageName(), info.getUserId())) {
- continue;
+ if (isPackageInstalled(spi.getPackageName(), spi.getPackageUserId())) {
+ return;
}
- gonePackages = ArrayUtils.add(gonePackages,
- PackageWithUser.of(info.getUserId(), info.getPackageName()));
- }
- if (gonePackages != null) {
+ gonePackages.add(PackageWithUser.of(spi));
+ });
+ if (gonePackages.size() > 0) {
for (int i = gonePackages.size() - 1; i >= 0; i--) {
final PackageWithUser pu = gonePackages.get(i);
cleanUpPackageLocked(pu.packageName, userId, pu.userId);
@@ -1761,25 +1789,18 @@ public class ShortcutService extends IShortcutService.Stub {
Slog.d(TAG, String.format("handlePackageAdded: %s user=%d", packageName, userId));
}
synchronized (mLock) {
- final ShortcutPackageInfo existing = getUserShortcutsLocked(userId)
- .getPackageInfo(userId, packageName);
-
- if (existing != null && existing.isShadow()) {
- Slog.w(TAG, "handlePackageAdded: TODO Restore not implemented");
- }
+ getUserShortcutsLocked(userId).unshadowPackage(this, packageName, userId);
}
}
private void handlePackageUpdateFinished(String packageName, @UserIdInt int userId) {
if (DEBUG) {
- Slog.d(TAG, String.format("handlePackageUpdateFinished: %s user=%d", packageName, userId));
+ Slog.d(TAG, String.format("handlePackageUpdateFinished: %s user=%d",
+ packageName, userId));
}
+
synchronized (mLock) {
- final ShortcutPackageInfo spi =
- getUserShortcutsLocked(userId).getPackageInfo(userId, packageName);
- if (spi != null) {
- spi.refreshAndSave(this, userId);
- }
+ getUserShortcutsLocked(userId).unshadowPackage(this, packageName, userId);
}
}
@@ -1792,13 +1813,14 @@ public class ShortcutService extends IShortcutService.Stub {
}
}
- // === Backup & restore ===
+ // === PackageManager interaction ===
PackageInfo getPackageInfoWithSignatures(String packageName, @UserIdInt int userId) {
return injectPackageInfo(packageName, userId, true);
}
int injectGetPackageUid(@NonNull String packageName, @UserIdInt int userId) {
+ final long token = injectClearCallingIdentity();
try {
return mIPackageManager.getPackageUid(packageName, PACKAGE_MATCH_FLAGS
, userId);
@@ -1806,12 +1828,15 @@ public class ShortcutService extends IShortcutService.Stub {
// Shouldn't happen.
Slog.wtf(TAG, "RemoteException", e);
return -1;
+ } finally {
+ injectRestoreCallingIdentity(token);
}
}
@VisibleForTesting
PackageInfo injectPackageInfo(String packageName, @UserIdInt int userId,
boolean getSignatures) {
+ final long token = injectClearCallingIdentity();
try {
return mIPackageManager.getPackageInfo(packageName, PACKAGE_MATCH_FLAGS
| (getSignatures ? PackageManager.GET_SIGNATURES : 0)
@@ -1820,17 +1845,22 @@ public class ShortcutService extends IShortcutService.Stub {
// Shouldn't happen.
Slog.wtf(TAG, "RemoteException", e);
return null;
+ } finally {
+ injectRestoreCallingIdentity(token);
}
}
@VisibleForTesting
ApplicationInfo injectApplicationInfo(String packageName, @UserIdInt int userId) {
+ final long token = injectClearCallingIdentity();
try {
return mIPackageManager.getApplicationInfo(packageName, PACKAGE_MATCH_FLAGS, userId);
} catch (RemoteException e) {
// Shouldn't happen.
Slog.wtf(TAG, "RemoteException", e);
return null;
+ } finally {
+ injectRestoreCallingIdentity(token);
}
}
@@ -1839,12 +1869,61 @@ public class ShortcutService extends IShortcutService.Stub {
return (ai != null) && ((ai.flags & flags) == flags);
}
+ private boolean isPackageInstalled(String packageName, int userId) {
+ return isApplicationFlagSet(packageName, userId, ApplicationInfo.FLAG_INSTALLED);
+ }
+
+ // === Backup & restore ===
+
boolean shouldBackupApp(String packageName, int userId) {
return isApplicationFlagSet(packageName, userId, ApplicationInfo.FLAG_ALLOW_BACKUP);
}
- private boolean isPackageInstalled(String packageName, int userId) {
- return isApplicationFlagSet(packageName, userId, ApplicationInfo.FLAG_INSTALLED);
+ @Override
+ public byte[] getBackupPayload(@UserIdInt int userId) throws RemoteException {
+ enforceSystem();
+ if (DEBUG) {
+ Slog.d(TAG, "Backing up user " + userId);
+ }
+ synchronized (mLock) {
+ final ShortcutUser user = getUserShortcutsLocked(userId);
+ if (user == null) {
+ Slog.w(TAG, "Can't backup: user not found: id=" + userId);
+ return null;
+ }
+
+ user.forAllPackageItems(spi -> spi.refreshPackageInfoAndSave(this));
+
+ // Then save.
+ final ByteArrayOutputStream os = new ByteArrayOutputStream(32 * 1024);
+ try {
+ saveUserInternalLocked(userId, os, /* forBackup */ true);
+ } catch (XmlPullParserException|IOException e) {
+ // Shouldn't happen.
+ Slog.w(TAG, "Backup failed.", e);
+ return null;
+ }
+ return os.toByteArray();
+ }
+ }
+
+ @Override
+ public void applyRestore(byte[] payload, @UserIdInt int userId) throws RemoteException {
+ enforceSystem();
+ if (DEBUG) {
+ Slog.d(TAG, "Restoring user " + userId);
+ }
+ final ShortcutUser user;
+ final ByteArrayInputStream is = new ByteArrayInputStream(payload);
+ try {
+ user = loadUserInternal(userId, is, /* fromBackup */ true);
+ } catch (XmlPullParserException|IOException e) {
+ Slog.w(TAG, "Restoration failed.", e);
+ return;
+ }
+ synchronized (mLock) {
+ mUsers.put(userId, user);
+ }
}
// === Dump ===
@@ -2202,19 +2281,4 @@ public class ShortcutService extends IShortcutService.Stub {
return pkg.findShortcutById(shortcutId);
}
}
-
- @VisibleForTesting
- ShortcutPackageInfo getPackageInfoForTest(String packageName, int userId) {
- return getPackageInfoForTest(packageName, userId, userId);
- }
-
- @VisibleForTesting
- ShortcutPackageInfo getPackageInfoForTest(String packageName, int userId, int packageUserId) {
- synchronized (mLock) {
- final ShortcutUser user = mUsers.get(userId);
- if (user == null) return null;
-
- return user.getPackageInfo(packageUserId, packageName);
- }
- }
}
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 19feb2aee943..487558f81e45 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -31,6 +31,7 @@ import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.io.PrintWriter;
+import java.util.function.Consumer;
/**
* User information used by {@link ShortcutService}.
@@ -56,6 +57,10 @@ class ShortcutUser {
return new PackageWithUser(launcherUserId, packageName);
}
+ public static PackageWithUser of(ShortcutPackageItem spi) {
+ return new PackageWithUser(spi.getPackageUserId(), spi.getPackageName());
+ }
+
@Override
public int hashCode() {
return packageName.hashCode() ^ userId;
@@ -84,8 +89,6 @@ class ShortcutUser {
private final ArrayMap<PackageWithUser, ShortcutLauncher> mLaunchers = new ArrayMap<>();
- private final ArrayMap<PackageWithUser, ShortcutPackageInfo> mPackageInfos = new ArrayMap<>();
-
private ComponentName mLauncherComponent;
public ShortcutUser(int userId) {
@@ -100,35 +103,14 @@ class ShortcutUser {
return mLaunchers;
}
- public ShortcutLauncher getLauncher(@UserIdInt int userId, @NonNull String packageName) {
- return mLaunchers.get(PackageWithUser.of(userId, packageName));
- }
-
public void addLauncher(ShortcutLauncher launcher) {
- mLaunchers.put(PackageWithUser.of(launcher.getUserId(), launcher.getPackageName()),
- launcher);
+ mLaunchers.put(PackageWithUser.of(launcher.getPackageUserId(),
+ launcher.getPackageName()), launcher);
}
public ShortcutLauncher removeLauncher(
- @UserIdInt int userId, @NonNull String packageName) {
- return mLaunchers.remove(PackageWithUser.of(userId, packageName));
- }
-
- public ArrayMap<PackageWithUser, ShortcutPackageInfo> getAllPackageInfos() {
- return mPackageInfos;
- }
-
- public ShortcutPackageInfo getPackageInfo(@UserIdInt int userId, @NonNull String packageName) {
- return mPackageInfos.get(PackageWithUser.of(userId, packageName));
- }
-
- public void addPackageInfo(ShortcutPackageInfo spi) {
- mPackageInfos.put(PackageWithUser.of(spi.getUserId(), spi.getPackageName()), spi);
- }
-
- public ShortcutPackageInfo removePackageInfo(
- @UserIdInt int userId, @NonNull String packageName) {
- return mPackageInfos.remove(PackageWithUser.of(userId, packageName));
+ @UserIdInt int packageUserId, @NonNull String packageName) {
+ return mLaunchers.remove(PackageWithUser.of(packageUserId, packageName));
}
public ShortcutPackage getPackageShortcuts(@NonNull String packageName) {
@@ -151,20 +133,37 @@ class ShortcutUser {
return ret;
}
- public void ensurePackageInfo(ShortcutService s, String packageName, @UserIdInt int userId) {
- final PackageWithUser key = PackageWithUser.of(userId, packageName);
- final ShortcutPackageInfo existing = mPackageInfos.get(key);
-
- if (existing != null) {
- return;
+ public void forAllPackageItems(Consumer<ShortcutPackageItem> callback) {
+ {
+ final int size = mLaunchers.size();
+ for (int i = 0; i < size; i++) {
+ callback.accept(mLaunchers.valueAt(i));
+ }
}
- if (ShortcutService.DEBUG) {
- Slog.d(TAG, String.format("Fetching package info: %s user=%d", packageName, userId));
+ {
+ final int size = mPackages.size();
+ for (int i = 0; i < size; i++) {
+ callback.accept(mPackages.valueAt(i));
+ }
}
- final ShortcutPackageInfo newSpi = ShortcutPackageInfo.generateForInstalledPackage(
- s, packageName, userId);
- mPackageInfos.put(key, newSpi);
- s.scheduleSaveUser(mUserId);
+ }
+
+ public void unshadowPackage(ShortcutService s, @NonNull String packageName,
+ @UserIdInt int packageUserId) {
+ forPackageItem(packageName, packageUserId, spi -> {
+ Slog.i(TAG, String.format("Restoring for %s, user=%d", packageName, packageUserId));
+ spi.ensureNotShadowAndSave(s);
+ });
+ }
+
+ public void forPackageItem(@NonNull String packageName, @UserIdInt int packageUserId,
+ Consumer<ShortcutPackageItem> callback) {
+ forAllPackageItems(spi -> {
+ if ((spi.getPackageUserId() == packageUserId)
+ && spi.getPackageName().equals(packageName)) {
+ callback.accept(spi);
+ }
+ });
}
public void saveToXml(ShortcutService s, XmlSerializer out, boolean forBackup)
@@ -174,12 +173,7 @@ class ShortcutUser {
ShortcutService.writeTagValue(out, TAG_LAUNCHER,
mLauncherComponent);
- {
- final int size = mPackageInfos.size();
- for (int i = 0; i < size; i++) {
- saveShortcutPackageItem(s, out, mPackageInfos.valueAt(i), forBackup);
- }
- }
+ // Can't use forEachPackageItem due to the checked exceptions.
{
final int size = mLaunchers.size();
for (int i = 0; i < size; i++) {
@@ -198,14 +192,19 @@ class ShortcutUser {
private void saveShortcutPackageItem(ShortcutService s, XmlSerializer out,
ShortcutPackageItem spi, boolean forBackup) throws IOException, XmlPullParserException {
- if (forBackup && !s.shouldBackupApp(spi.getPackageName(), mUserId)) {
- return; // Don't save.
+ if (forBackup) {
+ if (!s.shouldBackupApp(spi.getPackageName(), spi.getPackageUserId())) {
+ return; // Don't save.
+ }
+ if (spi.getPackageUserId() != spi.getOwnerUserId()) {
+ return; // Don't save cross-user information.
+ }
}
spi.saveToXml(out, forBackup);
}
- public static ShortcutUser loadFromXml(XmlPullParser parser, int userId)
- throws IOException, XmlPullParserException {
+ public static ShortcutUser loadFromXml(ShortcutService s, XmlPullParser parser, int userId,
+ boolean fromBackup) throws IOException, XmlPullParserException {
final ShortcutUser ret = new ShortcutUser(userId);
final int outerDepth = parser.getDepth();
@@ -217,31 +216,30 @@ class ShortcutUser {
}
final int depth = parser.getDepth();
final String tag = parser.getName();
- switch (tag) {
- case TAG_LAUNCHER: {
- ret.mLauncherComponent = ShortcutService.parseComponentNameAttribute(
- parser, ATTR_VALUE);
- continue;
- }
- case ShortcutPackage.TAG_ROOT: {
- final ShortcutPackage shortcuts = ShortcutPackage.loadFromXml(parser, userId);
-
- // Don't use addShortcut(), we don't need to save the icon.
- ret.getPackages().put(shortcuts.getPackageName(), shortcuts);
- continue;
- }
-
- case ShortcutLauncher.TAG_ROOT: {
- ret.addLauncher(ShortcutLauncher.loadFromXml(parser, userId));
- continue;
- }
- case ShortcutPackageInfo.TAG_ROOT: {
- ret.addPackageInfo(ShortcutPackageInfo.loadFromXml(parser, userId));
- continue;
+ if (depth == outerDepth + 1) {
+ switch (tag) {
+ case TAG_LAUNCHER: {
+ ret.mLauncherComponent = ShortcutService.parseComponentNameAttribute(
+ parser, ATTR_VALUE);
+ continue;
+ }
+ case ShortcutPackage.TAG_ROOT: {
+ final ShortcutPackage shortcuts = ShortcutPackage.loadFromXml(
+ s, parser, userId, fromBackup);
+
+ // Don't use addShortcut(), we don't need to save the icon.
+ ret.getPackages().put(shortcuts.getPackageName(), shortcuts);
+ continue;
+ }
+
+ case ShortcutLauncher.TAG_ROOT: {
+ ret.addLauncher(ShortcutLauncher.loadFromXml(parser, userId, fromBackup));
+ continue;
+ }
}
}
- throw ShortcutService.throwForInvalidTag(depth, tag);
+ ShortcutService.warnForInvalidTag(depth, tag);
}
return ret;
}
@@ -283,9 +281,5 @@ class ShortcutUser {
for (int i = 0; i < mPackages.size(); i++) {
mPackages.valueAt(i).dump(s, pw, prefix + " ");
}
-
- for (int i = 0; i < mPackageInfos.size(); i++) {
- mPackageInfos.valueAt(i).dump(s, pw, prefix + " ");
- }
}
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 14efc27ce35d..0a4effb26249 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1025,7 +1025,8 @@ public final class SystemServer {
mSystemServiceManager.startService(BACKUP_MANAGER_SERVICE_CLASS);
}
- if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)) {
+ if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)
+ || context.getResources().getBoolean(R.bool.config_enableAppWidgetService)) {
mSystemServiceManager.startService(APPWIDGET_SERVICE_CLASS);
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
index 61249ae7eaa1..f034d55d9736 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
@@ -797,7 +797,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
@NonNull
private List<ShortcutInfo> assertShortcutIds(@NonNull List<ShortcutInfo> actualShortcuts,
String... expectedIds) {
- assertEquals(expectedIds.length, actualShortcuts.size());
final HashSet<String> expected = new HashSet<>(list(expectedIds));
final HashSet<String> actual = new HashSet<>();
for (ShortcutInfo s : actualShortcuts) {
@@ -973,18 +972,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
assertTrue(b == null || b.size() == 0);
}
- private void assertShortcutPackageInfo(String packageName, int userId, int expectedVersion) {
- ShortcutPackageInfo spi = mService.getPackageInfoForTest(packageName, userId);
- assertNotNull(spi);
- assertEquals(expectedVersion, spi.getVersionCode());
-
- assertTrue(spi.canRestoreTo(genPackage(packageName, /*uid*/ 0, 9999999, packageName)));
- }
-
- private void assertNoShortcutPackageInfo(String packageName, int userId) {
- assertNull(mService.getPackageInfoForTest(packageName, userId));
- }
-
private ShortcutInfo getPackageShortcut(String packageName, String shortcutId, int userId) {
return mService.getPackageShortcutForTest(packageName, shortcutId, userId);
}
@@ -1229,11 +1216,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
"shortcut1", "shortcut2");
assertEquals(2, mManager.getRemainingCallCount());
- assertShortcutPackageInfo(CALLING_PACKAGE_1, USER_0, 1);
- assertNoShortcutPackageInfo(CALLING_PACKAGE_2, USER_0);
- assertNoShortcutPackageInfo(CALLING_PACKAGE_1, USER_10);
- assertNoShortcutPackageInfo(CALLING_PACKAGE_2, USER_10);
-
// TODO: Check fields
assertTrue(mManager.setDynamicShortcuts(list(si1)));
@@ -1260,11 +1242,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"))));
-
- assertShortcutPackageInfo(CALLING_PACKAGE_1, USER_0, 1);
- assertNoShortcutPackageInfo(CALLING_PACKAGE_2, USER_0);
- assertNoShortcutPackageInfo(CALLING_PACKAGE_1, USER_10);
- assertShortcutPackageInfo(CALLING_PACKAGE_2, USER_10, 2);
});
}
@@ -1302,7 +1279,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
- assertShortcutPackageInfo(CALLING_PACKAGE_2, USER_10, 2);
});
}
@@ -1608,7 +1584,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
Bitmap bmp;
setCaller(LAUNCHER_1);
-
// Check hasIconResource()/hasIconFile().
assertShortcutIds(assertAllHaveIconResId(mLauncherApps.getShortcutInfo(
CALLING_PACKAGE_1, list("res32x32"),
@@ -1898,12 +1873,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
// TODO Check bitmap removal too.
runWithCaller(CALLING_PACKAGE_2, USER_11, () -> {
- assertNoShortcutPackageInfo(CALLING_PACKAGE_2, USER_11);
-
mManager.updateShortcuts(list());
-
- // Even an empty update call will populate the package info.
- assertShortcutPackageInfo(CALLING_PACKAGE_2, USER_11, 2);
});
}
@@ -1940,7 +1910,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
assertTrue(mManager.setDynamicShortcuts(list(s2_2, s2_3, s2_4)));
setCaller(CALLING_PACKAGE_3);
- final ShortcutInfo s3_2 = makeShortcutWithTimestamp("s3", 5000);
+ final ShortcutInfo s3_2 = makeShortcutWithTimestamp("s3", START_TIME + 5000);
assertTrue(mManager.setDynamicShortcuts(list(s3_2)));
setCaller(LAUNCHER_1);
@@ -2100,16 +2070,9 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
// Pin some.
runWithCaller(LAUNCHER_1, USER_0, () -> {
- assertNoShortcutPackageInfo(LAUNCHER_1, USER_0);
-
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
list("s2", "s3"), getCallingUser());
- assertShortcutPackageInfo(LAUNCHER_1, USER_0, 4);
- assertNoShortcutPackageInfo(LAUNCHER_2, USER_0);
- assertNoShortcutPackageInfo(LAUNCHER_1, USER_10);
- assertNoShortcutPackageInfo(LAUNCHER_2, USER_10);
-
mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
list("s3", "s4", "s5"), getCallingUser());
@@ -2170,19 +2133,11 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
dumpsysOnLogcat();
- assertNoShortcutPackageInfo(LAUNCHER_1, USER_0);
- assertNoShortcutPackageInfo(LAUNCHER_2, USER_0);
- assertNoShortcutPackageInfo(LAUNCHER_1, USER_10);
- assertNoShortcutPackageInfo(LAUNCHER_2, USER_10);
-
// Pin some.
runWithCaller(LAUNCHER_1, USER_0, () -> {
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
list("s3", "s4"), getCallingUser());
- assertShortcutPackageInfo(LAUNCHER_1, USER_0, 4);
- assertNoShortcutPackageInfo(LAUNCHER_2, USER_0);
-
mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
list("s1", "s2", "s4"), getCallingUser());
});
@@ -2256,16 +2211,10 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
| ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
"s2");
- assertNoShortcutPackageInfo(LAUNCHER_2, USER_0);
- assertNoShortcutPackageInfo(LAUNCHER_2, USER_10);
-
// Now pin some.
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
list("s1", "s2"), getCallingUser());
- assertShortcutPackageInfo(LAUNCHER_2, USER_0, 5);
- assertNoShortcutPackageInfo(LAUNCHER_2, USER_10);
-
mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
list("s1", "s2"), getCallingUser());
@@ -2283,12 +2232,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
"s2");
});
- assertShortcutPackageInfo(CALLING_PACKAGE_1, USER_0, 1);
- assertShortcutPackageInfo(CALLING_PACKAGE_2, USER_0, 2);
- assertNoShortcutPackageInfo(CALLING_PACKAGE_3, USER_0);
- assertShortcutPackageInfo(LAUNCHER_1, USER_0, 4);
- assertShortcutPackageInfo(LAUNCHER_2, USER_0, 5);
-
// Re-initialize and load from the files.
mService.saveDirtyInfo();
initService();
@@ -2297,12 +2240,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
mService.handleUnlockUser(USER_0);
// Make sure package info is restored too.
- assertShortcutPackageInfo(CALLING_PACKAGE_1, USER_0, 1);
- assertShortcutPackageInfo(CALLING_PACKAGE_2, USER_0, 2);
- assertNoShortcutPackageInfo(CALLING_PACKAGE_3, USER_0);
- assertShortcutPackageInfo(LAUNCHER_1, USER_0, 4);
- assertShortcutPackageInfo(LAUNCHER_2, USER_0, 5);
-
runWithCaller(LAUNCHER_1, USER_0, () -> {
assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
@@ -3234,10 +3171,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
mService.getShortcutsForTest().get(UserHandle.USER_SYSTEM).setLauncherComponent(
mService, new ComponentName("pkg1", "class"));
- assertShortcutPackageInfo(CALLING_PACKAGE_1, USER_0, 1);
- assertShortcutPackageInfo(CALLING_PACKAGE_2, USER_0, 2);
- assertNoShortcutPackageInfo(CALLING_PACKAGE_3, USER_0);
-
// Restore.
mService.saveDirtyInfo();
initService();
@@ -3248,10 +3181,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
// this will pre-load the per-user info.
mService.handleUnlockUser(UserHandle.USER_SYSTEM);
- assertShortcutPackageInfo(CALLING_PACKAGE_1, USER_0, 1);
- assertShortcutPackageInfo(CALLING_PACKAGE_2, USER_0, 2);
- assertNoShortcutPackageInfo(CALLING_PACKAGE_3, USER_0);
-
// Now it's loaded.
assertEquals(1, mService.getShortcutsForTest().size());
@@ -3394,11 +3323,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
assertShortcutExists(CALLING_PACKAGE_1, "s10_1", USER_10);
assertShortcutExists(CALLING_PACKAGE_2, "s10_2", USER_10);
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_10));
-
mService.saveDirtyInfo();
// Nonexistent package.
@@ -3430,11 +3354,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
assertShortcutExists(CALLING_PACKAGE_1, "s10_1", USER_10);
assertShortcutExists(CALLING_PACKAGE_2, "s10_2", USER_10);
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_10));
-
mService.saveDirtyInfo();
// Remove a package.
@@ -3465,11 +3384,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
assertShortcutExists(CALLING_PACKAGE_1, "s10_1", USER_10);
assertShortcutExists(CALLING_PACKAGE_2, "s10_2", USER_10);
- assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_10));
-
mService.saveDirtyInfo();
// Remove a launcher.
@@ -3524,11 +3438,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
assertShortcutExists(CALLING_PACKAGE_1, "s10_1", USER_10);
assertShortcutNotExists(CALLING_PACKAGE_2, "s10_2", USER_10);
- assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10));
- assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_10));
-
mService.saveDirtyInfo();
// Remove the other launcher from user 10 too.
@@ -3585,7 +3494,8 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
mService.saveDirtyInfo();
}
- public void testHandleGonePackage_crossProfile() {
+
+ public void testSaveAndLoadUser_forBackup() {
// Create some shortcuts.
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
assertTrue(mManager.setDynamicShortcuts(list(
@@ -3667,26 +3577,79 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_P0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10));
-
- // These two shouldn't exist
- assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_P0, USER_0));
- assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0, USER_P0));
-
- assertNotNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_0, USER_0));
- assertNotNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_0, USER_P0));
- assertNotNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_P0, USER_0));
- assertNotNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_10, USER_10));
-
// Make sure all the information is persisted.
mService.saveDirtyInfo();
initService();
mService.handleUnlockUser(USER_0);
mService.handleUnlockUser(USER_P0);
mService.handleUnlockUser(USER_10);
+ }
+
+ public void testHandleGonePackage_crossProfile() {
+ // Create some shortcuts.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_P0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
+ });
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_P0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_P0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
+
+ // Pin some.
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s1"), HANDLE_USER_0);
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s2"), UserHandle.of(USER_P0));
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ list("s3"), HANDLE_USER_0);
+ });
+
+ runWithCaller(LAUNCHER_1, USER_P0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s2"), HANDLE_USER_0);
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s3"), UserHandle.of(USER_P0));
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ list("s1"), HANDLE_USER_0);
+ });
+
+ runWithCaller(LAUNCHER_1, USER_10, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s3"), HANDLE_USER_10);
+ });
+
+ // Check the state.
assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
@@ -3704,20 +3667,28 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_P0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10));
+ // Make sure all the information is persisted.
+ mService.saveDirtyInfo();
+ initService();
+ mService.handleUnlockUser(USER_0);
+ mService.handleUnlockUser(USER_P0);
+ mService.handleUnlockUser(USER_10);
- // These two shouldn't exist
- assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_P0, USER_0));
- assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0, USER_P0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
- assertNotNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_0, USER_0));
- assertNotNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_0, USER_P0));
- assertNotNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_P0, USER_0));
- assertNotNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_10, USER_10));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_P0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_P0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s2", USER_0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
// Start uninstalling.
uninstallPackage(USER_10, LAUNCHER_1);
@@ -3739,16 +3710,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_P0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10));
-
- assertNotNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_0, USER_0));
- assertNotNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_0, USER_P0));
- assertNotNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_P0, USER_0));
- assertNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_10, USER_10));
-
// Uninstall.
uninstallPackage(USER_10, CALLING_PACKAGE_1);
mService.cleanupGonePackages(USER_10);
@@ -3769,17 +3730,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_P0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0));
- assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10));
-
- assertNotNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_0, USER_0));
- assertNotNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_0, USER_P0));
- assertNotNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_P0, USER_0));
- assertNotNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_P0, USER_P0));
- assertNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_10, USER_10));
-
uninstallPackage(USER_P0, LAUNCHER_1);
mService.cleanupGonePackages(USER_0);
@@ -3799,17 +3749,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_P0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0));
- assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10));
-
- assertNotNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_0, USER_0));
- assertNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_0, USER_P0));
- assertNotNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_P0, USER_0));
- assertNotNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_P0, USER_P0));
- assertNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_10, USER_10));
-
mService.cleanupGonePackages(USER_P0);
assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
@@ -3828,17 +3767,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_P0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0));
- assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10));
-
- assertNotNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_0, USER_0));
- assertNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_0, USER_P0));
- assertNotNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_P0, USER_0));
- assertNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_P0, USER_P0));
- assertNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_10, USER_10));
-
uninstallPackage(USER_P0, CALLING_PACKAGE_1);
mService.saveDirtyInfo();
@@ -3863,17 +3791,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0));
- assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_P0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0));
- assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10));
-
- assertNotNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_0, USER_0));
- assertNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_0, USER_P0));
- assertNotNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_P0, USER_0));
- assertNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_P0, USER_P0));
- assertNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_10, USER_10));
-
// Uninstall
uninstallPackage(USER_0, LAUNCHER_1);
@@ -3899,17 +3816,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0));
- assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_P0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0));
- assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10));
-
- assertNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_0, USER_0));
- assertNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_0, USER_P0));
- assertNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_P0, USER_0));
- assertNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_P0, USER_P0));
- assertNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_10, USER_10));
-
uninstallPackage(USER_0, CALLING_PACKAGE_2);
mService.saveDirtyInfo();
@@ -3933,17 +3839,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
-
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0));
- assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_P0));
- assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0));
- assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10));
-
- assertNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_0, USER_0));
- assertNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_0, USER_P0));
- assertNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_P0, USER_0));
- assertNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_P0, USER_P0));
- assertNull(mService.getPackageInfoForTest(LAUNCHER_1, USER_10, USER_10));
}
// TODO Detailed test for hasShortcutPermissionInner().
@@ -3998,27 +3893,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
checkCanRestoreTo(false, spi2, 11, "x", "sig2x", "sig1", "y");
}
- public void testShortcutPackageInfoRefresh() {
- addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "sig1");
-
- final ShortcutPackageInfo spi1 = ShortcutPackageInfo.generateForInstalledPackage(
- mService, CALLING_PACKAGE_1, USER_0);
-
- checkCanRestoreTo(true, spi1, 10, "sig1");
-
- addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 11, "sig1", "sig2");
-
- spi1.refreshAndSave(mService, USER_0);
-
- mService.handleCleanupUser(USER_0);
- initService();
-
- checkCanRestoreTo(false, spi1, 10, "sig1", "sig2");
- checkCanRestoreTo(false, spi1, 11, "sig", "sig2");
- checkCanRestoreTo(false, spi1, 11, "sig1", "sig");
- checkCanRestoreTo(true, spi1, 11, "sig1", "sig2");
- }
-
public void testHandlePackageDelete() {
setCaller(CALLING_PACKAGE_1, USER_0);
assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
@@ -4038,73 +3912,56 @@ public class ShortcutManagerTest extends InstrumentationTestCase {
setCaller(CALLING_PACKAGE_3, USER_10);
assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_10));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_10));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
mService.mPackageMonitor.onReceive(getTestContext(),
genPackageDeleteIntent(CALLING_PACKAGE_1, USER_0));
- assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_10));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_10));
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
mService.mPackageMonitor.onReceive(getTestContext(),
genPackageDeleteIntent(CALLING_PACKAGE_2, USER_10));
- assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10));
- assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_10));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_10));
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
mInjectedPackages.remove(CALLING_PACKAGE_1);
mInjectedPackages.remove(CALLING_PACKAGE_3);
mService.handleUnlockUser(USER_0);
- assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0));
- assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10));
- assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_10));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_10));
+
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
mService.handleUnlockUser(USER_10);
- assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0));
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0));
- assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_0));
- assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10));
- assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_10));
- assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_10));
+
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
+ assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0));
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
}
public void testHandlePackageUpdate() {
- setCaller(CALLING_PACKAGE_1, USER_0);
- assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
-
- assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0));
- assertEquals(1, mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0).getVersionCode());
-
- addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 123);
-
- mService.mPackageMonitor.onReceive(getTestContext(),
- genPackageUpdateIntent("abc", USER_0));
- assertEquals(1, mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0).getVersionCode());
-
- mService.mPackageMonitor.onReceive(getTestContext(),
- genPackageUpdateIntent("abc", USER_10));
- assertEquals(1, mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0).getVersionCode());
-
- mService.mPackageMonitor.onReceive(getTestContext(),
- genPackageUpdateIntent(CALLING_PACKAGE_1, USER_0));
- assertEquals(123, mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0)
- .getVersionCode());
+ // TODO: Make sure unshadow is called.
}
}
diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable_linear_progress_bar.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable_linear_progress_bar.xml
index 96fd70ecf9cf..a6da114b511b 100644
--- a/tests/VectorDrawableTest/res/drawable/vector_drawable_linear_progress_bar.xml
+++ b/tests/VectorDrawableTest/res/drawable/vector_drawable_linear_progress_bar.xml
@@ -17,7 +17,7 @@
android:height="4dp"
android:viewportHeight="4"
android:viewportWidth="360"
- android:width="360dp" >
+ android:width="36dp" >
<group
android:name="linear_indeterminate"
diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java
index 087e68a841ed..9351f63551fb 100644
--- a/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java
+++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java
@@ -43,33 +43,37 @@ public class AnimatedVectorDrawableTest extends Activity implements View.OnClick
@Override
protected void onCreate(Bundle savedInstanceState) {
+ final int[] layerTypes = {View.LAYER_TYPE_SOFTWARE, View.LAYER_TYPE_HARDWARE};
super.onCreate(savedInstanceState);
ScrollView scrollView = new ScrollView(this);
GridLayout container = new GridLayout(this);
scrollView.addView(container);
- container.setColumnCount(1);
+ container.setColumnCount(2);
for (int i = 0; i < icon.length; i++) {
- Button button = new Button(this);
- button.setWidth(400);
- button.setHeight(400);
- button.setBackgroundResource(icon[i]);
- AnimatedVectorDrawable d = (AnimatedVectorDrawable) button.getBackground();
- d.registerAnimationCallback(new Animatable2.AnimationCallback() {
- @Override
- public void onAnimationStart(Drawable drawable) {
- Log.v(LOGCAT, "Animator start");
- }
+ for (int j = 0; j < layerTypes.length; j++) {
+ Button button = new Button(this);
+ button.setWidth(400);
+ button.setHeight(400);
+ button.setLayerType(layerTypes[j], null);
+ button.setBackgroundResource(icon[i]);
+ AnimatedVectorDrawable d = (AnimatedVectorDrawable) button.getBackground();
+ d.registerAnimationCallback(new Animatable2.AnimationCallback() {
+ @Override
+ public void onAnimationStart(Drawable drawable) {
+ Log.v(LOGCAT, "Animator start");
+ }
- @Override
- public void onAnimationEnd(Drawable drawable) {
+ @Override
+ public void onAnimationEnd(Drawable drawable) {
Log.v(LOGCAT, "Animator end");
- }
- });
+ }
+ });
- container.addView(button);
- button.setOnClickListener(this);
+ container.addView(button);
+ button.setOnClickListener(this);
+ }
}
setContentView(scrollView);