diff options
162 files changed, 8808 insertions, 5749 deletions
diff --git a/Android.mk b/Android.mk index c12a8e714271..024b2fdcaaf4 100644 --- a/Android.mk +++ b/Android.mk @@ -300,7 +300,6 @@ LOCAL_SRC_FILES += \ core/java/com/android/internal/app/IAssistScreenshotReceiver.aidl \ core/java/com/android/internal/app/IBatteryStats.aidl \ core/java/com/android/internal/app/IEphemeralResolver.aidl \ - core/java/com/android/internal/app/IProcessStats.aidl \ core/java/com/android/internal/app/ISoundTriggerService.aidl \ core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl \ core/java/com/android/internal/app/IVoiceInteractionSessionShowCallback.aidl \ @@ -308,6 +307,7 @@ LOCAL_SRC_FILES += \ core/java/com/android/internal/app/IVoiceInteractorCallback.aidl \ core/java/com/android/internal/app/IVoiceInteractorRequest.aidl \ core/java/com/android/internal/app/IMediaContainerService.aidl \ + core/java/com/android/internal/app/procstats/IProcessStats.aidl \ core/java/com/android/internal/appwidget/IAppWidgetService.aidl \ core/java/com/android/internal/appwidget/IAppWidgetHost.aidl \ core/java/com/android/internal/backup/IBackupTransport.aidl \ diff --git a/api/current.txt b/api/current.txt index 0857c8bb696e..dd70a3298aeb 100644 --- a/api/current.txt +++ b/api/current.txt @@ -416,9 +416,11 @@ package android { field public static final int contentAuthority = 16843408; // 0x1010290 field public static final int contentDescription = 16843379; // 0x1010273 field public static final int contentInsetEnd = 16843860; // 0x1010454 + field public static final int contentInsetEndWithActions = 16844070; // 0x1010526 field public static final int contentInsetLeft = 16843861; // 0x1010455 field public static final int contentInsetRight = 16843862; // 0x1010456 field public static final int contentInsetStart = 16843859; // 0x1010453 + field public static final int contentInsetStartWithNavigation = 16844069; // 0x1010525 field public static final int contextClickable = 16844007; // 0x10104e7 field public static final int contextPopupMenuStyle = 16844034; // 0x1010502 field public static final int controlX1 = 16843772; // 0x10103fc @@ -576,6 +578,7 @@ package android { 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 @@ package android.view { 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 @@ package android.view { 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 @@ package android.widget { method public void collapseActionView(); method public void dismissPopupMenus(); method public int getContentInsetEnd(); + method public int getContentInsetEndWithActions(); method public int getContentInsetLeft(); method public int getContentInsetRight(); method public int getContentInsetStart(); + method public int getContentInsetStartWithNavigation(); + method public int getCurrentContentInsetEnd(); + method public int getCurrentContentInsetLeft(); + method public int getCurrentContentInsetRight(); + method public int getCurrentContentInsetStart(); method public android.graphics.drawable.Drawable getLogo(); method public java.lang.CharSequence getLogoDescription(); method public android.view.Menu getMenu(); @@ -48308,6 +48319,8 @@ package android.widget { method public void inflateMenu(int); method public boolean isOverflowMenuShowing(); method protected void onLayout(boolean, int, int, int, int); + method public void setContentInsetEndWithActions(int); + method public void setContentInsetStartWithNavigation(int); method public void setContentInsetsAbsolute(int, int); method public void setContentInsetsRelative(int, int); method public void setLogo(int); @@ -57330,6 +57343,7 @@ package java.util { 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 @@ package java.util { 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 @@ package java.util { 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 @@ package java.util { 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 @@ package java.util { 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 @@ package java.util { 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 @@ package java.util.concurrent { 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 @@ package org.xmlpull.v1.sax2 { } } + diff --git a/api/system-current.txt b/api/system-current.txt index 219abdfabce0..0fe632d27d88 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -511,9 +511,11 @@ package android { field public static final int contentAuthority = 16843408; // 0x1010290 field public static final int contentDescription = 16843379; // 0x1010273 field public static final int contentInsetEnd = 16843860; // 0x1010454 + field public static final int contentInsetEndWithActions = 16844070; // 0x1010526 field public static final int contentInsetLeft = 16843861; // 0x1010455 field public static final int contentInsetRight = 16843862; // 0x1010456 field public static final int contentInsetStart = 16843859; // 0x1010453 + field public static final int contentInsetStartWithNavigation = 16844069; // 0x1010525 field public static final int contextClickable = 16844007; // 0x10104e7 field public static final int contextPopupMenuStyle = 16844034; // 0x1010502 field public static final int controlX1 = 16843772; // 0x10103fc @@ -671,6 +673,7 @@ package android { 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 @@ -31581,12 +31584,13 @@ package android.os { public class UpdateEngine { ctor public UpdateEngine(); - method public void applyPayload(java.lang.String, long, long, java.lang.String[]) throws android.os.RemoteException; - method public boolean bind(android.os.UpdateEngineCallback, android.os.Handler) throws android.os.RemoteException; - method public boolean bind(android.os.UpdateEngineCallback) throws android.os.RemoteException; - method public void cancel() throws android.os.RemoteException; - method public void resume() throws android.os.RemoteException; - method public void suspend() throws android.os.RemoteException; + method public void applyPayload(java.lang.String, long, long, java.lang.String[]); + method public boolean bind(android.os.UpdateEngineCallback, android.os.Handler); + method public boolean bind(android.os.UpdateEngineCallback); + method public void cancel(); + method public void resetStatus(); + method public void resume(); + method public void suspend(); } public static final class UpdateEngine.ErrorCodeConstants { @@ -45070,6 +45074,7 @@ package android.view { 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(); @@ -45115,6 +45120,7 @@ package android.view { 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(); @@ -48671,7 +48677,6 @@ package android.webkit { 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"; @@ -51351,9 +51356,15 @@ package android.widget { method public void collapseActionView(); method public void dismissPopupMenus(); method public int getContentInsetEnd(); + method public int getContentInsetEndWithActions(); method public int getContentInsetLeft(); method public int getContentInsetRight(); method public int getContentInsetStart(); + method public int getContentInsetStartWithNavigation(); + method public int getCurrentContentInsetEnd(); + method public int getCurrentContentInsetLeft(); + method public int getCurrentContentInsetRight(); + method public int getCurrentContentInsetStart(); method public android.graphics.drawable.Drawable getLogo(); method public java.lang.CharSequence getLogoDescription(); method public android.view.Menu getMenu(); @@ -51372,6 +51383,8 @@ package android.widget { method public void inflateMenu(int); method public boolean isOverflowMenuShowing(); method protected void onLayout(boolean, int, int, int, int); + method public void setContentInsetEndWithActions(int); + method public void setContentInsetStartWithNavigation(int); method public void setContentInsetsAbsolute(int, int); method public void setContentInsetsRelative(int, int); method public void setLogo(int); @@ -60394,6 +60407,7 @@ package java.util { 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(); @@ -61031,6 +61045,7 @@ package java.util { 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 { @@ -61051,6 +61066,9 @@ package java.util { 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); @@ -61058,13 +61076,19 @@ package java.util { 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(); } @@ -61209,9 +61233,11 @@ package java.util { 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[]); @@ -62126,9 +62152,11 @@ package java.util { 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; @@ -62594,6 +62622,7 @@ package java.util.concurrent { 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); @@ -69497,3 +69526,4 @@ package org.xmlpull.v1.sax2 { } } + diff --git a/api/test-current.txt b/api/test-current.txt index 6cf047a2d411..d59fa272088d 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -416,9 +416,11 @@ package android { field public static final int contentAuthority = 16843408; // 0x1010290 field public static final int contentDescription = 16843379; // 0x1010273 field public static final int contentInsetEnd = 16843860; // 0x1010454 + field public static final int contentInsetEndWithActions = 16844070; // 0x1010526 field public static final int contentInsetLeft = 16843861; // 0x1010455 field public static final int contentInsetRight = 16843862; // 0x1010456 field public static final int contentInsetStart = 16843859; // 0x1010453 + field public static final int contentInsetStartWithNavigation = 16844069; // 0x1010525 field public static final int contextClickable = 16844007; // 0x10104e7 field public static final int contextPopupMenuStyle = 16844034; // 0x1010502 field public static final int controlX1 = 16843772; // 0x10103fc @@ -576,6 +578,7 @@ package android { 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 @@ package android.view { 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 @@ package android.view { 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 @@ package android.widget { method public void collapseActionView(); method public void dismissPopupMenus(); method public int getContentInsetEnd(); + method public int getContentInsetEndWithActions(); method public int getContentInsetLeft(); method public int getContentInsetRight(); method public int getContentInsetStart(); + method public int getContentInsetStartWithNavigation(); + method public int getCurrentContentInsetEnd(); + method public int getCurrentContentInsetLeft(); + method public int getCurrentContentInsetRight(); + method public int getCurrentContentInsetStart(); method public android.graphics.drawable.Drawable getLogo(); method public java.lang.CharSequence getLogoDescription(); method public android.view.Menu getMenu(); @@ -48382,6 +48393,8 @@ package android.widget { method public void inflateMenu(int); method public boolean isOverflowMenuShowing(); method protected void onLayout(boolean, int, int, int, int); + method public void setContentInsetEndWithActions(int); + method public void setContentInsetStartWithNavigation(int); method public void setContentInsetsAbsolute(int, int); method public void setContentInsetsRelative(int, int); method public void setLogo(int); @@ -57404,6 +57417,7 @@ package java.util { 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 @@ package java.util { 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 @@ package java.util { 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 @@ package java.util { 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 @@ package java.util { 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 @@ package java.util { 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 @@ package java.util.concurrent { 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 @@ package org.xmlpull.v1.sax2 { } } + diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 887932a7dbbb..0d387e660f8a 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -5920,6 +5920,9 @@ public class Activity extends ContextThemeWrapper * @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/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 6380801af6b0..baaa55d34841 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -31,7 +31,7 @@ import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.util.Log; -import com.android.internal.app.ProcessStats; +import com.android.internal.app.procstats.ProcessStats; import com.android.internal.os.TransferPipe; import com.android.internal.util.FastPrintWriter; @@ -1410,10 +1410,10 @@ public class ActivityManager { public static final int RECENT_IGNORE_HOME_STACK_TASKS = 0x0008; /** - * Ignores all tasks that are on the docked stack. + * Ignores the top task in the docked stack. * @hide */ - public static final int RECENT_INGORE_DOCKED_STACK_TASKS = 0x0010; + public static final int RECENT_INGORE_DOCKED_STACK_TOP_TASK = 0x0010; /** * Ignores all tasks that are on the pinned stack. diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 66e0ada27b52..10259be8d430 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1581,14 +1581,6 @@ public class Intent implements Parcelable, Cloneable { = "android.intent.extra.UNINSTALL_ALL_USERS"; /** - * Specified when the uninstall confirmation dialog is not required to be shown. - * Use with {@link #ACTION_UNINSTALL_PACKAGE} - * @hide - */ - public static final String EXTRA_SKIP_UNINSTALL_CONFIRMATION = - "android.intent.extra.SKIP_UNINSTALL_CONFIRMATION"; - - /** * A string associated with a {@link #ACTION_UPGRADE_SETUP} activity * describing the last run version of the platform that was setup. * @hide diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index e3fb161cf0ab..a0238fb07e5e 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -316,6 +316,12 @@ interface IPackageManager { 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 8f9dcfc15d63..31d377b554f8 100644 --- a/core/java/android/content/pm/IShortcutService.aidl +++ b/core/java/android/content/pm/IShortcutService.aidl @@ -47,4 +47,8 @@ interface IShortcutService { int getIconMaxDimensions(String packageName, int userId); void resetThrottling(); // system only API for developer opsions + + byte[] getBackupPayload(int user); + + void applyRestore(in byte[] payload, int user); }
\ No newline at end of file diff --git a/core/java/android/content/pm/ShortcutServiceInternal.java b/core/java/android/content/pm/ShortcutServiceInternal.java index 4255582f7976..d57f2e6e5de5 100644 --- a/core/java/android/content/pm/ShortcutServiceInternal.java +++ b/core/java/android/content/pm/ShortcutServiceInternal.java @@ -40,28 +40,37 @@ public abstract class ShortcutServiceInternal { } 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/os/UpdateEngine.java b/core/java/android/os/UpdateEngine.java index 80e61462873f..bf03cce947e2 100644 --- a/core/java/android/os/UpdateEngine.java +++ b/core/java/android/os/UpdateEngine.java @@ -87,7 +87,7 @@ public class UpdateEngine { } @SystemApi - public boolean bind(final UpdateEngineCallback callback, final Handler handler) throws RemoteException { + public boolean bind(final UpdateEngineCallback callback, final Handler handler) { IUpdateEngineCallback updateEngineCallback = new IUpdateEngineCallback.Stub() { @Override public void onStatusUpdate(final int status, final float percent) { @@ -118,31 +118,60 @@ public class UpdateEngine { } }; - return mUpdateEngine.bind(updateEngineCallback); + try { + return mUpdateEngine.bind(updateEngineCallback); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } @SystemApi - public boolean bind(final UpdateEngineCallback callback) throws RemoteException { + public boolean bind(final UpdateEngineCallback callback) { return bind(callback, null); } @SystemApi - public void applyPayload(String url, long offset, long size, String[] headerKeyValuePairs) throws RemoteException { - mUpdateEngine.applyPayload(url, offset, size, headerKeyValuePairs); + public void applyPayload(String url, long offset, long size, String[] headerKeyValuePairs) { + try { + mUpdateEngine.applyPayload(url, offset, size, headerKeyValuePairs); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } @SystemApi - public void cancel() throws RemoteException { - mUpdateEngine.cancel(); + public void cancel() { + try { + mUpdateEngine.cancel(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } @SystemApi - public void suspend() throws RemoteException { - mUpdateEngine.suspend(); + public void suspend() { + try { + mUpdateEngine.suspend(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } @SystemApi - public void resume() throws RemoteException { - mUpdateEngine.resume(); + public void resume() { + try { + mUpdateEngine.resume(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @SystemApi + public void resetStatus() { + try { + mUpdateEngine.resetStatus(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } } } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index f7e0e033f0f2..a78f4687df2f 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6089,7 +6089,6 @@ public final class Settings { MOUNT_UMS_AUTOSTART, MOUNT_UMS_PROMPT, MOUNT_UMS_NOTIFY_ENABLED, - UI_NIGHT_MODE, SLEEP_TIMEOUT, DOUBLE_TAP_TO_WAKE, WAKE_GESTURE_ENABLED, diff --git a/core/java/android/text/Hyphenator.java b/core/java/android/text/Hyphenator.java index 085613f18dad..356804ea64bd 100644 --- a/core/java/android/text/Hyphenator.java +++ b/core/java/android/text/Hyphenator.java @@ -152,6 +152,9 @@ public class Hyphenator { {"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 @@ public class Hyphenator { // 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 @@ public class Hyphenator { // 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/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl index a1e2e946c48a..8e1609c587a3 100644 --- a/core/java/android/view/IWindowSession.aidl +++ b/core/java/android/view/IWindowSession.aidl @@ -116,14 +116,11 @@ interface IWindowSession { * @param top The new top position * @param right The new right position * @param bottom The new bottom position - * @param requestedWidth The new requested width - * @param requestedHeight The new requested height * @param deferTransactionUntilFrame Frame number from our parent (attached) to * defer this action until. * @param outFrame Rect in which is placed the new position/size on screen. */ void repositionChild(IWindow childWindow, int left, int top, int right, int bottom, - int requestedWidth, int requestedHeight, long deferTransactionUntilFrame, out Rect outFrame); /* diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 477ffd9b5ba4..8a8fb43cc3d4 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -490,7 +490,7 @@ public class SurfaceView extends View { | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE ; - if (!creating && !force && !mUpdateWindowNeeded) { + if (!creating && !force && !mUpdateWindowNeeded && !sizeChanged) { mLayout.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY; } else { @@ -584,18 +584,6 @@ public class SurfaceView extends View { mSurface.transferFrom(mNewSurface); if (visible && mSurface.isValid()) { - // We set SCALING_MODE_NO_SCALE_CROP to allow the WindowManager - // to update our Surface crop without requiring a new buffer from - // us. In the default mode of SCALING_MODE_FREEZE, surface geometry - // state (which includes crop) is only applied when a buffer - // with appropriate geometry is available. During drag resize - // it is quite frequent that a matching buffer will not be available - // (because we are constantly being resized and have fallen behind). - // However in such situations the WindowManager still needs to be able - // to update our crop to ensure we stay within the bounds of the containing - // window. - mSurface.setScalingMode(Surface.SCALING_MODE_NO_SCALE_CROP); - if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) { mSurfaceCreated = true; mIsCreating = true; @@ -666,7 +654,6 @@ public class SurfaceView extends View { mLocation[0], mLocation[1])); mSession.repositionChild(mWindow, mWindowSpaceLeft, mWindowSpaceTop, mLocation[0], mLocation[1], - mWindowSpaceWidth, mWindowSpaceHeight, -1, mWinFrame); } catch (RemoteException ex) { Log.e(TAG, "Exception from relayout", ex); @@ -703,7 +690,6 @@ public class SurfaceView extends View { } // Just using mRTLastReportedPosition as a dummy rect here session.repositionChild(window, left, top, right, bottom, - mWindowSpaceWidth, mWindowSpaceHeight, frameNumber, mRTLastReportedPosition); // Now overwrite mRTLastReportedPosition with our values diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 83c6e9e1ab83..6811aedadaa0 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -2429,7 +2429,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } } break; + case R.styleable.View_forceHasOverlappingRendering: + if (a.peekValue(attr) != null) { + forceHasOverlappingRendering(a.getBoolean(attr, true)); + } + break; + } } @@ -12116,6 +12141,42 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * 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 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ void setDisplayListProperties(RenderNode renderNode) { if (renderNode != null) { - renderNode.setHasOverlappingRendering(hasOverlappingRendering()); + renderNode.setHasOverlappingRendering(getHasOverlappingRendering()); renderNode.setClipToBounds(mParent instanceof ViewGroup && ((ViewGroup) mParent).getClipChildren()); @@ -16855,9 +16919,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } else { // use layer paint to draw the bitmap, merging the two alphas, but also restore int layerPaintAlpha = mLayerPaint != null ? mLayerPaint.getAlpha() : 255; - mLayerPaint.setAlpha((int) (alpha * layerPaintAlpha)); + if (mLayerPaint == null && alpha < 1) { + mLayerPaint = new Paint(); + mLayerPaint.setAlpha((int) (alpha * layerPaintAlpha)); + } canvas.drawBitmap(cache, 0.0f, 0.0f, mLayerPaint); - mLayerPaint.setAlpha(layerPaintAlpha); + if (mLayerPaint != null) { + mLayerPaint.setAlpha(layerPaintAlpha); + } } } diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java index f18b7acf64f0..c5b8849e88ab 100644 --- a/core/java/android/view/ViewPropertyAnimator.java +++ b/core/java/android/view/ViewPropertyAnimator.java @@ -1113,19 +1113,19 @@ public class ViewPropertyAnimator { if (mListener != null) { mListener.onAnimationEnd(animation); } - if (mAnimatorOnEndMap != null) { - Runnable r = mAnimatorOnEndMap.get(animation); + if (mAnimatorCleanupMap != null) { + Runnable r = mAnimatorCleanupMap.get(animation); if (r != null) { r.run(); } - mAnimatorOnEndMap.remove(animation); + mAnimatorCleanupMap.remove(animation); } - if (mAnimatorCleanupMap != null) { - Runnable r = mAnimatorCleanupMap.get(animation); + if (mAnimatorOnEndMap != null) { + Runnable r = mAnimatorOnEndMap.get(animation); if (r != null) { r.run(); } - mAnimatorCleanupMap.remove(animation); + mAnimatorOnEndMap.remove(animation); } mAnimatorMap.remove(animation); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index fdf69795c968..5d4ee87432a6 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -85,6 +85,7 @@ import android.view.inputmethod.InputMethodManager; import android.widget.Scroller; import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; import com.android.internal.os.IResultReceiver; import com.android.internal.os.SomeArgs; import com.android.internal.policy.PhoneFallbackEventHandler; @@ -154,7 +155,12 @@ public final class ViewRootImpl implements ViewParent, static final ArrayList<ComponentCallbacks> sConfigCallbacks = new ArrayList(); - final ArrayList<WindowCallbacks> mWindowCallbacks = new ArrayList(); + /** + * This list must only be modified by the main thread, so a lock is only needed when changing + * the list or when accessing the list from a non-main thread. + */ + @GuardedBy("mWindowCallbacks") + final ArrayList<WindowCallbacks> mWindowCallbacks = new ArrayList<>(); final Context mContext; final IWindowSession mWindowSession; final Display mDisplay; @@ -2120,7 +2126,7 @@ public final class ViewRootImpl implements ViewParent, 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(); @@ -2169,7 +2175,6 @@ public final class ViewRootImpl implements ViewParent, } } } - private void handleOutOfResourcesException(Surface.OutOfResourcesException e) { Log.e(mTag, "OutOfResourcesException initializing HW surface", e); try { @@ -2437,10 +2442,8 @@ public final class ViewRootImpl implements ViewParent, @Override public void onHardwarePostDraw(DisplayListCanvas canvas) { drawAccessibilityFocusedDrawableIfNeeded(canvas); - synchronized (mWindowCallbacks) { - for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { - mWindowCallbacks.get(i).onPostDraw(canvas); - } + for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { + mWindowCallbacks.get(i).onPostDraw(canvas); } } @@ -7092,11 +7095,9 @@ public final class ViewRootImpl implements ViewParent, Rect stableInsets, int resizeMode) { if (!mDragResizing) { mDragResizing = true; - synchronized (mWindowCallbacks) { - for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { - mWindowCallbacks.get(i).onWindowDragResizeStart(initialBounds, fullscreen, - systemInsets, stableInsets, resizeMode); - } + for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { + mWindowCallbacks.get(i).onWindowDragResizeStart(initialBounds, fullscreen, + systemInsets, stableInsets, resizeMode); } mFullRedrawNeeded = true; } @@ -7108,10 +7109,8 @@ public final class ViewRootImpl implements ViewParent, private void endDragResizing() { if (mDragResizing) { mDragResizing = false; - synchronized (mWindowCallbacks) { - for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { - mWindowCallbacks.get(i).onWindowDragResizeEnd(); - } + for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { + mWindowCallbacks.get(i).onWindowDragResizeEnd(); } mFullRedrawNeeded = true; } @@ -7119,13 +7118,11 @@ public final class ViewRootImpl implements ViewParent, private boolean updateContentDrawBounds() { boolean updated = false; - synchronized (mWindowCallbacks) { - for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { - updated |= mWindowCallbacks.get(i).onContentDrawn( - mWindowAttributes.surfaceInsets.left, - mWindowAttributes.surfaceInsets.top, - mWidth, mHeight); - } + for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { + updated |= mWindowCallbacks.get(i).onContentDrawn( + mWindowAttributes.surfaceInsets.left, + mWindowAttributes.surfaceInsets.top, + mWidth, mHeight); } return updated | (mDragResizing && mReportNextDraw); } @@ -7134,10 +7131,8 @@ public final class ViewRootImpl implements ViewParent, if (mReportNextDraw) { mWindowDrawCountDown = new CountDownLatch(mWindowCallbacks.size()); } - synchronized (mWindowCallbacks) { - for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { - mWindowCallbacks.get(i).onRequestDraw(mReportNextDraw); - } + for (int i = mWindowCallbacks.size() - 1; i >= 0; i--) { + mWindowCallbacks.get(i).onRequestDraw(mReportNextDraw); } } diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java index 929fdac82c28..750931ab661c 100644 --- a/core/java/android/view/WindowInsets.java +++ b/core/java/android/view/WindowInsets.java @@ -91,6 +91,7 @@ public final class WindowInsets { mWindowDecorInsetsConsumed = src.mWindowDecorInsetsConsumed; mStableInsetsConsumed = src.mStableInsetsConsumed; mIsRound = src.mIsRound; + mAlwaysConsumeNavBar = src.mAlwaysConsumeNavBar; } /** @hide */ diff --git a/core/java/android/view/WindowManagerInternal.java b/core/java/android/view/WindowManagerInternal.java index c22d60d94818..3ad730b08590 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 class WindowManagerInternal { 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 bdaf291a2d3c..14821118f728 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -2926,8 +2926,10 @@ public class AccessibilityNodeInfo implements Parcelable { 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 @@ public class AccessibilityNodeInfo implements Parcelable { 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 @@ public class AccessibilityNodeInfo implements Parcelable { 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 f1bf890b3c86..d884f199dd86 100644 --- a/core/java/android/webkit/WebViewFactory.java +++ b/core/java/android/webkit/WebViewFactory.java @@ -128,11 +128,6 @@ public final class WebViewFactory { 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 18687c975432..92631da2da90 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -173,9 +173,6 @@ public class PopupWindow { 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 @@ public class PopupWindow { mPopupViewInitialLayoutDirectionInherited = (mContentView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT); - mPopupWidth = p.width; - mPopupHeight = p.height; } /** @@ -2006,7 +2001,7 @@ public class PopupWindow { * @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 @@ public class PopupWindow { * @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 @@ public class PopupWindow { 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 31950970219f..a9af654b3ae9 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** + * 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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * 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 06daf61937c1..5b0a90adde33 100644 --- a/core/java/android/widget/Toolbar.java +++ b/core/java/android/widget/Toolbar.java @@ -106,6 +106,8 @@ import java.util.List; * @attr ref android.R.styleable#Toolbar_contentInsetLeft * @attr ref android.R.styleable#Toolbar_contentInsetRight * @attr ref android.R.styleable#Toolbar_contentInsetStart + * @attr ref android.R.styleable#Toolbar_contentInsetStartWithNavigation + * @attr ref android.R.styleable#Toolbar_contentInsetEndWithActions * @attr ref android.R.styleable#Toolbar_gravity * @attr ref android.R.styleable#Toolbar_logo * @attr ref android.R.styleable#Toolbar_logoDescription @@ -159,6 +161,8 @@ public class Toolbar extends ViewGroup { private int mTitleMarginBottom; private final RtlSpacingHelper mContentInsets = new RtlSpacingHelper(); + private int mContentInsetStartWithNavigation; + private int mContentInsetEndWithActions; private int mGravity = Gravity.START | Gravity.CENTER_VERTICAL; @@ -272,6 +276,11 @@ public class Toolbar extends ViewGroup { mContentInsets.setRelative(contentInsetStart, contentInsetEnd); } + mContentInsetStartWithNavigation = a.getDimensionPixelOffset( + R.styleable.Toolbar_contentInsetStartWithNavigation, RtlSpacingHelper.UNDEFINED); + mContentInsetEndWithActions = a.getDimensionPixelOffset( + R.styleable.Toolbar_contentInsetEndWithActions, RtlSpacingHelper.UNDEFINED); + mCollapseIcon = a.getDrawable(R.styleable.Toolbar_collapseIcon); mCollapseDescription = a.getText(R.styleable.Toolbar_collapseContentDescription); @@ -1055,7 +1064,7 @@ public class Toolbar extends ViewGroup { } /** - * Set the content insets for this toolbar relative to layout direction. + * Sets the content insets for this toolbar relative to layout direction. * * <p>The content inset affects the valid area for Toolbar content other than * the navigation button and menu. Insets define the minimum margin for these components @@ -1069,13 +1078,15 @@ public class Toolbar extends ViewGroup { * @see #getContentInsetEnd() * @see #getContentInsetLeft() * @see #getContentInsetRight() + * @attr ref android.R.styleable#Toolbar_contentInsetEnd + * @attr ref android.R.styleable#Toolbar_contentInsetStart */ public void setContentInsetsRelative(int contentInsetStart, int contentInsetEnd) { mContentInsets.setRelative(contentInsetStart, contentInsetEnd); } /** - * Get the starting content inset for this toolbar. + * Gets the starting content inset for this toolbar. * * <p>The content inset affects the valid area for Toolbar content other than * the navigation button and menu. Insets define the minimum margin for these components @@ -1088,13 +1099,14 @@ public class Toolbar extends ViewGroup { * @see #getContentInsetEnd() * @see #getContentInsetLeft() * @see #getContentInsetRight() + * @attr ref android.R.styleable#Toolbar_contentInsetStart */ public int getContentInsetStart() { return mContentInsets.getStart(); } /** - * Get the ending content inset for this toolbar. + * Gets the ending content inset for this toolbar. * * <p>The content inset affects the valid area for Toolbar content other than * the navigation button and menu. Insets define the minimum margin for these components @@ -1107,13 +1119,14 @@ public class Toolbar extends ViewGroup { * @see #getContentInsetStart() * @see #getContentInsetLeft() * @see #getContentInsetRight() + * @attr ref android.R.styleable#Toolbar_contentInsetEnd */ public int getContentInsetEnd() { return mContentInsets.getEnd(); } /** - * Set the content insets for this toolbar. + * Sets the content insets for this toolbar. * * <p>The content inset affects the valid area for Toolbar content other than * the navigation button and menu. Insets define the minimum margin for these components @@ -1127,13 +1140,15 @@ public class Toolbar extends ViewGroup { * @see #getContentInsetEnd() * @see #getContentInsetLeft() * @see #getContentInsetRight() + * @attr ref android.R.styleable#Toolbar_contentInsetLeft + * @attr ref android.R.styleable#Toolbar_contentInsetRight */ public void setContentInsetsAbsolute(int contentInsetLeft, int contentInsetRight) { mContentInsets.setAbsolute(contentInsetLeft, contentInsetRight); } /** - * Get the left content inset for this toolbar. + * Gets the left content inset for this toolbar. * * <p>The content inset affects the valid area for Toolbar content other than * the navigation button and menu. Insets define the minimum margin for these components @@ -1146,13 +1161,14 @@ public class Toolbar extends ViewGroup { * @see #getContentInsetStart() * @see #getContentInsetEnd() * @see #getContentInsetRight() + * @attr ref android.R.styleable#Toolbar_contentInsetLeft */ public int getContentInsetLeft() { return mContentInsets.getLeft(); } /** - * Get the right content inset for this toolbar. + * Gets the right content inset for this toolbar. * * <p>The content inset affects the valid area for Toolbar content other than * the navigation button and menu. Insets define the minimum margin for these components @@ -1165,11 +1181,160 @@ public class Toolbar extends ViewGroup { * @see #getContentInsetStart() * @see #getContentInsetEnd() * @see #getContentInsetLeft() + * @attr ref android.R.styleable#Toolbar_contentInsetRight */ public int getContentInsetRight() { return mContentInsets.getRight(); } + /** + * Gets the start content inset to use when a navigation button is present. + * + * <p>Different content insets are often called for when additional buttons are present + * in the toolbar, as well as at different toolbar sizes. The larger value of + * {@link #getContentInsetStart()} and this value will be used during layout.</p> + * + * @return the start content inset used when a navigation icon has been set in pixels + * + * @see #setContentInsetStartWithNavigation(int) + * @attr ref android.R.styleable#Toolbar_contentInsetStartWithNavigation + */ + public int getContentInsetStartWithNavigation() { + return mContentInsetStartWithNavigation != RtlSpacingHelper.UNDEFINED + ? mContentInsetStartWithNavigation + : getContentInsetStart(); + } + + /** + * Sets the start content inset to use when a navigation button is present. + * + * <p>Different content insets are often called for when additional buttons are present + * in the toolbar, as well as at different toolbar sizes. The larger value of + * {@link #getContentInsetStart()} and this value will be used during layout.</p> + * + * @param insetStartWithNavigation the inset to use when a navigation icon has been set + * in pixels + * + * @see #getContentInsetStartWithNavigation() + * @attr ref android.R.styleable#Toolbar_contentInsetStartWithNavigation + */ + public void setContentInsetStartWithNavigation(int insetStartWithNavigation) { + if (insetStartWithNavigation < 0) { + insetStartWithNavigation = RtlSpacingHelper.UNDEFINED; + } + if (insetStartWithNavigation != mContentInsetStartWithNavigation) { + mContentInsetStartWithNavigation = insetStartWithNavigation; + if (getNavigationIcon() != null) { + requestLayout(); + } + } + } + + /** + * Gets the end content inset to use when action buttons are present. + * + * <p>Different content insets are often called for when additional buttons are present + * in the toolbar, as well as at different toolbar sizes. The larger value of + * {@link #getContentInsetEnd()} and this value will be used during layout.</p> + * + * @return the end content inset used when a menu has been set in pixels + * + * @see #setContentInsetEndWithActions(int) + * @attr ref android.R.styleable#Toolbar_contentInsetEndWithActions + */ + public int getContentInsetEndWithActions() { + return mContentInsetEndWithActions != RtlSpacingHelper.UNDEFINED + ? mContentInsetEndWithActions + : getContentInsetEnd(); + } + + /** + * Sets the start content inset to use when action buttons are present. + * + * <p>Different content insets are often called for when additional buttons are present + * in the toolbar, as well as at different toolbar sizes. The larger value of + * {@link #getContentInsetEnd()} and this value will be used during layout.</p> + * + * @param insetEndWithActions the inset to use when a menu has been set in pixels + * + * @see #setContentInsetEndWithActions(int) + * @attr ref android.R.styleable#Toolbar_contentInsetEndWithActions + */ + public void setContentInsetEndWithActions(int insetEndWithActions) { + if (insetEndWithActions < 0) { + insetEndWithActions = RtlSpacingHelper.UNDEFINED; + } + if (insetEndWithActions != mContentInsetEndWithActions) { + mContentInsetEndWithActions = insetEndWithActions; + if (getNavigationIcon() != null) { + requestLayout(); + } + } + } + + /** + * Gets the content inset that will be used on the starting side of the bar in the current + * toolbar configuration. + * + * @return the current content inset start in pixels + * + * @see #getContentInsetStartWithNavigation() + */ + public int getCurrentContentInsetStart() { + return getNavigationIcon() != null + ? Math.max(getContentInsetStart(), Math.max(mContentInsetStartWithNavigation, 0)) + : getContentInsetStart(); + } + + /** + * Gets the content inset that will be used on the ending side of the bar in the current + * toolbar configuration. + * + * @return the current content inset end in pixels + * + * @see #getContentInsetEndWithActions() + */ + public int getCurrentContentInsetEnd() { + boolean hasActions = false; + if (mMenuView != null) { + final MenuBuilder mb = mMenuView.peekMenu(); + hasActions = mb != null && mb.hasVisibleItems(); + } + return hasActions + ? Math.max(getContentInsetEnd(), Math.max(mContentInsetEndWithActions, 0)) + : getContentInsetEnd(); + } + + /** + * Gets the content inset that will be used on the left side of the bar in the current + * toolbar configuration. + * + * @return the current content inset left in pixels + * + * @see #getContentInsetStartWithNavigation() + * @see #getContentInsetEndWithActions() + */ + public int getCurrentContentInsetLeft() { + return isLayoutRtl() + ? getCurrentContentInsetEnd() + : getCurrentContentInsetStart(); + } + + /** + * Gets the content inset that will be used on the right side of the bar in the current + * toolbar configuration. + * + * @return the current content inset right in pixels + * + * @see #getContentInsetStartWithNavigation() + * @see #getContentInsetEndWithActions() + */ + public int getCurrentContentInsetRight() { + return isLayoutRtl() + ? getCurrentContentInsetStart() + : getCurrentContentInsetEnd(); + } + private void ensureNavButtonView() { if (mNavButtonView == null) { mNavButtonView = new ImageButton(getContext(), null, 0, mNavButtonStyle); @@ -1406,7 +1571,7 @@ public class Toolbar extends ViewGroup { childState = combineMeasuredStates(childState, mCollapseButtonView.getMeasuredState()); } - final int contentInsetStart = getContentInsetStart(); + final int contentInsetStart = getCurrentContentInsetStart(); width += Math.max(contentInsetStart, navWidth); collapsingMargins[marginStartIndex] = Math.max(0, contentInsetStart - navWidth); @@ -1420,7 +1585,7 @@ public class Toolbar extends ViewGroup { childState = combineMeasuredStates(childState, mMenuView.getMeasuredState()); } - final int contentInsetEnd = getContentInsetEnd(); + final int contentInsetEnd = getCurrentContentInsetEnd(); width += Math.max(contentInsetEnd, menuWidth); collapsingMargins[marginEndIndex] = Math.max(0, contentInsetEnd - menuWidth); @@ -1543,10 +1708,12 @@ public class Toolbar extends ViewGroup { } } - collapsingMargins[0] = Math.max(0, getContentInsetLeft() - left); - collapsingMargins[1] = Math.max(0, getContentInsetRight() - (width - paddingRight - right)); - left = Math.max(left, getContentInsetLeft()); - right = Math.min(right, width - paddingRight - getContentInsetRight()); + final int contentInsetLeft = getCurrentContentInsetLeft(); + final int contentInsetRight = getCurrentContentInsetRight(); + collapsingMargins[0] = Math.max(0, contentInsetLeft - left); + collapsingMargins[1] = Math.max(0, contentInsetRight - (width - paddingRight - right)); + left = Math.max(left, contentInsetLeft); + right = Math.min(right, width - paddingRight - contentInsetRight); if (shouldLayout(mExpandedActionView)) { if (isRtl) { diff --git a/core/java/com/android/internal/app/ProcessMap.java b/core/java/com/android/internal/app/ProcessMap.java index 23a8bd7a348c..81036f7ecba8 100644 --- a/core/java/com/android/internal/app/ProcessMap.java +++ b/core/java/com/android/internal/app/ProcessMap.java @@ -54,4 +54,8 @@ public class ProcessMap<E> { public ArrayMap<String, SparseArray<E>> getMap() { return mMap; } + + public int size() { + return mMap.size(); + } } diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java deleted file mode 100644 index 17ca904d50b0..000000000000 --- a/core/java/com/android/internal/app/ProcessStats.java +++ /dev/null @@ -1,3794 +0,0 @@ -/* - * Copyright (C) 2013 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.internal.app; - -import android.os.Parcel; -import android.os.Parcelable; -import android.os.SystemClock; -import android.os.SystemProperties; -import android.os.UserHandle; -import android.text.format.DateFormat; -import android.util.ArrayMap; -import android.util.ArraySet; -import android.util.DebugUtils; -import android.util.Log; -import android.util.Slog; -import android.util.SparseArray; -import android.util.TimeUtils; - -import com.android.internal.util.GrowingArrayUtils; - -import dalvik.system.VMRuntime; -import libcore.util.EmptyArray; - -import java.io.IOException; -import java.io.InputStream; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.Objects; - -public final class ProcessStats implements Parcelable { - static final String TAG = "ProcessStats"; - static final boolean DEBUG = false; - static final boolean DEBUG_PARCEL = false; - - public static final String SERVICE_NAME = "procstats"; - - // How often the service commits its data, giving the minimum batching - // that is done. - public static long COMMIT_PERIOD = 3*60*60*1000; // Commit current stats every 3 hours - - // Minimum uptime period before committing. If the COMMIT_PERIOD has elapsed but - // the total uptime has not exceeded this amount, then the commit will be held until - // it is reached. - public static long COMMIT_UPTIME_PERIOD = 60*60*1000; // Must have at least 1 hour elapsed - - public static final int STATE_NOTHING = -1; - public static final int STATE_PERSISTENT = 0; - public static final int STATE_TOP = 1; - public static final int STATE_IMPORTANT_FOREGROUND = 2; - public static final int STATE_IMPORTANT_BACKGROUND = 3; - public static final int STATE_BACKUP = 4; - public static final int STATE_HEAVY_WEIGHT = 5; - public static final int STATE_SERVICE = 6; - public static final int STATE_SERVICE_RESTARTING = 7; - public static final int STATE_RECEIVER = 8; - public static final int STATE_HOME = 9; - public static final int STATE_LAST_ACTIVITY = 10; - public static final int STATE_CACHED_ACTIVITY = 11; - public static final int STATE_CACHED_ACTIVITY_CLIENT = 12; - public static final int STATE_CACHED_EMPTY = 13; - public static final int STATE_COUNT = STATE_CACHED_EMPTY+1; - - public static final int PSS_SAMPLE_COUNT = 0; - public static final int PSS_MINIMUM = 1; - public static final int PSS_AVERAGE = 2; - public static final int PSS_MAXIMUM = 3; - public static final int PSS_USS_MINIMUM = 4; - public static final int PSS_USS_AVERAGE = 5; - public static final int PSS_USS_MAXIMUM = 6; - public static final int PSS_COUNT = PSS_USS_MAXIMUM+1; - - public static final int SYS_MEM_USAGE_SAMPLE_COUNT = 0; - public static final int SYS_MEM_USAGE_CACHED_MINIMUM = 1; - public static final int SYS_MEM_USAGE_CACHED_AVERAGE = 2; - public static final int SYS_MEM_USAGE_CACHED_MAXIMUM = 3; - public static final int SYS_MEM_USAGE_FREE_MINIMUM = 4; - public static final int SYS_MEM_USAGE_FREE_AVERAGE = 5; - public static final int SYS_MEM_USAGE_FREE_MAXIMUM = 6; - public static final int SYS_MEM_USAGE_ZRAM_MINIMUM = 7; - public static final int SYS_MEM_USAGE_ZRAM_AVERAGE = 8; - public static final int SYS_MEM_USAGE_ZRAM_MAXIMUM = 9; - public static final int SYS_MEM_USAGE_KERNEL_MINIMUM = 10; - public static final int SYS_MEM_USAGE_KERNEL_AVERAGE = 11; - public static final int SYS_MEM_USAGE_KERNEL_MAXIMUM = 12; - public static final int SYS_MEM_USAGE_NATIVE_MINIMUM = 13; - public static final int SYS_MEM_USAGE_NATIVE_AVERAGE = 14; - public static final int SYS_MEM_USAGE_NATIVE_MAXIMUM = 15; - public static final int SYS_MEM_USAGE_COUNT = SYS_MEM_USAGE_NATIVE_MAXIMUM+1; - - public static final int ADJ_NOTHING = -1; - public static final int ADJ_MEM_FACTOR_NORMAL = 0; - public static final int ADJ_MEM_FACTOR_MODERATE = 1; - public static final int ADJ_MEM_FACTOR_LOW = 2; - public static final int ADJ_MEM_FACTOR_CRITICAL = 3; - public static final int ADJ_MEM_FACTOR_COUNT = ADJ_MEM_FACTOR_CRITICAL+1; - public static final int ADJ_SCREEN_MOD = ADJ_MEM_FACTOR_COUNT; - public static final int ADJ_SCREEN_OFF = 0; - public static final int ADJ_SCREEN_ON = ADJ_SCREEN_MOD; - public static final int ADJ_COUNT = ADJ_SCREEN_ON*2; - - public static final int FLAG_COMPLETE = 1<<0; - public static final int FLAG_SHUTDOWN = 1<<1; - public static final int FLAG_SYSPROPS = 1<<2; - - public static final int[] ALL_MEM_ADJ = new int[] { ADJ_MEM_FACTOR_NORMAL, - ADJ_MEM_FACTOR_MODERATE, ADJ_MEM_FACTOR_LOW, ADJ_MEM_FACTOR_CRITICAL }; - - public static final int[] ALL_SCREEN_ADJ = new int[] { ADJ_SCREEN_OFF, ADJ_SCREEN_ON }; - - public static final int[] NON_CACHED_PROC_STATES = new int[] { - STATE_PERSISTENT, STATE_TOP, STATE_IMPORTANT_FOREGROUND, - STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, STATE_HEAVY_WEIGHT, - STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER - }; - - public static final int[] BACKGROUND_PROC_STATES = new int[] { - STATE_IMPORTANT_FOREGROUND, STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, - STATE_HEAVY_WEIGHT, STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER - }; - - // Map from process states to the states we track. - static final int[] PROCESS_STATE_TO_STATE = new int[] { - STATE_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT - STATE_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT_UI - STATE_TOP, // ActivityManager.PROCESS_STATE_TOP - STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE - STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE - STATE_TOP, // ActivityManager.PROCESS_STATE_TOP_SLEEPING - STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND - STATE_IMPORTANT_BACKGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND - STATE_BACKUP, // ActivityManager.PROCESS_STATE_BACKUP - STATE_HEAVY_WEIGHT, // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT - STATE_SERVICE, // ActivityManager.PROCESS_STATE_SERVICE - STATE_RECEIVER, // ActivityManager.PROCESS_STATE_RECEIVER - STATE_HOME, // ActivityManager.PROCESS_STATE_HOME - STATE_LAST_ACTIVITY, // ActivityManager.PROCESS_STATE_LAST_ACTIVITY - STATE_CACHED_ACTIVITY, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY - STATE_CACHED_ACTIVITY_CLIENT, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT - STATE_CACHED_EMPTY, // ActivityManager.PROCESS_STATE_CACHED_EMPTY - }; - - public static final int[] ALL_PROC_STATES = new int[] { STATE_PERSISTENT, - STATE_TOP, STATE_IMPORTANT_FOREGROUND, STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, - STATE_HEAVY_WEIGHT, STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER, - STATE_HOME, STATE_LAST_ACTIVITY, STATE_CACHED_ACTIVITY, - STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_EMPTY - }; - - static final String[] STATE_NAMES = new String[] { - "Persist", "Top ", "ImpFg ", "ImpBg ", - "Backup ", "HeavyWt", "Service", "ServRst", - "Receivr", "Home ", - "LastAct", "CchAct ", "CchCAct", "CchEmty" - }; - - public static final String[] ADJ_SCREEN_NAMES_CSV = new String[] { - "off", "on" - }; - - public static final String[] ADJ_MEM_NAMES_CSV = new String[] { - "norm", "mod", "low", "crit" - }; - - public static final String[] STATE_NAMES_CSV = new String[] { - "pers", "top", "impfg", "impbg", "backup", "heavy", - "service", "service-rs", "receiver", "home", "lastact", - "cch-activity", "cch-aclient", "cch-empty" - }; - - static final String[] ADJ_SCREEN_TAGS = new String[] { - "0", "1" - }; - - static final String[] ADJ_MEM_TAGS = new String[] { - "n", "m", "l", "c" - }; - - static final String[] STATE_TAGS = new String[] { - "p", "t", "f", "b", "u", "w", - "s", "x", "r", "h", "l", "a", "c", "e" - }; - - static final String CSV_SEP = "\t"; - - // Current version of the parcel format. - private static final int PARCEL_VERSION = 18; - // In-memory Parcel magic number, used to detect attempts to unmarshall bad data - private static final int MAGIC = 0x50535453; - - // Where the "type"/"state" part of the data appears in an offset integer. - static int OFFSET_TYPE_SHIFT = 0; - static int OFFSET_TYPE_MASK = 0xff; - // Where the "which array" part of the data appears in an offset integer. - static int OFFSET_ARRAY_SHIFT = 8; - static int OFFSET_ARRAY_MASK = 0xff; - // Where the "index into array" part of the data appears in an offset integer. - static int OFFSET_INDEX_SHIFT = 16; - static int OFFSET_INDEX_MASK = 0xffff; - - public String mReadError; - public String mTimePeriodStartClockStr; - public int mFlags; - - public final ProcessMap<SparseArray<PackageState>> mPackages - = new ProcessMap<SparseArray<PackageState>>(); - public final ProcessMap<ProcessState> mProcesses = new ProcessMap<ProcessState>(); - - public final long[] mMemFactorDurations = new long[ADJ_COUNT]; - public int mMemFactor = STATE_NOTHING; - public long mStartTime; - - public int[] mSysMemUsageTable = null; - public int mSysMemUsageTableSize = 0; - public final long[] mSysMemUsageArgs = new long[SYS_MEM_USAGE_COUNT]; - - public long mTimePeriodStartClock; - public long mTimePeriodStartRealtime; - public long mTimePeriodEndRealtime; - public long mTimePeriodStartUptime; - public long mTimePeriodEndUptime; - String mRuntime; - boolean mRunning; - - static final int LONGS_SIZE = 4096; - final ArrayList<long[]> mLongs = new ArrayList<long[]>(); - int mNextLong; - - int[] mAddLongTable; - int mAddLongTableSize; - - // For writing parcels. - ArrayMap<String, Integer> mCommonStringToIndex; - - // For reading parcels. - ArrayList<String> mIndexToCommonString; - - public ProcessStats(boolean running) { - mRunning = running; - reset(); - } - - public ProcessStats(Parcel in) { - reset(); - readFromParcel(in); - } - - public void add(ProcessStats other) { - ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = other.mPackages.getMap(); - for (int ip=0; ip<pkgMap.size(); ip++) { - final String pkgName = pkgMap.keyAt(ip); - final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip); - for (int iu=0; iu<uids.size(); iu++) { - final int uid = uids.keyAt(iu); - final SparseArray<PackageState> versions = uids.valueAt(iu); - for (int iv=0; iv<versions.size(); iv++) { - final int vers = versions.keyAt(iv); - final PackageState otherState = versions.valueAt(iv); - final int NPROCS = otherState.mProcesses.size(); - final int NSRVS = otherState.mServices.size(); - for (int iproc=0; iproc<NPROCS; iproc++) { - ProcessState otherProc = otherState.mProcesses.valueAt(iproc); - if (otherProc.mCommonProcess != otherProc) { - if (DEBUG) Slog.d(TAG, "Adding pkg " + pkgName + " uid " + uid - + " vers " + vers + " proc " + otherProc.mName); - ProcessState thisProc = getProcessStateLocked(pkgName, uid, vers, - otherProc.mName); - if (thisProc.mCommonProcess == thisProc) { - if (DEBUG) Slog.d(TAG, "Existing process is single-package, splitting"); - thisProc.mMultiPackage = true; - long now = SystemClock.uptimeMillis(); - final PackageState pkgState = getPackageStateLocked(pkgName, uid, - vers); - thisProc = thisProc.clone(thisProc.mPackage, now); - pkgState.mProcesses.put(thisProc.mName, thisProc); - } - thisProc.add(otherProc); - } - } - for (int isvc=0; isvc<NSRVS; isvc++) { - ServiceState otherSvc = otherState.mServices.valueAt(isvc); - if (DEBUG) Slog.d(TAG, "Adding pkg " + pkgName + " uid " + uid - + " service " + otherSvc.mName); - ServiceState thisSvc = getServiceStateLocked(pkgName, uid, vers, - otherSvc.mProcessName, otherSvc.mName); - thisSvc.add(otherSvc); - } - } - } - } - - ArrayMap<String, SparseArray<ProcessState>> procMap = other.mProcesses.getMap(); - for (int ip=0; ip<procMap.size(); ip++) { - SparseArray<ProcessState> uids = procMap.valueAt(ip); - for (int iu=0; iu<uids.size(); iu++) { - int uid = uids.keyAt(iu); - ProcessState otherProc = uids.valueAt(iu); - ProcessState thisProc = mProcesses.get(otherProc.mName, uid); - if (DEBUG) Slog.d(TAG, "Adding uid " + uid + " proc " + otherProc.mName); - if (thisProc == null) { - if (DEBUG) Slog.d(TAG, "Creating new process!"); - thisProc = new ProcessState(this, otherProc.mPackage, uid, otherProc.mVersion, - otherProc.mName); - mProcesses.put(otherProc.mName, uid, thisProc); - PackageState thisState = getPackageStateLocked(otherProc.mPackage, uid, - otherProc.mVersion); - if (!thisState.mProcesses.containsKey(otherProc.mName)) { - thisState.mProcesses.put(otherProc.mName, thisProc); - } - } - thisProc.add(otherProc); - } - } - - for (int i=0; i<ADJ_COUNT; i++) { - if (DEBUG) Slog.d(TAG, "Total duration #" + i + " inc by " - + other.mMemFactorDurations[i] + " from " - + mMemFactorDurations[i]); - mMemFactorDurations[i] += other.mMemFactorDurations[i]; - } - - for (int i=0; i<other.mSysMemUsageTableSize; i++) { - int ent = other.mSysMemUsageTable[i]; - int state = (ent>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; - long[] longs = other.mLongs.get((ent>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); - addSysMemUsage(state, longs, ((ent >> OFFSET_INDEX_SHIFT) & OFFSET_INDEX_MASK)); - } - - if (other.mTimePeriodStartClock < mTimePeriodStartClock) { - mTimePeriodStartClock = other.mTimePeriodStartClock; - mTimePeriodStartClockStr = other.mTimePeriodStartClockStr; - } - mTimePeriodEndRealtime += other.mTimePeriodEndRealtime - other.mTimePeriodStartRealtime; - mTimePeriodEndUptime += other.mTimePeriodEndUptime - other.mTimePeriodStartUptime; - } - - public void addSysMemUsage(long cachedMem, long freeMem, long zramMem, long kernelMem, - long nativeMem) { - if (mMemFactor != STATE_NOTHING) { - int state = mMemFactor * STATE_COUNT; - mSysMemUsageArgs[SYS_MEM_USAGE_SAMPLE_COUNT] = 1; - for (int i=0; i<3; i++) { - mSysMemUsageArgs[SYS_MEM_USAGE_CACHED_MINIMUM + i] = cachedMem; - mSysMemUsageArgs[SYS_MEM_USAGE_FREE_MINIMUM + i] = freeMem; - mSysMemUsageArgs[SYS_MEM_USAGE_ZRAM_MINIMUM + i] = zramMem; - mSysMemUsageArgs[SYS_MEM_USAGE_KERNEL_MINIMUM + i] = kernelMem; - mSysMemUsageArgs[SYS_MEM_USAGE_NATIVE_MINIMUM + i] = nativeMem; - } - addSysMemUsage(state, mSysMemUsageArgs, 0); - } - } - - void addSysMemUsage(int state, long[] data, int dataOff) { - int idx = binarySearch(mSysMemUsageTable, mSysMemUsageTableSize, state); - int off; - if (idx >= 0) { - off = mSysMemUsageTable[idx]; - } else { - mAddLongTable = mSysMemUsageTable; - mAddLongTableSize = mSysMemUsageTableSize; - off = addLongData(~idx, state, SYS_MEM_USAGE_COUNT); - mSysMemUsageTable = mAddLongTable; - mSysMemUsageTableSize = mAddLongTableSize; - } - long[] longs = mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); - idx = (off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK; - addSysMemUsage(longs, idx, data, dataOff); - } - - static void addSysMemUsage(long[] dstData, int dstOff, long[] addData, int addOff) { - final long dstCount = dstData[dstOff+SYS_MEM_USAGE_SAMPLE_COUNT]; - final long addCount = addData[addOff+SYS_MEM_USAGE_SAMPLE_COUNT]; - if (dstCount == 0) { - dstData[dstOff+SYS_MEM_USAGE_SAMPLE_COUNT] = addCount; - for (int i=SYS_MEM_USAGE_CACHED_MINIMUM; i<SYS_MEM_USAGE_COUNT; i++) { - dstData[dstOff+i] = addData[addOff+i]; - } - } else if (addCount > 0) { - dstData[dstOff+SYS_MEM_USAGE_SAMPLE_COUNT] = dstCount + addCount; - for (int i=SYS_MEM_USAGE_CACHED_MINIMUM; i<SYS_MEM_USAGE_COUNT; i+=3) { - if (dstData[dstOff+i] > addData[addOff+i]) { - dstData[dstOff+i] = addData[addOff+i]; - } - dstData[dstOff+i+1] = (long)( - ((dstData[dstOff+i+1]*(double)dstCount) - + (addData[addOff+i+1]*(double)addCount)) - / (dstCount+addCount) ); - if (dstData[dstOff+i+2] < addData[addOff+i+2]) { - dstData[dstOff+i+2] = addData[addOff+i+2]; - } - } - } - } - - public static final Parcelable.Creator<ProcessStats> CREATOR - = new Parcelable.Creator<ProcessStats>() { - public ProcessStats createFromParcel(Parcel in) { - return new ProcessStats(in); - } - - public ProcessStats[] newArray(int size) { - return new ProcessStats[size]; - } - }; - - private static void printScreenLabel(PrintWriter pw, int offset) { - switch (offset) { - case ADJ_NOTHING: - pw.print(" "); - break; - case ADJ_SCREEN_OFF: - pw.print("SOff/"); - break; - case ADJ_SCREEN_ON: - pw.print("SOn /"); - break; - default: - pw.print("????/"); - break; - } - } - - public static void printScreenLabelCsv(PrintWriter pw, int offset) { - switch (offset) { - case ADJ_NOTHING: - break; - case ADJ_SCREEN_OFF: - pw.print(ADJ_SCREEN_NAMES_CSV[0]); - break; - case ADJ_SCREEN_ON: - pw.print(ADJ_SCREEN_NAMES_CSV[1]); - break; - default: - pw.print("???"); - break; - } - } - - private static void printMemLabel(PrintWriter pw, int offset, char sep) { - switch (offset) { - case ADJ_NOTHING: - pw.print(" "); - if (sep != 0) pw.print(' '); - break; - case ADJ_MEM_FACTOR_NORMAL: - pw.print("Norm"); - if (sep != 0) pw.print(sep); - break; - case ADJ_MEM_FACTOR_MODERATE: - pw.print("Mod "); - if (sep != 0) pw.print(sep); - break; - case ADJ_MEM_FACTOR_LOW: - pw.print("Low "); - if (sep != 0) pw.print(sep); - break; - case ADJ_MEM_FACTOR_CRITICAL: - pw.print("Crit"); - if (sep != 0) pw.print(sep); - break; - default: - pw.print("????"); - if (sep != 0) pw.print(sep); - break; - } - } - - public static void printMemLabelCsv(PrintWriter pw, int offset) { - if (offset >= ADJ_MEM_FACTOR_NORMAL) { - if (offset <= ADJ_MEM_FACTOR_CRITICAL) { - pw.print(ADJ_MEM_NAMES_CSV[offset]); - } else { - pw.print("???"); - } - } - } - - public static long dumpSingleTime(PrintWriter pw, String prefix, long[] durations, - int curState, long curStartTime, long now) { - long totalTime = 0; - int printedScreen = -1; - for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) { - int printedMem = -1; - for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) { - int state = imem+iscreen; - long time = durations[state]; - String running = ""; - if (curState == state) { - time += now - curStartTime; - if (pw != null) { - running = " (running)"; - } - } - if (time != 0) { - if (pw != null) { - pw.print(prefix); - printScreenLabel(pw, printedScreen != iscreen - ? iscreen : STATE_NOTHING); - printedScreen = iscreen; - printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING, (char)0); - printedMem = imem; - pw.print(": "); - TimeUtils.formatDuration(time, pw); pw.println(running); - } - totalTime += time; - } - } - } - if (totalTime != 0 && pw != null) { - pw.print(prefix); - pw.print(" TOTAL: "); - TimeUtils.formatDuration(totalTime, pw); - pw.println(); - } - return totalTime; - } - - static void dumpAdjTimesCheckin(PrintWriter pw, String sep, long[] durations, - int curState, long curStartTime, long now) { - for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) { - for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) { - int state = imem+iscreen; - long time = durations[state]; - if (curState == state) { - time += now - curStartTime; - } - if (time != 0) { - printAdjTagAndValue(pw, state, time); - } - } - } - } - - static void dumpServiceTimeCheckin(PrintWriter pw, String label, String packageName, - int uid, int vers, String serviceName, ServiceState svc, int serviceType, int opCount, - int curState, long curStartTime, long now) { - if (opCount <= 0) { - return; - } - pw.print(label); - pw.print(","); - pw.print(packageName); - pw.print(","); - pw.print(uid); - pw.print(","); - pw.print(vers); - pw.print(","); - pw.print(serviceName); - pw.print(","); - pw.print(opCount); - boolean didCurState = false; - for (int i=0; i<svc.mDurationsTableSize; i++) { - int off = svc.mDurationsTable[i]; - int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; - int memFactor = type / ServiceState.SERVICE_COUNT; - type %= ServiceState.SERVICE_COUNT; - if (type != serviceType) { - continue; - } - long time = svc.mStats.getLong(off, 0); - if (curState == memFactor) { - didCurState = true; - time += now - curStartTime; - } - printAdjTagAndValue(pw, memFactor, time); - } - if (!didCurState && curState != STATE_NOTHING) { - printAdjTagAndValue(pw, curState, now - curStartTime); - } - pw.println(); - } - - public static void computeProcessData(ProcessState proc, ProcessDataCollection data, long now) { - data.totalTime = 0; - data.numPss = data.minPss = data.avgPss = data.maxPss = - data.minUss = data.avgUss = data.maxUss = 0; - for (int is=0; is<data.screenStates.length; is++) { - for (int im=0; im<data.memStates.length; im++) { - for (int ip=0; ip<data.procStates.length; ip++) { - int bucket = ((data.screenStates[is] + data.memStates[im]) * STATE_COUNT) - + data.procStates[ip]; - data.totalTime += proc.getDuration(bucket, now); - long samples = proc.getPssSampleCount(bucket); - if (samples > 0) { - long minPss = proc.getPssMinimum(bucket); - long avgPss = proc.getPssAverage(bucket); - long maxPss = proc.getPssMaximum(bucket); - long minUss = proc.getPssUssMinimum(bucket); - long avgUss = proc.getPssUssAverage(bucket); - long maxUss = proc.getPssUssMaximum(bucket); - if (data.numPss == 0) { - data.minPss = minPss; - data.avgPss = avgPss; - data.maxPss = maxPss; - data.minUss = minUss; - data.avgUss = avgUss; - data.maxUss = maxUss; - } else { - if (minPss < data.minPss) { - data.minPss = minPss; - } - data.avgPss = (long)( ((data.avgPss*(double)data.numPss) - + (avgPss*(double)samples)) / (data.numPss+samples) ); - if (maxPss > data.maxPss) { - data.maxPss = maxPss; - } - if (minUss < data.minUss) { - data.minUss = minUss; - } - data.avgUss = (long)( ((data.avgUss*(double)data.numPss) - + (avgUss*(double)samples)) / (data.numPss+samples) ); - if (maxUss > data.maxUss) { - data.maxUss = maxUss; - } - } - data.numPss += samples; - } - } - } - } - } - - static long computeProcessTimeLocked(ProcessState proc, int[] screenStates, int[] memStates, - int[] procStates, long now) { - long totalTime = 0; - /* - for (int i=0; i<proc.mDurationsTableSize; i++) { - int val = proc.mDurationsTable[i]; - totalTime += proc.mState.getLong(val, 0); - if ((val&0xff) == proc.mCurState) { - totalTime += now - proc.mStartTime; - } - } - */ - for (int is=0; is<screenStates.length; is++) { - for (int im=0; im<memStates.length; im++) { - for (int ip=0; ip<procStates.length; ip++) { - int bucket = ((screenStates[is] + memStates[im]) * STATE_COUNT) - + procStates[ip]; - totalTime += proc.getDuration(bucket, now); - } - } - } - proc.mTmpTotalTime = totalTime; - return totalTime; - } - - static class PssAggr { - long pss = 0; - long samples = 0; - - void add(long newPss, long newSamples) { - pss = (long)( (pss*(double)samples) + (newPss*(double)newSamples) ) - / (samples+newSamples); - samples += newSamples; - } - } - - public void computeTotalMemoryUse(TotalMemoryUseCollection data, long now) { - data.totalTime = 0; - for (int i=0; i<STATE_COUNT; i++) { - data.processStateWeight[i] = 0; - data.processStatePss[i] = 0; - data.processStateTime[i] = 0; - data.processStateSamples[i] = 0; - } - for (int i=0; i<SYS_MEM_USAGE_COUNT; i++) { - data.sysMemUsage[i] = 0; - } - data.sysMemCachedWeight = 0; - data.sysMemFreeWeight = 0; - data.sysMemZRamWeight = 0; - data.sysMemKernelWeight = 0; - data.sysMemNativeWeight = 0; - data.sysMemSamples = 0; - long[] totalMemUsage = new long[SYS_MEM_USAGE_COUNT]; - for (int i=0; i<mSysMemUsageTableSize; i++) { - int ent = mSysMemUsageTable[i]; - long[] longs = mLongs.get((ent>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); - int idx = (ent >> OFFSET_INDEX_SHIFT) & OFFSET_INDEX_MASK; - addSysMemUsage(totalMemUsage, 0, longs, idx); - } - for (int is=0; is<data.screenStates.length; is++) { - for (int im=0; im<data.memStates.length; im++) { - int memBucket = data.screenStates[is] + data.memStates[im]; - int stateBucket = memBucket * STATE_COUNT; - long memTime = mMemFactorDurations[memBucket]; - if (mMemFactor == memBucket) { - memTime += now - mStartTime; - } - data.totalTime += memTime; - int sysIdx = binarySearch(mSysMemUsageTable, mSysMemUsageTableSize, stateBucket); - long[] longs = totalMemUsage; - int idx = 0; - if (sysIdx >= 0) { - int ent = mSysMemUsageTable[sysIdx]; - long[] tmpLongs = mLongs.get((ent>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); - int tmpIdx = (ent >> OFFSET_INDEX_SHIFT) & OFFSET_INDEX_MASK; - if (tmpLongs[tmpIdx+SYS_MEM_USAGE_SAMPLE_COUNT] >= 3) { - addSysMemUsage(data.sysMemUsage, 0, longs, idx); - longs = tmpLongs; - idx = tmpIdx; - } - } - data.sysMemCachedWeight += longs[idx+SYS_MEM_USAGE_CACHED_AVERAGE] - * (double)memTime; - data.sysMemFreeWeight += longs[idx+SYS_MEM_USAGE_FREE_AVERAGE] - * (double)memTime; - data.sysMemZRamWeight += longs[idx+SYS_MEM_USAGE_ZRAM_AVERAGE] - * (double)memTime; - data.sysMemKernelWeight += longs[idx+SYS_MEM_USAGE_KERNEL_AVERAGE] - * (double)memTime; - data.sysMemNativeWeight += longs[idx+SYS_MEM_USAGE_NATIVE_AVERAGE] - * (double)memTime; - data.sysMemSamples += longs[idx+SYS_MEM_USAGE_SAMPLE_COUNT]; - } - } - ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); - for (int iproc=0; iproc<procMap.size(); iproc++) { - SparseArray<ProcessState> uids = procMap.valueAt(iproc); - for (int iu=0; iu<uids.size(); iu++) { - final ProcessState proc = uids.valueAt(iu); - final PssAggr fgPss = new PssAggr(); - final PssAggr bgPss = new PssAggr(); - final PssAggr cachedPss = new PssAggr(); - boolean havePss = false; - for (int i=0; i<proc.mDurationsTableSize; i++) { - int off = proc.mDurationsTable[i]; - int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; - int procState = type % STATE_COUNT; - long samples = proc.getPssSampleCount(type); - if (samples > 0) { - long avg = proc.getPssAverage(type); - havePss = true; - if (procState <= STATE_IMPORTANT_FOREGROUND) { - fgPss.add(avg, samples); - } else if (procState <= STATE_RECEIVER) { - bgPss.add(avg, samples); - } else { - cachedPss.add(avg, samples); - } - } - } - if (!havePss) { - continue; - } - boolean fgHasBg = false; - boolean fgHasCached = false; - boolean bgHasCached = false; - if (fgPss.samples < 3 && bgPss.samples > 0) { - fgHasBg = true; - fgPss.add(bgPss.pss, bgPss.samples); - } - if (fgPss.samples < 3 && cachedPss.samples > 0) { - fgHasCached = true; - fgPss.add(cachedPss.pss, cachedPss.samples); - } - if (bgPss.samples < 3 && cachedPss.samples > 0) { - bgHasCached = true; - bgPss.add(cachedPss.pss, cachedPss.samples); - } - if (bgPss.samples < 3 && !fgHasBg && fgPss.samples > 0) { - bgPss.add(fgPss.pss, fgPss.samples); - } - if (cachedPss.samples < 3 && !bgHasCached && bgPss.samples > 0) { - cachedPss.add(bgPss.pss, bgPss.samples); - } - if (cachedPss.samples < 3 && !fgHasCached && fgPss.samples > 0) { - cachedPss.add(fgPss.pss, fgPss.samples); - } - for (int i=0; i<proc.mDurationsTableSize; i++) { - final int off = proc.mDurationsTable[i]; - final int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; - long time = getLong(off, 0); - if (proc.mCurState == type) { - time += now - proc.mStartTime; - } - final int procState = type % STATE_COUNT; - data.processStateTime[procState] += time; - long samples = proc.getPssSampleCount(type); - long avg; - if (samples > 0) { - avg = proc.getPssAverage(type); - } else if (procState <= STATE_IMPORTANT_FOREGROUND) { - samples = fgPss.samples; - avg = fgPss.pss; - } else if (procState <= STATE_RECEIVER) { - samples = bgPss.samples; - avg = bgPss.pss; - } else { - samples = cachedPss.samples; - avg = cachedPss.pss; - } - double newAvg = ( (data.processStatePss[procState] - * (double)data.processStateSamples[procState]) - + (avg*(double)samples) - ) / (data.processStateSamples[procState]+samples); - data.processStatePss[procState] = (long)newAvg; - data.processStateSamples[procState] += samples; - data.processStateWeight[procState] += avg * (double)time; - } - } - } - } - - static void dumpProcessState(PrintWriter pw, String prefix, ProcessState proc, - int[] screenStates, int[] memStates, int[] procStates, long now) { - long totalTime = 0; - int printedScreen = -1; - for (int is=0; is<screenStates.length; is++) { - int printedMem = -1; - for (int im=0; im<memStates.length; im++) { - for (int ip=0; ip<procStates.length; ip++) { - final int iscreen = screenStates[is]; - final int imem = memStates[im]; - final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip]; - long time = proc.getDuration(bucket, now); - String running = ""; - if (proc.mCurState == bucket) { - running = " (running)"; - } - if (time != 0) { - pw.print(prefix); - if (screenStates.length > 1) { - printScreenLabel(pw, printedScreen != iscreen - ? iscreen : STATE_NOTHING); - printedScreen = iscreen; - } - if (memStates.length > 1) { - printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING, '/'); - printedMem = imem; - } - pw.print(STATE_NAMES[procStates[ip]]); pw.print(": "); - TimeUtils.formatDuration(time, pw); pw.println(running); - totalTime += time; - } - } - } - } - if (totalTime != 0) { - pw.print(prefix); - if (screenStates.length > 1) { - printScreenLabel(pw, STATE_NOTHING); - } - if (memStates.length > 1) { - printMemLabel(pw, STATE_NOTHING, '/'); - } - pw.print("TOTAL : "); - TimeUtils.formatDuration(totalTime, pw); - pw.println(); - } - } - - static void dumpProcessPss(PrintWriter pw, String prefix, ProcessState proc, int[] screenStates, - int[] memStates, int[] procStates) { - boolean printedHeader = false; - int printedScreen = -1; - for (int is=0; is<screenStates.length; is++) { - int printedMem = -1; - for (int im=0; im<memStates.length; im++) { - for (int ip=0; ip<procStates.length; ip++) { - final int iscreen = screenStates[is]; - final int imem = memStates[im]; - final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip]; - long count = proc.getPssSampleCount(bucket); - if (count > 0) { - if (!printedHeader) { - pw.print(prefix); - pw.print("PSS/USS ("); - pw.print(proc.mPssTableSize); - pw.println(" entries):"); - printedHeader = true; - } - pw.print(prefix); - pw.print(" "); - if (screenStates.length > 1) { - printScreenLabel(pw, printedScreen != iscreen - ? iscreen : STATE_NOTHING); - printedScreen = iscreen; - } - if (memStates.length > 1) { - printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING, '/'); - printedMem = imem; - } - pw.print(STATE_NAMES[procStates[ip]]); pw.print(": "); - pw.print(count); - pw.print(" samples "); - DebugUtils.printSizeValue(pw, proc.getPssMinimum(bucket) * 1024); - pw.print(" "); - DebugUtils.printSizeValue(pw, proc.getPssAverage(bucket) * 1024); - pw.print(" "); - DebugUtils.printSizeValue(pw, proc.getPssMaximum(bucket) * 1024); - pw.print(" / "); - DebugUtils.printSizeValue(pw, proc.getPssUssMinimum(bucket) * 1024); - pw.print(" "); - DebugUtils.printSizeValue(pw, proc.getPssUssAverage(bucket) * 1024); - pw.print(" "); - DebugUtils.printSizeValue(pw, proc.getPssUssMaximum(bucket) * 1024); - pw.println(); - } - } - } - } - if (proc.mNumExcessiveWake != 0) { - pw.print(prefix); pw.print("Killed for excessive wake locks: "); - pw.print(proc.mNumExcessiveWake); pw.println(" times"); - } - if (proc.mNumExcessiveCpu != 0) { - pw.print(prefix); pw.print("Killed for excessive CPU use: "); - pw.print(proc.mNumExcessiveCpu); pw.println(" times"); - } - if (proc.mNumCachedKill != 0) { - pw.print(prefix); pw.print("Killed from cached state: "); - pw.print(proc.mNumCachedKill); pw.print(" times from pss "); - DebugUtils.printSizeValue(pw, proc.mMinCachedKillPss * 1024); pw.print("-"); - DebugUtils.printSizeValue(pw, proc.mAvgCachedKillPss * 1024); pw.print("-"); - DebugUtils.printSizeValue(pw, proc.mMaxCachedKillPss * 1024); pw.println(); - } - } - - long getSysMemUsageValue(int state, int index) { - int idx = binarySearch(mSysMemUsageTable, mSysMemUsageTableSize, state); - return idx >= 0 ? getLong(mSysMemUsageTable[idx], index) : 0; - } - - void dumpSysMemUsageCategory(PrintWriter pw, String prefix, String label, - int bucket, int index) { - pw.print(prefix); pw.print(label); - pw.print(": "); - DebugUtils.printSizeValue(pw, getSysMemUsageValue(bucket, index) * 1024); - pw.print(" min, "); - DebugUtils.printSizeValue(pw, getSysMemUsageValue(bucket, index + 1) * 1024); - pw.print(" avg, "); - DebugUtils.printSizeValue(pw, getSysMemUsageValue(bucket, index+2) * 1024); - pw.println(" max"); - } - - void dumpSysMemUsage(PrintWriter pw, String prefix, int[] screenStates, - int[] memStates) { - int printedScreen = -1; - for (int is=0; is<screenStates.length; is++) { - int printedMem = -1; - for (int im=0; im<memStates.length; im++) { - final int iscreen = screenStates[is]; - final int imem = memStates[im]; - final int bucket = ((iscreen + imem) * STATE_COUNT); - long count = getSysMemUsageValue(bucket, SYS_MEM_USAGE_SAMPLE_COUNT); - if (count > 0) { - pw.print(prefix); - if (screenStates.length > 1) { - printScreenLabel(pw, printedScreen != iscreen - ? iscreen : STATE_NOTHING); - printedScreen = iscreen; - } - if (memStates.length > 1) { - printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING, '\0'); - printedMem = imem; - } - pw.print(": "); - pw.print(count); - pw.println(" samples:"); - dumpSysMemUsageCategory(pw, prefix, " Cached", bucket, - SYS_MEM_USAGE_CACHED_MINIMUM); - dumpSysMemUsageCategory(pw, prefix, " Free", bucket, - SYS_MEM_USAGE_FREE_MINIMUM); - dumpSysMemUsageCategory(pw, prefix, " ZRam", bucket, - SYS_MEM_USAGE_ZRAM_MINIMUM); - dumpSysMemUsageCategory(pw, prefix, " Kernel", bucket, - SYS_MEM_USAGE_KERNEL_MINIMUM); - dumpSysMemUsageCategory(pw, prefix, " Native", bucket, - SYS_MEM_USAGE_NATIVE_MINIMUM); - } - } - } - } - - static void dumpStateHeadersCsv(PrintWriter pw, String sep, int[] screenStates, - int[] memStates, int[] procStates) { - final int NS = screenStates != null ? screenStates.length : 1; - final int NM = memStates != null ? memStates.length : 1; - final int NP = procStates != null ? procStates.length : 1; - for (int is=0; is<NS; is++) { - for (int im=0; im<NM; im++) { - for (int ip=0; ip<NP; ip++) { - pw.print(sep); - boolean printed = false; - if (screenStates != null && screenStates.length > 1) { - printScreenLabelCsv(pw, screenStates[is]); - printed = true; - } - if (memStates != null && memStates.length > 1) { - if (printed) { - pw.print("-"); - } - printMemLabelCsv(pw, memStates[im]); - printed = true; - } - if (procStates != null && procStates.length > 1) { - if (printed) { - pw.print("-"); - } - pw.print(STATE_NAMES_CSV[procStates[ip]]); - } - } - } - } - } - - static void dumpProcessStateCsv(PrintWriter pw, ProcessState proc, - boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates, - boolean sepProcStates, int[] procStates, long now) { - final int NSS = sepScreenStates ? screenStates.length : 1; - final int NMS = sepMemStates ? memStates.length : 1; - final int NPS = sepProcStates ? procStates.length : 1; - for (int iss=0; iss<NSS; iss++) { - for (int ims=0; ims<NMS; ims++) { - for (int ips=0; ips<NPS; ips++) { - final int vsscreen = sepScreenStates ? screenStates[iss] : 0; - final int vsmem = sepMemStates ? memStates[ims] : 0; - final int vsproc = sepProcStates ? procStates[ips] : 0; - final int NSA = sepScreenStates ? 1 : screenStates.length; - final int NMA = sepMemStates ? 1 : memStates.length; - final int NPA = sepProcStates ? 1 : procStates.length; - long totalTime = 0; - for (int isa=0; isa<NSA; isa++) { - for (int ima=0; ima<NMA; ima++) { - for (int ipa=0; ipa<NPA; ipa++) { - final int vascreen = sepScreenStates ? 0 : screenStates[isa]; - final int vamem = sepMemStates ? 0 : memStates[ima]; - final int vaproc = sepProcStates ? 0 : procStates[ipa]; - final int bucket = ((vsscreen + vascreen + vsmem + vamem) - * STATE_COUNT) + vsproc + vaproc; - totalTime += proc.getDuration(bucket, now); - } - } - } - pw.print(CSV_SEP); - pw.print(totalTime); - } - } - } - } - - static void dumpProcessList(PrintWriter pw, String prefix, ArrayList<ProcessState> procs, - int[] screenStates, int[] memStates, int[] procStates, long now) { - String innerPrefix = prefix + " "; - for (int i=procs.size()-1; i>=0; i--) { - ProcessState proc = procs.get(i); - pw.print(prefix); - pw.print(proc.mName); - pw.print(" / "); - UserHandle.formatUid(pw, proc.mUid); - pw.print(" ("); - pw.print(proc.mDurationsTableSize); - pw.print(" entries)"); - pw.println(":"); - dumpProcessState(pw, innerPrefix, proc, screenStates, memStates, procStates, now); - if (proc.mPssTableSize > 0) { - dumpProcessPss(pw, innerPrefix, proc, screenStates, memStates, procStates); - } - } - } - - static void dumpProcessSummaryDetails(PrintWriter pw, ProcessState proc, String prefix, - String label, int[] screenStates, int[] memStates, int[] procStates, - long now, long totalTime, boolean full) { - ProcessDataCollection totals = new ProcessDataCollection(screenStates, - memStates, procStates); - computeProcessData(proc, totals, now); - double percentage = (double) totals.totalTime / (double) totalTime * 100; - // We don't print percentages < .01, so just drop those. - if (percentage >= 0.005 || totals.numPss != 0) { - if (prefix != null) { - pw.print(prefix); - } - if (label != null) { - pw.print(label); - } - totals.print(pw, totalTime, full); - if (prefix != null) { - pw.println(); - } - } - } - - static void dumpProcessSummaryLocked(PrintWriter pw, String prefix, - ArrayList<ProcessState> procs, int[] screenStates, int[] memStates, int[] procStates, - boolean inclUidVers, long now, long totalTime) { - for (int i=procs.size()-1; i>=0; i--) { - ProcessState proc = procs.get(i); - pw.print(prefix); - pw.print("* "); - pw.print(proc.mName); - pw.print(" / "); - UserHandle.formatUid(pw, proc.mUid); - pw.print(" / v"); - pw.print(proc.mVersion); - pw.println(":"); - dumpProcessSummaryDetails(pw, proc, prefix, " TOTAL: ", screenStates, memStates, - procStates, now, totalTime, true); - dumpProcessSummaryDetails(pw, proc, prefix, " Persistent: ", screenStates, memStates, - new int[] { STATE_PERSISTENT }, now, totalTime, true); - dumpProcessSummaryDetails(pw, proc, prefix, " Top: ", screenStates, memStates, - new int[] {STATE_TOP}, now, totalTime, true); - dumpProcessSummaryDetails(pw, proc, prefix, " Imp Fg: ", screenStates, memStates, - new int[] { STATE_IMPORTANT_FOREGROUND }, now, totalTime, true); - dumpProcessSummaryDetails(pw, proc, prefix, " Imp Bg: ", screenStates, memStates, - new int[] {STATE_IMPORTANT_BACKGROUND}, now, totalTime, true); - dumpProcessSummaryDetails(pw, proc, prefix, " Backup: ", screenStates, memStates, - new int[] {STATE_BACKUP}, now, totalTime, true); - dumpProcessSummaryDetails(pw, proc, prefix, " Heavy Wgt: ", screenStates, memStates, - new int[] {STATE_HEAVY_WEIGHT}, now, totalTime, true); - dumpProcessSummaryDetails(pw, proc, prefix, " Service: ", screenStates, memStates, - new int[] {STATE_SERVICE}, now, totalTime, true); - dumpProcessSummaryDetails(pw, proc, prefix, " Service Rs: ", screenStates, memStates, - new int[] {STATE_SERVICE_RESTARTING}, now, totalTime, true); - dumpProcessSummaryDetails(pw, proc, prefix, " Receiver: ", screenStates, memStates, - new int[] {STATE_RECEIVER}, now, totalTime, true); - dumpProcessSummaryDetails(pw, proc, prefix, " (Home): ", screenStates, memStates, - new int[] {STATE_HOME}, now, totalTime, true); - dumpProcessSummaryDetails(pw, proc, prefix, " (Last Act): ", screenStates, memStates, - new int[] {STATE_LAST_ACTIVITY}, now, totalTime, true); - dumpProcessSummaryDetails(pw, proc, prefix, " (Cached): ", screenStates, memStates, - new int[] {STATE_CACHED_ACTIVITY, STATE_CACHED_ACTIVITY_CLIENT, - STATE_CACHED_EMPTY}, now, totalTime, true); - } - } - - static void printPercent(PrintWriter pw, double fraction) { - fraction *= 100; - if (fraction < 1) { - pw.print(String.format("%.2f", fraction)); - } else if (fraction < 10) { - pw.print(String.format("%.1f", fraction)); - } else { - pw.print(String.format("%.0f", fraction)); - } - pw.print("%"); - } - - public static void dumpProcessListCsv(PrintWriter pw, ArrayList<ProcessState> procs, - boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates, - boolean sepProcStates, int[] procStates, long now) { - pw.print("process"); - pw.print(CSV_SEP); - pw.print("uid"); - pw.print(CSV_SEP); - pw.print("vers"); - dumpStateHeadersCsv(pw, CSV_SEP, sepScreenStates ? screenStates : null, - sepMemStates ? memStates : null, - sepProcStates ? procStates : null); - pw.println(); - for (int i=procs.size()-1; i>=0; i--) { - ProcessState proc = procs.get(i); - pw.print(proc.mName); - pw.print(CSV_SEP); - UserHandle.formatUid(pw, proc.mUid); - pw.print(CSV_SEP); - pw.print(proc.mVersion); - dumpProcessStateCsv(pw, proc, sepScreenStates, screenStates, - sepMemStates, memStates, sepProcStates, procStates, now); - pw.println(); - } - } - - static int printArrayEntry(PrintWriter pw, String[] array, int value, int mod) { - int index = value/mod; - if (index >= 0 && index < array.length) { - pw.print(array[index]); - } else { - pw.print('?'); - } - return value - index*mod; - } - - static void printProcStateTag(PrintWriter pw, int state) { - state = printArrayEntry(pw, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD*STATE_COUNT); - state = printArrayEntry(pw, ADJ_MEM_TAGS, state, STATE_COUNT); - printArrayEntry(pw, STATE_TAGS, state, 1); - } - - static void printAdjTag(PrintWriter pw, int state) { - state = printArrayEntry(pw, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD); - printArrayEntry(pw, ADJ_MEM_TAGS, state, 1); - } - - static void printProcStateTagAndValue(PrintWriter pw, int state, long value) { - pw.print(','); - printProcStateTag(pw, state); - pw.print(':'); - pw.print(value); - } - - static void printAdjTagAndValue(PrintWriter pw, int state, long value) { - pw.print(','); - printAdjTag(pw, state); - pw.print(':'); - pw.print(value); - } - - static void dumpAllProcessStateCheckin(PrintWriter pw, ProcessState proc, long now) { - boolean didCurState = false; - for (int i=0; i<proc.mDurationsTableSize; i++) { - int off = proc.mDurationsTable[i]; - int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; - long time = proc.mStats.getLong(off, 0); - if (proc.mCurState == type) { - didCurState = true; - time += now - proc.mStartTime; - } - printProcStateTagAndValue(pw, type, time); - } - if (!didCurState && proc.mCurState != STATE_NOTHING) { - printProcStateTagAndValue(pw, proc.mCurState, now - proc.mStartTime); - } - } - - static void dumpAllProcessPssCheckin(PrintWriter pw, ProcessState proc) { - for (int i=0; i<proc.mPssTableSize; i++) { - int off = proc.mPssTable[i]; - int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; - long count = proc.mStats.getLong(off, PSS_SAMPLE_COUNT); - long min = proc.mStats.getLong(off, PSS_MINIMUM); - long avg = proc.mStats.getLong(off, PSS_AVERAGE); - long max = proc.mStats.getLong(off, PSS_MAXIMUM); - long umin = proc.mStats.getLong(off, PSS_USS_MINIMUM); - long uavg = proc.mStats.getLong(off, PSS_USS_AVERAGE); - long umax = proc.mStats.getLong(off, PSS_USS_MAXIMUM); - pw.print(','); - printProcStateTag(pw, type); - pw.print(':'); - pw.print(count); - pw.print(':'); - pw.print(min); - pw.print(':'); - pw.print(avg); - pw.print(':'); - pw.print(max); - pw.print(':'); - pw.print(umin); - pw.print(':'); - pw.print(uavg); - pw.print(':'); - pw.print(umax); - } - } - - public void reset() { - if (DEBUG) Slog.d(TAG, "Resetting state of " + mTimePeriodStartClockStr); - resetCommon(); - mPackages.getMap().clear(); - mProcesses.getMap().clear(); - mMemFactor = STATE_NOTHING; - mStartTime = 0; - if (DEBUG) Slog.d(TAG, "State reset; now " + mTimePeriodStartClockStr); - } - - public void resetSafely() { - if (DEBUG) Slog.d(TAG, "Safely resetting state of " + mTimePeriodStartClockStr); - resetCommon(); - - // First initialize use count of all common processes. - final long now = SystemClock.uptimeMillis(); - final ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); - for (int ip=procMap.size()-1; ip>=0; ip--) { - final SparseArray<ProcessState> uids = procMap.valueAt(ip); - for (int iu=uids.size()-1; iu>=0; iu--) { - uids.valueAt(iu).mTmpNumInUse = 0; - } - } - - // Next reset or prune all per-package processes, and for the ones that are reset - // track this back to the common processes. - final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap(); - for (int ip=pkgMap.size()-1; ip>=0; ip--) { - final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip); - for (int iu=uids.size()-1; iu>=0; iu--) { - final SparseArray<PackageState> vpkgs = uids.valueAt(iu); - for (int iv=vpkgs.size()-1; iv>=0; iv--) { - final PackageState pkgState = vpkgs.valueAt(iv); - for (int iproc=pkgState.mProcesses.size()-1; iproc>=0; iproc--) { - final ProcessState ps = pkgState.mProcesses.valueAt(iproc); - if (ps.isInUse()) { - ps.resetSafely(now); - ps.mCommonProcess.mTmpNumInUse++; - ps.mCommonProcess.mTmpFoundSubProc = ps; - } else { - pkgState.mProcesses.valueAt(iproc).makeDead(); - pkgState.mProcesses.removeAt(iproc); - } - } - for (int isvc=pkgState.mServices.size()-1; isvc>=0; isvc--) { - final ServiceState ss = pkgState.mServices.valueAt(isvc); - if (ss.isInUse()) { - ss.resetSafely(now); - } else { - pkgState.mServices.removeAt(isvc); - } - } - if (pkgState.mProcesses.size() <= 0 && pkgState.mServices.size() <= 0) { - vpkgs.removeAt(iv); - } - } - if (vpkgs.size() <= 0) { - uids.removeAt(iu); - } - } - if (uids.size() <= 0) { - pkgMap.removeAt(ip); - } - } - - // Finally prune out any common processes that are no longer in use. - for (int ip=procMap.size()-1; ip>=0; ip--) { - final SparseArray<ProcessState> uids = procMap.valueAt(ip); - for (int iu=uids.size()-1; iu>=0; iu--) { - ProcessState ps = uids.valueAt(iu); - if (ps.isInUse() || ps.mTmpNumInUse > 0) { - // If this is a process for multiple packages, we could at this point - // be back down to one package. In that case, we want to revert back - // to a single shared ProcessState. We can do this by converting the - // current package-specific ProcessState up to the shared ProcessState, - // throwing away the current one we have here (because nobody else is - // using it). - if (!ps.mActive && ps.mMultiPackage && ps.mTmpNumInUse == 1) { - // Here we go... - ps = ps.mTmpFoundSubProc; - ps.mCommonProcess = ps; - uids.setValueAt(iu, ps); - } else { - ps.resetSafely(now); - } - } else { - ps.makeDead(); - uids.removeAt(iu); - } - } - if (uids.size() <= 0) { - procMap.removeAt(ip); - } - } - - mStartTime = now; - if (DEBUG) Slog.d(TAG, "State reset; now " + mTimePeriodStartClockStr); - } - - private void resetCommon() { - mTimePeriodStartClock = System.currentTimeMillis(); - buildTimePeriodStartClockStr(); - mTimePeriodStartRealtime = mTimePeriodEndRealtime = SystemClock.elapsedRealtime(); - mTimePeriodStartUptime = mTimePeriodEndUptime = SystemClock.uptimeMillis(); - mLongs.clear(); - mLongs.add(new long[LONGS_SIZE]); - mNextLong = 0; - Arrays.fill(mMemFactorDurations, 0); - mSysMemUsageTable = null; - mSysMemUsageTableSize = 0; - mStartTime = 0; - mReadError = null; - mFlags = 0; - evaluateSystemProperties(true); - } - - public boolean evaluateSystemProperties(boolean update) { - boolean changed = false; - String runtime = SystemProperties.get("persist.sys.dalvik.vm.lib.2", - VMRuntime.getRuntime().vmLibrary()); - if (!Objects.equals(runtime, mRuntime)) { - changed = true; - if (update) { - mRuntime = runtime; - } - } - return changed; - } - - private void buildTimePeriodStartClockStr() { - mTimePeriodStartClockStr = DateFormat.format("yyyy-MM-dd-HH-mm-ss", - mTimePeriodStartClock).toString(); - } - - static final int[] BAD_TABLE = new int[0]; - - private int[] readTableFromParcel(Parcel in, String name, String what) { - final int size = in.readInt(); - if (size < 0) { - Slog.w(TAG, "Ignoring existing stats; bad " + what + " table size: " + size); - return BAD_TABLE; - } - if (size == 0) { - return null; - } - final int[] table = new int[size]; - for (int i=0; i<size; i++) { - table[i] = in.readInt(); - if (DEBUG_PARCEL) Slog.i(TAG, "Reading in " + name + " table #" + i + ": " - + ProcessStats.printLongOffset(table[i])); - if (!validateLongOffset(table[i])) { - Slog.w(TAG, "Ignoring existing stats; bad " + what + " table entry: " - + ProcessStats.printLongOffset(table[i])); - return null; - } - } - return table; - } - - private void writeCompactedLongArray(Parcel out, long[] array, int num) { - for (int i=0; i<num; i++) { - long val = array[i]; - if (val < 0) { - Slog.w(TAG, "Time val negative: " + val); - val = 0; - } - if (val <= Integer.MAX_VALUE) { - out.writeInt((int)val); - } else { - int top = ~((int)((val>>32)&0x7fffffff)); - int bottom = (int)(val&0xfffffff); - out.writeInt(top); - out.writeInt(bottom); - } - } - } - - private void readCompactedLongArray(Parcel in, int version, long[] array, int num) { - if (version <= 10) { - in.readLongArray(array); - return; - } - final int alen = array.length; - if (num > alen) { - throw new RuntimeException("bad array lengths: got " + num + " array is " + alen); - } - int i; - for (i=0; i<num; i++) { - int val = in.readInt(); - if (val >= 0) { - array[i] = val; - } else { - int bottom = in.readInt(); - array[i] = (((long)~val)<<32) | bottom; - } - } - while (i < alen) { - array[i] = 0; - i++; - } - } - - private void writeCommonString(Parcel out, String name) { - Integer index = mCommonStringToIndex.get(name); - if (index != null) { - out.writeInt(index); - return; - } - index = mCommonStringToIndex.size(); - mCommonStringToIndex.put(name, index); - out.writeInt(~index); - out.writeString(name); - } - - private String readCommonString(Parcel in, int version) { - if (version <= 9) { - return in.readString(); - } - int index = in.readInt(); - if (index >= 0) { - return mIndexToCommonString.get(index); - } - index = ~index; - String name = in.readString(); - while (mIndexToCommonString.size() <= index) { - mIndexToCommonString.add(null); - } - mIndexToCommonString.set(index, name); - return name; - } - - @Override - public int describeContents() { - return 0; - } - - @Override - public void writeToParcel(Parcel out, int flags) { - writeToParcel(out, SystemClock.uptimeMillis(), flags); - } - - /** @hide */ - public void writeToParcel(Parcel out, long now, int flags) { - out.writeInt(MAGIC); - out.writeInt(PARCEL_VERSION); - out.writeInt(STATE_COUNT); - out.writeInt(ADJ_COUNT); - out.writeInt(PSS_COUNT); - out.writeInt(SYS_MEM_USAGE_COUNT); - out.writeInt(LONGS_SIZE); - - mCommonStringToIndex = new ArrayMap<String, Integer>(mProcesses.mMap.size()); - - // First commit all running times. - ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); - final int NPROC = procMap.size(); - for (int ip=0; ip<NPROC; ip++) { - SparseArray<ProcessState> uids = procMap.valueAt(ip); - final int NUID = uids.size(); - for (int iu=0; iu<NUID; iu++) { - uids.valueAt(iu).commitStateTime(now); - } - } - final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap(); - final int NPKG = pkgMap.size(); - for (int ip=0; ip<NPKG; ip++) { - final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip); - final int NUID = uids.size(); - for (int iu=0; iu<NUID; iu++) { - final SparseArray<PackageState> vpkgs = uids.valueAt(iu); - final int NVERS = vpkgs.size(); - for (int iv=0; iv<NVERS; iv++) { - PackageState pkgState = vpkgs.valueAt(iv); - final int NPROCS = pkgState.mProcesses.size(); - for (int iproc=0; iproc<NPROCS; iproc++) { - ProcessState proc = pkgState.mProcesses.valueAt(iproc); - if (proc.mCommonProcess != proc) { - proc.commitStateTime(now); - } - } - final int NSRVS = pkgState.mServices.size(); - for (int isvc=0; isvc<NSRVS; isvc++) { - pkgState.mServices.valueAt(isvc).commitStateTime(now); - } - } - } - } - - out.writeLong(mTimePeriodStartClock); - out.writeLong(mTimePeriodStartRealtime); - out.writeLong(mTimePeriodEndRealtime); - out.writeLong(mTimePeriodStartUptime); - out.writeLong(mTimePeriodEndUptime); - out.writeString(mRuntime); - out.writeInt(mFlags); - - out.writeInt(mLongs.size()); - out.writeInt(mNextLong); - for (int i=0; i<(mLongs.size()-1); i++) { - long[] array = mLongs.get(i); - writeCompactedLongArray(out, array, array.length); - } - long[] lastLongs = mLongs.get(mLongs.size() - 1); - writeCompactedLongArray(out, lastLongs, mNextLong); - - if (mMemFactor != STATE_NOTHING) { - mMemFactorDurations[mMemFactor] += now - mStartTime; - mStartTime = now; - } - writeCompactedLongArray(out, mMemFactorDurations, mMemFactorDurations.length); - - out.writeInt(mSysMemUsageTableSize); - for (int i=0; i<mSysMemUsageTableSize; i++) { - if (DEBUG_PARCEL) Slog.i(TAG, "Writing sys mem usage #" + i + ": " - + printLongOffset(mSysMemUsageTable[i])); - out.writeInt(mSysMemUsageTable[i]); - } - - out.writeInt(NPROC); - for (int ip=0; ip<NPROC; ip++) { - writeCommonString(out, procMap.keyAt(ip)); - final SparseArray<ProcessState> uids = procMap.valueAt(ip); - final int NUID = uids.size(); - out.writeInt(NUID); - for (int iu=0; iu<NUID; iu++) { - out.writeInt(uids.keyAt(iu)); - final ProcessState proc = uids.valueAt(iu); - writeCommonString(out, proc.mPackage); - out.writeInt(proc.mVersion); - proc.writeToParcel(out, now); - } - } - out.writeInt(NPKG); - for (int ip=0; ip<NPKG; ip++) { - writeCommonString(out, pkgMap.keyAt(ip)); - final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip); - final int NUID = uids.size(); - out.writeInt(NUID); - for (int iu=0; iu<NUID; iu++) { - out.writeInt(uids.keyAt(iu)); - final SparseArray<PackageState> vpkgs = uids.valueAt(iu); - final int NVERS = vpkgs.size(); - out.writeInt(NVERS); - for (int iv=0; iv<NVERS; iv++) { - out.writeInt(vpkgs.keyAt(iv)); - final PackageState pkgState = vpkgs.valueAt(iv); - final int NPROCS = pkgState.mProcesses.size(); - out.writeInt(NPROCS); - for (int iproc=0; iproc<NPROCS; iproc++) { - writeCommonString(out, pkgState.mProcesses.keyAt(iproc)); - final ProcessState proc = pkgState.mProcesses.valueAt(iproc); - if (proc.mCommonProcess == proc) { - // This is the same as the common process we wrote above. - out.writeInt(0); - } else { - // There is separate data for this package's process. - out.writeInt(1); - proc.writeToParcel(out, now); - } - } - final int NSRVS = pkgState.mServices.size(); - out.writeInt(NSRVS); - for (int isvc=0; isvc<NSRVS; isvc++) { - out.writeString(pkgState.mServices.keyAt(isvc)); - final ServiceState svc = pkgState.mServices.valueAt(isvc); - writeCommonString(out, svc.mProcessName); - svc.writeToParcel(out, now); - } - } - } - } - - mCommonStringToIndex = null; - } - - private boolean readCheckedInt(Parcel in, int val, String what) { - int got; - if ((got=in.readInt()) != val) { - mReadError = "bad " + what + ": " + got; - return false; - } - return true; - } - - static byte[] readFully(InputStream stream, int[] outLen) throws IOException { - int pos = 0; - final int initialAvail = stream.available(); - byte[] data = new byte[initialAvail > 0 ? (initialAvail+1) : 16384]; - while (true) { - int amt = stream.read(data, pos, data.length-pos); - if (DEBUG_PARCEL) Slog.i("foo", "Read " + amt + " bytes at " + pos - + " of avail " + data.length); - if (amt < 0) { - if (DEBUG_PARCEL) Slog.i("foo", "**** FINISHED READING: pos=" + pos - + " len=" + data.length); - outLen[0] = pos; - return data; - } - pos += amt; - if (pos >= data.length) { - byte[] newData = new byte[pos+16384]; - if (DEBUG_PARCEL) Slog.i(TAG, "Copying " + pos + " bytes to new array len " - + newData.length); - System.arraycopy(data, 0, newData, 0, pos); - data = newData; - } - } - } - - public void read(InputStream stream) { - try { - int[] len = new int[1]; - byte[] raw = readFully(stream, len); - Parcel in = Parcel.obtain(); - in.unmarshall(raw, 0, len[0]); - in.setDataPosition(0); - stream.close(); - - readFromParcel(in); - } catch (IOException e) { - mReadError = "caught exception: " + e; - } - } - - public void readFromParcel(Parcel in) { - final boolean hadData = mPackages.getMap().size() > 0 - || mProcesses.getMap().size() > 0; - if (hadData) { - resetSafely(); - } - - if (!readCheckedInt(in, MAGIC, "magic number")) { - return; - } - int version = in.readInt(); - if (version != PARCEL_VERSION) { - mReadError = "bad version: " + version; - return; - } - if (!readCheckedInt(in, STATE_COUNT, "state count")) { - return; - } - if (!readCheckedInt(in, ADJ_COUNT, "adj count")) { - return; - } - if (!readCheckedInt(in, PSS_COUNT, "pss count")) { - return; - } - if (!readCheckedInt(in, SYS_MEM_USAGE_COUNT, "sys mem usage count")) { - return; - } - if (!readCheckedInt(in, LONGS_SIZE, "longs size")) { - return; - } - - mIndexToCommonString = new ArrayList<String>(); - - mTimePeriodStartClock = in.readLong(); - buildTimePeriodStartClockStr(); - mTimePeriodStartRealtime = in.readLong(); - mTimePeriodEndRealtime = in.readLong(); - mTimePeriodStartUptime = in.readLong(); - mTimePeriodEndUptime = in.readLong(); - mRuntime = in.readString(); - mFlags = in.readInt(); - - final int NLONGS = in.readInt(); - final int NEXTLONG = in.readInt(); - mLongs.clear(); - for (int i=0; i<(NLONGS-1); i++) { - while (i >= mLongs.size()) { - mLongs.add(new long[LONGS_SIZE]); - } - readCompactedLongArray(in, version, mLongs.get(i), LONGS_SIZE); - } - long[] longs = new long[LONGS_SIZE]; - mNextLong = NEXTLONG; - readCompactedLongArray(in, version, longs, NEXTLONG); - mLongs.add(longs); - - readCompactedLongArray(in, version, mMemFactorDurations, mMemFactorDurations.length); - - mSysMemUsageTable = readTableFromParcel(in, TAG, "sys mem usage"); - if (mSysMemUsageTable == BAD_TABLE) { - return; - } - mSysMemUsageTableSize = mSysMemUsageTable != null ? mSysMemUsageTable.length : 0; - - int NPROC = in.readInt(); - if (NPROC < 0) { - mReadError = "bad process count: " + NPROC; - return; - } - while (NPROC > 0) { - NPROC--; - final String procName = readCommonString(in, version); - if (procName == null) { - mReadError = "bad process name"; - return; - } - int NUID = in.readInt(); - if (NUID < 0) { - mReadError = "bad uid count: " + NUID; - return; - } - while (NUID > 0) { - NUID--; - final int uid = in.readInt(); - if (uid < 0) { - mReadError = "bad uid: " + uid; - return; - } - final String pkgName = readCommonString(in, version); - if (pkgName == null) { - mReadError = "bad process package name"; - return; - } - final int vers = in.readInt(); - ProcessState proc = hadData ? mProcesses.get(procName, uid) : null; - if (proc != null) { - if (!proc.readFromParcel(in, false)) { - return; - } - } else { - proc = new ProcessState(this, pkgName, uid, vers, procName); - if (!proc.readFromParcel(in, true)) { - return; - } - } - if (DEBUG_PARCEL) Slog.d(TAG, "Adding process: " + procName + " " + uid - + " " + proc); - mProcesses.put(procName, uid, proc); - } - } - - if (DEBUG_PARCEL) Slog.d(TAG, "Read " + mProcesses.getMap().size() + " processes"); - - int NPKG = in.readInt(); - if (NPKG < 0) { - mReadError = "bad package count: " + NPKG; - return; - } - while (NPKG > 0) { - NPKG--; - final String pkgName = readCommonString(in, version); - if (pkgName == null) { - mReadError = "bad package name"; - return; - } - int NUID = in.readInt(); - if (NUID < 0) { - mReadError = "bad uid count: " + NUID; - return; - } - while (NUID > 0) { - NUID--; - final int uid = in.readInt(); - if (uid < 0) { - mReadError = "bad uid: " + uid; - return; - } - int NVERS = in.readInt(); - if (NVERS < 0) { - mReadError = "bad versions count: " + NVERS; - return; - } - while (NVERS > 0) { - NVERS--; - final int vers = in.readInt(); - PackageState pkgState = new PackageState(pkgName, uid); - SparseArray<PackageState> vpkg = mPackages.get(pkgName, uid); - if (vpkg == null) { - vpkg = new SparseArray<PackageState>(); - mPackages.put(pkgName, uid, vpkg); - } - vpkg.put(vers, pkgState); - int NPROCS = in.readInt(); - if (NPROCS < 0) { - mReadError = "bad package process count: " + NPROCS; - return; - } - while (NPROCS > 0) { - NPROCS--; - String procName = readCommonString(in, version); - if (procName == null) { - mReadError = "bad package process name"; - return; - } - int hasProc = in.readInt(); - if (DEBUG_PARCEL) Slog.d(TAG, "Reading package " + pkgName + " " + uid - + " process " + procName + " hasProc=" + hasProc); - ProcessState commonProc = mProcesses.get(procName, uid); - if (DEBUG_PARCEL) Slog.d(TAG, "Got common proc " + procName + " " + uid - + ": " + commonProc); - if (commonProc == null) { - mReadError = "no common proc: " + procName; - return; - } - if (hasProc != 0) { - // The process for this package is unique to the package; we - // need to load it. We don't need to do anything about it if - // it is not unique because if someone later looks for it - // they will find and use it from the global procs. - ProcessState proc = hadData ? pkgState.mProcesses.get(procName) : null; - if (proc != null) { - if (!proc.readFromParcel(in, false)) { - return; - } - } else { - proc = new ProcessState(commonProc, pkgName, uid, vers, procName, - 0); - if (!proc.readFromParcel(in, true)) { - return; - } - } - if (DEBUG_PARCEL) Slog.d(TAG, "Adding package " + pkgName + " process: " - + procName + " " + uid + " " + proc); - pkgState.mProcesses.put(procName, proc); - } else { - if (DEBUG_PARCEL) Slog.d(TAG, "Adding package " + pkgName + " process: " - + procName + " " + uid + " " + commonProc); - pkgState.mProcesses.put(procName, commonProc); - } - } - int NSRVS = in.readInt(); - if (NSRVS < 0) { - mReadError = "bad package service count: " + NSRVS; - return; - } - while (NSRVS > 0) { - NSRVS--; - String serviceName = in.readString(); - if (serviceName == null) { - mReadError = "bad package service name"; - return; - } - String processName = version > 9 ? readCommonString(in, version) : null; - ServiceState serv = hadData ? pkgState.mServices.get(serviceName) : null; - if (serv == null) { - serv = new ServiceState(this, pkgName, serviceName, processName, null); - } - if (!serv.readFromParcel(in)) { - return; - } - if (DEBUG_PARCEL) Slog.d(TAG, "Adding package " + pkgName + " service: " - + serviceName + " " + uid + " " + serv); - pkgState.mServices.put(serviceName, serv); - } - } - } - } - - mIndexToCommonString = null; - - if (DEBUG_PARCEL) Slog.d(TAG, "Successfully read procstats!"); - } - - int addLongData(int index, int type, int num) { - int off = allocLongData(num); - mAddLongTable = GrowingArrayUtils.insert( - mAddLongTable != null ? mAddLongTable : EmptyArray.INT, - mAddLongTableSize, index, type | off); - mAddLongTableSize++; - return off; - } - - int allocLongData(int num) { - int whichLongs = mLongs.size()-1; - long[] longs = mLongs.get(whichLongs); - if (mNextLong + num > longs.length) { - longs = new long[LONGS_SIZE]; - mLongs.add(longs); - whichLongs++; - mNextLong = 0; - } - int off = (whichLongs<<OFFSET_ARRAY_SHIFT) | (mNextLong<<OFFSET_INDEX_SHIFT); - mNextLong += num; - return off; - } - - boolean validateLongOffset(int off) { - int arr = (off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK; - if (arr >= mLongs.size()) { - return false; - } - int idx = (off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK; - if (idx >= LONGS_SIZE) { - return false; - } - if (DEBUG_PARCEL) Slog.d(TAG, "Validated long " + printLongOffset(off) - + ": " + getLong(off, 0)); - return true; - } - - static String printLongOffset(int off) { - StringBuilder sb = new StringBuilder(16); - sb.append("a"); sb.append((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); - sb.append("i"); sb.append((off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK); - sb.append("t"); sb.append((off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK); - return sb.toString(); - } - - void setLong(int off, int index, long value) { - long[] longs = mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); - longs[index + ((off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK)] = value; - } - - long getLong(int off, int index) { - long[] longs = mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); - return longs[index + ((off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK)]; - } - - static int binarySearch(int[] array, int size, int value) { - int lo = 0; - int hi = size - 1; - - while (lo <= hi) { - int mid = (lo + hi) >>> 1; - int midVal = (array[mid] >> OFFSET_TYPE_SHIFT) & OFFSET_TYPE_MASK; - - if (midVal < value) { - lo = mid + 1; - } else if (midVal > value) { - hi = mid - 1; - } else { - return mid; // value found - } - } - return ~lo; // value not present - } - - public PackageState getPackageStateLocked(String packageName, int uid, int vers) { - SparseArray<PackageState> vpkg = mPackages.get(packageName, uid); - if (vpkg == null) { - vpkg = new SparseArray<PackageState>(); - mPackages.put(packageName, uid, vpkg); - } - PackageState as = vpkg.get(vers); - if (as != null) { - return as; - } - as = new PackageState(packageName, uid); - vpkg.put(vers, as); - return as; - } - - public ProcessState getProcessStateLocked(String packageName, int uid, int vers, - String processName) { - final PackageState pkgState = getPackageStateLocked(packageName, uid, vers); - ProcessState ps = pkgState.mProcesses.get(processName); - if (ps != null) { - return ps; - } - ProcessState commonProc = mProcesses.get(processName, uid); - if (commonProc == null) { - commonProc = new ProcessState(this, packageName, uid, vers, processName); - mProcesses.put(processName, uid, commonProc); - if (DEBUG) Slog.d(TAG, "GETPROC created new common " + commonProc); - } - if (!commonProc.mMultiPackage) { - if (packageName.equals(commonProc.mPackage) && vers == commonProc.mVersion) { - // This common process is not in use by multiple packages, and - // is for the calling package, so we can just use it directly. - ps = commonProc; - if (DEBUG) Slog.d(TAG, "GETPROC also using for pkg " + commonProc); - } else { - if (DEBUG) Slog.d(TAG, "GETPROC need to split common proc!"); - // This common process has not been in use by multiple packages, - // but it was created for a different package than the caller. - // We need to convert it to a multi-package process. - commonProc.mMultiPackage = true; - // To do this, we need to make two new process states, one a copy - // of the current state for the process under the original package - // name, and the second a free new process state for it as the - // new package name. - long now = SystemClock.uptimeMillis(); - // First let's make a copy of the current process state and put - // that under the now unique state for its original package name. - final PackageState commonPkgState = getPackageStateLocked(commonProc.mPackage, - uid, commonProc.mVersion); - if (commonPkgState != null) { - ProcessState cloned = commonProc.clone(commonProc.mPackage, now); - if (DEBUG) Slog.d(TAG, "GETPROC setting clone to pkg " + commonProc.mPackage - + ": " + cloned); - commonPkgState.mProcesses.put(commonProc.mName, cloned); - // If this has active services, we need to update their process pointer - // to point to the new package-specific process state. - for (int i=commonPkgState.mServices.size()-1; i>=0; i--) { - ServiceState ss = commonPkgState.mServices.valueAt(i); - if (ss.mProc == commonProc) { - if (DEBUG) Slog.d(TAG, "GETPROC switching service to cloned: " - + ss); - ss.mProc = cloned; - } else if (DEBUG) { - Slog.d(TAG, "GETPROC leaving proc of " + ss); - } - } - } else { - Slog.w(TAG, "Cloning proc state: no package state " + commonProc.mPackage - + "/" + uid + " for proc " + commonProc.mName); - } - // And now make a fresh new process state for the new package name. - ps = new ProcessState(commonProc, packageName, uid, vers, processName, now); - if (DEBUG) Slog.d(TAG, "GETPROC created new pkg " + ps); - } - } else { - // The common process is for multiple packages, we need to create a - // separate object for the per-package data. - ps = new ProcessState(commonProc, packageName, uid, vers, processName, - SystemClock.uptimeMillis()); - if (DEBUG) Slog.d(TAG, "GETPROC created new pkg " + ps); - } - pkgState.mProcesses.put(processName, ps); - if (DEBUG) Slog.d(TAG, "GETPROC adding new pkg " + ps); - return ps; - } - - public ProcessStats.ServiceState getServiceStateLocked(String packageName, int uid, int vers, - String processName, String className) { - final ProcessStats.PackageState as = getPackageStateLocked(packageName, uid, vers); - ProcessStats.ServiceState ss = as.mServices.get(className); - if (ss != null) { - if (DEBUG) Slog.d(TAG, "GETSVC: returning existing " + ss); - return ss; - } - final ProcessStats.ProcessState ps = processName != null - ? getProcessStateLocked(packageName, uid, vers, processName) : null; - ss = new ProcessStats.ServiceState(this, packageName, className, processName, ps); - as.mServices.put(className, ss); - if (DEBUG) Slog.d(TAG, "GETSVC: creating " + ss + " in " + ps); - return ss; - } - - private void dumpProcessInternalLocked(PrintWriter pw, String prefix, ProcessState proc, - boolean dumpAll) { - if (dumpAll) { - pw.print(prefix); pw.print("myID="); - pw.print(Integer.toHexString(System.identityHashCode(proc))); - pw.print(" mCommonProcess="); - pw.print(Integer.toHexString(System.identityHashCode(proc.mCommonProcess))); - pw.print(" mPackage="); pw.println(proc.mPackage); - if (proc.mMultiPackage) { - pw.print(prefix); pw.print("mMultiPackage="); pw.println(proc.mMultiPackage); - } - if (proc != proc.mCommonProcess) { - pw.print(prefix); pw.print("Common Proc: "); pw.print(proc.mCommonProcess.mName); - pw.print("/"); pw.print(proc.mCommonProcess.mUid); - pw.print(" pkg="); pw.println(proc.mCommonProcess.mPackage); - } - } - if (proc.mActive) { - pw.print(prefix); pw.print("mActive="); pw.println(proc.mActive); - } - if (proc.mDead) { - pw.print(prefix); pw.print("mDead="); pw.println(proc.mDead); - } - if (proc.mNumActiveServices != 0 || proc.mNumStartedServices != 0) { - pw.print(prefix); pw.print("mNumActiveServices="); pw.print(proc.mNumActiveServices); - pw.print(" mNumStartedServices="); - pw.println(proc.mNumStartedServices); - } - } - - public void dumpLocked(PrintWriter pw, String reqPackage, long now, boolean dumpSummary, - boolean dumpAll, boolean activeOnly) { - long totalTime = dumpSingleTime(null, null, mMemFactorDurations, mMemFactor, - mStartTime, now); - boolean sepNeeded = false; - if (mSysMemUsageTable != null) { - pw.println("System memory usage:"); - dumpSysMemUsage(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ); - sepNeeded = true; - } - ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap(); - boolean printedHeader = false; - for (int ip=0; ip<pkgMap.size(); ip++) { - final String pkgName = pkgMap.keyAt(ip); - final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip); - for (int iu=0; iu<uids.size(); iu++) { - final int uid = uids.keyAt(iu); - final SparseArray<PackageState> vpkgs = uids.valueAt(iu); - for (int iv=0; iv<vpkgs.size(); iv++) { - final int vers = vpkgs.keyAt(iv); - final PackageState pkgState = vpkgs.valueAt(iv); - final int NPROCS = pkgState.mProcesses.size(); - final int NSRVS = pkgState.mServices.size(); - final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName); - if (!pkgMatch) { - boolean procMatch = false; - for (int iproc=0; iproc<NPROCS; iproc++) { - ProcessState proc = pkgState.mProcesses.valueAt(iproc); - if (reqPackage.equals(proc.mName)) { - procMatch = true; - break; - } - } - if (!procMatch) { - continue; - } - } - if (NPROCS > 0 || NSRVS > 0) { - if (!printedHeader) { - if (sepNeeded) pw.println(); - pw.println("Per-Package Stats:"); - printedHeader = true; - sepNeeded = true; - } - pw.print(" * "); pw.print(pkgName); pw.print(" / "); - UserHandle.formatUid(pw, uid); pw.print(" / v"); - pw.print(vers); pw.println(":"); - } - if (!dumpSummary || dumpAll) { - for (int iproc=0; iproc<NPROCS; iproc++) { - ProcessState proc = pkgState.mProcesses.valueAt(iproc); - if (!pkgMatch && !reqPackage.equals(proc.mName)) { - continue; - } - if (activeOnly && !proc.isInUse()) { - pw.print(" (Not active: "); - pw.print(pkgState.mProcesses.keyAt(iproc)); pw.println(")"); - continue; - } - pw.print(" Process "); - pw.print(pkgState.mProcesses.keyAt(iproc)); - if (proc.mCommonProcess.mMultiPackage) { - pw.print(" (multi, "); - } else { - pw.print(" (unique, "); - } - pw.print(proc.mDurationsTableSize); - pw.print(" entries)"); - pw.println(":"); - dumpProcessState(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ, - ALL_PROC_STATES, now); - dumpProcessPss(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ, - ALL_PROC_STATES); - dumpProcessInternalLocked(pw, " ", proc, dumpAll); - } - } else { - ArrayList<ProcessState> procs = new ArrayList<ProcessState>(); - for (int iproc=0; iproc<NPROCS; iproc++) { - ProcessState proc = pkgState.mProcesses.valueAt(iproc); - if (!pkgMatch && !reqPackage.equals(proc.mName)) { - continue; - } - if (activeOnly && !proc.isInUse()) { - continue; - } - procs.add(proc); - } - dumpProcessSummaryLocked(pw, " ", procs, ALL_SCREEN_ADJ, ALL_MEM_ADJ, - NON_CACHED_PROC_STATES, false, now, totalTime); - } - for (int isvc=0; isvc<NSRVS; isvc++) { - ServiceState svc = pkgState.mServices.valueAt(isvc); - if (!pkgMatch && !reqPackage.equals(svc.mProcessName)) { - continue; - } - if (activeOnly && !svc.isInUse()) { - pw.print(" (Not active: "); - pw.print(pkgState.mServices.keyAt(isvc)); pw.println(")"); - continue; - } - if (dumpAll) { - pw.print(" Service "); - } else { - pw.print(" * "); - } - pw.print(pkgState.mServices.keyAt(isvc)); - pw.println(":"); - pw.print(" Process: "); pw.println(svc.mProcessName); - dumpServiceStats(pw, " ", " ", " ", "Running", svc, - svc.mRunCount, ServiceState.SERVICE_RUN, svc.mRunState, - svc.mRunStartTime, now, totalTime, !dumpSummary || dumpAll); - dumpServiceStats(pw, " ", " ", " ", "Started", svc, - svc.mStartedCount, ServiceState.SERVICE_STARTED, svc.mStartedState, - svc.mStartedStartTime, now, totalTime, !dumpSummary || dumpAll); - dumpServiceStats(pw, " ", " ", " ", "Bound", svc, - svc.mBoundCount, ServiceState.SERVICE_BOUND, svc.mBoundState, - svc.mBoundStartTime, now, totalTime, !dumpSummary || dumpAll); - dumpServiceStats(pw, " ", " ", " ", "Executing", svc, - svc.mExecCount, ServiceState.SERVICE_EXEC, svc.mExecState, - svc.mExecStartTime, now, totalTime, !dumpSummary || dumpAll); - if (dumpAll) { - if (svc.mOwner != null) { - pw.print(" mOwner="); pw.println(svc.mOwner); - } - if (svc.mStarted || svc.mRestarting) { - pw.print(" mStarted="); pw.print(svc.mStarted); - pw.print(" mRestarting="); pw.println(svc.mRestarting); - } - } - } - } - } - } - - ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); - printedHeader = false; - int numShownProcs = 0, numTotalProcs = 0; - for (int ip=0; ip<procMap.size(); ip++) { - String procName = procMap.keyAt(ip); - SparseArray<ProcessState> uids = procMap.valueAt(ip); - for (int iu=0; iu<uids.size(); iu++) { - int uid = uids.keyAt(iu); - numTotalProcs++; - ProcessState proc = uids.valueAt(iu); - if (proc.mDurationsTableSize == 0 && proc.mCurState == STATE_NOTHING - && proc.mPssTableSize == 0) { - continue; - } - if (!proc.mMultiPackage) { - continue; - } - if (reqPackage != null && !reqPackage.equals(procName) - && !reqPackage.equals(proc.mPackage)) { - continue; - } - numShownProcs++; - if (sepNeeded) { - pw.println(); - } - sepNeeded = true; - if (!printedHeader) { - pw.println("Multi-Package Common Processes:"); - printedHeader = true; - } - if (activeOnly && !proc.isInUse()) { - pw.print(" (Not active: "); pw.print(procName); pw.println(")"); - continue; - } - pw.print(" * "); pw.print(procName); pw.print(" / "); - UserHandle.formatUid(pw, uid); - pw.print(" ("); pw.print(proc.mDurationsTableSize); - pw.print(" entries)"); pw.println(":"); - dumpProcessState(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ, - ALL_PROC_STATES, now); - dumpProcessPss(pw, " ", proc, ALL_SCREEN_ADJ, ALL_MEM_ADJ, - ALL_PROC_STATES); - dumpProcessInternalLocked(pw, " ", proc, dumpAll); - } - } - if (dumpAll) { - pw.println(); - pw.print(" Total procs: "); pw.print(numShownProcs); - pw.print(" shown of "); pw.print(numTotalProcs); pw.println(" total"); - } - - if (sepNeeded) { - pw.println(); - } - if (dumpSummary) { - pw.println("Summary:"); - dumpSummaryLocked(pw, reqPackage, now, activeOnly); - } else { - dumpTotalsLocked(pw, now); - } - - if (dumpAll) { - pw.println(); - pw.println("Internal state:"); - pw.print(" Num long arrays: "); pw.println(mLongs.size()); - pw.print(" Next long entry: "); pw.println(mNextLong); - pw.print(" mRunning="); pw.println(mRunning); - } - } - - public static long dumpSingleServiceTime(PrintWriter pw, String prefix, ServiceState service, - int serviceType, int curState, long curStartTime, long now) { - long totalTime = 0; - int printedScreen = -1; - for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) { - int printedMem = -1; - for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) { - int state = imem+iscreen; - long time = service.getDuration(serviceType, curState, curStartTime, - state, now); - String running = ""; - if (curState == state && pw != null) { - running = " (running)"; - } - if (time != 0) { - if (pw != null) { - pw.print(prefix); - printScreenLabel(pw, printedScreen != iscreen - ? iscreen : STATE_NOTHING); - printedScreen = iscreen; - printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING, (char)0); - printedMem = imem; - pw.print(": "); - TimeUtils.formatDuration(time, pw); pw.println(running); - } - totalTime += time; - } - } - } - if (totalTime != 0 && pw != null) { - pw.print(prefix); - pw.print(" TOTAL: "); - TimeUtils.formatDuration(totalTime, pw); - pw.println(); - } - return totalTime; - } - - void dumpServiceStats(PrintWriter pw, String prefix, String prefixInner, - String headerPrefix, String header, ServiceState service, - int count, int serviceType, int state, long startTime, long now, long totalTime, - boolean dumpAll) { - if (count != 0) { - if (dumpAll) { - pw.print(prefix); pw.print(header); - pw.print(" op count "); pw.print(count); pw.println(":"); - dumpSingleServiceTime(pw, prefixInner, service, serviceType, state, startTime, - now); - } else { - long myTime = dumpSingleServiceTime(null, null, service, serviceType, state, - startTime, now); - pw.print(prefix); pw.print(headerPrefix); pw.print(header); - pw.print(" count "); pw.print(count); - pw.print(" / time "); - printPercent(pw, (double)myTime/(double)totalTime); - pw.println(); - } - } - } - - public void dumpSummaryLocked(PrintWriter pw, String reqPackage, long now, boolean activeOnly) { - long totalTime = dumpSingleTime(null, null, mMemFactorDurations, mMemFactor, - mStartTime, now); - dumpFilteredSummaryLocked(pw, null, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, - ALL_PROC_STATES, NON_CACHED_PROC_STATES, now, totalTime, reqPackage, activeOnly); - pw.println(); - dumpTotalsLocked(pw, now); - } - - long printMemoryCategory(PrintWriter pw, String prefix, String label, double memWeight, - long totalTime, long curTotalMem, int samples) { - if (memWeight != 0) { - long mem = (long)(memWeight * 1024 / totalTime); - pw.print(prefix); - pw.print(label); - pw.print(": "); - DebugUtils.printSizeValue(pw, mem); - pw.print(" ("); - pw.print(samples); - pw.print(" samples)"); - pw.println(); - return curTotalMem + mem; - } - return curTotalMem; - } - - void dumpTotalsLocked(PrintWriter pw, long now) { - pw.println("Run time Stats:"); - dumpSingleTime(pw, " ", mMemFactorDurations, mMemFactor, mStartTime, now); - pw.println(); - pw.println("Memory usage:"); - TotalMemoryUseCollection totalMem = new TotalMemoryUseCollection(ALL_SCREEN_ADJ, - ALL_MEM_ADJ); - computeTotalMemoryUse(totalMem, now); - long totalPss = 0; - totalPss = printMemoryCategory(pw, " ", "Kernel ", totalMem.sysMemKernelWeight, - totalMem.totalTime, totalPss, totalMem.sysMemSamples); - totalPss = printMemoryCategory(pw, " ", "Native ", totalMem.sysMemNativeWeight, - totalMem.totalTime, totalPss, totalMem.sysMemSamples); - for (int i=0; i<STATE_COUNT; i++) { - // Skip restarting service state -- that is not actually a running process. - if (i != STATE_SERVICE_RESTARTING) { - totalPss = printMemoryCategory(pw, " ", STATE_NAMES[i], - totalMem.processStateWeight[i], totalMem.totalTime, totalPss, - totalMem.processStateSamples[i]); - } - } - totalPss = printMemoryCategory(pw, " ", "Cached ", totalMem.sysMemCachedWeight, - totalMem.totalTime, totalPss, totalMem.sysMemSamples); - totalPss = printMemoryCategory(pw, " ", "Free ", totalMem.sysMemFreeWeight, - totalMem.totalTime, totalPss, totalMem.sysMemSamples); - totalPss = printMemoryCategory(pw, " ", "Z-Ram ", totalMem.sysMemZRamWeight, - totalMem.totalTime, totalPss, totalMem.sysMemSamples); - pw.print(" TOTAL : "); - DebugUtils.printSizeValue(pw, totalPss); - pw.println(); - printMemoryCategory(pw, " ", STATE_NAMES[STATE_SERVICE_RESTARTING], - totalMem.processStateWeight[STATE_SERVICE_RESTARTING], totalMem.totalTime, totalPss, - totalMem.processStateSamples[STATE_SERVICE_RESTARTING]); - pw.println(); - pw.print(" Start time: "); - pw.print(DateFormat.format("yyyy-MM-dd HH:mm:ss", mTimePeriodStartClock)); - pw.println(); - pw.print(" Total elapsed time: "); - TimeUtils.formatDuration( - (mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime) - - mTimePeriodStartRealtime, pw); - boolean partial = true; - if ((mFlags&FLAG_SHUTDOWN) != 0) { - pw.print(" (shutdown)"); - partial = false; - } - if ((mFlags&FLAG_SYSPROPS) != 0) { - pw.print(" (sysprops)"); - partial = false; - } - if ((mFlags&FLAG_COMPLETE) != 0) { - pw.print(" (complete)"); - partial = false; - } - if (partial) { - pw.print(" (partial)"); - } - pw.print(' '); - pw.print(mRuntime); - pw.println(); - } - - void dumpFilteredSummaryLocked(PrintWriter pw, String header, String prefix, - int[] screenStates, int[] memStates, int[] procStates, - int[] sortProcStates, long now, long totalTime, String reqPackage, boolean activeOnly) { - ArrayList<ProcessState> procs = collectProcessesLocked(screenStates, memStates, - procStates, sortProcStates, now, reqPackage, activeOnly); - if (procs.size() > 0) { - if (header != null) { - pw.println(); - pw.println(header); - } - dumpProcessSummaryLocked(pw, prefix, procs, screenStates, memStates, - sortProcStates, true, now, totalTime); - } - } - - public ArrayList<ProcessState> collectProcessesLocked(int[] screenStates, int[] memStates, - int[] procStates, int sortProcStates[], long now, String reqPackage, - boolean activeOnly) { - final ArraySet<ProcessState> foundProcs = new ArraySet<ProcessState>(); - final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap(); - for (int ip=0; ip<pkgMap.size(); ip++) { - final String pkgName = pkgMap.keyAt(ip); - final SparseArray<SparseArray<PackageState>> procs = pkgMap.valueAt(ip); - for (int iu=0; iu<procs.size(); iu++) { - final SparseArray<PackageState> vpkgs = procs.valueAt(iu); - final int NVERS = vpkgs.size(); - for (int iv=0; iv<NVERS; iv++) { - final PackageState state = vpkgs.valueAt(iv); - final int NPROCS = state.mProcesses.size(); - final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName); - for (int iproc=0; iproc<NPROCS; iproc++) { - final ProcessState proc = state.mProcesses.valueAt(iproc); - if (!pkgMatch && !reqPackage.equals(proc.mName)) { - continue; - } - if (activeOnly && !proc.isInUse()) { - continue; - } - foundProcs.add(proc.mCommonProcess); - } - } - } - } - ArrayList<ProcessState> outProcs = new ArrayList<ProcessState>(foundProcs.size()); - for (int i=0; i<foundProcs.size(); i++) { - ProcessState proc = foundProcs.valueAt(i); - if (computeProcessTimeLocked(proc, screenStates, memStates, procStates, now) > 0) { - outProcs.add(proc); - if (procStates != sortProcStates) { - computeProcessTimeLocked(proc, screenStates, memStates, sortProcStates, now); - } - } - } - Collections.sort(outProcs, new Comparator<ProcessState>() { - @Override - public int compare(ProcessState lhs, ProcessState rhs) { - if (lhs.mTmpTotalTime < rhs.mTmpTotalTime) { - return -1; - } else if (lhs.mTmpTotalTime > rhs.mTmpTotalTime) { - return 1; - } - return 0; - } - }); - return outProcs; - } - - String collapseString(String pkgName, String itemName) { - if (itemName.startsWith(pkgName)) { - final int ITEMLEN = itemName.length(); - final int PKGLEN = pkgName.length(); - if (ITEMLEN == PKGLEN) { - return ""; - } else if (ITEMLEN >= PKGLEN) { - if (itemName.charAt(PKGLEN) == '.') { - return itemName.substring(PKGLEN); - } - } - } - return itemName; - } - - public void dumpCheckinLocked(PrintWriter pw, String reqPackage) { - final long now = SystemClock.uptimeMillis(); - final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap(); - pw.println("vers,5"); - pw.print("period,"); pw.print(mTimePeriodStartClockStr); - pw.print(","); pw.print(mTimePeriodStartRealtime); pw.print(","); - pw.print(mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime); - boolean partial = true; - if ((mFlags&FLAG_SHUTDOWN) != 0) { - pw.print(",shutdown"); - partial = false; - } - if ((mFlags&FLAG_SYSPROPS) != 0) { - pw.print(",sysprops"); - partial = false; - } - if ((mFlags&FLAG_COMPLETE) != 0) { - pw.print(",complete"); - partial = false; - } - if (partial) { - pw.print(",partial"); - } - pw.println(); - pw.print("config,"); pw.println(mRuntime); - for (int ip=0; ip<pkgMap.size(); ip++) { - final String pkgName = pkgMap.keyAt(ip); - if (reqPackage != null && !reqPackage.equals(pkgName)) { - continue; - } - final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip); - for (int iu=0; iu<uids.size(); iu++) { - final int uid = uids.keyAt(iu); - final SparseArray<PackageState> vpkgs = uids.valueAt(iu); - for (int iv=0; iv<vpkgs.size(); iv++) { - final int vers = vpkgs.keyAt(iv); - final PackageState pkgState = vpkgs.valueAt(iv); - final int NPROCS = pkgState.mProcesses.size(); - final int NSRVS = pkgState.mServices.size(); - for (int iproc=0; iproc<NPROCS; iproc++) { - ProcessState proc = pkgState.mProcesses.valueAt(iproc); - pw.print("pkgproc,"); - pw.print(pkgName); - pw.print(","); - pw.print(uid); - pw.print(","); - pw.print(vers); - pw.print(","); - pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc))); - dumpAllProcessStateCheckin(pw, proc, now); - pw.println(); - if (proc.mPssTableSize > 0) { - pw.print("pkgpss,"); - pw.print(pkgName); - pw.print(","); - pw.print(uid); - pw.print(","); - pw.print(vers); - pw.print(","); - pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc))); - dumpAllProcessPssCheckin(pw, proc); - pw.println(); - } - if (proc.mNumExcessiveWake > 0 || proc.mNumExcessiveCpu > 0 - || proc.mNumCachedKill > 0) { - pw.print("pkgkills,"); - pw.print(pkgName); - pw.print(","); - pw.print(uid); - pw.print(","); - pw.print(vers); - pw.print(","); - pw.print(collapseString(pkgName, pkgState.mProcesses.keyAt(iproc))); - pw.print(","); - pw.print(proc.mNumExcessiveWake); - pw.print(","); - pw.print(proc.mNumExcessiveCpu); - pw.print(","); - pw.print(proc.mNumCachedKill); - pw.print(","); - pw.print(proc.mMinCachedKillPss); - pw.print(":"); - pw.print(proc.mAvgCachedKillPss); - pw.print(":"); - pw.print(proc.mMaxCachedKillPss); - pw.println(); - } - } - for (int isvc=0; isvc<NSRVS; isvc++) { - String serviceName = collapseString(pkgName, - pkgState.mServices.keyAt(isvc)); - ServiceState svc = pkgState.mServices.valueAt(isvc); - dumpServiceTimeCheckin(pw, "pkgsvc-run", pkgName, uid, vers, serviceName, - svc, ServiceState.SERVICE_RUN, svc.mRunCount, - svc.mRunState, svc.mRunStartTime, now); - dumpServiceTimeCheckin(pw, "pkgsvc-start", pkgName, uid, vers, serviceName, - svc, ServiceState.SERVICE_STARTED, svc.mStartedCount, - svc.mStartedState, svc.mStartedStartTime, now); - dumpServiceTimeCheckin(pw, "pkgsvc-bound", pkgName, uid, vers, serviceName, - svc, ServiceState.SERVICE_BOUND, svc.mBoundCount, - svc.mBoundState, svc.mBoundStartTime, now); - dumpServiceTimeCheckin(pw, "pkgsvc-exec", pkgName, uid, vers, serviceName, - svc, ServiceState.SERVICE_EXEC, svc.mExecCount, - svc.mExecState, svc.mExecStartTime, now); - } - } - } - } - - ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); - for (int ip=0; ip<procMap.size(); ip++) { - String procName = procMap.keyAt(ip); - SparseArray<ProcessState> uids = procMap.valueAt(ip); - for (int iu=0; iu<uids.size(); iu++) { - int uid = uids.keyAt(iu); - ProcessState procState = uids.valueAt(iu); - if (procState.mDurationsTableSize > 0) { - pw.print("proc,"); - pw.print(procName); - pw.print(","); - pw.print(uid); - dumpAllProcessStateCheckin(pw, procState, now); - pw.println(); - } - if (procState.mPssTableSize > 0) { - pw.print("pss,"); - pw.print(procName); - pw.print(","); - pw.print(uid); - dumpAllProcessPssCheckin(pw, procState); - pw.println(); - } - if (procState.mNumExcessiveWake > 0 || procState.mNumExcessiveCpu > 0 - || procState.mNumCachedKill > 0) { - pw.print("kills,"); - pw.print(procName); - pw.print(","); - pw.print(uid); - pw.print(","); - pw.print(procState.mNumExcessiveWake); - pw.print(","); - pw.print(procState.mNumExcessiveCpu); - pw.print(","); - pw.print(procState.mNumCachedKill); - pw.print(","); - pw.print(procState.mMinCachedKillPss); - pw.print(":"); - pw.print(procState.mAvgCachedKillPss); - pw.print(":"); - pw.print(procState.mMaxCachedKillPss); - pw.println(); - } - } - } - pw.print("total"); - dumpAdjTimesCheckin(pw, ",", mMemFactorDurations, mMemFactor, - mStartTime, now); - pw.println(); - if (mSysMemUsageTable != null) { - pw.print("sysmemusage"); - for (int i=0; i<mSysMemUsageTableSize; i++) { - int off = mSysMemUsageTable[i]; - int type = (off>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; - pw.print(","); - printProcStateTag(pw, type); - for (int j=SYS_MEM_USAGE_SAMPLE_COUNT; j<SYS_MEM_USAGE_COUNT; j++) { - if (j > SYS_MEM_USAGE_CACHED_MINIMUM) { - pw.print(":"); - } - pw.print(getLong(off, j)); - } - } - } - pw.println(); - TotalMemoryUseCollection totalMem = new TotalMemoryUseCollection(ALL_SCREEN_ADJ, - ALL_MEM_ADJ); - computeTotalMemoryUse(totalMem, now); - pw.print("weights,"); - pw.print(totalMem.totalTime); - pw.print(","); - pw.print(totalMem.sysMemCachedWeight); - pw.print(":"); - pw.print(totalMem.sysMemSamples); - pw.print(","); - pw.print(totalMem.sysMemFreeWeight); - pw.print(":"); - pw.print(totalMem.sysMemSamples); - pw.print(","); - pw.print(totalMem.sysMemZRamWeight); - pw.print(":"); - pw.print(totalMem.sysMemSamples); - pw.print(","); - pw.print(totalMem.sysMemKernelWeight); - pw.print(":"); - pw.print(totalMem.sysMemSamples); - pw.print(","); - pw.print(totalMem.sysMemNativeWeight); - pw.print(":"); - pw.print(totalMem.sysMemSamples); - for (int i=0; i<STATE_COUNT; i++) { - pw.print(","); - pw.print(totalMem.processStateWeight[i]); - pw.print(":"); - pw.print(totalMem.processStateSamples[i]); - } - pw.println(); - } - - public static class DurationsTable { - public final ProcessStats mStats; - public final String mName; - public int[] mDurationsTable; - public int mDurationsTableSize; - - public DurationsTable(ProcessStats stats, String name) { - mStats = stats; - mName = name; - } - - void copyDurationsTo(DurationsTable other) { - if (mDurationsTable != null) { - mStats.mAddLongTable = new int[mDurationsTable.length]; - mStats.mAddLongTableSize = 0; - for (int i=0; i<mDurationsTableSize; i++) { - int origEnt = mDurationsTable[i]; - int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; - int newOff = mStats.addLongData(i, type, 1); - mStats.mAddLongTable[i] = newOff | type; - mStats.setLong(newOff, 0, mStats.getLong(origEnt, 0)); - } - other.mDurationsTable = mStats.mAddLongTable; - other.mDurationsTableSize = mStats.mAddLongTableSize; - } else { - other.mDurationsTable = null; - other.mDurationsTableSize = 0; - } - } - - void addDurations(DurationsTable other) { - for (int i=0; i<other.mDurationsTableSize; i++) { - int ent = other.mDurationsTable[i]; - int state = (ent>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; - if (DEBUG) Slog.d(TAG, "Adding state " + state + " duration " - + other.mStats.getLong(ent, 0)); - addDuration(state, other.mStats.getLong(ent, 0)); - } - } - - void resetDurationsSafely() { - mDurationsTable = null; - mDurationsTableSize = 0; - } - - void writeDurationsToParcel(Parcel out) { - out.writeInt(mDurationsTableSize); - for (int i=0; i<mDurationsTableSize; i++) { - if (DEBUG_PARCEL) Slog.i(TAG, "Writing in " + mName + " dur #" + i + ": " - + printLongOffset(mDurationsTable[i])); - out.writeInt(mDurationsTable[i]); - } - } - - boolean readDurationsFromParcel(Parcel in) { - mDurationsTable = mStats.readTableFromParcel(in, mName, "durations"); - if (mDurationsTable == BAD_TABLE) { - return false; - } - mDurationsTableSize = mDurationsTable != null ? mDurationsTable.length : 0; - return true; - } - - void addDuration(int state, long dur) { - int idx = binarySearch(mDurationsTable, mDurationsTableSize, state); - int off; - if (idx >= 0) { - off = mDurationsTable[idx]; - } else { - mStats.mAddLongTable = mDurationsTable; - mStats.mAddLongTableSize = mDurationsTableSize; - off = mStats.addLongData(~idx, state, 1); - mDurationsTable = mStats.mAddLongTable; - mDurationsTableSize = mStats.mAddLongTableSize; - } - long[] longs = mStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); - if (DEBUG) Slog.d(TAG, "Duration of " + mName + " state " + state + " inc by " + dur - + " from " + longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK]); - longs[(off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK] += dur; - } - - long getDuration(int state, long now) { - int idx = binarySearch(mDurationsTable, mDurationsTableSize, state); - return idx >= 0 ? mStats.getLong(mDurationsTable[idx], 0) : 0; - } - } - - final public static class ProcessStateHolder { - public final int appVersion; - public ProcessStats.ProcessState state; - - public ProcessStateHolder(int _appVersion) { - appVersion = _appVersion; - } - } - - public static final class ProcessState extends DurationsTable { - public ProcessState mCommonProcess; - public final String mPackage; - public final int mUid; - public final int mVersion; - - //final long[] mDurations = new long[STATE_COUNT*ADJ_COUNT]; - int mCurState = STATE_NOTHING; - long mStartTime; - - int mLastPssState = STATE_NOTHING; - long mLastPssTime; - int[] mPssTable; - int mPssTableSize; - - boolean mActive; - int mNumActiveServices; - int mNumStartedServices; - - int mNumExcessiveWake; - int mNumExcessiveCpu; - - int mNumCachedKill; - long mMinCachedKillPss; - long mAvgCachedKillPss; - long mMaxCachedKillPss; - - boolean mMultiPackage; - boolean mDead; - - public long mTmpTotalTime; - int mTmpNumInUse; - ProcessState mTmpFoundSubProc; - - /** - * Create a new top-level process state, for the initial case where there is only - * a single package running in a process. The initial state is not running. - */ - public ProcessState(ProcessStats processStats, String pkg, int uid, int vers, String name) { - super(processStats, name); - mCommonProcess = this; - mPackage = pkg; - mUid = uid; - mVersion = vers; - } - - /** - * Create a new per-package process state for an existing top-level process - * state. The current running state of the top-level process is also copied, - * marked as started running at 'now'. - */ - public ProcessState(ProcessState commonProcess, String pkg, int uid, int vers, String name, - long now) { - super(commonProcess.mStats, name); - mCommonProcess = commonProcess; - mPackage = pkg; - mUid = uid; - mVersion = vers; - mCurState = commonProcess.mCurState; - mStartTime = now; - } - - ProcessState clone(String pkg, long now) { - ProcessState pnew = new ProcessState(this, pkg, mUid, mVersion, mName, now); - copyDurationsTo(pnew); - if (mPssTable != null) { - mStats.mAddLongTable = new int[mPssTable.length]; - mStats.mAddLongTableSize = 0; - for (int i=0; i<mPssTableSize; i++) { - int origEnt = mPssTable[i]; - int type = (origEnt>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; - int newOff = mStats.addLongData(i, type, PSS_COUNT); - mStats.mAddLongTable[i] = newOff | type; - for (int j=0; j<PSS_COUNT; j++) { - mStats.setLong(newOff, j, mStats.getLong(origEnt, j)); - } - } - pnew.mPssTable = mStats.mAddLongTable; - pnew.mPssTableSize = mStats.mAddLongTableSize; - } - pnew.mNumExcessiveWake = mNumExcessiveWake; - pnew.mNumExcessiveCpu = mNumExcessiveCpu; - pnew.mNumCachedKill = mNumCachedKill; - pnew.mMinCachedKillPss = mMinCachedKillPss; - pnew.mAvgCachedKillPss = mAvgCachedKillPss; - pnew.mMaxCachedKillPss = mMaxCachedKillPss; - pnew.mActive = mActive; - pnew.mNumActiveServices = mNumActiveServices; - pnew.mNumStartedServices = mNumStartedServices; - return pnew; - } - - void add(ProcessState other) { - addDurations(other); - for (int i=0; i<other.mPssTableSize; i++) { - int ent = other.mPssTable[i]; - int state = (ent>>OFFSET_TYPE_SHIFT)&OFFSET_TYPE_MASK; - addPss(state, (int) other.mStats.getLong(ent, PSS_SAMPLE_COUNT), - other.mStats.getLong(ent, PSS_MINIMUM), - other.mStats.getLong(ent, PSS_AVERAGE), - other.mStats.getLong(ent, PSS_MAXIMUM), - other.mStats.getLong(ent, PSS_USS_MINIMUM), - other.mStats.getLong(ent, PSS_USS_AVERAGE), - other.mStats.getLong(ent, PSS_USS_MAXIMUM)); - } - mNumExcessiveWake += other.mNumExcessiveWake; - mNumExcessiveCpu += other.mNumExcessiveCpu; - if (other.mNumCachedKill > 0) { - addCachedKill(other.mNumCachedKill, other.mMinCachedKillPss, - other.mAvgCachedKillPss, other.mMaxCachedKillPss); - } - } - - void resetSafely(long now) { - resetDurationsSafely(); - mStartTime = now; - mLastPssState = STATE_NOTHING; - mLastPssTime = 0; - mPssTable = null; - mPssTableSize = 0; - mNumExcessiveWake = 0; - mNumExcessiveCpu = 0; - mNumCachedKill = 0; - mMinCachedKillPss = mAvgCachedKillPss = mMaxCachedKillPss = 0; - } - - void makeDead() { - mDead = true; - } - - private void ensureNotDead() { - if (!mDead) { - return; - } - Slog.wtfStack(TAG, "ProcessState dead: name=" + mName - + " pkg=" + mPackage + " uid=" + mUid + " common.name=" + mCommonProcess.mName); - } - - void writeToParcel(Parcel out, long now) { - out.writeInt(mMultiPackage ? 1 : 0); - writeDurationsToParcel(out); - out.writeInt(mPssTableSize); - for (int i=0; i<mPssTableSize; i++) { - if (DEBUG_PARCEL) Slog.i(TAG, "Writing in " + mName + " pss #" + i + ": " - + printLongOffset(mPssTable[i])); - out.writeInt(mPssTable[i]); - } - out.writeInt(mNumExcessiveWake); - out.writeInt(mNumExcessiveCpu); - out.writeInt(mNumCachedKill); - if (mNumCachedKill > 0) { - out.writeLong(mMinCachedKillPss); - out.writeLong(mAvgCachedKillPss); - out.writeLong(mMaxCachedKillPss); - } - } - - boolean readFromParcel(Parcel in, boolean fully) { - boolean multiPackage = in.readInt() != 0; - if (fully) { - mMultiPackage = multiPackage; - } - if (DEBUG_PARCEL) Slog.d(TAG, "Reading durations table..."); - if (!readDurationsFromParcel(in)) { - return false; - } - if (DEBUG_PARCEL) Slog.d(TAG, "Reading pss table..."); - mPssTable = mStats.readTableFromParcel(in, mName, "pss"); - if (mPssTable == BAD_TABLE) { - return false; - } - mPssTableSize = mPssTable != null ? mPssTable.length : 0; - mNumExcessiveWake = in.readInt(); - mNumExcessiveCpu = in.readInt(); - mNumCachedKill = in.readInt(); - if (mNumCachedKill > 0) { - mMinCachedKillPss = in.readLong(); - mAvgCachedKillPss = in.readLong(); - mMaxCachedKillPss = in.readLong(); - } else { - mMinCachedKillPss = mAvgCachedKillPss = mMaxCachedKillPss = 0; - } - return true; - } - - public void makeActive() { - ensureNotDead(); - mActive = true; - } - - public void makeInactive() { - mActive = false; - } - - public boolean isInUse() { - return mActive || mNumActiveServices > 0 || mNumStartedServices > 0 - || mCurState != STATE_NOTHING; - } - - /** - * Update the current state of the given list of processes. - * - * @param state Current ActivityManager.PROCESS_STATE_* - * @param memFactor Current mem factor constant. - * @param now Current time. - * @param pkgList Processes to update. - */ - public void setState(int state, int memFactor, long now, - ArrayMap<String, ProcessStateHolder> pkgList) { - if (state < 0) { - state = mNumStartedServices > 0 - ? (STATE_SERVICE_RESTARTING+(memFactor*STATE_COUNT)) : STATE_NOTHING; - } else { - state = PROCESS_STATE_TO_STATE[state] + (memFactor*STATE_COUNT); - } - - // First update the common process. - mCommonProcess.setState(state, now); - - // If the common process is not multi-package, there is nothing else to do. - if (!mCommonProcess.mMultiPackage) { - return; - } - - if (pkgList != null) { - for (int ip=pkgList.size()-1; ip>=0; ip--) { - pullFixedProc(pkgList, ip).setState(state, now); - } - } - } - - void setState(int state, long now) { - ensureNotDead(); - if (mCurState != state) { - //Slog.i(TAG, "Setting state in " + mName + "/" + mPackage + ": " + state); - commitStateTime(now); - mCurState = state; - } - } - - void commitStateTime(long now) { - if (mCurState != STATE_NOTHING) { - long dur = now - mStartTime; - if (dur > 0) { - addDuration(mCurState, dur); - } - } - mStartTime = now; - } - - void incActiveServices(String serviceName) { - if (DEBUG && "".equals(mName)) { - RuntimeException here = new RuntimeException("here"); - here.fillInStackTrace(); - Slog.d(TAG, "incActiveServices: " + this + " service=" + serviceName - + " to " + (mNumActiveServices+1), here); - } - if (mCommonProcess != this) { - mCommonProcess.incActiveServices(serviceName); - } - mNumActiveServices++; - } - - void decActiveServices(String serviceName) { - if (DEBUG && "".equals(mName)) { - RuntimeException here = new RuntimeException("here"); - here.fillInStackTrace(); - Slog.d(TAG, "decActiveServices: " + this + " service=" + serviceName - + " to " + (mNumActiveServices-1), here); - } - if (mCommonProcess != this) { - mCommonProcess.decActiveServices(serviceName); - } - mNumActiveServices--; - if (mNumActiveServices < 0) { - Slog.wtfStack(TAG, "Proc active services underrun: pkg=" + mPackage - + " uid=" + mUid + " proc=" + mName + " service=" + serviceName); - mNumActiveServices = 0; - } - } - - void incStartedServices(int memFactor, long now, String serviceName) { - if (false) { - RuntimeException here = new RuntimeException("here"); - here.fillInStackTrace(); - Slog.d(TAG, "incStartedServices: " + this + " service=" + serviceName - + " to " + (mNumStartedServices+1), here); - } - if (mCommonProcess != this) { - mCommonProcess.incStartedServices(memFactor, now, serviceName); - } - mNumStartedServices++; - if (mNumStartedServices == 1 && mCurState == STATE_NOTHING) { - setState(STATE_SERVICE_RESTARTING + (memFactor*STATE_COUNT), now); - } - } - - void decStartedServices(int memFactor, long now, String serviceName) { - if (false) { - RuntimeException here = new RuntimeException("here"); - here.fillInStackTrace(); - Slog.d(TAG, "decActiveServices: " + this + " service=" + serviceName - + " to " + (mNumStartedServices-1), here); - } - if (mCommonProcess != this) { - mCommonProcess.decStartedServices(memFactor, now, serviceName); - } - mNumStartedServices--; - if (mNumStartedServices == 0 && (mCurState%STATE_COUNT) == STATE_SERVICE_RESTARTING) { - setState(STATE_NOTHING, now); - } else if (mNumStartedServices < 0) { - Slog.wtfStack(TAG, "Proc started services underrun: pkg=" - + mPackage + " uid=" + mUid + " name=" + mName); - mNumStartedServices = 0; - } - } - - public void addPss(long pss, long uss, boolean always, - ArrayMap<String, ProcessStateHolder> pkgList) { - ensureNotDead(); - if (!always) { - if (mLastPssState == mCurState && SystemClock.uptimeMillis() - < (mLastPssTime+(30*1000))) { - return; - } - } - mLastPssState = mCurState; - mLastPssTime = SystemClock.uptimeMillis(); - if (mCurState != STATE_NOTHING) { - // First update the common process. - mCommonProcess.addPss(mCurState, 1, pss, pss, pss, uss, uss, uss); - - // If the common process is not multi-package, there is nothing else to do. - if (!mCommonProcess.mMultiPackage) { - return; - } - - if (pkgList != null) { - for (int ip=pkgList.size()-1; ip>=0; ip--) { - pullFixedProc(pkgList, ip).addPss(mCurState, 1, - pss, pss, pss, uss, uss, uss); - } - } - } - } - - void addPss(int state, int inCount, long minPss, long avgPss, long maxPss, long minUss, - long avgUss, long maxUss) { - int idx = binarySearch(mPssTable, mPssTableSize, state); - int off; - if (idx >= 0) { - off = mPssTable[idx]; - } else { - mStats.mAddLongTable = mPssTable; - mStats.mAddLongTableSize = mPssTableSize; - off = mStats.addLongData(~idx, state, PSS_COUNT); - mPssTable = mStats.mAddLongTable; - mPssTableSize = mStats.mAddLongTableSize; - } - long[] longs = mStats.mLongs.get((off>>OFFSET_ARRAY_SHIFT)&OFFSET_ARRAY_MASK); - idx = (off>>OFFSET_INDEX_SHIFT)&OFFSET_INDEX_MASK; - long count = longs[idx+PSS_SAMPLE_COUNT]; - if (count == 0) { - longs[idx+PSS_SAMPLE_COUNT] = inCount; - longs[idx+PSS_MINIMUM] = minPss; - longs[idx+PSS_AVERAGE] = avgPss; - longs[idx+PSS_MAXIMUM] = maxPss; - longs[idx+PSS_USS_MINIMUM] = minUss; - longs[idx+PSS_USS_AVERAGE] = avgUss; - longs[idx+PSS_USS_MAXIMUM] = maxUss; - } else { - longs[idx+PSS_SAMPLE_COUNT] = count+inCount; - if (longs[idx+PSS_MINIMUM] > minPss) { - longs[idx+PSS_MINIMUM] = minPss; - } - longs[idx+PSS_AVERAGE] = (long)( - ((longs[idx+PSS_AVERAGE]*(double)count)+(avgPss*(double)inCount)) - / (count+inCount) ); - if (longs[idx+PSS_MAXIMUM] < maxPss) { - longs[idx+PSS_MAXIMUM] = maxPss; - } - if (longs[idx+PSS_USS_MINIMUM] > minUss) { - longs[idx+PSS_USS_MINIMUM] = minUss; - } - longs[idx+PSS_USS_AVERAGE] = (long)( - ((longs[idx+PSS_USS_AVERAGE]*(double)count)+(avgUss*(double)inCount)) - / (count+inCount) ); - if (longs[idx+PSS_USS_MAXIMUM] < maxUss) { - longs[idx+PSS_USS_MAXIMUM] = maxUss; - } - } - } - - public void reportExcessiveWake(ArrayMap<String, ProcessStateHolder> pkgList) { - ensureNotDead(); - mCommonProcess.mNumExcessiveWake++; - if (!mCommonProcess.mMultiPackage) { - return; - } - - for (int ip=pkgList.size()-1; ip>=0; ip--) { - pullFixedProc(pkgList, ip).mNumExcessiveWake++; - } - } - - public void reportExcessiveCpu(ArrayMap<String, ProcessStateHolder> pkgList) { - ensureNotDead(); - mCommonProcess.mNumExcessiveCpu++; - if (!mCommonProcess.mMultiPackage) { - return; - } - - for (int ip=pkgList.size()-1; ip>=0; ip--) { - pullFixedProc(pkgList, ip).mNumExcessiveCpu++; - } - } - - private void addCachedKill(int num, long minPss, long avgPss, long maxPss) { - if (mNumCachedKill <= 0) { - mNumCachedKill = num; - mMinCachedKillPss = minPss; - mAvgCachedKillPss = avgPss; - mMaxCachedKillPss = maxPss; - } else { - if (minPss < mMinCachedKillPss) { - mMinCachedKillPss = minPss; - } - if (maxPss > mMaxCachedKillPss) { - mMaxCachedKillPss = maxPss; - } - mAvgCachedKillPss = (long)( ((mAvgCachedKillPss*(double)mNumCachedKill) + avgPss) - / (mNumCachedKill+num) ); - mNumCachedKill += num; - } - } - - public void reportCachedKill(ArrayMap<String, ProcessStateHolder> pkgList, long pss) { - ensureNotDead(); - mCommonProcess.addCachedKill(1, pss, pss, pss); - if (!mCommonProcess.mMultiPackage) { - return; - } - - for (int ip=pkgList.size()-1; ip>=0; ip--) { - pullFixedProc(pkgList, ip).addCachedKill(1, pss, pss, pss); - } - } - - ProcessState pullFixedProc(String pkgName) { - if (mMultiPackage) { - // The array map is still pointing to a common process state - // that is now shared across packages. Update it to point to - // the new per-package state. - SparseArray<PackageState> vpkg = mStats.mPackages.get(pkgName, mUid); - if (vpkg == null) { - throw new IllegalStateException("Didn't find package " + pkgName - + " / " + mUid); - } - PackageState pkg = vpkg.get(mVersion); - if (pkg == null) { - throw new IllegalStateException("Didn't find package " + pkgName - + " / " + mUid + " vers " + mVersion); - } - ProcessState proc = pkg.mProcesses.get(mName); - if (proc == null) { - throw new IllegalStateException("Didn't create per-package process " - + mName + " in pkg " + pkgName + " / " + mUid + " vers " + mVersion); - } - return proc; - } - return this; - } - - private ProcessState pullFixedProc(ArrayMap<String, ProcessStateHolder> pkgList, - int index) { - ProcessStateHolder holder = pkgList.valueAt(index); - ProcessState proc = holder.state; - if (mDead && proc.mCommonProcess != proc) { - // Somehow we are contining to use a process state that is dead, because - // it was not being told it was active during the last commit. We can recover - // from this by generating a fresh new state, but this is bad because we - // are losing whatever data we had in the old process state. - Log.wtf(TAG, "Pulling dead proc: name=" + mName + " pkg=" + mPackage - + " uid=" + mUid + " common.name=" + mCommonProcess.mName); - proc = mStats.getProcessStateLocked(proc.mPackage, proc.mUid, proc.mVersion, - proc.mName); - } - if (proc.mMultiPackage) { - // The array map is still pointing to a common process state - // that is now shared across packages. Update it to point to - // the new per-package state. - SparseArray<PackageState> vpkg = mStats.mPackages.get(pkgList.keyAt(index), - proc.mUid); - if (vpkg == null) { - throw new IllegalStateException("No existing package " - + pkgList.keyAt(index) + "/" + proc.mUid - + " for multi-proc " + proc.mName); - } - PackageState pkg = vpkg.get(proc.mVersion); - if (pkg == null) { - throw new IllegalStateException("No existing package " - + pkgList.keyAt(index) + "/" + proc.mUid - + " for multi-proc " + proc.mName + " version " + proc.mVersion); - } - String savedName = proc.mName; - proc = pkg.mProcesses.get(proc.mName); - if (proc == null) { - throw new IllegalStateException("Didn't create per-package process " - + savedName + " in pkg " + pkg.mPackageName + "/" + pkg.mUid); - } - holder.state = proc; - } - return proc; - } - - long getDuration(int state, long now) { - long time = super.getDuration(state, now); - if (mCurState == state) { - time += now - mStartTime; - } - return time; - } - - long getPssSampleCount(int state) { - int idx = binarySearch(mPssTable, mPssTableSize, state); - return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_SAMPLE_COUNT) : 0; - } - - long getPssMinimum(int state) { - int idx = binarySearch(mPssTable, mPssTableSize, state); - return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_MINIMUM) : 0; - } - - long getPssAverage(int state) { - int idx = binarySearch(mPssTable, mPssTableSize, state); - return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_AVERAGE) : 0; - } - - long getPssMaximum(int state) { - int idx = binarySearch(mPssTable, mPssTableSize, state); - return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_MAXIMUM) : 0; - } - - long getPssUssMinimum(int state) { - int idx = binarySearch(mPssTable, mPssTableSize, state); - return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_USS_MINIMUM) : 0; - } - - long getPssUssAverage(int state) { - int idx = binarySearch(mPssTable, mPssTableSize, state); - return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_USS_AVERAGE) : 0; - } - - long getPssUssMaximum(int state) { - int idx = binarySearch(mPssTable, mPssTableSize, state); - return idx >= 0 ? mStats.getLong(mPssTable[idx], PSS_USS_MAXIMUM) : 0; - } - - public String toString() { - StringBuilder sb = new StringBuilder(128); - sb.append("ProcessState{").append(Integer.toHexString(System.identityHashCode(this))) - .append(" ").append(mName).append("/").append(mUid) - .append(" pkg=").append(mPackage); - if (mMultiPackage) sb.append(" (multi)"); - if (mCommonProcess != this) sb.append(" (sub)"); - sb.append("}"); - return sb.toString(); - } - } - - public static final class ServiceState extends DurationsTable { - public final String mPackage; - public final String mProcessName; - ProcessState mProc; - - Object mOwner; - - public static final int SERVICE_RUN = 0; - public static final int SERVICE_STARTED = 1; - public static final int SERVICE_BOUND = 2; - public static final int SERVICE_EXEC = 3; - static final int SERVICE_COUNT = 4; - - int mRunCount; - public int mRunState = STATE_NOTHING; - long mRunStartTime; - - boolean mStarted; - boolean mRestarting; - int mStartedCount; - public int mStartedState = STATE_NOTHING; - long mStartedStartTime; - - int mBoundCount; - public int mBoundState = STATE_NOTHING; - long mBoundStartTime; - - int mExecCount; - public int mExecState = STATE_NOTHING; - long mExecStartTime; - - public ServiceState(ProcessStats processStats, String pkg, String name, - String processName, ProcessState proc) { - super(processStats, name); - mPackage = pkg; - mProcessName = processName; - mProc = proc; - } - - public void applyNewOwner(Object newOwner) { - if (mOwner != newOwner) { - if (mOwner == null) { - mOwner = newOwner; - mProc.incActiveServices(mName); - } else { - // There was already an old owner, reset this object for its - // new owner. - mOwner = newOwner; - if (mStarted || mBoundState != STATE_NOTHING || mExecState != STATE_NOTHING) { - long now = SystemClock.uptimeMillis(); - if (mStarted) { - if (DEBUG) Slog.d(TAG, "Service has new owner " + newOwner - + " from " + mOwner + " while started: pkg=" - + mPackage + " service=" + mName + " proc=" + mProc); - setStarted(false, 0, now); - } - if (mBoundState != STATE_NOTHING) { - if (DEBUG) Slog.d(TAG, "Service has new owner " + newOwner - + " from " + mOwner + " while bound: pkg=" - + mPackage + " service=" + mName + " proc=" + mProc); - setBound(false, 0, now); - } - if (mExecState != STATE_NOTHING) { - if (DEBUG) Slog.d(TAG, "Service has new owner " + newOwner - + " from " + mOwner + " while executing: pkg=" - + mPackage + " service=" + mName + " proc=" + mProc); - setExecuting(false, 0, now); - } - } - } - } - } - - public void clearCurrentOwner(Object owner, boolean silently) { - if (mOwner == owner) { - mProc.decActiveServices(mName); - if (mStarted || mBoundState != STATE_NOTHING || mExecState != STATE_NOTHING) { - long now = SystemClock.uptimeMillis(); - if (mStarted) { - if (!silently) { - Slog.wtfStack(TAG, "Service owner " + owner - + " cleared while started: pkg=" + mPackage + " service=" - + mName + " proc=" + mProc); - } - setStarted(false, 0, now); - } - if (mBoundState != STATE_NOTHING) { - if (!silently) { - Slog.wtfStack(TAG, "Service owner " + owner - + " cleared while bound: pkg=" + mPackage + " service=" - + mName + " proc=" + mProc); - } - setBound(false, 0, now); - } - if (mExecState != STATE_NOTHING) { - if (!silently) { - Slog.wtfStack(TAG, "Service owner " + owner - + " cleared while exec: pkg=" + mPackage + " service=" - + mName + " proc=" + mProc); - } - setExecuting(false, 0, now); - } - } - mOwner = null; - } - } - - public boolean isInUse() { - return mOwner != null || mRestarting; - } - - public boolean isRestarting() { - return mRestarting; - } - - void add(ServiceState other) { - addDurations(other); - mRunCount += other.mRunCount; - mStartedCount += other.mStartedCount; - mBoundCount += other.mBoundCount; - mExecCount += other.mExecCount; - } - - void resetSafely(long now) { - resetDurationsSafely(); - mRunCount = mRunState != STATE_NOTHING ? 1 : 0; - mStartedCount = mStartedState != STATE_NOTHING ? 1 : 0; - mBoundCount = mBoundState != STATE_NOTHING ? 1 : 0; - mExecCount = mExecState != STATE_NOTHING ? 1 : 0; - mRunStartTime = mStartedStartTime = mBoundStartTime = mExecStartTime = now; - } - - void writeToParcel(Parcel out, long now) { - writeDurationsToParcel(out); - out.writeInt(mRunCount); - out.writeInt(mStartedCount); - out.writeInt(mBoundCount); - out.writeInt(mExecCount); - } - - boolean readFromParcel(Parcel in) { - if (!readDurationsFromParcel(in)) { - return false; - } - mRunCount = in.readInt(); - mStartedCount = in.readInt(); - mBoundCount = in.readInt(); - mExecCount = in.readInt(); - return true; - } - - void commitStateTime(long now) { - if (mRunState != STATE_NOTHING) { - addDuration(SERVICE_RUN + (mRunState*SERVICE_COUNT), now - mRunStartTime); - mRunStartTime = now; - } - if (mStartedState != STATE_NOTHING) { - addDuration(SERVICE_STARTED + (mStartedState*SERVICE_COUNT), - now - mStartedStartTime); - mStartedStartTime = now; - } - if (mBoundState != STATE_NOTHING) { - addDuration(SERVICE_BOUND + (mBoundState*SERVICE_COUNT), now - mBoundStartTime); - mBoundStartTime = now; - } - if (mExecState != STATE_NOTHING) { - addDuration(SERVICE_EXEC + (mExecState*SERVICE_COUNT), now - mExecStartTime); - mExecStartTime = now; - } - } - - private void updateRunning(int memFactor, long now) { - final int state = (mStartedState != STATE_NOTHING || mBoundState != STATE_NOTHING - || mExecState != STATE_NOTHING) ? memFactor : STATE_NOTHING; - if (mRunState != state) { - if (mRunState != STATE_NOTHING) { - addDuration(SERVICE_RUN + (mRunState*SERVICE_COUNT), - now - mRunStartTime); - } else if (state != STATE_NOTHING) { - mRunCount++; - } - mRunState = state; - mRunStartTime = now; - } - } - - public void setStarted(boolean started, int memFactor, long now) { - if (mOwner == null) { - Slog.wtf(TAG, "Starting service " + this + " without owner"); - } - mStarted = started; - updateStartedState(memFactor, now); - } - - public void setRestarting(boolean restarting, int memFactor, long now) { - mRestarting = restarting; - updateStartedState(memFactor, now); - } - - void updateStartedState(int memFactor, long now) { - final boolean wasStarted = mStartedState != STATE_NOTHING; - final boolean started = mStarted || mRestarting; - final int state = started ? memFactor : STATE_NOTHING; - if (mStartedState != state) { - if (mStartedState != STATE_NOTHING) { - addDuration(SERVICE_STARTED + (mStartedState*SERVICE_COUNT), - now - mStartedStartTime); - } else if (started) { - mStartedCount++; - } - mStartedState = state; - mStartedStartTime = now; - mProc = mProc.pullFixedProc(mPackage); - if (wasStarted != started) { - if (started) { - mProc.incStartedServices(memFactor, now, mName); - } else { - mProc.decStartedServices(memFactor, now, mName); - } - } - updateRunning(memFactor, now); - } - } - - public void setBound(boolean bound, int memFactor, long now) { - if (mOwner == null) { - Slog.wtf(TAG, "Binding service " + this + " without owner"); - } - final int state = bound ? memFactor : STATE_NOTHING; - if (mBoundState != state) { - if (mBoundState != STATE_NOTHING) { - addDuration(SERVICE_BOUND + (mBoundState*SERVICE_COUNT), - now - mBoundStartTime); - } else if (bound) { - mBoundCount++; - } - mBoundState = state; - mBoundStartTime = now; - updateRunning(memFactor, now); - } - } - - public void setExecuting(boolean executing, int memFactor, long now) { - if (mOwner == null) { - Slog.wtf(TAG, "Executing service " + this + " without owner"); - } - final int state = executing ? memFactor : STATE_NOTHING; - if (mExecState != state) { - if (mExecState != STATE_NOTHING) { - addDuration(SERVICE_EXEC + (mExecState*SERVICE_COUNT), now - mExecStartTime); - } else if (executing) { - mExecCount++; - } - mExecState = state; - mExecStartTime = now; - updateRunning(memFactor, now); - } - } - - private long getDuration(int opType, int curState, long startTime, int memFactor, - long now) { - int state = opType + (memFactor*SERVICE_COUNT); - long time = getDuration(state, now); - if (curState == memFactor) { - time += now - startTime; - } - return time; - } - - public String toString() { - return "ServiceState{" + Integer.toHexString(System.identityHashCode(this)) - + " " + mName + " pkg=" + mPackage + " proc=" - + Integer.toHexString(System.identityHashCode(this)) + "}"; - } - } - - public static final class PackageState { - public final ArrayMap<String, ProcessState> mProcesses - = new ArrayMap<String, ProcessState>(); - public final ArrayMap<String, ServiceState> mServices - = new ArrayMap<String, ServiceState>(); - public final String mPackageName; - public final int mUid; - - public PackageState(String packageName, int uid) { - mUid = uid; - mPackageName = packageName; - } - } - - public static final class ProcessDataCollection { - final int[] screenStates; - final int[] memStates; - final int[] procStates; - - public long totalTime; - public long numPss; - public long minPss; - public long avgPss; - public long maxPss; - public long minUss; - public long avgUss; - public long maxUss; - - public ProcessDataCollection(int[] _screenStates, int[] _memStates, int[] _procStates) { - screenStates = _screenStates; - memStates = _memStates; - procStates = _procStates; - } - - void print(PrintWriter pw, long overallTime, boolean full) { - if (totalTime > overallTime) { - pw.print("*"); - } - printPercent(pw, (double) totalTime / (double) overallTime); - if (numPss > 0) { - pw.print(" ("); - DebugUtils.printSizeValue(pw, minPss * 1024); - pw.print("-"); - DebugUtils.printSizeValue(pw, avgPss * 1024); - pw.print("-"); - DebugUtils.printSizeValue(pw, maxPss * 1024); - pw.print("/"); - DebugUtils.printSizeValue(pw, minUss * 1024); - pw.print("-"); - DebugUtils.printSizeValue(pw, avgUss * 1024); - pw.print("-"); - DebugUtils.printSizeValue(pw, maxUss * 1024); - if (full) { - pw.print(" over "); - pw.print(numPss); - } - pw.print(")"); - } - } - } - - public static class TotalMemoryUseCollection { - final int[] screenStates; - final int[] memStates; - - public TotalMemoryUseCollection(int[] _screenStates, int[] _memStates) { - screenStates = _screenStates; - memStates = _memStates; - } - - public long totalTime; - public long[] processStatePss = new long[STATE_COUNT]; - public double[] processStateWeight = new double[STATE_COUNT]; - public long[] processStateTime = new long[STATE_COUNT]; - public int[] processStateSamples = new int[STATE_COUNT]; - public long[] sysMemUsage = new long[SYS_MEM_USAGE_COUNT]; - public double sysMemCachedWeight; - public double sysMemFreeWeight; - public double sysMemZRamWeight; - public double sysMemKernelWeight; - public double sysMemNativeWeight; - public int sysMemSamples; - } -} diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 5726de1e8c3e..0e02ed6f126d 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -393,12 +393,7 @@ public class ResolverActivity extends Activity { 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/app/procstats/DumpUtils.java b/core/java/com/android/internal/app/procstats/DumpUtils.java new file mode 100644 index 000000000000..ebedc89cab1e --- /dev/null +++ b/core/java/com/android/internal/app/procstats/DumpUtils.java @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2013 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.internal.app.procstats; + +import android.os.Parcel; +import android.os.Parcelable; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.os.UserHandle; +import android.text.format.DateFormat; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.DebugUtils; +import android.util.Log; +import android.util.Slog; +import android.util.SparseArray; +import android.util.TimeUtils; + +import static com.android.internal.app.procstats.ProcessStats.*; + +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.Objects; + +/** + * Utilities for dumping. + */ +public final class DumpUtils { + public static final String[] STATE_NAMES = new String[] { + "Persist", "Top ", "ImpFg ", "ImpBg ", + "Backup ", "HeavyWt", "Service", "ServRst", + "Receivr", "Home ", + "LastAct", "CchAct ", "CchCAct", "CchEmty" + }; + + public static final String[] ADJ_SCREEN_NAMES_CSV = new String[] { + "off", "on" + }; + + public static final String[] ADJ_MEM_NAMES_CSV = new String[] { + "norm", "mod", "low", "crit" + }; + + public static final String[] STATE_NAMES_CSV = new String[] { + "pers", "top", "impfg", "impbg", "backup", "heavy", + "service", "service-rs", "receiver", "home", "lastact", + "cch-activity", "cch-aclient", "cch-empty" + }; + + static final String[] ADJ_SCREEN_TAGS = new String[] { + "0", "1" + }; + + static final String[] ADJ_MEM_TAGS = new String[] { + "n", "m", "l", "c" + }; + + static final String[] STATE_TAGS = new String[] { + "p", "t", "f", "b", "u", "w", + "s", "x", "r", "h", "l", "a", "c", "e" + }; + + static final String CSV_SEP = "\t"; + + /** + * No instantiate + */ + private DumpUtils() { + } + + public static void printScreenLabel(PrintWriter pw, int offset) { + switch (offset) { + case ADJ_NOTHING: + pw.print(" "); + break; + case ADJ_SCREEN_OFF: + pw.print("SOff/"); + break; + case ADJ_SCREEN_ON: + pw.print("SOn /"); + break; + default: + pw.print("????/"); + break; + } + } + + public static void printScreenLabelCsv(PrintWriter pw, int offset) { + switch (offset) { + case ADJ_NOTHING: + break; + case ADJ_SCREEN_OFF: + pw.print(ADJ_SCREEN_NAMES_CSV[0]); + break; + case ADJ_SCREEN_ON: + pw.print(ADJ_SCREEN_NAMES_CSV[1]); + break; + default: + pw.print("???"); + break; + } + } + + public static void printMemLabel(PrintWriter pw, int offset, char sep) { + switch (offset) { + case ADJ_NOTHING: + pw.print(" "); + if (sep != 0) pw.print(' '); + break; + case ADJ_MEM_FACTOR_NORMAL: + pw.print("Norm"); + if (sep != 0) pw.print(sep); + break; + case ADJ_MEM_FACTOR_MODERATE: + pw.print("Mod "); + if (sep != 0) pw.print(sep); + break; + case ADJ_MEM_FACTOR_LOW: + pw.print("Low "); + if (sep != 0) pw.print(sep); + break; + case ADJ_MEM_FACTOR_CRITICAL: + pw.print("Crit"); + if (sep != 0) pw.print(sep); + break; + default: + pw.print("????"); + if (sep != 0) pw.print(sep); + break; + } + } + + public static void printMemLabelCsv(PrintWriter pw, int offset) { + if (offset >= ADJ_MEM_FACTOR_NORMAL) { + if (offset <= ADJ_MEM_FACTOR_CRITICAL) { + pw.print(ADJ_MEM_NAMES_CSV[offset]); + } else { + pw.print("???"); + } + } + } + + public static void printPercent(PrintWriter pw, double fraction) { + fraction *= 100; + if (fraction < 1) { + pw.print(String.format("%.2f", fraction)); + } else if (fraction < 10) { + pw.print(String.format("%.1f", fraction)); + } else { + pw.print(String.format("%.0f", fraction)); + } + pw.print("%"); + } + + public static void printProcStateTag(PrintWriter pw, int state) { + state = printArrayEntry(pw, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD*STATE_COUNT); + state = printArrayEntry(pw, ADJ_MEM_TAGS, state, STATE_COUNT); + printArrayEntry(pw, STATE_TAGS, state, 1); + } + + public static void printAdjTag(PrintWriter pw, int state) { + state = printArrayEntry(pw, ADJ_SCREEN_TAGS, state, ADJ_SCREEN_MOD); + printArrayEntry(pw, ADJ_MEM_TAGS, state, 1); + } + + public static void printProcStateTagAndValue(PrintWriter pw, int state, long value) { + pw.print(','); + printProcStateTag(pw, state); + pw.print(':'); + pw.print(value); + } + + public static void printAdjTagAndValue(PrintWriter pw, int state, long value) { + pw.print(','); + printAdjTag(pw, state); + pw.print(':'); + pw.print(value); + } + + public static long dumpSingleTime(PrintWriter pw, String prefix, long[] durations, + int curState, long curStartTime, long now) { + long totalTime = 0; + int printedScreen = -1; + for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) { + int printedMem = -1; + for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) { + int state = imem+iscreen; + long time = durations[state]; + String running = ""; + if (curState == state) { + time += now - curStartTime; + if (pw != null) { + running = " (running)"; + } + } + if (time != 0) { + if (pw != null) { + pw.print(prefix); + printScreenLabel(pw, printedScreen != iscreen + ? iscreen : STATE_NOTHING); + printedScreen = iscreen; + printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING, (char)0); + printedMem = imem; + pw.print(": "); + TimeUtils.formatDuration(time, pw); pw.println(running); + } + totalTime += time; + } + } + } + if (totalTime != 0 && pw != null) { + pw.print(prefix); + pw.print(" TOTAL: "); + TimeUtils.formatDuration(totalTime, pw); + pw.println(); + } + return totalTime; + } + + public static void dumpAdjTimesCheckin(PrintWriter pw, String sep, long[] durations, + int curState, long curStartTime, long now) { + for (int iscreen=0; iscreen<ADJ_COUNT; iscreen+=ADJ_SCREEN_MOD) { + for (int imem=0; imem<ADJ_MEM_FACTOR_COUNT; imem++) { + int state = imem+iscreen; + long time = durations[state]; + if (curState == state) { + time += now - curStartTime; + } + if (time != 0) { + printAdjTagAndValue(pw, state, time); + } + } + } + } + + private static void dumpStateHeadersCsv(PrintWriter pw, String sep, int[] screenStates, + int[] memStates, int[] procStates) { + final int NS = screenStates != null ? screenStates.length : 1; + final int NM = memStates != null ? memStates.length : 1; + final int NP = procStates != null ? procStates.length : 1; + for (int is=0; is<NS; is++) { + for (int im=0; im<NM; im++) { + for (int ip=0; ip<NP; ip++) { + pw.print(sep); + boolean printed = false; + if (screenStates != null && screenStates.length > 1) { + printScreenLabelCsv(pw, screenStates[is]); + printed = true; + } + if (memStates != null && memStates.length > 1) { + if (printed) { + pw.print("-"); + } + printMemLabelCsv(pw, memStates[im]); + printed = true; + } + if (procStates != null && procStates.length > 1) { + if (printed) { + pw.print("-"); + } + pw.print(STATE_NAMES_CSV[procStates[ip]]); + } + } + } + } + } + + /* + * Doesn't seem to be used. + * + public static void dumpProcessList(PrintWriter pw, String prefix, ArrayList<ProcessState> procs, + int[] screenStates, int[] memStates, int[] procStates, long now) { + String innerPrefix = prefix + " "; + for (int i=procs.size()-1; i>=0; i--) { + ProcessState proc = procs.get(i); + pw.print(prefix); + pw.print(proc.mName); + pw.print(" / "); + UserHandle.formatUid(pw, proc.mUid); + pw.print(" ("); + pw.print(proc.durations.getKeyCount()); + pw.print(" entries)"); + pw.println(":"); + proc.dumpProcessState(pw, innerPrefix, screenStates, memStates, procStates, now); + if (proc.pssTable.getKeyCount() > 0) { + proc.dumpPss(pw, innerPrefix, screenStates, memStates, procStates); + } + } + } + */ + + public static void dumpProcessSummaryLocked(PrintWriter pw, String prefix, + ArrayList<ProcessState> procs, int[] screenStates, int[] memStates, int[] procStates, + long now, long totalTime) { + for (int i=procs.size()-1; i>=0; i--) { + final ProcessState proc = procs.get(i); + proc.dumpSummary(pw, prefix, screenStates, memStates, procStates, now, totalTime); + } + } + + public static void dumpProcessListCsv(PrintWriter pw, ArrayList<ProcessState> procs, + boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates, + boolean sepProcStates, int[] procStates, long now) { + pw.print("process"); + pw.print(CSV_SEP); + pw.print("uid"); + pw.print(CSV_SEP); + pw.print("vers"); + dumpStateHeadersCsv(pw, CSV_SEP, sepScreenStates ? screenStates : null, + sepMemStates ? memStates : null, + sepProcStates ? procStates : null); + pw.println(); + for (int i=procs.size()-1; i>=0; i--) { + ProcessState proc = procs.get(i); + pw.print(proc.getName()); + pw.print(CSV_SEP); + UserHandle.formatUid(pw, proc.getUid()); + pw.print(CSV_SEP); + pw.print(proc.getVersion()); + proc.dumpCsv(pw, sepScreenStates, screenStates, sepMemStates, + memStates, sepProcStates, procStates, now); + pw.println(); + } + } + + public static int printArrayEntry(PrintWriter pw, String[] array, int value, int mod) { + int index = value/mod; + if (index >= 0 && index < array.length) { + pw.print(array[index]); + } else { + pw.print('?'); + } + return value - index*mod; + } + + public static String collapseString(String pkgName, String itemName) { + if (itemName.startsWith(pkgName)) { + final int ITEMLEN = itemName.length(); + final int PKGLEN = pkgName.length(); + if (ITEMLEN == PKGLEN) { + return ""; + } else if (ITEMLEN >= PKGLEN) { + if (itemName.charAt(PKGLEN) == '.') { + return itemName.substring(PKGLEN); + } + } + } + return itemName; + } +} diff --git a/core/java/com/android/internal/app/procstats/DurationsTable.java b/core/java/com/android/internal/app/procstats/DurationsTable.java new file mode 100644 index 000000000000..b711ca1fa6cc --- /dev/null +++ b/core/java/com/android/internal/app/procstats/DurationsTable.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2013 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.internal.app.procstats; + +/** + * Sparse mapping table to store durations of processes, etc running in different + * states. + */ +public class DurationsTable extends SparseMappingTable.Table { + public DurationsTable(SparseMappingTable tableData) { + super(tableData); + } + + /** + * Add all of the durations from the other table into this one. + * Resultant durations will be the sum of what is currently in the table + * and the new value. + */ + public void addDurations(DurationsTable from) { + final int N = from.getKeyCount(); + for (int i=0; i<N; i++) { + final int key = from.getKeyAt(i); + this.addDuration(SparseMappingTable.getIdFromKey(key), from.getValue(key)); + } + } + + /** + * Add the value into the value stored for the state. + * + * Resultant duration will be the sum of what is currently in the table + * and the new value. + */ + public void addDuration(int state, long value) { + final int key = getOrAddKey((byte)state, 1); + setValue(key, getValue(key) + value); + } + + /* + public long getDuration(int state, long now) { + final int key = getKey((byte)state); + if (key != SparseMappingTable.INVALID_KEY) { + return getValue(key); + } else { + return 0; + } + } + */ +} + + diff --git a/core/java/com/android/internal/app/IProcessStats.aidl b/core/java/com/android/internal/app/procstats/IProcessStats.aidl index 6fadf2f41746..44867c7719cd 100644 --- a/core/java/com/android/internal/app/IProcessStats.aidl +++ b/core/java/com/android/internal/app/procstats/IProcessStats.aidl @@ -14,11 +14,11 @@ * limitations under the License. */ -package com.android.internal.app; +package com.android.internal.app.procstats; import android.content.ComponentName; import android.os.ParcelFileDescriptor; -import com.android.internal.app.ProcessStats; +import com.android.internal.app.procstats.ProcessStats; interface IProcessStats { byte[] getCurrentStats(out List<ParcelFileDescriptor> historic); diff --git a/core/java/com/android/internal/app/procstats/ProcessState.java b/core/java/com/android/internal/app/procstats/ProcessState.java new file mode 100644 index 000000000000..80d60706e0ea --- /dev/null +++ b/core/java/com/android/internal/app/procstats/ProcessState.java @@ -0,0 +1,1188 @@ +/* + * Copyright (C) 2013 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.internal.app.procstats; + +import android.os.Parcel; +import android.os.Parcelable; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.os.UserHandle; +import android.text.format.DateFormat; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.DebugUtils; +import android.util.Log; +import android.util.Slog; +import android.util.SparseArray; +import android.util.TimeUtils; + +import com.android.internal.app.procstats.ProcessStats; +import com.android.internal.app.procstats.ProcessStats.PackageState; +import com.android.internal.app.procstats.ProcessStats.ProcessStateHolder; +import com.android.internal.app.procstats.ProcessStats.TotalMemoryUseCollection; +import static com.android.internal.app.procstats.ProcessStats.PSS_SAMPLE_COUNT; +import static com.android.internal.app.procstats.ProcessStats.PSS_MINIMUM; +import static com.android.internal.app.procstats.ProcessStats.PSS_AVERAGE; +import static com.android.internal.app.procstats.ProcessStats.PSS_MAXIMUM; +import static com.android.internal.app.procstats.ProcessStats.PSS_USS_MINIMUM; +import static com.android.internal.app.procstats.ProcessStats.PSS_USS_AVERAGE; +import static com.android.internal.app.procstats.ProcessStats.PSS_USS_MAXIMUM; +import static com.android.internal.app.procstats.ProcessStats.PSS_COUNT; +import static com.android.internal.app.procstats.ProcessStats.STATE_NOTHING; +import static com.android.internal.app.procstats.ProcessStats.STATE_PERSISTENT; +import static com.android.internal.app.procstats.ProcessStats.STATE_TOP; +import static com.android.internal.app.procstats.ProcessStats.STATE_IMPORTANT_FOREGROUND; +import static com.android.internal.app.procstats.ProcessStats.STATE_IMPORTANT_BACKGROUND; +import static com.android.internal.app.procstats.ProcessStats.STATE_BACKUP; +import static com.android.internal.app.procstats.ProcessStats.STATE_HEAVY_WEIGHT; +import static com.android.internal.app.procstats.ProcessStats.STATE_SERVICE; +import static com.android.internal.app.procstats.ProcessStats.STATE_SERVICE_RESTARTING; +import static com.android.internal.app.procstats.ProcessStats.STATE_RECEIVER; +import static com.android.internal.app.procstats.ProcessStats.STATE_HOME; +import static com.android.internal.app.procstats.ProcessStats.STATE_LAST_ACTIVITY; +import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY; +import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_ACTIVITY_CLIENT; +import static com.android.internal.app.procstats.ProcessStats.STATE_CACHED_EMPTY; +import static com.android.internal.app.procstats.ProcessStats.STATE_COUNT; + +import dalvik.system.VMRuntime; +import libcore.util.EmptyArray; + +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.Objects; + +public final class ProcessState { + private static final String TAG = "ProcessStats"; + private static final boolean DEBUG = false; + private static final boolean DEBUG_PARCEL = false; + + // Map from process states to the states we track. + private static final int[] PROCESS_STATE_TO_STATE = new int[] { + STATE_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT + STATE_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT_UI + STATE_TOP, // ActivityManager.PROCESS_STATE_TOP + STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE + STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE + STATE_TOP, // ActivityManager.PROCESS_STATE_TOP_SLEEPING + STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND + STATE_IMPORTANT_BACKGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND + STATE_BACKUP, // ActivityManager.PROCESS_STATE_BACKUP + STATE_HEAVY_WEIGHT, // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT + STATE_SERVICE, // ActivityManager.PROCESS_STATE_SERVICE + STATE_RECEIVER, // ActivityManager.PROCESS_STATE_RECEIVER + STATE_HOME, // ActivityManager.PROCESS_STATE_HOME + STATE_LAST_ACTIVITY, // ActivityManager.PROCESS_STATE_LAST_ACTIVITY + STATE_CACHED_ACTIVITY, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY + STATE_CACHED_ACTIVITY_CLIENT, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT + STATE_CACHED_EMPTY, // ActivityManager.PROCESS_STATE_CACHED_EMPTY + }; + + public static final Comparator<ProcessState> COMPARATOR = new Comparator<ProcessState>() { + @Override + public int compare(ProcessState lhs, ProcessState rhs) { + if (lhs.mTmpTotalTime < rhs.mTmpTotalTime) { + return -1; + } else if (lhs.mTmpTotalTime > rhs.mTmpTotalTime) { + return 1; + } + return 0; + } + }; + + static class PssAggr { + long pss = 0; + long samples = 0; + + void add(long newPss, long newSamples) { + pss = (long)( (pss*(double)samples) + (newPss*(double)newSamples) ) + / (samples+newSamples); + samples += newSamples; + } + } + + // Used by reset to count rather than storing extra maps. Be careful. + public int tmpNumInUse; + public ProcessState tmpFoundSubProc; + + private final ProcessStats mStats; + private final String mName; + private final String mPackage; + private final int mUid; + private final int mVersion; + private final DurationsTable mDurations; + private final PssTable mPssTable; + + private ProcessState mCommonProcess; + private int mCurState = STATE_NOTHING; + private long mStartTime; + + private int mLastPssState = STATE_NOTHING; + private long mLastPssTime; + + private boolean mActive; + private int mNumActiveServices; + private int mNumStartedServices; + + private int mNumExcessiveWake; + private int mNumExcessiveCpu; + + private int mNumCachedKill; + private long mMinCachedKillPss; + private long mAvgCachedKillPss; + private long mMaxCachedKillPss; + + private boolean mMultiPackage; + private boolean mDead; + + // Set in computeProcessTimeLocked and used by COMPARATOR to sort. Be careful. + private long mTmpTotalTime; + + /** + * Create a new top-level process state, for the initial case where there is only + * a single package running in a process. The initial state is not running. + */ + public ProcessState(ProcessStats processStats, String pkg, int uid, int vers, String name) { + mStats = processStats; + mName = name; + mCommonProcess = this; + mPackage = pkg; + mUid = uid; + mVersion = vers; + mDurations = new DurationsTable(processStats.mTableData); + mPssTable = new PssTable(processStats.mTableData); + } + + /** + * Create a new per-package process state for an existing top-level process + * state. The current running state of the top-level process is also copied, + * marked as started running at 'now'. + */ + public ProcessState(ProcessState commonProcess, String pkg, int uid, int vers, String name, + long now) { + mStats = commonProcess.mStats; + mName = name; + mCommonProcess = commonProcess; + mPackage = pkg; + mUid = uid; + mVersion = vers; + mCurState = commonProcess.mCurState; + mStartTime = now; + mDurations = new DurationsTable(commonProcess.mStats.mTableData); + mPssTable = new PssTable(commonProcess.mStats.mTableData); + } + + public ProcessState clone(long now) { + ProcessState pnew = new ProcessState(this, mPackage, mUid, mVersion, mName, now); + pnew.mDurations.addDurations(mDurations); + pnew.mPssTable.copyFrom(mPssTable, PSS_COUNT); + pnew.mNumExcessiveWake = mNumExcessiveWake; + pnew.mNumExcessiveCpu = mNumExcessiveCpu; + pnew.mNumCachedKill = mNumCachedKill; + pnew.mMinCachedKillPss = mMinCachedKillPss; + pnew.mAvgCachedKillPss = mAvgCachedKillPss; + pnew.mMaxCachedKillPss = mMaxCachedKillPss; + pnew.mActive = mActive; + pnew.mNumActiveServices = mNumActiveServices; + pnew.mNumStartedServices = mNumStartedServices; + return pnew; + } + + public String getName() { + return mName; + } + + public ProcessState getCommonProcess() { + return mCommonProcess; + } + + /** + * Say that we are not part of a shared process, so mCommonProcess = this. + */ + public void makeStandalone() { + mCommonProcess = this; + } + + public String getPackage() { + return mPackage; + } + + public int getUid() { + return mUid; + } + + public int getVersion() { + return mVersion; + } + + public boolean isMultiPackage() { + return mMultiPackage; + } + + public void setMultiPackage(boolean val) { + mMultiPackage = val; + } + + public int getDurationsBucketCount() { + return mDurations.getKeyCount(); + } + + public void add(ProcessState other) { + mDurations.addDurations(other.mDurations); + mPssTable.mergeStats(other.mPssTable); + mNumExcessiveWake += other.mNumExcessiveWake; + mNumExcessiveCpu += other.mNumExcessiveCpu; + if (other.mNumCachedKill > 0) { + addCachedKill(other.mNumCachedKill, other.mMinCachedKillPss, + other.mAvgCachedKillPss, other.mMaxCachedKillPss); + } + } + + public void resetSafely(long now) { + mDurations.resetTable(); + mPssTable.resetTable(); + mStartTime = now; + mLastPssState = STATE_NOTHING; + mLastPssTime = 0; + mNumExcessiveWake = 0; + mNumExcessiveCpu = 0; + mNumCachedKill = 0; + mMinCachedKillPss = mAvgCachedKillPss = mMaxCachedKillPss = 0; + } + + public void makeDead() { + mDead = true; + } + + private void ensureNotDead() { + if (!mDead) { + return; + } + Slog.wtfStack(TAG, "ProcessState dead: name=" + mName + + " pkg=" + mPackage + " uid=" + mUid + " common.name=" + mCommonProcess.mName); + } + + public void writeToParcel(Parcel out, long now) { + out.writeInt(mMultiPackage ? 1 : 0); + mDurations.writeToParcel(out); + mPssTable.writeToParcel(out); + out.writeInt(mNumExcessiveWake); + out.writeInt(mNumExcessiveCpu); + out.writeInt(mNumCachedKill); + if (mNumCachedKill > 0) { + out.writeLong(mMinCachedKillPss); + out.writeLong(mAvgCachedKillPss); + out.writeLong(mMaxCachedKillPss); + } + } + + public boolean readFromParcel(Parcel in, boolean fully) { + boolean multiPackage = in.readInt() != 0; + if (fully) { + mMultiPackage = multiPackage; + } + if (DEBUG_PARCEL) Slog.d(TAG, "Reading durations table..."); + if (!mDurations.readFromParcel(in)) { + return false; + } + if (DEBUG_PARCEL) Slog.d(TAG, "Reading pss table..."); + if (!mPssTable.readFromParcel(in)) { + return false; + } + mNumExcessiveWake = in.readInt(); + mNumExcessiveCpu = in.readInt(); + mNumCachedKill = in.readInt(); + if (mNumCachedKill > 0) { + mMinCachedKillPss = in.readLong(); + mAvgCachedKillPss = in.readLong(); + mMaxCachedKillPss = in.readLong(); + } else { + mMinCachedKillPss = mAvgCachedKillPss = mMaxCachedKillPss = 0; + } + return true; + } + + public void makeActive() { + ensureNotDead(); + mActive = true; + } + + public void makeInactive() { + mActive = false; + } + + public boolean isInUse() { + return mActive || mNumActiveServices > 0 || mNumStartedServices > 0 + || mCurState != STATE_NOTHING; + } + + public boolean isActive() { + return mActive; + } + + public boolean hasAnyData() { + return !(mDurations.getKeyCount() == 0 + && mCurState == STATE_NOTHING + && mPssTable.getKeyCount() == 0); + } + + /** + * Update the current state of the given list of processes. + * + * @param state Current ActivityManager.PROCESS_STATE_* + * @param memFactor Current mem factor constant. + * @param now Current time. + * @param pkgList Processes to update. + */ + public void setState(int state, int memFactor, long now, + ArrayMap<String, ProcessStateHolder> pkgList) { + if (state < 0) { + state = mNumStartedServices > 0 + ? (STATE_SERVICE_RESTARTING+(memFactor*STATE_COUNT)) : STATE_NOTHING; + } else { + state = PROCESS_STATE_TO_STATE[state] + (memFactor*STATE_COUNT); + } + + // First update the common process. + mCommonProcess.setState(state, now); + + // If the common process is not multi-package, there is nothing else to do. + if (!mCommonProcess.mMultiPackage) { + return; + } + + if (pkgList != null) { + for (int ip=pkgList.size()-1; ip>=0; ip--) { + pullFixedProc(pkgList, ip).setState(state, now); + } + } + } + + public void setState(int state, long now) { + ensureNotDead(); + if (mCurState != state) { + //Slog.i(TAG, "Setting state in " + mName + "/" + mPackage + ": " + state); + commitStateTime(now); + mCurState = state; + } + } + + public void commitStateTime(long now) { + if (mCurState != STATE_NOTHING) { + long dur = now - mStartTime; + if (dur > 0) { + mDurations.addDuration(mCurState, dur); + } + } + mStartTime = now; + } + + public void incActiveServices(String serviceName) { + if (DEBUG && "".equals(mName)) { + RuntimeException here = new RuntimeException("here"); + here.fillInStackTrace(); + Slog.d(TAG, "incActiveServices: " + this + " service=" + serviceName + + " to " + (mNumActiveServices+1), here); + } + if (mCommonProcess != this) { + mCommonProcess.incActiveServices(serviceName); + } + mNumActiveServices++; + } + + public void decActiveServices(String serviceName) { + if (DEBUG && "".equals(mName)) { + RuntimeException here = new RuntimeException("here"); + here.fillInStackTrace(); + Slog.d(TAG, "decActiveServices: " + this + " service=" + serviceName + + " to " + (mNumActiveServices-1), here); + } + if (mCommonProcess != this) { + mCommonProcess.decActiveServices(serviceName); + } + mNumActiveServices--; + if (mNumActiveServices < 0) { + Slog.wtfStack(TAG, "Proc active services underrun: pkg=" + mPackage + + " uid=" + mUid + " proc=" + mName + " service=" + serviceName); + mNumActiveServices = 0; + } + } + + public void incStartedServices(int memFactor, long now, String serviceName) { + if (false) { + RuntimeException here = new RuntimeException("here"); + here.fillInStackTrace(); + Slog.d(TAG, "incStartedServices: " + this + " service=" + serviceName + + " to " + (mNumStartedServices+1), here); + } + if (mCommonProcess != this) { + mCommonProcess.incStartedServices(memFactor, now, serviceName); + } + mNumStartedServices++; + if (mNumStartedServices == 1 && mCurState == STATE_NOTHING) { + setState(STATE_SERVICE_RESTARTING + (memFactor*STATE_COUNT), now); + } + } + + public void decStartedServices(int memFactor, long now, String serviceName) { + if (false) { + RuntimeException here = new RuntimeException("here"); + here.fillInStackTrace(); + Slog.d(TAG, "decActiveServices: " + this + " service=" + serviceName + + " to " + (mNumStartedServices-1), here); + } + if (mCommonProcess != this) { + mCommonProcess.decStartedServices(memFactor, now, serviceName); + } + mNumStartedServices--; + if (mNumStartedServices == 0 && (mCurState%STATE_COUNT) == STATE_SERVICE_RESTARTING) { + setState(STATE_NOTHING, now); + } else if (mNumStartedServices < 0) { + Slog.wtfStack(TAG, "Proc started services underrun: pkg=" + + mPackage + " uid=" + mUid + " name=" + mName); + mNumStartedServices = 0; + } + } + + public void addPss(long pss, long uss, boolean always, + ArrayMap<String, ProcessStateHolder> pkgList) { + ensureNotDead(); + if (!always) { + if (mLastPssState == mCurState && SystemClock.uptimeMillis() + < (mLastPssTime+(30*1000))) { + return; + } + } + mLastPssState = mCurState; + mLastPssTime = SystemClock.uptimeMillis(); + if (mCurState != STATE_NOTHING) { + // First update the common process. + mCommonProcess.mPssTable.mergeStats(mCurState, 1, pss, pss, pss, uss, uss, uss); + + // If the common process is not multi-package, there is nothing else to do. + if (!mCommonProcess.mMultiPackage) { + return; + } + + if (pkgList != null) { + for (int ip=pkgList.size()-1; ip>=0; ip--) { + pullFixedProc(pkgList, ip).mPssTable.mergeStats(mCurState, 1, + pss, pss, pss, uss, uss, uss); + } + } + } + } + + public void reportExcessiveWake(ArrayMap<String, ProcessStateHolder> pkgList) { + ensureNotDead(); + mCommonProcess.mNumExcessiveWake++; + if (!mCommonProcess.mMultiPackage) { + return; + } + + for (int ip=pkgList.size()-1; ip>=0; ip--) { + pullFixedProc(pkgList, ip).mNumExcessiveWake++; + } + } + + public void reportExcessiveCpu(ArrayMap<String, ProcessStateHolder> pkgList) { + ensureNotDead(); + mCommonProcess.mNumExcessiveCpu++; + if (!mCommonProcess.mMultiPackage) { + return; + } + + for (int ip=pkgList.size()-1; ip>=0; ip--) { + pullFixedProc(pkgList, ip).mNumExcessiveCpu++; + } + } + + private void addCachedKill(int num, long minPss, long avgPss, long maxPss) { + if (mNumCachedKill <= 0) { + mNumCachedKill = num; + mMinCachedKillPss = minPss; + mAvgCachedKillPss = avgPss; + mMaxCachedKillPss = maxPss; + } else { + if (minPss < mMinCachedKillPss) { + mMinCachedKillPss = minPss; + } + if (maxPss > mMaxCachedKillPss) { + mMaxCachedKillPss = maxPss; + } + mAvgCachedKillPss = (long)( ((mAvgCachedKillPss*(double)mNumCachedKill) + avgPss) + / (mNumCachedKill+num) ); + mNumCachedKill += num; + } + } + + public void reportCachedKill(ArrayMap<String, ProcessStateHolder> pkgList, long pss) { + ensureNotDead(); + mCommonProcess.addCachedKill(1, pss, pss, pss); + if (!mCommonProcess.mMultiPackage) { + return; + } + + for (int ip=pkgList.size()-1; ip>=0; ip--) { + pullFixedProc(pkgList, ip).addCachedKill(1, pss, pss, pss); + } + } + + public ProcessState pullFixedProc(String pkgName) { + if (mMultiPackage) { + // The array map is still pointing to a common process state + // that is now shared across packages. Update it to point to + // the new per-package state. + SparseArray<PackageState> vpkg = mStats.mPackages.get(pkgName, mUid); + if (vpkg == null) { + throw new IllegalStateException("Didn't find package " + pkgName + + " / " + mUid); + } + PackageState pkg = vpkg.get(mVersion); + if (pkg == null) { + throw new IllegalStateException("Didn't find package " + pkgName + + " / " + mUid + " vers " + mVersion); + } + ProcessState proc = pkg.mProcesses.get(mName); + if (proc == null) { + throw new IllegalStateException("Didn't create per-package process " + + mName + " in pkg " + pkgName + " / " + mUid + " vers " + mVersion); + } + return proc; + } + return this; + } + + private ProcessState pullFixedProc(ArrayMap<String, ProcessStateHolder> pkgList, + int index) { + ProcessStateHolder holder = pkgList.valueAt(index); + ProcessState proc = holder.state; + if (mDead && proc.mCommonProcess != proc) { + // Somehow we are contining to use a process state that is dead, because + // it was not being told it was active during the last commit. We can recover + // from this by generating a fresh new state, but this is bad because we + // are losing whatever data we had in the old process state. + Log.wtf(TAG, "Pulling dead proc: name=" + mName + " pkg=" + mPackage + + " uid=" + mUid + " common.name=" + mCommonProcess.mName); + proc = mStats.getProcessStateLocked(proc.mPackage, proc.mUid, proc.mVersion, + proc.mName); + } + if (proc.mMultiPackage) { + // The array map is still pointing to a common process state + // that is now shared across packages. Update it to point to + // the new per-package state. + SparseArray<PackageState> vpkg = mStats.mPackages.get(pkgList.keyAt(index), + proc.mUid); + if (vpkg == null) { + throw new IllegalStateException("No existing package " + + pkgList.keyAt(index) + "/" + proc.mUid + + " for multi-proc " + proc.mName); + } + PackageState pkg = vpkg.get(proc.mVersion); + if (pkg == null) { + throw new IllegalStateException("No existing package " + + pkgList.keyAt(index) + "/" + proc.mUid + + " for multi-proc " + proc.mName + " version " + proc.mVersion); + } + String savedName = proc.mName; + proc = pkg.mProcesses.get(proc.mName); + if (proc == null) { + throw new IllegalStateException("Didn't create per-package process " + + savedName + " in pkg " + pkg.mPackageName + "/" + pkg.mUid); + } + holder.state = proc; + } + return proc; + } + + public long getDuration(int state, long now) { + long time = mDurations.getValueForId((byte)state); + if (mCurState == state) { + time += now - mStartTime; + } + return time; + } + + public long getPssSampleCount(int state) { + return mPssTable.getValueForId((byte)state, PSS_SAMPLE_COUNT); + } + + public long getPssMinimum(int state) { + return mPssTable.getValueForId((byte)state, PSS_MINIMUM); + } + + public long getPssAverage(int state) { + return mPssTable.getValueForId((byte)state, PSS_AVERAGE); + } + + public long getPssMaximum(int state) { + return mPssTable.getValueForId((byte)state, PSS_MAXIMUM); + } + + public long getPssUssMinimum(int state) { + return mPssTable.getValueForId((byte)state, PSS_USS_MINIMUM); + } + + public long getPssUssAverage(int state) { + return mPssTable.getValueForId((byte)state, PSS_USS_AVERAGE); + } + + public long getPssUssMaximum(int state) { + return mPssTable.getValueForId((byte)state, PSS_USS_MAXIMUM); + } + + /** + * Sums up the PSS data and adds it to 'data'. + * + * @param data The aggregate data is added here. + * @param now SystemClock.uptimeMillis() + */ + public void aggregatePss(TotalMemoryUseCollection data, long now) { + final PssAggr fgPss = new PssAggr(); + final PssAggr bgPss = new PssAggr(); + final PssAggr cachedPss = new PssAggr(); + boolean havePss = false; + for (int i=0; i<mDurations.getKeyCount(); i++) { + final int key = mDurations.getKeyAt(i); + int type = SparseMappingTable.getIdFromKey(key); + int procState = type % STATE_COUNT; + long samples = getPssSampleCount(type); + if (samples > 0) { + long avg = getPssAverage(type); + havePss = true; + if (procState <= STATE_IMPORTANT_FOREGROUND) { + fgPss.add(avg, samples); + } else if (procState <= STATE_RECEIVER) { + bgPss.add(avg, samples); + } else { + cachedPss.add(avg, samples); + } + } + } + if (!havePss) { + return; + } + boolean fgHasBg = false; + boolean fgHasCached = false; + boolean bgHasCached = false; + if (fgPss.samples < 3 && bgPss.samples > 0) { + fgHasBg = true; + fgPss.add(bgPss.pss, bgPss.samples); + } + if (fgPss.samples < 3 && cachedPss.samples > 0) { + fgHasCached = true; + fgPss.add(cachedPss.pss, cachedPss.samples); + } + if (bgPss.samples < 3 && cachedPss.samples > 0) { + bgHasCached = true; + bgPss.add(cachedPss.pss, cachedPss.samples); + } + if (bgPss.samples < 3 && !fgHasBg && fgPss.samples > 0) { + bgPss.add(fgPss.pss, fgPss.samples); + } + if (cachedPss.samples < 3 && !bgHasCached && bgPss.samples > 0) { + cachedPss.add(bgPss.pss, bgPss.samples); + } + if (cachedPss.samples < 3 && !fgHasCached && fgPss.samples > 0) { + cachedPss.add(fgPss.pss, fgPss.samples); + } + for (int i=0; i<mDurations.getKeyCount(); i++) { + final int key = mDurations.getKeyAt(i); + final int type = SparseMappingTable.getIdFromKey(key); + long time = mDurations.getValue(key); + if (mCurState == type) { + time += now - mStartTime; + } + final int procState = type % STATE_COUNT; + data.processStateTime[procState] += time; + long samples = getPssSampleCount(type); + long avg; + if (samples > 0) { + avg = getPssAverage(type); + } else if (procState <= STATE_IMPORTANT_FOREGROUND) { + samples = fgPss.samples; + avg = fgPss.pss; + } else if (procState <= STATE_RECEIVER) { + samples = bgPss.samples; + avg = bgPss.pss; + } else { + samples = cachedPss.samples; + avg = cachedPss.pss; + } + double newAvg = ( (data.processStatePss[procState] + * (double)data.processStateSamples[procState]) + + (avg*(double)samples) + ) / (data.processStateSamples[procState]+samples); + data.processStatePss[procState] = (long)newAvg; + data.processStateSamples[procState] += samples; + data.processStateWeight[procState] += avg * (double)time; + } + } + + public long computeProcessTimeLocked(int[] screenStates, int[] memStates, + int[] procStates, long now) { + long totalTime = 0; + for (int is=0; is<screenStates.length; is++) { + for (int im=0; im<memStates.length; im++) { + for (int ip=0; ip<procStates.length; ip++) { + int bucket = ((screenStates[is] + memStates[im]) * STATE_COUNT) + + procStates[ip]; + totalTime += getDuration(bucket, now); + } + } + } + mTmpTotalTime = totalTime; + return totalTime; + } + + public void dumpSummary(PrintWriter pw, String prefix, + int[] screenStates, int[] memStates, int[] procStates, + long now, long totalTime) { + pw.print(prefix); + pw.print("* "); + pw.print(mName); + pw.print(" / "); + UserHandle.formatUid(pw, mUid); + pw.print(" / v"); + pw.print(mVersion); + pw.println(":"); + dumpProcessSummaryDetails(pw, prefix, " TOTAL: ", screenStates, memStates, + procStates, now, totalTime, true); + dumpProcessSummaryDetails(pw, prefix, " Persistent: ", screenStates, memStates, + new int[] { STATE_PERSISTENT }, now, totalTime, true); + dumpProcessSummaryDetails(pw, prefix, " Top: ", screenStates, memStates, + new int[] {STATE_TOP}, now, totalTime, true); + dumpProcessSummaryDetails(pw, prefix, " Imp Fg: ", screenStates, memStates, + new int[] { STATE_IMPORTANT_FOREGROUND }, now, totalTime, true); + dumpProcessSummaryDetails(pw, prefix, " Imp Bg: ", screenStates, memStates, + new int[] {STATE_IMPORTANT_BACKGROUND}, now, totalTime, true); + dumpProcessSummaryDetails(pw, prefix, " Backup: ", screenStates, memStates, + new int[] {STATE_BACKUP}, now, totalTime, true); + dumpProcessSummaryDetails(pw, prefix, " Heavy Wgt: ", screenStates, memStates, + new int[] {STATE_HEAVY_WEIGHT}, now, totalTime, true); + dumpProcessSummaryDetails(pw, prefix, " Service: ", screenStates, memStates, + new int[] {STATE_SERVICE}, now, totalTime, true); + dumpProcessSummaryDetails(pw, prefix, " Service Rs: ", screenStates, memStates, + new int[] {STATE_SERVICE_RESTARTING}, now, totalTime, true); + dumpProcessSummaryDetails(pw, prefix, " Receiver: ", screenStates, memStates, + new int[] {STATE_RECEIVER}, now, totalTime, true); + dumpProcessSummaryDetails(pw, prefix, " (Home): ", screenStates, memStates, + new int[] {STATE_HOME}, now, totalTime, true); + dumpProcessSummaryDetails(pw, prefix, " (Last Act): ", screenStates, memStates, + new int[] {STATE_LAST_ACTIVITY}, now, totalTime, true); + dumpProcessSummaryDetails(pw, prefix, " (Cached): ", screenStates, memStates, + new int[] {STATE_CACHED_ACTIVITY, STATE_CACHED_ACTIVITY_CLIENT, + STATE_CACHED_EMPTY}, now, totalTime, true); + } + + public void dumpProcessState(PrintWriter pw, String prefix, + int[] screenStates, int[] memStates, int[] procStates, long now) { + long totalTime = 0; + int printedScreen = -1; + for (int is=0; is<screenStates.length; is++) { + int printedMem = -1; + for (int im=0; im<memStates.length; im++) { + for (int ip=0; ip<procStates.length; ip++) { + final int iscreen = screenStates[is]; + final int imem = memStates[im]; + final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip]; + long time = mDurations.getValueForId((byte)bucket); + String running = ""; + if (mCurState == bucket) { + running = " (running)"; + } + if (time != 0) { + pw.print(prefix); + if (screenStates.length > 1) { + DumpUtils.printScreenLabel(pw, printedScreen != iscreen + ? iscreen : STATE_NOTHING); + printedScreen = iscreen; + } + if (memStates.length > 1) { + DumpUtils.printMemLabel(pw, + printedMem != imem ? imem : STATE_NOTHING, '/'); + printedMem = imem; + } + pw.print(DumpUtils.STATE_NAMES[procStates[ip]]); pw.print(": "); + TimeUtils.formatDuration(time, pw); pw.println(running); + totalTime += time; + } + } + } + } + if (totalTime != 0) { + pw.print(prefix); + if (screenStates.length > 1) { + DumpUtils.printScreenLabel(pw, STATE_NOTHING); + } + if (memStates.length > 1) { + DumpUtils.printMemLabel(pw, STATE_NOTHING, '/'); + } + pw.print("TOTAL : "); + TimeUtils.formatDuration(totalTime, pw); + pw.println(); + } + } + + public void dumpPss(PrintWriter pw, String prefix, + int[] screenStates, int[] memStates, int[] procStates) { + boolean printedHeader = false; + int printedScreen = -1; + for (int is=0; is<screenStates.length; is++) { + int printedMem = -1; + for (int im=0; im<memStates.length; im++) { + for (int ip=0; ip<procStates.length; ip++) { + final int iscreen = screenStates[is]; + final int imem = memStates[im]; + final int bucket = ((iscreen + imem) * STATE_COUNT) + procStates[ip]; + long count = getPssSampleCount(bucket); + if (count > 0) { + if (!printedHeader) { + pw.print(prefix); + pw.print("PSS/USS ("); + pw.print(mPssTable.getKeyCount()); + pw.println(" entries):"); + printedHeader = true; + } + pw.print(prefix); + pw.print(" "); + if (screenStates.length > 1) { + DumpUtils.printScreenLabel(pw, + printedScreen != iscreen ? iscreen : STATE_NOTHING); + printedScreen = iscreen; + } + if (memStates.length > 1) { + DumpUtils.printMemLabel(pw, + printedMem != imem ? imem : STATE_NOTHING, '/'); + printedMem = imem; + } + pw.print(DumpUtils.STATE_NAMES[procStates[ip]]); pw.print(": "); + pw.print(count); + pw.print(" samples "); + DebugUtils.printSizeValue(pw, getPssMinimum(bucket) * 1024); + pw.print(" "); + DebugUtils.printSizeValue(pw, getPssAverage(bucket) * 1024); + pw.print(" "); + DebugUtils.printSizeValue(pw, getPssMaximum(bucket) * 1024); + pw.print(" / "); + DebugUtils.printSizeValue(pw, getPssUssMinimum(bucket) * 1024); + pw.print(" "); + DebugUtils.printSizeValue(pw, getPssUssAverage(bucket) * 1024); + pw.print(" "); + DebugUtils.printSizeValue(pw, getPssUssMaximum(bucket) * 1024); + pw.println(); + } + } + } + } + if (mNumExcessiveWake != 0) { + pw.print(prefix); pw.print("Killed for excessive wake locks: "); + pw.print(mNumExcessiveWake); pw.println(" times"); + } + if (mNumExcessiveCpu != 0) { + pw.print(prefix); pw.print("Killed for excessive CPU use: "); + pw.print(mNumExcessiveCpu); pw.println(" times"); + } + if (mNumCachedKill != 0) { + pw.print(prefix); pw.print("Killed from cached state: "); + pw.print(mNumCachedKill); pw.print(" times from pss "); + DebugUtils.printSizeValue(pw, mMinCachedKillPss * 1024); pw.print("-"); + DebugUtils.printSizeValue(pw, mAvgCachedKillPss * 1024); pw.print("-"); + DebugUtils.printSizeValue(pw, mMaxCachedKillPss * 1024); pw.println(); + } + } + + private void dumpProcessSummaryDetails(PrintWriter pw, String prefix, + String label, int[] screenStates, int[] memStates, int[] procStates, + long now, long totalTime, boolean full) { + ProcessStats.ProcessDataCollection totals = new ProcessStats.ProcessDataCollection( + screenStates, memStates, procStates); + computeProcessData(totals, now); + final double percentage = (double) totals.totalTime / (double) totalTime * 100; + // We don't print percentages < .01, so just drop those. + if (percentage >= 0.005 || totals.numPss != 0) { + if (prefix != null) { + pw.print(prefix); + } + if (label != null) { + pw.print(label); + } + totals.print(pw, totalTime, full); + if (prefix != null) { + pw.println(); + } + } + } + + public void dumpInternalLocked(PrintWriter pw, String prefix, boolean dumpAll) { + if (dumpAll) { + pw.print(prefix); pw.print("myID="); + pw.print(Integer.toHexString(System.identityHashCode(this))); + pw.print(" mCommonProcess="); + pw.print(Integer.toHexString(System.identityHashCode(mCommonProcess))); + pw.print(" mPackage="); pw.println(mPackage); + if (mMultiPackage) { + pw.print(prefix); pw.print("mMultiPackage="); pw.println(mMultiPackage); + } + if (this != mCommonProcess) { + pw.print(prefix); pw.print("Common Proc: "); pw.print(mCommonProcess.mName); + pw.print("/"); pw.print(mCommonProcess.mUid); + pw.print(" pkg="); pw.println(mCommonProcess.mPackage); + } + } + if (mActive) { + pw.print(prefix); pw.print("mActive="); pw.println(mActive); + } + if (mDead) { + pw.print(prefix); pw.print("mDead="); pw.println(mDead); + } + if (mNumActiveServices != 0 || mNumStartedServices != 0) { + pw.print(prefix); pw.print("mNumActiveServices="); pw.print(mNumActiveServices); + pw.print(" mNumStartedServices="); + pw.println(mNumStartedServices); + } + } + + public void computeProcessData(ProcessStats.ProcessDataCollection data, long now) { + data.totalTime = 0; + data.numPss = data.minPss = data.avgPss = data.maxPss = + data.minUss = data.avgUss = data.maxUss = 0; + for (int is=0; is<data.screenStates.length; is++) { + for (int im=0; im<data.memStates.length; im++) { + for (int ip=0; ip<data.procStates.length; ip++) { + int bucket = ((data.screenStates[is] + data.memStates[im]) * STATE_COUNT) + + data.procStates[ip]; + data.totalTime += getDuration(bucket, now); + long samples = getPssSampleCount(bucket); + if (samples > 0) { + long minPss = getPssMinimum(bucket); + long avgPss = getPssAverage(bucket); + long maxPss = getPssMaximum(bucket); + long minUss = getPssUssMinimum(bucket); + long avgUss = getPssUssAverage(bucket); + long maxUss = getPssUssMaximum(bucket); + if (data.numPss == 0) { + data.minPss = minPss; + data.avgPss = avgPss; + data.maxPss = maxPss; + data.minUss = minUss; + data.avgUss = avgUss; + data.maxUss = maxUss; + } else { + if (minPss < data.minPss) { + data.minPss = minPss; + } + data.avgPss = (long)( ((data.avgPss*(double)data.numPss) + + (avgPss*(double)samples)) / (data.numPss+samples) ); + if (maxPss > data.maxPss) { + data.maxPss = maxPss; + } + if (minUss < data.minUss) { + data.minUss = minUss; + } + data.avgUss = (long)( ((data.avgUss*(double)data.numPss) + + (avgUss*(double)samples)) / (data.numPss+samples) ); + if (maxUss > data.maxUss) { + data.maxUss = maxUss; + } + } + data.numPss += samples; + } + } + } + } + } + + public void dumpCsv(PrintWriter pw, + boolean sepScreenStates, int[] screenStates, boolean sepMemStates, + int[] memStates, boolean sepProcStates, int[] procStates, long now) { + final int NSS = sepScreenStates ? screenStates.length : 1; + final int NMS = sepMemStates ? memStates.length : 1; + final int NPS = sepProcStates ? procStates.length : 1; + for (int iss=0; iss<NSS; iss++) { + for (int ims=0; ims<NMS; ims++) { + for (int ips=0; ips<NPS; ips++) { + final int vsscreen = sepScreenStates ? screenStates[iss] : 0; + final int vsmem = sepMemStates ? memStates[ims] : 0; + final int vsproc = sepProcStates ? procStates[ips] : 0; + final int NSA = sepScreenStates ? 1 : screenStates.length; + final int NMA = sepMemStates ? 1 : memStates.length; + final int NPA = sepProcStates ? 1 : procStates.length; + long totalTime = 0; + for (int isa=0; isa<NSA; isa++) { + for (int ima=0; ima<NMA; ima++) { + for (int ipa=0; ipa<NPA; ipa++) { + final int vascreen = sepScreenStates ? 0 : screenStates[isa]; + final int vamem = sepMemStates ? 0 : memStates[ima]; + final int vaproc = sepProcStates ? 0 : procStates[ipa]; + final int bucket = ((vsscreen + vascreen + vsmem + vamem) + * STATE_COUNT) + vsproc + vaproc; + totalTime += getDuration(bucket, now); + } + } + } + pw.print(DumpUtils.CSV_SEP); + pw.print(totalTime); + } + } + } + } + + public void dumpPackageProcCheckin(PrintWriter pw, String pkgName, int uid, int vers, + String itemName, long now) { + pw.print("pkgproc,"); + pw.print(pkgName); + pw.print(","); + pw.print(uid); + pw.print(","); + pw.print(vers); + pw.print(","); + pw.print(DumpUtils.collapseString(pkgName, itemName)); + dumpAllStateCheckin(pw, now); + pw.println(); + if (mPssTable.getKeyCount() > 0) { + pw.print("pkgpss,"); + pw.print(pkgName); + pw.print(","); + pw.print(uid); + pw.print(","); + pw.print(vers); + pw.print(","); + pw.print(DumpUtils.collapseString(pkgName, itemName)); + dumpAllPssCheckin(pw); + pw.println(); + } + if (mNumExcessiveWake > 0 || mNumExcessiveCpu > 0 || mNumCachedKill > 0) { + pw.print("pkgkills,"); + pw.print(pkgName); + pw.print(","); + pw.print(uid); + pw.print(","); + pw.print(vers); + pw.print(","); + pw.print(DumpUtils.collapseString(pkgName, itemName)); + pw.print(","); + pw.print(mNumExcessiveWake); + pw.print(","); + pw.print(mNumExcessiveCpu); + pw.print(","); + pw.print(mNumCachedKill); + pw.print(","); + pw.print(mMinCachedKillPss); + pw.print(":"); + pw.print(mAvgCachedKillPss); + pw.print(":"); + pw.print(mMaxCachedKillPss); + pw.println(); + } + } + + public void dumpProcCheckin(PrintWriter pw, String procName, int uid, long now) { + if (mDurations.getKeyCount() > 0) { + pw.print("proc,"); + pw.print(procName); + pw.print(","); + pw.print(uid); + dumpAllStateCheckin(pw, now); + pw.println(); + } + if (mPssTable.getKeyCount() > 0) { + pw.print("pss,"); + pw.print(procName); + pw.print(","); + pw.print(uid); + dumpAllPssCheckin(pw); + pw.println(); + } + if (mNumExcessiveWake > 0 || mNumExcessiveCpu > 0 || mNumCachedKill > 0) { + pw.print("kills,"); + pw.print(procName); + pw.print(","); + pw.print(uid); + pw.print(","); + pw.print(mNumExcessiveWake); + pw.print(","); + pw.print(mNumExcessiveCpu); + pw.print(","); + pw.print(mNumCachedKill); + pw.print(","); + pw.print(mMinCachedKillPss); + pw.print(":"); + pw.print(mAvgCachedKillPss); + pw.print(":"); + pw.print(mMaxCachedKillPss); + pw.println(); + } + } + + public void dumpAllStateCheckin(PrintWriter pw, long now) { + boolean didCurState = false; + for (int i=0; i<mDurations.getKeyCount(); i++) { + final int key = mDurations.getKeyAt(i); + final int type = SparseMappingTable.getIdFromKey(key); + long time = mDurations.getValue(key); + if (mCurState == type) { + didCurState = true; + time += now - mStartTime; + } + DumpUtils.printProcStateTagAndValue(pw, type, time); + } + if (!didCurState && mCurState != STATE_NOTHING) { + DumpUtils.printProcStateTagAndValue(pw, mCurState, now - mStartTime); + } + } + + public void dumpAllPssCheckin(PrintWriter pw) { + final int N = mPssTable.getKeyCount(); + for (int i=0; i<N; i++) { + final int key = mPssTable.getKeyAt(i); + final int type = SparseMappingTable.getIdFromKey(key); + pw.print(','); + DumpUtils.printProcStateTag(pw, type); + pw.print(':'); + pw.print(mPssTable.getValue(key, PSS_SAMPLE_COUNT)); + pw.print(':'); + pw.print(mPssTable.getValue(key, PSS_MINIMUM)); + pw.print(':'); + pw.print(mPssTable.getValue(key, PSS_AVERAGE)); + pw.print(':'); + pw.print(mPssTable.getValue(key, PSS_MAXIMUM)); + pw.print(':'); + pw.print(mPssTable.getValue(key, PSS_USS_MINIMUM)); + pw.print(':'); + pw.print(mPssTable.getValue(key, PSS_USS_AVERAGE)); + pw.print(':'); + pw.print(mPssTable.getValue(key, PSS_USS_MAXIMUM)); + } + } + + public String toString() { + StringBuilder sb = new StringBuilder(128); + sb.append("ProcessState{").append(Integer.toHexString(System.identityHashCode(this))) + .append(" ").append(mName).append("/").append(mUid) + .append(" pkg=").append(mPackage); + if (mMultiPackage) sb.append(" (multi)"); + if (mCommonProcess != this) sb.append(" (sub)"); + sb.append("}"); + return sb.toString(); + } +} diff --git a/core/java/com/android/internal/app/ProcessStats.aidl b/core/java/com/android/internal/app/procstats/ProcessStats.aidl index 48b1f8516381..33639a0c8c31 100644 --- a/core/java/com/android/internal/app/ProcessStats.aidl +++ b/core/java/com/android/internal/app/procstats/ProcessStats.aidl @@ -14,6 +14,6 @@ ** limitations under the License. */ -package com.android.internal.app; +package com.android.internal.app.procstats; parcelable ProcessStats; diff --git a/core/java/com/android/internal/app/procstats/ProcessStats.java b/core/java/com/android/internal/app/procstats/ProcessStats.java new file mode 100644 index 000000000000..06542f7af7e7 --- /dev/null +++ b/core/java/com/android/internal/app/procstats/ProcessStats.java @@ -0,0 +1,1621 @@ +/* + * Copyright (C) 2013 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.internal.app.procstats; + +import android.os.Parcel; +import android.os.Parcelable; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.os.UserHandle; +import android.text.format.DateFormat; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.DebugUtils; +import android.util.Log; +import android.util.Slog; +import android.util.SparseArray; +import android.util.TimeUtils; + +import com.android.internal.app.ProcessMap; +import com.android.internal.app.procstats.DurationsTable; +import com.android.internal.app.procstats.ProcessState; +import com.android.internal.app.procstats.PssTable; +import com.android.internal.app.procstats.ServiceState; +import com.android.internal.app.procstats.SparseMappingTable; +import com.android.internal.app.procstats.SysMemUsageTable; +import com.android.internal.app.procstats.DumpUtils.*; + +import dalvik.system.VMRuntime; +import libcore.util.EmptyArray; + +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.Objects; + +public final class ProcessStats implements Parcelable { + public static final String TAG = "ProcessStats"; + static final boolean DEBUG = false; + static final boolean DEBUG_PARCEL = false; + + public static final String SERVICE_NAME = "procstats"; + + // How often the service commits its data, giving the minimum batching + // that is done. + public static long COMMIT_PERIOD = 3*60*60*1000; // Commit current stats every 3 hours + + // Minimum uptime period before committing. If the COMMIT_PERIOD has elapsed but + // the total uptime has not exceeded this amount, then the commit will be held until + // it is reached. + public static long COMMIT_UPTIME_PERIOD = 60*60*1000; // Must have at least 1 hour elapsed + + public static final int STATE_NOTHING = -1; + public static final int STATE_PERSISTENT = 0; + public static final int STATE_TOP = 1; + public static final int STATE_IMPORTANT_FOREGROUND = 2; + public static final int STATE_IMPORTANT_BACKGROUND = 3; + public static final int STATE_BACKUP = 4; + public static final int STATE_HEAVY_WEIGHT = 5; + public static final int STATE_SERVICE = 6; + public static final int STATE_SERVICE_RESTARTING = 7; + public static final int STATE_RECEIVER = 8; + public static final int STATE_HOME = 9; + public static final int STATE_LAST_ACTIVITY = 10; + public static final int STATE_CACHED_ACTIVITY = 11; + public static final int STATE_CACHED_ACTIVITY_CLIENT = 12; + public static final int STATE_CACHED_EMPTY = 13; + public static final int STATE_COUNT = STATE_CACHED_EMPTY+1; + + public static final int PSS_SAMPLE_COUNT = 0; + public static final int PSS_MINIMUM = 1; + public static final int PSS_AVERAGE = 2; + public static final int PSS_MAXIMUM = 3; + public static final int PSS_USS_MINIMUM = 4; + public static final int PSS_USS_AVERAGE = 5; + public static final int PSS_USS_MAXIMUM = 6; + public static final int PSS_COUNT = PSS_USS_MAXIMUM+1; + + public static final int SYS_MEM_USAGE_SAMPLE_COUNT = 0; + public static final int SYS_MEM_USAGE_CACHED_MINIMUM = 1; + public static final int SYS_MEM_USAGE_CACHED_AVERAGE = 2; + public static final int SYS_MEM_USAGE_CACHED_MAXIMUM = 3; + public static final int SYS_MEM_USAGE_FREE_MINIMUM = 4; + public static final int SYS_MEM_USAGE_FREE_AVERAGE = 5; + public static final int SYS_MEM_USAGE_FREE_MAXIMUM = 6; + public static final int SYS_MEM_USAGE_ZRAM_MINIMUM = 7; + public static final int SYS_MEM_USAGE_ZRAM_AVERAGE = 8; + public static final int SYS_MEM_USAGE_ZRAM_MAXIMUM = 9; + public static final int SYS_MEM_USAGE_KERNEL_MINIMUM = 10; + public static final int SYS_MEM_USAGE_KERNEL_AVERAGE = 11; + public static final int SYS_MEM_USAGE_KERNEL_MAXIMUM = 12; + public static final int SYS_MEM_USAGE_NATIVE_MINIMUM = 13; + public static final int SYS_MEM_USAGE_NATIVE_AVERAGE = 14; + public static final int SYS_MEM_USAGE_NATIVE_MAXIMUM = 15; + public static final int SYS_MEM_USAGE_COUNT = SYS_MEM_USAGE_NATIVE_MAXIMUM+1; + + public static final int ADJ_NOTHING = -1; + public static final int ADJ_MEM_FACTOR_NORMAL = 0; + public static final int ADJ_MEM_FACTOR_MODERATE = 1; + public static final int ADJ_MEM_FACTOR_LOW = 2; + public static final int ADJ_MEM_FACTOR_CRITICAL = 3; + public static final int ADJ_MEM_FACTOR_COUNT = ADJ_MEM_FACTOR_CRITICAL+1; + public static final int ADJ_SCREEN_MOD = ADJ_MEM_FACTOR_COUNT; + public static final int ADJ_SCREEN_OFF = 0; + public static final int ADJ_SCREEN_ON = ADJ_SCREEN_MOD; + public static final int ADJ_COUNT = ADJ_SCREEN_ON*2; + + public static final int FLAG_COMPLETE = 1<<0; + public static final int FLAG_SHUTDOWN = 1<<1; + public static final int FLAG_SYSPROPS = 1<<2; + + public static final int[] ALL_MEM_ADJ = new int[] { ADJ_MEM_FACTOR_NORMAL, + ADJ_MEM_FACTOR_MODERATE, ADJ_MEM_FACTOR_LOW, ADJ_MEM_FACTOR_CRITICAL }; + + public static final int[] ALL_SCREEN_ADJ = new int[] { ADJ_SCREEN_OFF, ADJ_SCREEN_ON }; + + public static final int[] NON_CACHED_PROC_STATES = new int[] { + STATE_PERSISTENT, STATE_TOP, STATE_IMPORTANT_FOREGROUND, + STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, STATE_HEAVY_WEIGHT, + STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER + }; + + public static final int[] BACKGROUND_PROC_STATES = new int[] { + STATE_IMPORTANT_FOREGROUND, STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, + STATE_HEAVY_WEIGHT, STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER + }; + + public static final int[] ALL_PROC_STATES = new int[] { STATE_PERSISTENT, + STATE_TOP, STATE_IMPORTANT_FOREGROUND, STATE_IMPORTANT_BACKGROUND, STATE_BACKUP, + STATE_HEAVY_WEIGHT, STATE_SERVICE, STATE_SERVICE_RESTARTING, STATE_RECEIVER, + STATE_HOME, STATE_LAST_ACTIVITY, STATE_CACHED_ACTIVITY, + STATE_CACHED_ACTIVITY_CLIENT, STATE_CACHED_EMPTY + }; + + // Current version of the parcel format. + private static final int PARCEL_VERSION = 19; + // In-memory Parcel magic number, used to detect attempts to unmarshall bad data + private static final int MAGIC = 0x50535454; + + public String mReadError; + public String mTimePeriodStartClockStr; + public int mFlags; + + public final ProcessMap<SparseArray<PackageState>> mPackages + = new ProcessMap<SparseArray<PackageState>>(); + public final ProcessMap<ProcessState> mProcesses = new ProcessMap<ProcessState>(); + + public final long[] mMemFactorDurations = new long[ADJ_COUNT]; + public int mMemFactor = STATE_NOTHING; + public long mStartTime; + + public long mTimePeriodStartClock; + public long mTimePeriodStartRealtime; + public long mTimePeriodEndRealtime; + public long mTimePeriodStartUptime; + public long mTimePeriodEndUptime; + String mRuntime; + boolean mRunning; + + public final SparseMappingTable mTableData = new SparseMappingTable(); + + int[] mAddLongTable; + int mAddLongTableSize; + + public final long[] mSysMemUsageArgs = new long[SYS_MEM_USAGE_COUNT]; + public final SysMemUsageTable mSysMemUsage = new SysMemUsageTable(mTableData); + + // For writing parcels. + ArrayMap<String, Integer> mCommonStringToIndex; + + // For reading parcels. + ArrayList<String> mIndexToCommonString; + + public ProcessStats(boolean running) { + mRunning = running; + reset(); + } + + public ProcessStats(Parcel in) { + reset(); + readFromParcel(in); + } + + public void add(ProcessStats other) { + ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = other.mPackages.getMap(); + for (int ip=0; ip<pkgMap.size(); ip++) { + final String pkgName = pkgMap.keyAt(ip); + final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip); + for (int iu=0; iu<uids.size(); iu++) { + final int uid = uids.keyAt(iu); + final SparseArray<PackageState> versions = uids.valueAt(iu); + for (int iv=0; iv<versions.size(); iv++) { + final int vers = versions.keyAt(iv); + final PackageState otherState = versions.valueAt(iv); + final int NPROCS = otherState.mProcesses.size(); + final int NSRVS = otherState.mServices.size(); + for (int iproc=0; iproc<NPROCS; iproc++) { + ProcessState otherProc = otherState.mProcesses.valueAt(iproc); + if (otherProc.getCommonProcess() != otherProc) { + if (DEBUG) Slog.d(TAG, "Adding pkg " + pkgName + " uid " + uid + + " vers " + vers + " proc " + otherProc.getName()); + ProcessState thisProc = getProcessStateLocked(pkgName, uid, vers, + otherProc.getName()); + if (thisProc.getCommonProcess() == thisProc) { + if (DEBUG) Slog.d(TAG, "Existing process is single-package, splitting"); + thisProc.setMultiPackage(true); + long now = SystemClock.uptimeMillis(); + final PackageState pkgState = getPackageStateLocked(pkgName, uid, + vers); + thisProc = thisProc.clone(now); + pkgState.mProcesses.put(thisProc.getName(), thisProc); + } + thisProc.add(otherProc); + } + } + for (int isvc=0; isvc<NSRVS; isvc++) { + ServiceState otherSvc = otherState.mServices.valueAt(isvc); + if (DEBUG) Slog.d(TAG, "Adding pkg " + pkgName + " uid " + uid + + " service " + otherSvc.getName()); + ServiceState thisSvc = getServiceStateLocked(pkgName, uid, vers, + otherSvc.getProcessName(), otherSvc.getName()); + thisSvc.add(otherSvc); + } + } + } + } + + ArrayMap<String, SparseArray<ProcessState>> procMap = other.mProcesses.getMap(); + for (int ip=0; ip<procMap.size(); ip++) { + SparseArray<ProcessState> uids = procMap.valueAt(ip); + for (int iu=0; iu<uids.size(); iu++) { + int uid = uids.keyAt(iu); + ProcessState otherProc = uids.valueAt(iu); + final String name = otherProc.getName(); + final String pkg = otherProc.getPackage(); + final int vers = otherProc.getVersion(); + ProcessState thisProc = mProcesses.get(name, uid); + if (DEBUG) Slog.d(TAG, "Adding uid " + uid + " proc " + name); + if (thisProc == null) { + if (DEBUG) Slog.d(TAG, "Creating new process!"); + thisProc = new ProcessState(this, pkg, uid, vers, name); + mProcesses.put(name, uid, thisProc); + PackageState thisState = getPackageStateLocked(pkg, uid, vers); + if (!thisState.mProcesses.containsKey(name)) { + thisState.mProcesses.put(name, thisProc); + } + } + thisProc.add(otherProc); + } + } + + for (int i=0; i<ADJ_COUNT; i++) { + if (DEBUG) Slog.d(TAG, "Total duration #" + i + " inc by " + + other.mMemFactorDurations[i] + " from " + + mMemFactorDurations[i]); + mMemFactorDurations[i] += other.mMemFactorDurations[i]; + } + + mSysMemUsage.mergeStats(other.mSysMemUsage); + + if (other.mTimePeriodStartClock < mTimePeriodStartClock) { + mTimePeriodStartClock = other.mTimePeriodStartClock; + mTimePeriodStartClockStr = other.mTimePeriodStartClockStr; + } + mTimePeriodEndRealtime += other.mTimePeriodEndRealtime - other.mTimePeriodStartRealtime; + mTimePeriodEndUptime += other.mTimePeriodEndUptime - other.mTimePeriodStartUptime; + } + + public void addSysMemUsage(long cachedMem, long freeMem, long zramMem, long kernelMem, + long nativeMem) { + if (mMemFactor != STATE_NOTHING) { + int state = mMemFactor * STATE_COUNT; + mSysMemUsageArgs[SYS_MEM_USAGE_SAMPLE_COUNT] = 1; + for (int i=0; i<3; i++) { + mSysMemUsageArgs[SYS_MEM_USAGE_CACHED_MINIMUM + i] = cachedMem; + mSysMemUsageArgs[SYS_MEM_USAGE_FREE_MINIMUM + i] = freeMem; + mSysMemUsageArgs[SYS_MEM_USAGE_ZRAM_MINIMUM + i] = zramMem; + mSysMemUsageArgs[SYS_MEM_USAGE_KERNEL_MINIMUM + i] = kernelMem; + mSysMemUsageArgs[SYS_MEM_USAGE_NATIVE_MINIMUM + i] = nativeMem; + } + mSysMemUsage.mergeStats(state, mSysMemUsageArgs, 0); + } + } + + public static final Parcelable.Creator<ProcessStats> CREATOR + = new Parcelable.Creator<ProcessStats>() { + public ProcessStats createFromParcel(Parcel in) { + return new ProcessStats(in); + } + + public ProcessStats[] newArray(int size) { + return new ProcessStats[size]; + } + }; + + public void computeTotalMemoryUse(TotalMemoryUseCollection data, long now) { + data.totalTime = 0; + for (int i=0; i<STATE_COUNT; i++) { + data.processStateWeight[i] = 0; + data.processStatePss[i] = 0; + data.processStateTime[i] = 0; + data.processStateSamples[i] = 0; + } + for (int i=0; i<SYS_MEM_USAGE_COUNT; i++) { + data.sysMemUsage[i] = 0; + } + data.sysMemCachedWeight = 0; + data.sysMemFreeWeight = 0; + data.sysMemZRamWeight = 0; + data.sysMemKernelWeight = 0; + data.sysMemNativeWeight = 0; + data.sysMemSamples = 0; + final long[] totalMemUsage = mSysMemUsage.getTotalMemUsage(); + for (int is=0; is<data.screenStates.length; is++) { + for (int im=0; im<data.memStates.length; im++) { + int memBucket = data.screenStates[is] + data.memStates[im]; + int stateBucket = memBucket * STATE_COUNT; + long memTime = mMemFactorDurations[memBucket]; + if (mMemFactor == memBucket) { + memTime += now - mStartTime; + } + data.totalTime += memTime; + final int sysKey = mSysMemUsage.getKey((byte)stateBucket); + long[] longs = totalMemUsage; + int idx = 0; + if (sysKey != SparseMappingTable.INVALID_KEY) { + final long[] tmpLongs = mSysMemUsage.getArrayForKey(sysKey); + final int tmpIndex = SparseMappingTable.getIndexFromKey(sysKey); + if (tmpLongs[tmpIndex+SYS_MEM_USAGE_SAMPLE_COUNT] >= 3) { + SysMemUsageTable.mergeSysMemUsage(data.sysMemUsage, 0, longs, idx); + longs = tmpLongs; + idx = tmpIndex; + } + } + data.sysMemCachedWeight += longs[idx+SYS_MEM_USAGE_CACHED_AVERAGE] + * (double)memTime; + data.sysMemFreeWeight += longs[idx+SYS_MEM_USAGE_FREE_AVERAGE] + * (double)memTime; + data.sysMemZRamWeight += longs[idx+SYS_MEM_USAGE_ZRAM_AVERAGE] + * (double)memTime; + data.sysMemKernelWeight += longs[idx+SYS_MEM_USAGE_KERNEL_AVERAGE] + * (double)memTime; + data.sysMemNativeWeight += longs[idx+SYS_MEM_USAGE_NATIVE_AVERAGE] + * (double)memTime; + data.sysMemSamples += longs[idx+SYS_MEM_USAGE_SAMPLE_COUNT]; + } + } + ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); + for (int iproc=0; iproc<procMap.size(); iproc++) { + SparseArray<ProcessState> uids = procMap.valueAt(iproc); + for (int iu=0; iu<uids.size(); iu++) { + final ProcessState proc = uids.valueAt(iu); + proc.aggregatePss(data, now); + } + } + } + + public void reset() { + if (DEBUG) Slog.d(TAG, "Resetting state of " + mTimePeriodStartClockStr); + resetCommon(); + mPackages.getMap().clear(); + mProcesses.getMap().clear(); + mMemFactor = STATE_NOTHING; + mStartTime = 0; + if (DEBUG) Slog.d(TAG, "State reset; now " + mTimePeriodStartClockStr); + } + + public void resetSafely() { + if (DEBUG) Slog.d(TAG, "Safely resetting state of " + mTimePeriodStartClockStr); + resetCommon(); + + // First initialize use count of all common processes. + final long now = SystemClock.uptimeMillis(); + final ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); + for (int ip=procMap.size()-1; ip>=0; ip--) { + final SparseArray<ProcessState> uids = procMap.valueAt(ip); + for (int iu=uids.size()-1; iu>=0; iu--) { + uids.valueAt(iu).tmpNumInUse = 0; + } + } + + // Next reset or prune all per-package processes, and for the ones that are reset + // track this back to the common processes. + final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap(); + for (int ip=pkgMap.size()-1; ip>=0; ip--) { + final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip); + for (int iu=uids.size()-1; iu>=0; iu--) { + final SparseArray<PackageState> vpkgs = uids.valueAt(iu); + for (int iv=vpkgs.size()-1; iv>=0; iv--) { + final PackageState pkgState = vpkgs.valueAt(iv); + for (int iproc=pkgState.mProcesses.size()-1; iproc>=0; iproc--) { + final ProcessState ps = pkgState.mProcesses.valueAt(iproc); + if (ps.isInUse()) { + ps.resetSafely(now); + ps.getCommonProcess().tmpNumInUse++; + ps.getCommonProcess().tmpFoundSubProc = ps; + } else { + pkgState.mProcesses.valueAt(iproc).makeDead(); + pkgState.mProcesses.removeAt(iproc); + } + } + for (int isvc=pkgState.mServices.size()-1; isvc>=0; isvc--) { + final ServiceState ss = pkgState.mServices.valueAt(isvc); + if (ss.isInUse()) { + ss.resetSafely(now); + } else { + pkgState.mServices.removeAt(isvc); + } + } + if (pkgState.mProcesses.size() <= 0 && pkgState.mServices.size() <= 0) { + vpkgs.removeAt(iv); + } + } + if (vpkgs.size() <= 0) { + uids.removeAt(iu); + } + } + if (uids.size() <= 0) { + pkgMap.removeAt(ip); + } + } + + // Finally prune out any common processes that are no longer in use. + for (int ip=procMap.size()-1; ip>=0; ip--) { + final SparseArray<ProcessState> uids = procMap.valueAt(ip); + for (int iu=uids.size()-1; iu>=0; iu--) { + ProcessState ps = uids.valueAt(iu); + if (ps.isInUse() || ps.tmpNumInUse > 0) { + // If this is a process for multiple packages, we could at this point + // be back down to one package. In that case, we want to revert back + // to a single shared ProcessState. We can do this by converting the + // current package-specific ProcessState up to the shared ProcessState, + // throwing away the current one we have here (because nobody else is + // using it). + if (!ps.isActive() && ps.isMultiPackage() && ps.tmpNumInUse == 1) { + // Here we go... + ps = ps.tmpFoundSubProc; + ps.makeStandalone(); + uids.setValueAt(iu, ps); + } else { + ps.resetSafely(now); + } + } else { + ps.makeDead(); + uids.removeAt(iu); + } + } + if (uids.size() <= 0) { + procMap.removeAt(ip); + } + } + + mStartTime = now; + if (DEBUG) Slog.d(TAG, "State reset; now " + mTimePeriodStartClockStr); + } + + private void resetCommon() { + mTimePeriodStartClock = System.currentTimeMillis(); + buildTimePeriodStartClockStr(); + mTimePeriodStartRealtime = mTimePeriodEndRealtime = SystemClock.elapsedRealtime(); + mTimePeriodStartUptime = mTimePeriodEndUptime = SystemClock.uptimeMillis(); + mTableData.reset(); + Arrays.fill(mMemFactorDurations, 0); + mSysMemUsage.resetTable(); + mStartTime = 0; + mReadError = null; + mFlags = 0; + evaluateSystemProperties(true); + } + + public boolean evaluateSystemProperties(boolean update) { + boolean changed = false; + String runtime = SystemProperties.get("persist.sys.dalvik.vm.lib.2", + VMRuntime.getRuntime().vmLibrary()); + if (!Objects.equals(runtime, mRuntime)) { + changed = true; + if (update) { + mRuntime = runtime; + } + } + return changed; + } + + private void buildTimePeriodStartClockStr() { + mTimePeriodStartClockStr = DateFormat.format("yyyy-MM-dd-HH-mm-ss", + mTimePeriodStartClock).toString(); + } + + static final int[] BAD_TABLE = new int[0]; + + private void writeCompactedLongArray(Parcel out, long[] array, int num) { + for (int i=0; i<num; i++) { + long val = array[i]; + if (val < 0) { + Slog.w(TAG, "Time val negative: " + val); + val = 0; + } + if (val <= Integer.MAX_VALUE) { + out.writeInt((int)val); + } else { + int top = ~((int)((val>>32)&0x7fffffff)); + int bottom = (int)(val&0xfffffff); + out.writeInt(top); + out.writeInt(bottom); + } + } + } + + private void readCompactedLongArray(Parcel in, int version, long[] array, int num) { + if (version <= 10) { + in.readLongArray(array); + return; + } + final int alen = array.length; + if (num > alen) { + throw new RuntimeException("bad array lengths: got " + num + " array is " + alen); + } + int i; + for (i=0; i<num; i++) { + int val = in.readInt(); + if (val >= 0) { + array[i] = val; + } else { + int bottom = in.readInt(); + array[i] = (((long)~val)<<32) | bottom; + } + } + while (i < alen) { + array[i] = 0; + i++; + } + } + + private void writeCommonString(Parcel out, String name) { + Integer index = mCommonStringToIndex.get(name); + if (index != null) { + out.writeInt(index); + return; + } + index = mCommonStringToIndex.size(); + mCommonStringToIndex.put(name, index); + out.writeInt(~index); + out.writeString(name); + } + + private String readCommonString(Parcel in, int version) { + if (version <= 9) { + return in.readString(); + } + int index = in.readInt(); + if (index >= 0) { + return mIndexToCommonString.get(index); + } + index = ~index; + String name = in.readString(); + while (mIndexToCommonString.size() <= index) { + mIndexToCommonString.add(null); + } + mIndexToCommonString.set(index, name); + return name; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel out, int flags) { + writeToParcel(out, SystemClock.uptimeMillis(), flags); + } + + /** @hide */ + public void writeToParcel(Parcel out, long now, int flags) { + out.writeInt(MAGIC); + out.writeInt(PARCEL_VERSION); + out.writeInt(STATE_COUNT); + out.writeInt(ADJ_COUNT); + out.writeInt(PSS_COUNT); + out.writeInt(SYS_MEM_USAGE_COUNT); + out.writeInt(SparseMappingTable.ARRAY_SIZE); + + mCommonStringToIndex = new ArrayMap<String, Integer>(mProcesses.size()); + + // First commit all running times. + ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); + final int NPROC = procMap.size(); + for (int ip=0; ip<NPROC; ip++) { + SparseArray<ProcessState> uids = procMap.valueAt(ip); + final int NUID = uids.size(); + for (int iu=0; iu<NUID; iu++) { + uids.valueAt(iu).commitStateTime(now); + } + } + final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap(); + final int NPKG = pkgMap.size(); + for (int ip=0; ip<NPKG; ip++) { + final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip); + final int NUID = uids.size(); + for (int iu=0; iu<NUID; iu++) { + final SparseArray<PackageState> vpkgs = uids.valueAt(iu); + final int NVERS = vpkgs.size(); + for (int iv=0; iv<NVERS; iv++) { + PackageState pkgState = vpkgs.valueAt(iv); + final int NPROCS = pkgState.mProcesses.size(); + for (int iproc=0; iproc<NPROCS; iproc++) { + ProcessState proc = pkgState.mProcesses.valueAt(iproc); + if (proc.getCommonProcess() != proc) { + proc.commitStateTime(now); + } + } + final int NSRVS = pkgState.mServices.size(); + for (int isvc=0; isvc<NSRVS; isvc++) { + pkgState.mServices.valueAt(isvc).commitStateTime(now); + } + } + } + } + + out.writeLong(mTimePeriodStartClock); + out.writeLong(mTimePeriodStartRealtime); + out.writeLong(mTimePeriodEndRealtime); + out.writeLong(mTimePeriodStartUptime); + out.writeLong(mTimePeriodEndUptime); + out.writeString(mRuntime); + out.writeInt(mFlags); + + mTableData.writeToParcel(out); + + if (mMemFactor != STATE_NOTHING) { + mMemFactorDurations[mMemFactor] += now - mStartTime; + mStartTime = now; + } + writeCompactedLongArray(out, mMemFactorDurations, mMemFactorDurations.length); + + mSysMemUsage.writeToParcel(out); + + out.writeInt(NPROC); + for (int ip=0; ip<NPROC; ip++) { + writeCommonString(out, procMap.keyAt(ip)); + final SparseArray<ProcessState> uids = procMap.valueAt(ip); + final int NUID = uids.size(); + out.writeInt(NUID); + for (int iu=0; iu<NUID; iu++) { + out.writeInt(uids.keyAt(iu)); + final ProcessState proc = uids.valueAt(iu); + writeCommonString(out, proc.getPackage()); + out.writeInt(proc.getVersion()); + proc.writeToParcel(out, now); + } + } + out.writeInt(NPKG); + for (int ip=0; ip<NPKG; ip++) { + writeCommonString(out, pkgMap.keyAt(ip)); + final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip); + final int NUID = uids.size(); + out.writeInt(NUID); + for (int iu=0; iu<NUID; iu++) { + out.writeInt(uids.keyAt(iu)); + final SparseArray<PackageState> vpkgs = uids.valueAt(iu); + final int NVERS = vpkgs.size(); + out.writeInt(NVERS); + for (int iv=0; iv<NVERS; iv++) { + out.writeInt(vpkgs.keyAt(iv)); + final PackageState pkgState = vpkgs.valueAt(iv); + final int NPROCS = pkgState.mProcesses.size(); + out.writeInt(NPROCS); + for (int iproc=0; iproc<NPROCS; iproc++) { + writeCommonString(out, pkgState.mProcesses.keyAt(iproc)); + final ProcessState proc = pkgState.mProcesses.valueAt(iproc); + if (proc.getCommonProcess() == proc) { + // This is the same as the common process we wrote above. + out.writeInt(0); + } else { + // There is separate data for this package's process. + out.writeInt(1); + proc.writeToParcel(out, now); + } + } + final int NSRVS = pkgState.mServices.size(); + out.writeInt(NSRVS); + for (int isvc=0; isvc<NSRVS; isvc++) { + out.writeString(pkgState.mServices.keyAt(isvc)); + final ServiceState svc = pkgState.mServices.valueAt(isvc); + writeCommonString(out, svc.getProcessName()); + svc.writeToParcel(out, now); + } + } + } + } + + mCommonStringToIndex = null; + } + + private boolean readCheckedInt(Parcel in, int val, String what) { + int got; + if ((got=in.readInt()) != val) { + mReadError = "bad " + what + ": " + got; + return false; + } + return true; + } + + static byte[] readFully(InputStream stream, int[] outLen) throws IOException { + int pos = 0; + final int initialAvail = stream.available(); + byte[] data = new byte[initialAvail > 0 ? (initialAvail+1) : 16384]; + while (true) { + int amt = stream.read(data, pos, data.length-pos); + if (DEBUG_PARCEL) Slog.i("foo", "Read " + amt + " bytes at " + pos + + " of avail " + data.length); + if (amt < 0) { + if (DEBUG_PARCEL) Slog.i("foo", "**** FINISHED READING: pos=" + pos + + " len=" + data.length); + outLen[0] = pos; + return data; + } + pos += amt; + if (pos >= data.length) { + byte[] newData = new byte[pos+16384]; + if (DEBUG_PARCEL) Slog.i(TAG, "Copying " + pos + " bytes to new array len " + + newData.length); + System.arraycopy(data, 0, newData, 0, pos); + data = newData; + } + } + } + + public void read(InputStream stream) { + try { + int[] len = new int[1]; + byte[] raw = readFully(stream, len); + Parcel in = Parcel.obtain(); + in.unmarshall(raw, 0, len[0]); + in.setDataPosition(0); + stream.close(); + + readFromParcel(in); + } catch (IOException e) { + mReadError = "caught exception: " + e; + } + } + + public void readFromParcel(Parcel in) { + final boolean hadData = mPackages.getMap().size() > 0 + || mProcesses.getMap().size() > 0; + if (hadData) { + resetSafely(); + } + + if (!readCheckedInt(in, MAGIC, "magic number")) { + return; + } + int version = in.readInt(); + if (version != PARCEL_VERSION) { + mReadError = "bad version: " + version; + return; + } + if (!readCheckedInt(in, STATE_COUNT, "state count")) { + return; + } + if (!readCheckedInt(in, ADJ_COUNT, "adj count")) { + return; + } + if (!readCheckedInt(in, PSS_COUNT, "pss count")) { + return; + } + if (!readCheckedInt(in, SYS_MEM_USAGE_COUNT, "sys mem usage count")) { + return; + } + if (!readCheckedInt(in, SparseMappingTable.ARRAY_SIZE, "longs size")) { + return; + } + + mIndexToCommonString = new ArrayList<String>(); + + mTimePeriodStartClock = in.readLong(); + buildTimePeriodStartClockStr(); + mTimePeriodStartRealtime = in.readLong(); + mTimePeriodEndRealtime = in.readLong(); + mTimePeriodStartUptime = in.readLong(); + mTimePeriodEndUptime = in.readLong(); + mRuntime = in.readString(); + mFlags = in.readInt(); + mTableData.readFromParcel(in); + readCompactedLongArray(in, version, mMemFactorDurations, mMemFactorDurations.length); + if (!mSysMemUsage.readFromParcel(in)) { + return; + } + + int NPROC = in.readInt(); + if (NPROC < 0) { + mReadError = "bad process count: " + NPROC; + return; + } + while (NPROC > 0) { + NPROC--; + final String procName = readCommonString(in, version); + if (procName == null) { + mReadError = "bad process name"; + return; + } + int NUID = in.readInt(); + if (NUID < 0) { + mReadError = "bad uid count: " + NUID; + return; + } + while (NUID > 0) { + NUID--; + final int uid = in.readInt(); + if (uid < 0) { + mReadError = "bad uid: " + uid; + return; + } + final String pkgName = readCommonString(in, version); + if (pkgName == null) { + mReadError = "bad process package name"; + return; + } + final int vers = in.readInt(); + ProcessState proc = hadData ? mProcesses.get(procName, uid) : null; + if (proc != null) { + if (!proc.readFromParcel(in, false)) { + return; + } + } else { + proc = new ProcessState(this, pkgName, uid, vers, procName); + if (!proc.readFromParcel(in, true)) { + return; + } + } + if (DEBUG_PARCEL) Slog.d(TAG, "Adding process: " + procName + " " + uid + + " " + proc); + mProcesses.put(procName, uid, proc); + } + } + + if (DEBUG_PARCEL) Slog.d(TAG, "Read " + mProcesses.getMap().size() + " processes"); + + int NPKG = in.readInt(); + if (NPKG < 0) { + mReadError = "bad package count: " + NPKG; + return; + } + while (NPKG > 0) { + NPKG--; + final String pkgName = readCommonString(in, version); + if (pkgName == null) { + mReadError = "bad package name"; + return; + } + int NUID = in.readInt(); + if (NUID < 0) { + mReadError = "bad uid count: " + NUID; + return; + } + while (NUID > 0) { + NUID--; + final int uid = in.readInt(); + if (uid < 0) { + mReadError = "bad uid: " + uid; + return; + } + int NVERS = in.readInt(); + if (NVERS < 0) { + mReadError = "bad versions count: " + NVERS; + return; + } + while (NVERS > 0) { + NVERS--; + final int vers = in.readInt(); + PackageState pkgState = new PackageState(pkgName, uid); + SparseArray<PackageState> vpkg = mPackages.get(pkgName, uid); + if (vpkg == null) { + vpkg = new SparseArray<PackageState>(); + mPackages.put(pkgName, uid, vpkg); + } + vpkg.put(vers, pkgState); + int NPROCS = in.readInt(); + if (NPROCS < 0) { + mReadError = "bad package process count: " + NPROCS; + return; + } + while (NPROCS > 0) { + NPROCS--; + String procName = readCommonString(in, version); + if (procName == null) { + mReadError = "bad package process name"; + return; + } + int hasProc = in.readInt(); + if (DEBUG_PARCEL) Slog.d(TAG, "Reading package " + pkgName + " " + uid + + " process " + procName + " hasProc=" + hasProc); + ProcessState commonProc = mProcesses.get(procName, uid); + if (DEBUG_PARCEL) Slog.d(TAG, "Got common proc " + procName + " " + uid + + ": " + commonProc); + if (commonProc == null) { + mReadError = "no common proc: " + procName; + return; + } + if (hasProc != 0) { + // The process for this package is unique to the package; we + // need to load it. We don't need to do anything about it if + // it is not unique because if someone later looks for it + // they will find and use it from the global procs. + ProcessState proc = hadData ? pkgState.mProcesses.get(procName) : null; + if (proc != null) { + if (!proc.readFromParcel(in, false)) { + return; + } + } else { + proc = new ProcessState(commonProc, pkgName, uid, vers, procName, + 0); + if (!proc.readFromParcel(in, true)) { + return; + } + } + if (DEBUG_PARCEL) Slog.d(TAG, "Adding package " + pkgName + " process: " + + procName + " " + uid + " " + proc); + pkgState.mProcesses.put(procName, proc); + } else { + if (DEBUG_PARCEL) Slog.d(TAG, "Adding package " + pkgName + " process: " + + procName + " " + uid + " " + commonProc); + pkgState.mProcesses.put(procName, commonProc); + } + } + int NSRVS = in.readInt(); + if (NSRVS < 0) { + mReadError = "bad package service count: " + NSRVS; + return; + } + while (NSRVS > 0) { + NSRVS--; + String serviceName = in.readString(); + if (serviceName == null) { + mReadError = "bad package service name"; + return; + } + String processName = version > 9 ? readCommonString(in, version) : null; + ServiceState serv = hadData ? pkgState.mServices.get(serviceName) : null; + if (serv == null) { + serv = new ServiceState(this, pkgName, serviceName, processName, null); + } + if (!serv.readFromParcel(in)) { + return; + } + if (DEBUG_PARCEL) Slog.d(TAG, "Adding package " + pkgName + " service: " + + serviceName + " " + uid + " " + serv); + pkgState.mServices.put(serviceName, serv); + } + } + } + } + + mIndexToCommonString = null; + + if (DEBUG_PARCEL) Slog.d(TAG, "Successfully read procstats!"); + } + + public PackageState getPackageStateLocked(String packageName, int uid, int vers) { + SparseArray<PackageState> vpkg = mPackages.get(packageName, uid); + if (vpkg == null) { + vpkg = new SparseArray<PackageState>(); + mPackages.put(packageName, uid, vpkg); + } + PackageState as = vpkg.get(vers); + if (as != null) { + return as; + } + as = new PackageState(packageName, uid); + vpkg.put(vers, as); + return as; + } + + public ProcessState getProcessStateLocked(String packageName, int uid, int vers, + String processName) { + final PackageState pkgState = getPackageStateLocked(packageName, uid, vers); + ProcessState ps = pkgState.mProcesses.get(processName); + if (ps != null) { + return ps; + } + ProcessState commonProc = mProcesses.get(processName, uid); + if (commonProc == null) { + commonProc = new ProcessState(this, packageName, uid, vers, processName); + mProcesses.put(processName, uid, commonProc); + if (DEBUG) Slog.d(TAG, "GETPROC created new common " + commonProc); + } + if (!commonProc.isMultiPackage()) { + if (packageName.equals(commonProc.getPackage()) && vers == commonProc.getVersion()) { + // This common process is not in use by multiple packages, and + // is for the calling package, so we can just use it directly. + ps = commonProc; + if (DEBUG) Slog.d(TAG, "GETPROC also using for pkg " + commonProc); + } else { + if (DEBUG) Slog.d(TAG, "GETPROC need to split common proc!"); + // This common process has not been in use by multiple packages, + // but it was created for a different package than the caller. + // We need to convert it to a multi-package process. + commonProc.setMultiPackage(true); + // To do this, we need to make two new process states, one a copy + // of the current state for the process under the original package + // name, and the second a free new process state for it as the + // new package name. + long now = SystemClock.uptimeMillis(); + // First let's make a copy of the current process state and put + // that under the now unique state for its original package name. + final PackageState commonPkgState = getPackageStateLocked(commonProc.getPackage(), + uid, commonProc.getVersion()); + if (commonPkgState != null) { + ProcessState cloned = commonProc.clone(now); + if (DEBUG) Slog.d(TAG, "GETPROC setting clone to pkg " + commonProc.getPackage() + + ": " + cloned); + commonPkgState.mProcesses.put(commonProc.getName(), cloned); + // If this has active services, we need to update their process pointer + // to point to the new package-specific process state. + for (int i=commonPkgState.mServices.size()-1; i>=0; i--) { + ServiceState ss = commonPkgState.mServices.valueAt(i); + if (ss.getProcess() == commonProc) { + if (DEBUG) Slog.d(TAG, "GETPROC switching service to cloned: " + ss); + ss.setProcess(cloned); + } else if (DEBUG) { + Slog.d(TAG, "GETPROC leaving proc of " + ss); + } + } + } else { + Slog.w(TAG, "Cloning proc state: no package state " + commonProc.getPackage() + + "/" + uid + " for proc " + commonProc.getName()); + } + // And now make a fresh new process state for the new package name. + ps = new ProcessState(commonProc, packageName, uid, vers, processName, now); + if (DEBUG) Slog.d(TAG, "GETPROC created new pkg " + ps); + } + } else { + // The common process is for multiple packages, we need to create a + // separate object for the per-package data. + ps = new ProcessState(commonProc, packageName, uid, vers, processName, + SystemClock.uptimeMillis()); + if (DEBUG) Slog.d(TAG, "GETPROC created new pkg " + ps); + } + pkgState.mProcesses.put(processName, ps); + if (DEBUG) Slog.d(TAG, "GETPROC adding new pkg " + ps); + return ps; + } + + public ServiceState getServiceStateLocked(String packageName, int uid, int vers, + String processName, String className) { + final ProcessStats.PackageState as = getPackageStateLocked(packageName, uid, vers); + ServiceState ss = as.mServices.get(className); + if (ss != null) { + if (DEBUG) Slog.d(TAG, "GETSVC: returning existing " + ss); + return ss; + } + final ProcessState ps = processName != null + ? getProcessStateLocked(packageName, uid, vers, processName) : null; + ss = new ServiceState(this, packageName, className, processName, ps); + as.mServices.put(className, ss); + if (DEBUG) Slog.d(TAG, "GETSVC: creating " + ss + " in " + ps); + return ss; + } + + public void dumpLocked(PrintWriter pw, String reqPackage, long now, boolean dumpSummary, + boolean dumpAll, boolean activeOnly) { + long totalTime = DumpUtils.dumpSingleTime(null, null, mMemFactorDurations, mMemFactor, + mStartTime, now); + boolean sepNeeded = false; + if (mSysMemUsage.getKeyCount() > 0) { + pw.println("System memory usage:"); + mSysMemUsage.dump(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ); + sepNeeded = true; + } + ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap(); + boolean printedHeader = false; + for (int ip=0; ip<pkgMap.size(); ip++) { + final String pkgName = pkgMap.keyAt(ip); + final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip); + for (int iu=0; iu<uids.size(); iu++) { + final int uid = uids.keyAt(iu); + final SparseArray<PackageState> vpkgs = uids.valueAt(iu); + for (int iv=0; iv<vpkgs.size(); iv++) { + final int vers = vpkgs.keyAt(iv); + final PackageState pkgState = vpkgs.valueAt(iv); + final int NPROCS = pkgState.mProcesses.size(); + final int NSRVS = pkgState.mServices.size(); + final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName); + if (!pkgMatch) { + boolean procMatch = false; + for (int iproc=0; iproc<NPROCS; iproc++) { + ProcessState proc = pkgState.mProcesses.valueAt(iproc); + if (reqPackage.equals(proc.getName())) { + procMatch = true; + break; + } + } + if (!procMatch) { + continue; + } + } + if (NPROCS > 0 || NSRVS > 0) { + if (!printedHeader) { + if (sepNeeded) pw.println(); + pw.println("Per-Package Stats:"); + printedHeader = true; + sepNeeded = true; + } + pw.print(" * "); pw.print(pkgName); pw.print(" / "); + UserHandle.formatUid(pw, uid); pw.print(" / v"); + pw.print(vers); pw.println(":"); + } + if (!dumpSummary || dumpAll) { + for (int iproc=0; iproc<NPROCS; iproc++) { + ProcessState proc = pkgState.mProcesses.valueAt(iproc); + if (!pkgMatch && !reqPackage.equals(proc.getName())) { + continue; + } + if (activeOnly && !proc.isInUse()) { + pw.print(" (Not active: "); + pw.print(pkgState.mProcesses.keyAt(iproc)); pw.println(")"); + continue; + } + pw.print(" Process "); + pw.print(pkgState.mProcesses.keyAt(iproc)); + if (proc.getCommonProcess().isMultiPackage()) { + pw.print(" (multi, "); + } else { + pw.print(" (unique, "); + } + pw.print(proc.getDurationsBucketCount()); + pw.print(" entries)"); + pw.println(":"); + proc.dumpProcessState(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, + ALL_PROC_STATES, now); + proc.dumpPss(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, + ALL_PROC_STATES); + proc.dumpInternalLocked(pw, " ", dumpAll); + } + } else { + ArrayList<ProcessState> procs = new ArrayList<ProcessState>(); + for (int iproc=0; iproc<NPROCS; iproc++) { + ProcessState proc = pkgState.mProcesses.valueAt(iproc); + if (!pkgMatch && !reqPackage.equals(proc.getName())) { + continue; + } + if (activeOnly && !proc.isInUse()) { + continue; + } + procs.add(proc); + } + DumpUtils.dumpProcessSummaryLocked(pw, " ", procs, + ALL_SCREEN_ADJ, ALL_MEM_ADJ, NON_CACHED_PROC_STATES, + now, totalTime); + } + for (int isvc=0; isvc<NSRVS; isvc++) { + ServiceState svc = pkgState.mServices.valueAt(isvc); + if (!pkgMatch && !reqPackage.equals(svc.getProcessName())) { + continue; + } + if (activeOnly && !svc.isInUse()) { + pw.print(" (Not active: "); + pw.print(pkgState.mServices.keyAt(isvc)); pw.println(")"); + continue; + } + if (dumpAll) { + pw.print(" Service "); + } else { + pw.print(" * "); + } + pw.print(pkgState.mServices.keyAt(isvc)); + pw.println(":"); + pw.print(" Process: "); pw.println(svc.getProcessName()); + svc.dumpStats(pw, " ", " ", " ", + now, totalTime, dumpSummary, dumpAll); + } + } + } + } + + ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); + printedHeader = false; + int numShownProcs = 0, numTotalProcs = 0; + for (int ip=0; ip<procMap.size(); ip++) { + String procName = procMap.keyAt(ip); + SparseArray<ProcessState> uids = procMap.valueAt(ip); + for (int iu=0; iu<uids.size(); iu++) { + int uid = uids.keyAt(iu); + numTotalProcs++; + final ProcessState proc = uids.valueAt(iu); + if (proc.hasAnyData()) { + continue; + } + if (!proc.isMultiPackage()) { + continue; + } + if (reqPackage != null && !reqPackage.equals(procName) + && !reqPackage.equals(proc.getPackage())) { + continue; + } + numShownProcs++; + if (sepNeeded) { + pw.println(); + } + sepNeeded = true; + if (!printedHeader) { + pw.println("Multi-Package Common Processes:"); + printedHeader = true; + } + if (activeOnly && !proc.isInUse()) { + pw.print(" (Not active: "); pw.print(procName); pw.println(")"); + continue; + } + pw.print(" * "); pw.print(procName); pw.print(" / "); + UserHandle.formatUid(pw, uid); + pw.print(" ("); pw.print(proc.getDurationsBucketCount()); + pw.print(" entries)"); pw.println(":"); + proc.dumpProcessState(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, + ALL_PROC_STATES, now); + proc.dumpPss(pw, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, ALL_PROC_STATES); + proc.dumpInternalLocked(pw, " ", dumpAll); + } + } + if (dumpAll) { + pw.println(); + pw.print(" Total procs: "); pw.print(numShownProcs); + pw.print(" shown of "); pw.print(numTotalProcs); pw.println(" total"); + } + + if (sepNeeded) { + pw.println(); + } + if (dumpSummary) { + pw.println("Summary:"); + dumpSummaryLocked(pw, reqPackage, now, activeOnly); + } else { + dumpTotalsLocked(pw, now); + } + + if (dumpAll) { + pw.println(); + pw.println("Internal state:"); + /* + pw.print(" Num long arrays: "); pw.println(mLongs.size()); + pw.print(" Next long entry: "); pw.println(mNextLong); + */ + pw.print(" mRunning="); pw.println(mRunning); + } + } + + public void dumpSummaryLocked(PrintWriter pw, String reqPackage, long now, boolean activeOnly) { + long totalTime = DumpUtils.dumpSingleTime(null, null, mMemFactorDurations, mMemFactor, + mStartTime, now); + dumpFilteredSummaryLocked(pw, null, " ", ALL_SCREEN_ADJ, ALL_MEM_ADJ, + ALL_PROC_STATES, NON_CACHED_PROC_STATES, now, totalTime, reqPackage, activeOnly); + pw.println(); + dumpTotalsLocked(pw, now); + } + + long printMemoryCategory(PrintWriter pw, String prefix, String label, double memWeight, + long totalTime, long curTotalMem, int samples) { + if (memWeight != 0) { + long mem = (long)(memWeight * 1024 / totalTime); + pw.print(prefix); + pw.print(label); + pw.print(": "); + DebugUtils.printSizeValue(pw, mem); + pw.print(" ("); + pw.print(samples); + pw.print(" samples)"); + pw.println(); + return curTotalMem + mem; + } + return curTotalMem; + } + + void dumpTotalsLocked(PrintWriter pw, long now) { + pw.println("Run time Stats:"); + DumpUtils.dumpSingleTime(pw, " ", mMemFactorDurations, mMemFactor, mStartTime, now); + pw.println(); + pw.println("Memory usage:"); + TotalMemoryUseCollection totalMem = new TotalMemoryUseCollection(ALL_SCREEN_ADJ, + ALL_MEM_ADJ); + computeTotalMemoryUse(totalMem, now); + long totalPss = 0; + totalPss = printMemoryCategory(pw, " ", "Kernel ", totalMem.sysMemKernelWeight, + totalMem.totalTime, totalPss, totalMem.sysMemSamples); + totalPss = printMemoryCategory(pw, " ", "Native ", totalMem.sysMemNativeWeight, + totalMem.totalTime, totalPss, totalMem.sysMemSamples); + for (int i=0; i<STATE_COUNT; i++) { + // Skip restarting service state -- that is not actually a running process. + if (i != STATE_SERVICE_RESTARTING) { + totalPss = printMemoryCategory(pw, " ", DumpUtils.STATE_NAMES[i], + totalMem.processStateWeight[i], totalMem.totalTime, totalPss, + totalMem.processStateSamples[i]); + } + } + totalPss = printMemoryCategory(pw, " ", "Cached ", totalMem.sysMemCachedWeight, + totalMem.totalTime, totalPss, totalMem.sysMemSamples); + totalPss = printMemoryCategory(pw, " ", "Free ", totalMem.sysMemFreeWeight, + totalMem.totalTime, totalPss, totalMem.sysMemSamples); + totalPss = printMemoryCategory(pw, " ", "Z-Ram ", totalMem.sysMemZRamWeight, + totalMem.totalTime, totalPss, totalMem.sysMemSamples); + pw.print(" TOTAL : "); + DebugUtils.printSizeValue(pw, totalPss); + pw.println(); + printMemoryCategory(pw, " ", DumpUtils.STATE_NAMES[STATE_SERVICE_RESTARTING], + totalMem.processStateWeight[STATE_SERVICE_RESTARTING], totalMem.totalTime, totalPss, + totalMem.processStateSamples[STATE_SERVICE_RESTARTING]); + pw.println(); + pw.print(" Start time: "); + pw.print(DateFormat.format("yyyy-MM-dd HH:mm:ss", mTimePeriodStartClock)); + pw.println(); + pw.print(" Total elapsed time: "); + TimeUtils.formatDuration( + (mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime) + - mTimePeriodStartRealtime, pw); + boolean partial = true; + if ((mFlags&FLAG_SHUTDOWN) != 0) { + pw.print(" (shutdown)"); + partial = false; + } + if ((mFlags&FLAG_SYSPROPS) != 0) { + pw.print(" (sysprops)"); + partial = false; + } + if ((mFlags&FLAG_COMPLETE) != 0) { + pw.print(" (complete)"); + partial = false; + } + if (partial) { + pw.print(" (partial)"); + } + pw.print(' '); + pw.print(mRuntime); + pw.println(); + } + + void dumpFilteredSummaryLocked(PrintWriter pw, String header, String prefix, + int[] screenStates, int[] memStates, int[] procStates, + int[] sortProcStates, long now, long totalTime, String reqPackage, boolean activeOnly) { + ArrayList<ProcessState> procs = collectProcessesLocked(screenStates, memStates, + procStates, sortProcStates, now, reqPackage, activeOnly); + if (procs.size() > 0) { + if (header != null) { + pw.println(); + pw.println(header); + } + DumpUtils.dumpProcessSummaryLocked(pw, prefix, procs, screenStates, memStates, + sortProcStates, now, totalTime); + } + } + + public ArrayList<ProcessState> collectProcessesLocked(int[] screenStates, int[] memStates, + int[] procStates, int sortProcStates[], long now, String reqPackage, + boolean activeOnly) { + final ArraySet<ProcessState> foundProcs = new ArraySet<ProcessState>(); + final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap(); + for (int ip=0; ip<pkgMap.size(); ip++) { + final String pkgName = pkgMap.keyAt(ip); + final SparseArray<SparseArray<PackageState>> procs = pkgMap.valueAt(ip); + for (int iu=0; iu<procs.size(); iu++) { + final SparseArray<PackageState> vpkgs = procs.valueAt(iu); + final int NVERS = vpkgs.size(); + for (int iv=0; iv<NVERS; iv++) { + final PackageState state = vpkgs.valueAt(iv); + final int NPROCS = state.mProcesses.size(); + final boolean pkgMatch = reqPackage == null || reqPackage.equals(pkgName); + for (int iproc=0; iproc<NPROCS; iproc++) { + final ProcessState proc = state.mProcesses.valueAt(iproc); + if (!pkgMatch && !reqPackage.equals(proc.getName())) { + continue; + } + if (activeOnly && !proc.isInUse()) { + continue; + } + foundProcs.add(proc.getCommonProcess()); + } + } + } + } + ArrayList<ProcessState> outProcs = new ArrayList<ProcessState>(foundProcs.size()); + for (int i=0; i<foundProcs.size(); i++) { + ProcessState proc = foundProcs.valueAt(i); + if (proc.computeProcessTimeLocked(screenStates, memStates, procStates, now) > 0) { + outProcs.add(proc); + if (procStates != sortProcStates) { + proc.computeProcessTimeLocked(screenStates, memStates, sortProcStates, now); + } + } + } + Collections.sort(outProcs, ProcessState.COMPARATOR); + return outProcs; + } + + public void dumpCheckinLocked(PrintWriter pw, String reqPackage) { + final long now = SystemClock.uptimeMillis(); + final ArrayMap<String, SparseArray<SparseArray<PackageState>>> pkgMap = mPackages.getMap(); + pw.println("vers,5"); + pw.print("period,"); pw.print(mTimePeriodStartClockStr); + pw.print(","); pw.print(mTimePeriodStartRealtime); pw.print(","); + pw.print(mRunning ? SystemClock.elapsedRealtime() : mTimePeriodEndRealtime); + boolean partial = true; + if ((mFlags&FLAG_SHUTDOWN) != 0) { + pw.print(",shutdown"); + partial = false; + } + if ((mFlags&FLAG_SYSPROPS) != 0) { + pw.print(",sysprops"); + partial = false; + } + if ((mFlags&FLAG_COMPLETE) != 0) { + pw.print(",complete"); + partial = false; + } + if (partial) { + pw.print(",partial"); + } + pw.println(); + pw.print("config,"); pw.println(mRuntime); + for (int ip=0; ip<pkgMap.size(); ip++) { + final String pkgName = pkgMap.keyAt(ip); + if (reqPackage != null && !reqPackage.equals(pkgName)) { + continue; + } + final SparseArray<SparseArray<PackageState>> uids = pkgMap.valueAt(ip); + for (int iu=0; iu<uids.size(); iu++) { + final int uid = uids.keyAt(iu); + final SparseArray<PackageState> vpkgs = uids.valueAt(iu); + for (int iv=0; iv<vpkgs.size(); iv++) { + final int vers = vpkgs.keyAt(iv); + final PackageState pkgState = vpkgs.valueAt(iv); + final int NPROCS = pkgState.mProcesses.size(); + final int NSRVS = pkgState.mServices.size(); + for (int iproc=0; iproc<NPROCS; iproc++) { + ProcessState proc = pkgState.mProcesses.valueAt(iproc); + proc.dumpPackageProcCheckin(pw, pkgName, uid, vers, + pkgState.mProcesses.keyAt(iproc), now); + } + for (int isvc=0; isvc<NSRVS; isvc++) { + final String serviceName = DumpUtils.collapseString(pkgName, + pkgState.mServices.keyAt(isvc)); + final ServiceState svc = pkgState.mServices.valueAt(isvc); + svc.dumpTimesCheckin(pw, pkgName, uid, vers, serviceName, now); + } + } + } + } + + ArrayMap<String, SparseArray<ProcessState>> procMap = mProcesses.getMap(); + for (int ip=0; ip<procMap.size(); ip++) { + String procName = procMap.keyAt(ip); + SparseArray<ProcessState> uids = procMap.valueAt(ip); + for (int iu=0; iu<uids.size(); iu++) { + final int uid = uids.keyAt(iu); + final ProcessState procState = uids.valueAt(iu); + procState.dumpProcCheckin(pw, procName, uid, now); + } + } + pw.print("total"); + DumpUtils.dumpAdjTimesCheckin(pw, ",", mMemFactorDurations, mMemFactor, mStartTime, now); + pw.println(); + final int sysMemUsageCount = mSysMemUsage.getKeyCount(); + if (sysMemUsageCount > 0) { + pw.print("sysmemusage"); + for (int i=0; i<sysMemUsageCount; i++) { + final int key = mSysMemUsage.getKeyAt(i); + final int type = SparseMappingTable.getIdFromKey(key); + pw.print(","); + DumpUtils.printProcStateTag(pw, type); + for (int j=SYS_MEM_USAGE_SAMPLE_COUNT; j<SYS_MEM_USAGE_COUNT; j++) { + if (j > SYS_MEM_USAGE_CACHED_MINIMUM) { + pw.print(":"); + } + pw.print(mSysMemUsage.getValue(key, j)); + } + } + } + pw.println(); + TotalMemoryUseCollection totalMem = new TotalMemoryUseCollection(ALL_SCREEN_ADJ, + ALL_MEM_ADJ); + computeTotalMemoryUse(totalMem, now); + pw.print("weights,"); + pw.print(totalMem.totalTime); + pw.print(","); + pw.print(totalMem.sysMemCachedWeight); + pw.print(":"); + pw.print(totalMem.sysMemSamples); + pw.print(","); + pw.print(totalMem.sysMemFreeWeight); + pw.print(":"); + pw.print(totalMem.sysMemSamples); + pw.print(","); + pw.print(totalMem.sysMemZRamWeight); + pw.print(":"); + pw.print(totalMem.sysMemSamples); + pw.print(","); + pw.print(totalMem.sysMemKernelWeight); + pw.print(":"); + pw.print(totalMem.sysMemSamples); + pw.print(","); + pw.print(totalMem.sysMemNativeWeight); + pw.print(":"); + pw.print(totalMem.sysMemSamples); + for (int i=0; i<STATE_COUNT; i++) { + pw.print(","); + pw.print(totalMem.processStateWeight[i]); + pw.print(":"); + pw.print(totalMem.processStateSamples[i]); + } + pw.println(); + } + + + final public static class ProcessStateHolder { + public final int appVersion; + public ProcessState state; + + public ProcessStateHolder(int _appVersion) { + appVersion = _appVersion; + } + } + + public static final class PackageState { + public final ArrayMap<String, ProcessState> mProcesses + = new ArrayMap<String, ProcessState>(); + public final ArrayMap<String, ServiceState> mServices + = new ArrayMap<String, ServiceState>(); + public final String mPackageName; + public final int mUid; + + public PackageState(String packageName, int uid) { + mUid = uid; + mPackageName = packageName; + } + } + + public static final class ProcessDataCollection { + final int[] screenStates; + final int[] memStates; + final int[] procStates; + + public long totalTime; + public long numPss; + public long minPss; + public long avgPss; + public long maxPss; + public long minUss; + public long avgUss; + public long maxUss; + + public ProcessDataCollection(int[] _screenStates, int[] _memStates, int[] _procStates) { + screenStates = _screenStates; + memStates = _memStates; + procStates = _procStates; + } + + void print(PrintWriter pw, long overallTime, boolean full) { + if (totalTime > overallTime) { + pw.print("*"); + } + DumpUtils.printPercent(pw, (double) totalTime / (double) overallTime); + if (numPss > 0) { + pw.print(" ("); + DebugUtils.printSizeValue(pw, minPss * 1024); + pw.print("-"); + DebugUtils.printSizeValue(pw, avgPss * 1024); + pw.print("-"); + DebugUtils.printSizeValue(pw, maxPss * 1024); + pw.print("/"); + DebugUtils.printSizeValue(pw, minUss * 1024); + pw.print("-"); + DebugUtils.printSizeValue(pw, avgUss * 1024); + pw.print("-"); + DebugUtils.printSizeValue(pw, maxUss * 1024); + if (full) { + pw.print(" over "); + pw.print(numPss); + } + pw.print(")"); + } + } + } + + public static class TotalMemoryUseCollection { + final int[] screenStates; + final int[] memStates; + + public TotalMemoryUseCollection(int[] _screenStates, int[] _memStates) { + screenStates = _screenStates; + memStates = _memStates; + } + + public long totalTime; + public long[] processStatePss = new long[STATE_COUNT]; + public double[] processStateWeight = new double[STATE_COUNT]; + public long[] processStateTime = new long[STATE_COUNT]; + public int[] processStateSamples = new int[STATE_COUNT]; + public long[] sysMemUsage = new long[SYS_MEM_USAGE_COUNT]; + public double sysMemCachedWeight; + public double sysMemFreeWeight; + public double sysMemZRamWeight; + public double sysMemKernelWeight; + public double sysMemNativeWeight; + public int sysMemSamples; + } + +} diff --git a/core/java/com/android/internal/app/procstats/PssTable.java b/core/java/com/android/internal/app/procstats/PssTable.java new file mode 100644 index 000000000000..b6df983d2ac8 --- /dev/null +++ b/core/java/com/android/internal/app/procstats/PssTable.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2013 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.internal.app.procstats; + +import static com.android.internal.app.procstats.ProcessStats.PSS_SAMPLE_COUNT; +import static com.android.internal.app.procstats.ProcessStats.PSS_MINIMUM; +import static com.android.internal.app.procstats.ProcessStats.PSS_AVERAGE; +import static com.android.internal.app.procstats.ProcessStats.PSS_MAXIMUM; +import static com.android.internal.app.procstats.ProcessStats.PSS_USS_MINIMUM; +import static com.android.internal.app.procstats.ProcessStats.PSS_USS_AVERAGE; +import static com.android.internal.app.procstats.ProcessStats.PSS_USS_MAXIMUM; +import static com.android.internal.app.procstats.ProcessStats.PSS_COUNT; + +/** + * Class to accumulate PSS data. + */ +public class PssTable extends SparseMappingTable.Table { + /** + * Construct the PssTable with 'tableData' as backing store + * for the longs data. + */ + public PssTable(SparseMappingTable tableData) { + super(tableData); + } + + /** + * Merge the the values from the other table into this one. + */ + public void mergeStats(PssTable that) { + final int N = that.getKeyCount(); + for (int i=0; i<N; i++) { + final int key = that.getKeyAt(i); + final int state = SparseMappingTable.getIdFromKey(key); + mergeStats(state, (int)that.getValue(key, PSS_SAMPLE_COUNT), + that.getValue(key, PSS_MINIMUM), + that.getValue(key, PSS_AVERAGE), + that.getValue(key, PSS_MAXIMUM), + that.getValue(key, PSS_USS_MINIMUM), + that.getValue(key, PSS_USS_AVERAGE), + that.getValue(key, PSS_USS_MAXIMUM)); + } + } + + /** + * Merge the supplied PSS data in. The new min pss will be the minimum of the existing + * one and the new one, the average will now incorporate the new average, etc. + */ + public void mergeStats(int state, int inCount, long minPss, long avgPss, long maxPss, + long minUss, long avgUss, long maxUss) { + final int key = getOrAddKey((byte)state, PSS_COUNT); + final long count = getValue(key, PSS_SAMPLE_COUNT); + if (count == 0) { + setValue(key, PSS_SAMPLE_COUNT, inCount); + setValue(key, PSS_MINIMUM, minPss); + setValue(key, PSS_AVERAGE, avgPss); + setValue(key, PSS_MAXIMUM, maxPss); + setValue(key, PSS_USS_MINIMUM, minUss); + setValue(key, PSS_USS_AVERAGE, avgUss); + setValue(key, PSS_USS_MAXIMUM, maxUss); + } else { + setValue(key, PSS_SAMPLE_COUNT, count + inCount); + + long val; + + val = getValue(key, PSS_MINIMUM); + if (val > minPss) { + setValue(key, PSS_MINIMUM, minPss); + } + + val = getValue(key, PSS_AVERAGE); + setValue(key, PSS_AVERAGE, + (long)(((val*(double)count)+(avgPss*(double)inCount)) / (count+inCount))); + + val = getValue(key, PSS_MAXIMUM); + if (val < maxPss) { + setValue(key, PSS_MAXIMUM, maxPss); + } + + val = getValue(key, PSS_USS_MINIMUM); + if (val > minUss) { + setValue(key, PSS_USS_MINIMUM, minUss); + } + + val = getValue(key, PSS_USS_AVERAGE); + setValue(key, PSS_AVERAGE, + (long)(((val*(double)count)+(avgUss*(double)inCount)) / (count+inCount))); + + val = getValue(key, PSS_USS_MAXIMUM); + if (val < maxUss) { + setValue(key, PSS_USS_MAXIMUM, maxUss); + } + } + } +} diff --git a/core/java/com/android/internal/app/procstats/ServiceState.java b/core/java/com/android/internal/app/procstats/ServiceState.java new file mode 100644 index 000000000000..2e11c43872f6 --- /dev/null +++ b/core/java/com/android/internal/app/procstats/ServiceState.java @@ -0,0 +1,502 @@ +/* + * Copyright (C) 2013 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.internal.app.procstats; + + +import android.os.Parcel; +import android.os.Parcelable; +import android.os.SystemClock; +import android.os.SystemProperties; +import android.os.UserHandle; +import android.text.format.DateFormat; +import android.util.ArrayMap; +import android.util.ArraySet; +import android.util.DebugUtils; +import android.util.Log; +import android.util.Slog; +import android.util.SparseArray; +import android.util.TimeUtils; + +import com.android.internal.app.procstats.ProcessStats; +import static com.android.internal.app.procstats.ProcessStats.STATE_NOTHING; + +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Comparator; +import java.util.Objects; + +public final class ServiceState { + private static final String TAG = "ProcessStats"; + private static final boolean DEBUG = false; + + public static final int SERVICE_RUN = 0; + public static final int SERVICE_STARTED = 1; + public static final int SERVICE_BOUND = 2; + public static final int SERVICE_EXEC = 3; + public static final int SERVICE_COUNT = 4; + + private final String mPackage; + private final String mProcessName; + private final String mName; + private final DurationsTable mDurations; + + private ProcessState mProc; + private Object mOwner; + + private int mRunCount; + private int mRunState = STATE_NOTHING; + private long mRunStartTime; + + private boolean mStarted; + private boolean mRestarting; + private int mStartedCount; + private int mStartedState = STATE_NOTHING; + private long mStartedStartTime; + + private int mBoundCount; + private int mBoundState = STATE_NOTHING; + private long mBoundStartTime; + + private int mExecCount; + private int mExecState = STATE_NOTHING; + private long mExecStartTime; + + public ServiceState(ProcessStats processStats, String pkg, String name, + String processName, ProcessState proc) { + mPackage = pkg; + mName = name; + mProcessName = processName; + mProc = proc; + mDurations = new DurationsTable(processStats.mTableData); + } + + public String getPackage() { + return mPackage; + } + + public String getProcessName() { + return mProcessName; + } + + public String getName() { + return mName; + } + + public ProcessState getProcess() { + return mProc; + } + + public void setProcess(ProcessState proc) { + mProc = proc; + } + + public void setMemFactor(int memFactor, long now) { + if (isRestarting()) { + setRestarting(true, memFactor, now); + } else if (isInUse()) { + if (mStartedState != ProcessStats.STATE_NOTHING) { + setStarted(true, memFactor, now); + } + if (mBoundState != ProcessStats.STATE_NOTHING) { + setBound(true, memFactor, now); + } + if (mExecState != ProcessStats.STATE_NOTHING) { + setExecuting(true, memFactor, now); + } + } + } + + public void applyNewOwner(Object newOwner) { + if (mOwner != newOwner) { + if (mOwner == null) { + mOwner = newOwner; + mProc.incActiveServices(mName); + } else { + // There was already an old owner, reset this object for its + // new owner. + mOwner = newOwner; + if (mStarted || mBoundState != STATE_NOTHING || mExecState != STATE_NOTHING) { + long now = SystemClock.uptimeMillis(); + if (mStarted) { + if (DEBUG) Slog.d(TAG, "Service has new owner " + newOwner + + " from " + mOwner + " while started: pkg=" + + mPackage + " service=" + mName + " proc=" + mProc); + setStarted(false, 0, now); + } + if (mBoundState != STATE_NOTHING) { + if (DEBUG) Slog.d(TAG, "Service has new owner " + newOwner + + " from " + mOwner + " while bound: pkg=" + + mPackage + " service=" + mName + " proc=" + mProc); + setBound(false, 0, now); + } + if (mExecState != STATE_NOTHING) { + if (DEBUG) Slog.d(TAG, "Service has new owner " + newOwner + + " from " + mOwner + " while executing: pkg=" + + mPackage + " service=" + mName + " proc=" + mProc); + setExecuting(false, 0, now); + } + } + } + } + } + + public void clearCurrentOwner(Object owner, boolean silently) { + if (mOwner == owner) { + mProc.decActiveServices(mName); + if (mStarted || mBoundState != STATE_NOTHING || mExecState != STATE_NOTHING) { + long now = SystemClock.uptimeMillis(); + if (mStarted) { + if (!silently) { + Slog.wtfStack(TAG, "Service owner " + owner + + " cleared while started: pkg=" + mPackage + " service=" + + mName + " proc=" + mProc); + } + setStarted(false, 0, now); + } + if (mBoundState != STATE_NOTHING) { + if (!silently) { + Slog.wtfStack(TAG, "Service owner " + owner + + " cleared while bound: pkg=" + mPackage + " service=" + + mName + " proc=" + mProc); + } + setBound(false, 0, now); + } + if (mExecState != STATE_NOTHING) { + if (!silently) { + Slog.wtfStack(TAG, "Service owner " + owner + + " cleared while exec: pkg=" + mPackage + " service=" + + mName + " proc=" + mProc); + } + setExecuting(false, 0, now); + } + } + mOwner = null; + } + } + + public boolean isInUse() { + return mOwner != null || mRestarting; + } + + public boolean isRestarting() { + return mRestarting; + } + + public void add(ServiceState other) { + mDurations.addDurations(other.mDurations); + mRunCount += other.mRunCount; + mStartedCount += other.mStartedCount; + mBoundCount += other.mBoundCount; + mExecCount += other.mExecCount; + } + + public void resetSafely(long now) { + mDurations.resetTable(); + mRunCount = mRunState != STATE_NOTHING ? 1 : 0; + mStartedCount = mStartedState != STATE_NOTHING ? 1 : 0; + mBoundCount = mBoundState != STATE_NOTHING ? 1 : 0; + mExecCount = mExecState != STATE_NOTHING ? 1 : 0; + mRunStartTime = mStartedStartTime = mBoundStartTime = mExecStartTime = now; + } + + public void writeToParcel(Parcel out, long now) { + mDurations.writeToParcel(out); + out.writeInt(mRunCount); + out.writeInt(mStartedCount); + out.writeInt(mBoundCount); + out.writeInt(mExecCount); + } + + public boolean readFromParcel(Parcel in) { + if (!mDurations.readFromParcel(in)) { + return false; + } + mRunCount = in.readInt(); + mStartedCount = in.readInt(); + mBoundCount = in.readInt(); + mExecCount = in.readInt(); + return true; + } + + public void commitStateTime(long now) { + if (mRunState != STATE_NOTHING) { + mDurations.addDuration(SERVICE_RUN + (mRunState*SERVICE_COUNT), + now - mRunStartTime); + mRunStartTime = now; + } + if (mStartedState != STATE_NOTHING) { + mDurations.addDuration(SERVICE_STARTED + (mStartedState*SERVICE_COUNT), + now - mStartedStartTime); + mStartedStartTime = now; + } + if (mBoundState != STATE_NOTHING) { + mDurations.addDuration(SERVICE_BOUND + (mBoundState*SERVICE_COUNT), + now - mBoundStartTime); + mBoundStartTime = now; + } + if (mExecState != STATE_NOTHING) { + mDurations.addDuration(SERVICE_EXEC + (mExecState*SERVICE_COUNT), + now - mExecStartTime); + mExecStartTime = now; + } + } + + private void updateRunning(int memFactor, long now) { + final int state = (mStartedState != STATE_NOTHING || mBoundState != STATE_NOTHING + || mExecState != STATE_NOTHING) ? memFactor : STATE_NOTHING; + if (mRunState != state) { + if (mRunState != STATE_NOTHING) { + mDurations.addDuration(SERVICE_RUN + (mRunState*SERVICE_COUNT), + now - mRunStartTime); + } else if (state != STATE_NOTHING) { + mRunCount++; + } + mRunState = state; + mRunStartTime = now; + } + } + + public void setStarted(boolean started, int memFactor, long now) { + if (mOwner == null) { + Slog.wtf(TAG, "Starting service " + this + " without owner"); + } + mStarted = started; + updateStartedState(memFactor, now); + } + + public void setRestarting(boolean restarting, int memFactor, long now) { + mRestarting = restarting; + updateStartedState(memFactor, now); + } + + public void updateStartedState(int memFactor, long now) { + final boolean wasStarted = mStartedState != STATE_NOTHING; + final boolean started = mStarted || mRestarting; + final int state = started ? memFactor : STATE_NOTHING; + if (mStartedState != state) { + if (mStartedState != STATE_NOTHING) { + mDurations.addDuration(SERVICE_STARTED + (mStartedState*SERVICE_COUNT), + now - mStartedStartTime); + } else if (started) { + mStartedCount++; + } + mStartedState = state; + mStartedStartTime = now; + mProc = mProc.pullFixedProc(mPackage); + if (wasStarted != started) { + if (started) { + mProc.incStartedServices(memFactor, now, mName); + } else { + mProc.decStartedServices(memFactor, now, mName); + } + } + updateRunning(memFactor, now); + } + } + + public void setBound(boolean bound, int memFactor, long now) { + if (mOwner == null) { + Slog.wtf(TAG, "Binding service " + this + " without owner"); + } + final int state = bound ? memFactor : STATE_NOTHING; + if (mBoundState != state) { + if (mBoundState != STATE_NOTHING) { + mDurations.addDuration(SERVICE_BOUND + (mBoundState*SERVICE_COUNT), + now - mBoundStartTime); + } else if (bound) { + mBoundCount++; + } + mBoundState = state; + mBoundStartTime = now; + updateRunning(memFactor, now); + } + } + + public void setExecuting(boolean executing, int memFactor, long now) { + if (mOwner == null) { + Slog.wtf(TAG, "Executing service " + this + " without owner"); + } + final int state = executing ? memFactor : STATE_NOTHING; + if (mExecState != state) { + if (mExecState != STATE_NOTHING) { + mDurations.addDuration(SERVICE_EXEC + (mExecState*SERVICE_COUNT), + now - mExecStartTime); + } else if (executing) { + mExecCount++; + } + mExecState = state; + mExecStartTime = now; + updateRunning(memFactor, now); + } + } + + public long getDuration(int opType, int curState, long startTime, int memFactor, + long now) { + int state = opType + (memFactor*SERVICE_COUNT); + long time = mDurations.getValueForId((byte)state); + if (curState == memFactor) { + time += now - startTime; + } + return time; + } + + public void dumpStats(PrintWriter pw, String prefix, String prefixInner, String headerPrefix, + long now, long totalTime, boolean dumpSummary, boolean dumpAll) { + dumpStats(pw, prefix, prefixInner, headerPrefix, "Running", + mRunCount, ServiceState.SERVICE_RUN, mRunState, + mRunStartTime, now, totalTime, !dumpSummary || dumpAll); + dumpStats(pw, prefix, prefixInner, headerPrefix, "Started", + mStartedCount, ServiceState.SERVICE_STARTED, mStartedState, + mStartedStartTime, now, totalTime, !dumpSummary || dumpAll); + dumpStats(pw, prefix, prefixInner, headerPrefix, "Bound", + mBoundCount, ServiceState.SERVICE_BOUND, mBoundState, + mBoundStartTime, now, totalTime, !dumpSummary || dumpAll); + dumpStats(pw, prefix, prefixInner, headerPrefix, "Executing", + mExecCount, ServiceState.SERVICE_EXEC, mExecState, + mExecStartTime, now, totalTime, !dumpSummary || dumpAll); + if (dumpAll) { + if (mOwner != null) { + pw.print(" mOwner="); pw.println(mOwner); + } + if (mStarted || mRestarting) { + pw.print(" mStarted="); pw.print(mStarted); + pw.print(" mRestarting="); pw.println(mRestarting); + } + } + } + + private void dumpStats(PrintWriter pw, String prefix, String prefixInner, + String headerPrefix, String header, + int count, int serviceType, int state, long startTime, long now, long totalTime, + boolean dumpAll) { + if (count != 0) { + if (dumpAll) { + pw.print(prefix); pw.print(header); + pw.print(" op count "); pw.print(count); pw.println(":"); + dumpTime(pw, prefixInner, serviceType, state, startTime, now); + } else { + long myTime = dumpTime(null, null, serviceType, state, startTime, now); + pw.print(prefix); pw.print(headerPrefix); pw.print(header); + pw.print(" count "); pw.print(count); + pw.print(" / time "); + DumpUtils.printPercent(pw, (double)myTime/(double)totalTime); + pw.println(); + } + } + } + + public long dumpTime(PrintWriter pw, String prefix, + int serviceType, int curState, long curStartTime, long now) { + long totalTime = 0; + int printedScreen = -1; + for (int iscreen=0; iscreen<ProcessStats.ADJ_COUNT; iscreen+=ProcessStats.ADJ_SCREEN_MOD) { + int printedMem = -1; + for (int imem=0; imem<ProcessStats.ADJ_MEM_FACTOR_COUNT; imem++) { + int state = imem+iscreen; + long time = getDuration(serviceType, curState, curStartTime, state, now); + String running = ""; + if (curState == state && pw != null) { + running = " (running)"; + } + if (time != 0) { + if (pw != null) { + pw.print(prefix); + DumpUtils.printScreenLabel(pw, printedScreen != iscreen + ? iscreen : STATE_NOTHING); + printedScreen = iscreen; + DumpUtils.printMemLabel(pw, printedMem != imem ? imem : STATE_NOTHING, + (char)0); + printedMem = imem; + pw.print(": "); + TimeUtils.formatDuration(time, pw); pw.println(running); + } + totalTime += time; + } + } + } + if (totalTime != 0 && pw != null) { + pw.print(prefix); + pw.print(" TOTAL: "); + TimeUtils.formatDuration(totalTime, pw); + pw.println(); + } + return totalTime; + } + + public void dumpTimesCheckin(PrintWriter pw, String pkgName, int uid, int vers, + String serviceName, long now) { + dumpTimeCheckin(pw, "pkgsvc-run", pkgName, uid, vers, serviceName, + ServiceState.SERVICE_RUN, mRunCount, mRunState, mRunStartTime, now); + dumpTimeCheckin(pw, "pkgsvc-start", pkgName, uid, vers, serviceName, + ServiceState.SERVICE_STARTED, mStartedCount, mStartedState, mStartedStartTime, now); + dumpTimeCheckin(pw, "pkgsvc-bound", pkgName, uid, vers, serviceName, + ServiceState.SERVICE_BOUND, mBoundCount, mBoundState, mBoundStartTime, now); + dumpTimeCheckin(pw, "pkgsvc-exec", pkgName, uid, vers, serviceName, + ServiceState.SERVICE_EXEC, mExecCount, mExecState, mExecStartTime, now); + } + + private void dumpTimeCheckin(PrintWriter pw, String label, String packageName, + int uid, int vers, String serviceName, int serviceType, int opCount, + int curState, long curStartTime, long now) { + if (opCount <= 0) { + return; + } + pw.print(label); + pw.print(","); + pw.print(packageName); + pw.print(","); + pw.print(uid); + pw.print(","); + pw.print(vers); + pw.print(","); + pw.print(serviceName); + pw.print(","); + pw.print(opCount); + boolean didCurState = false; + final int N = mDurations.getKeyCount(); + for (int i=0; i<N; i++) { + final int key = mDurations.getKeyAt(i); + long time = mDurations.getValue(key); + int type = SparseMappingTable.getIdFromKey(key); + int memFactor = type / ServiceState.SERVICE_COUNT; + type %= ServiceState.SERVICE_COUNT; + if (type != serviceType) { + continue; + } + if (curState == memFactor) { + didCurState = true; + time += now - curStartTime; + } + DumpUtils.printAdjTagAndValue(pw, memFactor, time); + } + if (!didCurState && curState != STATE_NOTHING) { + DumpUtils.printAdjTagAndValue(pw, curState, now - curStartTime); + } + pw.println(); + } + + + public String toString() { + return "ServiceState{" + Integer.toHexString(System.identityHashCode(this)) + + " " + mName + " pkg=" + mPackage + " proc=" + + Integer.toHexString(System.identityHashCode(this)) + "}"; + } +} diff --git a/core/java/com/android/internal/app/procstats/SparseMappingTable.java b/core/java/com/android/internal/app/procstats/SparseMappingTable.java new file mode 100644 index 000000000000..64c49a2bc80d --- /dev/null +++ b/core/java/com/android/internal/app/procstats/SparseMappingTable.java @@ -0,0 +1,627 @@ +/* + * Copyright (C) 2013 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.internal.app.procstats; + +import android.os.Build; +import android.os.Parcel; +import android.util.Slog; +import libcore.util.EmptyArray; + +import java.util.ArrayList; +import java.util.Arrays; + +import com.android.internal.util.GrowingArrayUtils; + +/** + * Class that contains a set of tables mapping byte ids to long values. + * + * This class is used to store the ProcessStats data. This data happens to be + * a set of very sparse tables, that is mostly append or overwrite, with infrequent + * resets of the data. + * + * Data is stored as a list of large long[] arrays containing the actual values. There are a + * set of Table objects that each contain a small array mapping the byte IDs to a position + * in the larger arrays. + * + * The data itself is either a single long value or a range of long values which are always + * stored continguously in one of the long arrays. When the caller allocates a slot with + * getOrAddKey, an int key is returned. That key can be re-retreived with getKey without + * allocating the value. The data can then be set or retrieved with that key. + */ +public class SparseMappingTable { + private static final String TAG = "SparseMappingTable"; + + // How big each array is. + public static final int ARRAY_SIZE = 4096; + + public static final int INVALID_KEY = 0xffffffff; + + // Where the "type"/"state" part of the data appears in an offset integer. + private static final int ID_SHIFT = 0; + private static final int ID_MASK = 0xff; + // Where the "which array" part of the data appears in an offset integer. + private static final int ARRAY_SHIFT = 8; + private static final int ARRAY_MASK = 0xff; + // Where the "index into array" part of the data appears in an offset integer. + private static final int INDEX_SHIFT = 16; + private static final int INDEX_MASK = 0xffff; + + private int mSequence; + private int mNextIndex; + private final ArrayList<long[]> mLongs = new ArrayList<long[]>(); + + /** + * A table of data as stored in a SparseMappingTable. + */ + public static class Table { + // When mSequence is this this our data better be empty + private static final int UNINITIALIZED_SEQUENCE = -1; + + private SparseMappingTable mParent; + private int mSequence = UNINITIALIZED_SEQUENCE; + private int[] mTable; + private int mSize; + + public Table(SparseMappingTable parent) { + mParent = parent; + mSequence = parent.mSequence; + } + + /** + * Pulls the data from 'copyFrom' and stores it in our own longs table. + * + * @param copyFrom The Table to copy from + * @param valueCount The number of values to copy for each key + */ + public void copyFrom(Table copyFrom, int valueCount) { + mTable = null; + mSize = 0; + + final int N = copyFrom.getKeyCount(); + for (int i=0; i<N; i++) { + final int theirKey = copyFrom.getKeyAt(i); + final long[] theirLongs = copyFrom.mParent.mLongs.get(getArrayFromKey(theirKey)); + + final byte id = SparseMappingTable.getIdFromKey(theirKey); + + final int myKey = this.getOrAddKey((byte)id, valueCount); + final long[] myLongs = mParent.mLongs.get(getArrayFromKey(myKey)); + + System.arraycopy(theirLongs, getIndexFromKey(theirKey), + myLongs, getIndexFromKey(myKey), valueCount); + } + } + + /** + * Allocates data in the buffer, and stores that key in the mapping for this + * table. + * + * @param id The id of the item (will be used in making the key) + * @param count The number of bytes to allocate. Must be less than + * SparseMappingTable.ARRAY_SIZE. + * + * @return The 'key' for this data value, which contains both the id itself + * and the location in the long arrays that the data is actually stored + * but should be considered opaque to the caller. + */ + public int getOrAddKey(byte id, int count) { + // This is the only place we add data to mParent.mLongs, so this is the time + // to update our sequence to match there. + if (mSequence == UNINITIALIZED_SEQUENCE) { + mSequence = mParent.mSequence; + } + + assertConsistency(); + + final int idx = binarySearch(id); + if (idx >= 0) { + // Found + return mTable[idx]; + } else { + // Not found. Need to allocate it. + + // Get an array with enough space to store 'count' values. + final ArrayList<long[]> list = mParent.mLongs; + int whichArray = list.size()-1; + long[] array = list.get(whichArray); + if (mParent.mNextIndex + count > array.length) { + // if it won't fit then make a new array. + array = new long[ARRAY_SIZE]; + list.add(array); + whichArray++; + mParent.mNextIndex = 0; + } + + // The key is a combination of whichArray, which index in that array, and + // the table value itself, which will be used for lookup + final int key = (whichArray << ARRAY_SHIFT) + | (mParent.mNextIndex << INDEX_SHIFT) + | (((int)id) << ID_SHIFT); + + mParent.mNextIndex += count; + + // Store the key in the sparse lookup table for this Table object. + mTable = GrowingArrayUtils.insert(mTable != null ? mTable : EmptyArray.INT, + mSize, ~idx, key); + mSize++; + + return key; + } + } + + /** + * Looks up a key in the table. + * + * @return The key from this table or INVALID_KEY if the id is not found. + */ + public int getKey(byte id) { + assertConsistency(); + + final int idx = binarySearch(id); + if (idx >= 0) { + return mTable[idx]; + } else { + return INVALID_KEY; + } + } + + /** + * Get the value for the given key and offset from that key. + * + * @param key A key as obtained from getKey or getOrAddKey. + * @param value The value to set. + */ + public long getValue(int key) { + return getValue(key, 0); + } + + /** + * Get the value for the given key and offset from that key. + * + * @param key A key as obtained from getKey or getOrAddKey. + * @param index The offset from that key. Must be less than the count + * provided to getOrAddKey when the space was allocated. + * @param value The value to set. + * + * @return the value, or 0 in case of an error + */ + public long getValue(int key, int index) { + assertConsistency(); + + try { + final long[] array = mParent.mLongs.get(getArrayFromKey(key)); + return array[getIndexFromKey(key) + index]; + } catch (IndexOutOfBoundsException ex) { + logOrThrow("key=0x" + Integer.toHexString(key) + + " index=" + index + " -- " + dumpInternalState(), ex); + return 0; + } + } + + /** + * Set the value for the given id at offset 0 from that id. + * If the id is not found, return 0 instead. + * + * @param id The id of the item. + */ + public long getValueForId(byte id) { + return getValueForId(id, 0); + } + + /** + * Set the value for the given id and index offset from that id. + * If the id is not found, return 0 instead. + * + * @param id The id of the item. + * @param index The offset from that key. Must be less than the count + * provided to getOrAddKey when the space was allocated. + */ + public long getValueForId(byte id, int index) { + assertConsistency(); + + final int idx = binarySearch(id); + if (idx >= 0) { + final int key = mTable[idx]; + try { + final long[] array = mParent.mLongs.get(getArrayFromKey(key)); + return array[getIndexFromKey(key) + index]; + } catch (IndexOutOfBoundsException ex) { + logOrThrow("id=0x" + Integer.toHexString(id) + " idx=" + idx + + " key=0x" + Integer.toHexString(key) + " index=" + index + + " -- " + dumpInternalState(), ex); + return 0; + } + } else { + return 0; + } + } + + /** + * Return the raw storage long[] for the given key. + */ + public long[] getArrayForKey(int key) { + assertConsistency(); + + return mParent.mLongs.get(getArrayFromKey(key)); + } + + /** + * Set the value for the given key and offset from that key. + * + * @param key A key as obtained from getKey or getOrAddKey. + * @param value The value to set. + */ + public void setValue(int key, long value) { + setValue(key, 0, value); + } + + /** + * Set the value for the given key and offset from that key. + * + * @param key A key as obtained from getKey or getOrAddKey. + * @param index The offset from that key. Must be less than the count + * provided to getOrAddKey when the space was allocated. + * @param value The value to set. + */ + public void setValue(int key, int index, long value) { + assertConsistency(); + + if (value < 0) { + logOrThrow("can't store negative values" + + " key=0x" + Integer.toHexString(key) + + " index=" + index + " value=" + value + + " -- " + dumpInternalState()); + return; + } + + try { + final long[] array = mParent.mLongs.get(getArrayFromKey(key)); + array[getIndexFromKey(key) + index] = value; + } catch (IndexOutOfBoundsException ex) { + logOrThrow("key=0x" + Integer.toHexString(key) + + " index=" + index + " value=" + value + + " -- " + dumpInternalState(), ex); + return; + } + } + + /** + * Clear out the table, and reset the sequence numbers so future writes + * without allocations will assert. + */ + public void resetTable() { + // Clear out our table. + mTable = null; + mSize = 0; + + // Reset our sequence number. This will make all read/write calls + // start to fail, and then when we re-allocate it will be re-synced + // to that of mParent. + mSequence = UNINITIALIZED_SEQUENCE; + } + + /** + * Write the keys stored in the table to the parcel. The parent must + * be separately written. Does not save the actual data. + */ + public void writeToParcel(Parcel out) { + out.writeInt(mSequence); + out.writeInt(mSize); + for (int i=0; i<mSize; i++) { + out.writeInt(mTable[i]); + } + } + + /** + * Read the keys from the parcel. The parent (with its long array) must + * have been previously initialized. + */ + public boolean readFromParcel(Parcel in) { + // Read the state + mSequence = in.readInt(); + mSize = in.readInt(); + if (mSize != 0) { + mTable = new int[mSize]; + for (int i=0; i<mSize; i++) { + mTable[i] = in.readInt(); + } + } else { + mTable = null; + } + + // Make sure we're all healthy + if (validateKeys(true)) { + return true; + } else { + // Clear it out + mSize = 0; + mTable = null; + return false; + } + } + + /** + * Return the number of keys that have been added to this Table. + */ + public int getKeyCount() { + return mSize; + } + + /** + * Get the key at the given index in our table. + */ + public int getKeyAt(int i) { + return mTable[i]; + } + + /** + * Throw an exception if one of a variety of internal consistency checks fails. + */ + private void assertConsistency() { + // Assert that our sequewnce number has been initialized. If it hasn't + // that means someone tried to read or write data without allocating it + // since we were created or reset. + if (mSequence == UNINITIALIZED_SEQUENCE) { + logOrThrow("mSequence == UNINITIALIZED_SEQUENCE in" + + " SparseMappingTable.Table. mParent.mSequence=" + mParent.mSequence); + } + + // Assert that our sequence number matches mParent's. If it isn't that means + // we have been reset and our + if (mSequence != mParent.mSequence) { + if (mSequence < mParent.mSequence) { + logOrThrow("Sequence mismatch. SparseMappingTable.resetTable()" + + " called but not Table.resetTable() -- " + + dumpInternalState()); + } else if (mSequence > mParent.mSequence) { + logOrThrow("Sequence mismatch. Table.resetTable()" + + " called but not SparseMappingTable.resetTable() -- " + + dumpInternalState()); + } + } + } + + /** + * Finds the 'id' inside the array of length size (physical size of the array + * is not used). + * + * @return The index of the array, or the bitwise not (~index) of where it + * would go if you wanted to insert 'id' into the array. + */ + private int binarySearch(byte id) { + int lo = 0; + int hi = mSize - 1; + + while (lo <= hi) { + int mid = (lo + hi) >>> 1; + byte midId = (byte)((mTable[mid] >> ID_SHIFT) & ID_MASK); + + if (midId < id) { + lo = mid + 1; + } else if (midId > id) { + hi = mid - 1; + } else { + return mid; // id found + } + } + return ~lo; // id not present + } + + /** + * Check that all the keys are valid locations in the long arrays. + * + * If any aren't, log it and return false. Else return true. + */ + private boolean validateKeys(boolean log) { + ArrayList<long[]> longs = mParent.mLongs; + final int longsSize = longs.size(); + + final int N = mSize; + for (int i=0; i<N; i++) { + final int key = mTable[i]; + final int arrayIndex = getArrayFromKey(key); + final int index = getIndexFromKey(key); + if (arrayIndex >= longsSize || index >= longs.get(arrayIndex).length) { + if (log) { + Slog.w(TAG, "Invalid stats at index " + i + " -- " + dumpInternalState()); + } + return false; + } + } + + return true; + } + + public String dumpInternalState() { + StringBuilder sb = new StringBuilder(); + sb.append("SparseMappingTable.Table{mSequence="); + sb.append(mSequence); + sb.append(" mParent.mSequence="); + sb.append(mParent.mSequence); + sb.append(" mParent.mLongs.size()="); + sb.append(mParent.mLongs.size()); + sb.append(" mSize="); + sb.append(mSize); + sb.append(" mTable="); + if (mTable == null) { + sb.append("null"); + } else { + final int N = mTable.length; + sb.append('['); + for (int i=0; i<N; i++) { + final int key = mTable[i]; + sb.append("0x"); + sb.append(Integer.toHexString((key >> ID_SHIFT) & ID_MASK)); + sb.append("/0x"); + sb.append(Integer.toHexString((key >> ARRAY_SHIFT) & ARRAY_MASK)); + sb.append("/0x"); + sb.append(Integer.toHexString((key >> INDEX_SHIFT) & INDEX_MASK)); + if (i != N-1) { + sb.append(", "); + } + } + sb.append(']'); + } + sb.append(" clazz="); + sb.append(getClass().getName()); + sb.append('}'); + + return sb.toString(); + } + } + + /** + * Wipe out all the data. + */ + public void reset() { + // Clear out mLongs, and prime it with a new array of data + mLongs.clear(); + mLongs.add(new long[ARRAY_SIZE]); + mNextIndex = 0; + + // Increment out sequence counter, because all of the tables will + // now be out of sync with the data. + mSequence++; + } + + /** + * Write the data arrays to the parcel. + */ + public void writeToParcel(Parcel out) { + out.writeInt(mSequence); + out.writeInt(mNextIndex); + final int N = mLongs.size(); + out.writeInt(N); + for (int i=0; i<N-1; i++) { + final long[] array = mLongs.get(i); + out.writeInt(array.length); + writeCompactedLongArray(out, array, array.length); + } + // save less for the last one. upon re-loading they'll just start a new array. + final long[] lastLongs = mLongs.get(N-1); + out.writeInt(mNextIndex); + writeCompactedLongArray(out, lastLongs, mNextIndex); + } + + /** + * Read the data arrays from the parcel. + */ + public void readFromParcel(Parcel in) { + mSequence = in.readInt(); + mNextIndex = in.readInt(); + + mLongs.clear(); + final int N = in.readInt(); + for (int i=0; i<N; i++) { + final int size = in.readInt(); + final long[] array = new long[size]; + readCompactedLongArray(in, array, size); + mLongs.add(array); + } + } + + /** + * Write the long array to the parcel in a compacted form. Does not allow negative + * values in the array. + */ + private static void writeCompactedLongArray(Parcel out, long[] array, int num) { + for (int i=0; i<num; i++) { + long val = array[i]; + if (val < 0) { + Slog.w(TAG, "Time val negative: " + val); + val = 0; + } + if (val <= Integer.MAX_VALUE) { + out.writeInt((int)val); + } else { + int top = ~((int)((val>>32)&0x7fffffff)); + int bottom = (int)(val&0xfffffff); + out.writeInt(top); + out.writeInt(bottom); + } + } + } + + /** + * Read the compacted array into the long[]. + */ + private static void readCompactedLongArray(Parcel in, long[] array, int num) { + final int alen = array.length; + if (num > alen) { + logOrThrow("bad array lengths: got " + num + " array is " + alen); + return; + } + int i; + for (i=0; i<num; i++) { + int val = in.readInt(); + if (val >= 0) { + array[i] = val; + } else { + int bottom = in.readInt(); + array[i] = (((long)~val)<<32) | bottom; + } + } + while (i < alen) { + array[i] = 0; + i++; + } + } + + /** + * Extract the id from a key. + */ + public static byte getIdFromKey(int key) { + return (byte)((key >> ID_SHIFT) & ID_MASK); + } + + /** + * Gets the index of the array in the list of arrays. + * + * Not to be confused with getIndexFromKey. + */ + public static int getArrayFromKey(int key) { + return (key >> ARRAY_SHIFT) & ARRAY_MASK; + } + + /** + * Gets the index of a value in a long[]. + * + * Not to be confused with getArrayFromKey. + */ + public static int getIndexFromKey(int key) { + return (key >> INDEX_SHIFT) & INDEX_MASK; + } + + /** + * Do a Slog.wtf or throw an exception (thereby crashing the system process if + * this is a debug build.) + */ + private static void logOrThrow(String message) { + logOrThrow(message, null); + } + + /** + * Do a Slog.wtf or throw an exception (thereby crashing the system process if + * this is an eng build.) + */ + private static void logOrThrow(String message, Throwable th) { + Slog.wtf(TAG, message, th); + if (Build.TYPE.equals("eng")) { + throw new RuntimeException(message, th); + } + } +} diff --git a/core/java/com/android/internal/app/procstats/SysMemUsageTable.java b/core/java/com/android/internal/app/procstats/SysMemUsageTable.java new file mode 100644 index 000000000000..e71bc55596d9 --- /dev/null +++ b/core/java/com/android/internal/app/procstats/SysMemUsageTable.java @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2013 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.internal.app.procstats; + +import android.util.DebugUtils; + +import static com.android.internal.app.procstats.ProcessStats.STATE_COUNT; +import static com.android.internal.app.procstats.ProcessStats.STATE_NOTHING; +import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_SAMPLE_COUNT; +import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_CACHED_MINIMUM; +import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_CACHED_AVERAGE; +import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_CACHED_MAXIMUM; +import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_FREE_MINIMUM; +import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_FREE_AVERAGE; +import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_FREE_MAXIMUM; +import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_ZRAM_MINIMUM; +import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_ZRAM_AVERAGE; +import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_ZRAM_MAXIMUM; +import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_KERNEL_MINIMUM; +import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_KERNEL_AVERAGE; +import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_KERNEL_MAXIMUM; +import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_NATIVE_MINIMUM; +import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_NATIVE_AVERAGE; +import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_NATIVE_MAXIMUM; +import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_COUNT; + +import java.io.PrintWriter; + + +/** + * Class to accumulate system mem usage data. + */ +public class SysMemUsageTable extends SparseMappingTable.Table { + /** + * Construct the SysMemUsageTable with 'tableData' as backing store + * for the longs data. + */ + public SysMemUsageTable(SparseMappingTable tableData) { + super(tableData); + } + + /** + * Merge the stats given into our own values. + * + * @param that SysMemUsageTable to copy from. + */ + public void mergeStats(SysMemUsageTable that) { + final int N = that.getKeyCount(); + for (int i=0; i<N; i++) { + final int key = that.getKeyAt(i); + + final int state = SparseMappingTable.getIdFromKey(key); + final long[] addData = that.getArrayForKey(key); + final int addOff = SparseMappingTable.getIndexFromKey(key); + + mergeStats(state, addData, addOff); + } + } + + /** + * Merge the stats given into our own values. + * + * @param state The state + * @param addData The data array to copy + * @param addOff The index in addOff to start copying from + */ + public void mergeStats(int state, long[] addData, int addOff) { + final int key = getOrAddKey((byte)state, SYS_MEM_USAGE_COUNT); + + final long[] dstData = getArrayForKey(key); + final int dstOff = SparseMappingTable.getIndexFromKey(key); + + SysMemUsageTable.mergeSysMemUsage(dstData, dstOff, addData, addOff); + } + + /** + * Return a long[] containing the merge of all of the usage in this table. + */ + public long[] getTotalMemUsage() { + long[] total = new long[SYS_MEM_USAGE_COUNT]; + final int N = getKeyCount(); + for (int i=0; i<N; i++) { + final int key = getKeyAt(i); + + final long[] addData = getArrayForKey(key); + final int addOff = SparseMappingTable.getIndexFromKey(key); + + SysMemUsageTable.mergeSysMemUsage(total, 0, addData, addOff); + } + return total; + } + + /** + * Merge the stats from one raw long[] into another. + * + * @param dstData The destination array + * @param dstOff The index in the destination array to start from + * @param addData The source array + * @param addOff The index in the source array to start from + */ + public static void mergeSysMemUsage(long[] dstData, int dstOff, + long[] addData, int addOff) { + final long dstCount = dstData[dstOff+SYS_MEM_USAGE_SAMPLE_COUNT]; + final long addCount = addData[addOff+SYS_MEM_USAGE_SAMPLE_COUNT]; + if (dstCount == 0) { + dstData[dstOff+SYS_MEM_USAGE_SAMPLE_COUNT] = addCount; + for (int i=SYS_MEM_USAGE_CACHED_MINIMUM; i<SYS_MEM_USAGE_COUNT; i++) { + dstData[dstOff+i] = addData[addOff+i]; + } + } else if (addCount > 0) { + dstData[dstOff+SYS_MEM_USAGE_SAMPLE_COUNT] = dstCount + addCount; + for (int i=SYS_MEM_USAGE_CACHED_MINIMUM; i<SYS_MEM_USAGE_COUNT; i+=3) { + if (dstData[dstOff+i] > addData[addOff+i]) { + dstData[dstOff+i] = addData[addOff+i]; + } + dstData[dstOff+i+1] = (long)( + ((dstData[dstOff+i+1]*(double)dstCount) + + (addData[addOff+i+1]*(double)addCount)) + / (dstCount+addCount) ); + if (dstData[dstOff+i+2] < addData[addOff+i+2]) { + dstData[dstOff+i+2] = addData[addOff+i+2]; + } + } + } + } + + + public void dump(PrintWriter pw, String prefix, int[] screenStates, int[] memStates) { + int printedScreen = -1; + for (int is=0; is<screenStates.length; is++) { + int printedMem = -1; + for (int im=0; im<memStates.length; im++) { + final int iscreen = screenStates[is]; + final int imem = memStates[im]; + final int bucket = ((iscreen + imem) * STATE_COUNT); + long count = getValueForId((byte)bucket, SYS_MEM_USAGE_SAMPLE_COUNT); + if (count > 0) { + pw.print(prefix); + if (screenStates.length > 1) { + DumpUtils.printScreenLabel(pw, printedScreen != iscreen + ? iscreen : STATE_NOTHING); + printedScreen = iscreen; + } + if (memStates.length > 1) { + DumpUtils.printMemLabel(pw, + printedMem != imem ? imem : STATE_NOTHING, '\0'); + printedMem = imem; + } + pw.print(": "); + pw.print(count); + pw.println(" samples:"); + dumpCategory(pw, prefix, " Cached", bucket, SYS_MEM_USAGE_CACHED_MINIMUM); + dumpCategory(pw, prefix, " Free", bucket, SYS_MEM_USAGE_FREE_MINIMUM); + dumpCategory(pw, prefix, " ZRam", bucket, SYS_MEM_USAGE_ZRAM_MINIMUM); + dumpCategory(pw, prefix, " Kernel", bucket, SYS_MEM_USAGE_KERNEL_MINIMUM); + dumpCategory(pw, prefix, " Native", bucket, SYS_MEM_USAGE_NATIVE_MINIMUM); + } + } + } + } + + private void dumpCategory(PrintWriter pw, String prefix, String label, int bucket, int index) { + pw.print(prefix); pw.print(label); + pw.print(": "); + DebugUtils.printSizeValue(pw, getValueForId((byte)bucket, index) * 1024); + pw.print(" min, "); + DebugUtils.printSizeValue(pw, getValueForId((byte)bucket, index + 1) * 1024); + pw.print(" avg, "); + DebugUtils.printSizeValue(pw, getValueForId((byte)bucket, index+2) * 1024); + pw.println(" max"); + } + +} + + diff --git a/core/java/com/android/internal/app/procstats/package.html b/core/java/com/android/internal/app/procstats/package.html new file mode 100644 index 000000000000..db6f78bbf628 --- /dev/null +++ b/core/java/com/android/internal/app/procstats/package.html @@ -0,0 +1,3 @@ +<body> +{@hide} +</body>
\ No newline at end of file diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java index 10027b6a723a..5e8f4a250816 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.Intent; 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/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index 9904893f3e5c..4b695b93d0e9 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -183,6 +183,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind private boolean mLastHasBottomStableInset = false; private boolean mLastHasRightStableInset = false; private int mLastWindowFlags = 0; + private boolean mLastShouldAlwaysConsumeNavBar = false; private int mRootScrollY = 0; @@ -996,6 +997,7 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind boolean hasRightStableInset = insets.getStableInsetRight() != 0; disallowAnimate |= (hasRightStableInset != mLastHasRightStableInset); mLastHasRightStableInset = hasRightStableInset; + mLastShouldAlwaysConsumeNavBar = insets.shouldAlwaysConsumeNavBar(); } boolean navBarToRightEdge = isNavBarToRightEdge(mLastBottomInset, mLastRightInset); @@ -1016,12 +1018,11 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind // When we expand the window with FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, we still need // to ensure that the rest of the view hierarchy doesn't notice it, unless they've // explicitly asked for it. - boolean consumingNavBar = (attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 && (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0 && (sysUiVisibility & SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0 - || (insets != null && insets.shouldAlwaysConsumeNavBar()); + || mLastShouldAlwaysConsumeNavBar; // If we didn't request fullscreen layout, but we still got it because of the // mForceWindowDrawsStatusBarBackground flag, also consume top inset. diff --git a/core/java/com/android/internal/view/menu/ActionMenuItemView.java b/core/java/com/android/internal/view/menu/ActionMenuItemView.java index bd97e5d47714..4738f5ed5817 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 @@ public class ActionMenuItemView extends TextView } 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 31b2f9619ed6..df57639a819e 100644 --- a/core/java/com/android/internal/view/menu/MenuBuilder.java +++ b/core/java/com/android/internal/view/menu/MenuBuilder.java @@ -65,7 +65,6 @@ public class MenuBuilder implements Menu { private final Context mContext; private final Resources mResources; - private final boolean mShowCascadingMenus; /** * Whether the shortcuts should be qwerty-accessible. Use isQwertyMode() @@ -188,9 +187,6 @@ public class MenuBuilder implements Menu { public MenuBuilder(Context context) { mContext = context; mResources = context.getResources(); - mShowCascadingMenus = context.getResources().getBoolean( - com.android.internal.R.bool.config_enableCascadingSubmenus); - mItems = new ArrayList<MenuItemImpl>(); mVisibleItems = new ArrayList<MenuItemImpl>(); @@ -915,10 +911,6 @@ public class MenuBuilder implements Menu { close(true /* closeAllMenus */); } } else if (itemImpl.hasSubMenu() || providerHasSubMenu) { - if (!mShowCascadingMenus) { - close(false /* closeAllMenus */); - } - if (!itemImpl.hasSubMenu()) { itemImpl.setSubMenu(new SubMenuBuilder(getContext(), this, itemImpl)); } diff --git a/core/java/com/android/internal/view/menu/StandardMenuPopup.java b/core/java/com/android/internal/view/menu/StandardMenuPopup.java index 2cb224edf1d5..8ced36f78d75 100644 --- a/core/java/com/android/internal/view/menu/StandardMenuPopup.java +++ b/core/java/com/android/internal/view/menu/StandardMenuPopup.java @@ -240,7 +240,10 @@ final class StandardMenuPopup extends MenuPopup implements OnDismissListener, On mTreeObserver = null; } mShownAnchorView.removeOnAttachStateChangeListener(mAttachStateChangeListener); - mOnDismissListener.onDismiss(); + + if (mOnDismissListener != null) { + mOnDismissListener.onDismiss(); + } } @Override @@ -265,6 +268,13 @@ final class StandardMenuPopup extends MenuPopup implements OnDismissListener, On subPopup.setPresenterCallback(mPresenterCallback); subPopup.setForceShowIcon(mAdapter.getForceShowIcon()); + // Pass responsibility for handling onDismiss to the submenu. + subPopup.setOnDismissListener(mOnDismissListener); + mOnDismissListener = null; + + // Close this menu popup to make room for the submenu popup. + dismiss(); + // Show the new sub-menu popup at the same location as this popup. if (subPopup.tryShow(mXOffset, mYOffset)) { if (mPresenterCallback != null) { diff --git a/core/res/res/layout/chooser_grid.xml b/core/res/res/layout/chooser_grid.xml index 41726fb71f7f..d12c8ba2f2ef 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 00c25e65c1b4..4b8640c91f70 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 3bbb352bca8e..1ec5c0fa5cb8 100644 --- a/core/res/res/values-sw600dp/dimens_material.xml +++ b/core/res/res/values-sw600dp/dimens_material.xml @@ -23,6 +23,8 @@ <dimen name="action_bar_default_height_material">64dp</dimen> <!-- Default content inset of an action bar. --> <dimen name="action_bar_content_inset_material">24dp</dimen> + <!-- Default content inset of an action bar with navigation present. --> + <dimen name="action_bar_content_inset_with_nav">80dp</dimen> <!-- Default start padding of an action bar. --> <dimen name="action_bar_default_padding_start_material">8dp</dimen> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index a52c4e5ffbf4..d7381abed1fb 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -2846,6 +2846,11 @@ i <!-- 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 @@ i <!-- Minimum inset for content views within a bar. Navigation buttons and menu views are excepted. Only valid for some themes and configurations. --> <attr name="contentInsetRight" format="dimension" /> + <!-- Minimum inset for content views within a bar when a navigation button + is present, such as the Up button. Only valid for some themes and configurations. --> + <attr name="contentInsetStartWithNavigation" format="dimension" /> + <!-- Minimum inset for content views within a bar when actions from a menu + are present. Only valid for some themes and configurations. --> + <attr name="contentInsetEndWithActions" format="dimension" /> <!-- Elevation for the action bar itself --> <attr name="elevation" /> <!-- Reference to a theme that should be used to inflate popups @@ -8006,6 +8017,8 @@ i <attr name="contentInsetEnd" /> <attr name="contentInsetLeft" /> <attr name="contentInsetRight" /> + <attr name="contentInsetStartWithNavigation" /> + <attr name="contentInsetEndWithActions" /> <attr name="maxButtonHeight" format="dimension" /> <attr name="navigationButtonStyle" format="reference" /> <attr name="buttonGravity"> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 6ecaa1faac55..892b3d58b388 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2497,4 +2497,8 @@ <!-- True if the device supports at least one form of multi-window. E.g. freeform, split-screen, picture-in-picture. --> <bool name="config_supportsMultiWindow">true</bool> + + <!-- True if the device requires AppWidgetService even if it does not have + the PackageManager.FEATURE_APP_WIDGETS feature --> + <bool name="config_enableAppWidgetService">false</bool> </resources> diff --git a/core/res/res/values/dimens_material.xml b/core/res/res/values/dimens_material.xml index 2fe4f6652a87..ad2b335de5bb 100644 --- a/core/res/res/values/dimens_material.xml +++ b/core/res/res/values/dimens_material.xml @@ -41,6 +41,8 @@ <dimen name="action_bar_default_padding_end_material">0dp</dimen> <!-- Default content inset of an action bar. --> <dimen name="action_bar_content_inset_material">16dp</dimen> + <!-- Default content inset of an action bar when a navigation button is present. --> + <dimen name="action_bar_content_inset_with_nav">72dp</dimen> <!-- Vertical padding around action bar icons. --> <dimen name="action_bar_icon_vertical_padding_material">16dp</dimen> <!-- Top margin for action bar subtitles --> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 7fd5456e5a96..0839187c48d1 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 e94a91d81328..6041f637c89d 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 86b9f1d1b7d5..790dcfa3f127 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -1234,6 +1234,7 @@ please see styles_device_defaults.xml. <item name="collapseIcon">?attr/homeAsUpIndicator</item> <item name="collapseContentDescription">@string/toolbar_collapse_description</item> <item name="contentInsetStart">16dp</item> + <item name="contentInsetStartWithNavigation">@dimen/action_bar_content_inset_with_nav</item> <item name="touchscreenBlocksFocus">true</item> </style> diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml index e636bc09d2bd..2420c1a96a6c 100644 --- a/core/res/res/values/styles_material.xml +++ b/core/res/res/values/styles_material.xml @@ -311,7 +311,7 @@ please see styles_device_defaults.xml. <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 @@ please see styles_device_defaults.xml. <item name="homeLayout">@layout/action_bar_home_material</item> <item name="gravity">center_vertical</item> <item name="contentInsetStart">@dimen/action_bar_content_inset_material</item> + <item name="contentInsetStartWithNavigation">@dimen/action_bar_content_inset_with_nav</item> <item name="contentInsetEnd">@dimen/action_bar_content_inset_material</item> <item name="elevation">@dimen/action_bar_elevation_material</item> <item name="popupTheme">?attr/actionBarPopupTheme</item> diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index 6526571e3a11..694e9342000d 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -307,6 +307,7 @@ <java-symbol type="bool" name="config_supportsMultiWindow" /> <java-symbol type="bool" name="config_guestUserEphemeral" /> <java-symbol type="bool" name="config_localDisplaysMirrorContent" /> + <java-symbol type="bool" name="config_enableAppWidgetService" /> <java-symbol type="string" name="config_defaultPictureInPictureBounds" /> <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_threshold" /> <java-symbol type="integer" name="config_wifi_framework_5GHz_preference_boost_factor" /> diff --git a/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java b/core/tests/coretests/src/android/widget/SuggestionsPopupWindowTest.java index a37abf185097..59ffd566909f 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.app.Activity; 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 @@ public class SuggestionsPopupWindowTest extends ActivityInstrumentationTestCase2 } @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 3fbc16a655aa..923b8299a60a 100644 --- a/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java +++ b/core/tests/coretests/src/android/widget/TextViewActivityMouseTest.java @@ -44,7 +44,6 @@ import com.android.frameworks.coretests.R; 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 @@ public class TextViewActivityMouseTest extends ActivityInstrumentationTestCase2< } @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 291fdc4f8e05..0dae7961ff20 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -1494,9 +1494,14 @@ public class Paint { } /** - * 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 @@ public class Paint { * 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 01f9cde823c7..1f1785139257 100644 --- a/libs/hwui/Android.mk +++ b/libs/hwui/Android.mk @@ -322,6 +322,7 @@ LOCAL_SRC_FILES += \ $(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 859036543b4a..b70d5868c718 100644 --- a/libs/hwui/BakedOpState.cpp +++ b/libs/hwui/BakedOpState.cpp @@ -108,5 +108,63 @@ ResolvedRenderState::ResolvedRenderState(const ClipRect* clipRect, const Rect& d clippedBounds.doIntersect(clipRect->rect); } +BakedOpState* BakedOpState::tryConstruct(LinearAllocator& allocator, + Snapshot& snapshot, const RecordedOp& recordedOp) { + if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; + BakedOpState* bakedState = allocator.create_trivial<BakedOpState>( + allocator, snapshot, recordedOp, false); + if (bakedState->computedState.clippedBounds.isEmpty()) { + // bounds are empty, so op is rejected + allocator.rewindIfLastAlloc(bakedState); + return nullptr; + } + return bakedState; +} + +BakedOpState* BakedOpState::tryConstructUnbounded(LinearAllocator& allocator, + Snapshot& snapshot, const RecordedOp& recordedOp) { + if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; + return allocator.create_trivial<BakedOpState>(allocator, snapshot, recordedOp); +} + +BakedOpState* BakedOpState::tryStrokeableOpConstruct(LinearAllocator& allocator, + Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior) { + if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; + bool expandForStroke = (strokeBehavior == StrokeBehavior::StyleDefined) + ? (recordedOp.paint && recordedOp.paint->getStyle() != SkPaint::kFill_Style) + : true; + + BakedOpState* bakedState = allocator.create_trivial<BakedOpState>( + allocator, snapshot, recordedOp, expandForStroke); + if (bakedState->computedState.clippedBounds.isEmpty()) { + // bounds are empty, so op is rejected + // NOTE: this won't succeed if a clip was allocated + allocator.rewindIfLastAlloc(bakedState); + return nullptr; + } + return bakedState; +} + +BakedOpState* BakedOpState::tryShadowOpConstruct(LinearAllocator& allocator, + Snapshot& snapshot, const ShadowOp* shadowOpPtr) { + if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; + + // clip isn't empty, so construct the op + return allocator.create_trivial<BakedOpState>(allocator, snapshot, shadowOpPtr); +} + +BakedOpState* BakedOpState::directConstruct(LinearAllocator& allocator, + const ClipRect* clip, const Rect& dstRect, const RecordedOp& recordedOp) { + return allocator.create_trivial<BakedOpState>(clip, dstRect, recordedOp); +} + +void BakedOpState::setupOpacity(const SkPaint* paint) { + computedState.opaqueOverClippedBounds = computedState.transform.isSimple() + && computedState.clipState->mode == ClipMode::Rectangle + && MathUtils::areEqual(alpha, 1.0f) + && !roundRectClipState + && PaintUtils::isOpaquePaint(paint); +} + } // namespace uirenderer } // namespace android diff --git a/libs/hwui/BakedOpState.h b/libs/hwui/BakedOpState.h index 4e3cb8a15e24..e1441fca5ee2 100644 --- a/libs/hwui/BakedOpState.h +++ b/libs/hwui/BakedOpState.h @@ -93,6 +93,7 @@ public: Rect clippedBounds; int clipSideFlags = 0; const SkPath* localProjectionPathMask = nullptr; + bool opaqueOverClippedBounds = false; }; /** @@ -103,23 +104,10 @@ public: class BakedOpState { public: static BakedOpState* tryConstruct(LinearAllocator& allocator, - Snapshot& snapshot, const RecordedOp& recordedOp) { - if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; - BakedOpState* bakedState = allocator.create_trivial<BakedOpState>( - allocator, snapshot, recordedOp, false); - if (bakedState->computedState.clippedBounds.isEmpty()) { - // bounds are empty, so op is rejected - allocator.rewindIfLastAlloc(bakedState); - return nullptr; - } - return bakedState; - } + Snapshot& snapshot, const RecordedOp& recordedOp); static BakedOpState* tryConstructUnbounded(LinearAllocator& allocator, - Snapshot& snapshot, const RecordedOp& recordedOp) { - if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; - return allocator.create_trivial<BakedOpState>(allocator, snapshot, recordedOp); - } + Snapshot& snapshot, const RecordedOp& recordedOp); enum class StrokeBehavior { // stroking is forced, regardless of style on paint (such as for lines) @@ -129,35 +117,16 @@ public: }; static BakedOpState* tryStrokeableOpConstruct(LinearAllocator& allocator, - Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior) { - if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; - bool expandForStroke = (strokeBehavior == StrokeBehavior::StyleDefined) - ? (recordedOp.paint && recordedOp.paint->getStyle() != SkPaint::kFill_Style) - : true; - - BakedOpState* bakedState = allocator.create_trivial<BakedOpState>( - allocator, snapshot, recordedOp, expandForStroke); - if (bakedState->computedState.clippedBounds.isEmpty()) { - // bounds are empty, so op is rejected - // NOTE: this won't succeed if a clip was allocated - allocator.rewindIfLastAlloc(bakedState); - return nullptr; - } - return bakedState; - } + Snapshot& snapshot, const RecordedOp& recordedOp, StrokeBehavior strokeBehavior); static BakedOpState* tryShadowOpConstruct(LinearAllocator& allocator, - Snapshot& snapshot, const ShadowOp* shadowOpPtr) { - if (CC_UNLIKELY(snapshot.getRenderTargetClip().isEmpty())) return nullptr; - - // clip isn't empty, so construct the op - return allocator.create_trivial<BakedOpState>(allocator, snapshot, shadowOpPtr); - } + Snapshot& snapshot, const ShadowOp* shadowOpPtr); static BakedOpState* directConstruct(LinearAllocator& allocator, - const ClipRect* clip, const Rect& dstRect, const RecordedOp& recordedOp) { - return allocator.create_trivial<BakedOpState>(clip, dstRect, recordedOp); - } + const ClipRect* clip, const Rect& dstRect, const RecordedOp& recordedOp); + + // Set opaqueOverClippedBounds. If this method isn't called, the op is assumed translucent. + void setupOpacity(const SkPaint* paint); // computed state: ResolvedRenderState computedState; diff --git a/libs/hwui/FrameBuilder.cpp b/libs/hwui/FrameBuilder.cpp index b1314feebf34..b18836f175ea 100644 --- a/libs/hwui/FrameBuilder.cpp +++ b/libs/hwui/FrameBuilder.cpp @@ -481,12 +481,17 @@ void FrameBuilder::deferRenderNodeOp(const RenderNodeOp& op) { * Defers an unmergeable, strokeable op, accounting correctly * for paint's style on the bounds being computed. */ -const BakedOpState* FrameBuilder::deferStrokeableOp(const RecordedOp& op, batchid_t batchId, +BakedOpState* FrameBuilder::deferStrokeableOp(const RecordedOp& op, batchid_t batchId, BakedOpState::StrokeBehavior strokeBehavior) { // Note: here we account for stroke when baking the op BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct( mAllocator, *mCanvasState.writableSnapshot(), op, strokeBehavior); if (!bakedState) return nullptr; // quick rejected + + if (op.opId == RecordedOpId::RectOp && op.paint->getStyle() != SkPaint::kStroke_Style) { + bakedState->setupOpacity(op.paint); + } + currentLayer().deferUnmergeableOp(mAllocator, bakedState, batchId); return bakedState; } @@ -516,6 +521,7 @@ static bool hasMergeableClip(const BakedOpState& state) { void FrameBuilder::deferBitmapOp(const BitmapOp& op) { BakedOpState* bakedState = tryBakeOpState(op); if (!bakedState) return; // quick rejected + bakedState->setupOpacity(op.paint); // Don't merge non-simply transformed or neg scale ops, SET_TEXTURE doesn't handle rotation // Don't merge A8 bitmaps - the paint's color isn't compared by mergeId, or in diff --git a/libs/hwui/FrameBuilder.h b/libs/hwui/FrameBuilder.h index 0b7a6062456a..02c05cb1bbbe 100644 --- a/libs/hwui/FrameBuilder.h +++ b/libs/hwui/FrameBuilder.h @@ -201,7 +201,7 @@ private: return mAllocator.create<SkPath>(); } - const BakedOpState* deferStrokeableOp(const RecordedOp& op, batchid_t batchId, + BakedOpState* deferStrokeableOp(const RecordedOp& op, batchid_t batchId, BakedOpState::StrokeBehavior strokeBehavior = BakedOpState::StrokeBehavior::StyleDefined); /** diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp index 2246cf9c1948..76e587e162b6 100644 --- a/libs/hwui/JankTracker.cpp +++ b/libs/hwui/JankTracker.cpp @@ -244,7 +244,7 @@ void JankTracker::addFrame(const FrameInfo& frame) { 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 e6a95ff177a4..eea11bff7d8a 100644 --- a/libs/hwui/LayerBuilder.cpp +++ b/libs/hwui/LayerBuilder.cpp @@ -236,6 +236,21 @@ void LayerBuilder::deferLayerClear(const Rect& rect) { mClearRects.push_back(rect); } +void LayerBuilder::onDeferOp(LinearAllocator& allocator, const BakedOpState* bakedState) { + if (bakedState->op->opId != RecordedOpId::CopyToLayerOp) { + // First non-CopyToLayer, so stop stashing up layer clears for unclipped save layers, + // and issue them together in one draw. + flushLayerClears(allocator); + + if (CC_UNLIKELY(activeUnclippedSaveLayers.empty() + && bakedState->computedState.opaqueOverClippedBounds + && bakedState->computedState.clippedBounds.contains(repaintRect))) { + // discard all deferred drawing ops, since new one will occlude them + clear(); + } + } +} + void LayerBuilder::flushLayerClears(LinearAllocator& allocator) { if (CC_UNLIKELY(!mClearRects.empty())) { const int vertCount = mClearRects.size() * 4; @@ -270,11 +285,7 @@ void LayerBuilder::flushLayerClears(LinearAllocator& allocator) { void LayerBuilder::deferUnmergeableOp(LinearAllocator& allocator, BakedOpState* op, batchid_t batchId) { - if (batchId != OpBatchType::CopyToLayer) { - // if first op after one or more unclipped saveLayers, flush the layer clears - flushLayerClears(allocator); - } - + onDeferOp(allocator, op); OpBatch* targetBatch = mBatchLookup[batchId]; size_t insertBatchIndex = mBatches.size(); @@ -295,10 +306,7 @@ void LayerBuilder::deferUnmergeableOp(LinearAllocator& allocator, void LayerBuilder::deferMergeableOp(LinearAllocator& allocator, BakedOpState* op, batchid_t batchId, mergeid_t mergeId) { - if (batchId != OpBatchType::CopyToLayer) { - // if first op after one or more unclipped saveLayers, flush the layer clears - flushLayerClears(allocator); - } + onDeferOp(allocator, op); MergingOpBatch* targetBatch = nullptr; // Try to merge with any existing batch with same mergeId @@ -348,6 +356,14 @@ void LayerBuilder::replayBakedOpsImpl(void* arg, } } +void LayerBuilder::clear() { + mBatches.clear(); + for (int i = 0; i < OpBatchType::Count; i++) { + mBatchLookup[i] = nullptr; + mMergingBatchLookup[i].clear(); + } +} + void LayerBuilder::dump() const { ALOGD("LayerBuilder %p, %ux%u buffer %p, blo %p, rn %p (%s)", this, width, height, offscreenBuffer, beginLayerOp, diff --git a/libs/hwui/LayerBuilder.h b/libs/hwui/LayerBuilder.h index 4a7ca2de9b1b..4de432c5e7be 100644 --- a/libs/hwui/LayerBuilder.h +++ b/libs/hwui/LayerBuilder.h @@ -100,9 +100,7 @@ public: return mBatches.empty(); } - void clear() { - mBatches.clear(); - } + void clear(); void dump() const; @@ -117,6 +115,7 @@ public: // list of deferred CopyFromLayer ops, to be deferred upon encountering EndUnclippedLayerOps std::vector<BakedOpState*> activeUnclippedSaveLayers; private: + void onDeferOp(LinearAllocator& allocator, const BakedOpState* bakedState); void flushLayerClears(LinearAllocator& allocator); std::vector<BatchBase*> mBatches; diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp index 9a825fdec601..8e04c8715f62 100644 --- a/libs/hwui/font/Font.cpp +++ b/libs/hwui/font/Font.cpp @@ -356,8 +356,6 @@ void Font::measure(const SkPaint* paint, const glyph_t* glyphs, } 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 000000000000..df3d041e722d --- /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 9daf6334b507..0aef620c7429 100644 --- a/libs/hwui/tests/microbench/FrameBuilderBench.cpp +++ b/libs/hwui/tests/microbench/FrameBuilderBench.cpp @@ -113,15 +113,17 @@ static auto SCENES = { }; 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 e97aaa6ac688..ba22f91cde9d 100644 --- a/libs/hwui/tests/unit/FrameBuilderTests.cpp +++ b/libs/hwui/tests/unit/FrameBuilderTests.cpp @@ -216,6 +216,80 @@ RENDERTHREAD_TEST(FrameBuilder, simpleBatching) { << "Expect number of ops = 2 * loop count"; } +RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_rects) { + class AvoidOverdrawRectsTestRenderer : public TestRendererBase { + public: + void onRectOp(const RectOp& op, const BakedOpState& state) override { + EXPECT_EQ(mIndex++, 0) << "Should be one rect"; + EXPECT_EQ(Rect(10, 10, 190, 190), op.unmappedBounds) + << "Last rect should occlude others."; + } + }; + auto node = TestUtils::createNode(0, 0, 200, 200, + [](RenderProperties& props, RecordingCanvas& canvas) { + canvas.drawRect(0, 0, 200, 200, SkPaint()); + canvas.drawRect(0, 0, 200, 200, SkPaint()); + canvas.drawRect(10, 10, 190, 190, SkPaint()); + }); + + // Damage (and therefore clip) is same as last draw, subset of renderable area. + // This means last op occludes other contents, and they'll be rejected to avoid overdraw. + SkRect damageRect = SkRect::MakeLTRB(10, 10, 190, 190); + FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, damageRect, 200, 200, + TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); + + EXPECT_EQ(3u, node->getDisplayList()->getOps().size()) + << "Recording must not have rejected ops, in order for this test to be valid"; + + AvoidOverdrawRectsTestRenderer renderer; + frameBuilder.replayBakedOps<TestDispatcher>(renderer); + EXPECT_EQ(1, renderer.getIndex()) << "Expect exactly one op"; +} + +RENDERTHREAD_TEST(FrameBuilder, avoidOverdraw_bitmaps) { + static SkBitmap opaqueBitmap = TestUtils::createSkBitmap(50, 50, + SkColorType::kRGB_565_SkColorType); + static SkBitmap transpBitmap = TestUtils::createSkBitmap(50, 50, + SkColorType::kAlpha_8_SkColorType); + class AvoidOverdrawBitmapsTestRenderer : public TestRendererBase { + public: + void onBitmapOp(const BitmapOp& op, const BakedOpState& state) override { + EXPECT_LT(mIndex++, 2) << "Should be two bitmaps"; + switch(mIndex++) { + case 0: + EXPECT_EQ(opaqueBitmap.pixelRef(), op.bitmap->pixelRef()); + break; + case 1: + EXPECT_EQ(transpBitmap.pixelRef(), op.bitmap->pixelRef()); + break; + default: + ADD_FAILURE() << "Only two ops expected."; + } + } + }; + + auto node = TestUtils::createNode(0, 0, 50, 50, + [](RenderProperties& props, RecordingCanvas& canvas) { + canvas.drawRect(0, 0, 50, 50, SkPaint()); + canvas.drawRect(0, 0, 50, 50, SkPaint()); + canvas.drawBitmap(transpBitmap, 0, 0, nullptr); + + // only the below draws should remain, since they're + canvas.drawBitmap(opaqueBitmap, 0, 0, nullptr); + canvas.drawBitmap(transpBitmap, 0, 0, nullptr); + }); + + FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(50, 50), 50, 50, + TestUtils::createSyncedNodeList(node), sLightGeometry, Caches::getInstance()); + + EXPECT_EQ(5u, node->getDisplayList()->getOps().size()) + << "Recording must not have rejected ops, in order for this test to be valid"; + + AvoidOverdrawBitmapsTestRenderer renderer; + frameBuilder.replayBakedOps<TestDispatcher>(renderer); + EXPECT_EQ(2, renderer.getIndex()) << "Expect exactly one op"; +} + RENDERTHREAD_TEST(FrameBuilder, clippedMerging) { class ClippedMergingTestRenderer : public TestRendererBase { public: diff --git a/libs/hwui/tests/unit/SkiaCanvasTests.cpp b/libs/hwui/tests/unit/SkiaCanvasTests.cpp index d6279b4a2974..5a011938e2bb 100644 --- a/libs/hwui/tests/unit/SkiaCanvasTests.cpp +++ b/libs/hwui/tests/unit/SkiaCanvasTests.cpp @@ -34,6 +34,7 @@ TEST(SkiaCanvasProxy, drawGlyphsViaPicture) { SkPaint paint; paint.setAntiAlias(true); paint.setTextSize(20); + paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); static const char* text = "testing text bounds"; // draw text directly into Recording canvas diff --git a/libs/hwui/utils/PaintUtils.h b/libs/hwui/utils/PaintUtils.h index db537130e12e..4faab9a5f648 100644 --- a/libs/hwui/utils/PaintUtils.h +++ b/libs/hwui/utils/PaintUtils.h @@ -67,6 +67,21 @@ public: && getXfermode(paint.getXfermode()) == SkXfermode::kSrcOver_Mode; } + static bool isOpaquePaint(const SkPaint* paint) { + if (!paint) return true; // default (paintless) behavior is SrcOver, black + + if (paint->getAlpha() != 0xFF + || PaintUtils::isBlendedShader(paint->getShader()) + || PaintUtils::isBlendedColorFilter(paint->getColorFilter())) { + return false; + } + + // Only let simple srcOver / src blending modes declare opaque, since behavior is clear. + SkXfermode::Mode mode = getXfermode(paint->getXfermode()); + return mode == SkXfermode::Mode::kSrcOver_Mode + || mode == SkXfermode::Mode::kSrc_Mode; + } + static bool isBlendedShader(const SkShader* shader) { if (shader == nullptr) { return false; diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java index d9caf030a8fb..b6ff41ef15d0 100644 --- a/media/java/android/media/AudioTrack.java +++ b/media/java/android/media/AudioTrack.java @@ -1120,7 +1120,7 @@ public class AudioTrack implements AudioRouting * <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 @@ public class AudioTrack implements AudioRouting * @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 @@ public class AudioTrack implements AudioRouting * <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 11c163bdfbcd..919fc560951b 100644 --- a/packages/DocumentsUI/perf-tests/Android.mk +++ b/packages/DocumentsUI/perf-tests/Android.mk @@ -11,7 +11,7 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) \ ../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 000000000000..cb2d904c9a9c --- /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 1bc802a00ec6..6147a7187de7 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 android.provider.DocumentsProvider; 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 @@ public class StressProvider extends DocumentsProvider { // 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 @@ public class StressProvider extends DocumentsProvider { }; 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 @@ public class StressProvider extends DocumentsProvider { @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); + } + + 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); } - mIds.add(STRESS_ROOT_0_DOC_ID); - mIds.add(STRESS_ROOT_1_DOC_ID); + + 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 class StressProvider extends DocumentsProvider { 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; + } + + 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; } - builder.append(index); // Append a number to guarantee uniqueness. - return builder.toString(); } } diff --git a/packages/DocumentsUI/src/com/android/documentsui/Metrics.java b/packages/DocumentsUI/src/com/android/documentsui/Metrics.java index e6b22e6ed73f..7ad4a094b327 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.net.Uri; 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 @@ public final class Metrics { * @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 35da8cc6357b..8b4f40ef38be 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 @@ public class RootsFragment extends Fragment { 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 @@ public class RootsFragment extends Fragment { * @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 @@ public class RootsFragment extends Fragment { 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 d21afee93434..655359a70c24 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.TextUtils; 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 @@ public final class Shared { /* * 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 16b7660e8ba4..534a483474d3 100644 --- a/packages/DocumentsUI/src/com/android/documentsui/State.java +++ b/packages/DocumentsUI/src/com/android/documentsui/State.java @@ -43,10 +43,19 @@ public class State implements android.os.Parcelable { 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 class State implements android.os.Parcelable { 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 da9f9c30261a..7c1e2196f6d5 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 @@ public class DirectoryListBot extends BaseBot { return true; } - private UiObject findDocumentsList() { + public UiObject findDocumentsList() { return findObject( "com.android.documentsui:id/container_directory", DIR_LIST_ID); diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java index 3b5513ab12cf..793544082af9 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java +++ b/packages/PrintSpooler/src/com/android/printspooler/ui/FusedPrintersProvider.java @@ -267,9 +267,12 @@ public final class FusedPrintersProvider extends Loader<List<PrinterInfo>> // The contract is that if we already have a valid, // result the we have to deliver it immediately. - if (!mPrinters.isEmpty()) { - deliverResult(new ArrayList<>(mPrinters)); - } + (new Handler(Looper.getMainLooper())).post(new Runnable() { + @Override public void run() { + deliverResult(new ArrayList<>(mPrinters)); + } + }); + // Always load the data to ensure discovery period is // started and to make sure obsolete printers are updated. onForceLoad(); diff --git a/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java b/packages/SettingsLib/src/com/android/settingslib/drawer/SettingsDrawerActivity.java index 64910fdf8695..c3a50890456a 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 @@ public class SettingsDrawerActivity extends Activity { 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/Shell/src/com/android/shell/BugreportStorageProvider.java b/packages/Shell/src/com/android/shell/BugreportStorageProvider.java index 814aa8cb8c06..49759c5f6ee1 100644 --- a/packages/Shell/src/com/android/shell/BugreportStorageProvider.java +++ b/packages/Shell/src/com/android/shell/BugreportStorageProvider.java @@ -56,7 +56,7 @@ public class BugreportStorageProvider extends DocumentsProvider { final MatrixCursor result = new MatrixCursor(resolveRootProjection(projection)); final RowBuilder row = result.newRow(); row.add(Root.COLUMN_ROOT_ID, DOC_ID_ROOT); - row.add(Root.COLUMN_FLAGS, Root.FLAG_LOCAL_ONLY | Root.FLAG_ADVANCED); + row.add(Root.COLUMN_FLAGS, Root.FLAG_LOCAL_ONLY); row.add(Root.COLUMN_ICON, android.R.mipmap.sym_def_app_icon); row.add(Root.COLUMN_TITLE, getContext().getString(R.string.bugreport_storage_title)); row.add(Root.COLUMN_DOCUMENT_ID, DOC_ID_ROOT); diff --git a/packages/SystemUI/res/drawable/ic_data_saver.xml b/packages/SystemUI/res/drawable/ic_data_saver.xml index 426238c05f4b..73567723e3fa 100644 --- a/packages/SystemUI/res/drawable/ic_data_saver.xml +++ b/packages/SystemUI/res/drawable/ic_data_saver.xml @@ -20,9 +20,11 @@ android:viewportHeight="24.0"> <path android:fillColor="#FFFFFFFF" - android:pathData=" - M9.0,16.0l2.0,0.0L11.0,8.0L9.0,8.0l0.0,8.0z - m3.0,-14.0C6.48,2.0 2.0,6.48 2.0,12.0s4.48,10.0 10.0,10.0 10.0,-4.48 10.0,-10.0S17.52,2.0 12.0,2.0z - m0.0,18.0c-4.41,0.0 -8.0,-3.59 -8.0,-8.0s3.59,-8.0 8.0,-8.0 8.0,3.59 8.0,8.0 -3.59,8.0 -8.0,8.0z - m1.0,-4.0l2.0,0.0l0.0,-8.0l-2.0,0.0l0.0,8.0z"/> + android:pathData="M12.0,19.0c-3.9,0.0 -7.0,-3.1 -7.0,-7.0c0.0,-3.5 2.6,-6.4 6.0,-6.9L11.0,2.0C5.9,2.5 2.0,6.8 2.0,12.0c0.0,5.5 4.5,10.0 10.0,10.0c3.3,0.0 6.2,-1.6 8.1,-4.1l-2.6,-1.5C16.2,18.0 14.2,19.0 12.0,19.0z"/> + <path + android:fillColor="#4DFFFFFF" + android:pathData="M13.0,2.0l0.0,3.0c3.4,0.5 6.0,3.4 6.0,6.9c0.0,0.9 -0.2,1.8 -0.5,2.5l2.6,1.5c0.6,-1.2 0.9,-2.6 0.9,-4.1C22.0,6.8 18.0,2.6 13.0,2.0z"/> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M16.0,11.0l0.0,2.0 -3.0,0.0 0.0,3.0 -2.0,0.0 0.0,-3.0 -3.0,0.0 0.0,-2.0 3.0,0.0 0.0,-3.0 2.0,0.0 0.0,3.0z"/> </vector> diff --git a/packages/SystemUI/res/drawable/ic_data_saver_off.xml b/packages/SystemUI/res/drawable/ic_data_saver_off.xml index 0713548fcd4d..fd9701eded9c 100644 --- a/packages/SystemUI/res/drawable/ic_data_saver_off.xml +++ b/packages/SystemUI/res/drawable/ic_data_saver_off.xml @@ -17,12 +17,12 @@ android:width="24.0dp" android:height="24.0dp" android:viewportWidth="24.0" - android:viewportHeight="24.0"> + android:viewportHeight="24.0" + android:tint="#4DFFFFFF"> <path - android:fillColor="#4DFFFFFF" - android:pathData=" - M9.0,16.0l2.0,0.0L11.0,8.0L9.0,8.0l0.0,8.0z - m3.0,-14.0C6.48,2.0 2.0,6.48 2.0,12.0s4.48,10.0 10.0,10.0 10.0,-4.48 10.0,-10.0S17.52,2.0 12.0,2.0z - m0.0,18.0c-4.41,0.0 -8.0,-3.59 -8.0,-8.0s3.59,-8.0 8.0,-8.0 8.0,3.59 8.0,8.0 -3.59,8.0 -8.0,8.0z - m1.0,-4.0l2.0,0.0l0.0,-8.0l-2.0,0.0l0.0,8.0z"/> + android:fillColor="#FFFFFFFF" + android:pathData="M12.0,19.0c-3.9,0.0 -7.0,-3.1 -7.0,-7.0c0.0,-3.5 2.6,-6.4 6.0,-6.9L11.0,2.0C5.9,2.5 2.0,6.8 2.0,12.0c0.0,5.5 4.5,10.0 10.0,10.0c3.3,0.0 6.2,-1.6 8.1,-4.1l-2.6,-1.5C16.2,18.0 14.2,19.0 12.0,19.0z"/> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M13.0,2.0l0.0,3.0c3.4,0.5 6.0,3.4 6.0,6.9c0.0,0.9 -0.2,1.8 -0.5,2.5l2.6,1.5c0.6,-1.2 0.9,-2.6 0.9,-4.1C22.0,6.8 18.0,2.6 13.0,2.0z"/> </vector> diff --git a/packages/SystemUI/res/drawable/stat_sys_data_saver.xml b/packages/SystemUI/res/drawable/stat_sys_data_saver.xml index a45f9a2ac798..c36678d489cd 100644 --- a/packages/SystemUI/res/drawable/stat_sys_data_saver.xml +++ b/packages/SystemUI/res/drawable/stat_sys_data_saver.xml @@ -23,10 +23,12 @@ android:viewportHeight="24.0"> <path android:fillColor="#FFFFFFFF" - android:pathData=" - M9.0,16.0l2.0,0.0L11.0,8.0L9.0,8.0l0.0,8.0z - m3.0,-14.0C6.48,2.0 2.0,6.48 2.0,12.0s4.48,10.0 10.0,10.0 10.0,-4.48 10.0,-10.0S17.52,2.0 12.0,2.0z - m0.0,18.0c-4.41,0.0 -8.0,-3.59 -8.0,-8.0s3.59,-8.0 8.0,-8.0 8.0,3.59 8.0,8.0 -3.59,8.0 -8.0,8.0z - m1.0,-4.0l2.0,0.0l0.0,-8.0l-2.0,0.0l0.0,8.0z"/> + android:pathData="M12.0,19.0c-3.9,0.0 -7.0,-3.1 -7.0,-7.0c0.0,-3.5 2.6,-6.4 6.0,-6.9L11.0,2.0C5.9,2.5 2.0,6.8 2.0,12.0c0.0,5.5 4.5,10.0 10.0,10.0c3.3,0.0 6.2,-1.6 8.1,-4.1l-2.6,-1.5C16.2,18.0 14.2,19.0 12.0,19.0z"/> + <path + android:fillColor="#4DFFFFFF" + android:pathData="M13.0,2.0l0.0,3.0c3.4,0.5 6.0,3.4 6.0,6.9c0.0,0.9 -0.2,1.8 -0.5,2.5l2.6,1.5c0.6,-1.2 0.9,-2.6 0.9,-4.1C22.0,6.8 18.0,2.6 13.0,2.0z"/> + <path + android:fillColor="#FFFFFFFF" + android:pathData="M16.0,11.0l0.0,2.0 -3.0,0.0 0.0,3.0 -2.0,0.0 0.0,-3.0 -3.0,0.0 0.0,-2.0 3.0,0.0 0.0,-3.0 2.0,0.0 0.0,3.0z"/> </vector> </inset> diff --git a/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml b/packages/SystemUI/res/layout/keyboard_shortcuts_category_title.xml index 6cb8470eaf21..381fb165df10 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 4845425c233e..087f61eda211 100755 --- a/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/BatteryMeterDrawable.java @@ -452,7 +452,7 @@ public class BatteryMeterDrawable extends Drawable implements DemoMode, 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 @@ public class BatteryMeterDrawable extends Drawable implements DemoMode, 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/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java index 815c6797e0dd..0b6eabaf6e57 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java @@ -41,7 +41,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha private static final String MOVE_FULL_ROWS = "sysui_qs_move_whole_rows"; public static final float EXPANDED_TILE_DELAY = .7f; - private static final float LAST_ROW_EXPANDED_DELAY = .84f; + private static final float LAST_ROW_EXPANDED_DELAY = .86f; private final ArrayList<View> mAllViews = new ArrayList<>(); private final ArrayList<View> mTopFiveQs = new ArrayList<>(); @@ -139,7 +139,9 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha int count = 0; int[] loc1 = new int[2]; int[] loc2 = new int[2]; + int lastXDiff = 0; int lastYDiff = 0; + int lastX = 0; clearAnimationState(); mAllViews.clear(); @@ -155,10 +157,12 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha // Quick tiles. QSTileBaseView quickTileView = mQuickQsPanel.getTileView(tile); + lastX = loc1[0]; getRelativePosition(loc1, quickTileView.getIcon(), mQsContainer); getRelativePosition(loc2, tileIcon, mQsContainer); final int xDiff = loc2[0] - loc1[0]; final int yDiff = loc2[1] - loc1[1]; + lastXDiff = loc1[0] - lastX; lastYDiff = yDiff; // Move the quick tile right from its location to the new one. translationXBuilder.addFloat(quickTileView, "translationX", 0, xDiff); @@ -177,9 +181,20 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha mAllViews.add(tileIcon); mAllViews.add(quickTileView); } else if (mFullRows && isIconInAnimatedRow(count)) { + // TODO: Refactor some of this, it shares a lot with the above block. + // Move the last tile position over by the last difference between quick tiles. + // This makes the extra icons seems as if they are coming from positions in the + // quick panel. + loc1[0] += lastXDiff; + getRelativePosition(loc2, tileIcon, mQsContainer); + final int xDiff = loc2[0] - loc1[0]; + final int yDiff = loc2[1] - loc1[1]; + firstPageBuilder.addFloat(tileView, "translationY", mQsPanel.getHeight(), 0); - translationYBuilder.addFloat(label, "translationY", -lastYDiff, 0); - translationYBuilder.addFloat(tileIcon, "translationY", -lastYDiff, 0); + translationXBuilder.addFloat(tileView, "translationX", -xDiff, 0); + translationYBuilder.addFloat(label, "translationY", -yDiff, 0); + translationYBuilder.addFloat(tileIcon, "translationY", -yDiff, 0); + mAllViews.add(tileIcon); } else { lastRowBuilder.addFloat(tileView, "alpha", 0, 1); diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java index fe7ac711e8c1..b89c2f67a220 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 @@ public class RecentsActivity extends Activity implements ViewTreeObserver.OnPreD 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 77f77393d7ab..71610534f1e6 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 class RecentsActivityLaunchState { 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 class RecentsActivityLaunchState { 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 40613f0d753e..73c6e6e89c58 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 class RecentsConfiguration { 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 7daef649adfc..ffc037dba5e5 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.internal.logging.MetricsLogger; 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 class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener public void onConfigurationChanged() { reloadHeaderBarLayout(); updateHeaderBarLayout(null /* stack */); - Recents.getConfiguration().updateOnConfigurationChange(); } /** @@ -438,7 +436,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener } // Launch the task - ssp.startActivityFromRecents(mContext, toTask.key.id, toTask.title, launchOpts); + ssp.startActivityFromRecents(mContext, toTask.key, toTask.title, launchOpts); } /** @@ -510,7 +508,7 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener MetricsLogger.count(mContext, "overview_affiliated_task_launch", 1); // Launch the task - ssp.startActivityFromRecents(mContext, toTask.key.id, toTask.title, launchOpts); + ssp.startActivityFromRecents(mContext, toTask.key, toTask.title, launchOpts); } public void showNextAffiliatedTask() { @@ -852,13 +850,12 @@ public class RecentsImpl implements ActivityOptions.OnAnimationFinishedListener // 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/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java index 3b759c02c8ca..1a4b40f507e8 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java +++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java @@ -18,6 +18,7 @@ package com.android.systemui.recents.misc; import static android.app.ActivityManager.StackId.DOCKED_STACK_ID; import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; +import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; import static android.app.ActivityManager.StackId.HOME_STACK_ID; import static android.app.ActivityManager.StackId.PINNED_STACK_ID; import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT; @@ -77,6 +78,7 @@ import com.android.internal.os.BackgroundThread; import com.android.systemui.R; import com.android.systemui.recents.RecentsDebugFlags; import com.android.systemui.recents.RecentsImpl; +import com.android.systemui.recents.model.Task; import com.android.systemui.recents.tv.RecentsTvImpl; import com.android.systemui.recents.model.ThumbnailData; @@ -284,7 +286,7 @@ public class SystemServicesProxy { int numTasksToQuery = Math.max(minNumTasksToQuery, numLatestTasks); List<ActivityManager.RecentTaskInfo> tasks = mAm.getRecentTasksForUser(numTasksToQuery, ActivityManager.RECENT_IGNORE_HOME_STACK_TASKS | - ActivityManager.RECENT_INGORE_DOCKED_STACK_TASKS | + ActivityManager.RECENT_INGORE_DOCKED_STACK_TOP_TASK | ActivityManager.RECENT_INGORE_PINNED_STACK_TASKS | ActivityManager.RECENT_IGNORE_UNAVAILABLE | ActivityManager.RECENT_INCLUDE_PROFILES | @@ -962,11 +964,20 @@ public class SystemServicesProxy { } /** Starts an activity from recents. */ - public boolean startActivityFromRecents(Context context, int taskId, String taskName, + public boolean startActivityFromRecents(Context context, Task.TaskKey taskKey, String taskName, ActivityOptions options) { if (mIam != null) { try { - mIam.startActivityFromRecents(taskId, options == null ? null : options.toBundle()); + if (taskKey.stackId == DOCKED_STACK_ID) { + // We show non-visible docked tasks in Recents, but we always want to launch + // them in the fullscreen stack. + if (options == null) { + options = ActivityOptions.makeBasic(); + } + options.setLaunchStackId(FULLSCREEN_WORKSPACE_STACK_ID); + } + mIam.startActivityFromRecents( + taskKey.id, options == null ? null : options.toBundle()); return true; } catch (Exception e) { Log.e(TAG, context.getString(R.string.recents_launch_error_message, taskName), e); diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java index bd3143f688cc..a69f8a2cf399 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvTransitionHelper.java @@ -89,7 +89,7 @@ public class RecentsTvTransitionHelper { private void startTaskActivity(TaskStack stack, Task task, @Nullable TaskCardView taskView, ActivityOptions opts,final ActivityOptions.OnAnimationStartedListener animStartedListener) { SystemServicesProxy ssp = Recents.getSystemServices(); - if (ssp.startActivityFromRecents(mContext, task.key.id, task.title, opts)) { + if (ssp.startActivityFromRecents(mContext, task.key, task.title, opts)) { // Keep track of the index of the task launch int taskIndexFromFront = 0; int taskIndex = stack.indexOfStackTask(task); diff --git a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java index 3d5176f2554e..d966614efb1b 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java +++ b/packages/SystemUI/src/com/android/systemui/recents/tv/views/RecentsTvView.java @@ -109,7 +109,7 @@ public class RecentsTvView extends FrameLayout { Task task = mTaskStackHorizontalView.getFocusedTask(); if (task != null) { SystemServicesProxy ssp = Recents.getSystemServices(); - ssp.startActivityFromRecents(getContext(), task.key.id, task.title, null); + ssp.startActivityFromRecents(getContext(), task.key, task.title, null); return true; } } @@ -123,7 +123,7 @@ public class RecentsTvView extends FrameLayout { Task task = stack.getLaunchTarget(); if (task != null) { SystemServicesProxy ssp = Recents.getSystemServices(); - ssp.startActivityFromRecents(getContext(), task.key.id, task.title, null); + ssp.startActivityFromRecents(getContext(), task.key, task.title, null); return true; } } @@ -140,7 +140,7 @@ public class RecentsTvView extends FrameLayout { TaskCardView tv = taskViews.get(j); if (tv.getTask() == task) { SystemServicesProxy ssp = Recents.getSystemServices(); - ssp.startActivityFromRecents(getContext(), task.key.id, task.title, null); + ssp.startActivityFromRecents(getContext(), task.key, task.title, null); return true; } } diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java index 98616f49ee88..9dc3fb14936a 100644 --- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java @@ -186,7 +186,7 @@ public class RecentsTransitionHelper { ActivityOptions opts, IAppTransitionAnimationSpecsFuture transitionFuture, final ActivityOptions.OnAnimationStartedListener animStartedListener) { SystemServicesProxy ssp = Recents.getSystemServices(); - if (ssp.startActivityFromRecents(mContext, task.key.id, task.title, opts)) { + if (ssp.startActivityFromRecents(mContext, task.key, task.title, opts)) { // Keep track of the index of the task launch int taskIndexFromFront = 0; int taskIndex = stack.indexOfStackTask(task); 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 a1ba49338c36..b23b01f93320 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 @@ public class RecentsView extends FrameLayout { // 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 class RecentsView extends FrameLayout { 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 1c7d6096950e..8db81f73f700 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.Animator; 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 @@ public class TaskStackAnimationHelper { 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 @@ public class TaskStackAnimationHelper { 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 @@ public class TaskStackAnimationHelper { 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 8a1727a20271..4b1faf3ef395 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 @@ public class TaskStackLayoutAlgorithm { 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 90328714c51e..04f153fb7996 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.Context; 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.ConfigurationChangedEvent; 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } // 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal /** * 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 @@ public class TaskStackView extends FrameLayout implements TaskStack.TaskStackCal } 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 ba3e774ea2b7..6746a67ef0bc 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 @@ import android.view.ViewGroup; */ 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 @@ public final class KeyboardShortcutKeysLayout extends ViewGroup { 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 @@ public final class KeyboardShortcutKeysLayout extends ViewGroup { 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 @@ public final class KeyboardShortcutKeysLayout extends ViewGroup { 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 bb43899c44e1..d9bf5390c8b4 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 @@ class CarNavigationBarController { 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 12035b9314e3..023c4d07d708 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 @@ public class NotificationPanelView extends PanelView implements } 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 @@ public class NotificationPanelView extends PanelView implements 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 0e64dcd187b0..15ad1f1b6099 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 @@ public class PipControlsView extends LinearLayout implements PipManager.Listener @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 6976c0b1d312..e3ed92cb5e88 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 @@ public class ZenModePanel extends LinearLayout { = 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 @@ public class ZenModePanel extends LinearLayout { 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 @@ public class ZenModePanel extends LinearLayout { 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 @@ public class ZenModePanel extends LinearLayout { 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 @@ public class ZenModePanel extends LinearLayout { } } + 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 @@ public class ZenModePanel extends LinearLayout { } } - 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 fc9296674d89..f2ef06520b92 100644 --- a/rs/java/android/renderscript/Allocation.java +++ b/rs/java/android/renderscript/Allocation.java @@ -142,7 +142,10 @@ public class Allocation extends BaseObj { } 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 a093d9225deb..e15b785ba718 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 @@ class MagnificationController { 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 @@ class MagnificationController { 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 @@ class MagnificationController { */ 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 @@ class MagnificationController { */ 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 @@ class MagnificationController { * 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 @@ class MagnificationController { 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 @@ class MagnificationController { 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 @@ class MagnificationController { 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 @@ class MagnificationController { 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 b737ae2c3ee4..6288b56d1f1d 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 @@ public class BackupManagerService { 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 @@ public class BackupManagerService { } } + // 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 @@ public class BackupManagerService { 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 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF // ----- 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 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF } 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 f197c1e07665..09f240f4240c 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 @@ public class PackageManagerBackupAgent extends BackupAgent { 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 @@ public class PackageManagerBackupAgent extends BackupAgent { 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 @@ public class PackageManagerBackupAgent extends BackupAgent { 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 @@ public class PackageManagerBackupAgent extends BackupAgent { 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 @@ public class PackageManagerBackupAgent extends BackupAgent { } 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/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java index e7db2a87c865..57ba1b9ff69b 100644 --- a/services/core/java/com/android/server/AppOpsService.java +++ b/services/core/java/com/android/server/AppOpsService.java @@ -25,7 +25,6 @@ import java.io.IOException; import java.io.PrintWriter; import java.nio.charset.StandardCharsets; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; @@ -1645,7 +1644,9 @@ public class AppOpsService extends IAppOpsService.Stub { int userId = UserHandle.USER_SYSTEM; String packageName; String opStr; + String modeStr; int op; + int mode; int packageUid; Shell(IAppOpsService iface, AppOpsService internal) { @@ -1681,6 +1682,59 @@ public class AppOpsService extends IAppOpsService.Stub { } } + int strModeToMode(String modeStr, PrintWriter err) { + switch (modeStr) { + case "allow": + return AppOpsManager.MODE_ALLOWED; + case "deny": + return AppOpsManager.MODE_ERRORED; + case "ignore": + return AppOpsManager.MODE_IGNORED; + case "default": + return AppOpsManager.MODE_DEFAULT; + } + try { + return Integer.parseInt(modeStr); + } catch (NumberFormatException e) { + } + err.println("Error: Mode " + modeStr + " is not valid"); + return -1; + } + + int parseUserOpMode(int defMode, PrintWriter err) throws RemoteException { + userId = UserHandle.USER_CURRENT; + opStr = null; + modeStr = null; + for (String argument; (argument = getNextArg()) != null;) { + if ("--user".equals(argument)) { + userId = UserHandle.parseUserArg(getNextArgRequired()); + } else { + if (opStr == null) { + opStr = argument; + } else if (modeStr == null) { + modeStr = argument; + break; + } + } + } + if (opStr == null) { + err.println("Error: Operation not specified."); + return -1; + } + op = strOpToOp(opStr, err); + if (op < 0) { + return -1; + } + if (modeStr != null) { + if ((mode=strModeToMode(modeStr, err)) < 0) { + return -1; + } + } else { + mode = defMode; + } + return 0; + } + int parseUserPackageOp(boolean reqOp, PrintWriter err) throws RemoteException { userId = UserHandle.USER_CURRENT; packageName = null; @@ -1742,6 +1796,8 @@ public class AppOpsService extends IAppOpsService.Stub { pw.println(" Set the mode for a particular application and operation."); pw.println(" get [--user <USER_ID>] <PACKAGE> [<OP>]"); pw.println(" Return the mode for a particular application and optional operation."); + pw.println(" query-op [--user <USER_ID>] <OP> [<MODE>]"); + pw.println(" Print all packages that currently have the given op in the given mode."); pw.println(" reset [--user <USER_ID>] [<PACKAGE>]"); pw.println(" Reset the given application or all applications to default modes."); pw.println(" write-settings"); @@ -1775,23 +1831,9 @@ public class AppOpsService extends IAppOpsService.Stub { return -1; } - final int mode; - switch (modeStr) { - case "allow": - mode = AppOpsManager.MODE_ALLOWED; - break; - case "deny": - mode = AppOpsManager.MODE_ERRORED; - break; - case "ignore": - mode = AppOpsManager.MODE_IGNORED; - break; - case "default": - mode = AppOpsManager.MODE_DEFAULT; - break; - default: - err.println("Error: Mode " + modeStr + " is not valid,"); - return -1; + final int mode = shell.strModeToMode(modeStr, err); + if (mode < 0) { + return -1; } shell.mInterface.setMode(shell.op, shell.packageUid, shell.packageName, mode); @@ -1856,6 +1898,34 @@ public class AppOpsService extends IAppOpsService.Stub { } return 0; } + case "query-op": { + int res = shell.parseUserOpMode(AppOpsManager.MODE_IGNORED, err); + if (res < 0) { + return res; + } + List<AppOpsManager.PackageOps> ops = shell.mInterface.getPackagesForOps( + new int[] {shell.op}); + if (ops == null || ops.size() <= 0) { + pw.println("No operations."); + return 0; + } + for (int i=0; i<ops.size(); i++) { + final AppOpsManager.PackageOps pkg = ops.get(i); + boolean hasMatch = false; + final List<AppOpsManager.OpEntry> entries = ops.get(i).getOps(); + for (int j=0; j<entries.size(); j++) { + AppOpsManager.OpEntry ent = entries.get(j); + if (ent.getOp() == shell.op && ent.getMode() == shell.mode) { + hasMatch = true; + break; + } + } + if (hasMatch) { + pw.println(pkg.getPackageName()); + } + } + return 0; + } case "reset": { String packageName = null; int userId = UserHandle.USER_CURRENT; diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java index 45008dcf72a1..9e2f11670017 100644 --- a/services/core/java/com/android/server/MountService.java +++ b/services/core/java/com/android/server/MountService.java @@ -165,6 +165,7 @@ class MountService extends IMountService.Stub public void onStart() { mMountService = new MountService(getContext()); publishBinderService("mount", mMountService); + mMountService.start(); } @Override @@ -430,9 +431,13 @@ class MountService extends IMountService.Stub = { "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 @@ class MountService extends IMountService.Stub 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 @@ class MountService extends IMountService.Stub } } + 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 aee28fb6b295..000000000000 --- 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/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index f51fb6c68cd4..f99caba59b40 100755 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -43,7 +43,8 @@ import android.os.TransactionTooLargeException; import android.util.ArrayMap; import android.util.ArraySet; -import com.android.internal.app.ProcessStats; +import com.android.internal.app.procstats.ProcessStats; +import com.android.internal.app.procstats.ServiceState; import com.android.internal.os.BatteryStatsImpl; import com.android.internal.os.TransferPipe; import com.android.internal.util.FastPrintWriter; @@ -61,7 +62,6 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; -import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; @@ -480,7 +480,7 @@ public final class ActiveServices { ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r, boolean callerFg, boolean addToStarting) throws TransactionTooLargeException { - ProcessStats.ServiceState stracker = r.getTracker(); + ServiceState stracker = r.getTracker(); if (stracker != null) { stracker.setStarted(true, mAm.mProcessStats.getMemFactorLocked(), r.lastActivity); } @@ -932,7 +932,7 @@ public final class ActiveServices { s.lastActivity = SystemClock.uptimeMillis(); if (!s.hasAutoCreateConnections()) { // This is the first binding, let the tracker know. - ProcessStats.ServiceState stracker = s.getTracker(); + ServiceState stracker = s.getTracker(); if (stracker != null) { stracker.setBound(true, mAm.mProcessStats.getMemFactorLocked(), s.lastActivity); @@ -1279,7 +1279,7 @@ public final class ActiveServices { // Before going further -- if this app is not allowed to run in the // background, then at this point we aren't going to let it period. final int allowed = mAm.checkAllowBackgroundLocked( - sInfo.applicationInfo.uid, sInfo.packageName, callingPid); + sInfo.applicationInfo.uid, sInfo.packageName, callingPid, true); if (allowed != ActivityManager.APP_START_MODE_NORMAL) { Slog.w(TAG, "Background execution not allowed: service " + service + " to " + name.flattenToShortString() @@ -1365,7 +1365,7 @@ public final class ActiveServices { long now = SystemClock.uptimeMillis(); if (r.executeNesting == 0) { r.executeFg = fg; - ProcessStats.ServiceState stracker = r.getTracker(); + ServiceState stracker = r.getTracker(); if (stracker != null) { stracker.setExecuting(true, mAm.mProcessStats.getMemFactorLocked(), now); } diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 9070849c8092..cd4d472be42b 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -26,8 +26,8 @@ import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsService; import com.android.internal.app.IVoiceInteractor; import com.android.internal.app.ProcessMap; -import com.android.internal.app.ProcessStats; import com.android.internal.app.SystemUserHomeActivity; +import com.android.internal.app.procstats.ProcessStats; import com.android.internal.os.BackgroundThread; import com.android.internal.os.BatteryStatsImpl; import com.android.internal.os.IResultReceiver; @@ -1488,7 +1488,6 @@ public final class ActivityManagerService extends ActivityManagerNative final ServiceThread mHandlerThread; final MainHandler mHandler; final UiHandler mUiHandler; - final ProcessStartLogger mProcessStartLogger; PackageManagerInternal mPackageManagerInt; @@ -2460,8 +2459,6 @@ public final class ActivityManagerService extends ActivityManagerNative 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 @@ public final class ActivityManagerService extends ActivityManagerNative 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 @@ public final class ActivityManagerService extends ActivityManagerNative } }, dumpheapFilter); - mProcessStartLogger.registerListener(mContext); - // Let system services know. mSystemServiceManager.startBootPhase(SystemService.PHASE_BOOT_COMPLETED); @@ -7535,14 +7535,15 @@ public final class ActivityManagerService extends ActivityManagerNative public int getAppStartMode(int uid, String packageName) { synchronized (this) { - return checkAllowBackgroundLocked(uid, packageName, -1); + return checkAllowBackgroundLocked(uid, packageName, -1, true); } } - int checkAllowBackgroundLocked(int uid, String packageName, int callingPid) { + int checkAllowBackgroundLocked(int uid, String packageName, int callingPid, + boolean allowWhenForeground) { UidRecord uidRec = mActiveUids.get(uid); if (!mLenientBackgroundCheck) { - if (uidRec == null + if (!allowWhenForeground || uidRec == null || uidRec.curProcState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) { if (mAppOpsService.noteOperation(AppOpsManager.OP_RUN_IN_BACKGROUND, uid, packageName) != AppOpsManager.MODE_ALLOWED) { @@ -8845,10 +8846,11 @@ public final class ActivityManagerService extends ActivityManagerNative continue; } } - if ((flags & ActivityManager.RECENT_INGORE_DOCKED_STACK_TASKS) != 0) { - if (tr.stack != null && tr.stack.isDockedStack()) { + if ((flags & ActivityManager.RECENT_INGORE_DOCKED_STACK_TOP_TASK) != 0) { + final ActivityStack stack = tr.stack; + if (stack != null && stack.isDockedStack() && stack.topTask() == tr) { if (DEBUG_RECENTS) Slog.d(TAG_RECENTS, - "Skipping, docked stack task: " + tr); + "Skipping, top task in docked stack: " + tr); continue; } } diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index e4ffde2ad009..a9ef1d60ebf6 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -1871,7 +1871,6 @@ final class ActivityStack { // appropriate for it. mStackSupervisor.mStoppingActivities.remove(r); mStackSupervisor.mGoingToSleepActivities.remove(r); - mStackSupervisor.mWaitingVisibleActivities.remove(r); } catch (Exception e) { // Just skip on any failure; we'll make it // visible when it next restarts. @@ -3090,10 +3089,14 @@ final class ActivityStack { return false; } - if (stack.isHomeStack()) { + final ActivityRecord top = stack.topRunningActivityLocked(); + + if (stack.isHomeStack() && (top == null || !top.visible)) { + // If we will be focusing on the home stack next and its current top activity isn't + // visible, then use the task return to value to determine the home task to display next. return mStackSupervisor.moveHomeStackTaskToTop(taskToReturnTo, reason); } - return mService.setFocusedActivityLocked(stack.topRunningActivityLocked(), myReason); + return mService.setFocusedActivityLocked(top, myReason); } final void stopActivityLocked(ActivityRecord r) { @@ -4784,6 +4787,8 @@ final class ActivityStack { " 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/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index e3ca3ea90443..3bbc4521fda0 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -947,8 +947,6 @@ class ActivityStarter { // We didn't do anything... but it was needed (a.k.a., client don't use that // intent!) And for paranoia, make sure we have correctly resumed the top activity. resumeTargetStackIfNeeded(); - mSupervisor.showNonResizeableDockToastIfNeeded(mStartActivity.task, - preferredLaunchStackId, mTargetStack.mStackId); return START_TASK_TO_FRONT; } } @@ -989,7 +987,7 @@ class ActivityStarter { top.deliverNewIntentLocked( mCallingUid, mStartActivity.intent, mStartActivity.launchedFromPackage); mSupervisor.showNonResizeableDockToastIfNeeded(mStartActivity.task, - preferredLaunchStackId, mTargetStack.mStackId); + preferredLaunchStackId, topStack.mStackId); return START_DELIVERED_TO_TOP; } diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index ffa3b5b2e2e8..3d420471ea82 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 @@ public final class BatteryStatsService extends IBatteryStats.Stub // 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/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 45e3a76d824e..37c7765f26e3 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -50,7 +50,6 @@ import android.util.EventLog; import android.util.Slog; import android.util.TimeUtils; import com.android.server.DeviceIdleController; -import com.android.server.LocalServices; import static com.android.server.am.ActivityManagerDebugConfig.*; @@ -563,7 +562,7 @@ public final class BroadcastQueue { } if (!skip) { final int allowed = mService.checkAllowBackgroundLocked(filter.receiverList.uid, - filter.packageName, -1); + filter.packageName, -1, true); if (allowed == ActivityManager.APP_START_MODE_DISABLED) { Slog.w(TAG, "Background execution not allowed: receiving " + r.intent @@ -1102,21 +1101,21 @@ public final class BroadcastQueue { if (!skip) { final int allowed = mService.checkAllowBackgroundLocked( - info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, -1); + info.activityInfo.applicationInfo.uid, info.activityInfo.packageName, -1, + false); if (allowed != ActivityManager.APP_START_MODE_NORMAL) { // We won't allow this receiver to be launched if the app has been - // completely disabled from launches, or it is delayed and the broadcast - // was not explicitly sent to it and this would result in a new process - // for it being created. + // completely disabled from launches, or it was not explicitly sent + // to it and the app is in a state that should not receive it + // (depending on how checkAllowBackgroundLocked has determined that). if (allowed == ActivityManager.APP_START_MODE_DISABLED) { Slog.w(TAG, "Background execution disabled: receiving " + r.intent + " to " + component.flattenToShortString()); skip = true; - } - if (((r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0) + } else if (((r.intent.getFlags()&Intent.FLAG_RECEIVER_EXCLUDE_BACKGROUND) != 0) || (r.intent.getComponent() == null - && r.intent.getPackage() == null && app == null + && r.intent.getPackage() == null && ((r.intent.getFlags() & Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND) == 0))) { Slog.w(TAG, "Background execution not allowed: receiving " diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index bacbd3e5e25a..0993ce65eb6d 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -24,7 +24,8 @@ import android.util.ArraySet; import android.util.DebugUtils; import android.util.EventLog; import android.util.Slog; -import com.android.internal.app.ProcessStats; +import com.android.internal.app.procstats.ProcessStats; +import com.android.internal.app.procstats.ProcessState; import com.android.internal.os.BatteryStatsImpl; import android.app.ActivityManager; @@ -69,7 +70,7 @@ final class ProcessRecord { IApplicationThread thread; // the actual proc... may be null only if // 'persistent' is true (in which case we // are in the process of launching the app) - ProcessStats.ProcessState baseProcessTracker; + ProcessState baseProcessTracker; BatteryStatsImpl.Uid.Proc curProcBatteryStats; int pid; // The process of this application; 0 if none int[] gids; // The gids this process was launched with @@ -444,7 +445,7 @@ final class ProcessRecord { public void makeActive(IApplicationThread _thread, ProcessStatsService tracker) { if (thread == null) { - final ProcessStats.ProcessState origBase = baseProcessTracker; + final ProcessState origBase = baseProcessTracker; if (origBase != null) { origBase.setState(ProcessStats.STATE_NOTHING, tracker.getMemFactorLocked(), SystemClock.uptimeMillis(), pkgList); @@ -470,7 +471,7 @@ final class ProcessRecord { public void makeInactive(ProcessStatsService tracker) { thread = null; - final ProcessStats.ProcessState origBase = baseProcessTracker; + final ProcessState origBase = baseProcessTracker; if (origBase != null) { if (origBase != null) { origBase.setState(ProcessStats.STATE_NOTHING, @@ -696,7 +697,7 @@ final class ProcessRecord { } pkgList.clear(); - ProcessStats.ProcessState ps = tracker.getProcessStateLocked( + ProcessState ps = tracker.getProcessStateLocked( info.packageName, uid, info.versionCode, processName); ProcessStats.ProcessStateHolder holder = new ProcessStats.ProcessStateHolder( info.versionCode); 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 39fbeb5e1614..000000000000 --- 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/am/ProcessStatsService.java b/services/core/java/com/android/server/am/ProcessStatsService.java index 9634dffa8e2c..8d2b1c2fda79 100644 --- a/services/core/java/com/android/server/am/ProcessStatsService.java +++ b/services/core/java/com/android/server/am/ProcessStatsService.java @@ -28,8 +28,11 @@ import android.util.AtomicFile; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; -import com.android.internal.app.IProcessStats; -import com.android.internal.app.ProcessStats; +import com.android.internal.app.procstats.DumpUtils; +import com.android.internal.app.procstats.IProcessStats; +import com.android.internal.app.procstats.ProcessState; +import com.android.internal.app.procstats.ProcessStats; +import com.android.internal.app.procstats.ServiceState; import com.android.internal.os.BackgroundThread; import java.io.File; @@ -107,12 +110,12 @@ public final class ProcessStatsService extends IProcessStats.Stub { } } - public ProcessStats.ProcessState getProcessStateLocked(String packageName, + public ProcessState getProcessStateLocked(String packageName, int uid, int versionCode, String processName) { return mProcessStats.getProcessStateLocked(packageName, uid, versionCode, processName); } - public ProcessStats.ServiceState getServiceStateLocked(String packageName, int uid, + public ServiceState getServiceStateLocked(String packageName, int uid, int versionCode, String processName, String className) { return mProcessStats.getServiceStateLocked(packageName, uid, versionCode, processName, className); @@ -143,22 +146,10 @@ public final class ProcessStatsService extends IProcessStats.Stub { final SparseArray<ProcessStats.PackageState> vers = uids.valueAt(iuid); for (int iver=vers.size()-1; iver>=0; iver--) { final ProcessStats.PackageState pkg = vers.valueAt(iver); - final ArrayMap<String, ProcessStats.ServiceState> services = pkg.mServices; + final ArrayMap<String, ServiceState> services = pkg.mServices; for (int isvc=services.size()-1; isvc>=0; isvc--) { - final ProcessStats.ServiceState service = services.valueAt(isvc); - if (service.isRestarting()) { - service.setRestarting(true, memFactor, now); - } else if (service.isInUse()) { - if (service.mStartedState != ProcessStats.STATE_NOTHING) { - service.setStarted(true, memFactor, now); - } - if (service.mBoundState != ProcessStats.STATE_NOTHING) { - service.setBound(true, memFactor, now); - } - if (service.mExecState != ProcessStats.STATE_NOTHING) { - service.setExecuting(true, memFactor, now); - } - } + final ServiceState service = services.valueAt(isvc); + service.setMemFactor(memFactor, now); } } } @@ -294,12 +285,11 @@ public final class ProcessStatsService extends IProcessStats.Stub { if (stats.mReadError != null) { Slog.w(TAG, "Ignoring existing stats; " + stats.mReadError); if (DEBUG) { - ArrayMap<String, SparseArray<ProcessStats.ProcessState>> procMap - = stats.mProcesses.getMap(); + ArrayMap<String, SparseArray<ProcessState>> procMap = stats.mProcesses.getMap(); final int NPROC = procMap.size(); for (int ip=0; ip<NPROC; ip++) { Slog.w(TAG, "Process: " + procMap.keyAt(ip)); - SparseArray<ProcessStats.ProcessState> uids = procMap.valueAt(ip); + SparseArray<ProcessState> uids = procMap.valueAt(ip); final int NUID = uids.size(); for (int iu=0; iu<NUID; iu++) { Slog.w(TAG, " Uid " + uids.keyAt(iu) + ": " + uids.valueAt(iu)); @@ -387,13 +377,13 @@ public final class ProcessStatsService extends IProcessStats.Stub { boolean dumpFilteredProcessesCsvLocked(PrintWriter pw, String header, boolean sepScreenStates, int[] screenStates, boolean sepMemStates, int[] memStates, boolean sepProcStates, int[] procStates, long now, String reqPackage) { - ArrayList<ProcessStats.ProcessState> procs = mProcessStats.collectProcessesLocked( + ArrayList<ProcessState> procs = mProcessStats.collectProcessesLocked( screenStates, memStates, procStates, procStates, now, reqPackage, false); if (procs.size() > 0) { if (header != null) { pw.println(header); } - ProcessStats.dumpProcessListCsv(pw, procs, sepScreenStates, screenStates, + DumpUtils.dumpProcessListCsv(pw, procs, sepScreenStates, screenStates, sepMemStates, memStates, sepProcStates, procStates, now); return true; } @@ -668,8 +658,8 @@ public final class ProcessStatsService extends IProcessStats.Stub { } boolean[] sep = new boolean[1]; String[] error = new String[1]; - csvScreenStats = parseStateList(ProcessStats.ADJ_SCREEN_NAMES_CSV, ProcessStats.ADJ_SCREEN_MOD, - args[i], sep, error); + csvScreenStats = parseStateList(DumpUtils.ADJ_SCREEN_NAMES_CSV, + ProcessStats.ADJ_SCREEN_MOD, args[i], sep, error); if (csvScreenStats == null) { pw.println("Error in \"" + args[i] + "\": " + error[0]); dumpHelp(pw); @@ -685,7 +675,8 @@ public final class ProcessStatsService extends IProcessStats.Stub { } boolean[] sep = new boolean[1]; String[] error = new String[1]; - csvMemStats = parseStateList(ProcessStats.ADJ_MEM_NAMES_CSV, 1, args[i], sep, error); + csvMemStats = parseStateList(DumpUtils.ADJ_MEM_NAMES_CSV, 1, args[i], + sep, error); if (csvMemStats == null) { pw.println("Error in \"" + args[i] + "\": " + error[0]); dumpHelp(pw); @@ -701,7 +692,8 @@ public final class ProcessStatsService extends IProcessStats.Stub { } boolean[] sep = new boolean[1]; String[] error = new String[1]; - csvProcStats = parseStateList(ProcessStats.STATE_NAMES_CSV, 1, args[i], sep, error); + csvProcStats = parseStateList(DumpUtils.STATE_NAMES_CSV, 1, args[i], + sep, error); if (csvProcStats == null) { pw.println("Error in \"" + args[i] + "\": " + error[0]); dumpHelp(pw); @@ -839,19 +831,19 @@ public final class ProcessStatsService extends IProcessStats.Stub { if (!csvSepScreenStats) { for (int i=0; i<csvScreenStats.length; i++) { pw.print(" "); - ProcessStats.printScreenLabelCsv(pw, csvScreenStats[i]); + DumpUtils.printScreenLabelCsv(pw, csvScreenStats[i]); } } if (!csvSepMemStats) { for (int i=0; i<csvMemStats.length; i++) { pw.print(" "); - ProcessStats.printMemLabelCsv(pw, csvMemStats[i]); + DumpUtils.printMemLabelCsv(pw, csvMemStats[i]); } } if (!csvSepProcStats) { for (int i=0; i<csvProcStats.length; i++) { pw.print(" "); - pw.print(ProcessStats.STATE_NAMES_CSV[csvProcStats[i]]); + pw.print(DumpUtils.STATE_NAMES_CSV[csvProcStats[i]]); } } pw.println(); diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 87cb40ea3e9f..5075c3aad4a1 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -16,7 +16,7 @@ package com.android.server.am; -import com.android.internal.app.ProcessStats; +import com.android.internal.app.procstats.ServiceState; import com.android.internal.os.BatteryStatsImpl; import com.android.server.LocalServices; import com.android.server.notification.NotificationManagerInternal; @@ -88,8 +88,8 @@ final class ServiceRecord extends Binder { ProcessRecord app; // where this service is running or null. ProcessRecord isolatedProc; // keep track of isolated process, if requested - ProcessStats.ServiceState tracker; // tracking service execution, may be null - ProcessStats.ServiceState restartTracker; // tracking service restart + ServiceState tracker; // tracking service execution, may be null + ServiceState restartTracker; // tracking service restart boolean delayed; // are we waiting to start this service in the background? boolean isForeground; // is service currently in foreground mode? int foregroundId; // Notification ID of last foreground req. @@ -326,7 +326,7 @@ final class ServiceRecord extends Binder { createdFromFg = callerIsFg; } - public ProcessStats.ServiceState getTracker() { + public ServiceState getTracker() { if (tracker != null) { return tracker; } diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java index 27acd91735be..0f1ebebce23e 100644 --- a/services/core/java/com/android/server/am/TaskRecord.java +++ b/services/core/java/com/android/server/am/TaskRecord.java @@ -126,10 +126,14 @@ final class TaskRecord { private static final String ATTR_RESIZE_MODE = "resize_mode"; private static final String ATTR_PRIVILEGED = "privileged"; private static final String ATTR_NON_FULLSCREEN_BOUNDS = "non_fullscreen_bounds"; + private static final String ATTR_MINIMAL_WIDTH = "minimal_width"; + private static final String ATTR_MINIMAL_HEIGHT = "minimal_height"; + private static final String TASK_THUMBNAIL_SUFFIX = "_task_thumbnail"; static final int INVALID_TASK_ID = -1; + static final int INVALID_MINIMAL_SIZE = -1; final int taskId; // Unique identifier for this task. String affinity; // The affinity name for this task, or null; may change identity. @@ -323,7 +327,7 @@ final class TaskRecord { TaskThumbnailInfo lastThumbnailInfo, int taskAffiliation, int prevTaskId, int nextTaskId, int taskAffiliationColor, int callingUid, String callingPackage, int resizeMode, boolean privileged, boolean _realActivitySuspended, - boolean userSetupComplete) { + boolean userSetupComplete, int minimalWidth, int minimalHeight) { mService = service; mFilename = String.valueOf(_taskId) + TASK_THUMBNAIL_SUFFIX + TaskPersister.IMAGE_EXTENSION; @@ -363,8 +367,8 @@ final class TaskRecord { mCallingPackage = callingPackage; mResizeMode = resizeMode; mPrivileged = privileged; - ActivityInfo info = (mActivities.size() > 0) ? mActivities.get(0).info : null; - setMinDimensions(info); + mMinimalWidth = minimalWidth; + mMinimalHeight = minimalHeight; } void touchActiveTime() { @@ -476,8 +480,8 @@ final class TaskRecord { mMinimalWidth = info.windowLayout.minimalWidth; mMinimalHeight = info.windowLayout.minimalHeight; } else { - mMinimalWidth = -1; - mMinimalHeight = -1; + mMinimalWidth = INVALID_MINIMAL_SIZE; + mMinimalHeight = INVALID_MINIMAL_SIZE; } } @@ -1146,6 +1150,8 @@ final class TaskRecord { out.attribute( null, ATTR_NON_FULLSCREEN_BOUNDS, mLastNonFullscreenBounds.flattenToString()); } + out.attribute(null, ATTR_MINIMAL_WIDTH, String.valueOf(mMinimalWidth)); + out.attribute(null, ATTR_MINIMAL_HEIGHT, String.valueOf(mMinimalHeight)); if (affinityIntent != null) { out.startTag(null, TAG_AFFINITYINTENT); @@ -1210,6 +1216,8 @@ final class TaskRecord { int resizeMode = RESIZE_MODE_FORCE_RESIZEABLE; boolean privileged = false; Rect bounds = null; + int minimalWidth = INVALID_MINIMAL_SIZE; + int minimalHeight = INVALID_MINIMAL_SIZE; for (int attrNdx = in.getAttributeCount() - 1; attrNdx >= 0; --attrNdx) { final String attrName = in.getAttributeName(attrNdx); @@ -1277,6 +1285,10 @@ final class TaskRecord { privileged = Boolean.valueOf(attrValue); } else if (ATTR_NON_FULLSCREEN_BOUNDS.equals(attrName)) { bounds = Rect.unflattenFromString(attrValue); + } else if (ATTR_MINIMAL_WIDTH.equals(attrName)) { + minimalWidth = Integer.valueOf(attrValue); + } else if (ATTR_MINIMAL_HEIGHT.equals(attrName)) { + minimalHeight = Integer.valueOf(attrValue); } else { Slog.w(TAG, "TaskRecord: Unknown attribute=" + attrName); } @@ -1337,7 +1349,7 @@ final class TaskRecord { activities, firstActiveTime, lastActiveTime, lastTimeOnTop, neverRelinquishIdentity, taskDescription, thumbnailInfo, taskAffiliation, prevTaskId, nextTaskId, taskAffiliationColor, callingUid, callingPackage, resizeMode, privileged, - realActivitySuspended, userSetupComplete); + realActivitySuspended, userSetupComplete, minimalWidth, minimalHeight); task.updateOverrideConfiguration(bounds); for (int activityNdx = activities.size() - 1; activityNdx >=0; --activityNdx) { @@ -1358,10 +1370,10 @@ final class TaskRecord { // so that the user can not render the task too small to manipulate. We don't need // to do this for the pinned stack as the bounds are controlled by the system. if (stack.mStackId != PINNED_STACK_ID) { - if (minimalWidth == -1) { + if (minimalWidth == INVALID_MINIMAL_SIZE) { minimalWidth = mService.mStackSupervisor.mDefaultMinimalSizeOfResizeableTask; } - if (minimalHeight == -1) { + if (minimalHeight == INVALID_MINIMAL_SIZE) { minimalHeight = mService.mStackSupervisor.mDefaultMinimalSizeOfResizeableTask; } } 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 000000000000..e5d564dec459 --- /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/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java index 6b00f5fe893b..fa8620f20a8f 100644 --- a/services/core/java/com/android/server/job/JobSchedulerService.java +++ b/services/core/java/com/android/server/job/JobSchedulerService.java @@ -58,8 +58,8 @@ import android.util.SparseIntArray; import android.util.TimeUtils; import com.android.internal.app.IBatteryStats; +import com.android.internal.app.procstats.ProcessStats; import com.android.internal.util.ArrayUtils; -import com.android.internal.app.ProcessStats; import com.android.server.DeviceIdleController; import com.android.server.LocalServices; import com.android.server.job.JobStore.JobStatusFunctor; diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index c303cebe2733..7c71fbc82e77 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 @@ public class LauncherAppsService extends SystemService { 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 @@ public class LauncherAppsService extends SystemService { 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 @@ public class LauncherAppsService extends SystemService { 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); + + // 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(callingPackage, - packageName, shortcutId, user.getIdentifier()); + final Intent intent = mShortcutServiceInternal.createShortcutIntent(getCallingUserId(), + callingPackage, packageName, shortcutId, user.getIdentifier()); if (intent == null) { return false; } @@ -713,9 +719,11 @@ public class LauncherAppsService extends SystemService { 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 @@ public class LauncherAppsService extends SystemService { // (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 d2fd762c7f3f..442643a976e2 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.ActivityManagerNative; 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ public class PackageManagerService extends IPackageManager.Stub { // 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); 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 000000000000..c47dda4681f9 --- /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 740a8f768139..7699f305fb17 100644 --- a/services/core/java/com/android/server/pm/ShortcutLauncher.java +++ b/services/core/java/com/android/server/pm/ShortcutLauncher.java @@ -32,7 +32,7 @@ import java.util.List; /** * Launcher information used by {@link ShortcutService}. */ -class ShortcutLauncher implements ShortcutPackageItem { +class ShortcutLauncher extends ShortcutPackageItem { private static final String TAG = ShortcutService.TAG; static final String TAG_ROOT = "launcher-pins"; @@ -40,37 +40,38 @@ class ShortcutLauncher implements ShortcutPackageItem { 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 @@ class ShortcutLauncher implements ShortcutPackageItem { // Pin shortcuts. Make sure only pin the ones that were visible to the caller. // i.e. a non-dynamic, pinned shortcut by *other launchers* shouldn't be pinned here. - final ShortcutPackage packageShortcuts = - s.getPackageShortcutsLocked(packageName, mUserId); final ArraySet<String> newSet = new ArraySet<>(); for (int i = 0; i < idSize; i++) { @@ -96,7 +95,7 @@ class ShortcutLauncher implements ShortcutPackageItem { } mPinnedShortcuts.put(packageName, newSet); } - s.getPackageShortcutsLocked(packageName, mUserId).refreshPinnedFlags(s); + packageShortcuts.refreshPinnedFlags(s); } /** @@ -113,15 +112,18 @@ class ShortcutLauncher implements ShortcutPackageItem { /** * Persist. */ - public void saveToXml(XmlSerializer out, boolean forBackup) throws IOException { + @Override + public void saveToXml(XmlSerializer out, boolean forBackup) + throws IOException { final int size = mPinnedShortcuts.size(); if (size == 0) { return; // Nothing to write. } out.startTag(null, TAG_ROOT); - ShortcutService.writeAttr(out, ATTR_PACKAGE_NAME, - mPackageName); + ShortcutService.writeAttr(out, ATTR_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 @@ class ShortcutLauncher implements ShortcutPackageItem { /** * 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 @@ class ShortcutLauncher implements ShortcutPackageItem { } final int depth = parser.getDepth(); final String tag = parser.getName(); - switch (tag) { - case TAG_PACKAGE: { - final String packageName = ShortcutService.parseStringAttribute(parser, - ATTR_PACKAGE_NAME); - ids = new ArraySet<>(); - ret.mPinnedShortcuts.put(packageName, ids); - continue; + if (depth == outerDepth + 1) { + switch (tag) { + case ShortcutPackageInfo.TAG_ROOT: + spi = ShortcutPackageInfo.loadFromXml(parser); + continue; + case TAG_PACKAGE: { + final String packageName = ShortcutService.parseStringAttribute(parser, + ATTR_PACKAGE_NAME); + ids = new ArraySet<>(); + ret.mPinnedShortcuts.put(packageName, ids); + continue; + } } - case TAG_PIN: { - ids.add(ShortcutService.parseStringAttribute(parser, - ATTR_VALUE)); - continue; + } + if (depth == outerDepth + 2) { + switch (tag) { + case TAG_PIN: { + ids.add(ShortcutService.parseStringAttribute(parser, + ATTR_VALUE)); + continue; + } } } - throw ShortcutService.throwForInvalidTag(depth, tag); + ShortcutService.warnForInvalidTag(depth, tag); + } + if (spi != null) { + ret.replacePackageInfo(spi); } return ret; } @@ -183,7 +205,12 @@ class ShortcutLauncher implements ShortcutPackageItem { 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 359ea1c956c5..f9414328885c 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackage.java +++ b/services/core/java/com/android/server/pm/ShortcutPackage.java @@ -17,7 +17,6 @@ package com.android.server.pm; import android.annotation.NonNull; import android.annotation.Nullable; -import android.annotation.UserIdInt; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ShortcutInfo; @@ -41,7 +40,7 @@ import java.util.function.Predicate; /** * Package information used by {@link ShortcutService}. */ -class ShortcutPackage implements ShortcutPackageItem { +class ShortcutPackage extends ShortcutPackageItem { private static final String TAG = ShortcutService.TAG; static final String TAG_ROOT = "package"; @@ -63,12 +62,6 @@ class ShortcutPackage implements ShortcutPackageItem { private static final String ATTR_ICON_RES = "icon-res"; private static final String ATTR_BITMAP_PATH = "bitmap-path"; - @UserIdInt - private final int mUserId; - - @NonNull - private final String mPackageName; - /** * All the shortcuts from the package, keyed on IDs. */ @@ -89,21 +82,23 @@ class ShortcutPackage implements ShortcutPackageItem { */ private long mLastResetTime; - ShortcutPackage(int userId, String packageName) { - mUserId = userId; - mPackageName = packageName; + public ShortcutPackage(int packageUserId, String packageName, ShortcutPackageInfo spi) { + super(packageUserId, packageName, spi != null ? spi : ShortcutPackageInfo.newEmpty()); } - @UserIdInt - public int getUserId() { - return mUserId; + public ShortcutPackage(int packageUserId, String packageName) { + this(packageUserId, packageName, null); } - @NonNull - public String getPackageName() { - return mPackageName; + @Override + public int getOwnerUserId() { + // For packages, always owner user == package user. + return getPackageUserId(); } + /** + * 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 @@ class ShortcutPackage implements ShortcutPackageItem { @NonNull String id) { final ShortcutInfo shortcut = mShortcuts.remove(id); if (shortcut != null) { - s.removeIcon(mUserId, shortcut); + s.removeIcon(getPackageUserId(), shortcut); shortcut.clearFlags(ShortcutInfo.FLAG_DYNAMIC | ShortcutInfo.FLAG_PINNED); } return shortcut; @@ -121,7 +116,7 @@ class ShortcutPackage implements ShortcutPackageItem { void addShortcut(@NonNull ShortcutService s, @NonNull ShortcutInfo newShortcut) { deleteShortcut(s, newShortcut.getId()); - s.saveIconAndFixUpShortcut(mUserId, newShortcut); + s.saveIconAndFixUpShortcut(getPackageUserId(), newShortcut); mShortcuts.put(newShortcut.getId(), newShortcut); } @@ -229,12 +224,13 @@ class ShortcutPackage implements ShortcutPackageItem { } // 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 @@ class ShortcutPackage implements ShortcutPackageItem { * 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 @@ class ShortcutPackage implements ShortcutPackageItem { pw.print(prefix); pw.print("Package: "); - pw.print(mPackageName); + pw.print(getPackageName()); pw.println(); pw.print(prefix); @@ -365,6 +373,9 @@ class ShortcutPackage implements ShortcutPackageItem { pw.print(s.formatTime(mLastResetTime)); pw.println(); + getPackageInfo().dump(s, pw, prefix + " "); + pw.println(); + pw.println(" Shortcuts:"); long totalBitmapSize = 0; final ArrayMap<String, ShortcutInfo> shortcuts = mShortcuts; @@ -391,6 +402,7 @@ class ShortcutPackage implements ShortcutPackageItem { pw.println(")"); } + @Override public void saveToXml(@NonNull XmlSerializer out, boolean forBackup) throws IOException, XmlPullParserException { final int size = mShortcuts.size(); @@ -401,10 +413,11 @@ class ShortcutPackage implements ShortcutPackageItem { out.startTag(null, TAG_ROOT); - ShortcutService.writeAttr(out, ATTR_NAME, mPackageName); + ShortcutService.writeAttr(out, ATTR_NAME, getPackageName()); ShortcutService.writeAttr(out, ATTR_DYNAMIC_COUNT, mDynamicShortcutCount); ShortcutService.writeAttr(out, ATTR_CALL_COUNT, mApiCallCount); ShortcutService.writeAttr(out, ATTR_LAST_RESET, mLastResetTime); + getPackageInfo().saveToXml(out); for (int j = 0; j < size; j++) { saveShortcut(out, mShortcuts.valueAt(j), forBackup); @@ -449,13 +462,14 @@ class ShortcutPackage implements ShortcutPackageItem { out.endTag(null, TAG_SHORTCUT); } - public static ShortcutPackage loadFromXml(XmlPullParser parser, int userId) + public static ShortcutPackage loadFromXml(ShortcutService s, XmlPullParser parser, + int ownerUserId, boolean fromBackup) throws IOException, XmlPullParserException { final String packageName = ShortcutService.parseStringAttribute(parser, ATTR_NAME); - final ShortcutPackage ret = new ShortcutPackage(userId, packageName); + final ShortcutPackage ret = new ShortcutPackage(ownerUserId, packageName); ret.mDynamicShortcutCount = ShortcutService.parseIntAttribute(parser, ATTR_DYNAMIC_COUNT); @@ -463,6 +477,7 @@ class ShortcutPackage implements ShortcutPackageItem { ShortcutService.parseIntAttribute(parser, ATTR_CALL_COUNT); ret.mLastResetTime = ShortcutService.parseLongAttribute(parser, ATTR_LAST_RESET); + ShortcutPackageInfo spi = null; final int outerDepth = parser.getDepth(); int type; @@ -473,15 +488,23 @@ class ShortcutPackage implements ShortcutPackageItem { } final int depth = parser.getDepth(); final String tag = parser.getName(); - switch (tag) { - case TAG_SHORTCUT: - final ShortcutInfo si = parseShortcut(parser, packageName); - - // Don't use addShortcut(), we don't need to save the icon. - ret.mShortcuts.put(si.getId(), si); - continue; + if (depth == outerDepth + 1) { + switch (tag) { + case ShortcutPackageInfo.TAG_ROOT: + spi = ShortcutPackageInfo.loadFromXml(parser); + continue; + case TAG_SHORTCUT: + final ShortcutInfo si = parseShortcut(parser, packageName); + + // Don't use addShortcut(), we don't need to save the icon. + ret.mShortcuts.put(si.getId(), si); + continue; + } } - throw ShortcutService.throwForInvalidTag(depth, tag); + ShortcutService.warnForInvalidTag(depth, tag); + } + if (spi != null) { + ret.replacePackageInfo(spi); } return ret; } @@ -507,8 +530,7 @@ class ShortcutPackage implements ShortcutPackageItem { title = ShortcutService.parseStringAttribute(parser, ATTR_TITLE); intent = ShortcutService.parseIntentAttribute(parser, ATTR_INTENT); weight = (int) ShortcutService.parseLongAttribute(parser, ATTR_WEIGHT); - lastChangedTimestamp = (int) ShortcutService.parseLongAttribute(parser, - ATTR_TIMESTAMP); + lastChangedTimestamp = ShortcutService.parseLongAttribute(parser, ATTR_TIMESTAMP); flags = (int) ShortcutService.parseLongAttribute(parser, ATTR_FLAGS); iconRes = (int) ShortcutService.parseLongAttribute(parser, ATTR_ICON_RES); bitmapPath = ShortcutService.parseStringAttribute(parser, ATTR_BITMAP_PATH); diff --git a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java index 00ea6793f4bc..5f706b83271e 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackageInfo.java +++ b/services/core/java/com/android/server/pm/ShortcutPackageInfo.java @@ -15,14 +15,11 @@ */ package com.android.server.pm; -import android.annotation.NonNull; import android.annotation.UserIdInt; -import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; -import android.content.pm.Signature; import android.util.Slog; -import com.android.internal.util.Preconditions; +import com.android.server.backup.BackupUtils; import libcore.io.Base64; import libcore.util.HexEncoding; @@ -33,34 +30,21 @@ import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.io.PrintWriter; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.util.ArrayList; -import java.util.Arrays; /** * Package information used by {@link android.content.pm.ShortcutManager} for backup / restore. - * - * TODO: The methods about signature hashes are copied from BackupManagerService, which is not - * visible here. Unify the code. */ -class ShortcutPackageInfo implements ShortcutPackageItem { +class ShortcutPackageInfo { private static final String TAG = ShortcutService.TAG; static final String TAG_ROOT = "package-info"; - private static final String ATTR_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 @@ class ShortcutPackageInfo implements ShortcutPackageItem { 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 @@ class ShortcutPackageInfo implements ShortcutPackageItem { return mVersionCode; } - private static byte[] hashSignature(Signature sig) { - try { - MessageDigest digest = MessageDigest.getInstance("SHA-256"); - digest.update(sig.toByteArray()); - return digest.digest(); - } catch (NoSuchAlgorithmException e) { - Slog.w(TAG, "No SHA-256 algorithm found!"); - } - return null; - } - - private static ArrayList<byte[]> hashSignatureArray(Signature[] sigs) { - if (sigs == null) { - return null; - } - - ArrayList<byte[]> hashes = new ArrayList<byte[]>(sigs.length); - for (Signature s : sigs) { - hashes.add(hashSignature(s)); - } - return hashes; - } - - private static boolean signaturesMatch(ArrayList<byte[]> storedSigHashes, PackageInfo target) { - if (target == null) { - return false; - } - - // If the target resides on the system partition, we allow it to restore - // data from the like-named package in a restore set even if the signatures - // do not match. (Unlike general applications, those flashed to the system - // partition will be signed with the device's platform certificate, so on - // different phones the same system app will have different signatures.) - if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { - return true; - } - - // Allow unsigned apps, but not signed on one device and unsigned on the other - // !!! TODO: is this the right policy? - Signature[] deviceSigs = target.signatures; - if ((storedSigHashes == null || storedSigHashes.size() == 0) - && (deviceSigs == null || deviceSigs.length == 0)) { - return true; - } - if (storedSigHashes == null || deviceSigs == null) { - return false; - } - - // !!! TODO: this demands that every stored signature match one - // that is present on device, and does not demand the converse. - // Is this this right policy? - final int nStored = storedSigHashes.size(); - final int nDevice = deviceSigs.length; - - // hash each on-device signature - ArrayList<byte[]> deviceHashes = new ArrayList<byte[]>(nDevice); - for (int i = 0; i < nDevice; i++) { - deviceHashes.add(hashSignature(deviceSigs[i])); - } - - // now ensure that each stored sig (hash) matches an on-device sig (hash) - for (int n = 0; n < nStored; n++) { - boolean match = false; - final byte[] storedHash = storedSigHashes.get(n); - for (int i = 0; i < nDevice; i++) { - if (Arrays.equals(storedHash, deviceHashes.get(i))) { - match = true; - break; - } - } - // match is false when no on-device sig matched one of the stored ones - if (!match) { - return false; - } - } - - return true; - } - public boolean canRestoreTo(PackageInfo target) { if (target.versionCode < mVersionCode) { Slog.w(TAG, String.format("Package current version %d < backed up version %d", target.versionCode, mVersionCode)); return false; } - if (!signaturesMatch(mSigHashes, target)) { + if (!BackupUtils.signaturesMatch(mSigHashes, target)) { Slog.w(TAG, "Package signature mismtach"); return false; } @@ -191,36 +93,34 @@ class ShortcutPackageInfo implements ShortcutPackageItem { } public static ShortcutPackageInfo generateForInstalledPackage( - ShortcutService s, String packageName, @UserIdInt int userId) { - final PackageInfo pi = s.getPackageInfo(packageName, userId, /*signature=*/ true); + 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.getPackageInfo(mPackageName, userId, /*getSignatures=*/ true); + 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 @@ class ShortcutPackageInfo implements ShortcutPackageItem { 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 @@ class ShortcutPackageInfo implements ShortcutPackageItem { } final int depth = parser.getDepth(); final String tag = parser.getName(); - switch (tag) { - case TAG_SIGNATURE: { - final String hash = ShortcutService.parseStringAttribute( - parser, ATTR_SIGNATURE_HASH); - hashes.add(Base64.decode(hash.getBytes())); - continue; + + if (depth == outerDepth + 1) { + switch (tag) { + case TAG_SIGNATURE: { + final String hash = ShortcutService.parseStringAttribute( + parser, ATTR_SIGNATURE_HASH); + hashes.add(Base64.decode(hash.getBytes())); + continue; + } } } - throw ShortcutService.throwForInvalidTag(depth, tag); + ShortcutService.warnForInvalidTag(depth, tag); } - return new ShortcutPackageInfo(packageName, 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 526c84db6963..de2709d3d7ac 100644 --- a/services/core/java/com/android/server/pm/ShortcutPackageItem.java +++ b/services/core/java/com/android/server/pm/ShortcutPackageItem.java @@ -17,15 +17,69 @@ package com.android.server.pm; import android.annotation.NonNull; +import com.android.internal.util.Preconditions; + import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import java.io.IOException; -public interface ShortcutPackageItem { +abstract class ShortcutPackageItem { + private final int mPackageUserId; + private final String mPackageName; + + private ShortcutPackageInfo mPackageInfo; + + protected ShortcutPackageItem(int packageUserId, @NonNull String packageName, + @NonNull ShortcutPackageInfo packageInfo) { + mPackageUserId = packageUserId; + mPackageName = Preconditions.checkStringNotEmpty(packageName); + mPackageInfo = Preconditions.checkNotNull(packageInfo); + } + + /** + * ID of the user who actually has this package running on. For {@link ShortcutPackage}, + * this is the same thing as {@link #getOwnerUserId}, but if it's a {@link ShortcutLauncher} and + * {@link #getOwnerUserId} is of a work profile, then this ID could be the user who owns the + * profile. + */ + public int getPackageUserId() { + return mPackageUserId; + } + + /** + * ID of the user who sees the shortcuts from this instance. + */ + public abstract int getOwnerUserId(); + @NonNull - String getPackageName(); + public String getPackageName() { + return mPackageName; + } + + public ShortcutPackageInfo getPackageInfo() { + return mPackageInfo; + } + + /** + * Should be only used when loading from a file.o + */ + protected void replacePackageInfo(@NonNull ShortcutPackageInfo packageInfo) { + mPackageInfo = Preconditions.checkNotNull(packageInfo); + } + + public void refreshPackageInfoAndSave(ShortcutService s) { + mPackageInfo.refresh(s, this); + s.scheduleSaveUser(getOwnerUserId()); + } + + public void ensureNotShadowAndSave(ShortcutService s) { + if (mPackageInfo.isShadow()) { + mPackageInfo.setShadow(false); + s.scheduleSaveUser(getOwnerUserId()); + } + } - void saveToXml(@NonNull XmlSerializer out, boolean forBackup) + public abstract void saveToXml(@NonNull XmlSerializer out, boolean forBackup) throws IOException, XmlPullParserException; } diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 0b33adace0d4..76a2dfadc264 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; 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.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.content.PackageMonitor; import com.android.internal.os.BackgroundThread; -import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.Preconditions; import com.android.server.LocalServices; import com.android.server.SystemService; +import com.android.server.pm.ShortcutUser.PackageWithUser; import libcore.io.IoUtils; @@ -85,6 +84,10 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; @@ -92,6 +95,7 @@ import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.io.PrintWriter; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; @@ -104,14 +108,7 @@ import java.util.function.Predicate; * * - 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). * @@ -260,6 +257,11 @@ public class ShortcutService extends IShortcutService.Stub { @GuardedBy("mLock") private List<Integer> mDirtyUserIds = new ArrayList<>(); + private static final int PACKAGE_MATCH_FLAGS = + PackageManager.MATCH_DIRECT_BOOT_AWARE + | PackageManager.MATCH_DIRECT_BOOT_UNAWARE + | PackageManager.MATCH_UNINSTALLED_PACKAGES; + public ShortcutService(Context context) { this(context, BackgroundThread.get().getLooper()); } @@ -450,16 +452,24 @@ public class ShortcutService extends IShortcutService.Stub { 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; } } @@ -625,31 +635,45 @@ public class ShortcutService extends IShortcutService.Stub { } path.mkdirs(); final AtomicFile file = new AtomicFile(path); - FileOutputStream outs = null; + FileOutputStream os = null; try { - outs = file.startWrite(); - - // Write to XML - XmlSerializer out = new FastXmlSerializer(); - out.setOutput(outs, StandardCharsets.UTF_8.name()); - out.startDocument(null, true); - - getUserShortcutsLocked(userId).saveToXml(this, out, /* forBackup= */ false); + os = file.startWrite(); - out.endDocument(); + saveUserInternalLocked(userId, os, /* forBackup= */ false); - // 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); @@ -667,30 +691,8 @@ public class ShortcutService extends IShortcutService.Stub { } return null; } - ShortcutUser ret = null; try { - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(in, StandardCharsets.UTF_8.name()); - - int type; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { - if (type != XmlPullParser.START_TAG) { - continue; - } - final int depth = parser.getDepth(); - - final String tag = parser.getName(); - if (DEBUG_LOAD) { - Slog.d(TAG, String.format("depth=%d type=%d name=%s", - depth, type, tag)); - } - if ((depth == 1) && ShortcutUser.TAG_ROOT.equals(tag)) { - ret = ShortcutUser.loadFromXml(parser, userId); - continue; - } - throwForInvalidTag(depth, tag); - } - return ret; + return loadUserInternal(userId, in, /* forBackup= */ false); } catch (IOException|XmlPullParserException e) { Slog.e(TAG, "Failed to read file " + file.getBaseFile(), e); return null; @@ -699,6 +701,36 @@ public class ShortcutService extends IShortcutService.Stub { } } + private ShortcutUser loadUserInternal(@UserIdInt int userId, InputStream is, + boolean fromBackup) throws XmlPullParserException, IOException { + + final BufferedInputStream bis = new BufferedInputStream(is); + + ShortcutUser ret = null; + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(bis, StandardCharsets.UTF_8.name()); + + int type; + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { + if (type != XmlPullParser.START_TAG) { + continue; + } + final int depth = parser.getDepth(); + + final String tag = parser.getName(); + if (DEBUG_LOAD) { + Slog.d(TAG, String.format("depth=%d type=%d name=%s", + depth, type, tag)); + } + if ((depth == 1) && ShortcutUser.TAG_ROOT.equals(tag)) { + ret = ShortcutUser.loadFromXml(this, parser, userId, fromBackup); + continue; + } + throwForInvalidTag(depth, tag); + } + return ret; + } + private void scheduleSaveBaseState() { scheduleSaveInner(UserHandle.USER_NULL); // Special case -- use USER_NULL for base state. } @@ -820,8 +852,8 @@ public class ShortcutService extends IShortcutService.Stub { @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 === @@ -1034,6 +1066,10 @@ public class ShortcutService extends IShortcutService.Stub { Preconditions.checkState(isCallerShell(), "Caller must be shell"); } + private void enforceSystem() { + Preconditions.checkState(isCallerSystem(), "Caller must be system"); + } + private void verifyCaller(@NonNull String packageName, @UserIdInt int userId) { Preconditions.checkStringNotEmpty(packageName, "packageName"); @@ -1053,19 +1089,6 @@ public class ShortcutService extends IShortcutService.Stub { throw new SecurityException("Caller UID= doesn't own " + packageName); } - // Test overrides it. - int injectGetPackageUid(@NonNull String packageName, @UserIdInt int userId) { - try { - return mContext.getPackageManager().getPackageUidAsUser(packageName, - PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE - | PackageManager.MATCH_UNINSTALLED_PACKAGES, - userId); - } catch (NameNotFoundException e) { - return -1; - } - } - void postToHandler(Runnable r) { mHandler.post(r); } @@ -1187,10 +1210,10 @@ public class ShortcutService extends IShortcutService.Stub { final int size = newShortcuts.size(); synchronized (mLock) { - getUserShortcutsLocked(userId).ensurePackageInfo(this, packageName, userId); - final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); + ps.ensureNotShadowAndSave(this); + // Throttling. if (!ps.tryApiCall(this)) { return false; @@ -1224,10 +1247,10 @@ public class ShortcutService extends IShortcutService.Stub { final int size = newShortcuts.size(); synchronized (mLock) { - getUserShortcutsLocked(userId).ensurePackageInfo(this, packageName, userId); - final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); + ps.ensureNotShadowAndSave(this); + // Throttling. if (!ps.tryApiCall(this)) { return false; @@ -1263,10 +1286,10 @@ public class ShortcutService extends IShortcutService.Stub { verifyCaller(packageName, userId); synchronized (mLock) { - getUserShortcutsLocked(userId).ensurePackageInfo(this, packageName, userId); - final ShortcutPackage ps = getPackageShortcutsLocked(packageName, userId); + ps.ensureNotShadowAndSave(this); + // Throttling. if (!ps.tryApiCall(this)) { return false; @@ -1332,8 +1355,7 @@ public class ShortcutService extends IShortcutService.Stub { 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); } @@ -1471,22 +1493,29 @@ public class ShortcutService extends IShortcutService.Stub { // === House keeping === @VisibleForTesting - void cleanUpPackageLocked(String packageName, int userId) { - final boolean wasUserLoaded = isUserLoadedLocked(userId); + void cleanUpPackageLocked(String packageName, int owningUserId, int packageUserId) { + + // TODO Don't remove shadow packages' information. + + final boolean wasUserLoaded = isUserLoadedLocked(owningUserId); - final ShortcutUser mUser = getUserShortcutsLocked(userId); + 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. @@ -1494,18 +1523,15 @@ public class ShortcutService extends IShortcutService.Stub { 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); } } @@ -1514,7 +1540,7 @@ public class ShortcutService extends IShortcutService.Stub { */ 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) { @@ -1526,14 +1552,14 @@ public class ShortcutService extends IShortcutService.Stub { 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); } @@ -1542,7 +1568,7 @@ public class ShortcutService extends IShortcutService.Stub { 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) { @@ -1562,11 +1588,11 @@ public class ShortcutService extends IShortcutService.Stub { ((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. @@ -1578,40 +1604,75 @@ public class ShortcutService extends IShortcutService.Stub { 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(); } } @@ -1623,7 +1684,8 @@ public class ShortcutService extends IShortcutService.Stub { } @Override - public int getShortcutIconResId(@NonNull String callingPackage, + public int getShortcutIconResId(int launcherUserId, + @NonNull String callingPackage, @NonNull ShortcutInfo shortcut, int userId) { Preconditions.checkNotNull(shortcut, "shortcut"); @@ -1636,7 +1698,8 @@ public class ShortcutService extends IShortcutService.Stub { } @Override - public ParcelFileDescriptor getShortcutIconFd(@NonNull String callingPackage, + public ParcelFileDescriptor getShortcutIconFd(int launcherUserId, + @NonNull String callingPackage, @NonNull ShortcutInfo shortcutIn, int userId) { Preconditions.checkNotNull(shortcutIn, "shortcut"); @@ -1662,8 +1725,9 @@ public class ShortcutService extends IShortcutService.Stub { } @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); } } @@ -1692,27 +1756,30 @@ public class ShortcutService extends IShortcutService.Stub { * 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) { - for (int i = gonePackages.size() - 1; i >= 0; i--) { - handlePackageGone(gonePackages.get(i), userId); + 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--) { + final PackageWithUser pu = gonePackages.get(i); + cleanUpPackageLocked(pu.packageName, userId, pu.userId); + } } } } @@ -1722,28 +1789,18 @@ public class ShortcutService extends IShortcutService.Stub { 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) { - final PackageInfo pi = getPackageInfo(packageName, userId); - if (pi == null) { - Slog.w(TAG, "Package not found: " + packageName); - return; + if (DEBUG) { + 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); } } @@ -1752,54 +1809,121 @@ public class ShortcutService extends IShortcutService.Stub { Slog.d(TAG, String.format("handlePackageRemoved: %s user=%d", packageName, userId)); } synchronized (mLock) { - cleanUpPackageLocked(packageName, userId); - } - } - - private void handlePackageGone(String packageName, @UserIdInt int userId) { - if (DEBUG) { - Slog.d(TAG, String.format("handlePackageGone: %s user=%d", packageName, userId)); - } - synchronized (mLock) { - cleanUpPackageLocked(packageName, userId); + cleanUpPackageLocked(packageName, userId, userId); } } - // === Backup & restore === + // === PackageManager interaction === - PackageInfo getPackageInfo(String packageName, @UserIdInt int userId) { - return getPackageInfo(packageName, userId, /*getSignatures=*/ false); + PackageInfo getPackageInfoWithSignatures(String packageName, @UserIdInt int userId) { + return injectPackageInfo(packageName, userId, true); } - PackageInfo getPackageInfo(String packageName, @UserIdInt int userId, - boolean getSignatures) { - return injectPackageInfo(packageName, userId, getSignatures); + int injectGetPackageUid(@NonNull String packageName, @UserIdInt int userId) { + final long token = injectClearCallingIdentity(); + try { + return mIPackageManager.getPackageUid(packageName, PACKAGE_MATCH_FLAGS + , userId); + } catch (RemoteException e) { + // 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, - PackageManager.MATCH_DIRECT_BOOT_AWARE - | PackageManager.MATCH_DIRECT_BOOT_UNAWARE + return mIPackageManager.getPackageInfo(packageName, PACKAGE_MATCH_FLAGS | (getSignatures ? PackageManager.GET_SIGNATURES : 0) , userId); } catch (RemoteException e) { // Shouldn't happen. Slog.wtf(TAG, "RemoteException", e); return null; + } finally { + injectRestoreCallingIdentity(token); } } - boolean shouldBackupApp(String packageName, int userId) { - final PackageInfo pi = getPackageInfo(packageName, userId); - return (pi != null) && - ((pi.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0); + @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); + } + } + + private boolean isApplicationFlagSet(String packageName, int userId, int flags) { + final ApplicationInfo ai = injectApplicationInfo(packageName, userId); + return (ai != null) && ((ai.flags & flags) == flags); } private boolean isPackageInstalled(String packageName, int userId) { - return getPackageInfo(packageName, userId) != null; + return isApplicationFlagSet(packageName, userId, ApplicationInfo.FLAG_INSTALLED); + } + + // === Backup & restore === + + boolean shouldBackupApp(String packageName, int userId) { + return isApplicationFlagSet(packageName, userId, ApplicationInfo.FLAG_ALLOW_BACKUP); + } + + @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 === @@ -2157,14 +2281,4 @@ public class ShortcutService extends IShortcutService.Stub { 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 1a00cda78816..487558f81e45 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.content.ComponentName; 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 org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.io.PrintWriter; +import java.util.function.Consumer; /** * User information used by {@link ShortcutService}. @@ -41,14 +44,50 @@ class ShortcutUser { 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 @@ class ShortcutUser { 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 @@ class ShortcutUser { 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); - - if (existing != null) { - return; + public void forAllPackageItems(Consumer<ShortcutPackageItem> callback) { + { + final int size = mLaunchers.size(); + for (int i = 0; i < size; i++) { + callback.accept(mLaunchers.valueAt(i)); + } } - if (ShortcutService.DEBUG) { - Slog.d(TAG, String.format("Fetching package info: %s user=%d", packageName, userId)); + { + final int size = mPackages.size(); + for (int i = 0; i < size; i++) { + callback.accept(mPackages.valueAt(i)); + } } - final ShortcutPackageInfo newSpi = ShortcutPackageInfo.generateForInstalledPackage( - s, packageName, userId); - mPackageInfos.put(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 @@ class ShortcutUser { ShortcutService.writeTagValue(out, TAG_LAUNCHER, mLauncherComponent); - { - final int size = mPackageInfos.size(); - for (int i = 0; i < size; i++) { - saveShortcutPackageItem(s, out, mPackageInfos.valueAt(i), forBackup); - } - } + // Can't use forEachPackageItem due to the checked exceptions. { final int size = mLaunchers.size(); for (int i = 0; i < size; i++) { @@ -132,14 +192,19 @@ class ShortcutUser { private void saveShortcutPackageItem(ShortcutService s, XmlSerializer out, ShortcutPackageItem spi, boolean forBackup) throws IOException, XmlPullParserException { - if (forBackup && !s.shouldBackupApp(spi.getPackageName(), mUserId)) { - return; // Don't save. + if (forBackup) { + if (!s.shouldBackupApp(spi.getPackageName(), spi.getPackageUserId())) { + return; // Don't save. + } + if (spi.getPackageUserId() != spi.getOwnerUserId()) { + return; // Don't save cross-user information. + } } spi.saveToXml(out, forBackup); } - public static ShortcutUser loadFromXml(XmlPullParser parser, int userId) - throws IOException, XmlPullParserException { + public static ShortcutUser loadFromXml(ShortcutService s, XmlPullParser parser, int userId, + boolean fromBackup) throws IOException, XmlPullParserException { final ShortcutUser ret = new ShortcutUser(userId); final int outerDepth = parser.getDepth(); @@ -151,37 +216,30 @@ class ShortcutUser { } final int depth = parser.getDepth(); final String tag = parser.getName(); - switch (tag) { - case TAG_LAUNCHER: { - ret.mLauncherComponent = ShortcutService.parseComponentNameAttribute( - parser, ATTR_VALUE); - continue; - } - case ShortcutPackage.TAG_ROOT: { - final ShortcutPackage shortcuts = ShortcutPackage.loadFromXml(parser, userId); - // Don't use addShortcut(), we don't need to save the icon. - ret.getPackages().put(shortcuts.getPackageName(), shortcuts); - continue; - } - - case ShortcutLauncher.TAG_ROOT: { - final ShortcutLauncher shortcuts = - ShortcutLauncher.loadFromXml(parser, userId); - - ret.getLaunchers().put(shortcuts.getPackageName(), shortcuts); - continue; - } - - case ShortcutPackageInfo.TAG_ROOT: { - final ShortcutPackageInfo pi = - ShortcutPackageInfo.loadFromXml(parser); - - ret.getPackageInfos().put(pi.getPackageName(), pi); - continue; + if (depth == outerDepth + 1) { + switch (tag) { + case TAG_LAUNCHER: { + ret.mLauncherComponent = ShortcutService.parseComponentNameAttribute( + parser, ATTR_VALUE); + continue; + } + case ShortcutPackage.TAG_ROOT: { + final ShortcutPackage shortcuts = ShortcutPackage.loadFromXml( + s, parser, userId, fromBackup); + + // Don't use addShortcut(), we don't need to save the icon. + ret.getPackages().put(shortcuts.getPackageName(), shortcuts); + continue; + } + + case ShortcutLauncher.TAG_ROOT: { + ret.addLauncher(ShortcutLauncher.loadFromXml(parser, userId, fromBackup)); + continue; + } } } - throw ShortcutService.throwForInvalidTag(depth, tag); + ShortcutService.warnForInvalidTag(depth, tag); } return ret; } @@ -223,9 +281,5 @@ class ShortcutUser { for (int i = 0; i < mPackages.size(); i++) { mPackages.valueAt(i).dump(s, pw, prefix + " "); } - - for (int i = 0; i < mPackageInfos.size(); i++) { - mPackageInfos.valueAt(i).dump(s, pw, prefix + " "); - } } } diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index 5263c3707929..7f0da1ec669b 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.BroadcastReceiver; 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 @@ public class UserManagerService extends IUserManager.Stub { } // 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 @@ public class UserManagerService extends IUserManager.Stub { updateUserIds(); initDefaultGuestRestrictions(); - writeUserListLP(); writeUserLP(userData); + writeUserListLP(); } private String getOwnerName() { @@ -1542,8 +1541,10 @@ public class UserManagerService extends IUserManager.Stub { 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 @@ public class UserManagerService extends IUserManager.Stub { 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 @@ public class UserManagerService extends IUserManager.Stub { userData.info = userInfo; mUsers.put(userId, userData); } + writeUserLP(userData); writeUserListLP(); if (parent != null) { if (isManagedProfile) { @@ -2196,7 +2198,13 @@ public class UserManagerService extends IUserManager.Stub { } 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 @@ public class UserManagerService extends IUserManager.Stub { 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/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 164536646090..574faa01a241 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -2777,11 +2777,19 @@ public class PhoneWindowManager implements WindowManagerPolicy { int insets = mWindowManagerFuncs.getDockedDividerInsetsLw(); // If the divider is behind the navigation bar, don't animate. - if (mNavigationBar != null + final Rect frame = win.getFrameLw(); + final boolean behindNavBar = mNavigationBar != null && ((mNavigationBarOnBottom - && win.getFrameLw().top + insets >= mNavigationBar.getFrameLw().top) + && frame.top + insets >= mNavigationBar.getFrameLw().top) || (!mNavigationBarOnBottom - && win.getFrameLw().left + insets >= mNavigationBar.getFrameLw().left))) { + && frame.left + insets >= mNavigationBar.getFrameLw().left)); + final boolean landscape = frame.height() > frame.width(); + final boolean offscreenLandscape = landscape && (frame.right - insets <= 0 + || frame.left + insets >= win.getDisplayFrameLw().right); + final boolean offscreenPortrait = !landscape && (frame.top - insets <= 0 + || frame.bottom + insets >= win.getDisplayFrameLw().bottom); + final boolean offscreen = offscreenLandscape || offscreenPortrait; + if (behindNavBar || offscreen) { return 0; } if (transit == TRANSIT_ENTER || transit == TRANSIT_SHOW) { @@ -6291,8 +6299,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public boolean isNavBarForcedShownLw(WindowState windowState) { - return mForceShowSystemBars - && !windowState.getFrameLw().equals(windowState.getDisplayFrameLw()); + return mForceShowSystemBars; } @Override diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java index 2c158188edbc..98d44ac3bffe 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 static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 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 @@ final class AccessibilityController { } } + 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 @@ final class AccessibilityController { return spec; } + public void getMagnificationRegionsLocked(Region outMagnified, Region outAvailable) { + mMagnifedViewport.getBoundsLocked(outMagnified, outAvailable); + } + public void destroyLocked() { mMagnifedViewport.destroyWindow(); } @@ -413,6 +424,7 @@ final class AccessibilityController { 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 @@ final class AccessibilityController { 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 class AccessibilityController { 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 @@ final class AccessibilityController { 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 @@ final class AccessibilityController { 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 @@ final class AccessibilityController { 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 @@ final class AccessibilityController { 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/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 521221136c80..28379f491318 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -429,6 +429,7 @@ class DisplayContent { } if (getDockedStackVisibleForUserLocked() != null) { mDividerControllerLocked.getTouchRegion(mTmpRect); + mTmpRegion.set(mTmpRect); mTouchExcludeRegion.op(mTmpRegion, Op.UNION); } if (mTapDetector != null) { diff --git a/services/core/java/com/android/server/wm/DockedStackDividerController.java b/services/core/java/com/android/server/wm/DockedStackDividerController.java index 6741aba35f3c..68ea4df0c0b0 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 android.view.animation.Interpolator; import com.android.server.wm.DimLayer.DimLayerUser; +import java.util.ArrayList; + /** * Keeps information about the docked stack divider. */ @@ -87,7 +89,7 @@ public class DockedStackDividerController implements DimLayerUser { 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 @@ public class DockedStackDividerController implements DimLayerUser { 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 @@ public class DockedStackDividerController implements DimLayerUser { 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 @@ public class DockedStackDividerController implements DimLayerUser { } mMinimizedDock = minimizedDock; + mAnimatingForIme = false; if (minimizedDock) { if (animate) { startAdjustAnimation(0f, 1f); @@ -358,7 +361,7 @@ public class DockedStackDividerController implements DimLayerUser { } private void startAdjustAnimation(float from, float to) { - mAnimating = true; + mAnimatingForMinimizedDockedStack = true; mAnimationStarted = false; mAnimationStart = from; mAnimationTarget = to; @@ -380,10 +383,45 @@ public class DockedStackDividerController implements DimLayerUser { } 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 @@ public class DockedStackDividerController implements DimLayerUser { } } if (t >= 1.0f) { - mAnimating = false; + mAnimatingForMinimizedDockedStack = false; return false; } else { return true; diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java index c0c1ed8e2d0c..daeb860e2ef5 100644 --- a/services/core/java/com/android/server/wm/Session.java +++ b/services/core/java/com/android/server/wm/Session.java @@ -195,10 +195,8 @@ final class Session extends IWindowSession.Stub @Override public void repositionChild(IWindow window, int left, int top, int right, int bottom, - int requestedWidth, int requestedHeight, long deferTransactionUntilFrame, Rect outFrame) { mService.repositionChild(this, window, left, top, right, bottom, - requestedWidth, requestedHeight, deferTransactionUntilFrame, outFrame); } diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 4e8f19e5fbfd..1f03c041b477 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 @@ class Task implements DimLayer.DimLayerUser { 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 8d67771e0bff..0bf7102ddeb8 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 @@ public class TaskStack implements DimLayer.DimLayerUser, // 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 @@ public class TaskStack implements DimLayer.DimLayerUser, 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 @@ public class TaskStack implements DimLayer.DimLayerUser, 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 @@ public class TaskStack implements DimLayer.DimLayerUser, 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 5771d691c694..1a9e206db986 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2531,7 +2531,6 @@ public class WindowManagerService extends IWindowManager.Stub void repositionChild(Session session, IWindow client, int left, int top, int right, int bottom, - int requestedWidth, int requestedHeight, long deferTransactionUntilFrame, Rect outFrame) { Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "repositionChild"); long origId = Binder.clearCallingIdentity(); @@ -2547,7 +2546,6 @@ public class WindowManagerService extends IWindowManager.Stub "repositionChild called but window is not" + "attached to a parent win=" + win); } - win.setRequestedSize(requestedWidth, requestedHeight); win.mAttrs.x = left; win.mAttrs.y = top; @@ -7380,8 +7378,9 @@ public class WindowManagerService extends IWindowManager.Stub 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(); @@ -7391,12 +7390,14 @@ public class WindowManagerService extends IWindowManager.Stub 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); } } @@ -10725,6 +10726,19 @@ public class WindowManagerService extends IWindowManager.Stub } @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 3b0081d6376f..3e5ddbce5a7b 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 @@ class WindowSurfacePlacer { // 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 @@ class WindowSurfacePlacer { 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 d8b856b9b753..b2cd69d9f371 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 5975405f5216..0a4effb26249 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -153,6 +153,8 @@ public final class SystemServer { "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 @@ public final class SystemServer { if (context.getPackageManager().hasSystemFeature (PackageManager.FEATURE_WATCH)) { - mSystemServiceManager.startService(ThermalObserver.class); + mSystemServiceManager.startService(THERMAL_OBSERVER_CLASS); } } @@ -1023,7 +1025,8 @@ public final class SystemServer { mSystemServiceManager.startService(BACKUP_MANAGER_SERVICE_CLASS); } - if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS)) { + if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_APP_WIDGETS) + || context.getResources().getBoolean(R.bool.config_enableAppWidgetService)) { mSystemServiceManager.startService(APPWIDGET_SERVICE_CLASS); } diff --git a/services/net/java/android/net/apf/ApfCapabilities.java b/services/net/java/android/net/apf/ApfCapabilities.java index f169411b385c..0ec50c485ad0 100644 --- a/services/net/java/android/net/apf/ApfCapabilities.java +++ b/services/net/java/android/net/apf/ApfCapabilities.java @@ -43,4 +43,9 @@ public class ApfCapabilities { 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 66c7909e5b2a..c25fae306c39 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.State; 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 class IpManager extends StateMachine { } } + 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 class IpManager extends StateMachine { } } - 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 a30d2b5caeef..f034d55d9736 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.anyString; 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.SystemService; 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 @@ public class ShortcutManagerTest extends InstrumentationTestCase { */ 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 @@ public class ShortcutManagerTest extends InstrumentationTestCase { } /** 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 @@ public class ShortcutManagerTest extends InstrumentationTestCase { } /** 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 @@ public class ShortcutManagerTest extends InstrumentationTestCase { void injectRestoreCallingIdentity(long token) { mInjectedCallingUid = (int) token; } + + @Override + public void startActivityAsUser(@RequiresPermission Intent intent, @Nullable Bundle options, + UserHandle userId) { + } } /** ShortcutService with injection override methods. */ @@ -261,6 +271,12 @@ public class ShortcutManagerTest extends InstrumentationTestCase { } @Override + ApplicationInfo injectApplicationInfo(String packageName, @UserIdInt int userId) { + PackageInfo pi = injectPackageInfo(packageName, userId, /* getSignatures= */ false); + return pi != null ? pi.applicationInfo : null; + } + + @Override void postToHandler(Runnable r) { final long token = mContext.injectClearCallingIdentity(); super.postToHandler(r); @@ -304,9 +320,17 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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 @@ -327,6 +351,11 @@ public class ShortcutManagerTest extends InstrumentationTestCase { r.run(); mContext.injectRestoreCallingIdentity(token); } + + @Override + int injectBinderCallingUid() { + return mInjectedCallingUid; + } } private class LauncherAppsTestable extends LauncherApps { @@ -365,6 +394,8 @@ public class ShortcutManagerTest extends InstrumentationTestCase { private Map<String, PackageInfo> mInjectedPackages; + private ArrayList<PackageWithUser> mUninstalledPackages; + private PackageManager mMockPackageManager; private PackageManagerInternal mMockPackageManagerInternal; private UserManager mMockUserManager; @@ -387,6 +418,13 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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; @@ -404,7 +442,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { protected void setUp() throws Exception { super.setUp(); - mServiceContext = new ServiceContext(); + mServiceContext = spy(new ServiceContext()); mClientContext = new ClientContext(); mMockPackageManager = mock(PackageManager.class); @@ -422,6 +460,8 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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. @@ -460,6 +500,10 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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++){ @@ -473,6 +517,8 @@ public class ShortcutManagerTest extends InstrumentationTestCase { pi.packageName = packageName; pi.applicationInfo = new ApplicationInfo(); pi.applicationInfo.uid = uid; + pi.applicationInfo.flags = ApplicationInfo.FLAG_INSTALLED + | ApplicationInfo.FLAG_ALLOW_BACKUP; pi.versionCode = version; pi.signatures = genSignatures(signatures); @@ -483,6 +529,10 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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); @@ -493,6 +543,9 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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; @@ -744,8 +797,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { @NonNull private List<ShortcutInfo> assertShortcutIds(@NonNull List<ShortcutInfo> actualShortcuts, String... expectedIds) { - assertEquals(expectedIds.length, actualShortcuts.size()); - final HashSet<String> expected = new HashSet<>(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()); @@ -876,6 +928,21 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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()); @@ -905,18 +972,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase { assertTrue(b == null || b.size() == 0); } - private void assertShortcutPackageInfo(String packageName, int userId, int expectedVersion) { - ShortcutPackageInfo spi = mService.getPackageInfoForTest(packageName, userId); - assertNotNull(spi); - assertEquals(expectedVersion, spi.getVersionCode()); - - assertTrue(spi.canRestoreTo(genPackage(packageName, /*uid*/ 0, 9999999, packageName))); - } - - private void assertNoShortcutPackageInfo(String packageName, int userId) { - assertNull(mService.getPackageInfoForTest(packageName, userId)); - } - private ShortcutInfo getPackageShortcut(String packageName, String shortcutId, int userId) { return mService.getPackageShortcutForTest(packageName, shortcutId, userId); } @@ -929,6 +984,39 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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()); } @@ -1122,26 +1210,21 @@ public class ShortcutManagerTest extends InstrumentationTestCase { /* 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()); @@ -1152,18 +1235,13 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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")))); }); } @@ -1176,7 +1254,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { assertEquals(3, mManager.getRemainingCallCount()); - assertTrue(mManager.setDynamicShortcuts(Arrays.asList(si1))); + assertTrue(mManager.setDynamicShortcuts(list(si1))); assertEquals(2, mManager.getRemainingCallCount()); assertShortcutIds(assertAllNotKeyFieldsOnly( mManager.getDynamicShortcuts()), @@ -1201,7 +1279,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase { runWithCaller(CALLING_PACKAGE_2, USER_10, () -> { assertTrue(mManager.addDynamicShortcut(makeShortcut("s1"))); - assertShortcutPackageInfo(CALLING_PACKAGE_2, USER_10, 2); }); } @@ -1210,7 +1287,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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"); @@ -1252,7 +1329,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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"); @@ -1269,7 +1346,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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 @@ -1281,62 +1358,62 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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()); @@ -1346,7 +1423,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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()); } @@ -1354,7 +1431,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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()); @@ -1373,7 +1450,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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. @@ -1384,21 +1461,21 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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; @@ -1409,11 +1486,11 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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. @@ -1422,37 +1499,37 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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() { @@ -1469,7 +1546,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { // 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), @@ -1489,7 +1566,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { // 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) @@ -1507,26 +1584,25 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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 @@ -1621,7 +1697,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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, @@ -1650,7 +1726,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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() @@ -1675,7 +1751,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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"), @@ -1684,7 +1760,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { ))); }); runWithCaller(CALLING_PACKAGE_2, UserHandle.USER_SYSTEM, () -> { - assertTrue(mManager.setDynamicShortcuts(Arrays.asList( + assertTrue(mManager.setDynamicShortcuts(list( makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3"), @@ -1693,9 +1769,9 @@ public class ShortcutManagerTest extends InstrumentationTestCase { ))); }); 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, () -> { @@ -1735,7 +1811,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { .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() @@ -1749,7 +1825,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { .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, () -> { @@ -1797,12 +1873,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { // TODO Check bitmap removal too. runWithCaller(CALLING_PACKAGE_2, USER_11, () -> { - assertNoShortcutPackageInfo(CALLING_PACKAGE_2, USER_11); - - mManager.updateShortcuts(Arrays.asList()); - - // Even an empty update call will populate the package info. - assertShortcutPackageInfo(CALLING_PACKAGE_2, USER_11, 2); + mManager.updateShortcuts(list()); }); } @@ -1830,17 +1901,17 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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); @@ -1878,7 +1949,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { // Pin some shortcuts. mLauncherApps.pinShortcuts(CALLING_PACKAGE_2, - Arrays.asList("s3", "s4"), getCallingUser()); + list("s3", "s4"), getCallingUser()); // Pinned ones only assertAllPinned(assertAllHaveTitle(assertAllNotHaveIntents(assertShortcutIds( @@ -1921,7 +1992,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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); @@ -1933,14 +2004,14 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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(); @@ -1959,19 +2030,19 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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()); } @@ -1982,38 +2053,31 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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. @@ -2058,32 +2122,24 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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(); @@ -2155,18 +2211,12 @@ public class ShortcutManagerTest extends InstrumentationTestCase { | ShortcutQuery.FLAG_GET_DYNAMIC), getCallingUser())), "s2"); - assertNoShortcutPackageInfo(LAUNCHER_2, USER_0); - assertNoShortcutPackageInfo(LAUNCHER_2, USER_10); - // Now pin some. mLauncherApps.pinShortcuts(CALLING_PACKAGE_1, - 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, @@ -2182,12 +2232,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase { "s2"); }); - assertShortcutPackageInfo(CALLING_PACKAGE_1, USER_0, 1); - assertShortcutPackageInfo(CALLING_PACKAGE_2, USER_0, 2); - assertNoShortcutPackageInfo(CALLING_PACKAGE_3, USER_0); - assertShortcutPackageInfo(LAUNCHER_1, USER_0, 4); - assertShortcutPackageInfo(LAUNCHER_2, USER_0, 5); - // Re-initialize and load from the files. mService.saveDirtyInfo(); initService(); @@ -2196,12 +2240,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase { mService.handleUnlockUser(USER_0); // Make sure package info is restored too. - assertShortcutPackageInfo(CALLING_PACKAGE_1, USER_0, 1); - assertShortcutPackageInfo(CALLING_PACKAGE_2, USER_0, 2); - assertNoShortcutPackageInfo(CALLING_PACKAGE_3, USER_0); - assertShortcutPackageInfo(LAUNCHER_1, USER_0, 4); - assertShortcutPackageInfo(LAUNCHER_2, USER_0, 5); - runWithCaller(LAUNCHER_1, USER_0, () -> { assertShortcutIds(assertAllPinned(assertAllNotKeyFieldsOnly( mLauncherApps.getShortcuts(buildQuery(/* time =*/ 0, CALLING_PACKAGE_1, @@ -2258,7 +2296,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { // 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( @@ -2280,7 +2318,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { "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( @@ -2291,8 +2329,8 @@ public class ShortcutManagerTest extends InstrumentationTestCase { // 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, @@ -2312,8 +2350,8 @@ public class ShortcutManagerTest extends InstrumentationTestCase { // 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, @@ -2332,7 +2370,502 @@ public class ShortcutManagerTest extends InstrumentationTestCase { }); } - 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( @@ -2352,7 +2885,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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( @@ -2363,15 +2896,15 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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); @@ -2379,17 +2912,16 @@ public class ShortcutManagerTest extends InstrumentationTestCase { // 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 @@ -2411,7 +2943,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { }); runWithCaller(CALLING_PACKAGE_1, UserHandle.USER_SYSTEM, () -> { - assertTrue(mManager.setDynamicShortcuts(Arrays.asList( + assertTrue(mManager.setDynamicShortcuts(list( makeShortcut("s1"), makeShortcut("s2"), makeShortcut("s3")))); }); @@ -2420,7 +2952,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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"); @@ -2428,7 +2960,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { // 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(); @@ -2436,7 +2968,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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"); @@ -2444,7 +2976,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { // 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(); @@ -2465,7 +2997,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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"); @@ -2481,7 +3013,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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"); @@ -2489,7 +3021,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { // 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")))); }); @@ -2498,7 +3030,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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"); @@ -2514,13 +3046,13 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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(); @@ -2528,7 +3060,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { verify(c0).onShortcutsChanged( eq(CALLING_PACKAGE_2), shortcuts.capture(), - eq(UserHandle.of(USER_0)) + eq(HANDLE_USER_0) ); assertEquals(0, shortcuts.getValue().size()); } @@ -2536,7 +3068,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { // === Test for persisting === public void testSaveAndLoadUser_empty() { - assertTrue(mManager.setDynamicShortcuts(Arrays.asList())); + assertTrue(mManager.setDynamicShortcuts(list())); Log.i(TAG, "Saved state"); dumpsysOnLogcat(); @@ -2576,7 +3108,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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()); @@ -2603,7 +3135,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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()); @@ -2630,7 +3162,7 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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()); @@ -2639,10 +3171,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase { mService.getShortcutsForTest().get(UserHandle.USER_SYSTEM).setLauncherComponent( mService, new ComponentName("pkg1", "class")); - assertShortcutPackageInfo(CALLING_PACKAGE_1, USER_0, 1); - assertShortcutPackageInfo(CALLING_PACKAGE_2, USER_0, 2); - assertNoShortcutPackageInfo(CALLING_PACKAGE_3, USER_0); - // Restore. mService.saveDirtyInfo(); initService(); @@ -2653,10 +3181,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase { // this will pre-load the per-user info. mService.handleUnlockUser(UserHandle.USER_SYSTEM); - assertShortcutPackageInfo(CALLING_PACKAGE_1, USER_0, 1); - assertShortcutPackageInfo(CALLING_PACKAGE_2, USER_0, 2); - assertNoShortcutPackageInfo(CALLING_PACKAGE_3, USER_0); - // Now it's loaded. assertEquals(1, mService.getShortcutsForTest().size()); @@ -2707,45 +3231,45 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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. @@ -2773,15 +3297,19 @@ public class ShortcutManagerTest extends InstrumentationTestCase { // 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), @@ -2795,25 +3323,24 @@ public class ShortcutManagerTest extends InstrumentationTestCase { assertShortcutExists(CALLING_PACKAGE_1, "s10_1", USER_10); assertShortcutExists(CALLING_PACKAGE_2, "s10_2", USER_10); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0)); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0)); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10)); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_10)); - mService.saveDirtyInfo(); // Nonexistent package. - 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), @@ -2827,24 +3354,23 @@ public class ShortcutManagerTest extends InstrumentationTestCase { assertShortcutExists(CALLING_PACKAGE_1, "s10_1", USER_10); assertShortcutExists(CALLING_PACKAGE_2, "s10_2", USER_10); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0)); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0)); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10)); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_10)); - mService.saveDirtyInfo(); // Remove a package. - 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), @@ -2858,24 +3384,22 @@ public class ShortcutManagerTest extends InstrumentationTestCase { assertShortcutExists(CALLING_PACKAGE_1, "s10_1", USER_10); assertShortcutExists(CALLING_PACKAGE_2, "s10_2", USER_10); - assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0)); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0)); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10)); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_10)); - mService.saveDirtyInfo(); // Remove a launcher. - 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), @@ -2890,16 +3414,19 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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), @@ -2911,24 +3438,22 @@ public class ShortcutManagerTest extends InstrumentationTestCase { assertShortcutExists(CALLING_PACKAGE_1, "s10_1", USER_10); assertShortcutNotExists(CALLING_PACKAGE_2, "s10_2", USER_10); - assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0)); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0)); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10)); - assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_10)); - mService.saveDirtyInfo(); // Remove the other launcher from user 10 too. - 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), @@ -2943,16 +3468,18 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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), @@ -2967,6 +3494,353 @@ public class ShortcutManagerTest extends InstrumentationTestCase { 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. @@ -3019,27 +3893,6 @@ public class ShortcutManagerTest extends InstrumentationTestCase { checkCanRestoreTo(false, spi2, 11, "x", "sig2x", "sig1", "y"); } - public void testShortcutPackageInfoRefresh() { - addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 10, "sig1"); - - final ShortcutPackageInfo spi1 = ShortcutPackageInfo.generateForInstalledPackage( - mService, CALLING_PACKAGE_1, USER_0); - - checkCanRestoreTo(true, spi1, 10, "sig1"); - - addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 11, "sig1", "sig2"); - - spi1.refreshAndSave(mService, USER_0); - - mService.handleCleanupUser(USER_0); - initService(); - - checkCanRestoreTo(false, spi1, 10, "sig1", "sig2"); - checkCanRestoreTo(false, spi1, 11, "sig", "sig2"); - checkCanRestoreTo(false, spi1, 11, "sig1", "sig"); - checkCanRestoreTo(true, spi1, 11, "sig1", "sig2"); - } - public void testHandlePackageDelete() { setCaller(CALLING_PACKAGE_1, USER_0); assertTrue(mManager.addDynamicShortcut(makeShortcut("s1"))); @@ -3059,73 +3912,56 @@ public class ShortcutManagerTest extends InstrumentationTestCase { setCaller(CALLING_PACKAGE_3, USER_10); assertTrue(mManager.addDynamicShortcut(makeShortcut("s1"))); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0)); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0)); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_0)); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10)); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_10)); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_10)); + assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0)); + assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0)); + assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0)); + assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10)); + assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10)); + assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10)); mService.mPackageMonitor.onReceive(getTestContext(), genPackageDeleteIntent(CALLING_PACKAGE_1, USER_0)); - assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0)); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0)); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_0)); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10)); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_10)); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_10)); + assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0)); + assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0)); + assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0)); + assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10)); + assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10)); + assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10)); mService.mPackageMonitor.onReceive(getTestContext(), genPackageDeleteIntent(CALLING_PACKAGE_2, USER_10)); - assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0)); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0)); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_0)); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10)); - assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_10)); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_10)); + assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0)); + assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0)); + assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0)); + assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10)); + assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10)); + assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10)); mInjectedPackages.remove(CALLING_PACKAGE_1); mInjectedPackages.remove(CALLING_PACKAGE_3); mService.handleUnlockUser(USER_0); - assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0)); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0)); - assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_0)); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10)); - assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_10)); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_10)); + + assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0)); + assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0)); + assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0)); + assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10)); + assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10)); + assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10)); mService.handleUnlockUser(USER_10); - assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0)); - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_0)); - assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_0)); - assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_10)); - assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_2, USER_10)); - assertNull(mService.getPackageInfoForTest(CALLING_PACKAGE_3, USER_10)); + + assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0)); + assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0)); + assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0)); + assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10)); + assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10)); + assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10)); } public void testHandlePackageUpdate() { - setCaller(CALLING_PACKAGE_1, USER_0); - assertTrue(mManager.addDynamicShortcut(makeShortcut("s1"))); - - assertNotNull(mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0)); - assertEquals(1, mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0).getVersionCode()); - - addPackage(CALLING_PACKAGE_1, CALLING_UID_1, 123); - - mService.mPackageMonitor.onReceive(getTestContext(), - genPackageUpdateIntent("abc", USER_0)); - assertEquals(1, mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0).getVersionCode()); - - mService.mPackageMonitor.onReceive(getTestContext(), - genPackageUpdateIntent("abc", USER_10)); - assertEquals(1, mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0).getVersionCode()); - - mService.mPackageMonitor.onReceive(getTestContext(), - genPackageUpdateIntent(CALLING_PACKAGE_1, USER_0)); - assertEquals(123, mService.getPackageInfoForTest(CALLING_PACKAGE_1, USER_0) - .getVersionCode()); + // TODO: Make sure unshadow is called. } } diff --git a/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 000000000000..c016e6104755 --- /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/services/usb/java/com/android/server/usb/UsbAlsaManager.java b/services/usb/java/com/android/server/usb/UsbAlsaManager.java index 129e5377882a..058de05f4e41 100644 --- a/services/usb/java/com/android/server/usb/UsbAlsaManager.java +++ b/services/usb/java/com/android/server/usb/UsbAlsaManager.java @@ -385,6 +385,7 @@ public final class UsbAlsaManager { UsbAudioDevice audioDevice = selectAudioCard(addedCard); if (audioDevice != null) { mAudioDevices.put(usbDevice, audioDevice); + Slog.i(TAG, "USB Audio Device Added: " + audioDevice); } // look for MIDI devices @@ -441,6 +442,7 @@ public final class UsbAlsaManager { } UsbAudioDevice audioDevice = mAudioDevices.remove(usbDevice); + Slog.i(TAG, "USB Audio Device Removed: " + audioDevice); if (audioDevice != null) { if (audioDevice.mHasPlayback || audioDevice.mHasCapture) { notifyDeviceState(audioDevice, false); diff --git a/telecomm/java/android/telecom/Conference.java b/telecomm/java/android/telecom/Conference.java index 06851eeb7f20..1ce4ade8241e 100644 --- a/telecomm/java/android/telecom/Conference.java +++ b/telecomm/java/android/telecom/Conference.java @@ -694,7 +694,7 @@ public abstract class Conference extends Conferenceable { 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 @@ public abstract class Conference extends Conferenceable { 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 310c957c94fa..d83cdb8fa148 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -1887,7 +1887,7 @@ public abstract class Connection extends Conferenceable { 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 @@ public abstract class Connection extends Conferenceable { mPreviousExtraKeys = new ArraySet<String>(); } mPreviousExtraKeys.clear(); - mPreviousExtraKeys.addAll(extras.keySet()); + if (extras != null) { + mPreviousExtraKeys.addAll(extras.keySet()); + } } /** @@ -2317,7 +2319,7 @@ public abstract class Connection extends Conferenceable { */ 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/SoundTriggerTestApp/AndroidManifest.xml b/tests/SoundTriggerTestApp/AndroidManifest.xml index dc4cdb59b8db..71d6001c3a56 100644 --- a/tests/SoundTriggerTestApp/AndroidManifest.xml +++ b/tests/SoundTriggerTestApp/AndroidManifest.xml @@ -2,7 +2,7 @@ package="com.android.test.soundtrigger"> <uses-permission android:name="android.permission.MANAGE_SOUND_TRIGGER" /> - <uses-permission android:name="android.permission.WAKE_LOCk" /> + <uses-permission android:name="android.permission.WAKE_LOCK" /> <application> <activity android:name="TestSoundTriggerActivity" diff --git a/tests/VectorDrawableTest/res/drawable/vector_drawable_linear_progress_bar.xml b/tests/VectorDrawableTest/res/drawable/vector_drawable_linear_progress_bar.xml index 96fd70ecf9cf..a6da114b511b 100644 --- a/tests/VectorDrawableTest/res/drawable/vector_drawable_linear_progress_bar.xml +++ b/tests/VectorDrawableTest/res/drawable/vector_drawable_linear_progress_bar.xml @@ -17,7 +17,7 @@ android:height="4dp" android:viewportHeight="4" android:viewportWidth="360" - android:width="360dp" > + android:width="36dp" > <group android:name="linear_indeterminate" diff --git a/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java b/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java index 087e68a841ed..9351f63551fb 100644 --- a/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java +++ b/tests/VectorDrawableTest/src/com/android/test/dynamic/AnimatedVectorDrawableTest.java @@ -43,33 +43,37 @@ public class AnimatedVectorDrawableTest extends Activity implements View.OnClick @Override protected void onCreate(Bundle savedInstanceState) { + final int[] layerTypes = {View.LAYER_TYPE_SOFTWARE, View.LAYER_TYPE_HARDWARE}; super.onCreate(savedInstanceState); ScrollView scrollView = new ScrollView(this); GridLayout container = new GridLayout(this); scrollView.addView(container); - container.setColumnCount(1); + container.setColumnCount(2); for (int i = 0; i < icon.length; i++) { - Button button = new Button(this); - button.setWidth(400); - button.setHeight(400); - button.setBackgroundResource(icon[i]); - AnimatedVectorDrawable d = (AnimatedVectorDrawable) button.getBackground(); - d.registerAnimationCallback(new Animatable2.AnimationCallback() { - @Override - public void onAnimationStart(Drawable drawable) { - Log.v(LOGCAT, "Animator start"); - } + for (int j = 0; j < layerTypes.length; j++) { + Button button = new Button(this); + button.setWidth(400); + button.setHeight(400); + button.setLayerType(layerTypes[j], null); + button.setBackgroundResource(icon[i]); + AnimatedVectorDrawable d = (AnimatedVectorDrawable) button.getBackground(); + d.registerAnimationCallback(new Animatable2.AnimationCallback() { + @Override + public void onAnimationStart(Drawable drawable) { + Log.v(LOGCAT, "Animator start"); + } - @Override - public void onAnimationEnd(Drawable drawable) { + @Override + public void onAnimationEnd(Drawable drawable) { Log.v(LOGCAT, "Animator end"); - } - }); + } + }); - container.addView(button); - button.setOnClickListener(this); + container.addView(button); + button.setOnClickListener(this); + } } setContentView(scrollView); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java index 53adb41af0cb..5a6a00fc3985 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java @@ -97,7 +97,6 @@ public final class BridgeWindowSession implements IWindowSession { @Override public void repositionChild(IWindow window, int left, int top, int right, int bottom, - int requestedWidth, int requestedHeight, long deferTransactionUntilFrame, Rect outFrame) { // pass for now. return; diff --git a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java index 035317ea402b..4c38c9bbe2cd 100644 --- a/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java +++ b/wifi/java/android/net/wifi/WifiActivityEnergyInfo.java @@ -19,6 +19,8 @@ package android.net.wifi; 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 @@ public final class WifiActivityEnergyInfo implements Parcelable { /** * @hide */ + public long[] mControllerTxTimePerLevelMs; + + /** + * @hide + */ public long mControllerRxTimeMs; /** @@ -62,10 +69,12 @@ public final class WifiActivityEnergyInfo implements Parcelable { 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 @@ public final class WifiActivityEnergyInfo implements Parcelable { + " timestamp=" + mTimestamp + " mStackState=" + mStackState + " mControllerTxTimeMs=" + mControllerTxTimeMs + + " mControllerTxTimePerLevelMs=" + Arrays.toString(mControllerTxTimePerLevelMs) + " mControllerRxTimeMs=" + mControllerRxTimeMs + " mControllerIdleTimeMs=" + mControllerIdleTimeMs + " mControllerEnergyUsed=" + mControllerEnergyUsed @@ -89,11 +99,12 @@ public final class WifiActivityEnergyInfo implements Parcelable { 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 @@ public final class WifiActivityEnergyInfo implements Parcelable { 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 @@ public final class WifiActivityEnergyInfo implements Parcelable { } /** + * @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 06dea072f2ed..fb2bdd455c62 100644 --- a/wifi/java/android/net/wifi/WifiConfiguration.java +++ b/wifi/java/android/net/wifi/WifiConfiguration.java @@ -938,6 +938,15 @@ public class WifiConfiguration implements Parcelable { 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 @@ public class WifiConfiguration implements Parcelable { 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 @@ public class WifiConfiguration implements Parcelable { mNetworkSelectionBSSID = source.mNetworkSelectionBSSID; setConnectChoice(source.getConnectChoice()); setConnectChoiceTimestamp(source.getConnectChoiceTimestamp()); + setHasEverConnected(source.getHasEverConnected()); } public void writeToParcel(Parcel dest) { @@ -1244,6 +1265,7 @@ public class WifiConfiguration implements Parcelable { } else { dest.writeInt(CONNECT_CHOICE_NOT_EXISTS); } + dest.writeInt(getHasEverConnected() ? 1 : 0); } public void readFromParcel(Parcel in) { @@ -1262,6 +1284,7 @@ public class WifiConfiguration implements Parcelable { setConnectChoice(null); setConnectChoiceTimestamp(INVALID_NETWORK_SELECTION_DISABLE_TIMESTAMP); } + setHasEverConnected(in.readInt() != 0); } } @@ -1389,6 +1412,8 @@ public class WifiConfiguration implements Parcelable { 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 1de4fd834fcd..edd400b56238 100644 --- a/wifi/java/android/net/wifi/WifiLinkLayerStats.java +++ b/wifi/java/android/net/wifi/WifiLinkLayerStats.java @@ -19,6 +19,8 @@ package android.net.wifi; 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 @@ public class WifiLinkLayerStats implements Parcelable { /** {@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 @@ public class WifiLinkLayerStats implements Parcelable { .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 @@ public class WifiLinkLayerStats implements Parcelable { 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 @@ public class WifiLinkLayerStats implements Parcelable { 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; |