Merge "Ensure we change SurfaceView size from UI thread." into nyc-dev
diff --git a/api/current.txt b/api/current.txt
index 0857c8b..dd70a32 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -416,9 +416,11 @@
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
@@ -576,6 +578,7 @@
field public static final int fontFamily = 16843692; // 0x10103ac
field public static final int fontFeatureSettings = 16843959; // 0x10104b7
field public static final int footerDividersEnabled = 16843311; // 0x101022f
+ field public static final int forceHasOverlappingRendering = 16844068; // 0x1010524
field public static final int foreground = 16843017; // 0x1010109
field public static final int foregroundGravity = 16843264; // 0x1010200
field public static final int foregroundTint = 16843885; // 0x101046d
@@ -42345,6 +42348,7 @@
method public void findViewsWithText(java.util.ArrayList<android.view.View>, java.lang.CharSequence, int);
method protected deprecated boolean fitSystemWindows(android.graphics.Rect);
method public android.view.View focusSearch(int);
+ method public void forceHasOverlappingRendering(boolean);
method public void forceLayout();
method public static int generateViewId();
method public java.lang.CharSequence getAccessibilityClassName();
@@ -42390,6 +42394,7 @@
method public boolean getGlobalVisibleRect(android.graphics.Rect, android.graphics.Point);
method public final boolean getGlobalVisibleRect(android.graphics.Rect);
method public android.os.Handler getHandler();
+ method public final boolean getHasOverlappingRendering();
method public final int getHeight();
method public void getHitRect(android.graphics.Rect);
method public int getHorizontalFadingEdgeLength();
@@ -48287,9 +48292,15 @@
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();
@@ -48308,6 +48319,8 @@
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);
@@ -57330,6 +57343,7 @@
method public void forEach(java.util.function.Consumer<? super E>);
method public E get(int);
method public boolean removeIf(java.util.function.Predicate<? super E>);
+ method public void replaceAll(java.util.function.UnaryOperator<E>);
method public int size();
method public void sort(java.util.Comparator<? super E>);
method public java.util.Spliterator<E> spliterator();
@@ -57967,6 +57981,7 @@
method public java.lang.Object clone();
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
+ method public boolean replace(K, V, V);
}
public class HashSet extends java.util.AbstractSet implements java.lang.Cloneable java.io.Serializable java.util.Set {
@@ -57987,6 +58002,9 @@
ctor public Hashtable(java.util.Map<? extends K, ? extends V>);
method public synchronized void clear();
method public synchronized java.lang.Object clone();
+ method public synchronized V compute(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
+ method public synchronized V computeIfAbsent(K, java.util.function.Function<? super K, ? extends V>);
+ method public synchronized V computeIfPresent(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public synchronized boolean contains(java.lang.Object);
method public synchronized boolean containsKey(java.lang.Object);
method public boolean containsValue(java.lang.Object);
@@ -57994,13 +58012,19 @@
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public synchronized void forEach(java.util.function.BiConsumer<? super K, ? super V>);
method public synchronized V get(java.lang.Object);
+ method public synchronized V getOrDefault(java.lang.Object, V);
method public synchronized boolean isEmpty();
method public java.util.Set<K> keySet();
method public synchronized java.util.Enumeration<K> keys();
+ method public synchronized V merge(K, V, java.util.function.BiFunction<? super V, ? super V, ? extends V>);
method public synchronized V put(K, V);
method public synchronized void putAll(java.util.Map<? extends K, ? extends V>);
+ method public synchronized V putIfAbsent(K, V);
method protected void rehash();
method public synchronized V remove(java.lang.Object);
+ method public synchronized boolean remove(java.lang.Object, java.lang.Object);
+ method public synchronized boolean replace(K, V, V);
+ method public synchronized V replace(K, V);
method public synchronized int size();
method public java.util.Collection<V> values();
}
@@ -58145,9 +58169,11 @@
method public abstract boolean remove(java.lang.Object);
method public abstract E remove(int);
method public abstract boolean removeAll(java.util.Collection<?>);
+ method public default void replaceAll(java.util.function.UnaryOperator<E>);
method public abstract boolean retainAll(java.util.Collection<?>);
method public abstract E set(int, E);
method public abstract int size();
+ method public default void sort(java.util.Comparator<? super E>);
method public abstract java.util.List<E> subList(int, int);
method public abstract java.lang.Object[] toArray();
method public abstract T[] toArray(T[]);
@@ -59062,9 +59088,11 @@
method public synchronized boolean removeElement(java.lang.Object);
method public synchronized void removeElementAt(int);
method public synchronized boolean removeIf(java.util.function.Predicate<? super E>);
+ method public synchronized void replaceAll(java.util.function.UnaryOperator<E>);
method public synchronized void setElementAt(E, int);
method public synchronized void setSize(int);
method public synchronized int size();
+ method public synchronized void sort(java.util.Comparator<? super E>);
method public java.util.Spliterator<E> spliterator();
method public synchronized void trimToSize();
field protected int capacityIncrement;
@@ -59530,6 +59558,7 @@
method public java.lang.Object clone();
method public boolean contains(java.lang.Object);
method public boolean containsAll(java.util.Collection<?>);
+ method public void forEach(java.util.function.Consumer<? super E>);
method public E get(int);
method public int indexOf(E, int);
method public int indexOf(java.lang.Object);
@@ -66433,3 +66462,4 @@
}
}
+
diff --git a/api/system-current.txt b/api/system-current.txt
index 99a4ad3..0fe632d 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -511,9 +511,11 @@
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
@@ -671,6 +673,7 @@
field public static final int fontFamily = 16843692; // 0x10103ac
field public static final int fontFeatureSettings = 16843959; // 0x10104b7
field public static final int footerDividersEnabled = 16843311; // 0x101022f
+ field public static final int forceHasOverlappingRendering = 16844068; // 0x1010524
field public static final int foreground = 16843017; // 0x1010109
field public static final int foregroundGravity = 16843264; // 0x1010200
field public static final int foregroundTint = 16843885; // 0x101046d
@@ -45071,6 +45074,7 @@
method public void findViewsWithText(java.util.ArrayList<android.view.View>, java.lang.CharSequence, int);
method protected deprecated boolean fitSystemWindows(android.graphics.Rect);
method public android.view.View focusSearch(int);
+ method public void forceHasOverlappingRendering(boolean);
method public void forceLayout();
method public static int generateViewId();
method public java.lang.CharSequence getAccessibilityClassName();
@@ -45116,6 +45120,7 @@
method public boolean getGlobalVisibleRect(android.graphics.Rect, android.graphics.Point);
method public final boolean getGlobalVisibleRect(android.graphics.Rect);
method public android.os.Handler getHandler();
+ method public final boolean getHasOverlappingRendering();
method public final int getHeight();
method public void getHitRect(android.graphics.Rect);
method public int getHorizontalFadingEdgeLength();
@@ -48672,7 +48677,6 @@
public final class WebViewFactory {
ctor public WebViewFactory();
method public static android.content.pm.PackageInfo getLoadedPackageInfo();
- method public static java.lang.String getWebViewPackageName();
method public static int loadWebViewNativeLibraryFromPackage(java.lang.String, java.lang.ClassLoader);
method public static void prepareWebViewInZygote();
field public static final java.lang.String CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY = "persist.sys.webview.vmsize";
@@ -51352,9 +51356,15 @@
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();
@@ -51373,6 +51383,8 @@
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);
@@ -60395,6 +60407,7 @@
method public void forEach(java.util.function.Consumer<? super E>);
method public E get(int);
method public boolean removeIf(java.util.function.Predicate<? super E>);
+ method public void replaceAll(java.util.function.UnaryOperator<E>);
method public int size();
method public void sort(java.util.Comparator<? super E>);
method public java.util.Spliterator<E> spliterator();
@@ -61032,6 +61045,7 @@
method public java.lang.Object clone();
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
+ method public boolean replace(K, V, V);
}
public class HashSet extends java.util.AbstractSet implements java.lang.Cloneable java.io.Serializable java.util.Set {
@@ -61052,6 +61066,9 @@
ctor public Hashtable(java.util.Map<? extends K, ? extends V>);
method public synchronized void clear();
method public synchronized java.lang.Object clone();
+ method public synchronized V compute(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
+ method public synchronized V computeIfAbsent(K, java.util.function.Function<? super K, ? extends V>);
+ method public synchronized V computeIfPresent(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public synchronized boolean contains(java.lang.Object);
method public synchronized boolean containsKey(java.lang.Object);
method public boolean containsValue(java.lang.Object);
@@ -61059,13 +61076,19 @@
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public synchronized void forEach(java.util.function.BiConsumer<? super K, ? super V>);
method public synchronized V get(java.lang.Object);
+ method public synchronized V getOrDefault(java.lang.Object, V);
method public synchronized boolean isEmpty();
method public java.util.Set<K> keySet();
method public synchronized java.util.Enumeration<K> keys();
+ method public synchronized V merge(K, V, java.util.function.BiFunction<? super V, ? super V, ? extends V>);
method public synchronized V put(K, V);
method public synchronized void putAll(java.util.Map<? extends K, ? extends V>);
+ method public synchronized V putIfAbsent(K, V);
method protected void rehash();
method public synchronized V remove(java.lang.Object);
+ method public synchronized boolean remove(java.lang.Object, java.lang.Object);
+ method public synchronized boolean replace(K, V, V);
+ method public synchronized V replace(K, V);
method public synchronized int size();
method public java.util.Collection<V> values();
}
@@ -61210,9 +61233,11 @@
method public abstract boolean remove(java.lang.Object);
method public abstract E remove(int);
method public abstract boolean removeAll(java.util.Collection<?>);
+ method public default void replaceAll(java.util.function.UnaryOperator<E>);
method public abstract boolean retainAll(java.util.Collection<?>);
method public abstract E set(int, E);
method public abstract int size();
+ method public default void sort(java.util.Comparator<? super E>);
method public abstract java.util.List<E> subList(int, int);
method public abstract java.lang.Object[] toArray();
method public abstract T[] toArray(T[]);
@@ -62127,9 +62152,11 @@
method public synchronized boolean removeElement(java.lang.Object);
method public synchronized void removeElementAt(int);
method public synchronized boolean removeIf(java.util.function.Predicate<? super E>);
+ method public synchronized void replaceAll(java.util.function.UnaryOperator<E>);
method public synchronized void setElementAt(E, int);
method public synchronized void setSize(int);
method public synchronized int size();
+ method public synchronized void sort(java.util.Comparator<? super E>);
method public java.util.Spliterator<E> spliterator();
method public synchronized void trimToSize();
field protected int capacityIncrement;
@@ -62595,6 +62622,7 @@
method public java.lang.Object clone();
method public boolean contains(java.lang.Object);
method public boolean containsAll(java.util.Collection<?>);
+ method public void forEach(java.util.function.Consumer<? super E>);
method public E get(int);
method public int indexOf(E, int);
method public int indexOf(java.lang.Object);
@@ -69498,3 +69526,4 @@
}
}
+
diff --git a/api/test-current.txt b/api/test-current.txt
index 6cf047a..d59fa27 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -416,9 +416,11 @@
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
@@ -576,6 +578,7 @@
field public static final int fontFamily = 16843692; // 0x10103ac
field public static final int fontFeatureSettings = 16843959; // 0x10104b7
field public static final int footerDividersEnabled = 16843311; // 0x101022f
+ field public static final int forceHasOverlappingRendering = 16844068; // 0x1010524
field public static final int foreground = 16843017; // 0x1010109
field public static final int foregroundGravity = 16843264; // 0x1010200
field public static final int foregroundTint = 16843885; // 0x101046d
@@ -42419,6 +42422,7 @@
method public void findViewsWithText(java.util.ArrayList<android.view.View>, java.lang.CharSequence, int);
method protected deprecated boolean fitSystemWindows(android.graphics.Rect);
method public android.view.View focusSearch(int);
+ method public void forceHasOverlappingRendering(boolean);
method public void forceLayout();
method public static int generateViewId();
method public java.lang.CharSequence getAccessibilityClassName();
@@ -42464,6 +42468,7 @@
method public boolean getGlobalVisibleRect(android.graphics.Rect, android.graphics.Point);
method public final boolean getGlobalVisibleRect(android.graphics.Rect);
method public android.os.Handler getHandler();
+ method public final boolean getHasOverlappingRendering();
method public final int getHeight();
method public void getHitRect(android.graphics.Rect);
method public int getHorizontalFadingEdgeLength();
@@ -48361,9 +48366,15 @@
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();
@@ -48382,6 +48393,8 @@
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);
@@ -57404,6 +57417,7 @@
method public void forEach(java.util.function.Consumer<? super E>);
method public E get(int);
method public boolean removeIf(java.util.function.Predicate<? super E>);
+ method public void replaceAll(java.util.function.UnaryOperator<E>);
method public int size();
method public void sort(java.util.Comparator<? super E>);
method public java.util.Spliterator<E> spliterator();
@@ -58041,6 +58055,7 @@
method public java.lang.Object clone();
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public void forEach(java.util.function.BiConsumer<? super K, ? super V>);
+ method public boolean replace(K, V, V);
}
public class HashSet extends java.util.AbstractSet implements java.lang.Cloneable java.io.Serializable java.util.Set {
@@ -58061,6 +58076,9 @@
ctor public Hashtable(java.util.Map<? extends K, ? extends V>);
method public synchronized void clear();
method public synchronized java.lang.Object clone();
+ method public synchronized V compute(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
+ method public synchronized V computeIfAbsent(K, java.util.function.Function<? super K, ? extends V>);
+ method public synchronized V computeIfPresent(K, java.util.function.BiFunction<? super K, ? super V, ? extends V>);
method public synchronized boolean contains(java.lang.Object);
method public synchronized boolean containsKey(java.lang.Object);
method public boolean containsValue(java.lang.Object);
@@ -58068,13 +58086,19 @@
method public java.util.Set<java.util.Map.Entry<K, V>> entrySet();
method public synchronized void forEach(java.util.function.BiConsumer<? super K, ? super V>);
method public synchronized V get(java.lang.Object);
+ method public synchronized V getOrDefault(java.lang.Object, V);
method public synchronized boolean isEmpty();
method public java.util.Set<K> keySet();
method public synchronized java.util.Enumeration<K> keys();
+ method public synchronized V merge(K, V, java.util.function.BiFunction<? super V, ? super V, ? extends V>);
method public synchronized V put(K, V);
method public synchronized void putAll(java.util.Map<? extends K, ? extends V>);
+ method public synchronized V putIfAbsent(K, V);
method protected void rehash();
method public synchronized V remove(java.lang.Object);
+ method public synchronized boolean remove(java.lang.Object, java.lang.Object);
+ method public synchronized boolean replace(K, V, V);
+ method public synchronized V replace(K, V);
method public synchronized int size();
method public java.util.Collection<V> values();
}
@@ -58219,9 +58243,11 @@
method public abstract boolean remove(java.lang.Object);
method public abstract E remove(int);
method public abstract boolean removeAll(java.util.Collection<?>);
+ method public default void replaceAll(java.util.function.UnaryOperator<E>);
method public abstract boolean retainAll(java.util.Collection<?>);
method public abstract E set(int, E);
method public abstract int size();
+ method public default void sort(java.util.Comparator<? super E>);
method public abstract java.util.List<E> subList(int, int);
method public abstract java.lang.Object[] toArray();
method public abstract T[] toArray(T[]);
@@ -59136,9 +59162,11 @@
method public synchronized boolean removeElement(java.lang.Object);
method public synchronized void removeElementAt(int);
method public synchronized boolean removeIf(java.util.function.Predicate<? super E>);
+ method public synchronized void replaceAll(java.util.function.UnaryOperator<E>);
method public synchronized void setElementAt(E, int);
method public synchronized void setSize(int);
method public synchronized int size();
+ method public synchronized void sort(java.util.Comparator<? super E>);
method public java.util.Spliterator<E> spliterator();
method public synchronized void trimToSize();
field protected int capacityIncrement;
@@ -59604,6 +59632,7 @@
method public java.lang.Object clone();
method public boolean contains(java.lang.Object);
method public boolean containsAll(java.util.Collection<?>);
+ method public void forEach(java.util.function.Consumer<? super E>);
method public E get(int);
method public int indexOf(E, int);
method public int indexOf(java.lang.Object);
@@ -66507,3 +66536,4 @@
}
}
+
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 887932a..0d387e6 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -5920,6 +5920,9 @@
* @return true if this is the topmost, non-finishing activity in its task.
*/
private boolean isTopOfTask() {
+ if (mToken == null || mWindow == null || !mWindowAdded) {
+ return false;
+ }
try {
return ActivityManagerNative.getDefault().isTopOfTask(mToken);
} catch (RemoteException e) {
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 66e0ada..10259be 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -1581,14 +1581,6 @@
= "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/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index e3fb161..a0238fb 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -316,6 +316,12 @@
int getApplicationEnabledSetting(in String packageName, int userId);
/**
+ * Logs process start information (including APK hash) to the security log.
+ */
+ void logAppProcessStartIfNeeded(String processName, int uid, String seinfo, String apkFile,
+ int pid);
+
+ /**
* As per {@link android.content.pm.PackageManager#flushPackageRestrictionsAsUser}.
*/
void flushPackageRestrictionsAsUser(in int userId);
diff --git a/core/java/android/content/pm/IShortcutService.aidl b/core/java/android/content/pm/IShortcutService.aidl
index 8f9dcfc..31d377b 100644
--- a/core/java/android/content/pm/IShortcutService.aidl
+++ b/core/java/android/content/pm/IShortcutService.aidl
@@ -47,4 +47,8 @@
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/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java
index 4255582..d57f2e6e 100644
--- a/core/java/android/content/pm/ShortcutServiceInternal.java
+++ b/core/java/android/content/pm/ShortcutServiceInternal.java
@@ -40,28 +40,37 @@
}
public abstract List<ShortcutInfo>
- getShortcuts(@NonNull String callingPackage, long changedSince,
+ getShortcuts(int launcherUserId,
+ @NonNull String callingPackage, long changedSince,
@Nullable String packageName, @Nullable ComponentName componentName,
@ShortcutQuery.QueryFlags int flags,
int userId);
public abstract List<ShortcutInfo>
- getShortcutInfo(@NonNull String callingPackage,
+ getShortcutInfo(int launcherUserId, @NonNull String callingPackage,
@NonNull String packageName, @Nullable List<String> ids, int userId);
- public abstract void pinShortcuts(@NonNull String callingPackage, @NonNull String packageName,
+
+ public abstract boolean
+ isPinnedByCaller(int launcherUserId, @NonNull String callingPackage,
+ @NonNull String packageName, @NonNull String id, int userId);
+
+ public abstract void pinShortcuts(int launcherUserId,
+ @NonNull String callingPackage, @NonNull String packageName,
@NonNull List<String> shortcutIds, int userId);
- public abstract Intent createShortcutIntent(@NonNull String callingPackage,
+ public abstract Intent createShortcutIntent(int launcherUserId, @NonNull String callingPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId);
public abstract void addListener(@NonNull ShortcutChangeListener listener);
- public abstract int getShortcutIconResId(@NonNull String callingPackage,
+ public abstract int getShortcutIconResId(int launcherUserId, @NonNull String callingPackage,
@NonNull ShortcutInfo shortcut, int userId);
- public abstract ParcelFileDescriptor getShortcutIconFd(@NonNull String callingPackage,
+ public abstract ParcelFileDescriptor getShortcutIconFd(int launcherUserId,
+ @NonNull String callingPackage,
@NonNull ShortcutInfo shortcut, int userId);
- public abstract boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId);
+ public abstract boolean hasShortcutHostPermission(int launcherUserId,
+ @NonNull String callingPackage);
}
diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java
index 085613f..356804e 100644
--- a/core/java/android/text/Hyphenator.java
+++ b/core/java/android/text/Hyphenator.java
@@ -152,6 +152,9 @@
{"en-UM", "en-US"}, // English (United States Minor Outlying Islands)
{"en-VI", "en-US"}, // English (Virgin Islands)
+ // All English locales other than those falling back to en-US are mapped to en-GB.
+ {"en", "en-GB"},
+
// For German, we're assuming the 1996 (and later) orthography by default.
{"de", "de-1996"},
// Liechtenstein uses the Swiss hyphenation rules for the 1901 orthography.
@@ -160,6 +163,9 @@
// Norwegian is very probably Norwegian Bokmål.
{"no", "nb"},
+ // Use mn-Cyrl. According to CLDR's likelySubtags.xml, mn is most likely to be mn-Cyrl.
+ {"mn", "mn-Cyrl"}, // Mongolian
+
// Fall back to Ethiopic script for languages likely to be written in Ethiopic.
// Data is from CLDR's likelySubtags.xml.
// TODO: Convert this to a mechanism using ICU4J's ULocale#addLikelySubtags().
@@ -182,15 +188,36 @@
// TODO: replace this with a discovery-based method that looks into /system/usr/hyphen-data
String[] availableLanguages = {
+ "as",
+ "bn",
+ "cy",
+ "da",
"de-1901", "de-1996", "de-CH-1901",
- "en-US",
+ "en-GB", "en-US",
"es",
+ "et",
+ "eu",
+ "fr",
+ "ga",
+ "gu",
+ "hi",
+ "hr",
"hu",
"hy",
+ "kn",
+ "ml",
+ "mn-Cyrl",
+ "mr",
"nb",
"nn",
+ "or",
+ "pa",
"pt",
- "und-Ethi"
+ "sl",
+ "ta",
+ "te",
+ "tk",
+ "und-Ethi",
};
for (int i = 0; i < availableLanguages.length; i++) {
String languageTag = availableLanguages[i];
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 4e6735a..6811aed 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -2429,7 +2429,11 @@
* 1 PFLAG3_SCROLL_INDICATOR_START
* 1 PFLAG3_SCROLL_INDICATOR_END
* 1 PFLAG3_ASSIST_BLOCKED
- * 1111111 PFLAG3_POINTER_ICON_MASK
+ * 1 PFLAG3_POINTER_ICON_NULL
+ * 1 PFLAG3_POINTER_ICON_VALUE_START
+ * 11111111 PFLAG3_POINTER_ICON_MASK
+ * 1 PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE
+ * 1 PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED
* |-------|-------|-------|-------|
*/
@@ -2518,8 +2522,6 @@
*/
static final int PFLAG3_SCROLL_INDICATOR_END = 0x2000;
- /* End of masks for mPrivateFlags3 */
-
static final int DRAG_MASK = PFLAG2_DRAG_CAN_ACCEPT | PFLAG2_DRAG_HOVERED;
static final int SCROLL_INDICATORS_NONE = 0x0000;
@@ -2651,6 +2653,23 @@
private static final int PFLAG3_POINTER_ICON_VALUE_START = 2 << PFLAG3_POINTER_ICON_LSHIFT;
/**
+ * Whether this view has rendered elements that overlap (see {@link
+ * #hasOverlappingRendering()}, {@link #forceHasOverlappingRendering(boolean)}, and
+ * {@link #getHasOverlappingRendering()} ). The value in this bit is only valid when
+ * PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED has been set. Otherwise, the value is
+ * determined by whatever {@link #hasOverlappingRendering()} returns.
+ */
+ private static final int PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE = 0x800000;
+
+ /**
+ * Whether {@link #forceHasOverlappingRendering(boolean)} has been called. When true, value
+ * in PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE is valid.
+ */
+ private static final int PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED = 0x1000000;
+
+ /* End of masks for mPrivateFlags3 */
+
+ /**
* Always allow a user to over-scroll this view, provided it is a
* view that can scroll.
*
@@ -4516,6 +4535,12 @@
}
}
break;
+ case R.styleable.View_forceHasOverlappingRendering:
+ if (a.peekValue(attr) != null) {
+ forceHasOverlappingRendering(a.getBoolean(attr, true));
+ }
+ break;
+
}
}
@@ -12116,6 +12141,42 @@
}
/**
+ * Sets the behavior for overlapping rendering for this view (see {@link
+ * #hasOverlappingRendering()} for more details on this behavior). Calling this method
+ * is an alternative to overriding {@link #hasOverlappingRendering()} in a subclass,
+ * providing the value which is then used internally. That is, when {@link
+ * #forceHasOverlappingRendering(boolean)} is called, the value of {@link
+ * #hasOverlappingRendering()} is ignored and the value passed into this method is used
+ * instead.
+ *
+ * @param hasOverlappingRendering The value for overlapping rendering to be used internally
+ * instead of that returned by {@link #hasOverlappingRendering()}.
+ *
+ * @attr ref android.R.styleable#View_forceHasOverlappingRendering
+ */
+ public void forceHasOverlappingRendering(boolean hasOverlappingRendering) {
+ mPrivateFlags3 |= PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED;
+ if (hasOverlappingRendering) {
+ mPrivateFlags3 |= PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE;
+ } else {
+ mPrivateFlags3 &= ~PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE;
+ }
+ }
+
+ /**
+ * Returns the value for overlapping rendering that is used internally. This is either
+ * the value passed into {@link #forceHasOverlappingRendering(boolean)}, if called, or
+ * the return value of {@link #hasOverlappingRendering()}, otherwise.
+ *
+ * @return The value for overlapping rendering being used internally.
+ */
+ public final boolean getHasOverlappingRendering() {
+ return (mPrivateFlags3 & PFLAG3_HAS_OVERLAPPING_RENDERING_FORCED) != 0 ?
+ (mPrivateFlags3 & PFLAG3_OVERLAPPING_RENDERING_FORCED_VALUE) != 0 :
+ hasOverlappingRendering();
+ }
+
+ /**
* Returns whether this View has content which overlaps.
*
* <p>This function, intended to be overridden by specific View types, is an optimization when
@@ -12131,6 +12192,9 @@
* necessitates that a View return true if it uses the methods internally without passing the
* {@link Canvas#CLIP_TO_LAYER_SAVE_FLAG}.</p>
*
+ * <p><strong>Note:</strong> The return value of this method is ignored if {@link
+ * #forceHasOverlappingRendering(boolean)} has been called on this view.</p>
+ *
* @return true if the content in this view might overlap, false otherwise.
*/
@ViewDebug.ExportedProperty(category = "drawing")
@@ -16566,7 +16630,7 @@
*/
void setDisplayListProperties(RenderNode renderNode) {
if (renderNode != null) {
- renderNode.setHasOverlappingRendering(hasOverlappingRendering());
+ renderNode.setHasOverlappingRendering(getHasOverlappingRendering());
renderNode.setClipToBounds(mParent instanceof ViewGroup
&& ((ViewGroup) mParent).getClipChildren());
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index f18b7ac..c5b8849 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -1113,13 +1113,6 @@
if (mListener != null) {
mListener.onAnimationEnd(animation);
}
- if (mAnimatorOnEndMap != null) {
- Runnable r = mAnimatorOnEndMap.get(animation);
- if (r != null) {
- r.run();
- }
- mAnimatorOnEndMap.remove(animation);
- }
if (mAnimatorCleanupMap != null) {
Runnable r = mAnimatorCleanupMap.get(animation);
if (r != null) {
@@ -1127,6 +1120,13 @@
}
mAnimatorCleanupMap.remove(animation);
}
+ if (mAnimatorOnEndMap != null) {
+ Runnable r = mAnimatorOnEndMap.get(animation);
+ if (r != null) {
+ r.run();
+ }
+ mAnimatorOnEndMap.remove(animation);
+ }
mAnimatorMap.remove(animation);
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 3b4163f..5d4ee874 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -2126,7 +2126,7 @@
boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
- if (!cancelDraw) {
+ if (!cancelDraw && !newSurface) {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
@@ -2175,7 +2175,6 @@
}
}
}
-
private void handleOutOfResourcesException(Surface.OutOfResourcesException e) {
Log.e(mTag, "OutOfResourcesException initializing HW surface", e);
try {
diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java
index c22d60d..3ad730b 100644
--- a/core/java/android/view/WindowManagerInternal.java
+++ b/core/java/android/view/WindowManagerInternal.java
@@ -16,6 +16,7 @@
package android.view;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.Rect;
import android.graphics.Region;
@@ -157,6 +158,15 @@
public abstract void setMagnificationSpec(MagnificationSpec spec);
/**
+ * Obtains the magnified and available regions.
+ *
+ * @param outMagnified the currently magnified region
+ * @param outAvailable the region available for magnification
+ */
+ public abstract void getMagnificationRegions(@NonNull Region outMagnified,
+ @NonNull Region outAvailable);
+
+ /**
* Gets the magnification and translation applied to a window given its token.
* Not all windows are magnified and the window manager policy determines which
* windows are magnified. The returned result also takes into account the compat
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index bdaf291..1482111 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -2926,8 +2926,10 @@
mInputType = other.mInputType;
mLiveRegion = other.mLiveRegion;
mDrawingOrderInParent = other.mDrawingOrderInParent;
- if (other.mExtras != null && !other.mExtras.isEmpty()) {
- getExtras().putAll(other.mExtras);
+ if (other.mExtras != null) {
+ mExtras = new Bundle(other.mExtras);
+ } else {
+ mExtras = null;
}
mRangeInfo = (other.mRangeInfo != null)
? RangeInfo.obtain(other.mRangeInfo) : null;
@@ -3006,7 +3008,9 @@
mDrawingOrderInParent = parcel.readInt();
if (parcel.readInt() == 1) {
- getExtras().putAll(parcel.readBundle());
+ mExtras = parcel.readBundle();
+ } else {
+ mExtras = null;
}
if (parcel.readInt() == 1) {
@@ -3073,9 +3077,7 @@
mTextSelectionEnd = UNDEFINED_SELECTION_INDEX;
mInputType = InputType.TYPE_NULL;
mLiveRegion = View.ACCESSIBILITY_LIVE_REGION_NONE;
- if (mExtras != null) {
- mExtras.clear();
- }
+ mExtras = null;
if (mRangeInfo != null) {
mRangeInfo.recycle();
mRangeInfo = null;
diff --git a/core/java/android/webkit/WebViewFactory.java b/core/java/android/webkit/WebViewFactory.java
index f1bf890..d884f19 100644
--- a/core/java/android/webkit/WebViewFactory.java
+++ b/core/java/android/webkit/WebViewFactory.java
@@ -128,11 +128,6 @@
public MissingWebViewPackageException(Exception e) { super(e); }
}
- // TODO (gsennton) remove when committing webview xts test change
- public static String getWebViewPackageName() {
- return null;
- }
-
/**
* @hide
*/
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 18687c9..92631da 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -173,9 +173,6 @@
private int mHeight = LayoutParams.WRAP_CONTENT;
private int mLastHeight;
- private int mPopupWidth;
- private int mPopupHeight;
-
private float mElevation;
private Drawable mBackground;
@@ -1298,8 +1295,6 @@
mPopupViewInitialLayoutDirectionInherited =
(mContentView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);
- mPopupWidth = p.width;
- mPopupHeight = p.height;
}
/**
@@ -2006,7 +2001,7 @@
* @param height the new height, must be >= 0 or -1 to ignore
*/
public void update(View anchor, int width, int height) {
- update(anchor, false, 0, 0, true, width, height);
+ update(anchor, false, 0, 0, width, height);
}
/**
@@ -2026,11 +2021,11 @@
* @param height the new height, must be >= 0 or -1 to ignore
*/
public void update(View anchor, int xoff, int yoff, int width, int height) {
- update(anchor, true, xoff, yoff, true, width, height);
+ update(anchor, true, xoff, yoff, width, height);
}
private void update(View anchor, boolean updateLocation, int xoff, int yoff,
- boolean updateDimension, int width, int height) {
+ int width, int height) {
if (!isShowing() || mContentView == null) {
return;
@@ -2055,13 +2050,13 @@
final int oldX = p.x;
final int oldY = p.y;
- if (updateDimension) {
- if (width == -1) {
- width = mPopupWidth;
- }
- if (height == -1) {
- height = mPopupHeight;
- }
+ // If an explicit width/height has not specified, use the most recent
+ // explicitly specified value (either from setWidth/Height or update).
+ if (width == -1) {
+ width = mWidth;
+ }
+ if (height == -1) {
+ height = mHeight;
}
final boolean aboveAnchor = findDropDownPosition(anchor, p, mAnchorXoff, mAnchorYoff,
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 3195097..a9af654 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -17,6 +17,7 @@
package android.widget;
import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
+
import android.R;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
@@ -3115,10 +3116,15 @@
}
/**
+ * Returns the font feature settings. The format is the same as the CSS
+ * font-feature-settings attribute:
+ * <a href="http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings">
+ * http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings</a>
+ *
* @return the currently set font feature settings. Default is null.
*
* @see #setFontFeatureSettings(String)
- * @see Paint#setFontFeatureSettings
+ * @see Paint#setFontFeatureSettings(String) Paint.setFontFeatureSettings(String)
*/
@Nullable
public String getFontFeatureSettings() {
@@ -3182,13 +3188,15 @@
}
/**
- * Sets font feature settings. The format is the same as the CSS
+ * Sets font feature settings. The format is the same as the CSS
* font-feature-settings attribute:
- * http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings
+ * <a href="http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings">
+ * http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings</a>
*
* @param fontFeatureSettings font feature settings represented as CSS compatible string
+ *
* @see #getFontFeatureSettings()
- * @see Paint#getFontFeatureSettings
+ * @see Paint#getFontFeatureSettings() Paint.getFontFeatureSettings()
*
* @attr ref android.R.styleable#TextView_fontFeatureSettings
*/
diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java
index 06daf61..5b0a90a 100644
--- a/core/java/android/widget/Toolbar.java
+++ b/core/java/android/widget/Toolbar.java
@@ -106,6 +106,8 @@
* @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 @@
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 @@
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 @@
}
/**
- * 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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
* @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 @@
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 @@
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 @@
}
}
- 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/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 5726de1..0e02ed6 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -393,12 +393,7 @@
final DisplayResolveInfo dri = mAdapter.getOtherProfile();
if (dri != null) {
mProfileView.setVisibility(View.VISIBLE);
- final ImageView icon = (ImageView) mProfileView.findViewById(R.id.icon);
- final TextView text = (TextView) mProfileView.findViewById(R.id.text1);
- if (!dri.hasDisplayIcon()) {
- new LoadIconIntoViewTask(dri, icon).execute();
- }
- icon.setImageDrawable(dri.getDisplayIcon());
+ final TextView text = (TextView) mProfileView.findViewById(R.id.profile_button);
text.setText(dri.getDisplayLabel());
} else {
mProfileView.setVisibility(View.GONE);
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index 10027b6..5e8f4a2 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -27,7 +27,6 @@
import android.content.pm.PackageInfo;
import android.os.Environment;
import android.os.ParcelFileDescriptor;
-import android.os.SELinux;
import android.system.ErrnoException;
import android.system.Os;
import android.system.StructStat;
diff --git a/core/java/com/android/internal/view/menu/ActionMenuItemView.java b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
index bd97e5d..4738f5e 100644
--- a/core/java/com/android/internal/view/menu/ActionMenuItemView.java
+++ b/core/java/com/android/internal/view/menu/ActionMenuItemView.java
@@ -346,16 +346,6 @@
}
return false;
}
-
- @Override
- protected boolean onForwardingStopped() {
- final ShowableListMenu popup = getPopup();
- if (popup != null) {
- popup.dismiss();
- return true;
- }
- return false;
- }
}
public static abstract class PopupCallback {
diff --git a/core/java/com/android/internal/view/menu/MenuBuilder.java b/core/java/com/android/internal/view/menu/MenuBuilder.java
index 31b2f96..df57639 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 @@
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 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 @@
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 2cb224e..8ced36f 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 @@
mTreeObserver = null;
}
mShownAnchorView.removeOnAttachStateChangeListener(mAttachStateChangeListener);
- mOnDismissListener.onDismiss();
+
+ if (mOnDismissListener != null) {
+ mOnDismissListener.onDismiss();
+ }
}
@Override
@@ -265,6 +268,13 @@
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/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml
index 41726fb..d12c8ba 100644
--- a/core/res/res/layout/chooser_grid.xml
+++ b/core/res/res/layout/chooser_grid.xml
@@ -25,63 +25,48 @@
android:maxCollapsedHeightSmall="56dp"
android:id="@id/contentPanel">
- <LinearLayout
+ <RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alwaysShow="true"
android:elevation="8dp"
android:paddingStart="16dp"
android:background="@color/white" >
+ <TextView android:id="@+id/profile_button"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_marginEnd="8dp"
+ android:paddingStart="8dp"
+ android:paddingEnd="8dp"
+ android:visibility="gone"
+ style="?attr/borderlessButtonStyle"
+ android:textAppearance="?attr/textAppearanceButton"
+ android:textColor="@color/material_deep_teal_500"
+ android:gravity="center_vertical"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentRight="true"
+ android:singleLine="true"/>
<ImageView android:id="@+id/title_icon"
android:layout_width="24dp"
android:layout_height="24dp"
- android:layout_gravity="start|center_vertical"
android:layout_marginEnd="16dp"
android:visibility="gone"
- android:scaleType="fitCenter" />
+ android:scaleType="fitCenter"
+ android:layout_below="@id/profile_button"
+ android:layout_alignParentLeft="true"
+ />
<TextView android:id="@+id/title"
- android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_weight="1"
+ android:layout_width="wrap_content"
android:textAppearance="?attr/textAppearanceMedium"
android:textSize="14sp"
android:gravity="start|center_vertical"
android:paddingEnd="?attr/dialogPreferredPadding"
android:paddingTop="12dp"
- android:paddingBottom="12dp" />
- <LinearLayout android:id="@+id/profile_button"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:layout_marginTop="4dp"
- android:layout_marginEnd="4dp"
- android:paddingStart="8dp"
- android:paddingEnd="8dp"
- android:paddingTop="4dp"
- android:paddingBottom="4dp"
- android:focusable="true"
- android:visibility="gone"
- style="?attr/borderlessButtonStyle">
- <ImageView android:id="@+id/icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_gravity="start|center_vertical"
- android:layout_marginStart="4dp"
- android:layout_marginEnd="16dp"
- android:layout_marginTop="12dp"
- android:layout_marginBottom="12dp"
- android:scaleType="fitCenter" />
- <TextView android:id="@id/text1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="start|center_vertical"
- android:layout_marginEnd="16dp"
- android:textAppearance="?attr/textAppearanceButton"
- android:textColor="?attr/textColorPrimary"
- android:minLines="1"
- android:maxLines="1"
- android:ellipsize="marquee" />
- </LinearLayout>
- </LinearLayout>
+ android:paddingBottom="12dp"
+ android:layout_below="@id/profile_button"
+ android:layout_toRightOf="@id/title_icon"/>
+ </RelativeLayout>
<ListView
android:layout_width="match_parent"
diff --git a/core/res/res/layout/resolver_list.xml b/core/res/res/layout/resolver_list.xml
index 00c25e6..4b8640c 100644
--- a/core/res/res/layout/resolver_list.xml
+++ b/core/res/res/layout/resolver_list.xml
@@ -25,56 +25,39 @@
android:maxCollapsedHeightSmall="56dp"
android:id="@id/contentPanel">
- <LinearLayout
+ <RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alwaysShow="true"
android:elevation="8dp"
android:background="@color/white" >
+ <TextView android:id="@+id/profile_button"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_marginEnd="8dp"
+ android:paddingStart="8dp"
+ android:paddingEnd="8dp"
+ android:visibility="gone"
+ style="?attr/borderlessButtonStyle"
+ android:textAppearance="?attr/textAppearanceButton"
+ android:textColor="@color/material_deep_teal_500"
+ android:gravity="center_vertical"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentRight="true"
+ android:singleLine="true"/>
<TextView android:id="@+id/title"
- android:layout_width="0dp"
+ android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_weight="1"
android:minHeight="56dp"
android:textAppearance="?attr/textAppearanceMedium"
android:gravity="start|center_vertical"
android:paddingStart="?attr/dialogPreferredPadding"
android:paddingEnd="?attr/dialogPreferredPadding"
android:paddingTop="8dp"
+ android:layout_below="@id/profile_button"
+ android:layout_alignParentLeft="true"
android:paddingBottom="8dp" />
- <LinearLayout android:id="@+id/profile_button"
- android:layout_width="wrap_content"
- android:layout_height="48dp"
- android:layout_marginTop="4dp"
- android:layout_marginEnd="4dp"
- android:paddingStart="8dp"
- android:paddingEnd="8dp"
- android:paddingTop="4dp"
- android:paddingBottom="4dp"
- android:focusable="true"
- android:visibility="gone"
- style="?attr/borderlessButtonStyle">
- <ImageView android:id="@+id/icon"
- android:layout_width="24dp"
- android:layout_height="24dp"
- android:layout_gravity="start|center_vertical"
- android:layout_marginStart="4dp"
- android:layout_marginEnd="16dp"
- android:layout_marginTop="12dp"
- android:layout_marginBottom="12dp"
- android:scaleType="fitCenter" />
- <TextView android:id="@id/text1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="start|center_vertical"
- android:layout_marginEnd="16dp"
- android:textAppearance="?attr/textAppearanceButton"
- android:textColor="?attr/textColorPrimary"
- android:minLines="1"
- android:maxLines="1"
- android:ellipsize="marquee" />
- </LinearLayout>
- </LinearLayout>
+ </RelativeLayout>
<ListView
android:layout_width="match_parent"
diff --git a/core/res/res/values-sw600dp/dimens_material.xml b/core/res/res/values-sw600dp/dimens_material.xml
index 3bbb352..1ec5c0f 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 a52c4e5..d7381ab 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2846,6 +2846,11 @@
<!-- Pointer icon of a hand sign while grabbing something. -->
<enum name="grabbing" value="1021" />
</attr>
+
+ <!-- Whether this view has elements that may overlap when drawn. See
+ {@link android.view.View#forceHasOverlappingRendering(boolean)}. -->
+ <attr name="forceHasOverlappingRendering" format="boolean" />
+
</declare-styleable>
<!-- Attributes that can be assigned to a tag for a particular View. -->
@@ -7639,6 +7644,12 @@
<!-- 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
@@ -8006,6 +8017,8 @@
<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 6ecaa1f..892b3d5 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 2fe4f66..ad2b335d 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 7fd5456..0839187 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -2710,6 +2710,9 @@
<public type="attr" name="popupEnterTransition" />
<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/strings.xml b/core/res/res/values/strings.xml
index e94a91d..6041f637c 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -563,11 +563,11 @@
<!-- Label for the Android system components when they are shown to the user. -->
<string name="android_system_label">Android System</string>
- <!-- Label for the user owner in the intent forwarding app. [CHAR LIMIT=15] -->
- <string name="user_owner_label">Personal</string>
+ <!-- Label for the user owner in the intent forwarding app. -->
+ <string name="user_owner_label">Switch to Personal</string>
- <!-- Label for a corporate profile in the intent forwarding app. [CHAR LIMIT=15] -->
- <string name="managed_profile_label">Work</string>
+ <!-- Label for a corporate profile in the intent forwarding app. -->
+ <string name="managed_profile_label">Switch to Work</string>
<!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgrouplab_contacts">Contacts</string>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 86b9f1d..790dcfa 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1234,6 +1234,7 @@
<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 e636bc0..2420c1a 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -311,7 +311,7 @@
<style name="TextAppearance.Material.Widget.PopupMenu.Header">
<item name="fontFamily">@string/font_family_title_material</item>
<item name="textSize">@dimen/text_size_menu_header_material</item>
- <item name="textColor">?attr/textColorSecondary</item>
+ <item name="textColor">?attr/colorAccent</item>
</style>
<style name="TextAppearance.Material.Widget.DropDownHint" parent="TextAppearance.Material.Menu" />
@@ -944,6 +944,7 @@
<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 6526571..694e934 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/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
index a37abf1..59ffd56 100644
--- a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
+++ b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java
@@ -20,6 +20,7 @@
import android.content.res.TypedArray;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
import android.text.Selection;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
@@ -42,6 +43,7 @@
}
@SmallTest
+ @Suppress
public void testTextAppearanceInSuggestionsPopup() {
final Activity activity = getActivity();
diff --git a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
index 3fbc16a..923b829 100644
--- a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
+++ b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java
@@ -44,7 +44,6 @@
import android.support.test.espresso.Espresso;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.SmallTest;
-import android.test.suitebuilder.annotation.Suppress;
import android.view.MotionEvent;
import android.widget.espresso.ContextMenuUtils;
@@ -98,7 +97,6 @@
}
@SmallTest
- @Suppress
public void testContextMenu() throws Exception {
final String text = "abc def ghi.";
onView(withId(R.id.textview)).perform(click());
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 291fdc4..0dae796 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1494,9 +1494,14 @@
}
/**
- * Get font feature settings. Default is null.
+ * Returns the font feature settings. The format is the same as the CSS
+ * font-feature-settings attribute:
+ * <a href="http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings">
+ * http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings</a>
*
- * @return the paint's currently set font feature settings.
+ * @return the paint's currently set font feature settings. Default is null.
+ *
+ * @see #setFontFeatureSettings(String)
*/
public String getFontFeatureSettings() {
return mFontFeatureSettings;
@@ -1506,7 +1511,10 @@
* Set font feature settings.
*
* The format is the same as the CSS font-feature-settings attribute:
- * http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings
+ * <a href="http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings">
+ * http://dev.w3.org/csswg/css-fonts/#propdef-font-feature-settings</a>
+ *
+ * @see #getFontFeatureSettings()
*
* @param settings the font feature settings string to use, may be null.
*/
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index 01f9cde..1f17851 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -322,6 +322,7 @@
$(hwui_test_common_src_files) \
tests/microbench/main.cpp \
tests/microbench/DisplayListCanvasBench.cpp \
+ tests/microbench/FontBench.cpp \
tests/microbench/LinearAllocatorBench.cpp \
tests/microbench/PathParserBench.cpp \
tests/microbench/ShadowBench.cpp \
diff --git a/libs/hwui/BakedOpState.cpp b/libs/hwui/BakedOpState.cpp
index 85903654..b70d586 100644
--- a/libs/hwui/BakedOpState.cpp
+++ b/libs/hwui/BakedOpState.cpp
@@ -108,5 +108,63 @@
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 4e3cb8a..e1441fc 100644
--- a/libs/hwui/BakedOpState.h
+++ b/libs/hwui/BakedOpState.h
@@ -93,6 +93,7 @@
Rect clippedBounds;
int clipSideFlags = 0;
const SkPath* localProjectionPathMask = nullptr;
+ bool opaqueOverClippedBounds = false;
};
/**
@@ -103,23 +104,10 @@
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 @@
};
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 b1314fe..b18836f 100644
--- a/libs/hwui/FrameBuilder.cpp
+++ b/libs/hwui/FrameBuilder.cpp
@@ -481,12 +481,17 @@
* 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 @@
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 0b7a606..02c05cb 100644
--- a/libs/hwui/FrameBuilder.h
+++ b/libs/hwui/FrameBuilder.h
@@ -201,7 +201,7 @@
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/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index 2246cf9c..76e587e 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -244,7 +244,7 @@
int64_t totalDuration =
frame[FrameInfoIndex::FrameCompleted] - frame[FrameInfoIndex::IntendedVsync];
uint32_t framebucket = frameCountIndexForFrameTime(
- totalDuration, mData->frameCounts.size());
+ totalDuration, mData->frameCounts.size() - 1);
// Keep the fast path as fast as possible.
if (CC_LIKELY(totalDuration < mFrameInterval)) {
mData->frameCounts[framebucket]++;
diff --git a/libs/hwui/LayerBuilder.cpp b/libs/hwui/LayerBuilder.cpp
index e6a95ff..eea11bf 100644
--- a/libs/hwui/LayerBuilder.cpp
+++ b/libs/hwui/LayerBuilder.cpp
@@ -236,6 +236,21 @@
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::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::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::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 4a7ca2d..4de432c 100644
--- a/libs/hwui/LayerBuilder.h
+++ b/libs/hwui/LayerBuilder.h
@@ -100,9 +100,7 @@
return mBatches.empty();
}
- void clear() {
- mBatches.clear();
- }
+ void clear();
void dump() const;
@@ -117,6 +115,7 @@
// 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/font/Font.cpp b/libs/hwui/font/Font.cpp
index 9a825fd..8e04c87 100644
--- a/libs/hwui/font/Font.cpp
+++ b/libs/hwui/font/Font.cpp
@@ -356,8 +356,6 @@
}
void Font::precache(const SkPaint* paint, const glyph_t* glyphs, int numGlyphs) {
- ATRACE_NAME("Precache Glyphs");
-
if (numGlyphs == 0 || glyphs == nullptr) {
return;
}
diff --git a/libs/hwui/tests/microbench/FontBench.cpp b/libs/hwui/tests/microbench/FontBench.cpp
new file mode 100644
index 0000000..df3d041
--- /dev/null
+++ b/libs/hwui/tests/microbench/FontBench.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <benchmark/benchmark.h>
+
+#include "GammaFontRenderer.h"
+#include "tests/common/TestUtils.h"
+
+#include <SkPaint.h>
+
+using namespace android;
+using namespace android::uirenderer;
+
+void BM_FontRenderer_precache_cachehits(benchmark::State& state) {
+ TestUtils::runOnRenderThread([&state](renderthread::RenderThread& thread) {
+ SkPaint paint;
+ paint.setTextSize(20);
+ paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ GammaFontRenderer gammaFontRenderer;
+ FontRenderer& fontRenderer = gammaFontRenderer.getFontRenderer();
+ fontRenderer.setFont(&paint, SkMatrix::I());
+
+ std::vector<glyph_t> glyphs;
+ std::vector<float> positions;
+ float totalAdvance;
+ uirenderer::Rect bounds;
+ TestUtils::layoutTextUnscaled(paint, "This is a test",
+ &glyphs, &positions, &totalAdvance, &bounds);
+
+ fontRenderer.precache(&paint, glyphs.data(), glyphs.size(), SkMatrix::I());
+
+ while (state.KeepRunning()) {
+ fontRenderer.precache(&paint, glyphs.data(), glyphs.size(), SkMatrix::I());
+ }
+ });
+}
+BENCHMARK(BM_FontRenderer_precache_cachehits);
diff --git a/libs/hwui/tests/microbench/FrameBuilderBench.cpp b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
index 9daf633..0aef620 100644
--- a/libs/hwui/tests/microbench/FrameBuilderBench.cpp
+++ b/libs/hwui/tests/microbench/FrameBuilderBench.cpp
@@ -113,15 +113,17 @@
};
void BM_FrameBuilder_defer_scene(benchmark::State& state) {
- const char* sceneName = *(SCENES.begin() + state.range_x());
- state.SetLabel(sceneName);
- auto nodes = getSyncedSceneNodes(sceneName);
- while (state.KeepRunning()) {
- FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
- SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h,
- nodes, sLightGeometry, Caches::getInstance());
- benchmark::DoNotOptimize(&frameBuilder);
- }
+ TestUtils::runOnRenderThread([&state](RenderThread& thread) {
+ const char* sceneName = *(SCENES.begin() + state.range_x());
+ state.SetLabel(sceneName);
+ auto nodes = getSyncedSceneNodes(sceneName);
+ while (state.KeepRunning()) {
+ FrameBuilder frameBuilder(sEmptyLayerUpdateQueue,
+ SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h,
+ nodes, sLightGeometry, Caches::getInstance());
+ benchmark::DoNotOptimize(&frameBuilder);
+ }
+ });
}
BENCHMARK(BM_FrameBuilder_defer_scene)->DenseRange(0, SCENES.size() - 1);
diff --git a/libs/hwui/tests/unit/FrameBuilderTests.cpp b/libs/hwui/tests/unit/FrameBuilderTests.cpp
index e97aaa6..ba22f91 100644
--- a/libs/hwui/tests/unit/FrameBuilderTests.cpp
+++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp
@@ -216,6 +216,80 @@
<< "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 db53713..4faab9a 100644
--- a/libs/hwui/utils/PaintUtils.h
+++ b/libs/hwui/utils/PaintUtils.h
@@ -67,6 +67,21 @@
&& 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/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index d9caf03..b6ff41e 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -1120,7 +1120,7 @@
* <p> See also {@link AudioManager#getProperty(String)} for key
* {@link AudioManager#PROPERTY_OUTPUT_FRAMES_PER_BUFFER}.
* @return current size in frames of the <code>AudioTrack</code> buffer.
- * @throws IllegalStateException
+ * @throws IllegalStateException if track is not initialized.
*/
public int getBufferSizeInFrames() {
return native_get_buffer_size_frames();
@@ -1147,7 +1147,7 @@
* @param bufferSizeInFrames requested buffer size
* @return the actual buffer size in frames or an error code,
* {@link #ERROR_BAD_VALUE}, {@link #ERROR_INVALID_OPERATION}
- * @throws IllegalStateException
+ * @throws IllegalStateException if track is not initialized.
*/
public int setBufferSizeInFrames(int bufferSizeInFrames) {
if (mDataLoadMode == MODE_STATIC || mState == STATE_UNINITIALIZED) {
@@ -1176,7 +1176,7 @@
* <p> See also {@link AudioManager#getProperty(String)} for key
* {@link AudioManager#PROPERTY_OUTPUT_FRAMES_PER_BUFFER}.
* @return maximum size in frames of the <code>AudioTrack</code> buffer.
- * @throws IllegalStateException
+ * @throws IllegalStateException if track is not initialized.
*/
public int getBufferCapacityInFrames() {
return native_get_buffer_capacity_frames();
diff --git a/packages/DocumentsUI/perf-tests/Android.mk b/packages/DocumentsUI/perf-tests/Android.mk
index 11c163b..919fc56 100644
--- a/packages/DocumentsUI/perf-tests/Android.mk
+++ b/packages/DocumentsUI/perf-tests/Android.mk
@@ -11,7 +11,7 @@
../tests/src/com/android/documentsui/StubProvider.java
LOCAL_JAVA_LIBRARIES := android-support-v4 android.test.runner
-LOCAL_STATIC_JAVA_LIBRARIES := mockito-target ub-uiautomator
+LOCAL_STATIC_JAVA_LIBRARIES := mockito-target ub-uiautomator ub-janktesthelper
LOCAL_PACKAGE_NAME := DocumentsUIPerfTests
LOCAL_INSTRUMENTATION_FOR := DocumentsUI
diff --git a/packages/DocumentsUI/perf-tests/src/com/android/documentsui/FilesJankPerfTest.java b/packages/DocumentsUI/perf-tests/src/com/android/documentsui/FilesJankPerfTest.java
new file mode 100644
index 0000000..cb2d904
--- /dev/null
+++ b/packages/DocumentsUI/perf-tests/src/com/android/documentsui/FilesJankPerfTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.documentsui;
+
+import static com.android.documentsui.StressProvider.DEFAULT_AUTHORITY;
+import static com.android.documentsui.StressProvider.STRESS_ROOT_0_ID;
+import static com.android.documentsui.StressProvider.STRESS_ROOT_2_ID;
+
+import android.app.Activity;
+import android.os.RemoteException;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import android.content.Intent;
+import android.content.Context;
+import android.support.test.jank.JankTest;
+import android.support.test.jank.JankTestBase;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.jank.GfxMonitor;
+import android.support.test.uiautomator.UiScrollable;
+import android.util.Log;
+
+import com.android.documentsui.FilesActivity;
+import com.android.documentsui.bots.RootsListBot;
+import com.android.documentsui.bots.DirectoryListBot;
+
+@LargeTest
+public class FilesJankPerfTest extends JankTestBase {
+ private static final String DOCUMENTSUI_PACKAGE = "com.android.documentsui";
+ private static final int MAX_FLINGS = 10;
+ private static final int BOT_TIMEOUT = 5000;
+
+ private RootsListBot mRootsListBot;
+ private DirectoryListBot mDirListBot;
+ private Activity mActivity = null;
+
+ public void setUpInLoop() {
+ final UiDevice device = UiDevice.getInstance(getInstrumentation());
+ final Context context = getInstrumentation().getTargetContext();
+ mRootsListBot = new RootsListBot(device, context, BOT_TIMEOUT);
+ mDirListBot = new DirectoryListBot(device, context, BOT_TIMEOUT);
+
+ final Intent intent = new Intent(context, FilesActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mActivity = getInstrumentation().startActivitySync(intent);
+ }
+
+ public void tearDownInLoop() {
+ if (mActivity != null) {
+ mActivity.finish();
+ mActivity = null;
+ }
+ }
+
+ public void setupAndOpenInLoop() throws Exception {
+ setUpInLoop();
+ openRoot();
+ }
+
+ public void openRoot() throws Exception {
+ mRootsListBot.openRoot(STRESS_ROOT_2_ID);
+ }
+
+ @JankTest(expectedFrames=0, beforeLoop="setUpInLoop", afterLoop="tearDownInLoop")
+ @GfxMonitor(processName=DOCUMENTSUI_PACKAGE)
+ public void testOpenRootJankPerformance() throws Exception {
+ openRoot();
+ getInstrumentation().waitForIdleSync();
+ }
+
+ @JankTest(expectedFrames=0, beforeLoop="setupAndOpenInLoop", afterLoop="tearDownInLoop")
+ @GfxMonitor(processName=DOCUMENTSUI_PACKAGE)
+ public void testFlingJankPerformance() throws Exception {
+ new UiScrollable(mDirListBot.findDocumentsList().getSelector()).flingToEnd(MAX_FLINGS);
+ getInstrumentation().waitForIdleSync();
+ }
+}
diff --git a/packages/DocumentsUI/perf-tests/src/com/android/documentsui/StressProvider.java b/packages/DocumentsUI/perf-tests/src/com/android/documentsui/StressProvider.java
index 1bc802a..6147a71 100644
--- a/packages/DocumentsUI/perf-tests/src/com/android/documentsui/StressProvider.java
+++ b/packages/DocumentsUI/perf-tests/src/com/android/documentsui/StressProvider.java
@@ -32,7 +32,9 @@
import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Random;
/**
@@ -46,11 +48,21 @@
// Empty root.
public static final String STRESS_ROOT_0_ID = "STRESS_ROOT_0";
- // Root with thousands of items.
+ // Root with thousands of directories.
public static final String STRESS_ROOT_1_ID = "STRESS_ROOT_1";
+ // Root with hundreds of files.
+ public static final String STRESS_ROOT_2_ID = "STRESS_ROOT_2";
+
private static final String STRESS_ROOT_0_DOC_ID = "STRESS_ROOT_0_DOC";
private static final String STRESS_ROOT_1_DOC_ID = "STRESS_ROOT_1_DOC";
+ private static final String STRESS_ROOT_2_DOC_ID = "STRESS_ROOT_2_DOC";
+
+ private static final int STRESS_ROOT_1_ITEMS = 10000;
+ private static final int STRESS_ROOT_2_ITEMS = 300;
+
+ private static final String MIME_TYPE_IMAGE = "image/jpeg";
+ private static final long REFERENCE_TIMESTAMP = 1459159369359L;
private static final String[] DEFAULT_ROOT_PROJECTION = new String[] {
Root.COLUMN_ROOT_ID, Root.COLUMN_FLAGS, Root.COLUMN_TITLE, Root.COLUMN_DOCUMENT_ID,
@@ -62,7 +74,12 @@
};
private String mAuthority = DEFAULT_AUTHORITY;
- private ArrayList<String> mIds = new ArrayList<>();
+
+ // Map from a root document id to children document ids.
+ private Map<String, ArrayList<StubDocument>> mChildDocuments = new HashMap<>();
+
+ private Map<String, StubDocument> mDocuments = new HashMap<>();
+ private Map<String, StubRoot> mRoots = new HashMap<>();
@Override
public void attachInfo(Context context, ProviderInfo info) {
@@ -72,20 +89,41 @@
@Override
public boolean onCreate() {
- mIds = new ArrayList();
- for (int i = 0; i < 10000; i++) {
- mIds.add(createRandomId(i));
+ StubDocument document;
+
+ ArrayList<StubDocument> children = new ArrayList<StubDocument>();
+ mChildDocuments.put(STRESS_ROOT_1_DOC_ID, children);
+ for (int i = 0; i < STRESS_ROOT_1_ITEMS; i++) {
+ document = StubDocument.createDirectory(i);
+ mDocuments.put(document.id, document);
+ children.add(document);
}
- mIds.add(STRESS_ROOT_0_DOC_ID);
- mIds.add(STRESS_ROOT_1_DOC_ID);
+
+ children = new ArrayList<StubDocument>();
+ mChildDocuments.put(STRESS_ROOT_2_DOC_ID, children);
+ for (int i = 0; i < STRESS_ROOT_2_ITEMS; i++) {
+ document = StubDocument.createFile(STRESS_ROOT_1_ITEMS + i);
+ mDocuments.put(document.id, document);
+ children.add(document);
+ }
+
+ mRoots.put(STRESS_ROOT_0_ID, new StubRoot(STRESS_ROOT_0_ID, STRESS_ROOT_0_DOC_ID));
+ mRoots.put(STRESS_ROOT_1_ID, new StubRoot(STRESS_ROOT_1_ID, STRESS_ROOT_1_DOC_ID));
+ mRoots.put(STRESS_ROOT_2_ID, new StubRoot(STRESS_ROOT_2_ID, STRESS_ROOT_2_DOC_ID));
+
+ mDocuments.put(STRESS_ROOT_0_DOC_ID, StubDocument.createDirectory(STRESS_ROOT_0_DOC_ID));
+ mDocuments.put(STRESS_ROOT_1_DOC_ID, StubDocument.createDirectory(STRESS_ROOT_1_DOC_ID));
+ mDocuments.put(STRESS_ROOT_2_DOC_ID, StubDocument.createDirectory(STRESS_ROOT_2_DOC_ID));
+
return true;
}
@Override
public Cursor queryRoots(String[] projection) throws FileNotFoundException {
final MatrixCursor result = new MatrixCursor(DEFAULT_ROOT_PROJECTION);
- includeRoot(result, STRESS_ROOT_0_ID, STRESS_ROOT_0_DOC_ID);
- includeRoot(result, STRESS_ROOT_1_ID, STRESS_ROOT_1_DOC_ID);
+ for (StubRoot root : mRoots.values()) {
+ includeRoot(result, root);
+ }
return result;
}
@@ -93,57 +131,112 @@
public Cursor queryDocument(String documentId, String[] projection)
throws FileNotFoundException {
final MatrixCursor result = new MatrixCursor(DEFAULT_DOCUMENT_PROJECTION);
- includeDocument(result, documentId);
+ final StubDocument document = mDocuments.get(documentId);
+ includeDocument(result, document);
return result;
}
@Override
- public Cursor queryChildDocuments(String parentDocumentId, String[] projection, String sortOrder)
+ public Cursor queryChildDocuments(String parentDocumentId, String[] projection,
+ String sortOrder)
throws FileNotFoundException {
final MatrixCursor result = new MatrixCursor(DEFAULT_DOCUMENT_PROJECTION);
- if (STRESS_ROOT_1_DOC_ID.equals(parentDocumentId)) {
- for (String id : mIds) {
- includeDocument(result, id);
+ final ArrayList<StubDocument> childDocuments = mChildDocuments.get(parentDocumentId);
+ if (childDocuments != null) {
+ for (StubDocument document : childDocuments) {
+ includeDocument(result, document);
}
}
return result;
}
@Override
- public ParcelFileDescriptor openDocument(String docId, String mode, CancellationSignal signal)
+ public ParcelFileDescriptor openDocument(String docId, String mode,
+ CancellationSignal signal)
throws FileNotFoundException {
throw new UnsupportedOperationException();
}
- private void includeRoot(MatrixCursor result, String rootId, String docId) {
+ private void includeRoot(MatrixCursor result, StubRoot root) {
final RowBuilder row = result.newRow();
- row.add(Root.COLUMN_ROOT_ID, rootId);
+ row.add(Root.COLUMN_ROOT_ID, root.id);
row.add(Root.COLUMN_FLAGS, 0);
- row.add(Root.COLUMN_TITLE, rootId);
- row.add(Root.COLUMN_DOCUMENT_ID, docId);
+ row.add(Root.COLUMN_TITLE, root.id);
+ row.add(Root.COLUMN_DOCUMENT_ID, root.documentId);
}
- private void includeDocument(MatrixCursor result, String id) {
+ private void includeDocument(MatrixCursor result, StubDocument document) {
final RowBuilder row = result.newRow();
- row.add(Document.COLUMN_DOCUMENT_ID, id);
- row.add(Document.COLUMN_DISPLAY_NAME, id);
- row.add(Document.COLUMN_SIZE, 0);
- row.add(Document.COLUMN_MIME_TYPE, DocumentsContract.Document.MIME_TYPE_DIR);
+ row.add(Document.COLUMN_DOCUMENT_ID, document.id);
+ row.add(Document.COLUMN_DISPLAY_NAME, document.id);
+ row.add(Document.COLUMN_SIZE, document.size);
+ row.add(Document.COLUMN_MIME_TYPE, document.mimeType);
row.add(Document.COLUMN_FLAGS, 0);
- row.add(Document.COLUMN_LAST_MODIFIED, null);
+ row.add(Document.COLUMN_LAST_MODIFIED, document.lastModified);
}
- private static String getDocumentIdForFile(File file) {
+ private static String getStubDocumentIdForFile(File file) {
return file.getAbsolutePath();
}
- private String createRandomId(int index) {
- final Random random = new Random(index);
- final StringBuilder builder = new StringBuilder();
- for (int i = 0; i < 20; i++) {
- builder.append((char) (random.nextInt(96) + 32));
+ private static class StubDocument {
+ final String mimeType;
+ final String id;
+ final int size;
+ final long lastModified;
+
+ private StubDocument(String mimeType, String id, int size, long lastModified) {
+ this.mimeType = mimeType;
+ this.id = id;
+ this.size = size;
+ this.lastModified = lastModified;
}
- builder.append(index); // Append a number to guarantee uniqueness.
- return builder.toString();
+
+ public static StubDocument createDirectory(int index) {
+ return new StubDocument(
+ DocumentsContract.Document.MIME_TYPE_DIR, createRandomId(index), 0,
+ createRandomTime(index));
+ }
+
+ public static StubDocument createDirectory(String id) {
+ return new StubDocument(DocumentsContract.Document.MIME_TYPE_DIR, id, 0, 0);
+ }
+
+ public static StubDocument createFile(int index) {
+ return new StubDocument(
+ MIME_TYPE_IMAGE, createRandomId(index), createRandomSize(index),
+ createRandomTime(index));
+ }
+
+ private static String createRandomId(int index) {
+ final Random random = new Random(index);
+ final StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < 20; i++) {
+ builder.append((char) (random.nextInt(96) + 32));
+ }
+ builder.append(index); // Append a number to guarantee uniqueness.
+ return builder.toString();
+ }
+
+ private static int createRandomSize(int index) {
+ final Random random = new Random(index);
+ return random.nextInt(1024 * 1024 * 100); // Up to 100 MB.
+ }
+
+ private static long createRandomTime(int index) {
+ final Random random = new Random(index);
+ // Up to 30 days backwards from REFERENCE_TIMESTAMP.
+ return REFERENCE_TIMESTAMP - random.nextLong() % 1000L * 60 * 60 * 24 * 30;
+ }
+ }
+
+ private static class StubRoot {
+ final String id;
+ final String documentId;
+
+ public StubRoot(String id, String documentId) {
+ this.id = id;
+ this.documentId = documentId;
+ }
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Metrics.java b/packages/DocumentsUI/src/com/android/documentsui/Metrics.java
index e6b22e6..7ad4a09 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Metrics.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Metrics.java
@@ -30,6 +30,7 @@
import android.provider.DocumentsContract;
import android.util.Log;
+import com.android.documentsui.State.ActionType;
import com.android.documentsui.model.DocumentInfo;
import com.android.documentsui.model.RootInfo;
import com.android.documentsui.services.FileOperationService;
@@ -502,7 +503,7 @@
* @param name The name of the histogram.
* @param bucket The bucket to increment.
*/
- private static void logHistogram(Context context, String name, int bucket) {
+ private static void logHistogram(Context context, String name, @ActionType int bucket) {
if (DEBUG) Log.d(TAG, name + ": " + bucket);
MetricsLogger.histogram(context, name, bucket);
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
index 35da8cc..8b4f40e 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/RootsFragment.java
@@ -17,6 +17,7 @@
package com.android.documentsui;
import static com.android.documentsui.Shared.DEBUG;
+import static com.android.documentsui.State.ACTION_OPEN_TREE;
import android.app.Fragment;
import android.app.FragmentManager;
@@ -117,7 +118,7 @@
Intent handlerAppIntent = getArguments().getParcelable(EXTRA_INCLUDE_APPS);
- mAdapter = new RootsAdapter(context, result, handlerAppIntent);
+ mAdapter = new RootsAdapter(context, result, handlerAppIntent, state);
mList.setAdapter(mAdapter);
onCurrentRootChanged();
@@ -308,8 +309,8 @@
* @param handlerAppIntent When not null, apps capable of handling the original
* intent will be included in list of roots (in special section at bottom).
*/
- public RootsAdapter(
- Context context, Collection<RootInfo> roots, @Nullable Intent handlerAppIntent) {
+ public RootsAdapter(Context context, Collection<RootInfo> roots,
+ @Nullable Intent handlerAppIntent, State state) {
super(context, 0);
final List<RootItem> libraries = new ArrayList<>();
@@ -320,7 +321,8 @@
if (root.isHome() && Shared.isHomeRootHidden(context)) {
continue;
- } else if (root.isAdvanced() && Shared.areAdvancedRootsHidden(context)) {
+ } else if (root.isAdvanced()
+ && Shared.areAdvancedRootsHidden(context, state)) {
continue;
} else if (root.isLibrary()) {
if (DEBUG) Log.d(TAG, "Adding " + root + " as library.");
diff --git a/packages/DocumentsUI/src/com/android/documentsui/Shared.java b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
index d21afee..655359a 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/Shared.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/Shared.java
@@ -22,6 +22,11 @@
import android.text.format.DateUtils;
import android.text.format.Time;
import android.view.WindowManager;
+
+import com.android.documentsui.State.ActionType;
+
+import static com.android.documentsui.State.ACTION_OPEN_TREE;
+
import android.app.AlertDialog;
import java.text.Collator;
@@ -179,8 +184,9 @@
/*
* Indicates if the advanced roots should be hidden.
*/
- public static boolean areAdvancedRootsHidden(Context context) {
- return context.getResources().getBoolean(R.bool.advanced_roots_hidden);
+ public static boolean areAdvancedRootsHidden(Context context, State state) {
+ return context.getResources().getBoolean(R.bool.advanced_roots_hidden)
+ && state.action != ACTION_OPEN_TREE;
}
}
diff --git a/packages/DocumentsUI/src/com/android/documentsui/State.java b/packages/DocumentsUI/src/com/android/documentsui/State.java
index 16b7660..534a483 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/State.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/State.java
@@ -43,10 +43,19 @@
private static final String TAG = "State";
+ @IntDef(flag = true, value = {
+ ACTION_BROWSE,
+ ACTION_PICK_COPY_DESTINATION,
+ ACTION_OPEN,
+ ACTION_CREATE,
+ ACTION_GET_CONTENT,
+ ACTION_OPEN_TREE
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ActionType {}
// File manager and related private picking activity.
public static final int ACTION_BROWSE = 1;
public static final int ACTION_PICK_COPY_DESTINATION = 2;
-
// All public picking activities
public static final int ACTION_OPEN = 3;
public static final int ACTION_CREATE = 4;
@@ -69,7 +78,7 @@
public static final int SORT_ORDER_LAST_MODIFIED = 2;
public static final int SORT_ORDER_SIZE = 3;
- public int action;
+ public @ActionType int action;
public String[] acceptMimes;
/** Derived from local preferences */
diff --git a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/DirectoryListBot.java b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/DirectoryListBot.java
index da9f9c3..7c1e2196 100644
--- a/packages/DocumentsUI/tests/src/com/android/documentsui/bots/DirectoryListBot.java
+++ b/packages/DocumentsUI/tests/src/com/android/documentsui/bots/DirectoryListBot.java
@@ -167,7 +167,7 @@
return true;
}
- private UiObject findDocumentsList() {
+ public UiObject findDocumentsList() {
return findObject(
"com.android.documentsui:id/container_directory",
DIR_LIST_ID);
diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
index 64910fd..c3a5089 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java
@@ -155,8 +155,10 @@
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
updateDrawer();
} else {
- mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
- mDrawerLayout = null;
+ if (mDrawerLayout != null) {
+ mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
+ mDrawerLayout = null;
+ }
}
}
diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
index 6cb8470..381fb16 100644
--- a/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
+++ b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml
@@ -16,7 +16,7 @@
~ limitations under the License
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="match_parent"
android:textSize="14sp"
android:paddingStart="24dp"
diff --git a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
index 4845425..087f61e 100755
--- a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java
@@ -452,7 +452,7 @@
boolean pctOpaque = false;
float pctX = 0, pctY = 0;
String pctText = null;
- if (!mPluggedIn && level > mCriticalLevel && mShowPercent) {
+ if (!mPluggedIn && !mPowerSaveEnabled && level > mCriticalLevel && mShowPercent) {
mTextPaint.setColor(getColorForLevel(level));
mTextPaint.setTextSize(height *
(SINGLE_DIGIT_PERCENT ? 0.75f
@@ -480,7 +480,7 @@
mShapePath.op(mClipPath, Path.Op.INTERSECT);
c.drawPath(mShapePath, mBatteryPaint);
- if (!mPluggedIn) {
+ if (!mPluggedIn && !mPowerSaveEnabled) {
if (level <= mCriticalLevel) {
// draw the warning text
final float x = mWidth * 0.5f;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index fe7ac71..b89c2f6 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -338,7 +338,7 @@
mRecentsView.updateStack(stack);
// Update the nav bar scrim, but defer the animation until the enter-window event
- boolean animateNavBarScrim = !launchState.launchedWhileDocking;
+ boolean animateNavBarScrim = !launchState.launchedViaDockGesture;
updateNavBarScrim(animateNavBarScrim, null);
// If this is a new instance relaunched by AM, without going through the normal mechanisms,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
index 77f7739..7161053 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivityLaunchState.java
@@ -29,10 +29,9 @@
public boolean launchedWithAltTab;
public boolean launchedFromApp;
- public boolean launchedFromAppDocked;
public boolean launchedFromHome;
public boolean launchedViaDragGesture;
- public boolean launchedWhileDocking;
+ public boolean launchedViaDockGesture;
public int launchedToTaskId;
public int launchedNumVisibleTasks;
public int launchedNumVisibleThumbnails;
@@ -40,18 +39,10 @@
public void reset() {
launchedFromHome = false;
launchedFromApp = false;
- launchedFromAppDocked = false;
launchedToTaskId = -1;
launchedWithAltTab = false;
launchedViaDragGesture = false;
- launchedWhileDocking = false;
- }
-
- /** Called when the configuration has changed, and we want to reset any configuration specific
- * members. */
- public void updateOnConfigurationChange() {
- launchedViaDragGesture = false;
- launchedWhileDocking = false;
+ launchedViaDockGesture = false;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 40613f0..73c6e6e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -80,12 +80,4 @@
public RecentsActivityLaunchState getLaunchState() {
return mLaunchState;
}
-
- /**
- * Called when the configuration has changed, and we want to reset any configuration specific
- * members.
- */
- public void updateOnConfigurationChange() {
- mLaunchState.updateOnConfigurationChange();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index c52d17f..ffc037d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -43,7 +43,6 @@
import com.android.systemui.R;
import com.android.systemui.SystemUIApplication;
import com.android.systemui.recents.events.EventBus;
-import com.android.systemui.recents.events.EventBus.Event;
import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
import com.android.systemui.recents.events.activity.EnterRecentsWindowLastAnimationFrameEvent;
import com.android.systemui.recents.events.activity.ForcedResizableEvent;
@@ -208,7 +207,6 @@
public void onConfigurationChanged() {
reloadHeaderBarLayout();
updateHeaderBarLayout(null /* stack */);
- Recents.getConfiguration().updateOnConfigurationChange();
}
/**
@@ -852,13 +850,12 @@
// Update the launch state
launchState.launchedFromHome = false;
launchState.launchedFromApp = mLaunchedWhileDocking;
+ launchState.launchedViaDockGesture = mLaunchedWhileDocking;
launchState.launchedToTaskId = (topTask != null) ? topTask.id : -1;
- launchState.launchedFromAppDocked = mLaunchedWhileDocking;
launchState.launchedWithAltTab = mTriggeredFromAltTab;
launchState.launchedNumVisibleTasks = stackVr.numVisibleTasks;
launchState.launchedNumVisibleThumbnails = stackVr.numVisibleThumbnails;
launchState.launchedViaDragGesture = mDraggingInRecents;
- launchState.launchedWhileDocking = mLaunchedWhileDocking;
if (!animate) {
startRecentsActivity(ActivityOptions.makeCustomAnimation(mContext, -1, -1));
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index a1ba493..b23b01f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -184,7 +184,7 @@
// If we are already occluded by the app, then set the final background scrim alpha now.
// Otherwise, defer until the enter animation completes to animate the scrim alpha with
// the tasks for the home animation.
- if (launchState.launchedWhileDocking || launchState.launchedFromApp
+ if (launchState.launchedViaDockGesture || launchState.launchedFromApp
|| isTaskStackEmpty) {
mBackgroundScrim.setAlpha((int) (DEFAULT_SCRIM_ALPHA * 255));
} else {
@@ -564,7 +564,7 @@
public final void onBusEvent(EnterRecentsWindowAnimationCompletedEvent event) {
RecentsActivityLaunchState launchState = Recents.getConfiguration().getLaunchState();
- if (!launchState.launchedWhileDocking && !launchState.launchedFromApp
+ if (!launchState.launchedViaDockGesture && !launchState.launchedFromApp
&& mStack.getTaskCount() > 0) {
animateBackgroundScrim(DEFAULT_SCRIM_ALPHA,
TaskStackAnimationHelper.ENTER_FROM_HOME_TRANSLATION_DURATION);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
index 1c7d609..8db81f7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackAnimationHelper.java
@@ -20,7 +20,6 @@
import android.animation.AnimatorListenerAdapter;
import android.content.Context;
import android.content.res.Resources;
-import android.graphics.RectF;
import android.util.Log;
import android.view.View;
import android.view.animation.Interpolator;
@@ -152,30 +151,26 @@
if (hideTask) {
tv.setVisibility(View.INVISIBLE);
- } else if (launchState.launchedFromApp && !launchState.launchedWhileDocking) {
+ } else if (launchState.launchedFromApp && !launchState.launchedViaDockGesture) {
if (task.isLaunchTarget) {
tv.onPrepareLaunchTargetForEnterAnimation();
} else if (currentTaskOccludesLaunchTarget) {
// Move the task view slightly lower so we can animate it in
- RectF bounds = new RectF(mTmpTransform.rect);
- bounds.offset(0, taskViewAffiliateGroupEnterOffset);
+ mTmpTransform.rect.offset(0, taskViewAffiliateGroupEnterOffset);
+ mTmpTransform.alpha = 0f;
+ mStackView.updateTaskViewToTransform(tv, mTmpTransform,
+ AnimationProps.IMMEDIATE);
tv.setClipViewInStack(false);
- tv.setAlpha(0f);
- tv.setLeftTopRightBottom((int) bounds.left, (int) bounds.top,
- (int) bounds.right, (int) bounds.bottom);
}
} else if (launchState.launchedFromHome) {
// Move the task view off screen (below) so we can animate it in
- RectF bounds = new RectF(mTmpTransform.rect);
- bounds.offset(0, offscreenYOffset);
- tv.setAlpha(0f);
- tv.setLeftTopRightBottom((int) bounds.left, (int) bounds.top, (int) bounds.right,
- (int) bounds.bottom);
- } else if (launchState.launchedWhileDocking) {
- RectF bounds = new RectF(mTmpTransform.rect);
- bounds.offset(0, launchedWhileDockingOffset);
- tv.setLeftTopRightBottom((int) bounds.left, (int) bounds.top, (int) bounds.right,
- (int) bounds.bottom);
+ mTmpTransform.rect.offset(0, offscreenYOffset);
+ mTmpTransform.alpha = 0f;
+ mStackView.updateTaskViewToTransform(tv, mTmpTransform, AnimationProps.IMMEDIATE);
+ } else if (launchState.launchedViaDockGesture) {
+ mTmpTransform.rect.offset(0, launchedWhileDockingOffset);
+ mTmpTransform.alpha = 0f;
+ mStackView.updateTaskViewToTransform(tv, mTmpTransform, AnimationProps.IMMEDIATE);
}
}
}
@@ -223,7 +218,7 @@
stackLayout.getStackTransform(task, stackScroller.getStackScroll(), mTmpTransform,
null);
- if (launchState.launchedFromApp && !launchState.launchedWhileDocking) {
+ if (launchState.launchedFromApp && !launchState.launchedViaDockGesture) {
if (task.isLaunchTarget) {
tv.onStartLaunchTargetEnterAnimation(mTmpTransform,
taskViewEnterFromAppDuration, mStackView.mScreenPinningEnabled,
@@ -262,7 +257,7 @@
if (i == taskViewCount - 1) {
tv.onStartFrontTaskEnterAnimation(mStackView.mScreenPinningEnabled);
}
- } else if (launchState.launchedWhileDocking) {
+ } else if (launchState.launchedViaDockGesture) {
// Animate the tasks up
AnimationProps taskAnimation = new AnimationProps()
.setDuration(AnimationProps.BOUNDS, (int) (ENTER_WHILE_DOCKING_DURATION +
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
index 8a1727a..4b1faf3 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackLayoutAlgorithm.java
@@ -552,7 +552,7 @@
mMaxScrollP = Math.max(mMinScrollP, (mNumStackTasks - 1) -
Math.max(0, mUnfocusedRange.getAbsoluteX(maxBottomNormX)));
boolean scrollToFront = launchState.launchedFromHome ||
- launchState.launchedFromAppDocked;
+ launchState.launchedViaDockGesture;
if (scrollToFront) {
mInitialScrollP = Utilities.clamp(launchTaskIndex, mMinScrollP, mMaxScrollP);
mInitialNormX = null;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 9032871..04f153f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -28,6 +28,7 @@
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
@@ -60,8 +61,8 @@
import com.android.systemui.recents.events.activity.DismissRecentsToHomeAnimationStarted;
import com.android.systemui.recents.events.activity.EnterRecentsTaskStackAnimationCompletedEvent;
import com.android.systemui.recents.events.activity.EnterRecentsWindowAnimationCompletedEvent;
-import com.android.systemui.recents.events.activity.HideStackActionButtonEvent;
import com.android.systemui.recents.events.activity.HideRecentsEvent;
+import com.android.systemui.recents.events.activity.HideStackActionButtonEvent;
import com.android.systemui.recents.events.activity.IterateRecentsEvent;
import com.android.systemui.recents.events.activity.LaunchNextTaskRequestEvent;
import com.android.systemui.recents.events.activity.LaunchTaskEvent;
@@ -117,8 +118,6 @@
private static final int LAUNCH_NEXT_SCROLL_BASE_DURATION = 216;
private static final int LAUNCH_NEXT_SCROLL_INCR_DURATION = 32;
- private static final ArraySet<Task.TaskKey> EMPTY_TASK_SET = new ArraySet<>();
-
// The actions to perform when resetting to initial state,
@Retention(RetentionPolicy.SOURCE)
@IntDef({INITIAL_STATE_UPDATE_NONE, INITIAL_STATE_UPDATE_ALL, INITIAL_STATE_UPDATE_LAYOUT_ONLY})
@@ -598,17 +597,14 @@
if (tv == null) {
tv = mViewPool.pickUpViewFromPool(task, task);
if (task.isFreeformTask()) {
- tv.updateViewPropertiesToTaskTransform(transform, AnimationProps.IMMEDIATE,
- mRequestUpdateClippingListener);
+ updateTaskViewToTransform(tv, transform, AnimationProps.IMMEDIATE);
} else {
if (transform.rect.top <= mLayoutAlgorithm.mStackRect.top) {
- tv.updateViewPropertiesToTaskTransform(
- mLayoutAlgorithm.getBackOfStackTransform(),
- AnimationProps.IMMEDIATE, mRequestUpdateClippingListener);
+ updateTaskViewToTransform(tv, mLayoutAlgorithm.getBackOfStackTransform(),
+ AnimationProps.IMMEDIATE);
} else {
- tv.updateViewPropertiesToTaskTransform(
- mLayoutAlgorithm.getFrontOfStackTransform(),
- AnimationProps.IMMEDIATE, mRequestUpdateClippingListener);
+ updateTaskViewToTransform(tv, mLayoutAlgorithm.getFrontOfStackTransform(),
+ AnimationProps.IMMEDIATE);
}
}
} else {
@@ -1215,7 +1211,7 @@
TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
mLayoutAlgorithm.initialize(mWindowRect, mStackBounds,
TaskStackLayoutAlgorithm.StackState.getStackStateForStack(mStack));
- updateLayoutAlgorithm(false /* boundScroll */, EMPTY_TASK_SET);
+ updateLayoutAlgorithm(false /* boundScroll */, mIgnoreTasks);
// If this is the first layout, then scroll to the front of the stack, then update the
// TaskViews with the stack so that we can lay them out
@@ -1225,7 +1221,7 @@
}
// Rebind all the views, including the ignore ones
- bindVisibleTaskViews(mStackScroller.getStackScroll(), EMPTY_TASK_SET,
+ bindVisibleTaskViews(mStackScroller.getStackScroll(), mIgnoreTasks,
false /* ignoreTaskOverrides */);
// Measure each of the TaskViews
@@ -1266,7 +1262,7 @@
mTmpTaskViews.addAll(mViewPool.getViews());
int taskViewCount = mTmpTaskViews.size();
for (int i = 0; i < taskViewCount; i++) {
- layoutTaskView(mTmpTaskViews.get(i));
+ layoutTaskView(changed, mTmpTaskViews.get(i));
}
if (changed) {
@@ -1274,8 +1270,9 @@
mStackScroller.boundScroll();
}
}
+
// Relayout all of the task views including the ignored ones
- relayoutTaskViews(AnimationProps.IMMEDIATE, EMPTY_TASK_SET);
+ relayoutTaskViews(AnimationProps.IMMEDIATE, mIgnoreTasks);
clipTaskViews();
if (mAwaitingFirstLayout || !mEnterAnimationComplete) {
@@ -1287,16 +1284,21 @@
/**
* Lays out a TaskView.
*/
- private void layoutTaskView(TaskView tv) {
- if (tv.getBackground() != null) {
- tv.getBackground().getPadding(mTmpRect);
+ private void layoutTaskView(boolean changed, TaskView tv) {
+ if (changed) {
+ if (tv.getBackground() != null) {
+ tv.getBackground().getPadding(mTmpRect);
+ } else {
+ mTmpRect.setEmpty();
+ }
+ Rect taskRect = mStableLayoutAlgorithm.mTaskRect;
+ tv.cancelTransformAnimation();
+ tv.layout(taskRect.left - mTmpRect.left, taskRect.top - mTmpRect.top,
+ taskRect.right + mTmpRect.right, taskRect.bottom + mTmpRect.bottom);
} else {
- mTmpRect.setEmpty();
+ // If the layout has not changed, then just lay it out again in-place
+ tv.layout(tv.getLeft(), tv.getTop(), tv.getRight(), tv.getBottom());
}
- Rect taskRect = mStableLayoutAlgorithm.mTaskRect;
- tv.cancelTransformAnimation();
- tv.layout(taskRect.left - mTmpRect.left, taskRect.top - mTmpRect.top,
- taskRect.right + mTmpRect.right, taskRect.bottom + mTmpRect.bottom);
}
/** Handler for the first layout. */
@@ -1509,7 +1511,7 @@
}
addViewInLayout(tv, insertIndex, params, true /* preventRequestLayout */);
measureTaskView(tv);
- layoutTaskView(tv);
+ layoutTaskView(true /* changed */, tv);
}
} else {
attachViewToParent(tv, insertIndex, tv.getLayoutParams());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutKeysLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutKeysLayout.java
index ba3e774..6746a67 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutKeysLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutKeysLayout.java
@@ -29,13 +29,16 @@
*/
public final class KeyboardShortcutKeysLayout extends ViewGroup {
private int mLineHeight;
+ private final Context mContext;
public KeyboardShortcutKeysLayout(Context context) {
super(context);
+ this.mContext = context;
}
public KeyboardShortcutKeysLayout(Context context, AttributeSet attrs) {
super(context, attrs);
+ this.mContext = context;
}
@Override
@@ -104,7 +107,9 @@
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount = getChildCount();
int fullRowWidth = r - l;
- int xPos = getPaddingLeft();
+ int xPos = isRTL()
+ ? fullRowWidth - getPaddingRight()
+ : getPaddingLeft();
int yPos = getPaddingTop();
int lastHorizontalSpacing = 0;
// The index of the child which starts the current row.
@@ -117,18 +122,25 @@
int currentChildWidth = currentChild.getMeasuredWidth();
LayoutParams lp = (LayoutParams) currentChild.getLayoutParams();
- // If the current child does not fit on this row.
- if (xPos + currentChildWidth > fullRowWidth) {
+ boolean childDoesNotFitOnRow = isRTL()
+ ? xPos - getPaddingLeft() - currentChildWidth < 0
+ : xPos + currentChildWidth > fullRowWidth;
+
+ if (childDoesNotFitOnRow) {
// Layout all the children on this row but the current one.
layoutChildrenOnRow(rowStartIdx, i, fullRowWidth, xPos, yPos,
lastHorizontalSpacing);
// Update the positions for starting on the new row.
- xPos = getPaddingLeft();
+ xPos = isRTL()
+ ? fullRowWidth - getPaddingRight()
+ : getPaddingLeft();
yPos += mLineHeight;
rowStartIdx = i;
}
- xPos += currentChildWidth + lp.mHorizontalSpacing;
+ xPos = isRTL()
+ ? xPos - currentChildWidth - lp.mHorizontalSpacing
+ : xPos + currentChildWidth + lp.mHorizontalSpacing;
lastHorizontalSpacing = lp.mHorizontalSpacing;
}
}
@@ -148,21 +160,41 @@
private void layoutChildrenOnRow(int startIndex, int endIndex, int fullRowWidth, int xPos,
int yPos, int lastHorizontalSpacing) {
- int freeSpace = fullRowWidth - xPos + lastHorizontalSpacing;
- xPos = getPaddingLeft() + freeSpace;
+ if (!isRTL()) {
+ xPos = getPaddingLeft() + fullRowWidth - xPos + lastHorizontalSpacing;
+ }
for (int j = startIndex; j < endIndex; ++j) {
View currentChild = getChildAt(j);
+ int currentChildWidth = currentChild.getMeasuredWidth();
+ LayoutParams lp = (LayoutParams) currentChild.getLayoutParams();
+ if (isRTL() && j == startIndex) {
+ xPos = fullRowWidth - xPos - getPaddingRight() - currentChildWidth
+ - lp.mHorizontalSpacing;
+ }
+
currentChild.layout(
xPos,
yPos,
- xPos + currentChild.getMeasuredWidth(),
+ xPos + currentChildWidth,
yPos + currentChild.getMeasuredHeight());
- xPos += currentChild.getMeasuredWidth()
- + ((LayoutParams) currentChild.getLayoutParams()).mHorizontalSpacing;
+
+ if (isRTL()) {
+ int nextChildWidth = j < endIndex - 1
+ ? getChildAt(j + 1).getMeasuredWidth()
+ : 0;
+ xPos -= nextChildWidth + lp.mHorizontalSpacing;
+ } else {
+ xPos += currentChildWidth + lp.mHorizontalSpacing;
+ }
}
}
+ private boolean isRTL() {
+ return mContext.getResources().getConfiguration().getLayoutDirection()
+ == View.LAYOUT_DIRECTION_RTL;
+ }
+
public static class LayoutParams extends ViewGroup.LayoutParams {
public final int mHorizontalSpacing;
public final int mVerticalSpacing;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
index bb43899..d9bf539 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarNavigationBarController.java
@@ -106,7 +106,7 @@
String category = getPackageCategory(packageName);
if (mFacetCategoryMap.containsKey(category)) {
- int index = mFacetCategoryMap.get(packageName);
+ int index = mFacetCategoryMap.get(category);
mFacetHasMultipleAppsCache.put(index, facetHasMultiplePackages(index));
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 12035b9..023c4d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -789,8 +789,9 @@
}
private boolean isInQsArea(float x, float y) {
- return (x >= mQsContainer.getX() && x <= mQsContainer.getX() + mQsContainer.getWidth()) &&
- (y <= mNotificationStackScroller.getBottomMostNotificationBottom()
+ return (x >= mQsDensityContainer.getX()
+ && x <= mQsDensityContainer.getX() + mQsDensityContainer.getWidth())
+ && (y <= mNotificationStackScroller.getBottomMostNotificationBottom()
|| y <= mQsContainer.getY() + mQsContainer.getHeight());
}
@@ -1337,7 +1338,8 @@
return false;
}
View header = mKeyguardShowing ? mKeyguardStatusBar : mQsContainer.getHeader();
- boolean onHeader = x >= header.getX() && x <= header.getX() + header.getWidth()
+ boolean onHeader = x >= mQsDensityContainer.getX()
+ && x <= mQsDensityContainer.getX() + mQsDensityContainer.getWidth()
&& y >= header.getTop() && y <= header.getBottom();
if (mQsExpanded) {
return onHeader || (yDiff < 0 && isInQsArea(x, y));
diff --git a/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java b/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java
index 0e64dcd..15ad1f1 100644
--- a/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java
+++ b/packages/SystemUI/src/com/android/systemui/tv/pip/PipControlsView.java
@@ -143,7 +143,9 @@
@Override
public void onClick(View v) {
mPipManager.closePip();
- mListener.onClosed();
+ if (mListener != null) {
+ mListener.onClosed();
+ }
}
});
mCloseButtonView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index 6976c0b..e3ed92c 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -86,7 +86,7 @@
= new Intent(Settings.ACTION_ZEN_MODE_PRIORITY_SETTINGS);
private final Context mContext;
- private final LayoutInflater mInflater;
+ protected final LayoutInflater mInflater;
private final H mHandler = new H();
private final ZenPrefs mPrefs;
private final TransitionHelper mTransitionHelper = new TransitionHelper();
@@ -95,12 +95,12 @@
private String mTag = TAG + "/" + Integer.toHexString(System.identityHashCode(this));
- private SegmentedButtons mZenButtons;
+ protected SegmentedButtons mZenButtons;
private View mZenIntroduction;
private TextView mZenIntroductionMessage;
private View mZenIntroductionConfirm;
private TextView mZenIntroductionCustomize;
- private LinearLayout mZenConditions;
+ protected LinearLayout mZenConditions;
private TextView mZenAlarmWarning;
private Callback mCallback;
@@ -148,10 +148,7 @@
mTransitionHelper.dump(fd, pw, args);
}
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
-
+ protected void createZenButtons() {
mZenButtons = (SegmentedButtons) findViewById(R.id.zen_buttons);
mZenButtons.addButton(R.string.interruption_level_none_twoline,
R.string.interruption_level_none_with_warning,
@@ -163,7 +160,12 @@
R.string.interruption_level_priority,
Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS);
mZenButtons.setCallback(mZenButtonsCallback);
+ }
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+ createZenButtons();
mZenIntroduction = findViewById(R.id.zen_introduction);
mZenIntroductionMessage = (TextView) findViewById(R.id.zen_introduction_message);
mSpTexts.add(mZenIntroductionMessage);
@@ -302,14 +304,18 @@
}
}
+ protected void addZenConditions(int count) {
+ for (int i = 0; i < count; i++) {
+ mZenConditions.addView(mInflater.inflate(R.layout.zen_mode_condition, this, false));
+ }
+ }
+
public void init(ZenModeController controller) {
mController = controller;
mCountdownConditionSupported = mController.isCountdownConditionSupported();
final int countdownDelta = mCountdownConditionSupported ? COUNTDOWN_CONDITION_COUNT : 0;
final int minConditions = 1 /*forever*/ + countdownDelta;
- for (int i = 0; i < minConditions; i++) {
- mZenConditions.addView(mInflater.inflate(R.layout.zen_mode_condition, this, false));
- }
+ addZenConditions(minConditions);
mSessionZen = getSelectedZen(-1);
handleUpdateManualRule(mController.getManualRule());
if (DEBUG) Log.d(mTag, "init mExitCondition=" + mExitCondition);
@@ -917,7 +923,7 @@
}
}
- private final SegmentedButtons.Callback mZenButtonsCallback = new SegmentedButtons.Callback() {
+ protected final SegmentedButtons.Callback mZenButtonsCallback = new SegmentedButtons.Callback() {
@Override
public void onSelected(final Object value, boolean fromClick) {
if (value != null && mZenButtons.isShown() && isAttachedToWindow()) {
diff --git a/rs/java/android/renderscript/Allocation.java b/rs/java/android/renderscript/Allocation.java
index fc92966..f2ef065 100644
--- a/rs/java/android/renderscript/Allocation.java
+++ b/rs/java/android/renderscript/Allocation.java
@@ -142,7 +142,10 @@
}
return Element.DataType.FLOAT_64;
}
- return null;
+
+ throw new RSIllegalArgumentException("Parameter of type " + cmp.getSimpleName() +
+ "[] is not compatible with data type " + mType.mElement.mType.name() +
+ " of allocation");
}
diff --git a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
index a093d92..e15b785 100644
--- a/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/MagnificationController.java
@@ -111,12 +111,18 @@
public void register() {
mScreenStateObserver.register();
mWindowStateObserver.register();
+
+ // Obtain initial state.
+ mWindowStateObserver.getRegions(mMagnifiedRegion, mAvailableRegion);
+ mMagnifiedRegion.getBounds(mMagnifiedBounds);
}
/**
* Unregisters magnification-related observers.
*/
public void unregister() {
+ mSpecAnimationBridge.cancel();
+
mScreenStateObserver.unregister();
mWindowStateObserver.unregister();
}
@@ -149,8 +155,10 @@
final float offsetY = sentSpec.offsetY;
// Compute the new center and update spec as needed.
- final float centerX = (mMagnifiedBounds.width() / 2.0f - offsetX) / scale;
- final float centerY = (mMagnifiedBounds.height() / 2.0f - offsetY) / scale;
+ final float centerX = (mMagnifiedBounds.width() / 2.0f
+ + mMagnifiedBounds.left - offsetX) / scale;
+ final float centerY = (mMagnifiedBounds.height() / 2.0f
+ + mMagnifiedBounds.top - offsetY) / scale;
if (updateSpec) {
setScaleAndCenter(scale, centerX, centerY, false);
} else {
@@ -246,7 +254,8 @@
*/
public float getCenterX() {
synchronized (mLock) {
- return (mMagnifiedBounds.width() / 2.0f - getOffsetX()) / getScale();
+ return (mMagnifiedBounds.width() / 2.0f
+ + mMagnifiedBounds.left - getOffsetX()) / getScale();
}
}
@@ -268,7 +277,8 @@
*/
public float getCenterY() {
synchronized (mLock) {
- return (mMagnifiedBounds.height() / 2.0f - getOffsetY()) / getScale();
+ return (mMagnifiedBounds.height() / 2.0f
+ + mMagnifiedBounds.top - getOffsetY()) / getScale();
}
}
@@ -471,18 +481,25 @@
* otherwise
*/
private boolean updateMagnificationSpecLocked(float scale, float centerX, float centerY) {
+ // Handle defaults.
+ if (Float.isNaN(centerX)) {
+ centerX = getCenterX();
+ }
+ if (Float.isNaN(centerY)) {
+ centerY = getCenterY();
+ }
+ if (Float.isNaN(scale)) {
+ scale = getScale();
+ }
+
+ // Ensure requested center is within the available region.
if (!availableRegionContains(centerX, centerY)) {
return false;
}
- boolean changed = false;
-
+ // Compute changes.
final MagnificationSpec currSpec = mCurrentMagnificationSpec;
-
- // Handle scale.
- if (Float.isNaN(scale)) {
- scale = getScale();
- }
+ boolean changed = false;
final float normScale = MathUtils.constrain(scale, MIN_SCALE, MAX_SCALE);
if (Float.compare(currSpec.scale, normScale) != 0) {
@@ -490,24 +507,16 @@
changed = true;
}
- // Handle X offset.
- if (Float.isNaN(centerX)) {
- centerX = getCenterX();
- }
-
- final float nonNormOffsetX = mMagnifiedBounds.width() / 2.0f - centerX * scale;
+ final float nonNormOffsetX = mMagnifiedBounds.width() / 2.0f
+ + mMagnifiedBounds.left - centerX * scale;
final float offsetX = MathUtils.constrain(nonNormOffsetX, getMinOffsetXLocked(), 0);
if (Float.compare(currSpec.offsetX, offsetX) != 0) {
currSpec.offsetX = offsetX;
changed = true;
}
- // Handle Y offset.
- if (Float.isNaN(centerY)) {
- centerY = getCenterY();
- }
-
- final float nonNormOffsetY = mMagnifiedBounds.height() / 2.0f - centerY * scale;
+ final float nonNormOffsetY = mMagnifiedBounds.height() / 2.0f
+ + mMagnifiedBounds.top - centerY * scale;
final float offsetY = MathUtils.constrain(nonNormOffsetY, getMinOffsetYLocked(), 0);
if (Float.compare(currSpec.offsetY, offsetY) != 0) {
currSpec.offsetY = offsetY;
@@ -661,6 +670,12 @@
mTransformationAnimator.setInterpolator(new DecelerateInterpolator(2.5f));
}
+ public void cancel() {
+ if (mTransformationAnimator != null && mTransformationAnimator.isRunning()) {
+ mTransformationAnimator.cancel();
+ }
+ }
+
public void updateSentSpec(MagnificationSpec spec, boolean animate) {
if (Thread.currentThread().getId() == mMainThreadId) {
// Already on the main thread, don't bother proxying.
@@ -811,9 +826,6 @@
private static final int MESSAGE_ON_USER_CONTEXT_CHANGED = 3;
private static final int MESSAGE_ON_ROTATION_CHANGED = 4;
- private final Rect mTempRect = new Rect();
- private final Rect mTempRect1 = new Rect();
-
private final MagnificationController mController;
private final WindowManagerInternal mWindowManager;
private final Handler mHandler;
@@ -884,6 +896,10 @@
mController.resetIfNeeded(true);
}
+ public void getRegions(@NonNull Region outMagnified, @NonNull Region outAvailable) {
+ mWindowManager.getMagnificationRegions(outMagnified, outAvailable);
+ }
+
private class CallbackHandler extends Handler {
public CallbackHandler(Context context) {
super(context.getMainLooper());
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index b737ae2..6288b56 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -1818,7 +1818,7 @@
File initSentinel = new File(stateDir, INIT_SENTINEL_FILE_NAME);
if (initSentinel.exists()) {
synchronized (mQueueLock) {
- mPendingInits.add(transportName);
+ mPendingInits.add(name);
// TODO: pick a better starting time than now + 1 minute
long delay = 1000 * 60; // one minute, in milliseconds
@@ -2316,6 +2316,25 @@
}
}
+ // What name is this transport registered under...?
+ private String getTransportName(IBackupTransport transport) {
+ if (MORE_DEBUG) {
+ Slog.v(TAG, "Searching for transport name of " + transport);
+ }
+ synchronized (mTransports) {
+ final int N = mTransports.size();
+ for (int i = 0; i < N; i++) {
+ if (mTransports.valueAt(i).equals(transport)) {
+ if (MORE_DEBUG) {
+ Slog.v(TAG, " Name found: " + mTransports.keyAt(i));
+ }
+ return mTransports.keyAt(i);
+ }
+ }
+ }
+ return null;
+ }
+
// fire off a backup agent, blocking until it attaches or times out
IBackupAgent bindToAgentSynchronous(ApplicationInfo app, int mode) {
IBackupAgent agent = null;
@@ -2921,7 +2940,15 @@
if (MORE_DEBUG) Slog.d(TAG, "Server requires init; rerunning");
addBackupTrace("init required; rerunning");
try {
- mPendingInits.add(mTransport.transportDirName());
+ final String name = getTransportName(mTransport);
+ if (name != null) {
+ mPendingInits.add(name);
+ } else {
+ if (DEBUG) {
+ Slog.w(TAG, "Couldn't find name of transport " + mTransport
+ + " for init");
+ }
+ }
} catch (Exception e) {
Slog.w(TAG, "Failed to query transport name heading for init", e);
// swallow it and proceed; we don't rely on this
@@ -7590,77 +7617,6 @@
// ----- Restore handling -----
- // new style: we only store the SHA-1 hashes of each sig, not the full block
- 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) {
- if (MORE_DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check");
- 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 (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigHashes
- + " device=" + deviceSigs);
- 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;
- }
-
- 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;
- }
-
// Old style: directly match the stored vs on device signature blocks
static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
if (target == null) {
@@ -8173,7 +8129,7 @@
}
Metadata metaInfo = mPmAgent.getRestoredMetadata(packageName);
- if (!signaturesMatch(metaInfo.sigHashes, mCurrentPackage)) {
+ if (!BackupUtils.signaturesMatch(metaInfo.sigHashes, mCurrentPackage)) {
Slog.w(TAG, "Signature mismatch restoring " + packageName);
EventLog.writeEvent(EventLogTags.RESTORE_AGENT_FAILURE, packageName,
"Signature mismatch");
diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
index f197c1e..09f240f 100644
--- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
+++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java
@@ -205,7 +205,7 @@
PackageManager.GET_SIGNATURES);
homeInstaller = mPackageManager.getInstallerPackageName(home.getPackageName());
homeVersion = homeInfo.versionCode;
- homeSigHashes = hashSignatureArray(homeInfo.signatures);
+ homeSigHashes = BackupUtils.hashSignatureArray(homeInfo.signatures);
} catch (NameNotFoundException e) {
Slog.w(TAG, "Can't access preferred home info");
// proceed as though there were no preferred home set
@@ -222,7 +222,7 @@
final boolean needHomeBackup = (homeVersion != mStoredHomeVersion)
|| !Objects.equals(home, mStoredHomeComponent)
|| (home != null
- && !BackupManagerService.signaturesMatch(mStoredHomeSigHashes, homeInfo));
+ && !BackupUtils.signaturesMatch(mStoredHomeSigHashes, homeInfo));
if (needHomeBackup) {
if (DEBUG) {
Slog.i(TAG, "Home preference changed; backing up new state " + home);
@@ -309,7 +309,7 @@
outputBuffer.reset();
outputBufferStream.writeInt(info.versionCode);
writeSignatureHashArray(outputBufferStream,
- hashSignatureArray(info.signatures));
+ BackupUtils.hashSignatureArray(info.signatures));
if (DEBUG) {
Slog.v(TAG, "+ writing metadata for " + packName
@@ -432,18 +432,6 @@
mRestoredSignatures = sigMap;
}
- 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(BackupManagerService.hashSignature(s));
- }
- return hashes;
- }
-
private static void writeSignatureHashArray(DataOutputStream out, ArrayList<byte[]> hashes)
throws IOException {
// the number of entries in the array
@@ -492,13 +480,8 @@
}
if (nonHashFound) {
- ArrayList<byte[]> hashes =
- new ArrayList<byte[]>(sigs.size());
- for (int i = 0; i < sigs.size(); i++) {
- Signature s = new Signature(sigs.get(i));
- hashes.add(BackupManagerService.hashSignature(s));
- }
- sigs = hashes;
+ // Replace with the hashes.
+ sigs = BackupUtils.hashSignatureArray(sigs);
}
return sigs;
diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java
index 45008dc..9e2f1167 100644
--- a/services/core/java/com/android/server/MountService.java
+++ b/services/core/java/com/android/server/MountService.java
@@ -165,6 +165,7 @@
public void onStart() {
mMountService = new MountService(getContext());
publishBinderService("mount", mMountService);
+ mMountService.start();
}
@Override
@@ -430,9 +431,13 @@
= { "password", "default", "pattern", "pin" };
private final Context mContext;
+
private final NativeDaemonConnector mConnector;
private final NativeDaemonConnector mCryptConnector;
+ private final Thread mConnectorThread;
+ private final Thread mCryptConnectorThread;
+
private volatile boolean mSystemReady = false;
private volatile boolean mBootCompleted = false;
private volatile boolean mDaemonConnected = false;
@@ -1494,17 +1499,13 @@
null);
mConnector.setDebug(true);
mConnector.setWarnIfHeld(mLock);
-
- Thread thread = new Thread(mConnector, VOLD_TAG);
- thread.start();
+ mConnectorThread = new Thread(mConnector, VOLD_TAG);
// Reuse parameters from first connector since they are tested and safe
mCryptConnector = new NativeDaemonConnector(this, "cryptd",
MAX_CONTAINERS * 2, CRYPTD_TAG, 25, null);
mCryptConnector.setDebug(true);
-
- Thread crypt_thread = new Thread(mCryptConnector, CRYPTD_TAG);
- crypt_thread.start();
+ mCryptConnectorThread = new Thread(mCryptConnector, CRYPTD_TAG);
final IntentFilter userFilter = new IntentFilter();
userFilter.addAction(Intent.ACTION_USER_ADDED);
@@ -1521,6 +1522,11 @@
}
}
+ private void start() {
+ mConnectorThread.start();
+ mCryptConnectorThread.start();
+ }
+
private void systemReady() {
mSystemReady = true;
mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget();
diff --git a/services/core/java/com/android/server/ThermalObserver.java b/services/core/java/com/android/server/ThermalObserver.java
deleted file mode 100644
index aee28fb..0000000
--- a/services/core/java/com/android/server/ThermalObserver.java
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2015 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server;
-
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.os.Binder;
-import android.os.Handler;
-import android.os.Message;
-import android.os.PowerManager;
-import android.os.UEventObserver;
-import android.os.UserHandle;
-
-import java.io.FileDescriptor;
-import java.io.PrintWriter;
-
-/**
- * ThermalObserver for monitoring temperature changes.
- */
-public class ThermalObserver extends SystemService {
- private static final String TAG = "ThermalObserver";
-
- private static final String CALLSTATE_UEVENT_MATCH =
- "DEVPATH=/devices/virtual/switch/thermalstate";
-
- private static final int MSG_THERMAL_STATE_CHANGED = 0;
-
- private static final int SWITCH_STATE_NORMAL = 0;
- private static final int SWITCH_STATE_WARNING = 1;
- private static final int SWITCH_STATE_EXCEEDED = 2;
-
- private final PowerManager mPowerManager;
- private final PowerManager.WakeLock mWakeLock;
-
- private final Object mLock = new Object();
- private Integer mLastState;
-
- private final UEventObserver mThermalWarningObserver = new UEventObserver() {
- @Override
- public void onUEvent(UEventObserver.UEvent event) {
- updateLocked(Integer.parseInt(event.get("SWITCH_STATE")));
- }
- };
-
- private final Handler mHandler = new Handler(true /*async*/) {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case MSG_THERMAL_STATE_CHANGED:
- handleThermalStateChange(msg.arg1);
- mWakeLock.release();
- break;
- }
- }
- };
-
- public ThermalObserver(Context context) {
- super(context);
- mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
- mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
-
- mThermalWarningObserver.startObserving(CALLSTATE_UEVENT_MATCH);
- }
-
- private void updateLocked(int state) {
- Message message = new Message();
- message.what = MSG_THERMAL_STATE_CHANGED;
- message.arg1 = state;
-
- mWakeLock.acquire();
- mHandler.sendMessage(message);
- }
-
- private void handleThermalStateChange(int state) {
- synchronized (mLock) {
- mLastState = state;
- Intent intent = new Intent(Intent.ACTION_THERMAL_EVENT);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
-
- final int thermalState;
-
- switch (state) {
- case SWITCH_STATE_WARNING:
- thermalState = Intent.EXTRA_THERMAL_STATE_WARNING;
- break;
- case SWITCH_STATE_EXCEEDED:
- thermalState = Intent.EXTRA_THERMAL_STATE_EXCEEDED;
- break;
- case SWITCH_STATE_NORMAL:
- default:
- thermalState = Intent.EXTRA_THERMAL_STATE_NORMAL;
- break;
- }
-
- intent.putExtra(Intent.EXTRA_THERMAL_STATE, thermalState);
-
- getContext().sendBroadcastAsUser(intent, UserHandle.ALL);
- }
- }
-
- @Override
- public void onStart() {
- publishBinderService(TAG, new BinderService());
- }
-
- private final class BinderService extends Binder {
- @Override
- protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
- if (getContext().checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
- != PackageManager.PERMISSION_GRANTED) {
- pw.println("Permission Denial: can't dump thermal observer service from from pid="
- + Binder.getCallingPid()
- + ", uid=" + Binder.getCallingUid());
- return;
- }
-
- final long ident = Binder.clearCallingIdentity();
- try {
- synchronized (mLock) {
- if (args == null || args.length == 0 || "-a".equals(args[0])) {
- pw.println("Current Thermal Observer Service state:");
- pw.println(" last state change: "
- + (mLastState != null ? mLastState : "none"));
- }
- }
- } finally {
- Binder.restoreCallingIdentity(ident);
- }
- }
- }
-}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 467fc49..cd4d472 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1488,7 +1488,6 @@
final ServiceThread mHandlerThread;
final MainHandler mHandler;
final UiHandler mUiHandler;
- final ProcessStartLogger mProcessStartLogger;
PackageManagerInternal mPackageManagerInt;
@@ -2460,8 +2459,6 @@
mHandler = new MainHandler(mHandlerThread.getLooper());
mUiHandler = new UiHandler();
- mProcessStartLogger = new ProcessStartLogger();
-
mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
"foreground", BROADCAST_FG_TIMEOUT, false);
mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
@@ -3594,7 +3591,12 @@
app.processName, hostingType,
hostingNameStr != null ? hostingNameStr : "");
- mProcessStartLogger.logIfNeededLocked(app, startResult);
+ try {
+ AppGlobals.getPackageManager().logAppProcessStartIfNeeded(app.processName, app.uid,
+ app.info.seinfo, app.info.sourceDir, startResult.pid);
+ } catch (RemoteException ex) {
+ // Ignore
+ }
if (app.persistent) {
Watchdog.getInstance().processStarted(app.processName, startResult.pid);
@@ -6537,8 +6539,6 @@
}
}, dumpheapFilter);
- mProcessStartLogger.registerListener(mContext);
-
// Let system services know.
mSystemServiceManager.startBootPhase(SystemService.PHASE_BOOT_COMPLETED);
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 5b4a96d..a9ef1d6 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -4787,6 +4787,8 @@
" Task id #" + task.taskId + "\n" +
" mFullscreen=" + task.mFullscreen + "\n" +
" mBounds=" + task.mBounds + "\n" +
+ " mMinimalWidth=" + task.mMinimalWidth + "\n" +
+ " mMinimalHeight=" + task.mMinimalHeight + "\n" +
" mLastNonFullscreenBounds=" + task.mLastNonFullscreenBounds);
if (printed) {
header = null;
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index ffa3b5b..3d42047 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -1224,7 +1224,8 @@
// WiFi keeps an accumulated total of stats, unlike Bluetooth.
// Keep the last WiFi stats so we can compute a delta.
@GuardedBy("mExternalStatsLock")
- private WifiActivityEnergyInfo mLastInfo = new WifiActivityEnergyInfo(0, 0, 0, 0, 0, 0);
+ private WifiActivityEnergyInfo mLastInfo =
+ new WifiActivityEnergyInfo(0, 0, 0, new long[]{0}, 0, 0, 0);
@GuardedBy("mExternalStatsLock")
private WifiActivityEnergyInfo pullWifiEnergyInfoLocked() {
diff --git a/services/core/java/com/android/server/am/ProcessStartLogger.java b/services/core/java/com/android/server/am/ProcessStartLogger.java
deleted file mode 100644
index 39fbeb5..0000000
--- a/services/core/java/com/android/server/am/ProcessStartLogger.java
+++ /dev/null
@@ -1,151 +0,0 @@
-package com.android.server.am;
-
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
-import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
-
-import android.app.AppGlobals;
-import android.app.admin.SecurityLog;
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
-import android.os.Handler;
-import android.os.HandlerThread;
-import android.os.Process;
-import android.os.RemoteException;
-import android.os.Process.ProcessStartResult;
-import android.util.Slog;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.util.HashMap;
-
-/**
- * A class that logs process start information (including APK hash) to the security log.
- */
-class ProcessStartLogger {
- private static final String CLASS_NAME = "ProcessStartLogger";
- private static final String TAG = TAG_WITH_CLASS_NAME ? CLASS_NAME : TAG_AM;
-
- final HandlerThread mHandlerProcessLoggingThread;
- Handler mHandlerProcessLogging;
- // Should only access in mHandlerProcessLoggingThread
- final HashMap<String, String> mProcessLoggingApkHashes;
-
- ProcessStartLogger() {
- mHandlerProcessLoggingThread = new HandlerThread(CLASS_NAME,
- Process.THREAD_PRIORITY_BACKGROUND);
- mProcessLoggingApkHashes = new HashMap();
- }
-
- void logIfNeededLocked(ProcessRecord app, ProcessStartResult startResult) {
- if (!SecurityLog.isLoggingEnabled()) {
- return;
- }
- if (!mHandlerProcessLoggingThread.isAlive()) {
- mHandlerProcessLoggingThread.start();
- mHandlerProcessLogging = new Handler(mHandlerProcessLoggingThread.getLooper());
- }
- mHandlerProcessLogging.post(new ProcessLoggingRunnable(app, startResult,
- System.currentTimeMillis()));
- }
-
- void registerListener(Context context) {
- IntentFilter packageChangedFilter = new IntentFilter();
- packageChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
- packageChangedFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
- context.registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)
- || Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())) {
- int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
- getSendingUserId());
- String packageName = intent.getData().getSchemeSpecificPart();
- try {
- ApplicationInfo info = AppGlobals.getPackageManager().getApplicationInfo(
- packageName, 0, userHandle);
- invaildateCache(info.sourceDir);
- } catch (RemoteException e) {
- }
- }
- }
- }, packageChangedFilter);
- }
-
- private void invaildateCache(final String apkPath) {
- if (mHandlerProcessLogging != null) {
- mHandlerProcessLogging.post(new Runnable() {
- @Override
- public void run() {
- mProcessLoggingApkHashes.remove(apkPath);
- }
- });
- }
- }
-
- private class ProcessLoggingRunnable implements Runnable {
-
- private final ProcessRecord app;
- private final Process.ProcessStartResult startResult;
- private final long startTimestamp;
-
- public ProcessLoggingRunnable(ProcessRecord app, Process.ProcessStartResult startResult,
- long startTimestamp){
- this.app = app;
- this.startResult = startResult;
- this.startTimestamp = startTimestamp;
- }
-
- @Override
- public void run() {
- String apkHash = computeStringHashOfApk(app);
- SecurityLog.writeEvent(SecurityLog.TAG_APP_PROCESS_START,
- app.processName,
- startTimestamp,
- app.uid,
- startResult.pid,
- app.info.seinfo,
- apkHash);
- }
-
- private String computeStringHashOfApk(ProcessRecord app){
- final String apkFile = app.info.sourceDir;
- if(apkFile == null) {
- return "No APK";
- }
- String apkHash = mProcessLoggingApkHashes.get(apkFile);
- if (apkHash == null) {
- try {
- byte[] hash = computeHashOfApkFile(apkFile);
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < hash.length; i++) {
- sb.append(String.format("%02x", hash[i]));
- }
- apkHash = sb.toString();
- mProcessLoggingApkHashes.put(apkFile, apkHash);
- } catch (IOException | NoSuchAlgorithmException e) {
- Slog.w(TAG, "computeStringHashOfApk() failed", e);
- }
- }
- return apkHash != null ? apkHash : "Failed to count APK hash";
- }
-
- private byte[] computeHashOfApkFile(String packageArchiveLocation)
- throws IOException, NoSuchAlgorithmException {
- MessageDigest md = MessageDigest.getInstance("SHA-256");
- FileInputStream input = new FileInputStream(new File(packageArchiveLocation));
- byte[] buffer = new byte[65536];
- int size;
- while((size = input.read(buffer)) > 0) {
- md.update(buffer, 0, size);
- }
- input.close();
- return md.digest();
- }
- }
-}
\ No newline at end of file
diff --git a/services/core/java/com/android/server/backup/BackupUtils.java b/services/core/java/com/android/server/backup/BackupUtils.java
new file mode 100644
index 0000000..e5d564d
--- /dev/null
+++ b/services/core/java/com/android/server/backup/BackupUtils.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.backup;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.Signature;
+import android.util.Slog;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+public class BackupUtils {
+ private static final String TAG = "BackupUtils";
+
+ private static final boolean DEBUG = false; // STOPSHIP if true
+
+ public 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) {
+ if (DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check");
+ 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 (DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigHashes
+ + " device=" + deviceSigs);
+ 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 static byte[] hashSignature(byte[] signature) {
+ try {
+ MessageDigest digest = MessageDigest.getInstance("SHA-256");
+ digest.update(signature);
+ return digest.digest();
+ } catch (NoSuchAlgorithmException e) {
+ Slog.w(TAG, "No SHA-256 algorithm found!");
+ }
+ return null;
+ }
+
+ public static byte[] hashSignature(Signature signature) {
+ return hashSignature(signature.toByteArray());
+ }
+
+ public static ArrayList<byte[]> hashSignatureArray(Signature[] sigs) {
+ if (sigs == null) {
+ return null;
+ }
+
+ ArrayList<byte[]> hashes = new ArrayList<>(sigs.length);
+ for (Signature s : sigs) {
+ hashes.add(hashSignature(s));
+ }
+ return hashes;
+ }
+
+ public static ArrayList<byte[]> hashSignatureArray(List<byte[]> sigs) {
+ if (sigs == null) {
+ return null;
+ }
+
+ ArrayList<byte[]> hashes = new ArrayList<>(sigs.size());
+ for (byte[] s : sigs) {
+ hashes.add(hashSignature(s));
+ }
+ return hashes;
+ }
+}
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index c303ceb..7c71fbc 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -335,40 +335,40 @@
verifyCallingPackage(callingPackage);
ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
- if (!mShortcutServiceInternal.hasShortcutHostPermission(callingPackage,
- user.getIdentifier())) {
+ if (!mShortcutServiceInternal.hasShortcutHostPermission(getCallingUserId(),
+ callingPackage)) {
throw new SecurityException("Caller can't access shortcut information");
}
}
@Override
public ParceledListSlice getShortcuts(String callingPackage, long changedSince,
- String packageName, ComponentName componentName, int flags, UserHandle user)
- throws RemoteException {
+ String packageName, ComponentName componentName, int flags, UserHandle user) {
ensureShortcutPermission(callingPackage, user);
return new ParceledListSlice<>(
- mShortcutServiceInternal.getShortcuts(callingPackage, changedSince, packageName,
- componentName, flags, user.getIdentifier()));
+ mShortcutServiceInternal.getShortcuts(getCallingUserId(),
+ callingPackage, changedSince, packageName,
+ componentName, flags, user.getIdentifier()));
}
@Override
public ParceledListSlice getShortcutInfo(String callingPackage, String packageName,
- List<String> ids, UserHandle user) throws RemoteException {
+ List<String> ids, UserHandle user) {
ensureShortcutPermission(callingPackage, user);
return new ParceledListSlice<>(
- mShortcutServiceInternal.getShortcutInfo(callingPackage, packageName,
- ids, user.getIdentifier()));
+ mShortcutServiceInternal.getShortcutInfo(getCallingUserId(),
+ callingPackage, packageName, ids, user.getIdentifier()));
}
@Override
public void pinShortcuts(String callingPackage, String packageName, List<String> ids,
- UserHandle user) throws RemoteException {
+ UserHandle user) {
ensureShortcutPermission(callingPackage, user);
- mShortcutServiceInternal.pinShortcuts(callingPackage, packageName,
- ids, user.getIdentifier());
+ mShortcutServiceInternal.pinShortcuts(getCallingUserId(),
+ callingPackage, packageName, ids, user.getIdentifier());
}
@Override
@@ -376,8 +376,8 @@
UserHandle user) {
ensureShortcutPermission(callingPackage, user);
- return mShortcutServiceInternal.getShortcutIconResId(callingPackage, shortcut,
- user.getIdentifier());
+ return mShortcutServiceInternal.getShortcutIconResId(getCallingUserId(),
+ callingPackage, shortcut, user.getIdentifier());
}
@Override
@@ -385,25 +385,31 @@
UserHandle user) {
ensureShortcutPermission(callingPackage, user);
- return mShortcutServiceInternal.getShortcutIconFd(callingPackage, shortcut,
- user.getIdentifier());
+ return mShortcutServiceInternal.getShortcutIconFd(getCallingUserId(),
+ callingPackage, shortcut, user.getIdentifier());
}
@Override
- public boolean hasShortcutHostPermission(String callingPackage) throws RemoteException {
+ public boolean hasShortcutHostPermission(String callingPackage) {
verifyCallingPackage(callingPackage);
- return mShortcutServiceInternal.hasShortcutHostPermission(callingPackage,
- getCallingUserId());
+ return mShortcutServiceInternal.hasShortcutHostPermission(getCallingUserId(),
+ callingPackage);
}
@Override
public boolean startShortcut(String callingPackage, String packageName, String shortcutId,
- Rect sourceBounds, Bundle startActivityOptions, UserHandle user)
- throws RemoteException {
- ensureShortcutPermission(callingPackage, user);
+ Rect sourceBounds, Bundle startActivityOptions, UserHandle user) {
+ verifyCallingPackage(callingPackage);
+ ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
- final Intent intent = mShortcutServiceInternal.createShortcutIntent(callingPackage,
- packageName, shortcutId, user.getIdentifier());
+ // Even without the permission, pinned shortcuts are always launchable.
+ if (!mShortcutServiceInternal.isPinnedByCaller(getCallingUserId(),
+ callingPackage, packageName, shortcutId, user.getIdentifier())) {
+ ensureShortcutPermission(callingPackage, user);
+ }
+
+ final Intent intent = mShortcutServiceInternal.createShortcutIntent(getCallingUserId(),
+ callingPackage, packageName, shortcutId, user.getIdentifier());
if (intent == null) {
return false;
}
@@ -713,9 +719,11 @@
BroadcastCookie cookie = (BroadcastCookie) mListeners.getBroadcastCookie(i);
if (!isEnabledProfileOf(user, cookie.user, "onShortcutChanged")) continue;
+ final int launcherUserId = cookie.user.getIdentifier();
+
// Make sure the caller has the permission.
- if (!mShortcutServiceInternal.hasShortcutHostPermission(cookie.packageName,
- cookie.user.getIdentifier())) {
+ if (!mShortcutServiceInternal.hasShortcutHostPermission(
+ launcherUserId, cookie.packageName)) {
continue;
}
// Each launcher has a different set of pinned shortcuts, so we need to do a
@@ -723,8 +731,9 @@
// (As of now, only one launcher has the permission at a time, so it's bit
// moot, but we may change the permission model eventually.)
final List<ShortcutInfo> list =
- mShortcutServiceInternal.getShortcuts(cookie.packageName,
- /* changedSince= */ 0, packageName, /* component= */ null,
+ mShortcutServiceInternal.getShortcuts(launcherUserId,
+ cookie.packageName,
+ /* changedSince= */ 0, packageName, /* component= */ null,
ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY
| ShortcutQuery.FLAG_GET_PINNED
| ShortcutQuery.FLAG_GET_DYNAMIC
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index d2fd762..442643a 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -106,6 +106,7 @@
import android.app.IActivityManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.IDevicePolicyManager;
+import android.app.admin.SecurityLog;
import android.app.backup.IBackupManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -459,6 +460,8 @@
final PackageHandler mHandler;
+ private final ProcessLoggingHandler mProcessLoggingHandler;
+
/**
* Messages for {@link #mHandler} that need to wait for system ready before
* being dispatched.
@@ -1712,6 +1715,8 @@
// Send installed broadcasts if the install/update is not ephemeral
if (!isEphemeral(res.pkg)) {
+ mProcessLoggingHandler.invalidateProcessLoggingBaseApkHash(res.pkg.baseCodePath);
+
// Send added for users that see the package for the first time
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
extras, 0 /*flags*/, null /*targetPackage*/,
@@ -2096,6 +2101,7 @@
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
mHandlerThread.start();
mHandler = new PackageHandler(mHandlerThread.getLooper());
+ mProcessLoggingHandler = new ProcessLoggingHandler();
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
File dataDir = Environment.getDataDirectory();
@@ -19458,4 +19464,26 @@
return new ArrayList<>(mPackages.values());
}
}
+
+ /**
+ * Logs process start information (including base APK hash) to the security log.
+ * @hide
+ */
+ public void logAppProcessStartIfNeeded(String processName, int uid, String seinfo,
+ String apkFile, int pid) {
+ if (!SecurityLog.isLoggingEnabled()) {
+ return;
+ }
+ Bundle data = new Bundle();
+ data.putLong("startTimestamp", System.currentTimeMillis());
+ data.putString("processName", processName);
+ data.putInt("uid", uid);
+ data.putString("seinfo", seinfo);
+ data.putString("apkFile", apkFile);
+ data.putInt("pid", pid);
+ Message msg = mProcessLoggingHandler.obtainMessage(
+ ProcessLoggingHandler.LOG_APP_PROCESS_START_MSG);
+ msg.setData(data);
+ mProcessLoggingHandler.sendMessage(msg);
+ }
}
diff --git a/services/core/java/com/android/server/pm/ProcessLoggingHandler.java b/services/core/java/com/android/server/pm/ProcessLoggingHandler.java
new file mode 100644
index 0000000..c47dda4
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ProcessLoggingHandler.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.app.admin.SecurityLog;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+
+import com.android.internal.os.BackgroundThread;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import android.util.Slog;
+
+public final class ProcessLoggingHandler extends Handler {
+
+ private static final String TAG = "ProcessLoggingHandler";
+ static final int LOG_APP_PROCESS_START_MSG = 1;
+ static final int INVALIDATE_BASE_APK_HASH_MSG = 2;
+
+ private final HashMap<String, String> mProcessLoggingBaseApkHashes = new HashMap();
+
+ ProcessLoggingHandler() {
+ super(BackgroundThread.getHandler().getLooper());
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case LOG_APP_PROCESS_START_MSG: {
+ Bundle bundle = msg.getData();
+ String processName = bundle.getString("processName");
+ int uid = bundle.getInt("uid");
+ String seinfo = bundle.getString("seinfo");
+ String apkFile = bundle.getString("apkFile");
+ int pid = bundle.getInt("pid");
+ long startTimestamp = bundle.getLong("startTimestamp");
+ String apkHash = computeStringHashOfApk(apkFile);
+ SecurityLog.writeEvent(SecurityLog.TAG_APP_PROCESS_START, processName,
+ startTimestamp, uid, pid, seinfo, apkHash);
+ break;
+ }
+ case INVALIDATE_BASE_APK_HASH_MSG: {
+ Bundle bundle = msg.getData();
+ mProcessLoggingBaseApkHashes.remove(bundle.getString("apkFile"));
+ break;
+ }
+ }
+ }
+
+ void invalidateProcessLoggingBaseApkHash(String apkPath) {
+ Bundle data = new Bundle();
+ data.putString("apkFile", apkPath);
+ Message msg = obtainMessage(INVALIDATE_BASE_APK_HASH_MSG);
+ msg.setData(data);
+ sendMessage(msg);
+ }
+
+ private String computeStringHashOfApk(String apkFile) {
+ if (apkFile == null) {
+ return "No APK";
+ }
+ String apkHash = mProcessLoggingBaseApkHashes.get(apkFile);
+ if (apkHash == null) {
+ try {
+ byte[] hash = computeHashOfApkFile(apkFile);
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < hash.length; i++) {
+ sb.append(String.format("%02x", hash[i]));
+ }
+ apkHash = sb.toString();
+ mProcessLoggingBaseApkHashes.put(apkFile, apkHash);
+ } catch (IOException | NoSuchAlgorithmException e) {
+ Slog.w(TAG, "computeStringHashOfApk() failed", e);
+ }
+ }
+ return apkHash != null ? apkHash : "Failed to count APK hash";
+ }
+
+ private byte[] computeHashOfApkFile(String packageArchiveLocation)
+ throws IOException, NoSuchAlgorithmException {
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+ FileInputStream input = new FileInputStream(new File(packageArchiveLocation));
+ byte[] buffer = new byte[65536];
+ int size;
+ while ((size = input.read(buffer)) > 0) {
+ md.update(buffer, 0, size);
+ }
+ input.close();
+ return md.digest();
+ }
+}
diff --git a/services/core/java/com/android/server/pm/ShortcutLauncher.java b/services/core/java/com/android/server/pm/ShortcutLauncher.java
index 740a8f7..7699f30 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 @@
/**
* 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";
@@ -40,37 +40,38 @@
private static final String TAG_PACKAGE = "package";
private static final String TAG_PIN = "pin";
+ private static final String ATTR_LAUNCHER_USER_ID = "launcher-user";
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;
+ private final int mOwnerUserId;
/**
* Package name -> IDs.
*/
final private ArrayMap<String, ArraySet<String>> mPinnedShortcuts = new ArrayMap<>();
- ShortcutLauncher(@UserIdInt int userId, @NonNull String packageName) {
- mUserId = userId;
- mPackageName = packageName;
+ 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);
}
- @NonNull
- public String getPackageName() {
- return mPackageName;
+ @Override
+ public int getOwnerUserId() {
+ return mOwnerUserId;
}
- public void pinShortcuts(@NonNull ShortcutService s, @NonNull String packageName,
- @NonNull List<String> ids) {
+ public void pinShortcuts(@NonNull ShortcutService s, @UserIdInt int packageUserId,
+ @NonNull String packageName, @NonNull List<String> ids) {
+ final ShortcutPackage packageShortcuts =
+ s.getPackageShortcutsLocked(packageName, packageUserId);
+
final int idSize = ids.size();
if (idSize == 0) {
mPinnedShortcuts.remove(packageName);
@@ -80,8 +81,6 @@
// 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++) {
@@ -96,7 +95,7 @@
}
mPinnedShortcuts.put(packageName, newSet);
}
- s.getPackageShortcutsLocked(packageName, mUserId).refreshPinnedFlags(s);
+ packageShortcuts.refreshPinnedFlags(s);
}
/**
@@ -113,15 +112,18 @@
/**
* 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_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);
@@ -142,12 +144,20 @@
/**
* Load.
*/
- public static ShortcutLauncher loadFromXml(XmlPullParser parser, int userId)
- 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 ShortcutLauncher ret = new ShortcutLauncher(userId, launcherPackageName);
+ // 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();
@@ -159,21 +169,33 @@
}
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;
- }
- case TAG_PIN: {
- ids.add(ShortcutService.parseStringAttribute(parser,
- ATTR_VALUE));
- 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;
+ }
}
}
- throw ShortcutService.throwForInvalidTag(depth, tag);
+ if (depth == outerDepth + 2) {
+ switch (tag) {
+ case TAG_PIN: {
+ ids.add(ShortcutService.parseStringAttribute(parser,
+ ATTR_VALUE));
+ continue;
+ }
+ }
+ }
+ ShortcutService.warnForInvalidTag(depth, tag);
+ }
+ if (spi != null) {
+ ret.replacePackageInfo(spi);
}
return ret;
}
@@ -183,7 +205,12 @@
pw.print(prefix);
pw.print("Launcher: ");
- pw.print(mPackageName);
+ 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 359ea1c..f941432 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 @@
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 @@
/**
* 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 @@
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,21 +82,23 @@
*/
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();
}
+ /**
+ * Note this does *not* provide a correct view to the calling launcher.
+ */
@Nullable
public ShortcutInfo findShortcutById(String id) {
return mShortcuts.get(id);
@@ -113,7 +108,7 @@
@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;
@@ -121,7 +116,7 @@
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);
}
@@ -229,12 +224,13 @@
}
// Then, for the pinned set for each launcher, set the pin flag one by one.
- final ArrayMap<String, ShortcutLauncher> launchers =
- s.getUserShortcutsLocked(mUserId).getLaunchers();
+ final ArrayMap<ShortcutUser.PackageWithUser, ShortcutLauncher> launchers =
+ 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;
@@ -301,13 +297,25 @@
* Find all shortcuts that match {@code query}.
*/
public void findAll(@NonNull ShortcutService s, @NonNull List<ShortcutInfo> result,
+ @Nullable Predicate<ShortcutInfo> query, int cloneFlag) {
+ findAll(s, result, query, cloneFlag, null, 0);
+ }
+
+ /**
+ * Find all shortcuts that match {@code query}.
+ *
+ * This will also provide a "view" for each launcher -- a non-dynamic shortcut that's not pinned
+ * by the calling launcher will not be included in the result, and also "isPinned" will be
+ * adjusted for the caller too.
+ */
+ public void findAll(@NonNull ShortcutService s, @NonNull List<ShortcutInfo> result,
@Nullable Predicate<ShortcutInfo> query, int cloneFlag,
- @Nullable String callingLauncher) {
+ @Nullable String callingLauncher, int launcherUserId) {
// Set of pinned shortcuts by the calling launcher.
final ArraySet<String> pinnedByCallerSet = (callingLauncher == null) ? null
- : s.getLauncherShortcuts(callingLauncher, mUserId)
- .getPinnedShortcutIds(mPackageName);
+ : s.getLauncherShortcuts(callingLauncher, getPackageUserId(), launcherUserId)
+ .getPinnedShortcutIds(getPackageName());
for (int i = 0; i < mShortcuts.size(); i++) {
final ShortcutInfo si = mShortcuts.valueAt(i);
@@ -347,7 +355,7 @@
pw.print(prefix);
pw.print("Package: ");
- pw.print(mPackageName);
+ pw.print(getPackageName());
pw.println();
pw.print(prefix);
@@ -365,6 +373,9 @@
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;
@@ -391,6 +402,7 @@
pw.println(")");
}
+ @Override
public void saveToXml(@NonNull XmlSerializer out, boolean forBackup)
throws IOException, XmlPullParserException {
final int size = mShortcuts.size();
@@ -401,10 +413,11 @@
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);
@@ -449,13 +462,14 @@
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);
@@ -463,6 +477,7 @@
ShortcutService.parseIntAttribute(parser, ATTR_CALL_COUNT);
ret.mLastResetTime =
ShortcutService.parseLongAttribute(parser, ATTR_LAST_RESET);
+ ShortcutPackageInfo spi = null;
final int outerDepth = parser.getDepth();
int type;
@@ -473,15 +488,23 @@
}
final int depth = parser.getDepth();
final String tag = parser.getName();
- switch (tag) {
- case TAG_SHORTCUT:
- final ShortcutInfo si = parseShortcut(parser, packageName);
+ 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;
+ // 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;
}
@@ -507,8 +530,7 @@
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 98dc608..5f706b8 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,34 +30,21 @@
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_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";
- public interface ShortcutPackageInfoHolder {
- ShortcutPackageInfo getShortcutPackageInfo();
- }
-
- private final String mPackageName;
-
/**
* When true, this package information was restored from the previous device, and the app hasn't
* been installed yet.
@@ -69,17 +53,14 @@
private int mVersionCode;
private ArrayList<byte[]> mSigHashes;
- private ShortcutPackageInfo(String packageName, int versionCode, ArrayList<byte[]> sigHashes,
- boolean isShadow) {
+ private ShortcutPackageInfo(int versionCode, ArrayList<byte[]> sigHashes, boolean isShadow) {
mVersionCode = versionCode;
mIsShadow = isShadow;
mSigHashes = sigHashes;
- mPackageName = Preconditions.checkNotNull(packageName);
}
- @NonNull
- public String getPackageName() {
- return mPackageName;
+ public static ShortcutPackageInfo newEmpty() {
+ return new ShortcutPackageInfo(0, new ArrayList<>(0), /* isShadow */ false);
}
public boolean isShadow() {
@@ -98,92 +79,13 @@
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;
}
@@ -191,36 +93,34 @@
}
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, 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_VERSION, mVersionCode);
ShortcutService.writeAttr(out, ATTR_SHADOW, mIsShadow);
@@ -235,7 +135,6 @@
public static ShortcutPackageInfo loadFromXml(XmlPullParser parser)
throws IOException, XmlPullParserException {
- final String packageName = ShortcutService.parseStringAttribute(parser, ATTR_NAME);
final int versionCode = ShortcutService.parseIntAttribute(parser, ATTR_VERSION);
final boolean shadow = ShortcutService.parseBooleanAttribute(parser, ATTR_SHADOW);
@@ -251,26 +150,27 @@
}
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, 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.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 526c84d..de2709d 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 @@
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 {
- @NonNull
- String getPackageName();
+abstract class ShortcutPackageItem {
+ private final int mPackageUserId;
+ private final String mPackageName;
- void saveToXml(@NonNull XmlSerializer out, boolean forBackup)
+ 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
+ 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());
+ }
+ }
+
+ 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 373e8cb..76a2dfa 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -31,7 +31,6 @@
import android.content.pm.LauncherApps.ShortcutQuery;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.ResolveInfo;
@@ -73,11 +72,11 @@
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;
import com.android.server.SystemService;
+import com.android.server.pm.ShortcutUser.PackageWithUser;
import libcore.io.IoUtils;
@@ -85,6 +84,10 @@
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.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,14 +108,7 @@
*
* - Default launcher check does take a few ms. Worth caching.
*
- * - Allow non-default launcher to start pinned shortcuts. (but not dynamic.)
- *
- * - Extract the user/package/launcher classes to their own files. Maybe rename so they all have
- * the same "Shortcut" prefix.
- *
- * - Listen to PACKAGE_*, remove orphan info, update timestamp for icon res
- * -> Need to scan all packages when a user starts too.
- * -> Clear data -> remove all dynamic? but not the pinned?
+ * - Clear data -> remove all dynamic? but not the pinned?
*
* - Scan and remove orphan bitmaps (just in case).
*
@@ -455,16 +452,24 @@
return (int) parseLongAttribute(parser, attribute);
}
+ static int parseIntAttribute(XmlPullParser parser, String attribute, int def) {
+ return (int) parseLongAttribute(parser, attribute, def);
+ }
+
static long parseLongAttribute(XmlPullParser parser, String attribute) {
+ return parseLongAttribute(parser, attribute, 0);
+ }
+
+ static long parseLongAttribute(XmlPullParser parser, String attribute, long def) {
final String value = parseStringAttribute(parser, attribute);
if (TextUtils.isEmpty(value)) {
- return 0;
+ return def;
}
try {
return Long.parseLong(value);
} catch (NumberFormatException e) {
Slog.e(TAG, "Error parsing long " + value);
- return 0;
+ return def;
}
}
@@ -630,31 +635,45 @@
}
path.mkdirs();
final AtomicFile file = new AtomicFile(path);
- FileOutputStream outs = null;
+ FileOutputStream os = null;
try {
- outs = file.startWrite();
+ os = file.startWrite();
- // Write to XML
- XmlSerializer out = new FastXmlSerializer();
- out.setOutput(outs, StandardCharsets.UTF_8.name());
- out.startDocument(null, true);
+ saveUserInternalLocked(userId, os, /* forBackup= */ false);
- getUserShortcutsLocked(userId).saveToXml(this, out, /* 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);
@@ -672,30 +691,8 @@
}
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;
@@ -704,6 +701,36 @@
}
}
+ 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.
}
@@ -825,8 +852,8 @@
@GuardedBy("mLock")
@NonNull
ShortcutLauncher getLauncherShortcuts(
- @NonNull String packageName, @UserIdInt int userId) {
- return getUserShortcutsLocked(userId).getLauncherShortcuts(packageName);
+ @NonNull String packageName, @UserIdInt int userId, @UserIdInt int launcherUserId) {
+ return getUserShortcutsLocked(userId).getLauncherShortcuts(packageName, launcherUserId);
}
// === Caller validation ===
@@ -1039,6 +1066,10 @@
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");
@@ -1179,10 +1210,10 @@
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;
@@ -1216,10 +1247,10 @@
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;
@@ -1255,10 +1286,10 @@
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;
@@ -1324,8 +1355,7 @@
final ArrayList<ShortcutInfo> ret = new ArrayList<>();
- getPackageShortcutsLocked(packageName, userId).findAll(this, ret, query, cloneFlags,
- /* callingLauncher= */ null);
+ getPackageShortcutsLocked(packageName, userId).findAll(this, ret, query, cloneFlags);
return new ParceledListSlice<>(ret);
}
@@ -1463,22 +1493,29 @@
// === House keeping ===
@VisibleForTesting
- void cleanUpPackageLocked(String packageName, int userId) {
- final boolean wasUserLoaded = isUserLoadedLocked(userId);
+ void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId) {
- final ShortcutUser mUser = getUserShortcutsLocked(userId);
+ // TODO Don't remove shadow packages' information.
+
+ final boolean wasUserLoaded = isUserLoadedLocked(owningUserId);
+
+ final ShortcutUser mUser = getUserShortcutsLocked(owningUserId);
boolean doNotify = false;
// First, remove the package from the package list (if the package is a publisher).
- if (mUser.getPackages().remove(packageName) != null) {
- doNotify = true;
+ if (packageUserId == owningUserId) {
+ if (mUser.getPackages().remove(packageName) != null) {
+ doNotify = true;
+ }
}
+
// Also remove from the launcher list (if the package is a launcher).
- mUser.getLaunchers().remove(packageName);
+ mUser.removeLauncher(packageUserId, packageName);
// Then remove pinned shortcuts from all launchers.
- for (int i = mUser.getLaunchers().size() - 1; i >= 0; i--) {
- mUser.getLaunchers().valueAt(i).cleanUpPackage(packageName);
+ final ArrayMap<PackageWithUser, ShortcutLauncher> launchers = mUser.getAllLaunchers();
+ for (int i = launchers.size() - 1; i >= 0; i--) {
+ launchers.valueAt(i).cleanUpPackage(packageName);
}
// Now there may be orphan shortcuts because we removed pinned shortucts at the previous
// step. Remove them too.
@@ -1486,18 +1523,15 @@
mUser.getPackages().valueAt(i).refreshPinnedFlags(this);
}
- // Remove the package info too.
- mUser.getPackageInfos().remove(packageName);
-
- scheduleSaveUser(userId);
+ scheduleSaveUser(owningUserId);
if (doNotify) {
- notifyListeners(packageName, userId);
+ notifyListeners(packageName, owningUserId);
}
if (!wasUserLoaded) {
// Note this will execute the scheduled save.
- unloadUserLocked(userId);
+ unloadUserLocked(owningUserId);
}
}
@@ -1506,7 +1540,7 @@
*/
private class LocalService extends ShortcutServiceInternal {
@Override
- public List<ShortcutInfo> getShortcuts(
+ public List<ShortcutInfo> getShortcuts(int launcherUserId,
@NonNull String callingPackage, long changedSince,
@Nullable String packageName, @Nullable ComponentName componentName,
int queryFlags, int userId) {
@@ -1518,14 +1552,14 @@
synchronized (mLock) {
if (packageName != null) {
- getShortcutsInnerLocked(
+ getShortcutsInnerLocked(launcherUserId,
callingPackage, packageName, changedSince,
componentName, queryFlags, userId, ret, cloneFlag);
} else {
final ArrayMap<String, ShortcutPackage> packages =
getUserShortcutsLocked(userId).getPackages();
for (int i = packages.size() - 1; i >= 0; i--) {
- getShortcutsInnerLocked(
+ getShortcutsInnerLocked(launcherUserId,
callingPackage, packages.keyAt(i), changedSince,
componentName, queryFlags, userId, ret, cloneFlag);
}
@@ -1534,7 +1568,7 @@
return ret;
}
- private void getShortcutsInnerLocked(@NonNull String callingPackage,
+ private void getShortcutsInnerLocked(int launcherUserId, @NonNull String callingPackage,
@Nullable String packageName,long changedSince,
@Nullable ComponentName componentName, int queryFlags,
int userId, ArrayList<ShortcutInfo> ret, int cloneFlag) {
@@ -1554,11 +1588,11 @@
((queryFlags & ShortcutQuery.FLAG_GET_PINNED) != 0)
&& si.isPinned();
return matchDynamic || matchPinned;
- }, cloneFlag, callingPackage);
+ }, cloneFlag, callingPackage, launcherUserId);
}
@Override
- public List<ShortcutInfo> getShortcutInfo(
+ public List<ShortcutInfo> getShortcutInfo(int launcherUserId,
@NonNull String callingPackage,
@NonNull String packageName, @Nullable List<String> ids, int userId) {
// Calling permission must be checked by LauncherAppsImpl.
@@ -1570,40 +1604,75 @@
getPackageShortcutsLocked(packageName, userId).findAll(
ShortcutService.this, ret,
(ShortcutInfo si) -> idSet.contains(si.getId()),
- ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER, callingPackage);
+ ShortcutInfo.CLONE_REMOVE_FOR_LAUNCHER, callingPackage, launcherUserId);
}
return ret;
}
@Override
- public void pinShortcuts(@NonNull String callingPackage, @NonNull String packageName,
+ public boolean isPinnedByCaller(int launcherUserId, @NonNull String callingPackage,
+ @NonNull String packageName, @NonNull String shortcutId, int userId) {
+ Preconditions.checkStringNotEmpty(packageName, "packageName");
+ Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
+
+ synchronized (mLock) {
+ final ShortcutInfo si = getShortcutInfoLocked(
+ launcherUserId, callingPackage, packageName, shortcutId, userId);
+ return si != null && si.isPinned();
+ }
+ }
+
+ public ShortcutInfo getShortcutInfoLocked(
+ int launcherUserId, @NonNull String callingPackage,
+ @NonNull String packageName, @NonNull String shortcutId, int userId) {
+ Preconditions.checkStringNotEmpty(packageName, "packageName");
+ Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
+
+ final ArrayList<ShortcutInfo> list = new ArrayList<>(1);
+ getPackageShortcutsLocked(packageName, userId).findAll(
+ ShortcutService.this, list,
+ (ShortcutInfo si) -> shortcutId.equals(si.getId()),
+ /* clone flags=*/ 0, callingPackage, launcherUserId);
+ return list.size() == 0 ? null : list.get(0);
+ }
+
+ @Override
+ public void pinShortcuts(int launcherUserId,
+ @NonNull String callingPackage, @NonNull String packageName,
@NonNull List<String> shortcutIds, int userId) {
// Calling permission must be checked by LauncherAppsImpl.
Preconditions.checkStringNotEmpty(packageName, "packageName");
Preconditions.checkNotNull(shortcutIds, "shortcutIds");
synchronized (mLock) {
- getUserShortcutsLocked(userId).ensurePackageInfo(
- ShortcutService.this, callingPackage, userId);
+ final ShortcutLauncher launcher =
+ getLauncherShortcuts(callingPackage, userId, launcherUserId);
- getLauncherShortcuts(callingPackage, userId).pinShortcuts(
- ShortcutService.this, packageName, shortcutIds);
+ launcher.ensureNotShadowAndSave(ShortcutService.this);
+
+ launcher.pinShortcuts(
+ ShortcutService.this, userId, packageName, shortcutIds);
}
userPackageChanged(packageName, userId);
}
@Override
- public Intent createShortcutIntent(@NonNull String callingPackage,
+ public Intent createShortcutIntent(int launcherUserId,
+ @NonNull String callingPackage,
@NonNull String packageName, @NonNull String shortcutId, int userId) {
// Calling permission must be checked by LauncherAppsImpl.
Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty");
Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty");
synchronized (mLock) {
- final ShortcutInfo fullShortcut =
- getPackageShortcutsLocked(packageName, userId)
- .findShortcutById(shortcutId);
- return fullShortcut == null ? null : fullShortcut.getIntent();
+ // Make sure the shortcut is actually visible to the launcher.
+ final ShortcutInfo si = getShortcutInfoLocked(
+ launcherUserId, callingPackage, packageName, shortcutId, userId);
+ // "si == null" should suffice here, but check the flags too just to make sure.
+ if (si == null || !(si.isDynamic() || si.isPinned())) {
+ return null;
+ }
+ return si.getIntent();
}
}
@@ -1615,7 +1684,8 @@
}
@Override
- public int getShortcutIconResId(@NonNull String callingPackage,
+ public int getShortcutIconResId(int launcherUserId,
+ @NonNull String callingPackage,
@NonNull ShortcutInfo shortcut, int userId) {
Preconditions.checkNotNull(shortcut, "shortcut");
@@ -1628,7 +1698,8 @@
}
@Override
- public ParcelFileDescriptor getShortcutIconFd(@NonNull String callingPackage,
+ public ParcelFileDescriptor getShortcutIconFd(int launcherUserId,
+ @NonNull String callingPackage,
@NonNull ShortcutInfo shortcutIn, int userId) {
Preconditions.checkNotNull(shortcutIn, "shortcut");
@@ -1654,8 +1725,9 @@
}
@Override
- public boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) {
- return ShortcutService.this.hasShortcutHostPermission(callingPackage, userId);
+ public boolean hasShortcutHostPermission(int launcherUserId,
+ @NonNull String callingPackage) {
+ return ShortcutService.this.hasShortcutHostPermission(callingPackage, launcherUserId);
}
}
@@ -1684,28 +1756,29 @@
* Called when a user is unlocked. Check all known packages still exist, and otherwise
* perform cleanup.
*/
- private void cleanupGonePackages(@UserIdInt int userId) {
+ @VisibleForTesting
+ void cleanupGonePackages(@UserIdInt int userId) {
if (DEBUG) {
Slog.d(TAG, "cleanupGonePackages() userId=" + userId);
}
- ArrayList<String> gonePackages = null;
+ final ArrayList<PackageWithUser> gonePackages = new ArrayList<>();
- final ShortcutUser user = getUserShortcutsLocked(userId);
- final ArrayMap<String, ShortcutPackageInfo> infos = user.getPackageInfos();
- for (int i = infos.size() -1; i >= 0; i--) {
- final ShortcutPackageInfo info = infos.valueAt(i);
- if (info.isShadow()) {
- continue;
- }
- if (isPackageInstalled(info.getPackageName(), userId)) {
- continue;
- }
- gonePackages = ArrayUtils.add(gonePackages, info.getPackageName());
- }
- if (gonePackages != null) {
- synchronized (mLock) {
+ synchronized (mLock) {
+ final ShortcutUser user = getUserShortcutsLocked(userId);
+
+ user.forAllPackageItems(spi -> {
+ if (spi.getPackageInfo().isShadow()) {
+ return; // Don't delete shadow information.
+ }
+ if (isPackageInstalled(spi.getPackageName(), spi.getPackageUserId())) {
+ return;
+ }
+ gonePackages.add(PackageWithUser.of(spi));
+ });
+ if (gonePackages.size() > 0) {
for (int i = gonePackages.size() - 1; i >= 0; i--) {
- cleanUpPackageLocked(gonePackages.get(i), userId);
+ final PackageWithUser pu = gonePackages.get(i);
+ cleanUpPackageLocked(pu.packageName, userId, pu.userId);
}
}
}
@@ -1716,26 +1789,18 @@
Slog.d(TAG, String.format("handlePackageAdded: %s user=%d", packageName, userId));
}
synchronized (mLock) {
- final ArrayMap<String, ShortcutPackageInfo> infos =
- getUserShortcutsLocked(userId).getPackageInfos();
- final ShortcutPackageInfo existing = infos.get(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).getPackageInfos().get(packageName);
- if (spi != null) {
- spi.refreshAndSave(this, userId);
- }
+ getUserShortcutsLocked(userId).unshadowPackage(this, packageName, userId);
}
}
@@ -1744,17 +1809,18 @@
Slog.d(TAG, String.format("handlePackageRemoved: %s user=%d", packageName, userId));
}
synchronized (mLock) {
- cleanUpPackageLocked(packageName, userId);
+ cleanUpPackageLocked(packageName, userId, userId);
}
}
- // === 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);
@@ -1762,12 +1828,15 @@
// 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)
@@ -1776,17 +1845,22 @@
// 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);
}
}
@@ -1795,12 +1869,61 @@
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 ===
@@ -2158,14 +2281,4 @@
return pkg.findShortcutById(shortcutId);
}
}
-
- @VisibleForTesting
- ShortcutPackageInfo getPackageInfoForTest(String packageName, int userId) {
- synchronized (mLock) {
- final ShortcutUser user = mUsers.get(userId);
- if (user == null) return null;
-
- return user.getPackageInfos().get(packageName);
- }
- }
}
diff --git a/services/core/java/com/android/server/pm/ShortcutUser.java b/services/core/java/com/android/server/pm/ShortcutUser.java
index 1a00cda..487558f 100644
--- a/services/core/java/com/android/server/pm/ShortcutUser.java
+++ b/services/core/java/com/android/server/pm/ShortcutUser.java
@@ -21,6 +21,8 @@
import android.util.ArrayMap;
import android.util.Slog;
+import com.android.internal.util.Preconditions;
+
import libcore.util.Objects;
import org.xmlpull.v1.XmlPullParser;
@@ -29,6 +31,7 @@
import java.io.IOException;
import java.io.PrintWriter;
+import java.util.function.Consumer;
/**
* User information used by {@link ShortcutService}.
@@ -41,14 +44,50 @@
private static final String ATTR_VALUE = "value";
+ static final class PackageWithUser {
+ final int userId;
+ final String packageName;
+
+ private PackageWithUser(int userId, String packageName) {
+ this.userId = userId;
+ this.packageName = Preconditions.checkNotNull(packageName);
+ }
+
+ public static PackageWithUser of(int launcherUserId, String packageName) {
+ 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;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof PackageWithUser)) {
+ return false;
+ }
+ final PackageWithUser that = (PackageWithUser) obj;
+
+ return userId == that.userId && packageName.equals(that.packageName);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("{Launcher: %d, %s}", userId, packageName);
+ }
+ }
+
@UserIdInt
final int mUserId;
private final ArrayMap<String, ShortcutPackage> mPackages = new ArrayMap<>();
- private final ArrayMap<String, ShortcutLauncher> mLaunchers = new ArrayMap<>();
-
- private final ArrayMap<String, ShortcutPackageInfo> mPackageInfos = new ArrayMap<>();
+ private final ArrayMap<PackageWithUser, ShortcutLauncher> mLaunchers = new ArrayMap<>();
private ComponentName mLauncherComponent;
@@ -60,12 +99,18 @@
return mPackages;
}
- public ArrayMap<String, ShortcutLauncher> getLaunchers() {
+ public ArrayMap<PackageWithUser, ShortcutLauncher> getAllLaunchers() {
return mLaunchers;
}
- public ArrayMap<String, ShortcutPackageInfo> getPackageInfos() {
- return mPackageInfos;
+ public void addLauncher(ShortcutLauncher launcher) {
+ mLaunchers.put(PackageWithUser.of(launcher.getPackageUserId(),
+ launcher.getPackageName()), launcher);
+ }
+
+ public ShortcutLauncher removeLauncher(
+ @UserIdInt int packageUserId, @NonNull String packageName) {
+ return mLaunchers.remove(PackageWithUser.of(packageUserId, packageName));
}
public ShortcutPackage getPackageShortcuts(@NonNull String packageName) {
@@ -77,28 +122,48 @@
return ret;
}
- public ShortcutLauncher getLauncherShortcuts(@NonNull String packageName) {
- ShortcutLauncher ret = mLaunchers.get(packageName);
+ public ShortcutLauncher getLauncherShortcuts(@NonNull String packageName,
+ @UserIdInt int launcherUserId) {
+ final PackageWithUser key = PackageWithUser.of(launcherUserId, packageName);
+ ShortcutLauncher ret = mLaunchers.get(key);
if (ret == null) {
- ret = new ShortcutLauncher(mUserId, packageName);
- mLaunchers.put(packageName, ret);
+ ret = new ShortcutLauncher(mUserId, packageName, launcherUserId);
+ mLaunchers.put(key, ret);
}
return ret;
}
- public void ensurePackageInfo(ShortcutService s, String packageName, @UserIdInt int userId) {
- final ShortcutPackageInfo existing = mPackageInfos.get(packageName);
+ public void forAllPackageItems(Consumer<ShortcutPackageItem> callback) {
+ {
+ final int size = mLaunchers.size();
+ for (int i = 0; i < size; i++) {
+ callback.accept(mLaunchers.valueAt(i));
+ }
+ }
+ {
+ final int size = mPackages.size();
+ for (int i = 0; i < size; i++) {
+ callback.accept(mPackages.valueAt(i));
+ }
+ }
+ }
- if (existing != null) {
- return;
- }
- if (ShortcutService.DEBUG) {
- Slog.d(TAG, String.format("Fetching package info: %s user=%d", packageName, userId));
- }
- final ShortcutPackageInfo newSpi = ShortcutPackageInfo.generateForInstalledPackage(
- s, packageName, userId);
- mPackageInfos.put(packageName, 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)
@@ -108,12 +173,7 @@
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++) {
@@ -132,14 +192,19 @@
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();
@@ -151,37 +216,30 @@
}
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;
- }
+ 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);
- case ShortcutLauncher.TAG_ROOT: {
- final ShortcutLauncher shortcuts =
- ShortcutLauncher.loadFromXml(parser, userId);
+ // Don't use addShortcut(), we don't need to save the icon.
+ ret.getPackages().put(shortcuts.getPackageName(), shortcuts);
+ continue;
+ }
- ret.getLaunchers().put(shortcuts.getPackageName(), shortcuts);
- continue;
- }
-
- case ShortcutPackageInfo.TAG_ROOT: {
- final ShortcutPackageInfo pi =
- ShortcutPackageInfo.loadFromXml(parser);
-
- ret.getPackageInfos().put(pi.getPackageName(), pi);
- continue;
+ case ShortcutLauncher.TAG_ROOT: {
+ ret.addLauncher(ShortcutLauncher.loadFromXml(parser, userId, fromBackup));
+ continue;
+ }
}
}
- throw ShortcutService.throwForInvalidTag(depth, tag);
+ ShortcutService.warnForInvalidTag(depth, tag);
}
return ret;
}
@@ -223,9 +281,5 @@
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/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 5263c37..7f0da1e 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -31,7 +31,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.UserInfo;
@@ -924,12 +923,12 @@
}
// Don't call them within the mRestrictionsLock.
synchronized (mPackagesLock) {
- if (globalChanged) {
- writeUserListLP();
- }
if (localChanged) {
writeUserLP(getUserDataNoChecks(userId));
}
+ if (globalChanged) {
+ writeUserListLP();
+ }
}
synchronized (mRestrictionsLock) {
@@ -1491,8 +1490,8 @@
updateUserIds();
initDefaultGuestRestrictions();
- writeUserListLP();
writeUserLP(userData);
+ writeUserListLP();
}
private String getOwnerName() {
@@ -1542,8 +1541,10 @@
serializer.attribute(null, ATTR_CREATION_TIME, Long.toString(userInfo.creationTime));
serializer.attribute(null, ATTR_LAST_LOGGED_IN_TIME,
Long.toString(userInfo.lastLoggedInTime));
- serializer.attribute(null, ATTR_LAST_LOGGED_IN_FINGERPRINT,
- userInfo.lastLoggedInFingerprint);
+ if (userInfo.lastLoggedInFingerprint != null) {
+ serializer.attribute(null, ATTR_LAST_LOGGED_IN_FINGERPRINT,
+ userInfo.lastLoggedInFingerprint);
+ }
if (userInfo.iconPath != null) {
serializer.attribute(null, ATTR_ICON_PATH, userInfo.iconPath);
}
@@ -1599,7 +1600,7 @@
serializer.endDocument();
userFile.finishWrite(fos);
} catch (Exception ioe) {
- Slog.e(LOG_TAG, "Error writing user info " + userData.info.id + "\n" + ioe);
+ Slog.e(LOG_TAG, "Error writing user info " + userData.info.id, ioe);
userFile.failWrite(fos);
}
}
@@ -1944,6 +1945,7 @@
userData.info = userInfo;
mUsers.put(userId, userData);
}
+ writeUserLP(userData);
writeUserListLP();
if (parent != null) {
if (isManagedProfile) {
@@ -2196,7 +2198,13 @@
}
private void removeUserState(final int userHandle) {
- mContext.getSystemService(StorageManager.class).destroyUserKey(userHandle);
+ try {
+ mContext.getSystemService(StorageManager.class).destroyUserKey(userHandle);
+ } catch (IllegalStateException e) {
+ // This may be simply because the user was partially created.
+ Slog.i(LOG_TAG,
+ "Destroying key for user " + userHandle + " failed, continuing anyway", e);
+ }
// Cleanup package manager settings
mPm.cleanUpUser(this, userHandle);
@@ -2211,13 +2219,13 @@
mCachedEffectiveUserRestrictions.remove(userHandle);
mDevicePolicyLocalUserRestrictions.remove(userHandle);
}
- // Remove user file
- AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + XML_SUFFIX));
- userFile.delete();
// Update the user list
synchronized (mPackagesLock) {
writeUserListLP();
}
+ // Remove user file
+ AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + XML_SUFFIX));
+ userFile.delete();
updateUserIds();
File userDir = Environment.getUserSystemDirectory(userHandle);
File renamedUserDir = Environment.getUserSystemDirectory(UserHandle.USER_NULL - userHandle);
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index 2c15818..98d44ac 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -21,6 +21,7 @@
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
+import android.annotation.NonNull;
import android.app.Service;
import android.content.Context;
import android.graphics.Canvas;
@@ -122,6 +123,12 @@
}
}
+ public void getMagnificationRegionsLocked(Region outMagnified, Region outAvailable) {
+ if (mDisplayMagnifier != null) {
+ mDisplayMagnifier.getMagnificationRegionsLocked(outMagnified, outAvailable);
+ }
+ }
+
public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
if (mDisplayMagnifier != null) {
mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle);
@@ -392,6 +399,10 @@
return spec;
}
+ public void getMagnificationRegionsLocked(Region outMagnified, Region outAvailable) {
+ mMagnifedViewport.getBoundsLocked(outMagnified, outAvailable);
+ }
+
public void destroyLocked() {
mMagnifedViewport.destroyWindow();
}
@@ -413,6 +424,7 @@
private final Matrix mTempMatrix = new Matrix();
private final Region mMagnifiedBounds = new Region();
+ private final Region mAvailableBounds = new Region();
private final Region mOldMagnifiedBounds = new Region();
private final Region mOldAvailableBounds = new Region();
@@ -450,6 +462,12 @@
recomputeBoundsLocked();
}
+ public void getBoundsLocked(@NonNull Region outMagnified,
+ @NonNull Region outAvailable) {
+ outMagnified.set(mMagnifiedBounds);
+ outAvailable.set(mAvailableBounds);
+ }
+
public void updateMagnificationSpecLocked(MagnificationSpec spec) {
if (spec != null) {
mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY);
@@ -469,14 +487,11 @@
final int screenWidth = mTempPoint.x;
final int screenHeight = mTempPoint.y;
- Region magnifiedBounds = mMagnifiedBounds;
- magnifiedBounds.set(0, 0, 0, 0);
-
- Region availableBounds = mTempRegion1;
- availableBounds.set(0, 0, screenWidth, screenHeight);
+ mMagnifiedBounds.set(0, 0, 0, 0);
+ mAvailableBounds.set(0, 0, screenWidth, screenHeight);
if (mCircularPath != null) {
- availableBounds.setPath(mCircularPath, availableBounds);
+ mAvailableBounds.setPath(mCircularPath, mAvailableBounds);
}
Region nonMagnifiedBounds = mTempRegion4;
@@ -505,8 +520,8 @@
matrix.mapRect(windowFrame);
windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
(int) windowFrame.right, (int) windowFrame.bottom);
- magnifiedBounds.op(windowBounds, Region.Op.UNION);
- magnifiedBounds.op(availableBounds, Region.Op.INTERSECT);
+ mMagnifiedBounds.op(windowBounds, Region.Op.UNION);
+ mMagnifiedBounds.op(mAvailableBounds, Region.Op.INTERSECT);
} else {
Region touchableRegion = mTempRegion3;
windowState.getTouchableRegion(touchableRegion);
@@ -518,12 +533,12 @@
windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
(int) windowFrame.right, (int) windowFrame.bottom);
nonMagnifiedBounds.op(windowBounds, Region.Op.UNION);
- windowBounds.op(magnifiedBounds, Region.Op.DIFFERENCE);
- availableBounds.op(windowBounds, Region.Op.DIFFERENCE);
+ windowBounds.op(mMagnifiedBounds, Region.Op.DIFFERENCE);
+ mAvailableBounds.op(windowBounds, Region.Op.DIFFERENCE);
}
Region accountedBounds = mTempRegion2;
- accountedBounds.set(magnifiedBounds);
+ accountedBounds.set(mMagnifiedBounds);
accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION);
accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT);
@@ -539,15 +554,15 @@
visibleWindows.clear();
- magnifiedBounds.op(mDrawBorderInset, mDrawBorderInset,
+ mMagnifiedBounds.op(mDrawBorderInset, mDrawBorderInset,
screenWidth - mDrawBorderInset, screenHeight - mDrawBorderInset,
Region.Op.INTERSECT);
- final boolean magnifiedChanged = !mOldMagnifiedBounds.equals(magnifiedBounds);
- final boolean availableChanged = !mOldAvailableBounds.equals(availableBounds);
+ final boolean magnifiedChanged = !mOldMagnifiedBounds.equals(mMagnifiedBounds);
+ final boolean availableChanged = !mOldAvailableBounds.equals(mAvailableBounds);
if (magnifiedChanged || availableChanged) {
if (magnifiedChanged) {
- mWindow.setBounds(magnifiedBounds);
+ mWindow.setBounds(mMagnifiedBounds);
Rect dirtyRect = mTempRect1;
if (mFullRedrawNeeded) {
mFullRedrawNeeded = false;
@@ -557,23 +572,23 @@
mWindow.invalidate(dirtyRect);
} else {
Region dirtyRegion = mTempRegion3;
- dirtyRegion.set(magnifiedBounds);
+ dirtyRegion.set(mMagnifiedBounds);
dirtyRegion.op(mOldMagnifiedBounds, Region.Op.UNION);
dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT);
dirtyRegion.getBounds(dirtyRect);
mWindow.invalidate(dirtyRect);
}
- mOldMagnifiedBounds.set(magnifiedBounds);
+ mOldMagnifiedBounds.set(mMagnifiedBounds);
}
if (availableChanged) {
- mOldAvailableBounds.set(availableBounds);
+ mOldAvailableBounds.set(mAvailableBounds);
}
final SomeArgs args = SomeArgs.obtain();
- args.arg1 = Region.obtain(magnifiedBounds);
- args.arg2 = Region.obtain(availableBounds);
+ args.arg1 = Region.obtain(mMagnifiedBounds);
+ args.arg2 = Region.obtain(mAvailableBounds);
mHandler.obtainMessage(
MyHandler.MESSAGE_NOTIFY_MAGNIFIED_BOUNDS_CHANGED, args).sendToTarget();
}
diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java
index 6741aba..68ea4df 100644
--- a/services/core/java/com/android/server/wm/DockedStackDividerController.java
+++ b/services/core/java/com/android/server/wm/DockedStackDividerController.java
@@ -41,6 +41,8 @@
import com.android.server.wm.DimLayer.DimLayerUser;
+import java.util.ArrayList;
+
/**
* Keeps information about the docked stack divider.
*/
@@ -87,7 +89,7 @@
private final DimLayer mDimLayer;
private boolean mMinimizedDock;
- private boolean mAnimating;
+ private boolean mAnimatingForMinimizedDockedStack;
private boolean mAnimationStarted;
private long mAnimationStartTime;
private float mAnimationStart;
@@ -96,7 +98,8 @@
private final Interpolator mMinimizedDockInterpolator;
private float mMaximizeMeetFraction;
private final Rect mTouchRegion = new Rect();
- private boolean mAdjustingForIme;
+ private boolean mAnimatingForIme;
+ private boolean mAdjustedForIme;
DockedStackDividerController(WindowManagerService service, DisplayContent displayContent) {
mService = service;
@@ -174,12 +177,11 @@
return mLastVisibility;
}
- void setAdjustingForIme(boolean adjusting) {
- mAdjustingForIme = adjusting;
- }
-
- boolean isAdjustingForIme() {
- return mAdjustingForIme;
+ void setAdjustedForIme(boolean adjusted, boolean animate) {
+ if (mAdjustedForIme != adjusted) {
+ mAnimatingForIme = animate;
+ mAdjustedForIme = adjusted;
+ }
}
void positionDockedStackedDivider(Rect frame) {
@@ -342,6 +344,7 @@
}
mMinimizedDock = minimizedDock;
+ mAnimatingForIme = false;
if (minimizedDock) {
if (animate) {
startAdjustAnimation(0f, 1f);
@@ -358,7 +361,7 @@
}
private void startAdjustAnimation(float from, float to) {
- mAnimating = true;
+ mAnimatingForMinimizedDockedStack = true;
mAnimationStarted = false;
mAnimationStart = from;
mAnimationTarget = to;
@@ -380,10 +383,45 @@
}
public boolean animate(long now) {
- if (!mAnimating) {
+ if (mAnimatingForMinimizedDockedStack) {
+ return animateForMinimizedDockedStack(now);
+ } else if (mAnimatingForIme) {
+ return animateForIme();
+ } else {
return false;
}
+ }
+ private boolean animateForIme() {
+ boolean updated = false;
+ boolean animating = false;
+
+ final ArrayList<TaskStack> stacks = mDisplayContent.getStacks();
+ for (int i = stacks.size() - 1; i >= 0; --i) {
+ final TaskStack stack = stacks.get(i);
+ if (stack != null && stack.isAdjustedForIme()) {
+ updated |= stack.updateAdjustForIme();
+ animating |= stack.isAnimatingForIme();
+ }
+ }
+
+ if (updated) {
+ mService.mWindowPlacerLocked.performSurfacePlacement();
+ }
+
+ if (!animating) {
+ mAnimatingForIme = false;
+ for (int i = stacks.size() - 1; i >= 0; --i) {
+ final TaskStack stack = stacks.get(i);
+ if (stack != null) {
+ stack.clearImeGoingAway();
+ }
+ }
+ }
+ return animating;
+ }
+
+ private boolean animateForMinimizedDockedStack(long now) {
final TaskStack stack = mDisplayContent.getDockedStackVisibleForUserLocked();
if (!mAnimationStarted) {
mAnimationStarted = true;
@@ -406,7 +444,7 @@
}
}
if (t >= 1.0f) {
- mAnimating = false;
+ mAnimatingForMinimizedDockedStack = false;
return false;
} else {
return true;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 4e8f19e..1f03c04 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -498,7 +498,15 @@
return;
}
- out.set(mBounds);
+ if (!mFullscreen) {
+ // When minimizing the docked stack when going home, we don't adjust the task bounds
+ // so we need to intersect the task bounds with the stack bounds here.
+ mStack.getBounds(mTmpRect);
+ mTmpRect.intersect(mBounds);
+ out.set(mTmpRect);
+ } else {
+ out.set(mBounds);
+ }
return;
}
diff --git a/services/core/java/com/android/server/wm/TaskStack.java b/services/core/java/com/android/server/wm/TaskStack.java
index 8d67771..0bf7102 100644
--- a/services/core/java/com/android/server/wm/TaskStack.java
+++ b/services/core/java/com/android/server/wm/TaskStack.java
@@ -55,6 +55,11 @@
// If the stack should be resized to fullscreen.
private static final boolean FULLSCREEN = true;
+ // When we have a top-bottom split screen, we shift the bottom stack up to accommodate
+ // the IME window. The static flag below controls whether to run animation when the
+ // IME window goes away.
+ private static final boolean ANIMATE_IME_GOING_AWAY = false;
+
/** Unique identifier */
final int mStackId;
@@ -107,6 +112,7 @@
private final Rect mLastContentBounds = new Rect();
private final Rect mTmpAdjustedBounds = new Rect();
private boolean mAdjustedForIme;
+ private boolean mImeGoingAway;
private WindowState mImeWin;
private float mMinimizeAmount;
private final int mDockedStackMinimizeThickness;
@@ -796,19 +802,54 @@
void setAdjustedForIme(WindowState imeWin) {
mAdjustedForIme = true;
mImeWin = imeWin;
- if (updateAdjustedBounds()) {
- getDisplayContent().mDividerControllerLocked.setAdjustingForIme(true);
+ mImeGoingAway = false;
+ }
+
+ boolean isAdjustedForIme() {
+ return mAdjustedForIme || mImeGoingAway;
+ }
+ void clearImeGoingAway() {
+ mImeGoingAway = false;
+ }
+
+ boolean isAnimatingForIme() {
+ return mImeWin != null && mImeWin.isAnimatingLw();
+ }
+
+ /**
+ * Update the stack's bounds (crop or position) according to the IME window's
+ * current position. When IME window is animated, the bottom stack is animated
+ * together to track the IME window's current position, and the top stack is
+ * cropped as necessary.
+ *
+ * @return true if a traversal should be performed after the adjustment.
+ */
+ boolean updateAdjustForIme() {
+ boolean stopped = false;
+ if (mImeGoingAway && (!ANIMATE_IME_GOING_AWAY || !isAnimatingForIme())) {
+ mImeWin = null;
+ mAdjustedForIme = false;
+ stopped = true;
}
+ // Make sure to run a traversal when the animation stops so that the stack
+ // is moved to its final position.
+ return updateAdjustedBounds() || stopped;
}
/**
* Resets the adjustment after it got adjusted for the IME.
+ * @param adjustBoundsNow if true, reset and update the bounds immediately and forget about
+ * animations; otherwise, set flag and animates the window away together
+ * with IME window.
*/
- void resetAdjustedForIme() {
- mAdjustedForIme = false;
- mImeWin = null;
- if (updateAdjustedBounds()) {
- getDisplayContent().mDividerControllerLocked.setAdjustingForIme(true);
+ void resetAdjustedForIme(boolean adjustBoundsNow) {
+ if (adjustBoundsNow) {
+ mImeWin = null;
+ mAdjustedForIme = false;
+ mImeGoingAway = false;
+ updateAdjustedBounds();
+ } else {
+ mImeGoingAway |= mAdjustedForIme;
}
}
@@ -843,6 +884,12 @@
getDisplayContent().getContentRect(displayContentRect);
contentBounds.set(displayContentRect);
int imeTop = Math.max(imeWin.getDisplayFrameLw().top, contentBounds.top);
+
+ // if IME window is animating, get its actual vertical shown position (but no smaller than
+ // the final target vertical position)
+ if (imeWin.isAnimatingLw()) {
+ imeTop = Math.max(imeTop, imeWin.getShownPositionLw().y);
+ }
imeTop += imeWin.getGivenContentInsetsLw().top;
if (contentBounds.bottom > imeTop) {
contentBounds.bottom = imeTop;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a3e7976..1a9e206 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -7378,8 +7378,9 @@
final WindowState imeWin = mInputMethodWindow;
final TaskStack focusedStack =
mCurrentFocus != null ? mCurrentFocus.getStack() : null;
+ final boolean dockVisible = isStackVisibleLocked(DOCKED_STACK_ID);
if (imeWin != null && imeWin.isVisibleLw() && imeWin.isDisplayedLw()
- && isStackVisibleLocked(DOCKED_STACK_ID)
+ && dockVisible
&& focusedStack != null
&& focusedStack.getDockSide() == DOCKED_BOTTOM){
final ArrayList<TaskStack> stacks = displayContent.getStacks();
@@ -7389,12 +7390,14 @@
stack.setAdjustedForIme(imeWin);
}
}
+ displayContent.mDividerControllerLocked.setAdjustedForIme(true, true);
} else {
final ArrayList<TaskStack> stacks = displayContent.getStacks();
for (int i = stacks.size() - 1; i >= 0; --i) {
final TaskStack stack = stacks.get(i);
- stack.resetAdjustedForIme();
+ stack.resetAdjustedForIme(!dockVisible);
}
+ displayContent.mDividerControllerLocked.setAdjustedForIme(false, dockVisible);
}
}
@@ -10723,6 +10726,19 @@
}
@Override
+ public void getMagnificationRegions(@NonNull Region outMagnified,
+ @NonNull Region outAvailable) {
+ synchronized (mWindowMap) {
+ if (mAccessibilityController != null) {
+ mAccessibilityController.getMagnificationRegionsLocked(
+ outMagnified, outAvailable);
+ } else {
+ throw new IllegalStateException("Magnification callbacks not set!");
+ }
+ }
+ }
+
+ @Override
public MagnificationSpec getCompatibleMagnificationSpecForWindow(IBinder windowToken) {
synchronized (mWindowMap) {
WindowState windowState = mWindowMap.get(windowToken);
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 3b0081d..3e5ddbc 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -693,16 +693,14 @@
// currently animating... let's do something.
final int left = w.mFrame.left;
final int top = w.mFrame.top;
- final boolean adjustedForMinimizedDockedStack = w.getTask() != null &&
- w.getTask().mStack.isAdjustedForMinimizedDockedStack();
+ final boolean adjustedForMinimizedDockOrIme = task != null
+ && (task.mStack.isAdjustedForMinimizedDockedStack()
+ || task.mStack.isAdjustedForIme());
if ((w.mAttrs.privateFlags & PRIVATE_FLAG_NO_MOVE_ANIMATION) == 0
- && !w.isDragResizing() && !adjustedForMinimizedDockedStack
+ && !w.isDragResizing() && !adjustedForMinimizedDockOrIme
&& (task == null || !w.getTask().mStack.getFreezeMovementAnimations())
&& !w.mWinAnimator.mLastHidden) {
winAnimator.setMoveAnimation(left, top);
- } else if (w.mAttrs.type == TYPE_DOCK_DIVIDER &&
- displayContent.getDockedDividerController().isAdjustingForIme()) {
- winAnimator.setMoveAnimation(left, top);
}
//TODO (multidisplay): Accessibility supported only for the default display.
@@ -819,8 +817,6 @@
mService.updateResizingWindows(w);
}
- displayContent.getDockedDividerController().setAdjustingForIme(false);
-
mService.mDisplayManagerInternal.setDisplayProperties(displayId,
mDisplayHasContent,
mPreferredRefreshRate,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index d8b856b..b2cd69d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -4870,8 +4870,8 @@
final ApplicationInfo ai;
try {
- ai = mContext.getPackageManager().getApplicationInfo(callerPackage, 0);
- } catch (NameNotFoundException e) {
+ ai = mIPackageManager.getApplicationInfo(callerPackage, 0, userHandle);
+ } catch (RemoteException e) {
throw new SecurityException(e);
}
@@ -4880,7 +4880,7 @@
legacyApp = true;
} else if ("com.google.android.apps.enterprise.dmagent".equals(ai.packageName)
&& ai.versionCode == 697) {
- // TODO: remove this once a new prebuilt is dropped
+ // TODO: STOPSHIP remove this (revert ag/895987) once a new prebuilt is dropped
legacyApp = true;
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 5975405..0a4effb 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -153,6 +153,8 @@
"com.android.server.MountService$Lifecycle";
private static final String SEARCH_MANAGER_SERVICE_CLASS =
"com.android.server.search.SearchManagerService$Lifecycle";
+ private static final String THERMAL_OBSERVER_CLASS =
+ "com.google.android.clockwork.ThermalObserver";
private static final String PERSISTENT_DATA_BLOCK_PROP = "ro.frp.pst";
@@ -959,7 +961,7 @@
if (context.getPackageManager().hasSystemFeature
(PackageManager.FEATURE_WATCH)) {
- mSystemServiceManager.startService(ThermalObserver.class);
+ mSystemServiceManager.startService(THERMAL_OBSERVER_CLASS);
}
}
@@ -1023,7 +1025,8 @@
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/net/java/android/net/apf/ApfCapabilities.java b/services/net/java/android/net/apf/ApfCapabilities.java
index f169411..0ec50c4 100644
--- a/services/net/java/android/net/apf/ApfCapabilities.java
+++ b/services/net/java/android/net/apf/ApfCapabilities.java
@@ -43,4 +43,9 @@
this.maximumApfProgramSize = maximumApfProgramSize;
this.apfPacketFormat = apfPacketFormat;
}
-}
\ No newline at end of file
+
+ public String toString() {
+ return String.format("%s{version: %d, maxSize: %d format: %d}", getClass().getSimpleName(),
+ apfVersionSupported, maximumApfProgramSize, apfPacketFormat);
+ }
+}
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
index 66c7909..c25fae3 100644
--- a/services/net/java/android/net/ip/IpManager.java
+++ b/services/net/java/android/net/ip/IpManager.java
@@ -45,6 +45,7 @@
import com.android.internal.util.StateMachine;
import com.android.server.net.NetlinkTracker;
+import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.NetworkInterface;
@@ -211,6 +212,8 @@
}
}
+ public static final String DUMP_ARG = "ipmanager";
+
private static final int CMD_STOP = 1;
private static final int CMD_START = 2;
private static final int CMD_CONFIRM = 3;
@@ -393,17 +396,18 @@
}
}
- public void dumpApf(PrintWriter writer) {
- writer.println("--------------------------------------------------------------------");
- writer.println("APF dump:");
+ public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
+ IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
+ pw.println("APF dump:");
+ pw.increaseIndent();
// Thread-unsafe access to mApfFilter but just used for debugging.
ApfFilter apfFilter = mApfFilter;
if (apfFilter != null) {
- apfFilter.dump(new IndentingPrintWriter(writer, " "));
+ apfFilter.dump(pw);
} else {
- writer.println("No apf support");
+ pw.println("No apf support");
}
- writer.println("--------------------------------------------------------------------");
+ pw.decreaseIndent();
}
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 bd4ed16..f034d55 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ShortcutManagerTest.java
@@ -20,11 +20,14 @@
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.app.Activity;
import android.content.BroadcastReceiver;
@@ -72,6 +75,7 @@
import com.android.server.pm.LauncherAppsService.LauncherAppsImpl;
import com.android.server.pm.ShortcutService.ConfigConstants;
import com.android.server.pm.ShortcutService.FileOutputStreamWithPath;
+import com.android.server.pm.ShortcutUser.PackageWithUser;
import libcore.io.IoUtils;
@@ -118,7 +122,8 @@
*/
private static final boolean ENABLE_DUMP = false; // DO NOT SUBMIT WITH true
- private class BaseContext extends MockContext {
+ // public for mockito
+ public class BaseContext extends MockContext {
@Override
public Object getSystemService(String name) {
switch (name) {
@@ -152,7 +157,7 @@
}
/** Context used in the client side */
- private class ClientContext extends BaseContext {
+ public class ClientContext extends BaseContext {
@Override
public String getPackageName() {
return mInjectedClientPackage;
@@ -160,7 +165,7 @@
}
/** Context used in the service side */
- private final class ServiceContext extends BaseContext {
+ public class ServiceContext extends BaseContext {
long injectClearCallingIdentity() {
final int prevCallingUid = mInjectedCallingUid;
mInjectedCallingUid = Process.SYSTEM_UID;
@@ -170,6 +175,11 @@
void injectRestoreCallingIdentity(long token) {
mInjectedCallingUid = (int) token;
}
+
+ @Override
+ public void startActivityAsUser(@RequiresPermission Intent intent, @Nullable Bundle options,
+ UserHandle userId) {
+ }
}
/** ShortcutService with injection override methods. */
@@ -310,9 +320,17 @@
if (getCallingUserId() == userToCheck.getIdentifier()) {
return; // okay
}
+ if (getCallingUserId() == USER_0 && userToCheck.getIdentifier() == USER_P0) {
+ return; // profile, okay.
+ }
+ if (getCallingUserId() == USER_P0 && userToCheck.getIdentifier() == USER_0) {
+ return; // profile, okay.
+ }
- assertEquals(Process.SYSTEM_UID, mInjectedCallingUid);
- // SKIP
+ if (mInjectedCallingUid != Process.SYSTEM_UID) {
+ throw new SecurityException("To access other users, you need to be SYSTEM" +
+ ", but current UID=" + mInjectedCallingUid);
+ }
}
@Override
@@ -333,6 +351,11 @@
r.run();
mContext.injectRestoreCallingIdentity(token);
}
+
+ @Override
+ int injectBinderCallingUid() {
+ return mInjectedCallingUid;
+ }
}
private class LauncherAppsTestable extends LauncherApps {
@@ -371,6 +394,8 @@
private Map<String, PackageInfo> mInjectedPackages;
+ private ArrayList<PackageWithUser> mUninstalledPackages;
+
private PackageManager mMockPackageManager;
private PackageManagerInternal mMockPackageManagerInternal;
private UserManager mMockUserManager;
@@ -393,6 +418,13 @@
private static final int USER_0 = UserHandle.USER_SYSTEM;
private static final int USER_10 = 10;
private static final int USER_11 = 11;
+ private static final int USER_P0 = 20; // profile of user 0
+
+ private static final UserHandle HANDLE_USER_0 = UserHandle.of(USER_0);
+ private static final UserHandle HANDLE_USER_10 = UserHandle.of(USER_10);
+ private static final UserHandle HANDLE_USER_11 = UserHandle.of(USER_11);
+ private static final UserHandle HANDLE_USER_P0 = UserHandle.of(USER_P0);
+
private static final long START_TIME = 1440000000101L;
@@ -410,7 +442,7 @@
protected void setUp() throws Exception {
super.setUp();
- mServiceContext = new ServiceContext();
+ mServiceContext = spy(new ServiceContext());
mClientContext = new ClientContext();
mMockPackageManager = mock(PackageManager.class);
@@ -428,6 +460,8 @@
addPackage(LAUNCHER_1, LAUNCHER_UID_1, 4);
addPackage(LAUNCHER_2, LAUNCHER_UID_2, 5);
+ mUninstalledPackages = new ArrayList<>();
+
mInjectedFilePathRoot = new File(getTestContext().getCacheDir(), "test-files");
// Empty the data directory.
@@ -466,6 +500,10 @@
addPackage(packageName, uid, version, packageName);
}
+ private <T> List<T> list(T... array) {
+ return Arrays.asList(array);
+ }
+
private Signature[] genSignatures(String... signatures) {
final Signature[] sigs = new Signature[signatures.length];
for (int i = 0; i < signatures.length; i++){
@@ -491,6 +529,10 @@
mInjectedPackages.put(packageName, genPackage(packageName, uid, version, signatures));
}
+ private void uninstallPackage(int userId, String packageName) {
+ mUninstalledPackages.add(PackageWithUser.of(userId, packageName));
+ }
+
PackageInfo getInjectedPackageInfo(String packageName, @UserIdInt int userId,
boolean getSignatures) {
final PackageInfo pi = mInjectedPackages.get(packageName);
@@ -501,6 +543,9 @@
ret.versionCode = pi.versionCode;
ret.applicationInfo = new ApplicationInfo(pi.applicationInfo);
ret.applicationInfo.uid = UserHandle.getUid(userId, pi.applicationInfo.uid);
+ if (mUninstalledPackages.contains(PackageWithUser.of(userId, packageName))) {
+ ret.applicationInfo.flags &= ~ApplicationInfo.FLAG_INSTALLED;
+ }
if (getSignatures) {
ret.signatures = pi.signatures;
@@ -752,8 +797,7 @@
@NonNull
private List<ShortcutInfo> assertShortcutIds(@NonNull List<ShortcutInfo> actualShortcuts,
String... expectedIds) {
- assertEquals(expectedIds.length, actualShortcuts.size());
- final HashSet<String> expected = new HashSet<>(Arrays.asList(expectedIds));
+ final HashSet<String> expected = new HashSet<>(list(expectedIds));
final HashSet<String> actual = new HashSet<>();
for (ShortcutInfo s : actualShortcuts) {
actual.add(s.getId());
@@ -884,6 +928,21 @@
return actualShortcuts;
}
+ private void assertDynamicOnly(ShortcutInfo si) {
+ assertTrue(si.isDynamic());
+ assertFalse(si.isPinned());
+ }
+
+ private void assertPinnedOnly(ShortcutInfo si) {
+ assertFalse(si.isDynamic());
+ assertTrue(si.isPinned());
+ }
+
+ private void assertDynamicAndPinned(ShortcutInfo si) {
+ assertTrue(si.isDynamic());
+ assertTrue(si.isPinned());
+ }
+
private void assertBitmapSize(int expectedWidth, int expectedHeight, @NonNull Bitmap bitmap) {
assertEquals("width", expectedWidth, bitmap.getWidth());
assertEquals("height", expectedHeight, bitmap.getHeight());
@@ -913,18 +972,6 @@
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);
}
@@ -937,6 +984,39 @@
assertTrue(getPackageShortcut(packageName, shortcutId, userId) == null);
}
+ private Intent launchShortcutAndGetIntent(
+ @NonNull String packageName, @NonNull String shortcutId, int userId) {
+ reset(mServiceContext);
+ assertTrue(mLauncherApps.startShortcut(packageName, shortcutId, null, null,
+ UserHandle.of(userId)));
+
+ final ArgumentCaptor<Intent> intentCaptor = ArgumentCaptor.forClass(Intent.class);
+ verify(mServiceContext).startActivityAsUser(
+ intentCaptor.capture(),
+ any(Bundle.class),
+ eq(UserHandle.of(userId)));
+ return intentCaptor.getValue();
+ }
+
+ private void assertShortcutLaunchable(@NonNull String packageName, @NonNull String shortcutId,
+ int userId) {
+ assertNotNull(launchShortcutAndGetIntent(packageName, shortcutId, userId));
+ }
+
+ private void assertShortcutNotLaunchable(@NonNull String packageName,
+ @NonNull String shortcutId, int userId) {
+ try {
+ final boolean ok = mLauncherApps.startShortcut(packageName, shortcutId, null, null,
+ UserHandle.of(userId));
+ if (!ok) {
+ return; // didn't launch, okay.
+ }
+ fail();
+ } catch (SecurityException expected) {
+ // security exception is okay too.
+ }
+ }
+
private ShortcutInfo getPackageShortcut(String packageName, String shortcutId) {
return getPackageShortcut(packageName, shortcutId, getCallingUserId());
}
@@ -1130,26 +1210,21 @@
/* weight */ 12);
final ShortcutInfo si3 = makeShortcut("shortcut3");
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1, si2)));
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()),
"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(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()),
"shortcut1");
assertEquals(1, mManager.getRemainingCallCount());
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList()));
+ assertTrue(mManager.setDynamicShortcuts(list()));
assertEquals(0, mManager.getDynamicShortcuts().size());
assertEquals(0, mManager.getRemainingCallCount());
@@ -1160,18 +1235,13 @@
dumpsysOnLogcat();
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si2, si3)));
+ assertTrue(mManager.setDynamicShortcuts(list(si2, si3)));
assertEquals(2, mManager.getDynamicShortcuts().size());
// TODO Check max number
runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(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);
+ assertTrue(mManager.setDynamicShortcuts(list(makeShortcut("s1"))));
});
}
@@ -1184,7 +1254,7 @@
assertEquals(3, mManager.getRemainingCallCount());
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertEquals(2, mManager.getRemainingCallCount());
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()),
@@ -1209,7 +1279,6 @@
runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
assertTrue(mManager.addDynamicShortcut(makeShortcut("s1")));
- assertShortcutPackageInfo(CALLING_PACKAGE_2, USER_10, 2);
});
}
@@ -1218,7 +1287,7 @@
final ShortcutInfo si2 = makeShortcut("shortcut2");
final ShortcutInfo si3 = makeShortcut("shortcut3");
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2, si3)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1, si2, si3)));
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()),
"shortcut1", "shortcut2", "shortcut3");
@@ -1260,7 +1329,7 @@
final ShortcutInfo si2 = makeShortcut("shortcut2");
final ShortcutInfo si3 = makeShortcut("shortcut3");
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2, si3)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1, si2, si3)));
assertShortcutIds(assertAllNotKeyFieldsOnly(
mManager.getDynamicShortcuts()),
"shortcut1", "shortcut2", "shortcut3");
@@ -1277,7 +1346,7 @@
assertEquals(0, mManager.getDynamicShortcuts().size());
// This should still work.
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2, si3)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1, si2, si3)));
assertEquals(3, mManager.getDynamicShortcuts().size());
// Still 1 call left
@@ -1289,62 +1358,62 @@
public void testThrottling() {
final ShortcutInfo si1 = makeShortcut("shortcut1");
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertEquals(2, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
mInjectedCurrentTimeLillis++;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertEquals(1, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
mInjectedCurrentTimeLillis++;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertEquals(0, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
// Reached the max
mInjectedCurrentTimeLillis++;
- assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertFalse(mManager.setDynamicShortcuts(list(si1)));
assertEquals(0, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
// Still throttled
mInjectedCurrentTimeLillis = START_TIME + INTERVAL - 1;
- assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertFalse(mManager.setDynamicShortcuts(list(si1)));
assertEquals(0, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
// Now it should work.
mInjectedCurrentTimeLillis++;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1))); // fail
+ assertTrue(mManager.setDynamicShortcuts(list(si1))); // fail
assertEquals(2, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime());
mInjectedCurrentTimeLillis++;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertEquals(1, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime());
mInjectedCurrentTimeLillis++;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertEquals(0, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime());
mInjectedCurrentTimeLillis++;
- assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertFalse(mManager.setDynamicShortcuts(list(si1)));
assertEquals(0, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL * 2, mManager.getRateLimitResetTime());
// 4 days later...
mInjectedCurrentTimeLillis = START_TIME + 4 * INTERVAL;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertEquals(2, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL * 5, mManager.getRateLimitResetTime());
mInjectedCurrentTimeLillis++;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertEquals(1, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL * 5, mManager.getRateLimitResetTime());
@@ -1354,7 +1423,7 @@
assertEquals(START_TIME + INTERVAL * 9, mManager.getRateLimitResetTime());
mInjectedCurrentTimeLillis++;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertEquals(2, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL * 9, mManager.getRateLimitResetTime());
}
@@ -1362,7 +1431,7 @@
public void testThrottling_rewind() {
final ShortcutInfo si1 = makeShortcut("shortcut1");
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertEquals(2, mManager.getRemainingCallCount());
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
@@ -1381,7 +1450,7 @@
mInjectedCurrentTimeLillis = START_TIME - 100000;
assertEquals(3, mManager.getRemainingCallCount());
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertEquals(2, mManager.getRemainingCallCount());
// Forward again, should be reset now.
@@ -1392,21 +1461,21 @@
public void testThrottling_perPackage() {
final ShortcutInfo si1 = makeShortcut("shortcut1");
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertEquals(2, mManager.getRemainingCallCount());
mInjectedCurrentTimeLillis++;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertEquals(1, mManager.getRemainingCallCount());
mInjectedCurrentTimeLillis++;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
assertEquals(0, mManager.getRemainingCallCount());
// Reached the max
mInjectedCurrentTimeLillis++;
- assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertFalse(mManager.setDynamicShortcuts(list(si1)));
// Try from a different caller.
mInjectedClientPackage = CALLING_PACKAGE_2;
@@ -1417,11 +1486,11 @@
assertEquals(3, mManager.getRemainingCallCount());
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si2)));
+ assertTrue(mManager.setDynamicShortcuts(list(si2)));
assertEquals(2, mManager.getRemainingCallCount());
mInjectedCurrentTimeLillis++;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si2)));
+ assertTrue(mManager.setDynamicShortcuts(list(si2)));
assertEquals(1, mManager.getRemainingCallCount());
// Back to the original caller, still throttled.
@@ -1430,37 +1499,37 @@
mInjectedCurrentTimeLillis = START_TIME + INTERVAL - 1;
assertEquals(0, mManager.getRemainingCallCount());
- assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertFalse(mManager.setDynamicShortcuts(list(si1)));
assertEquals(0, mManager.getRemainingCallCount());
// Now it should work.
mInjectedCurrentTimeLillis++;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
mInjectedCurrentTimeLillis++;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
mInjectedCurrentTimeLillis++;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
mInjectedCurrentTimeLillis++;
- assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertFalse(mManager.setDynamicShortcuts(list(si1)));
mInjectedCurrentTimeLillis = START_TIME + 4 * INTERVAL;
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1)));
- assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1)));
+ assertFalse(mManager.setDynamicShortcuts(list(si1)));
mInjectedClientPackage = CALLING_PACKAGE_2;
mInjectedCallingUid = CALLING_UID_2;
assertEquals(3, mManager.getRemainingCallCount());
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si2)));
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si2)));
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si2)));
- assertFalse(mManager.setDynamicShortcuts(Arrays.asList(si2)));
+ assertTrue(mManager.setDynamicShortcuts(list(si2)));
+ assertTrue(mManager.setDynamicShortcuts(list(si2)));
+ assertTrue(mManager.setDynamicShortcuts(list(si2)));
+ assertFalse(mManager.setDynamicShortcuts(list(si2)));
}
public void testIcons() {
@@ -1477,7 +1546,7 @@
// Set from package 1
setCaller(CALLING_PACKAGE_1);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ assertTrue(mManager.setDynamicShortcuts(list(
makeShortcutWithIcon("res32x32", res32x32),
makeShortcutWithIcon("res64x64", res64x64),
makeShortcutWithIcon("bmp32x32", bmp32x32),
@@ -1497,7 +1566,7 @@
// Call from another caller with the same ID, just to make sure storage is per-package.
setCaller(CALLING_PACKAGE_2);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ assertTrue(mManager.setDynamicShortcuts(list(
makeShortcutWithIcon("res32x32", res512x512),
makeShortcutWithIcon("res64x64", res512x512),
makeShortcutWithIcon("none", res512x512)
@@ -1515,26 +1584,25 @@
Bitmap bmp;
setCaller(LAUNCHER_1);
-
// Check hasIconResource()/hasIconFile().
assertShortcutIds(assertAllHaveIconResId(mLauncherApps.getShortcutInfo(
- CALLING_PACKAGE_1, Arrays.asList("res32x32"),
+ CALLING_PACKAGE_1, list("res32x32"),
getCallingUser())), "res32x32");
assertShortcutIds(assertAllHaveIconResId(mLauncherApps.getShortcutInfo(
- CALLING_PACKAGE_1, Arrays.asList("res64x64"), getCallingUser())),
+ CALLING_PACKAGE_1, list("res64x64"), getCallingUser())),
"res64x64");
assertShortcutIds(assertAllHaveIconFile(mLauncherApps.getShortcutInfo(
- CALLING_PACKAGE_1, Arrays.asList("bmp32x32"), getCallingUser())),
+ CALLING_PACKAGE_1, list("bmp32x32"), getCallingUser())),
"bmp32x32");
assertShortcutIds(assertAllHaveIconFile(mLauncherApps.getShortcutInfo(
- CALLING_PACKAGE_1, Arrays.asList("bmp64x64"), getCallingUser())),
+ CALLING_PACKAGE_1, list("bmp64x64"), getCallingUser())),
"bmp64x64");
assertShortcutIds(assertAllHaveIconFile(mLauncherApps.getShortcutInfo(
- CALLING_PACKAGE_1, Arrays.asList("bmp512x512"), getCallingUser())),
+ CALLING_PACKAGE_1, list("bmp512x512"), getCallingUser())),
"bmp512x512");
// Check
@@ -1629,7 +1697,7 @@
final File p11_1_3 = openIconFileForWriteAndGetPath(11, CALLING_PACKAGE_1);
// Make sure their paths are all unique
- assertAllUnique(Arrays.asList(
+ assertAllUnique(list(
p10_1_1,
p10_1_2,
p10_1_3,
@@ -1658,7 +1726,7 @@
assertEquals(p11_1_1.getParent(), p11_1_3.getParent());
// Check the parents are still unique.
- assertAllUnique(Arrays.asList(
+ assertAllUnique(list(
p10_1_1.getParent(),
p10_2_1.getParent(),
p11_1_1.getParent()
@@ -1683,7 +1751,7 @@
public void testUpdateShortcuts() {
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"),
makeShortcut("s2"),
makeShortcut("s3"),
@@ -1692,7 +1760,7 @@
)));
});
runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"),
makeShortcut("s2"),
makeShortcut("s3"),
@@ -1701,9 +1769,9 @@
)));
});
runWithCaller(LAUNCHER_1, UserHandle.USER_SYSTEM, () -> {
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList("s2", "s3"),
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s2", "s3"),
getCallingUser());
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, Arrays.asList("s4", "s5"),
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s4", "s5"),
getCallingUser());
});
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
@@ -1743,7 +1811,7 @@
.setTitle("new title")
.build();
- mManager.updateShortcuts(Arrays.asList(s2, s4));
+ mManager.updateShortcuts(list(s2, s4));
});
runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
ShortcutInfo s2 = makeShortcutBuilder()
@@ -1757,7 +1825,7 @@
.setIntent(new Intent(Intent.ACTION_ALL_APPS))
.build();
- mManager.updateShortcuts(Arrays.asList(s2, s4));
+ mManager.updateShortcuts(list(s2, s4));
});
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
@@ -1805,12 +1873,7 @@
// TODO Check bitmap removal too.
runWithCaller(CALLING_PACKAGE_2, USER_11, () -> {
- assertNoShortcutPackageInfo(CALLING_PACKAGE_2, USER_11);
-
- mManager.updateShortcuts(Arrays.asList());
-
- // Even an empty update call will populate the package info.
- assertShortcutPackageInfo(CALLING_PACKAGE_2, USER_11, 2);
+ mManager.updateShortcuts(list());
});
}
@@ -1838,17 +1901,17 @@
final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 5000);
final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 1000);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s1_1, s1_2)));
+ assertTrue(mManager.setDynamicShortcuts(list(s1_1, s1_2)));
setCaller(CALLING_PACKAGE_2);
final ShortcutInfo s2_2 = makeShortcutWithTimestamp("s2", 1500);
final ShortcutInfo s2_3 = makeShortcutWithTimestamp("s3", 3000);
final ShortcutInfo s2_4 = makeShortcutWithTimestamp("s4", 500);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s2_2, s2_3, s2_4)));
+ assertTrue(mManager.setDynamicShortcuts(list(s2_2, s2_3, s2_4)));
setCaller(CALLING_PACKAGE_3);
- final ShortcutInfo s3_2 = makeShortcutWithTimestamp("s3", 5000);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s3_2)));
+ final ShortcutInfo s3_2 = makeShortcutWithTimestamp("s3", START_TIME + 5000);
+ assertTrue(mManager.setDynamicShortcuts(list(s3_2)));
setCaller(LAUNCHER_1);
@@ -1886,7 +1949,7 @@
// Pin some shortcuts.
mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
- Arrays.asList("s3", "s4"), getCallingUser());
+ list("s3", "s4"), getCallingUser());
// Pinned ones only
assertAllPinned(assertAllHaveTitle(assertAllNotHaveIntents(assertShortcutIds(
@@ -1929,7 +1992,7 @@
makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
/* weight */ 12);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s1_1, s1_2)));
+ assertTrue(mManager.setDynamicShortcuts(list(s1_1, s1_2)));
dumpsysOnLogcat();
setCaller(CALLING_PACKAGE_2);
@@ -1941,14 +2004,14 @@
makeIntent(Intent.ACTION_ANSWER, ShortcutActivity2.class,
"key1", "val1", "nest", makeBundle("key", 123)),
/* weight */ 10);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s2_1)));
+ assertTrue(mManager.setDynamicShortcuts(list(s2_1)));
dumpsysOnLogcat();
// Pin some.
setCaller(LAUNCHER_1);
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
- Arrays.asList("s2"), getCallingUser());
+ list("s2"), getCallingUser());
dumpsysOnLogcat();
@@ -1967,19 +2030,19 @@
list = assertShortcutIds(assertAllHaveTitle(assertAllNotHaveIntents(
assertAllNotKeyFieldsOnly(
mLauncherApps.getShortcutInfo(CALLING_PACKAGE_1,
- Arrays.asList("s2", "s1", "s3", null), getCallingUser())))),
+ list("s2", "s1", "s3", null), getCallingUser())))),
"s1", "s2");
assertEquals("Title 1", findById(list, "s1").getTitle());
assertEquals("Title 2", findById(list, "s2").getTitle());
assertShortcutIds(assertAllHaveTitle(assertAllNotHaveIntents(
mLauncherApps.getShortcutInfo(CALLING_PACKAGE_1,
- Arrays.asList("s3"), getCallingUser())))
+ list("s3"), getCallingUser())))
/* none */);
list = assertShortcutIds(assertAllHaveTitle(assertAllNotHaveIntents(
mLauncherApps.getShortcutInfo(CALLING_PACKAGE_2,
- Arrays.asList("s1", "s2", "s3"), getCallingUser()))),
+ list("s1", "s2", "s3"), getCallingUser()))),
"s1");
assertEquals("ABC", findById(list, "s1").getTitle());
}
@@ -1990,38 +2053,31 @@
final ShortcutInfo s1_1 = makeShortcutWithTimestamp("s1", 1000);
final ShortcutInfo s1_2 = makeShortcutWithTimestamp("s2", 2000);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s1_1, s1_2)));
+ assertTrue(mManager.setDynamicShortcuts(list(s1_1, s1_2)));
});
runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
final ShortcutInfo s2_2 = makeShortcutWithTimestamp("s2", 1500);
final ShortcutInfo s2_3 = makeShortcutWithTimestamp("s3", 3000);
final ShortcutInfo s2_4 = makeShortcutWithTimestamp("s4", 500);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s2_2, s2_3, s2_4)));
+ assertTrue(mManager.setDynamicShortcuts(list(s2_2, s2_3, s2_4)));
});
runWithCaller(CALLING_PACKAGE_3, USER_0, () -> {
final ShortcutInfo s3_2 = makeShortcutWithTimestamp("s2", 1000);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s3_2)));
+ assertTrue(mManager.setDynamicShortcuts(list(s3_2)));
});
// Pin some.
runWithCaller(LAUNCHER_1, USER_0, () -> {
- assertNoShortcutPackageInfo(LAUNCHER_1, USER_0);
-
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
- Arrays.asList("s2", "s3"), getCallingUser());
-
- assertShortcutPackageInfo(LAUNCHER_1, USER_0, 4);
- assertNoShortcutPackageInfo(LAUNCHER_2, USER_0);
- assertNoShortcutPackageInfo(LAUNCHER_1, USER_10);
- assertNoShortcutPackageInfo(LAUNCHER_2, USER_10);
+ list("s2", "s3"), getCallingUser());
mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
- Arrays.asList("s3", "s4", "s5"), getCallingUser());
+ list("s3", "s4", "s5"), getCallingUser());
mLauncherApps.pinShortcuts(CALLING_PACKAGE_3,
- Arrays.asList("s3"), getCallingUser()); // Note ID doesn't exist
+ list("s3"), getCallingUser()); // Note ID doesn't exist
});
// Delete some.
@@ -2066,32 +2122,24 @@
public void testPinShortcutAndGetPinnedShortcuts_multi() {
// Create some shortcuts.
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
});
runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
});
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,
- Arrays.asList("s3", "s4"), getCallingUser());
-
- assertShortcutPackageInfo(LAUNCHER_1, USER_0, 4);
- assertNoShortcutPackageInfo(LAUNCHER_2, USER_0);
+ list("s3", "s4"), getCallingUser());
mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
- Arrays.asList("s1", "s2", "s4"), getCallingUser());
+ list("s1", "s2", "s4"), getCallingUser());
});
dumpsysOnLogcat();
@@ -2163,18 +2211,12 @@
| ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())),
"s2");
- assertNoShortcutPackageInfo(LAUNCHER_2, USER_0);
- assertNoShortcutPackageInfo(LAUNCHER_2, USER_10);
-
// Now pin some.
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
- Arrays.asList("s1", "s2"), getCallingUser());
-
- assertShortcutPackageInfo(LAUNCHER_2, USER_0, 5);
- assertNoShortcutPackageInfo(LAUNCHER_2, USER_10);
+ list("s1", "s2"), getCallingUser());
mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
- Arrays.asList("s1", "s2"), getCallingUser());
+ list("s1", "s2"), getCallingUser());
assertShortcutIds(assertAllDynamic(
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
@@ -2190,12 +2232,6 @@
"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();
@@ -2204,12 +2240,6 @@
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,
@@ -2266,7 +2296,7 @@
// Update pined. Note s2 and s3 are actually available, but not visible to this
// launcher, so still can't be pinned.
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList("s1", "s2", "s3", "s4"),
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1", "s2", "s3", "s4"),
getCallingUser());
assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
@@ -2288,7 +2318,7 @@
"s3");
// Now "s1" is visible, so can be pinned.
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList("s1", "s2", "s3", "s4"),
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s1", "s2", "s3", "s4"),
getCallingUser());
assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly(
@@ -2299,8 +2329,8 @@
// Now clear pinned shortcuts. First, from launcher 1.
runWithCaller(LAUNCHER_1, USER_0, () -> {
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList(), getCallingUser());
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, Arrays.asList(), getCallingUser());
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list(), getCallingUser());
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list(), getCallingUser());
assertEquals(0,
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
@@ -2320,8 +2350,8 @@
// Clear all pins from launcher 2.
runWithCaller(LAUNCHER_2, USER_0, () -> {
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList(), getCallingUser());
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, Arrays.asList(), getCallingUser());
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list(), getCallingUser());
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list(), getCallingUser());
assertEquals(0,
mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
@@ -2340,7 +2370,502 @@
});
}
- public void testCreateShortcutIntent() {
+ public void testPinShortcutAndGetPinnedShortcuts_crossProfile_plusLaunch() {
+ // Create some shortcuts.
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ 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"),
+ makeShortcut("s4"), makeShortcut("s5"), makeShortcut("s6"))));
+ });
+
+ // Pin some shortcuts and see the result.
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s1"), HANDLE_USER_0);
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ list("s1", "s2", "s3"), HANDLE_USER_0);
+ });
+
+ runWithCaller(LAUNCHER_1, USER_P0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s2"), HANDLE_USER_0);
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ list("s2", "s3"), HANDLE_USER_0);
+ });
+
+ runWithCaller(LAUNCHER_2, USER_P0, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s3"), HANDLE_USER_0);
+
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
+ list("s3"), HANDLE_USER_0);
+ });
+
+ runWithCaller(LAUNCHER_2, USER_10, () -> {
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
+ list("s1", "s2", "s3"), HANDLE_USER_10);
+ });
+
+ // Cross profile pinning.
+ final int PIN_AND_DYNAMIC = ShortcutQuery.FLAG_GET_PINNED | ShortcutQuery.FLAG_GET_DYNAMIC;
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+ });
+ runWithCaller(LAUNCHER_1, USER_P0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s2");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s2", "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+ });
+ runWithCaller(LAUNCHER_2, USER_P0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+ });
+ runWithCaller(LAUNCHER_2, USER_10, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_10)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_10)),
+ "s1", "s2", "s3", "s4", "s5", "s6");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_10)),
+ "s1", "s2", "s3", "s4", "s5", "s6");
+ });
+
+ // Remove some dynamic shortcuts.
+
+ runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"))));
+ });
+ runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"))));
+ });
+ runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
+ assertTrue(mManager.setDynamicShortcuts(list(
+ makeShortcut("s1"))));
+ });
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+ });
+ runWithCaller(LAUNCHER_1, USER_P0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s2");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2");
+
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s2", "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+ });
+ runWithCaller(LAUNCHER_2, USER_P0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s3");
+
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s3");
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+ });
+ runWithCaller(LAUNCHER_2, USER_10, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_10)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_10)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_10)),
+ "s1", "s2", "s3");
+
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+
+ assertShortcutNotLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+ });
+
+ // Save & load and make sure we still have the same information.
+ mService.saveDirtyInfo();
+ initService();
+ mService.handleUnlockUser(USER_0);
+
+ runWithCaller(LAUNCHER_1, USER_0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+ });
+ runWithCaller(LAUNCHER_1, USER_P0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s2");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2");
+
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s2", "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s2", "s3");
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+ });
+ runWithCaller(LAUNCHER_2, USER_P0, () -> {
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s3");
+
+ assertShortcutIds(assertAllPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_PINNED), HANDLE_USER_0)),
+ "s3");
+ assertShortcutIds(assertAllDynamic(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, ShortcutQuery.FLAG_GET_DYNAMIC), HANDLE_USER_0)),
+ "s1");
+ assertShortcutIds(assertAllDynamicOrPinned(
+ mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_2,
+ /* activity =*/ null, PIN_AND_DYNAMIC), HANDLE_USER_0)),
+ "s1", "s3");
+
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s1", USER_0);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_1, "s3", USER_0);
+
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s1", USER_0);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_2, "s2", USER_0);
+ assertShortcutLaunchable(CALLING_PACKAGE_2, "s3", USER_0);
+
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s1", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s2", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s3", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s4", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s5", USER_10);
+ assertShortcutNotLaunchable(CALLING_PACKAGE_1, "s6", USER_10);
+ });
+ }
+
+ public void testStartShortcut() {
// Create some shortcuts.
setCaller(CALLING_PACKAGE_1);
final ShortcutInfo s1_1 = makeShortcut(
@@ -2360,7 +2885,7 @@
makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
/* weight */ 12);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s1_1, s1_2)));
+ assertTrue(mManager.setDynamicShortcuts(list(s1_1, s1_2)));
setCaller(CALLING_PACKAGE_2);
final ShortcutInfo s2_1 = makeShortcut(
@@ -2371,15 +2896,15 @@
makeIntent(Intent.ACTION_ANSWER, ShortcutActivity.class,
"key1", "val1", "nest", makeBundle("key", 123)),
/* weight */ 10);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(s2_1)));
+ assertTrue(mManager.setDynamicShortcuts(list(s2_1)));
// Pin all.
setCaller(LAUNCHER_1);
mLauncherApps.pinShortcuts(CALLING_PACKAGE_1,
- Arrays.asList("s1", "s2"), getCallingUser());
+ list("s1", "s2"), getCallingUser());
mLauncherApps.pinShortcuts(CALLING_PACKAGE_2,
- Arrays.asList("s1"), getCallingUser());
+ list("s1"), getCallingUser());
// Just to make it complicated, delete some.
setCaller(CALLING_PACKAGE_1);
@@ -2387,17 +2912,16 @@
// intent and check.
setCaller(LAUNCHER_1);
+
Intent intent;
- intent = mInternal.createShortcutIntent(getCallingPackage(),
- CALLING_PACKAGE_1, "s1", getCallingUserId());
+ intent = launchShortcutAndGetIntent(CALLING_PACKAGE_1, "s1", USER_0);
assertEquals(ShortcutActivity2.class.getName(), intent.getComponent().getClassName());
- intent = mInternal.createShortcutIntent(getCallingPackage(),
- CALLING_PACKAGE_1, "s2", getCallingUserId());
+
+ intent = launchShortcutAndGetIntent(CALLING_PACKAGE_1, "s2", USER_0);
assertEquals(ShortcutActivity3.class.getName(), intent.getComponent().getClassName());
- intent = mInternal.createShortcutIntent(getCallingPackage(),
- CALLING_PACKAGE_2, "s1", getCallingUserId());
+ intent = launchShortcutAndGetIntent(CALLING_PACKAGE_2, "s1", USER_0);
assertEquals(ShortcutActivity.class.getName(), intent.getComponent().getClassName());
// TODO Check extra, etc
@@ -2419,7 +2943,7 @@
});
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
});
@@ -2428,7 +2952,7 @@
verify(c0).onShortcutsChanged(
eq(CALLING_PACKAGE_1),
shortcuts.capture(),
- eq(UserHandle.of(USER_0))
+ eq(HANDLE_USER_0)
);
assertShortcutIds(assertAllDynamic(shortcuts.getValue()),
"s1", "s2", "s3");
@@ -2436,7 +2960,7 @@
// From different package.
reset(c0);
runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> {
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
});
waitOnMainThread();
@@ -2444,7 +2968,7 @@
verify(c0).onShortcutsChanged(
eq(CALLING_PACKAGE_2),
shortcuts.capture(),
- eq(UserHandle.of(USER_0))
+ eq(HANDLE_USER_0)
);
assertShortcutIds(assertAllDynamic(shortcuts.getValue()),
"s1", "s2", "s3");
@@ -2452,7 +2976,7 @@
// Different user, callback shouldn't be called.
reset(c0);
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"))));
});
waitOnMainThread();
@@ -2473,7 +2997,7 @@
verify(c0).onShortcutsChanged(
eq(CALLING_PACKAGE_1),
shortcuts.capture(),
- eq(UserHandle.of(USER_0))
+ eq(HANDLE_USER_0)
);
assertShortcutIds(assertAllDynamic(shortcuts.getValue()),
"s1", "s2", "s3", "s4");
@@ -2489,7 +3013,7 @@
verify(c0).onShortcutsChanged(
eq(CALLING_PACKAGE_1),
shortcuts.capture(),
- eq(UserHandle.of(USER_0))
+ eq(HANDLE_USER_0)
);
assertShortcutIds(assertAllDynamic(shortcuts.getValue()),
"s2", "s3", "s4");
@@ -2497,7 +3021,7 @@
// Test for update
reset(c0);
runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> {
- assertTrue(mManager.updateShortcuts(Arrays.asList(
+ assertTrue(mManager.updateShortcuts(list(
makeShortcut("s1"), makeShortcut("s2"))));
});
@@ -2506,7 +3030,7 @@
verify(c0).onShortcutsChanged(
eq(CALLING_PACKAGE_1),
shortcuts.capture(),
- eq(UserHandle.of(USER_0))
+ eq(HANDLE_USER_0)
);
assertShortcutIds(assertAllDynamic(shortcuts.getValue()),
"s2", "s3", "s4");
@@ -2522,13 +3046,13 @@
verify(c0).onShortcutsChanged(
eq(CALLING_PACKAGE_1),
shortcuts.capture(),
- eq(UserHandle.of(USER_0))
+ eq(HANDLE_USER_0)
);
assertEquals(0, shortcuts.getValue().size());
// Remove CALLING_PACKAGE_2
reset(c0);
- mService.cleanUpPackageLocked(CALLING_PACKAGE_2, USER_0);
+ mService.cleanUpPackageLocked(CALLING_PACKAGE_2, USER_0, USER_0);
// Should get a callback with an empty list.
waitOnMainThread();
@@ -2536,7 +3060,7 @@
verify(c0).onShortcutsChanged(
eq(CALLING_PACKAGE_2),
shortcuts.capture(),
- eq(UserHandle.of(USER_0))
+ eq(HANDLE_USER_0)
);
assertEquals(0, shortcuts.getValue().size());
}
@@ -2544,7 +3068,7 @@
// === Test for persisting ===
public void testSaveAndLoadUser_empty() {
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList()));
+ assertTrue(mManager.setDynamicShortcuts(list()));
Log.i(TAG, "Saved state");
dumpsysOnLogcat();
@@ -2584,7 +3108,7 @@
makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
/* weight */ 12);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1, si2)));
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
assertEquals(2, mManager.getRemainingCallCount());
@@ -2611,7 +3135,7 @@
makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
/* weight */ 12);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1, si2)));
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
assertEquals(2, mManager.getRemainingCallCount());
@@ -2638,7 +3162,7 @@
makeIntent(Intent.ACTION_ASSIST, ShortcutActivity3.class),
/* weight */ 12);
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1, si2)));
+ assertTrue(mManager.setDynamicShortcuts(list(si1, si2)));
assertEquals(START_TIME + INTERVAL, mManager.getRateLimitResetTime());
assertEquals(2, mManager.getRemainingCallCount());
@@ -2647,10 +3171,6 @@
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();
@@ -2661,10 +3181,6 @@
// 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());
@@ -2715,45 +3231,45 @@
public void testCleanupPackage() {
runWithCaller(CALLING_PACKAGE_1, USER_0, () -> {
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s0_1"))));
});
runWithCaller(CALLING_PACKAGE_2, USER_0, () -> {
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s0_2"))));
});
runWithCaller(LAUNCHER_1, USER_0, () -> {
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList("s0_1"),
- UserHandle.of(USER_0));
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, Arrays.asList("s0_2"),
- UserHandle.of(USER_0));
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s0_1"),
+ HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s0_2"),
+ HANDLE_USER_0);
});
runWithCaller(LAUNCHER_2, USER_0, () -> {
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList("s0_1"),
- UserHandle.of(USER_0));
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, Arrays.asList("s0_2"),
- UserHandle.of(USER_0));
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s0_1"),
+ HANDLE_USER_0);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s0_2"),
+ HANDLE_USER_0);
});
runWithCaller(CALLING_PACKAGE_1, USER_10, () -> {
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s10_1"))));
});
runWithCaller(CALLING_PACKAGE_2, USER_10, () -> {
- assertTrue(mManager.setDynamicShortcuts(Arrays.asList(
+ assertTrue(mManager.setDynamicShortcuts(list(
makeShortcut("s10_2"))));
});
runWithCaller(LAUNCHER_1, USER_10, () -> {
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList("s10_1"),
- UserHandle.of(USER_10));
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, Arrays.asList("s10_2"),
- UserHandle.of(USER_10));
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s10_1"),
+ HANDLE_USER_10);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s10_2"),
+ HANDLE_USER_10);
});
runWithCaller(LAUNCHER_2, USER_10, () -> {
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, Arrays.asList("s10_1"),
- UserHandle.of(USER_10));
- mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, Arrays.asList("s10_2"),
- UserHandle.of(USER_10));
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, list("s10_1"),
+ HANDLE_USER_10);
+ mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, list("s10_2"),
+ HANDLE_USER_10);
});
// Remove all dynamic shortcuts; now all shortcuts are just pinned.
@@ -2781,15 +3297,19 @@
// Check the registered packages.
-
+ dumpsysOnLogcat();
assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
set(user0.getPackages().keySet()));
assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
set(user10.getPackages().keySet()));
- assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
- set(user0.getLaunchers().keySet()));
- assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
- set(user10.getLaunchers().keySet()));
+ assertEquals(
+ makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
+ PackageWithUser.of(USER_0, LAUNCHER_2)),
+ set(user0.getAllLaunchers().keySet()));
+ assertEquals(
+ makeSet(PackageWithUser.of(USER_10, LAUNCHER_1),
+ PackageWithUser.of(USER_10, LAUNCHER_2)),
+ set(user10.getAllLaunchers().keySet()));
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
"s0_1", "s0_2");
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -2803,25 +3323,24 @@
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.
- mService.cleanUpPackageLocked("abc", USER_0);
+ mService.cleanUpPackageLocked("abc", USER_0, USER_0);
// No changes.
assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
set(user0.getPackages().keySet()));
assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
set(user10.getPackages().keySet()));
- assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
- set(user0.getLaunchers().keySet()));
- assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
- set(user10.getLaunchers().keySet()));
+ assertEquals(
+ makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
+ PackageWithUser.of(USER_0, LAUNCHER_2)),
+ set(user0.getAllLaunchers().keySet()));
+ assertEquals(
+ makeSet(PackageWithUser.of(USER_10, LAUNCHER_1),
+ PackageWithUser.of(USER_10, LAUNCHER_2)),
+ set(user10.getAllLaunchers().keySet()));
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
"s0_1", "s0_2");
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -2835,24 +3354,23 @@
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.
- mService.cleanUpPackageLocked(CALLING_PACKAGE_1, USER_0);
+ mService.cleanUpPackageLocked(CALLING_PACKAGE_1, USER_0, USER_0);
assertEquals(makeSet(CALLING_PACKAGE_2),
set(user0.getPackages().keySet()));
assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
set(user10.getPackages().keySet()));
- assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
- set(user0.getLaunchers().keySet()));
- assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
- set(user10.getLaunchers().keySet()));
+ assertEquals(
+ makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
+ PackageWithUser.of(USER_0, LAUNCHER_2)),
+ set(user0.getAllLaunchers().keySet()));
+ assertEquals(
+ makeSet(PackageWithUser.of(USER_10, LAUNCHER_1),
+ PackageWithUser.of(USER_10, LAUNCHER_2)),
+ set(user10.getAllLaunchers().keySet()));
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
"s0_2");
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -2866,24 +3384,22 @@
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.
- mService.cleanUpPackageLocked(LAUNCHER_1, USER_10);
+ mService.cleanUpPackageLocked(LAUNCHER_1, USER_10, USER_10);
assertEquals(makeSet(CALLING_PACKAGE_2),
set(user0.getPackages().keySet()));
assertEquals(makeSet(CALLING_PACKAGE_1, CALLING_PACKAGE_2),
set(user10.getPackages().keySet()));
- assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
- set(user0.getLaunchers().keySet()));
- assertEquals(makeSet(LAUNCHER_2),
- set(user10.getLaunchers().keySet()));
+ assertEquals(
+ makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
+ PackageWithUser.of(USER_0, LAUNCHER_2)),
+ set(user0.getAllLaunchers().keySet()));
+ assertEquals(
+ makeSet(PackageWithUser.of(USER_10, LAUNCHER_2)),
+ set(user10.getAllLaunchers().keySet()));
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
"s0_2");
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -2898,16 +3414,19 @@
mService.saveDirtyInfo();
// Remove a package.
- mService.cleanUpPackageLocked(CALLING_PACKAGE_2, USER_10);
+ mService.cleanUpPackageLocked(CALLING_PACKAGE_2, USER_10, USER_10);
assertEquals(makeSet(CALLING_PACKAGE_2),
set(user0.getPackages().keySet()));
assertEquals(makeSet(CALLING_PACKAGE_1),
set(user10.getPackages().keySet()));
- assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
- set(user0.getLaunchers().keySet()));
- assertEquals(makeSet(LAUNCHER_2),
- set(user10.getLaunchers().keySet()));
+ assertEquals(
+ makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
+ PackageWithUser.of(USER_0, LAUNCHER_2)),
+ set(user0.getAllLaunchers().keySet()));
+ assertEquals(
+ makeSet(PackageWithUser.of(USER_10, LAUNCHER_2)),
+ set(user10.getAllLaunchers().keySet()));
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
"s0_2");
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -2919,24 +3438,22 @@
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.
- mService.cleanUpPackageLocked(LAUNCHER_2, USER_10);
+ mService.cleanUpPackageLocked(LAUNCHER_2, USER_10, USER_10);
assertEquals(makeSet(CALLING_PACKAGE_2),
set(user0.getPackages().keySet()));
assertEquals(makeSet(CALLING_PACKAGE_1),
set(user10.getPackages().keySet()));
- assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
- set(user0.getLaunchers().keySet()));
- assertEquals(makeSet(),
- set(user10.getLaunchers().keySet()));
+ assertEquals(
+ makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
+ PackageWithUser.of(USER_0, LAUNCHER_2)),
+ set(user0.getAllLaunchers().keySet()));
+ assertEquals(
+ makeSet(),
+ set(user10.getAllLaunchers().keySet()));
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
"s0_2");
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -2951,16 +3468,18 @@
mService.saveDirtyInfo();
// More remove.
- mService.cleanUpPackageLocked(CALLING_PACKAGE_1, USER_10);
+ mService.cleanUpPackageLocked(CALLING_PACKAGE_1, USER_10, USER_10);
assertEquals(makeSet(CALLING_PACKAGE_2),
set(user0.getPackages().keySet()));
assertEquals(makeSet(),
set(user10.getPackages().keySet()));
- assertEquals(makeSet(LAUNCHER_1, LAUNCHER_2),
- set(user0.getLaunchers().keySet()));
+ assertEquals(
+ makeSet(PackageWithUser.of(USER_0, LAUNCHER_1),
+ PackageWithUser.of(USER_0, LAUNCHER_2)),
+ set(user0.getAllLaunchers().keySet()));
assertEquals(makeSet(),
- set(user10.getLaunchers().keySet()));
+ set(user10.getAllLaunchers().keySet()));
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_1, USER_0),
"s0_2");
assertShortcutIds(getLauncherPinnedShortcuts(LAUNCHER_2, USER_0),
@@ -2975,6 +3494,353 @@
mService.saveDirtyInfo();
}
+
+ public void testSaveAndLoadUser_forBackup() {
+ // 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));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
+
+ 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));
+
+ // 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));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
+
+ 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));
+
+ // Make sure all the information is persisted.
+ mService.saveDirtyInfo();
+ initService();
+ mService.handleUnlockUser(USER_0);
+ mService.handleUnlockUser(USER_P0);
+ mService.handleUnlockUser(USER_10);
+
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
+
+ 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);
+ mService.cleanupGonePackages(USER_10);
+
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
+
+ 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));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
+
+ // Uninstall.
+ uninstallPackage(USER_10, CALLING_PACKAGE_1);
+ mService.cleanupGonePackages(USER_10);
+
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
+
+ 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));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
+
+ uninstallPackage(USER_P0, LAUNCHER_1);
+ mService.cleanupGonePackages(USER_0);
+
+ assertDynamicAndPinned(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));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_P0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s2", USER_0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
+
+ mService.cleanupGonePackages(USER_P0);
+
+ assertDynamicAndPinned(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));
+ assertDynamicAndPinned(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));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
+
+ uninstallPackage(USER_P0, CALLING_PACKAGE_1);
+
+ mService.saveDirtyInfo();
+ initService();
+ mService.handleUnlockUser(USER_0);
+ mService.handleUnlockUser(USER_P0);
+ mService.handleUnlockUser(USER_10);
+
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_P0));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_P0));
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_2, "s2", USER_0));
+ assertDynamicAndPinned(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
+
+ // Uninstall
+ uninstallPackage(USER_0, LAUNCHER_1);
+
+ mService.saveDirtyInfo();
+ initService();
+ mService.handleUnlockUser(USER_0);
+ mService.handleUnlockUser(USER_P0);
+ mService.handleUnlockUser(USER_10);
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_P0));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0));
+ assertNull(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));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
+
+ uninstallPackage(USER_0, CALLING_PACKAGE_2);
+
+ mService.saveDirtyInfo();
+ initService();
+ mService.handleUnlockUser(USER_0);
+ mService.handleUnlockUser(USER_P0);
+ mService.handleUnlockUser(USER_10);
+
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_0));
+ assertDynamicOnly(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_0));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_P0));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_P0));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_P0));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_2, "s1", USER_0));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_2, "s2", USER_0));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_2, "s3", USER_0));
+
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s1", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s2", USER_10));
+ assertNull(getPackageShortcut(CALLING_PACKAGE_1, "s3", USER_10));
+ }
+
// TODO Detailed test for hasShortcutPermissionInner().
// TODO Add tests for the command line functions too.
@@ -3027,27 +3893,6 @@
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")));
@@ -3067,73 +3912,56 @@
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/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
new file mode 100644
index 0000000..c016e61
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/pm/backup/BackupUtilsTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.server.pm.backup;
+
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageParser.Package;
+import android.content.pm.Signature;
+import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.server.backup.BackupUtils;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+
+@SmallTest
+public class BackupUtilsTest extends AndroidTestCase {
+
+ private Signature[] genSignatures(String... signatures) {
+ final Signature[] sigs = new Signature[signatures.length];
+ for (int i = 0; i < signatures.length; i++){
+ sigs[i] = new Signature(signatures[i].getBytes());
+ }
+ return sigs;
+ }
+
+ private PackageInfo genPackage(String... signatures) {
+ final PackageInfo pi = new PackageInfo();
+ pi.packageName = "package";
+ pi.applicationInfo = new ApplicationInfo();
+ pi.signatures = genSignatures(signatures);
+
+ return pi;
+ }
+
+ public void testSignaturesMatch() {
+ final ArrayList<byte[]> stored1 = BackupUtils.hashSignatureArray(Arrays.asList(
+ "abc".getBytes()));
+ final ArrayList<byte[]> stored2 = BackupUtils.hashSignatureArray(Arrays.asList(
+ "abc".getBytes(), "def".getBytes()));
+
+ PackageInfo pi;
+
+ // False for null package.
+ assertFalse(BackupUtils.signaturesMatch(stored1, null));
+
+ // If it's a system app, signatures don't matter.
+ pi = genPackage("xyz");
+ pi.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ assertTrue(BackupUtils.signaturesMatch(stored1, pi));
+
+ // Non system apps.
+ assertTrue(BackupUtils.signaturesMatch(stored1, genPackage("abc")));
+
+ // Superset is okay.
+ assertTrue(BackupUtils.signaturesMatch(stored1, genPackage("abc", "xyz")));
+ assertTrue(BackupUtils.signaturesMatch(stored1, genPackage("xyz", "abc")));
+
+ assertFalse(BackupUtils.signaturesMatch(stored1, genPackage("xyz")));
+ assertFalse(BackupUtils.signaturesMatch(stored1, genPackage("xyz", "def")));
+
+ assertTrue(BackupUtils.signaturesMatch(stored2, genPackage("def", "abc")));
+ assertTrue(BackupUtils.signaturesMatch(stored2, genPackage("x", "def", "abc", "y")));
+
+ // Subset is not okay.
+ assertFalse(BackupUtils.signaturesMatch(stored2, genPackage("abc")));
+ assertFalse(BackupUtils.signaturesMatch(stored2, genPackage("def")));
+ }
+
+ public void testHashSignature() {
+ final byte[] sig1 = "abc".getBytes();
+ final byte[] sig2 = "def".getBytes();
+
+ final byte[] hash1a = BackupUtils.hashSignature(sig1);
+ final byte[] hash1b = BackupUtils.hashSignature(new Signature(sig1));
+
+ final byte[] hash2a = BackupUtils.hashSignature(sig2);
+ final byte[] hash2b = BackupUtils.hashSignature(new Signature(sig2));
+
+ assertEquals(32, hash1a.length);
+ MoreAsserts.assertEquals(hash1a, hash1b);
+
+ assertEquals(32, hash2a.length);
+ MoreAsserts.assertEquals(hash2a, hash2b);
+
+ assertFalse(Arrays.equals(hash1a, hash2a));
+
+ final ArrayList<byte[]> listA = BackupUtils.hashSignatureArray(Arrays.asList(
+ "abc".getBytes(), "def".getBytes()));
+
+ final ArrayList<byte[]> listB = BackupUtils.hashSignatureArray(new Signature[]{
+ new Signature("abc".getBytes()), new Signature("def".getBytes())});
+
+ assertEquals(2, listA.size());
+ assertEquals(2, listB.size());
+
+ MoreAsserts.assertEquals(hash1a, listA.get(0));
+ MoreAsserts.assertEquals(hash1a, listB.get(0));
+
+ MoreAsserts.assertEquals(hash2a, listA.get(1));
+ MoreAsserts.assertEquals(hash2a, listB.get(1));
+ }
+}
diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java
index 06851ee..1ce4ade 100644
--- a/telecomm/java/android/telecom/Conference.java
+++ b/telecomm/java/android/telecom/Conference.java
@@ -694,7 +694,7 @@
if (mPreviousExtraKeys != null) {
List<String> toRemove = new ArrayList<String>();
for (String oldKey : mPreviousExtraKeys) {
- if (!extras.containsKey(oldKey)) {
+ if (extras == null || !extras.containsKey(oldKey)) {
toRemove.add(oldKey);
}
}
@@ -710,7 +710,9 @@
mPreviousExtraKeys = new ArraySet<String>();
}
mPreviousExtraKeys.clear();
- mPreviousExtraKeys.addAll(extras.keySet());
+ if (extras != null) {
+ mPreviousExtraKeys.addAll(extras.keySet());
+ }
}
/**
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 310c957..d83cdb8 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -1887,7 +1887,7 @@
if (mPreviousExtraKeys != null) {
List<String> toRemove = new ArrayList<String>();
for (String oldKey : mPreviousExtraKeys) {
- if (!extras.containsKey(oldKey)) {
+ if (extras == null || !extras.containsKey(oldKey)) {
toRemove.add(oldKey);
}
}
@@ -1902,7 +1902,9 @@
mPreviousExtraKeys = new ArraySet<String>();
}
mPreviousExtraKeys.clear();
- mPreviousExtraKeys.addAll(extras.keySet());
+ if (extras != null) {
+ mPreviousExtraKeys.addAll(extras.keySet());
+ }
}
/**
@@ -2317,7 +2319,7 @@
*/
public void sendConnectionEvent(String event, Bundle extras) {
for (Listener l : mListeners) {
- l.onConnectionEvent(this, event, null);
+ l.onConnectionEvent(this, event, extras);
}
}
}
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 96fd70e..a6da114 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 087e68a..9351f63 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 @@
@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);
diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
index 035317e..4c38c9b 100644
--- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
+++ b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java
@@ -19,6 +19,8 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Arrays;
+
/**
* Record of energy and activity information from controller and
* underlying wifi stack state. Timestamp the record with elapsed
@@ -44,6 +46,11 @@
/**
* @hide
*/
+ public long[] mControllerTxTimePerLevelMs;
+
+ /**
+ * @hide
+ */
public long mControllerRxTimeMs;
/**
@@ -62,10 +69,12 @@
public static final int STACK_STATE_STATE_IDLE = 3;
public WifiActivityEnergyInfo(long timestamp, int stackState,
- long txTime, long rxTime, long idleTime, long energyUsed) {
+ long txTime, long[] txTimePerLevel, long rxTime, long idleTime,
+ long energyUsed) {
mTimestamp = timestamp;
mStackState = stackState;
mControllerTxTimeMs = txTime;
+ mControllerTxTimePerLevelMs = txTimePerLevel;
mControllerRxTimeMs = rxTime;
mControllerIdleTimeMs = idleTime;
mControllerEnergyUsed = energyUsed;
@@ -77,6 +86,7 @@
+ " timestamp=" + mTimestamp
+ " mStackState=" + mStackState
+ " mControllerTxTimeMs=" + mControllerTxTimeMs
+ + " mControllerTxTimePerLevelMs=" + Arrays.toString(mControllerTxTimePerLevelMs)
+ " mControllerRxTimeMs=" + mControllerRxTimeMs
+ " mControllerIdleTimeMs=" + mControllerIdleTimeMs
+ " mControllerEnergyUsed=" + mControllerEnergyUsed
@@ -89,11 +99,12 @@
long timestamp = in.readLong();
int stackState = in.readInt();
long txTime = in.readLong();
+ long[] txTimePerLevel = in.createLongArray();
long rxTime = in.readLong();
long idleTime = in.readLong();
long energyUsed = in.readLong();
return new WifiActivityEnergyInfo(timestamp, stackState,
- txTime, rxTime, idleTime, energyUsed);
+ txTime, txTimePerLevel, rxTime, idleTime, energyUsed);
}
public WifiActivityEnergyInfo[] newArray(int size) {
return new WifiActivityEnergyInfo[size];
@@ -104,6 +115,7 @@
out.writeLong(mTimestamp);
out.writeInt(mStackState);
out.writeLong(mControllerTxTimeMs);
+ out.writeLongArray(mControllerTxTimePerLevelMs);
out.writeLong(mControllerRxTimeMs);
out.writeLong(mControllerIdleTimeMs);
out.writeLong(mControllerEnergyUsed);
@@ -128,6 +140,16 @@
}
/**
+ * @return tx time at power level provided in ms
+ */
+ public long getControllerTxTimeMillisAtLevel(int level) {
+ if (level < mControllerTxTimePerLevelMs.length) {
+ return mControllerTxTimePerLevelMs[level];
+ }
+ return 0;
+ }
+
+ /**
* @return rx time in ms
*/
public long getControllerRxTimeMillis() {
diff --git a/wifi/java/android/net/wifi/WifiConfiguration.java b/wifi/java/android/net/wifi/WifiConfiguration.java
index 06dea07..fb2bdd4 100644
--- a/wifi/java/android/net/wifi/WifiConfiguration.java
+++ b/wifi/java/android/net/wifi/WifiConfiguration.java
@@ -938,6 +938,15 @@
private boolean mSeenInLastQualifiedNetworkSelection;
/**
+ * Boolean indicating if we have ever successfully connected to this network.
+ *
+ * This value will be set to true upon a successful connection.
+ * This value will be set to false if a previous value was not stored in the config or if
+ * the credentials are updated (ex. a password change).
+ */
+ private boolean mHasEverConnected;
+
+ /**
* set whether this network is visible in latest Qualified Network Selection
* @param seen value set to candidate
*/
@@ -1027,7 +1036,18 @@
return QUALITY_NETWORK_SELECTION_STATUS[mStatus];
}
- private NetworkSelectionStatus() {};
+ public void setHasEverConnected(boolean value) {
+ mHasEverConnected = value;
+ }
+
+ public boolean getHasEverConnected() {
+ return mHasEverConnected;
+ }
+
+ private NetworkSelectionStatus() {
+ // previously stored configs will not have this parameter, so we default to false.
+ mHasEverConnected = false;
+ };
/**
* @param reason specific error reason
@@ -1226,6 +1246,7 @@
mNetworkSelectionBSSID = source.mNetworkSelectionBSSID;
setConnectChoice(source.getConnectChoice());
setConnectChoiceTimestamp(source.getConnectChoiceTimestamp());
+ setHasEverConnected(source.getHasEverConnected());
}
public void writeToParcel(Parcel dest) {
@@ -1244,6 +1265,7 @@
} else {
dest.writeInt(CONNECT_CHOICE_NOT_EXISTS);
}
+ dest.writeInt(getHasEverConnected() ? 1 : 0);
}
public void readFromParcel(Parcel in) {
@@ -1262,6 +1284,7 @@
setConnectChoice(null);
setConnectChoiceTimestamp(INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP);
}
+ setHasEverConnected(in.readInt() != 0);
}
}
@@ -1389,6 +1412,8 @@
sbuf.append(" connect choice set time: ").append(mNetworkSelectionStatus
.getConnectChoiceTimestamp());
}
+ sbuf.append(" hasEverConnected: ")
+ .append(mNetworkSelectionStatus.getHasEverConnected()).append("\n");
if (this.numAssociation > 0) {
sbuf.append(" numAssociation ").append(this.numAssociation).append("\n");
diff --git a/wifi/java/android/net/wifi/WifiLinkLayerStats.java b/wifi/java/android/net/wifi/WifiLinkLayerStats.java
index 1de4fd8..edd400b 100644
--- a/wifi/java/android/net/wifi/WifiLinkLayerStats.java
+++ b/wifi/java/android/net/wifi/WifiLinkLayerStats.java
@@ -19,6 +19,8 @@
import android.os.Parcelable;
import android.os.Parcel;
+import java.util.Arrays;
+
/**
* A class representing link layer statistics collected over a Wifi Interface.
*/
@@ -101,6 +103,8 @@
/** {@hide} */
public int tx_time;
/** {@hide} */
+ public int[] tx_time_per_level;
+ /** {@hide} */
public int rx_time;
/** {@hide} */
public int on_time_scan;
@@ -141,9 +145,10 @@
.append(" lost=").append(Long.toString(this.lostmpdu_vo))
.append(" retries=").append(Long.toString(this.retries_vo)).append('\n');
sbuf.append(" on_time : ").append(Integer.toString(this.on_time))
- .append(" tx_time=").append(Integer.toString(this.tx_time))
.append(" rx_time=").append(Integer.toString(this.rx_time))
- .append(" scan_time=").append(Integer.toString(this.on_time_scan)).append('\n');
+ .append(" scan_time=").append(Integer.toString(this.on_time_scan)).append('\n')
+ .append(" tx_time=").append(Integer.toString(this.tx_time))
+ .append(" tx_time_per_level=" + Arrays.toString(tx_time_per_level));
return sbuf.toString();
}
@@ -179,6 +184,7 @@
dest.writeString(BSSID);
dest.writeInt(on_time);
dest.writeInt(tx_time);
+ dest.writeIntArray(tx_time_per_level);
dest.writeInt(rx_time);
dest.writeInt(on_time_scan);
}
@@ -192,6 +198,7 @@
stats.BSSID = in.readString();
stats.on_time = in.readInt();
stats.tx_time = in.readInt();
+ stats.tx_time_per_level = in.createIntArray();
stats.rx_time = in.readInt();
stats.on_time_scan = in.readInt();
return stats;