diff options
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); |