diff options
97 files changed, 1617 insertions, 561 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 3574f8d05055..24c144c89ad2 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -719,7 +719,7 @@ public class Activity extends ContextThemeWrapper public static final int FINISH_TASK_WITH_ACTIVITY = 2; static final String FRAGMENTS_TAG = "android:fragments"; - private static final String LAST_ACCESSIBILITY_ID = "android:lastAccessibilityId"; + private static final String LAST_AUTOFILL_ID = "android:lastAutofillId"; private static final String AUTOFILL_RESET_NEEDED = "@android:autofillResetNeeded"; private static final String WINDOW_HIERARCHY_TAG = "android:viewHierarchyState"; @@ -853,8 +853,8 @@ public class Activity extends ContextThemeWrapper private boolean mAutoFillResetNeeded; - /** The last accessibility id that was returned from {@link #getNextAccessibilityId()} */ - private int mLastAccessibilityId = View.LAST_APP_ACCESSIBILITY_ID; + /** The last autofill id that was returned from {@link #getNextAutofillId()} */ + private int mLastAutofillId = View.LAST_APP_AUTOFILL_ID; private AutofillPopupWindow mAutofillPopupWindow; @@ -999,7 +999,7 @@ public class Activity extends ContextThemeWrapper } if (savedInstanceState != null) { mAutoFillResetNeeded = savedInstanceState.getBoolean(AUTOFILL_RESET_NEEDED, false); - mLastAccessibilityId = savedInstanceState.getInt(LAST_ACCESSIBILITY_ID, View.NO_ID); + mLastAutofillId = savedInstanceState.getInt(LAST_AUTOFILL_ID, View.NO_ID); if (mAutoFillResetNeeded) { getAutofillManager().onCreate(savedInstanceState); @@ -1348,24 +1348,23 @@ public class Activity extends ContextThemeWrapper } /** - * Gets the next accessibility ID. + * Gets the next autofill ID. * - * <p>All IDs will be bigger than {@link View#LAST_APP_ACCESSIBILITY_ID}. All IDs returned + * <p>All IDs will be bigger than {@link View#LAST_APP_AUTOFILL_ID}. All IDs returned * will be unique. * * @return A ID that is unique in the activity * * {@hide} */ - @Override - public int getNextAccessibilityId() { - if (mLastAccessibilityId == Integer.MAX_VALUE - 1) { - mLastAccessibilityId = View.LAST_APP_ACCESSIBILITY_ID; + public int getNextAutofillId() { + if (mLastAutofillId == Integer.MAX_VALUE - 1) { + mLastAutofillId = View.LAST_APP_AUTOFILL_ID; } - mLastAccessibilityId++; + mLastAutofillId++; - return mLastAccessibilityId; + return mLastAutofillId; } /** @@ -1563,7 +1562,7 @@ public class Activity extends ContextThemeWrapper protected void onSaveInstanceState(Bundle outState) { outState.putBundle(WINDOW_HIERARCHY_TAG, mWindow.saveHierarchyState()); - outState.putInt(LAST_ACCESSIBILITY_ID, mLastAccessibilityId); + outState.putInt(LAST_AUTOFILL_ID, mLastAutofillId); Parcelable p = mFragments.saveAllState(); if (p != null) { outState.putParcelable(FRAGMENTS_TAG, p); @@ -7455,7 +7454,7 @@ public class Activity extends ContextThemeWrapper /** @hide */ @Override - @NonNull public View[] findViewsByAccessibilityIdTraversal(@NonNull int[] viewIds) { + @NonNull public View[] findViewsByAutofillIdTraversal(@NonNull int[] viewIds) { final View[] views = new View[viewIds.length]; final ArrayList<ViewRootImpl> roots = WindowManagerGlobal.getInstance().getRootViews(getActivityToken()); @@ -7466,7 +7465,7 @@ public class Activity extends ContextThemeWrapper if (rootView != null) { for (int viewNum = 0; viewNum < viewIds.length; viewNum++) { if (views[viewNum] == null) { - views[viewNum] = rootView.findViewByAccessibilityIdTraversal( + views[viewNum] = rootView.findViewByAutofillIdTraversal( viewIds[viewNum]); } } @@ -7478,14 +7477,14 @@ public class Activity extends ContextThemeWrapper /** @hide */ @Override - @Nullable public View findViewByAccessibilityIdTraversal(int viewId) { + @Nullable public View findViewByAutofillIdTraversal(int viewId) { final ArrayList<ViewRootImpl> roots = WindowManagerGlobal.getInstance().getRootViews(getActivityToken()); for (int rootNum = 0; rootNum < roots.size(); rootNum++) { final View rootView = roots.get(rootNum).getView(); if (rootView != null) { - final View view = rootView.findViewByAccessibilityIdTraversal(viewId); + final View view = rootView.findViewByAutofillIdTraversal(viewId); if (view != null) { return view; } @@ -7499,7 +7498,7 @@ public class Activity extends ContextThemeWrapper @Override @NonNull public boolean[] getViewVisibility(@NonNull int[] viewIds) { final boolean[] isVisible = new boolean[viewIds.length]; - final View views[] = findViewsByAccessibilityIdTraversal(viewIds); + final View views[] = findViewsByAutofillIdTraversal(viewIds); for (int i = 0; i < viewIds.length; i++) { View view = views[i]; diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index d3b4b403e1ac..e5fe2402dae1 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -24,6 +24,7 @@ import android.content.Intent; import android.content.res.Configuration; import android.os.Bundle; import android.os.IBinder; +import android.os.SystemClock; import android.service.voice.IVoiceInteractionSession; import android.util.SparseIntArray; @@ -134,8 +135,10 @@ public abstract class ActivityManagerInternal { * * @param reasons A map from stack id to a reason integer why the transition was started,, which * must be one of the APP_TRANSITION_* values. + * @param timestamp The time at which the app transition started in + * {@link SystemClock#uptimeMillis()} timebase. */ - public abstract void notifyAppTransitionStarting(SparseIntArray reasons); + public abstract void notifyAppTransitionStarting(SparseIntArray reasons, long timestamp); /** * Callback for window manager to let activity manager know that the app transition was diff --git a/core/java/android/app/ActivityOptions.java b/core/java/android/app/ActivityOptions.java index cbb93a059613..6dead3e6c0dc 100644 --- a/core/java/android/app/ActivityOptions.java +++ b/core/java/android/app/ActivityOptions.java @@ -1244,7 +1244,7 @@ public class ActivityOptions { // Once we parcel the thumbnail for transfering over to the system, create a copy of // the bitmap to a hardware bitmap and pass through the GraphicBuffer if (mThumbnail != null) { - final Bitmap hwBitmap = mThumbnail.copy(Config.HARDWARE, true /* immutable */); + final Bitmap hwBitmap = mThumbnail.copy(Config.HARDWARE, false /* isMutable */); if (hwBitmap != null) { b.putParcelable(KEY_ANIM_THUMBNAIL, hwBitmap.createGraphicBufferHandle()); } else { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index db80c7259179..2303a3839789 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -488,27 +488,27 @@ public abstract class Context { */ public abstract Context getApplicationContext(); - /** Non-activity related accessibility ids are unique in the app */ - private static int sLastAccessibilityId = View.NO_ID; + /** Non-activity related autofill ids are unique in the app */ + private static int sLastAutofillId = View.NO_ID; /** - * Gets the next accessibility ID. + * Gets the next autofill ID. * - * <p>All IDs will be smaller or the same as {@link View#LAST_APP_ACCESSIBILITY_ID}. All IDs + * <p>All IDs will be smaller or the same as {@link View#LAST_APP_AUTOFILL_ID}. All IDs * returned will be unique. * * @return A ID that is unique in the process * * {@hide} */ - public int getNextAccessibilityId() { - if (sLastAccessibilityId == View.LAST_APP_ACCESSIBILITY_ID - 1) { - sLastAccessibilityId = View.NO_ID; + public int getNextAutofillId() { + if (sLastAutofillId == View.LAST_APP_AUTOFILL_ID - 1) { + sLastAutofillId = View.NO_ID; } - sLastAccessibilityId++; + sLastAutofillId++; - return sLastAccessibilityId; + return sLastAutofillId; } /** diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index e127ca319bd8..3b27905d2c11 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -956,8 +956,7 @@ public class ContextWrapper extends Context { /** * @hide */ - @Override - public int getNextAccessibilityId() { - return mBase.getNextAccessibilityId(); + public int getNextAutofillId() { + return mBase.getNextAutofillId(); } } diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java index 8678d95db17d..b69a23aa5854 100644 --- a/core/java/android/os/Handler.java +++ b/core/java/android/os/Handler.java @@ -696,6 +696,14 @@ public class Handler { } /** + * Return whether there are any messages or callbacks currently scheduled on this handler. + * @hide + */ + public final boolean hasMessagesOrCallbacks() { + return mQueue.hasMessages(this); + } + + /** * Check if there are any pending posts of messages with code 'what' and * whose obj is 'object' in the message queue. */ @@ -728,6 +736,18 @@ public class Handler { } } + /** + * @hide + */ + public final void dumpMine(Printer pw, String prefix) { + pw.println(prefix + this + " @ " + SystemClock.uptimeMillis()); + if (mLooper == null) { + pw.println(prefix + "looper uninitialized"); + } else { + mLooper.dump(pw, prefix + " ", this); + } + } + @Override public String toString() { return "Handler (" + getClass().getName() + ") {" diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java index 44dbcfb09582..04cceb8e80ba 100644 --- a/core/java/android/os/Looper.java +++ b/core/java/android/os/Looper.java @@ -310,7 +310,20 @@ public final class Looper { */ public void dump(@NonNull Printer pw, @NonNull String prefix) { pw.println(prefix + toString()); - mQueue.dump(pw, prefix + " "); + mQueue.dump(pw, prefix + " ", null); + } + + /** + * Dumps the state of the looper for debugging purposes. + * + * @param pw A printer to receive the contents of the dump. + * @param prefix A prefix to prepend to each line which is printed. + * @param handler Only dump messages for this Handler. + * @hide + */ + public void dump(@NonNull Printer pw, @NonNull String prefix, Handler handler) { + pw.println(prefix + toString()); + mQueue.dump(pw, prefix + " ", handler); } /** @hide */ diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java index 2a8c52e92c60..624e28a67ae6 100644 --- a/core/java/android/os/MessageQueue.java +++ b/core/java/android/os/MessageQueue.java @@ -620,6 +620,23 @@ public final class MessageQueue { } } + boolean hasMessages(Handler h) { + if (h == null) { + return false; + } + + synchronized (this) { + Message p = mMessages; + while (p != null) { + if (p.target == h) { + return true; + } + p = p.next; + } + return false; + } + } + void removeMessages(Handler h, int what, Object object) { if (h == null) { return; @@ -759,12 +776,14 @@ public final class MessageQueue { } } - void dump(Printer pw, String prefix) { + void dump(Printer pw, String prefix, Handler h) { synchronized (this) { long now = SystemClock.uptimeMillis(); int n = 0; for (Message msg = mMessages; msg != null; msg = msg.next) { - pw.println(prefix + "Message " + n + ": " + msg.toString(now)); + if (h == null || h == msg.target) { + pw.println(prefix + "Message " + n + ": " + msg.toString(now)); + } n++; } pw.println(prefix + "(Total messages: " + n + ", polling=" + isPollingLocked() diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index f1ce9d52e920..ee3e986cc323 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -9029,7 +9029,10 @@ public final class Settings { * <pre> * max_cached_processes (int) * background_settle_time (long) - * foreground_service_ui_min_time (long) + * fgservice_min_shown_time (long) + * fgservice_min_report_time (long) + * fgservice_screen_on_before_time (long) + * fgservice_screen_on_after_time (long) * content_provider_retain_time (long) * gc_timeout (long) * gc_min_interval (long) @@ -9818,6 +9821,16 @@ public final class Settings { public static final String ENABLE_EPHEMERAL_FEATURE = "enable_ephemeral_feature"; /** + * Toggle to enable/disable dexopt for instant applications. The default is for dexopt + * to be disabled. + * <p> + * Type: int (0 to disable, 1 to enable) + * + * @hide + */ + public static final String INSTANT_APP_DEXOPT_ENABLED = "instant_app_dexopt_enabled"; + + /** * The min period for caching installed instant apps in milliseconds. * <p> * Type: long diff --git a/core/java/android/service/autofill/FillContext.java b/core/java/android/service/autofill/FillContext.java index 6956c8ac7135..f8a87516854d 100644 --- a/core/java/android/service/autofill/FillContext.java +++ b/core/java/android/service/autofill/FillContext.java @@ -106,15 +106,15 @@ public final class FillContext implements Parcelable { } /** - * Finds {@link ViewNode}s that have the requested ids. + * Finds {@link ViewNode ViewNodes} that have the requested ids. * - * @param ids The ids of the node to find + * @param ids The ids of the node to find. * - * @return The nodes indexed in the same way as the ids + * @return The nodes indexed in the same way as the ids. * * @hide */ - @NonNull public ViewNode[] findViewNodesByAutofillIds(@NonNull AutofillId... ids) { + @NonNull public ViewNode[] findViewNodesByAutofillIds(@NonNull AutofillId[] ids) { final LinkedList<ViewNode> nodesToProcess = new LinkedList<>(); final ViewNode[] foundNodes = new AssistStructure.ViewNode[ids.length]; @@ -178,6 +178,30 @@ public final class FillContext implements Parcelable { return foundNodes; } + /** + * Finds the {@link ViewNode} that has the requested {@code id}, if any. + * + * @hide + */ + @Nullable public ViewNode findViewNodeByAutofillId(@NonNull AutofillId id) { + final LinkedList<ViewNode> nodesToProcess = new LinkedList<>(); + final int numWindowNodes = mStructure.getWindowNodeCount(); + for (int i = 0; i < numWindowNodes; i++) { + nodesToProcess.add(mStructure.getWindowNodeAt(i).getRootViewNode()); + } + while (!nodesToProcess.isEmpty()) { + final ViewNode node = nodesToProcess.removeFirst(); + if (id.equals(node.getAutofillId())) { + return node; + } + for (int i = 0; i < node.getChildCount(); i++) { + nodesToProcess.addLast(node.getChildAt(i)); + } + } + + return null; + } + public static final Parcelable.Creator<FillContext> CREATOR = new Parcelable.Creator<FillContext>() { @Override diff --git a/core/java/android/service/autofill/SaveInfo.java b/core/java/android/service/autofill/SaveInfo.java index fa3f55b99917..6ea7d5edb496 100644 --- a/core/java/android/service/autofill/SaveInfo.java +++ b/core/java/android/service/autofill/SaveInfo.java @@ -273,13 +273,24 @@ public final class SaveInfo implements Parcelable { * * <p>See {@link SaveInfo} for more info. * - * @throws IllegalArgumentException if {@code requiredIds} is {@code null} or empty. + * @throws IllegalArgumentException if {@code requiredIds} is {@code null} or empty, or if + * it contains any {@code null} entry. */ public Builder(@SaveDataType int type, @NonNull AutofillId[] requiredIds) { - Preconditions.checkArgument(requiredIds != null && requiredIds.length > 0, - "must have at least one required id: " + Arrays.toString(requiredIds)); + // TODO: add CTS unit tests (not integration) to assert the null cases mType = type; - mRequiredIds = requiredIds; + mRequiredIds = assertValid(requiredIds); + } + + private AutofillId[] assertValid(AutofillId[] ids) { + Preconditions.checkArgument(ids != null && ids.length > 0, + "must have at least one id: " + Arrays.toString(ids)); + for (int i = 0; i < ids.length; i++) { + final AutofillId id = ids[i]; + Preconditions.checkArgument(id != null, + "cannot have null id: " + Arrays.toString(ids)); + } + return ids; } /** @@ -302,12 +313,14 @@ public final class SaveInfo implements Parcelable { * * @param ids The ids of the optional views. * @return This builder. + * + * @throws IllegalArgumentException if {@code ids} is {@code null} or empty, or if + * it contains any {@code null} entry. */ - public @NonNull Builder setOptionalIds(@Nullable AutofillId[] ids) { + public @NonNull Builder setOptionalIds(@NonNull AutofillId[] ids) { + // TODO: add CTS unit tests (not integration) to assert the null cases throwIfDestroyed(); - if (ids != null && ids.length != 0) { - mOptionalIds = ids; - } + mOptionalIds = assertValid(ids); return this; } @@ -421,7 +434,10 @@ public final class SaveInfo implements Parcelable { final Builder builder = new Builder(parcel.readInt(), parcel.readParcelableArray(null, AutofillId.class)); builder.setNegativeAction(parcel.readInt(), parcel.readParcelable(null)); - builder.setOptionalIds(parcel.readParcelableArray(null, AutofillId.class)); + final AutofillId[] optionalIds = parcel.readParcelableArray(null, AutofillId.class); + if (optionalIds != null) { + builder.setOptionalIds(optionalIds); + } builder.setDescription(parcel.readCharSequence()); builder.setFlags(parcel.readInt()); return builder.build(); diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index 8bb3fa988a45..4f9dbd5ad7a0 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -52,7 +52,9 @@ public class Surface implements Parcelable { private static native long nativeCreateFromSurfaceTexture(SurfaceTexture surfaceTexture) throws OutOfResourcesException; + private static native long nativeCreateFromSurfaceControl(long surfaceControlNativeObject); + private static native long nativeGetFromSurfaceControl(long surfaceControlNativeObject); private static native long nativeLockCanvas(long nativeObject, Canvas canvas, Rect dirty) throws OutOfResourcesException; @@ -410,6 +412,9 @@ public class Surface implements Parcelable { * back from a client, converting it from the representation being managed * by the window manager to the representation the client uses to draw * in to it. + * + * @param other {@link SurfaceControl} to copy from. + * * @hide */ public void copyFrom(SurfaceControl other) { @@ -420,7 +425,39 @@ public class Surface implements Parcelable { long surfaceControlPtr = other.mNativeObject; if (surfaceControlPtr == 0) { throw new NullPointerException( - "SurfaceControl native object is null. Are you using a released SurfaceControl?"); + "null SurfaceControl native object. Are you using a released SurfaceControl?"); + } + long newNativeObject = nativeGetFromSurfaceControl(surfaceControlPtr); + + synchronized (mLock) { + if (mNativeObject != 0) { + nativeRelease(mNativeObject); + } + setNativeObjectLocked(newNativeObject); + } + } + + /** + * Gets a reference a surface created from this one. This surface now holds a reference + * to the same data as the original surface, and is -not- the owner. + * This is for use by the window manager when returning a window surface + * back from a client, converting it from the representation being managed + * by the window manager to the representation the client uses to draw + * in to it. + * + * @param other {@link SurfaceControl} to create surface from. + * + * @hide + */ + public void createFrom(SurfaceControl other) { + if (other == null) { + throw new IllegalArgumentException("other must not be null"); + } + + long surfaceControlPtr = other.mNativeObject; + if (surfaceControlPtr == 0) { + throw new NullPointerException( + "null SurfaceControl native object. Are you using a released SurfaceControl?"); } long newNativeObject = nativeCreateFromSurfaceControl(surfaceControlPtr); diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 679a9cd92bc2..34ceeb7bcc0d 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -641,6 +641,16 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb mSurface.copyFrom(mSurfaceControl); } + if (getContext().getApplicationInfo().targetSdkVersion + < Build.VERSION_CODES.O) { + // Some legacy applications use the underlying native {@link Surface} object + // as a key to whether anything has changed. In these cases, updates to the + // existing {@link Surface} will be ignored when the size changes. + // Therefore, we must explicitly recreate the {@link Surface} in these + // cases. + mSurface.createFrom(mSurfaceControl); + } + if (visible && mSurface.isValid()) { if (!mSurfaceCreated && (surfaceChanged || visibleChanged)) { mSurfaceCreated = true; diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java index 7cec957adf41..a140f280e7c3 100644 --- a/core/java/android/view/ThreadedRenderer.java +++ b/core/java/android/view/ThreadedRenderer.java @@ -970,6 +970,9 @@ public final class ThreadedRenderer { observer.mNative = null; } + /** Not actually public - internal use only. This doc to make lint happy */ + public static native void disableVsync(); + static native void setupShadersDiskCache(String cacheFile); private static native void nRotateProcessStatsBuffer(); diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index b093284ae9ff..57409d204aca 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -802,7 +802,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * {@hide} */ - public static final int LAST_APP_ACCESSIBILITY_ID = Integer.MAX_VALUE / 2; + public static final int LAST_APP_AUTOFILL_ID = Integer.MAX_VALUE / 2; /** * Attribute to find the autofilled highlight @@ -2045,6 +2045,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private SparseArray<Object> mKeyedTags; /** + * The next available accessibility id. + */ + private static int sNextAccessibilityViewId; + + /** * The animation currently associated with this view. * @hide */ @@ -2086,16 +2091,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback, @ViewDebug.ExportedProperty(resolveId = true) int mID = NO_ID; - /** The ID of this view for accessibility and autofill purposes. + /** The ID of this view for autofill purposes. * <ul> * <li>== {@link #NO_ID}: ID has not been assigned yet - * <li>≤ {@link #LAST_APP_ACCESSIBILITY_ID}: View is not part of a activity. The ID is + * <li>≤ {@link #LAST_APP_AUTOFILL_ID}: View is not part of a activity. The ID is * unique in the process. This might change * over activity lifecycle events. - * <li>> {@link #LAST_APP_ACCESSIBILITY_ID}: View is part of a activity. The ID is + * <li>> {@link #LAST_APP_AUTOFILL_ID}: View is part of a activity. The ID is * unique in the activity. This stays the same * over activity lifecycle events. */ + private int mAutofillViewId = NO_ID; + + // ID for accessibility purposes. This ID must be unique for every window private int mAccessibilityViewId = NO_ID; private int mAccessibilityCursorPosition = ACCESSIBILITY_CURSOR_POSITION_UNDEFINED; @@ -7723,7 +7731,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (mAutofillId == null) { // The autofill id needs to be unique, but its value doesn't matter, // so it's better to reuse the accessibility id to save space. - mAutofillId = new AutofillId(getAccessibilityViewId()); + mAutofillId = new AutofillId(getAutofillViewId()); } return mAutofillId; } @@ -7956,7 +7964,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, private boolean isAutofillable() { return getAutofillType() != AUTOFILL_TYPE_NONE && isImportantForAutofill() - && getAccessibilityViewId() > LAST_APP_ACCESSIBILITY_ID; + && getAutofillViewId() > LAST_APP_AUTOFILL_ID; } private void populateVirtualStructure(ViewStructure structure, @@ -8474,12 +8482,26 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public int getAccessibilityViewId() { if (mAccessibilityViewId == NO_ID) { - mAccessibilityViewId = mContext.getNextAccessibilityId(); + mAccessibilityViewId = sNextAccessibilityViewId++; } return mAccessibilityViewId; } /** + * Gets the unique identifier of this view on the screen for autofill purposes. + * + * @return The view autofill id. + * + * @hide + */ + public int getAutofillViewId() { + if (mAutofillViewId == NO_ID) { + mAutofillViewId = mContext.getNextAutofillId(); + } + return mAutofillViewId; + } + + /** * Gets the unique identifier of the window in which this View reseides. * * @return The window accessibility id. @@ -12117,7 +12139,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if (isAutofillable()) { AutofillManager afm = getAutofillManager(); - if (afm != null && getAccessibilityViewId() > LAST_APP_ACCESSIBILITY_ID) { + if (afm != null && getAutofillViewId() > LAST_APP_AUTOFILL_ID) { if (mVisibilityChangeForAutofillHandler != null) { mVisibilityChangeForAutofillHandler.removeMessages(0); } @@ -17547,16 +17569,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * * @return Returns a Parcelable object containing the view's current dynamic * state, or null if there is nothing interesting to save. - * @see #onRestoreInstanceState(android.os.Parcelable) - * @see #saveHierarchyState(android.util.SparseArray) - * @see #dispatchSaveInstanceState(android.util.SparseArray) + * @see #onRestoreInstanceState(Parcelable) + * @see #saveHierarchyState(SparseArray) + * @see #dispatchSaveInstanceState(SparseArray) * @see #setSaveEnabled(boolean) */ @CallSuper @Nullable protected Parcelable onSaveInstanceState() { mPrivateFlags |= PFLAG_SAVE_STATE_CALLED; if (mStartActivityRequestWho != null || isAutofilled() - || mAccessibilityViewId > LAST_APP_ACCESSIBILITY_ID) { + || mAutofillViewId > LAST_APP_AUTOFILL_ID) { BaseSavedState state = new BaseSavedState(AbsSavedState.EMPTY_STATE); if (mStartActivityRequestWho != null) { @@ -17567,13 +17589,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, state.mSavedData |= BaseSavedState.IS_AUTOFILLED; } - if (mAccessibilityViewId > LAST_APP_ACCESSIBILITY_ID) { - state.mSavedData |= BaseSavedState.ACCESSIBILITY_ID; + if (mAutofillViewId > LAST_APP_AUTOFILL_ID) { + state.mSavedData |= BaseSavedState.AUTOFILL_ID; } state.mStartActivityRequestWhoSaved = mStartActivityRequestWho; state.mIsAutofilled = isAutofilled(); - state.mAccessibilityViewId = mAccessibilityViewId; + state.mAutofillViewId = mAutofillViewId; return state; } return BaseSavedState.EMPTY_STATE; @@ -17651,8 +17673,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, if ((baseState.mSavedData & BaseSavedState.IS_AUTOFILLED) != 0) { setAutofilled(baseState.mIsAutofilled); } - if ((baseState.mSavedData & BaseSavedState.ACCESSIBILITY_ID) != 0) { - mAccessibilityViewId = baseState.mAccessibilityViewId; + if ((baseState.mSavedData & BaseSavedState.AUTOFILL_ID) != 0) { + mAutofillViewId = baseState.mAutofillViewId; } } } @@ -21476,7 +21498,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, * @param accessibilityId The searched accessibility id. * @return The found view. */ - final <T extends View> T findViewByAccessibilityId(int accessibilityId) { + final <T extends View> T findViewByAccessibilityId(int accessibilityId) { if (accessibilityId < 0) { return null; } @@ -21488,11 +21510,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** - * Performs the traversal to find a view by its unuque and stable accessibility id. + * Performs the traversal to find a view by its unique and stable accessibility id. * * <strong>Note:</strong>This method does not stop at the root namespace * boundary since the user can touch the screen at an arbitrary location - * potentially crossing the root namespace bounday which will send an + * potentially crossing the root namespace boundary which will send an * accessibility event to accessibility services and they should be able * to obtain the event source. Also accessibility ids are guaranteed to be * unique in the window. @@ -21509,6 +21531,23 @@ public class View implements Drawable.Callback, KeyEvent.Callback, } /** + * Performs the traversal to find a view by its autofill id. + * + * <strong>Note:</strong>This method does not stop at the root namespace + * boundary. + * + * @param autofillId The autofill id. + * @return The found view. + * @hide + */ + public <T extends View> T findViewByAutofillIdTraversal(int autofillId) { + if (getAutofillViewId() == autofillId) { + return (T) this; + } + return null; + } + + /** * Look for a child view with the given tag. If this view has the given * tag, return this view. * @@ -24974,13 +25013,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback, public static class BaseSavedState extends AbsSavedState { static final int START_ACTIVITY_REQUESTED_WHO_SAVED = 0b1; static final int IS_AUTOFILLED = 0b10; - static final int ACCESSIBILITY_ID = 0b100; + static final int AUTOFILL_ID = 0b100; // Flags that describe what data in this state is valid int mSavedData; String mStartActivityRequestWhoSaved; boolean mIsAutofilled; - int mAccessibilityViewId; + int mAutofillViewId; /** * Constructor used when reading from a parcel. Reads the state of the superclass. @@ -25003,7 +25042,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, mSavedData = source.readInt(); mStartActivityRequestWhoSaved = source.readString(); mIsAutofilled = source.readBoolean(); - mAccessibilityViewId = source.readInt(); + mAutofillViewId = source.readInt(); } /** @@ -25022,7 +25061,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, out.writeInt(mSavedData); out.writeString(mStartActivityRequestWhoSaved); out.writeBoolean(mIsAutofilled); - out.writeInt(mAccessibilityViewId); + out.writeInt(mAutofillViewId); } public static final Parcelable.Creator<BaseSavedState> CREATOR diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 4b79b8c5be9a..66df335f07da 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -1361,6 +1361,27 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return null; } + /** @hide */ + @Override + public View findViewByAutofillIdTraversal(int autofillId) { + View foundView = super.findViewByAutofillIdTraversal(autofillId); + if (foundView != null) { + return foundView; + } + + final int childrenCount = mChildrenCount; + final View[] children = mChildren; + for (int i = 0; i < childrenCount; i++) { + View child = children[i]; + foundView = child.findViewByAutofillIdTraversal(autofillId); + if (foundView != null) { + return foundView; + } + } + + return null; + } + @Override public void dispatchWindowFocusChanged(boolean hasFocus) { super.dispatchWindowFocusChanged(hasFocus); diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 2605b4a2e50c..fd950dbffb4b 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -2471,6 +2471,9 @@ public final class ViewRootImpl implements ViewParent, mInLayout = true; final View host = mView; + if (host == null) { + return; + } if (DEBUG_ORIENTATION || DEBUG_LAYOUT) { Log.v(mTag, "Laying out " + host + " to (" + host.getMeasuredWidth() + ", " + host.getMeasuredHeight() + ")"); @@ -2778,6 +2781,8 @@ public final class ViewRootImpl implements ViewParent, private void performDraw() { if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) { return; + } else if (mView == null) { + return; } final boolean fullRedrawNeeded = mFullRedrawNeeded; diff --git a/core/java/android/view/accessibility/AccessibilityManager.java b/core/java/android/view/accessibility/AccessibilityManager.java index 41a8b8a4b750..bdadcc7cd987 100644 --- a/core/java/android/view/accessibility/AccessibilityManager.java +++ b/core/java/android/view/accessibility/AccessibilityManager.java @@ -912,14 +912,14 @@ public final class AccessibilityManager { } /** - * Notifies that the availability of the accessibility button in the system's navigation area + * Notifies that the visibility of the accessibility button in the system's navigation area * has changed. * - * @param available {@code true} if the accessibility button is available within the system + * @param shown {@code true} if the accessibility button is visible within the system * navigation area, {@code false} otherwise * @hide */ - public void notifyAccessibilityButtonAvailabilityChanged(boolean available) { + public void notifyAccessibilityButtonVisibilityChanged(boolean shown) { final IAccessibilityManager service; synchronized (mLock) { service = getServiceLocked(); @@ -928,9 +928,9 @@ public final class AccessibilityManager { } } try { - service.notifyAccessibilityButtonAvailabilityChanged(available); + service.notifyAccessibilityButtonVisibilityChanged(shown); } catch (RemoteException re) { - Log.e(LOG_TAG, "Error while dispatching accessibility button availability change", re); + Log.e(LOG_TAG, "Error while dispatching accessibility button visibility change", re); } } diff --git a/core/java/android/view/accessibility/IAccessibilityManager.aidl b/core/java/android/view/accessibility/IAccessibilityManager.aidl index 06cb5dca8afa..3f499abd2e4d 100644 --- a/core/java/android/view/accessibility/IAccessibilityManager.aidl +++ b/core/java/android/view/accessibility/IAccessibilityManager.aidl @@ -64,7 +64,7 @@ interface IAccessibilityManager { void notifyAccessibilityButtonClicked(); - void notifyAccessibilityButtonAvailabilityChanged(boolean available); + void notifyAccessibilityButtonVisibilityChanged(boolean available); // Requires WRITE_SECURE_SETTINGS void performAccessibilityShortcut(); diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index 310ec1c938d0..5b04f41c7ee2 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -246,20 +246,20 @@ public final class AutofillManager { /** * Finds views by traversing the hierarchies of the client. * - * @param viewIds The accessibility ids of the views to find + * @param viewIds The autofill ids of the views to find * * @return And array containing the views (empty if no views found). */ - @NonNull View[] findViewsByAccessibilityIdTraversal(@NonNull int[] viewIds); + @NonNull View[] findViewsByAutofillIdTraversal(@NonNull int[] viewIds); /** * Finds a view by traversing the hierarchies of the client. * - * @param viewId The accessibility id of the views to find + * @param viewId The autofill id of the views to find * * @return The view, or {@code null} if not found */ - @Nullable View findViewByAccessibilityIdTraversal(int viewId); + @Nullable View findViewByAutofillIdTraversal(int viewId); /** * Runs the specified action on the UI thread. @@ -795,11 +795,11 @@ public final class AutofillManager { } private static AutofillId getAutofillId(View view) { - return new AutofillId(view.getAccessibilityViewId()); + return new AutofillId(view.getAutofillViewId()); } private static AutofillId getAutofillId(View parent, int virtualId) { - return new AutofillId(parent.getAccessibilityViewId(), virtualId); + return new AutofillId(parent.getAutofillViewId(), virtualId); } private void startSessionLocked(@NonNull AutofillId id, @NonNull Rect bounds, @@ -1039,7 +1039,7 @@ public final class AutofillManager { final int itemCount = ids.size(); int numApplied = 0; ArrayMap<View, SparseArray<AutofillValue>> virtualValues = null; - final View[] views = client.findViewsByAccessibilityIdTraversal(getViewIds(ids)); + final View[] views = client.findViewsByAutofillIdTraversal(getViewIds(ids)); for (int i = 0; i < itemCount; i++) { final AutofillId id = ids.get(i); @@ -1232,7 +1232,7 @@ public final class AutofillManager { return null; } - return client.findViewByAccessibilityIdTraversal(autofillId.getViewId()); + return client.findViewByAutofillIdTraversal(autofillId.getViewId()); } /** @hide */ diff --git a/core/java/android/widget/DayPickerView.java b/core/java/android/widget/DayPickerView.java index 1e8207a2e7b3..f712d5fa65ca 100644 --- a/core/java/android/widget/DayPickerView.java +++ b/core/java/android/widget/DayPickerView.java @@ -293,9 +293,19 @@ class DayPickerView extends ViewGroup { * @param setSelected whether to set the specified day as selected */ private void setDate(long timeInMillis, boolean animate, boolean setSelected) { + boolean dateClamped = false; + // Clamp the target day in milliseconds to the min or max if outside the range. + if (timeInMillis < mMinDate.getTimeInMillis()) { + timeInMillis = mMinDate.getTimeInMillis(); + dateClamped = true; + } else if (timeInMillis > mMaxDate.getTimeInMillis()) { + timeInMillis = mMaxDate.getTimeInMillis(); + dateClamped = true; + } + getTempCalendarForTime(timeInMillis); - if (setSelected) { + if (setSelected || dateClamped) { mSelectedDay.setTimeInMillis(timeInMillis); } @@ -353,13 +363,6 @@ class DayPickerView extends ViewGroup { public void onRangeChanged() { mAdapter.setRange(mMinDate, mMaxDate); - // Clamp the selected day to the new min/max. - if (mSelectedDay.before(mMinDate)) { - mSelectedDay.setTimeInMillis(mMinDate.getTimeInMillis()); - } else if (mSelectedDay.after(mMaxDate)) { - mSelectedDay.setTimeInMillis(mMaxDate.getTimeInMillis()); - } - // Changing the min/max date changes the selection position since we // don't really have stable IDs. Jumps immediately to the new position. setDate(mSelectedDay.getTimeInMillis(), false, false); diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 0e6e3aee28b1..45e5f8adc468 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -2584,14 +2584,18 @@ public class Editor { if (offset == -1) { return; } + stopTextActionModeWithPreservingSelection(); - final boolean isOnSelection = mTextView.hasSelection() - && offset >= mTextView.getSelectionStart() && offset <= mTextView.getSelectionEnd(); - if (!isOnSelection) { - // Right clicked position is not on the selection. Remove the selection and move the - // cursor to the right clicked position. - Selection.setSelection((Spannable) mTextView.getText(), offset); - stopTextActionMode(); + if (mTextView.canSelectText()) { + final boolean isOnSelection = mTextView.hasSelection() + && offset >= mTextView.getSelectionStart() + && offset <= mTextView.getSelectionEnd(); + if (!isOnSelection) { + // Right clicked position is not on the selection. Remove the selection and move the + // cursor to the right clicked position. + Selection.setSelection((Spannable) mTextView.getText(), offset); + stopTextActionMode(); + } } if (shouldOfferToShowSuggestions()) { diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 7117137ce2f8..aa6ffceea9f3 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -2342,24 +2342,26 @@ public class RemoteViews implements Parcelable, Filter { } - public synchronized RemoteViews clone() { - Preconditions.checkState(mIsRoot, "RemoteView has been attached to another RemoteView. " - + "May only clone the root of a RemoteView hierarchy."); + public RemoteViews clone() { + synchronized (this) { + Preconditions.checkState(mIsRoot, "RemoteView has been attached to another RemoteView. " + + "May only clone the root of a RemoteView hierarchy."); - Parcel p = Parcel.obtain(); + Parcel p = Parcel.obtain(); - // Do not parcel the Bitmap cache - doing so creates an expensive copy of all bitmaps. - // Instead pretend we're not owning the cache while parceling. - mIsRoot = false; - writeToParcel(p, PARCELABLE_ELIDE_DUPLICATES); - p.setDataPosition(0); - mIsRoot = true; + // Do not parcel the Bitmap cache - doing so creates an expensive copy of all bitmaps. + // Instead pretend we're not owning the cache while parceling. + mIsRoot = false; + writeToParcel(p, PARCELABLE_ELIDE_DUPLICATES); + p.setDataPosition(0); + mIsRoot = true; - RemoteViews rv = new RemoteViews(p, mBitmapCache.clone(), mApplication, 0); - rv.mIsRoot = true; + RemoteViews rv = new RemoteViews(p, mBitmapCache.clone(), mApplication, 0); + rv.mIsRoot = true; - p.recycle(); - return rv; + p.recycle(); + return rv; + } } public String getPackage() { diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index 5839fd50d79a..7744e0cd6b5d 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -394,6 +394,16 @@ static void nativeAllocateBuffers(JNIEnv* /* env */ , jclass /* clazz */, static jlong nativeCreateFromSurfaceControl(JNIEnv* env, jclass clazz, jlong surfaceControlNativeObj) { + sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(surfaceControlNativeObj)); + sp<Surface> surface(ctrl->createSurface()); + if (surface != NULL) { + surface->incStrong(&sRefBaseOwner); + } + return reinterpret_cast<jlong>(surface.get()); +} + +static jlong nativeGetFromSurfaceControl(JNIEnv* env, jclass clazz, + jlong surfaceControlNativeObj) { /* * This is used by the WindowManagerService just after constructing * a Surface and is necessary for returning the Surface reference to @@ -590,6 +600,8 @@ static const JNINativeMethod gSurfaceMethods[] = { (void*)nativeAllocateBuffers }, {"nativeCreateFromSurfaceControl", "(J)J", (void*)nativeCreateFromSurfaceControl }, + {"nativeGetFromSurfaceControl", "(J)J", + (void*)nativeGetFromSurfaceControl }, {"nativeReadFromParcel", "(JLandroid/os/Parcel;)J", (void*)nativeReadFromParcel }, {"nativeWriteToParcel", "(JLandroid/os/Parcel;)V", diff --git a/core/jni/android_view_ThreadedRenderer.cpp b/core/jni/android_view_ThreadedRenderer.cpp index 438b1233a286..c9251bcac835 100644 --- a/core/jni/android_view_ThreadedRenderer.cpp +++ b/core/jni/android_view_ThreadedRenderer.cpp @@ -932,6 +932,10 @@ static jobject android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode( return createBitmap(env, bitmap.release(), android::bitmap::kBitmapCreateFlag_Mutable); } +static void android_view_ThreadedRenderer_disableVsync(JNIEnv*, jclass) { + RenderProxy::disableVsync(); +} + // ---------------------------------------------------------------------------- // FrameMetricsObserver // ---------------------------------------------------------------------------- @@ -1030,6 +1034,7 @@ static const JNINativeMethod gMethods[] = { (void*)android_view_ThreadedRenderer_copySurfaceInto }, { "nCreateHardwareBitmap", "(JII)Landroid/graphics/Bitmap;", (void*)android_view_ThreadedRenderer_createHardwareBitmapFromRenderNode }, + { "disableVsync", "()V", (void*)android_view_ThreadedRenderer_disableVsync }, }; int register_android_view_ThreadedRenderer(JNIEnv* env) { diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index 325177126f04..8139176a7732 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -1288,10 +1288,10 @@ <string name="vpn_title_long" msgid="6400714798049252294">"VPN aktiveres af <xliff:g id="APP">%s</xliff:g>"</string> <string name="vpn_text" msgid="1610714069627824309">"Tryk for at administrere netværket."</string> <string name="vpn_text_long" msgid="4907843483284977618">"Forbundet til <xliff:g id="SESSION">%s</xliff:g>. Tryk for at administrere netværket."</string> - <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Opretter forbindelse til altid aktiveret VPN…"</string> - <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Altid aktiveret VPN er forbundet"</string> - <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Forbindelsen til altid aktiveret VPN er afbrudt"</string> - <string name="vpn_lockdown_error" msgid="6009249814034708175">"Fejl i altid aktiveret VPN"</string> + <string name="vpn_lockdown_connecting" msgid="6443438964440960745">"Opretter forbindelse til konstant VPN…"</string> + <string name="vpn_lockdown_connected" msgid="8202679674819213931">"Konstant VPN er forbundet"</string> + <string name="vpn_lockdown_disconnected" msgid="4532298952570796327">"Forbindelsen til konstant VPN er afbrudt"</string> + <string name="vpn_lockdown_error" msgid="6009249814034708175">"Fejl i konstant VPN"</string> <string name="vpn_lockdown_config" msgid="5099330695245008680">"Tryk for at konfigurere"</string> <string name="upload_file" msgid="2897957172366730416">"Vælg fil"</string> <string name="no_file_chosen" msgid="6363648562170759465">"Ingen fil er valgt"</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 778163c411e2..a341a8a8f8e0 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -447,7 +447,7 @@ <string name="permlab_changeTetherState" msgid="5952584964373017960">"Tethering-Konnektivität ändern"</string> <string name="permdesc_changeTetherState" msgid="1524441344412319780">"Ermöglicht der App, den Status der Tethering-Konnektivität zu ändern"</string> <string name="permlab_accessWifiState" msgid="5202012949247040011">"WLAN-Verbindungen abrufen"</string> - <string name="permdesc_accessWifiState" msgid="5002798077387803726">"Ermöglicht der App, Informationen zu WLANs abzurufen, etwa ob ein WLAN aktiviert ist, und den Namen verbundener WLAN-Geräte."</string> + <string name="permdesc_accessWifiState" msgid="5002798077387803726">"Ermöglicht der App, Informationen zu WLAN-Netzwerken abzurufen, etwa ob ein WLAN aktiviert ist, und den Namen verbundener WLAN-Geräte."</string> <string name="permlab_changeWifiState" msgid="6550641188749128035">"WLAN-Verbindungen herstellen und trennen"</string> <string name="permdesc_changeWifiState" msgid="7137950297386127533">"Ermöglicht der App, eine Verbindung zu WLAN-Zugangspunkten herzustellen und solche zu trennen und Änderungen an der Gerätekonfiguration für WLAN-Netzwerke vorzunehmen."</string> <string name="permlab_changeWifiMulticastState" msgid="1368253871483254784">"WLAN-Multicast-Empfang zulassen"</string> diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index aad81df0b7e5..b5872485b136 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -69,6 +69,7 @@ bool Properties::waitForGpuCompletion = false; bool Properties::forceDrawFrame = false; bool Properties::filterOutTestOverhead = false; +bool Properties::disableVsync = false; static int property_get_int(const char* key, int defaultValue) { char buf[PROPERTY_VALUE_MAX] = {'\0',}; diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index 9db64493928a..91b4a2d440e2 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -318,6 +318,12 @@ public: // any overhead they add static bool filterOutTestOverhead; + // Workaround a device lockup in edge cases by switching to async mode + // instead of the default vsync (b/38372997). Only system_server should hit this. + // Any existing RenderProxy & Surface combination will be unaffected, only things + // created after changing this. + static bool disableVsync; + // Used for testing only to change the render pipeline. #ifdef HWUI_GLES_WRAP_ENABLED static void overrideRenderPipelineType(RenderPipelineType); diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp index 44af5fd8d0dd..ed3070887b8b 100644 --- a/libs/hwui/renderthread/EglManager.cpp +++ b/libs/hwui/renderthread/EglManager.cpp @@ -279,6 +279,9 @@ bool EglManager::makeCurrent(EGLSurface surface, EGLint* errOut) { } } mCurrentSurface = surface; + if (Properties::disableVsync) { + eglSwapInterval(mEglDisplay, 0); + } return true; } diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index eed523810403..d842be9e7d6e 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -18,6 +18,7 @@ #include "DeferredLayerUpdater.h" #include "DisplayList.h" +#include "Properties.h" #include "Readback.h" #include "Rect.h" #include "renderthread/CanvasContext.h" @@ -708,6 +709,10 @@ void RenderProxy::onBitmapDestroyed(uint32_t pixelRefId) { thread.queue(task); } +void RenderProxy::disableVsync() { + Properties::disableVsync = true; +} + void RenderProxy::post(RenderTask* task) { mRenderThread.queue(task); } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index b21772cd88de..6f4e8cef4502 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -137,6 +137,8 @@ public: static int copyGraphicBufferInto(GraphicBuffer* buffer, SkBitmap* bitmap); static void onBitmapDestroyed(uint32_t pixelRefId); + + ANDROID_API static void disableVsync(); private: RenderThread& mRenderThread; CanvasContext* mContext; diff --git a/media/jni/android_media_ImageReader.cpp b/media/jni/android_media_ImageReader.cpp index 163c4b012262..f408e57e29e4 100644 --- a/media/jni/android_media_ImageReader.cpp +++ b/media/jni/android_media_ImageReader.cpp @@ -177,6 +177,7 @@ BufferItem* JNIImageReaderContext::getBufferItem() { } void JNIImageReaderContext::returnBufferItem(BufferItem* buffer) { + buffer->mGraphicBuffer = nullptr; mBuffers.push_back(buffer); } diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java index 6140428d353f..41952dfe7f70 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java +++ b/packages/PrintSpooler/src/com/android/printspooler/model/PageContentRepository.java @@ -16,6 +16,8 @@ package com.android.printspooler.model; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.ActivityManager; import android.content.ComponentName; import android.content.Context; @@ -29,21 +31,27 @@ import android.os.AsyncTask; import android.os.IBinder; import android.os.ParcelFileDescriptor; import android.os.RemoteException; +import android.print.PageRange; import android.print.PrintAttributes; -import android.print.PrintAttributes.MediaSize; import android.print.PrintAttributes.Margins; +import android.print.PrintAttributes.MediaSize; import android.print.PrintDocumentInfo; import android.util.ArrayMap; import android.util.Log; import android.view.View; + import com.android.internal.annotations.GuardedBy; import com.android.printspooler.renderer.IPdfRenderer; import com.android.printspooler.renderer.PdfManipulationService; import com.android.printspooler.util.BitmapSerializeUtils; +import com.android.printspooler.util.PageRangeUtils; + import dalvik.system.CloseGuard; + import libcore.io.IoUtils; import java.io.IOException; +import java.util.Arrays; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; @@ -69,8 +77,9 @@ public final class PageContentRepository { private RenderSpec mLastRenderSpec; - private int mScheduledPreloadFirstShownPage = INVALID_PAGE_INDEX; - private int mScheduledPreloadLastShownPage = INVALID_PAGE_INDEX; + @Nullable private PageRange mScheduledPreloadVisiblePages; + @Nullable private PageRange[] mScheduledPreloadSelectedPages; + @Nullable private PageRange[] mScheduledPreloadWrittenPages; private int mState; @@ -129,14 +138,24 @@ public final class PageContentRepository { } } - public void startPreload(int firstShownPage, int lastShownPage) { + /** + * Preload selected, written pages around visiblePages. + * + * @param visiblePages The pages currently visible + * @param selectedPages The pages currently selected (e.g. they might become visible by + * scrolling) + * @param writtenPages The pages currently in the document + */ + public void startPreload(@NonNull PageRange visiblePages, @NonNull PageRange[] selectedPages, + @NonNull PageRange[] writtenPages) { // If we do not have a render spec we have no clue what size the // preloaded bitmaps should be, so just take a note for what to do. if (mLastRenderSpec == null) { - mScheduledPreloadFirstShownPage = firstShownPage; - mScheduledPreloadLastShownPage = lastShownPage; + mScheduledPreloadVisiblePages = visiblePages; + mScheduledPreloadSelectedPages = selectedPages; + mScheduledPreloadWrittenPages = writtenPages; } else if (mState == STATE_OPENED) { - mRenderer.startPreload(firstShownPage, lastShownPage, mLastRenderSpec); + mRenderer.startPreload(visiblePages, selectedPages, writtenPages, mLastRenderSpec); } } @@ -222,11 +241,12 @@ public final class PageContentRepository { // We tired to preload but didn't know the bitmap size, now // that we know let us do the work. - if (mScheduledPreloadFirstShownPage != INVALID_PAGE_INDEX - && mScheduledPreloadLastShownPage != INVALID_PAGE_INDEX) { - startPreload(mScheduledPreloadFirstShownPage, mScheduledPreloadLastShownPage); - mScheduledPreloadFirstShownPage = INVALID_PAGE_INDEX; - mScheduledPreloadLastShownPage = INVALID_PAGE_INDEX; + if (mScheduledPreloadVisiblePages != null) { + startPreload(mScheduledPreloadVisiblePages, mScheduledPreloadSelectedPages, + mScheduledPreloadWrittenPages); + mScheduledPreloadVisiblePages = null; + mScheduledPreloadSelectedPages = null; + mScheduledPreloadWrittenPages = null; } if (mState == STATE_OPENED) { @@ -523,10 +543,45 @@ public final class PageContentRepository { mDestroyed = true; } - public void startPreload(int firstShownPage, int lastShownPage, RenderSpec renderSpec) { + /** + * How many pages are {@code pages} before pageNum. E.g. page 5 in [0-1], [4-7] has the + * index 4. + * + * @param pageNum The number of the page to find + * @param pages A normalized array of page ranges + * + * @return The index or {@link #INVALID_PAGE_INDEX} if not found + */ + private int findIndexOfPage(int pageNum, @NonNull PageRange[] pages) { + int pagesBefore = 0; + for (int i = 0; i < pages.length; i++) { + if (pages[i].contains(pageNum)) { + return pagesBefore + pageNum - pages[i].getStart(); + } else { + pagesBefore += pages[i].getSize(); + } + } + + return INVALID_PAGE_INDEX; + } + + void startPreload(@NonNull PageRange visiblePages, @NonNull PageRange[] selectedPages, + @NonNull PageRange[] writtenPages, RenderSpec renderSpec) { + if (PageRangeUtils.isAllPages(selectedPages)) { + selectedPages = new PageRange[]{new PageRange(0, mPageCount - 1)}; + } + if (DEBUG) { - Log.i(LOG_TAG, "Preloading pages around [" + firstShownPage - + "-" + lastShownPage + "]"); + Log.i(LOG_TAG, "Preloading pages around " + visiblePages + " from " + + Arrays.toString(selectedPages)); + } + + int firstVisiblePageIndex = findIndexOfPage(visiblePages.getStart(), selectedPages); + int lastVisiblePageIndex = findIndexOfPage(visiblePages.getEnd(), selectedPages); + + if (firstVisiblePageIndex == INVALID_PAGE_INDEX + || lastVisiblePageIndex == INVALID_PAGE_INDEX) { + return; } final int bitmapSizeInBytes = renderSpec.bitmapWidth * renderSpec.bitmapHeight @@ -534,28 +589,33 @@ public final class PageContentRepository { final int maxCachedPageCount = mPageContentCache.getMaxSizeInBytes() / bitmapSizeInBytes; final int halfPreloadCount = (maxCachedPageCount - - (lastShownPage - firstShownPage)) / 2 - 1; + - (lastVisiblePageIndex - firstVisiblePageIndex)) / 2 - 1; - final int excessFromStart; - if (firstShownPage - halfPreloadCount < 0) { - excessFromStart = halfPreloadCount - firstShownPage; - } else { - excessFromStart = 0; - } + final int fromIndex = Math.max(firstVisiblePageIndex - halfPreloadCount, 0); + final int toIndex = lastVisiblePageIndex + halfPreloadCount; - final int excessFromEnd; - if (lastShownPage + halfPreloadCount >= mPageCount) { - excessFromEnd = (lastShownPage + halfPreloadCount) - mPageCount; - } else { - excessFromEnd = 0; + if (DEBUG) { + Log.i(LOG_TAG, "fromIndex=" + fromIndex + " toIndex=" + toIndex); } - final int fromIndex = Math.max(firstShownPage - halfPreloadCount - excessFromEnd, 0); - final int toIndex = Math.min(lastShownPage + halfPreloadCount + excessFromStart, - mPageCount - 1); + int previousRangeSizes = 0; + for (int rangeNum = 0; rangeNum < selectedPages.length; rangeNum++) { + PageRange range = selectedPages[rangeNum]; + + int thisRangeStart = Math.max(0, fromIndex - previousRangeSizes); + int thisRangeEnd = Math.min(range.getSize(), toIndex - previousRangeSizes + 1); + + for (int i = thisRangeStart; i < thisRangeEnd; i++) { + if (PageRangeUtils.contains(writtenPages, range.getStart() + i)) { + if (DEBUG) { + Log.i(LOG_TAG, "Preloading " + (range.getStart() + i)); + } + + renderPage(range.getStart() + i, renderSpec, null); + } + } - for (int i = fromIndex; i <= toIndex; i++) { - renderPage(i, renderSpec, null); + previousRangeSizes += range.getSize(); } } diff --git a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java index 54400b3b8540..1eadb8e9765a 100644 --- a/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java +++ b/packages/PrintSpooler/src/com/android/printspooler/ui/PageAdapter.java @@ -16,6 +16,7 @@ package com.android.printspooler.ui; +import android.annotation.NonNull; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -24,8 +25,8 @@ import android.os.Handler; import android.os.Looper; import android.os.ParcelFileDescriptor; import android.print.PageRange; -import android.print.PrintAttributes.MediaSize; import android.print.PrintAttributes.Margins; +import android.print.PrintAttributes.MediaSize; import android.print.PrintDocumentInfo; import android.support.v7.widget.RecyclerView.Adapter; import android.support.v7.widget.RecyclerView.ViewHolder; @@ -33,11 +34,12 @@ import android.util.Log; import android.util.SparseArray; import android.view.LayoutInflater; import android.view.View; +import android.view.View.MeasureSpec; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; -import android.view.View.MeasureSpec; import android.widget.TextView; + import com.android.printspooler.R; import com.android.printspooler.model.OpenDocumentCallback; import com.android.printspooler.model.PageContentRepository; @@ -45,6 +47,7 @@ import com.android.printspooler.model.PageContentRepository.PageContentProvider; import com.android.printspooler.util.PageRangeUtils; import com.android.printspooler.widget.PageContentView; import com.android.printspooler.widget.PreviewPageFrame; + import dalvik.system.CloseGuard; import java.util.ArrayList; @@ -794,14 +797,16 @@ public final class PageAdapter extends Adapter<ViewHolder> { page.setTag(null); } - public void startPreloadContent(PageRange pageRangeInAdapter) { - final int startPageInDocument = computePageIndexInDocument(pageRangeInAdapter.getStart()); - final int startPageInFile = computePageIndexInFile(startPageInDocument); - final int endPageInDocument = computePageIndexInDocument(pageRangeInAdapter.getEnd()); - final int endPageInFile = computePageIndexInFile(endPageInDocument); - if (startPageInDocument != INVALID_PAGE_INDEX && endPageInDocument != INVALID_PAGE_INDEX) { - mPageContentRepository.startPreload(startPageInFile, endPageInFile); + void startPreloadContent(@NonNull PageRange visiblePagesInAdapter) { + int startVisibleDocument = computePageIndexInDocument(visiblePagesInAdapter.getStart()); + int endVisibleDocument = computePageIndexInDocument(visiblePagesInAdapter.getEnd()); + if (startVisibleDocument == INVALID_PAGE_INDEX + || endVisibleDocument == INVALID_PAGE_INDEX) { + return; } + + mPageContentRepository.startPreload(new PageRange(startVisibleDocument, endVisibleDocument), + mSelectedPages, mWrittenPages); } public void stopPreloadContent() { diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index 48bf180cb0ca..f5d514053984 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -273,7 +273,7 @@ <string name="show_all_anrs" msgid="28462979638729082">"Tots els errors sense resposta"</string> <string name="show_all_anrs_summary" msgid="641908614413544127">"Informa que una aplicació en segon pla no respon"</string> <string name="show_notification_channel_warnings" msgid="1399948193466922683">"Mostra avisos del canal de notificacions"</string> - <string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"Mostra un avís a la pantalla quan una app publica una notificació sense canal vàlid"</string> + <string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"Mostra un avís a la pantalla quan una aplicació publica una notificació sense un canal vàlid"</string> <string name="force_allow_on_external" msgid="3215759785081916381">"Força permís d\'aplicacions a l\'emmagatzem. extern"</string> <string name="force_allow_on_external_summary" msgid="3640752408258034689">"Permet que qualsevol aplicació es pugui escriure en un dispositiu d’emmagatzematge extern, independentment dels valors definits"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Força l\'ajust de la mida de les activitats"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index 00f0b5eb9192..6889e0155396 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -272,8 +272,8 @@ <string name="app_process_limit_title" msgid="4280600650253107163">"Limite proc. em 2º plano"</string> <string name="show_all_anrs" msgid="28462979638729082">"Mostrar todos os ANR"</string> <string name="show_all_anrs_summary" msgid="641908614413544127">"Mostrar erro \"Aplic. não Resp.\" p/ aplic. 2º plano"</string> - <string name="show_notification_channel_warnings" msgid="1399948193466922683">"Mostrar avisos do canal de notif."</string> - <string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"Mostra um aviso no ecrã quando uma aplic. publica uma notific. sem um canal válido"</string> + <string name="show_notification_channel_warnings" msgid="1399948193466922683">"Mostrar avisos do canal de notificações"</string> + <string name="show_notification_channel_warnings_summary" msgid="5536803251863694895">"Mostra um aviso no ecrã quando uma aplicação publica uma notificação sem o canal ser válido"</string> <string name="force_allow_on_external" msgid="3215759785081916381">"Forçar perm. de aplicações no armazenamento ext."</string> <string name="force_allow_on_external_summary" msgid="3640752408258034689">"Torna qualquer aplicação elegível para ser gravada no armazenamento externo, independentemente dos valores do manifesto"</string> <string name="force_resizable_activities" msgid="8615764378147824985">"Forçar as atividades a serem redimensionáveis"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index 33b973d72a38..9a0b1bf6433c 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -79,7 +79,7 @@ <string name="bluetooth_pairing_decline" msgid="4185420413578948140">"取消"</string> <string name="bluetooth_pairing_will_share_phonebook" msgid="4982239145676394429">"配對完成後,所配對的裝置即可在連線後存取你的聯絡人和通話紀錄。"</string> <string name="bluetooth_pairing_error_message" msgid="3748157733635947087">"無法與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 配對。"</string> - <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"無法與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 配對,因為 PIN 或密碼金鑰不正確。"</string> + <string name="bluetooth_pairing_pin_error_message" msgid="8337234855188925274">"無法與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 配對,因為 PIN 碼或密碼金鑰不正確。"</string> <string name="bluetooth_pairing_device_down_error_message" msgid="7870998403045801381">"無法與 <xliff:g id="DEVICE_NAME">%1$s</xliff:g> 通訊。"</string> <string name="bluetooth_pairing_rejected_error_message" msgid="1648157108520832454">"「<xliff:g id="DEVICE_NAME">%1$s</xliff:g>」拒絕配對要求。"</string> <string name="accessibility_wifi_off" msgid="1166761729660614716">"已關閉 Wi-Fi。"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java index 474de9074062..1cbb7450f7e1 100644 --- a/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java +++ b/packages/SettingsLib/src/com/android/settingslib/datetime/ZoneGetter.java @@ -88,21 +88,16 @@ public class ZoneGetter { private static final String XMLTAG_TIMEZONE = "timezone"; public static CharSequence getTimeZoneOffsetAndName(Context context, TimeZone tz, Date now) { - final Locale locale = Locale.getDefault(); - final CharSequence gmtText = getGmtOffsetText(context, locale, tz, now); - final TimeZoneNames timeZoneNames = TimeZoneNames.getInstance(locale); - final ZoneGetterData data = new ZoneGetterData(context); - - final boolean useExemplarLocationForLocalNames = - shouldUseExemplarLocationForLocalNames(data, timeZoneNames); - final CharSequence zoneName = getTimeZoneDisplayName(data, timeZoneNames, - useExemplarLocationForLocalNames, tz, tz.getID()); - if (zoneName == null) { + Locale locale = Locale.getDefault(); + CharSequence gmtText = getGmtOffsetText(context, locale, tz, now); + TimeZoneNames timeZoneNames = TimeZoneNames.getInstance(locale); + String zoneNameString = getZoneLongName(timeZoneNames, tz, now); + if (zoneNameString == null) { return gmtText; } // We don't use punctuation here to avoid having to worry about localizing that too! - return TextUtils.concat(gmtText, " ", zoneName); + return TextUtils.concat(gmtText, " ", zoneNameString); } public static List<Map<String, Object>> getZonesList(Context context) { diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java index 623d1a6716a7..0ea9d96bbe83 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java @@ -511,7 +511,10 @@ public class AccessPoint implements Comparable<AccessPoint> { } } - mSeen = seen; + // Only replace the previous value if we have a recent scan result to use + if (seen != 0) { + mSeen = seen; + } } /** @@ -940,8 +943,10 @@ public class AccessPoint implements Comparable<AccessPoint> { security = getSecurity(result); if (security == SECURITY_PSK) pskType = getPskType(result); - mRssi = result.level; - mSeen = result.timestamp; + + mScanResultCache.put(result.BSSID, result); + updateRssi(); + mSeen = result.timestamp; // even if the timestamp is old it is still valid } public void saveWifiState(Bundle savedState) { diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java index 40a59cf1ab82..fcc9090fc2f3 100644 --- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java +++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java @@ -142,6 +142,7 @@ public class WifiTracker { @VisibleForTesting Scanner mScanner; + private boolean mStaleScanResults = false; public WifiTracker(Context context, WifiListener wifiListener, boolean includeSaved, boolean includeScans) { @@ -330,7 +331,11 @@ public class WifiTracker { * Stop tracking wifi networks and scores. * * <p>This should always be called when done with a WifiTracker (if startTracking was called) to - * ensure proper cleanup and prevent any further callbacks from occuring. + * ensure proper cleanup and prevent any further callbacks from occurring. + * + * <p>Calling this method will set the {@link #mStaleScanResults} bit, which prevents + * {@link WifiListener#onAccessPointsChanged()} callbacks from being invoked (until the bit + * is unset on the next SCAN_RESULTS_AVAILABLE_ACTION). */ @MainThread public void stopTracking() { @@ -346,6 +351,7 @@ public class WifiTracker { mWorkHandler.removePendingMessages(); mMainHandler.removePendingMessages(); } + mStaleScanResults = true; } private void unregisterAndClearScoreCache() { @@ -712,6 +718,11 @@ public class WifiTracker { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); + + if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(action)) { + mStaleScanResults = false; + } + if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(action)) { updateWifiState(intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_UNKNOWN)); @@ -822,7 +833,9 @@ public class WifiTracker { switch (msg.what) { case MSG_UPDATE_ACCESS_POINTS: - updateAccessPointsLocked(); + if (!mStaleScanResults) { + updateAccessPointsLocked(); + } break; case MSG_UPDATE_NETWORK_INFO: updateNetworkInfo((NetworkInfo) msg.obj); diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/ZoneGetterTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/ZoneGetterTest.java index 703e9d29e6ac..a3345ee58b7d 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/ZoneGetterTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/utils/ZoneGetterTest.java @@ -47,9 +47,9 @@ public class ZoneGetterTest { } @Test - public void getTimeZoneOffsetAndName_setLondon_returnLondon() { - // Check it will ends with 'London', not 'British Summer Time' or sth else - testTimeZoneOffsetAndNameInner(TIME_ZONE_LONDON_ID, "London"); + public void getTimeZoneOffsetAndName_setLondon_returnBritishSummerTime() { + // Check it will ends with 'British Summer Time', not 'London' or sth else + testTimeZoneOffsetAndNameInner(TIME_ZONE_LONDON_ID, "British Summer Time"); } @Test diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java index c52643243063..eb9a7f6d0843 100644 --- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java +++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java @@ -28,6 +28,7 @@ import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoMoreInteractions; @@ -748,4 +749,36 @@ public class WifiTrackerTest { verifyNoMoreInteractions(mockWifiListener); } + + @Test + public void stopTrackingShouldSetStaleBitWhichPreventsCallbacksUntilNextScanResult() + throws Exception { + WifiTracker tracker = createMockedWifiTracker(); + startTracking(tracker); + tracker.stopTracking(); + + CountDownLatch latch1 = new CountDownLatch(1); + tracker.mMainHandler.post(() -> { + latch1.countDown(); + }); + assertTrue("Latch 1 timed out", latch1.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS)); + + startTracking(tracker); + + tracker.mReceiver.onReceive(mContext, new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION)); + tracker.mReceiver.onReceive( + mContext, new Intent(WifiManager.CONFIGURED_NETWORKS_CHANGED_ACTION)); + tracker.mReceiver.onReceive( + mContext, new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION)); + + CountDownLatch latch2 = new CountDownLatch(1); + tracker.mMainHandler.post(() -> { + latch2.countDown(); + }); + assertTrue("Latch 2 timed out", latch2.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS)); + + verify(mockWifiListener, never()).onAccessPointsChanged(); + + sendScanResultsAndProcess(tracker); // verifies onAccessPointsChanged is invoked + } } diff --git a/packages/SystemUI/res/layout/tv_pip_controls.xml b/packages/SystemUI/res/layout/tv_pip_controls.xml index 61ac6f6991ff..0b7bce13d761 100644 --- a/packages/SystemUI/res/layout/tv_pip_controls.xml +++ b/packages/SystemUI/res/layout/tv_pip_controls.xml @@ -22,24 +22,24 @@ <com.android.systemui.pip.tv.PipControlButtonView android:id="@+id/full_button" - android:layout_width="100dp" + android:layout_width="@dimen/picture_in_picture_button_width" android:layout_height="wrap_content" android:src="@drawable/ic_fullscreen_white_24dp" android:text="@string/pip_fullscreen" /> <com.android.systemui.pip.tv.PipControlButtonView android:id="@+id/close_button" - android:layout_width="100dp" + android:layout_width="@dimen/picture_in_picture_button_width" android:layout_height="wrap_content" - android:layout_marginStart="-50dp" + android:layout_marginStart="@dimen/picture_in_picture_button_start_margin" android:src="@drawable/ic_close_white" android:text="@string/pip_close" /> <com.android.systemui.pip.tv.PipControlButtonView android:id="@+id/play_pause_button" - android:layout_width="100dp" + android:layout_width="@dimen/picture_in_picture_button_width" android:layout_height="wrap_content" - android:layout_marginStart="-50dp" + android:layout_marginStart="@dimen/picture_in_picture_button_start_margin" android:src="@drawable/ic_pause_white" android:text="@string/pip_pause" android:visibility="gone" /> diff --git a/packages/SystemUI/res/layout/tv_pip_custom_control.xml b/packages/SystemUI/res/layout/tv_pip_custom_control.xml new file mode 100644 index 000000000000..dd0fce466576 --- /dev/null +++ b/packages/SystemUI/res/layout/tv_pip_custom_control.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 2017, 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. +*/ +--> +<com.android.systemui.pip.tv.PipControlButtonView + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="@dimen/picture_in_picture_button_width" + android:layout_height="wrap_content" + android:layout_marginStart="@dimen/picture_in_picture_button_start_margin" /> diff --git a/packages/SystemUI/res/values-tvdpi/dimens.xml b/packages/SystemUI/res/values-tvdpi/dimens.xml index 5327cee7cae8..4d978aacc65f 100644 --- a/packages/SystemUI/res/values-tvdpi/dimens.xml +++ b/packages/SystemUI/res/values-tvdpi/dimens.xml @@ -24,4 +24,8 @@ <fraction name="battery_subpixel_smoothing_right">10%</fraction> <dimen name="battery_margin_bottom">1px</dimen> + + <!-- The dimensions to user for picture-in-picture action buttons. --> + <dimen name="picture_in_picture_button_width">100dp</dimen> + <dimen name="picture_in_picture_button_start_margin">-50dp</dimen> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java index 0f69f471bc24..a5ee19824590 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java @@ -427,11 +427,7 @@ public class PipMenuActivity extends Activity { } else { actionsContainer.setVisibility(View.VISIBLE); if (mActionsGroup != null) { - // Hide extra views - for (int i = mActions.size(); i < mActionsGroup.getChildCount(); i++) { - mActionsGroup.getChildAt(i).setVisibility(View.GONE); - } - // Add needed views + // Ensure we have as many buttons as actions final LayoutInflater inflater = LayoutInflater.from(this); while (mActionsGroup.getChildCount() < mActions.size()) { final ImageView actionView = (ImageView) inflater.inflate( @@ -439,6 +435,13 @@ public class PipMenuActivity extends Activity { mActionsGroup.addView(actionView); } + // Update the visibility of all views + for (int i = 0; i < mActionsGroup.getChildCount(); i++) { + mActionsGroup.getChildAt(i).setVisibility(i < mActions.size() + ? View.VISIBLE + : View.GONE); + } + // Recreate the layout final boolean isLandscapePip = stackBounds != null && (stackBounds.width() > stackBounds.height()); @@ -460,10 +463,9 @@ public class PipMenuActivity extends Activity { Log.w(TAG, "Failed to send action", e); } }); - } else { - actionView.setAlpha(DISABLED_ACTION_ALPHA); } actionView.setEnabled(action.isEnabled()); + actionView.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA); // Update the margin between actions LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlButtonView.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlButtonView.java index 40a63d7d92c0..b21cd95626a7 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlButtonView.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlButtonView.java @@ -20,6 +20,7 @@ import android.animation.Animator; import android.animation.AnimatorInflater; import android.content.Context; import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; @@ -33,6 +34,7 @@ import com.android.systemui.R; * A view containing PIP controls including fullscreen, close, and media controls. */ public class PipControlButtonView extends RelativeLayout { + private OnFocusChangeListener mFocusChangeListener; private ImageView mIconImageView; ImageView mButtonImageView; @@ -122,18 +124,37 @@ public class PipControlButtonView extends RelativeLayout { } /** + * Sets the drawable for the button with the given drawable. + */ + public void setImageDrawable(Drawable d) { + mIconImageView.setImageDrawable(d); + } + + /** * Sets the drawable for the button with the given resource id. */ public void setImageResource(int resId) { - mIconImageView.setImageResource(resId); + if (resId != 0) { + mIconImageView.setImageResource(resId); + } + } + + /** + * Sets the text for description the with the given string. + */ + public void setText(CharSequence text) { + mButtonImageView.setContentDescription(text); + mDescriptionTextView.setText(text); } /** * Sets the text for description the with the given resource id. */ public void setText(int resId) { - mButtonImageView.setContentDescription(getContext().getString(resId)); - mDescriptionTextView.setText(resId); + if (resId != 0) { + mButtonImageView.setContentDescription(getContext().getString(resId)); + mDescriptionTextView.setText(resId); + } } private static void cancelAnimator(Animator animator) { diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java index acea3b6b12ad..10206d492e3a 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipControlsView.java @@ -16,12 +16,20 @@ package com.android.systemui.pip.tv; +import android.app.ActivityManager; +import android.app.PendingIntent.CanceledException; +import android.app.RemoteAction; import android.content.Context; +import android.graphics.Color; import android.media.session.MediaController; import android.media.session.PlaybackState; +import android.os.Handler; +import android.os.RemoteException; +import android.util.Log; import android.view.View; import android.view.Gravity; import android.view.LayoutInflater; +import android.widget.ImageView; import android.widget.LinearLayout; import android.util.AttributeSet; @@ -30,11 +38,19 @@ import com.android.systemui.R; import static android.media.session.PlaybackState.ACTION_PAUSE; import static android.media.session.PlaybackState.ACTION_PLAY; +import java.util.ArrayList; +import java.util.List; + /** * A view containing PIP controls including fullscreen, close, and media controls. */ public class PipControlsView extends LinearLayout { + + private static final String TAG = PipControlsView.class.getSimpleName(); + + private static final float DISABLED_ACTION_ALPHA = 0.54f; + /** * An interface to listen user action. */ @@ -47,19 +63,23 @@ public class PipControlsView extends LinearLayout { private MediaController mMediaController; - final PipManager mPipManager = PipManager.getInstance(); - Listener mListener; + private final PipManager mPipManager = PipManager.getInstance(); + private final LayoutInflater mLayoutInflater; + private final Handler mHandler; + private Listener mListener; private PipControlButtonView mFullButtonView; private PipControlButtonView mCloseButtonView; private PipControlButtonView mPlayPauseButtonView; + private ArrayList<PipControlButtonView> mCustomButtonViews = new ArrayList<>(); + private List<RemoteAction> mCustomActions = new ArrayList<>(); private PipControlButtonView mFocusedChild; private MediaController.Callback mMediaControllerCallback = new MediaController.Callback() { @Override public void onPlaybackStateChanged(PlaybackState state) { - updatePlayPauseView(); + updateUserActions(); } }; @@ -95,9 +115,10 @@ public class PipControlsView extends LinearLayout { public PipControlsView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); - LayoutInflater inflater = (LayoutInflater) getContext() + mLayoutInflater = (LayoutInflater) getContext() .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - inflater.inflate(R.layout.tv_pip_controls, this); + mLayoutInflater.inflate(R.layout.tv_pip_controls, this); + mHandler = new Handler(); setOrientation(LinearLayout.HORIZONTAL); setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL); @@ -176,21 +197,74 @@ public class PipControlsView extends LinearLayout { if (mMediaController != null) { mMediaController.registerCallback(mMediaControllerCallback); } - updatePlayPauseView(); + updateUserActions(); } - private void updatePlayPauseView() { - int state = mPipManager.getPlaybackState(); - if (state == PipManager.PLAYBACK_STATE_UNAVAILABLE) { + /** + * Updates the actions for the PIP. If there are no custom actions, then the media session + * actions are shown. + */ + private void updateUserActions() { + if (!mCustomActions.isEmpty()) { + // Ensure we have as many buttons as actions + while (mCustomButtonViews.size() < mCustomActions.size()) { + PipControlButtonView buttonView = (PipControlButtonView) mLayoutInflater.inflate( + R.layout.tv_pip_custom_control, this, false); + addView(buttonView); + mCustomButtonViews.add(buttonView); + } + + // Update the visibility of all views + for (int i = 0; i < mCustomButtonViews.size(); i++) { + mCustomButtonViews.get(i).setVisibility(i < mCustomActions.size() + ? View.VISIBLE + : View.GONE); + } + + // Update the state and visibility of the action buttons, and hide the rest + for (int i = 0; i < mCustomActions.size(); i++) { + final RemoteAction action = mCustomActions.get(i); + PipControlButtonView actionView = mCustomButtonViews.get(i); + + // TODO: Check if the action drawable has changed before we reload it + action.getIcon().loadDrawableAsync(getContext(), d -> { + d.setTint(Color.WHITE); + actionView.setImageDrawable(d); + }, mHandler); + actionView.setText(action.getContentDescription()); + if (action.isEnabled()) { + actionView.setOnClickListener(v -> { + try { + action.getActionIntent().send(); + } catch (CanceledException e) { + Log.w(TAG, "Failed to send action", e); + } + }); + } + actionView.setEnabled(action.isEnabled()); + actionView.setAlpha(action.isEnabled() ? 1f : DISABLED_ACTION_ALPHA); + } + + // Hide the media session buttons mPlayPauseButtonView.setVisibility(View.GONE); } else { - mPlayPauseButtonView.setVisibility(View.VISIBLE); - if (state == PipManager.PLAYBACK_STATE_PLAYING) { - mPlayPauseButtonView.setImageResource(R.drawable.ic_pause_white); - mPlayPauseButtonView.setText(R.string.pip_pause); + int state = mPipManager.getPlaybackState(); + if (state == PipManager.PLAYBACK_STATE_UNAVAILABLE) { + mPlayPauseButtonView.setVisibility(View.GONE); } else { - mPlayPauseButtonView.setImageResource(R.drawable.ic_play_arrow_white); - mPlayPauseButtonView.setText(R.string.pip_play); + mPlayPauseButtonView.setVisibility(View.VISIBLE); + if (state == PipManager.PLAYBACK_STATE_PLAYING) { + mPlayPauseButtonView.setImageResource(R.drawable.ic_pause_white); + mPlayPauseButtonView.setText(R.string.pip_pause); + } else { + mPlayPauseButtonView.setImageResource(R.drawable.ic_play_arrow_white); + mPlayPauseButtonView.setText(R.string.pip_play); + } + } + + // Hide all the custom action buttons + for (int i = 0; i < mCustomButtonViews.size(); i++) { + mCustomButtonViews.get(i).setVisibility(View.GONE); } } } @@ -203,6 +277,9 @@ public class PipControlsView extends LinearLayout { mCloseButtonView.reset(); mPlayPauseButtonView.reset(); mFullButtonView.requestFocus(); + for (int i = 0; i < mCustomButtonViews.size(); i++) { + mCustomButtonViews.get(i).reset(); + } } /** @@ -213,6 +290,15 @@ public class PipControlsView extends LinearLayout { } /** + * Updates the set of activity-defined actions. + */ + public void setActions(List<RemoteAction> actions) { + mCustomActions.clear(); + mCustomActions.addAll(actions); + updateUserActions(); + } + + /** * Returns the focused control button view to animate focused button. */ PipControlButtonView getFocusedButton() { diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java index f98310dd7aa1..ca58080c0260 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipManager.java @@ -20,6 +20,7 @@ import android.app.ActivityManager; import android.app.ActivityManager.RunningTaskInfo; import android.app.ActivityManager.StackInfo; import android.app.IActivityManager; +import android.app.RemoteAction; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.Context; @@ -124,6 +125,7 @@ public class PipManager implements BasePipManager { private MediaController mPipMediaController; private String[] mLastPackagesResourceGranted; private PipNotification mPipNotification; + private ParceledListSlice mCustomActions; private final PinnedStackListener mPinnedStackListener = new PinnedStackListener(); @@ -187,7 +189,14 @@ public class PipManager implements BasePipManager { } @Override - public void onActionsChanged(ParceledListSlice actions) {} + public void onActionsChanged(ParceledListSlice actions) { + mCustomActions = actions; + mHandler.post(() -> { + for (int i = mListeners.size() - 1; i >= 0; --i) { + mListeners.get(i).onPipMenuActionsChanged(mCustomActions); + } + }); + } } private PipManager() { } @@ -432,6 +441,7 @@ public class PipManager implements BasePipManager { } Intent intent = new Intent(mContext, PipMenuActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.putExtra(PipMenuActivity.EXTRA_CUSTOM_ACTIONS, mCustomActions); mContext.startActivity(intent); } @@ -690,6 +700,8 @@ public class PipManager implements BasePipManager { void onPipActivityClosed(); /** Invoked when the PIP menu gets shown. */ void onShowPipMenu(); + /** Invoked when the PIP menu actions change. */ + void onPipMenuActionsChanged(ParceledListSlice actions); /** Invoked when the PIPed activity is about to return back to the fullscreen. */ void onMoveToFullscreen(); /** Invoked when we are above to start resizing the Pip. */ diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java index ce1bea19ef60..82018ce9ddbe 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipMenuActivity.java @@ -19,22 +19,27 @@ package com.android.systemui.pip.tv; import android.animation.Animator; import android.animation.AnimatorInflater; import android.app.Activity; +import android.content.Intent; +import android.content.pm.ParceledListSlice; import android.os.Bundle; import android.view.View; import com.android.systemui.R; +import java.util.Collections; /** * Activity to show the PIP menu to control PIP. */ public class PipMenuActivity extends Activity implements PipManager.Listener { private static final String TAG = "PipMenuActivity"; + static final String EXTRA_CUSTOM_ACTIONS = "custom_actions"; + private final PipManager mPipManager = PipManager.getInstance(); private Animator mFadeInAnimation; private Animator mFadeOutAnimation; - private View mPipControlsView; + private PipControlsView mPipControlsView; private boolean mRestorePipSizeWhenClose; @Override @@ -51,6 +56,15 @@ public class PipMenuActivity extends Activity implements PipManager.Listener { mFadeOutAnimation = AnimatorInflater.loadAnimator( this, R.anim.tv_pip_menu_fade_out_animation); mFadeOutAnimation.setTarget(mPipControlsView); + + onPipMenuActionsChanged(getIntent().getParcelableExtra(EXTRA_CUSTOM_ACTIONS)); + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + + onPipMenuActionsChanged(getIntent().getParcelableExtra(EXTRA_CUSTOM_ACTIONS)); } private void restorePipAndFinish() { @@ -96,6 +110,12 @@ public class PipMenuActivity extends Activity implements PipManager.Listener { } @Override + public void onPipMenuActionsChanged(ParceledListSlice actions) { + boolean hasCustomActions = actions != null && !actions.getList().isEmpty(); + mPipControlsView.setActions(hasCustomActions ? actions.getList() : Collections.EMPTY_LIST); + } + + @Override public void onShowPipMenu() { } @Override diff --git a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java index c8f418554904..f0745a0791ef 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java +++ b/packages/SystemUI/src/com/android/systemui/pip/tv/PipNotification.java @@ -23,6 +23,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ParceledListSlice; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.drawable.Icon; @@ -81,6 +82,11 @@ public class PipNotification { } @Override + public void onPipMenuActionsChanged(ParceledListSlice actions) { + // no-op. + } + + @Override public void onMoveToFullscreen() { dismissPipNotification(); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java b/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java index eaf715ffc4c9..5b3ec08ce752 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/CellTileView.java @@ -35,9 +35,7 @@ public class CellTileView extends SignalTileView { public CellTileView(Context context) { super(context); mSignalDrawable = new SignalDrawable(mContext); - float dark = Utils.getColorAttr(context, android.R.attr.colorForeground) == 0xff000000 - ? 1 : 0; - mSignalDrawable.setDarkIntensity(dark); + mSignalDrawable.setDarkIntensity(isDark(mContext)); mSignalDrawable.setIntrinsicSize(context.getResources().getDimensionPixelSize( R.dimen.qs_tile_icon_size)); } @@ -50,6 +48,10 @@ public class CellTileView extends SignalTileView { } } + private static int isDark(Context context) { + return Utils.getColorAttr(context, android.R.attr.colorForeground) == 0xff000000 ? 1 : 0; + } + public static class SignalIcon extends Icon { private final int mState; @@ -64,7 +66,11 @@ public class CellTileView extends SignalTileView { @Override public Drawable getDrawable(Context context) { - return null; + //TODO: Not the optimal solution to create this drawable + SignalDrawable d = new SignalDrawable(context); + d.setDarkIntensity(isDark(context)); + d.setLevel(getState()); + return d; } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java index bd59fb03d59a..82e6a3562c8d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/HeadsUpTouchHelper.java @@ -106,6 +106,7 @@ public class HeadsUpTouchHelper implements Gefingerpoken { mPanel.setPanelScrimMinFraction((float) expandedHeight / mPanel.getMaxPanelHeight()); mPanel.startExpandMotion(x, y, true /* startTracking */, expandedHeight); + mPanel.startExpandingFromPeek(); // This call needs to be after the expansion start otherwise we will get a // flicker of one frame as it's not expanded yet. mHeadsUpManager.unpinAll(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java index b1d82b1198a3..0b46c2111c88 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java @@ -406,6 +406,10 @@ public abstract class PanelView extends FrameLayout { return Math.abs(yDiff) >= Math.abs(xDiff); } + protected void startExpandingFromPeek() { + mStatusBar.handlePeekToExpandTransistion(); + } + protected void startExpandMotion(float newX, float newY, boolean startTracking, float expandedHeight) { mInitialOffsetOnTouch = expandedHeight; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index cfbdc9241a98..f58fe8290a43 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -648,7 +648,7 @@ public class StatusBar extends SystemUI implements DemoMode, // Tracks notifications currently visible in mNotificationStackScroller and // emits visibility events via NoMan on changes. - private final Runnable mVisibilityReporter = new Runnable() { + protected final Runnable mVisibilityReporter = new Runnable() { private final ArraySet<NotificationVisibility> mTmpNewlyVisibleNotifications = new ArraySet<>(); private final ArraySet<NotificationVisibility> mTmpCurrentlyVisibleNotifications = @@ -3755,6 +3755,17 @@ public class StatusBar extends SystemUI implements DemoMode, } } + void handlePeekToExpandTransistion() { + try { + // consider the transition from peek to expanded to be a panel open, + // but not one that clears notification effects. + int notificationLoad = mNotificationData.getActiveNotifications().size(); + mBarService.onPanelRevealed(false, notificationLoad); + } catch (RemoteException ex) { + // Won't fail unless the world has ended. + } + } + /** * The LEDs are turned off when the notification panel is shown, even just a little bit. * See also StatusBar.setPanelExpanded for another place where we attempt to do this. @@ -3770,8 +3781,6 @@ public class StatusBar extends SystemUI implements DemoMode, int notificationLoad = mNotificationData.getActiveNotifications().size(); if (pinnedHeadsUp && isPanelFullyCollapsed()) { notificationLoad = 1; - } else { - mMetricsLogger.histogram("note_load", notificationLoad); } mBarService.onPanelRevealed(clearNotificationEffects, notificationLoad); } else { @@ -5041,6 +5050,12 @@ public class StatusBar extends SystemUI implements DemoMode, mDozing = mDozingRequested && mState == StatusBarState.KEYGUARD || mFingerprintUnlockController.getMode() == FingerprintUnlockController.MODE_WAKE_AND_UNLOCK_PULSING; + // When in wake-and-unlock we may not have received a change to mState + // but we still should not be dozing, manually set to false. + if (mFingerprintUnlockController.getMode() == + FingerprintUnlockController.MODE_WAKE_AND_UNLOCK) { + mDozing = false; + } mStatusBarWindowManager.setDozing(mDozing); updateDozingState(); Trace.endSection(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index db6647c10aca..0e3ea7a07e7d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -20,37 +20,54 @@ import static android.app.NotificationManager.IMPORTANCE_HIGH; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; +import static junit.framework.TestCase.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verify; import android.app.Notification; import android.metrics.LogMaker; import android.os.Handler; import android.os.HandlerThread; import android.os.IPowerManager; -import android.os.Looper; +import android.os.Message; import android.os.PowerManager; +import android.os.RemoteException; import android.os.UserHandle; import android.service.notification.StatusBarNotification; import android.support.test.filters.SmallTest; import android.support.test.metricshelper.MetricsAsserts; import android.support.test.runner.AndroidJUnit4; +import android.testing.AndroidTestingRunner; +import android.testing.TestableLooper; +import android.testing.TestableLooper.MessageHandler; +import android.testing.TestableLooper.RunWithLooper; import android.util.DisplayMetrics; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.logging.testing.FakeMetricsLogger; +import com.android.internal.statusbar.IStatusBarService; import com.android.keyguard.KeyguardHostView.OnDismissAction; import com.android.systemui.SysuiTestCase; import com.android.systemui.recents.misc.SystemServicesProxy; import com.android.systemui.statusbar.ActivatableNotificationView; import com.android.systemui.statusbar.KeyguardIndicationController; import com.android.systemui.statusbar.NotificationData; +import com.android.systemui.statusbar.NotificationData.Entry; +import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.stack.NotificationStackScrollLayout; @@ -58,21 +75,26 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.ArrayList; + @SmallTest -@RunWith(AndroidJUnit4.class) +@RunWith(AndroidTestingRunner.class) +@RunWithLooper public class StatusBarTest extends SysuiTestCase { StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; UnlockMethodCache mUnlockMethodCache; KeyguardIndicationController mKeyguardIndicationController; NotificationStackScrollLayout mStackScroller; - StatusBar mStatusBar; + TestableStatusBar mStatusBar; FakeMetricsLogger mMetricsLogger; HeadsUpManager mHeadsUpManager; NotificationData mNotificationData; PowerManager mPowerManager; SystemServicesProxy mSystemServicesProxy; NotificationPanelView mNotificationPanelView; + IStatusBarService mBarService; + ArrayList<Entry> mNotificationList; private DisplayMetrics mDisplayMetrics = new DisplayMetrics(); @Before @@ -86,18 +108,20 @@ public class StatusBarTest extends SysuiTestCase { mNotificationData = mock(NotificationData.class); mSystemServicesProxy = mock(SystemServicesProxy.class); mNotificationPanelView = mock(NotificationPanelView.class); + mNotificationList = mock(ArrayList.class); IPowerManager powerManagerService = mock(IPowerManager.class); HandlerThread handlerThread = new HandlerThread("TestThread"); handlerThread.start(); mPowerManager = new PowerManager(mContext, powerManagerService, new Handler(handlerThread.getLooper())); when(powerManagerService.isInteractive()).thenReturn(true); + mBarService = mock(IStatusBarService.class); mDependency.injectTestDependency(MetricsLogger.class, mMetricsLogger); mStatusBar = new TestableStatusBar(mStatusBarKeyguardViewManager, mUnlockMethodCache, mKeyguardIndicationController, mStackScroller, mHeadsUpManager, - mNotificationData, mPowerManager, mSystemServicesProxy, mNotificationPanelView); - + mNotificationData, mPowerManager, mSystemServicesProxy, mNotificationPanelView, + mBarService); doAnswer(invocation -> { OnDismissAction onDismissAction = (OnDismissAction) invocation.getArguments()[0]; onDismissAction.onDismiss(); @@ -111,6 +135,15 @@ public class StatusBarTest extends SysuiTestCase { }).when(mStatusBarKeyguardViewManager).addAfterKeyguardGoneRunnable(any()); when(mStackScroller.getActivatedChild()).thenReturn(null); + TestableLooper.get(this).setMessageHandler(new MessageHandler() { + @Override + public boolean onMessageHandled(Message m) { + if (m.getCallback() == mStatusBar.mVisibilityReporter) { + return false; + } + return true; + } + }); } @Test @@ -284,11 +317,80 @@ public class StatusBarTest extends SysuiTestCase { assertFalse(mStatusBar.shouldPeek(entry, sbn)); } + @Test + public void testLogHidden() { + try { + mStatusBar.handleVisibleToUserChanged(false); + verify(mBarService, times(1)).onPanelHidden(); + verify(mBarService, never()).onPanelRevealed(anyBoolean(), anyInt()); + } catch (RemoteException e) { + fail(); + } + } + + @Test + public void testPanelOpenForPeek() { + when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(true); + when(mNotificationData.getActiveNotifications()).thenReturn(mNotificationList); + when(mNotificationList.size()).thenReturn(5); + when(mNotificationPanelView.isFullyCollapsed()).thenReturn(true); + mStatusBar.setBarStateForTest(StatusBarState.SHADE); + + try { + mStatusBar.handleVisibleToUserChanged(true); + + verify(mBarService, never()).onPanelHidden(); + verify(mBarService, times(1)).onPanelRevealed(false, 1); + } catch (RemoteException e) { + fail(); + } + TestableLooper.get(this).processAllMessages(); + } + + @Test + public void testPanelOpenAndClear() { + when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false); + when(mNotificationData.getActiveNotifications()).thenReturn(mNotificationList); + when(mNotificationList.size()).thenReturn(5); + when(mNotificationPanelView.isFullyCollapsed()).thenReturn(false); + mStatusBar.setBarStateForTest(StatusBarState.SHADE); + + try { + mStatusBar.handleVisibleToUserChanged(true); + + verify(mBarService, never()).onPanelHidden(); + verify(mBarService, times(1)).onPanelRevealed(true, 5); + } catch (RemoteException e) { + fail(); + } + TestableLooper.get(this).processAllMessages(); + } + + @Test + public void testPanelOpenAndNoClear() { + when(mHeadsUpManager.hasPinnedHeadsUp()).thenReturn(false); + when(mNotificationData.getActiveNotifications()).thenReturn(mNotificationList); + when(mNotificationList.size()).thenReturn(5); + when(mNotificationPanelView.isFullyCollapsed()).thenReturn(false); + mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD); + + try { + mStatusBar.handleVisibleToUserChanged(true); + + verify(mBarService, never()).onPanelHidden(); + verify(mBarService, times(1)).onPanelRevealed(false, 5); + } catch (RemoteException e) { + fail(); + } + TestableLooper.get(this).processAllMessages(); + } + static class TestableStatusBar extends StatusBar { public TestableStatusBar(StatusBarKeyguardViewManager man, UnlockMethodCache unlock, KeyguardIndicationController key, NotificationStackScrollLayout stack, HeadsUpManager hum, NotificationData nd, - PowerManager pm, SystemServicesProxy ssp, NotificationPanelView panelView) { + PowerManager pm, SystemServicesProxy ssp, NotificationPanelView panelView, + IStatusBarService barService) { mStatusBarKeyguardViewManager = man; mUnlockMethodCache = unlock; mKeyguardIndicationController = key; @@ -299,11 +401,11 @@ public class StatusBarTest extends SysuiTestCase { mPowerManager = pm; mSystemServicesProxy = ssp; mNotificationPanel = panelView; + mBarService = barService; } - @Override - protected H createHandler() { - return null; + public void setBarStateForTest(int state) { + mState = state; } } }
\ No newline at end of file diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index fa78f10f3e85..7ddc1a226194 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -243,6 +243,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { private WindowsForAccessibilityCallback mWindowsForAccessibilityCallback; + private boolean mIsAccessibilityButtonShown; + private UserState getCurrentUserStateLocked() { return getUserStateLocked(mCurrentUserId); } @@ -881,21 +883,21 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } /** - * Invoked remotely over AIDL by SysUi when the availability of the accessibility + * Invoked remotely over AIDL by SysUi when the visibility of the accessibility * button within the system's navigation area has changed. * - * @param available {@code true} if the accessibility button is available to the + * @param shown {@code true} if the accessibility button is shown to the * user, {@code false} otherwise */ @Override - public void notifyAccessibilityButtonAvailabilityChanged(boolean available) { + public void notifyAccessibilityButtonVisibilityChanged(boolean shown) { if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.STATUS_BAR_SERVICE) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException("Caller does not hold permission " + android.Manifest.permission.STATUS_BAR_SERVICE); } synchronized (mLock) { - notifyAccessibilityButtonAvailabilityChangedLocked(available); + notifyAccessibilityButtonVisibilityChangedLocked(shown); } } @@ -1200,13 +1202,14 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } } - private void notifyAccessibilityButtonAvailabilityChangedLocked(boolean available) { + private void notifyAccessibilityButtonVisibilityChangedLocked(boolean available) { final UserState state = getCurrentUserStateLocked(); - state.mIsAccessibilityButtonAvailable = available; + mIsAccessibilityButtonShown = available; for (int i = state.mBoundServices.size() - 1; i >= 0; i--) { final Service service = state.mBoundServices.get(i); if (service.mRequestAccessibilityButton) { - service.notifyAccessibilityButtonAvailabilityChangedLocked(available); + service.notifyAccessibilityButtonAvailabilityChangedLocked( + service.isAccessibilityButtonAvailableLocked(state)); } } } @@ -1733,7 +1736,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { scheduleUpdateInputFilter(userState); scheduleUpdateClientsIfNeededLocked(userState); updateRelevantEventsLocked(userState); - updateAccessibilityButtonTargets(userState); + updateAccessibilityButtonTargetsLocked(userState); } private void updateAccessibilityFocusBehaviorLocked(UserState userState) { @@ -2183,18 +2186,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } } - private void updateAccessibilityButtonTargets(UserState userState) { - final List<Service> services; - synchronized (mLock) { - services = userState.mBoundServices; - int numServices = services.size(); - for (int i = 0; i < numServices; i++) { - final Service service = services.get(i); - if (service.mRequestAccessibilityButton) { - boolean available = service.mComponentName.equals( - userState.mServiceAssignedToAccessibilityButton); - service.notifyAccessibilityButtonAvailabilityChangedLocked(available); - } + private void updateAccessibilityButtonTargetsLocked(UserState userState) { + for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) { + final Service service = userState.mBoundServices.get(i); + if (service.mRequestAccessibilityButton) { + service.notifyAccessibilityButtonAvailabilityChangedLocked( + service.isAccessibilityButtonAvailableLocked(userState)); } } } @@ -2501,7 +2498,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { case MSG_SHOW_ACCESSIBILITY_BUTTON_CHOOSER: { showAccessibilityButtonTargetSelection(); - } + } break; } } @@ -2656,6 +2653,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { boolean mRequestAccessibilityButton; + boolean mReceivedAccessibilityButtonCallbackSinceBind; + + boolean mLastAccessibilityButtonCallbackState; + int mFetchFlags; long mNotificationTimeout; @@ -3596,9 +3597,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { return false; } userState = getCurrentUserStateLocked(); + return isAccessibilityButtonAvailableLocked(userState); } - - return mRequestAccessibilityButton && userState.mIsAccessibilityButtonAvailable; } @Override @@ -3656,6 +3656,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { mService = null; } mServiceInterface = null; + mReceivedAccessibilityButtonCallbackSinceBind = false; } public boolean isConnectedLocked() { @@ -3728,6 +3729,48 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } } + private boolean isAccessibilityButtonAvailableLocked(UserState userState) { + // If the service does not request the accessibility button, it isn't available + if (!mRequestAccessibilityButton) { + return false; + } + + // If the accessibility button isn't currently shown, it cannot be available to services + if (!mIsAccessibilityButtonShown) { + return false; + } + + // If magnification is on and assigned to the accessibility button, services cannot be + if (userState.mIsNavBarMagnificationEnabled + && userState.mIsNavBarMagnificationAssignedToAccessibilityButton) { + return false; + } + + int requestingServices = 0; + for (int i = userState.mBoundServices.size() - 1; i >= 0; i--) { + final Service service = userState.mBoundServices.get(i); + if (service.mRequestAccessibilityButton) { + requestingServices++; + } + } + + if (requestingServices == 1) { + // If only a single service is requesting, it must be this service, and the + // accessibility button is available to it + return true; + } else { + // With more than one active service, we derive the target from the user's settings + if (userState.mServiceAssignedToAccessibilityButton == null) { + // If the user has not made an assignment, we treat the button as available to + // all services until the user interacts with the button to make an assignment + return true; + } else { + // If an assignment was made, it defines availability + return mComponentName.equals(userState.mServiceAssignedToAccessibilityButton); + } + } + } + /** * Notifies an accessibility service client for a scheduled event given the event type. * @@ -3875,6 +3918,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { } private void notifyAccessibilityButtonAvailabilityChangedInternal(boolean available) { + // Only notify the service if it's not been notified or the state has changed + if (mReceivedAccessibilityButtonCallbackSinceBind + && (mLastAccessibilityButtonCallbackState == available)) { + return; + } + mReceivedAccessibilityButtonCallbackSinceBind = true; + mLastAccessibilityButtonCallbackState = available; final IAccessibilityServiceClient listener; synchronized (mLock) { listener = mServiceInterface; @@ -4874,7 +4924,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { public int mSoftKeyboardShowMode = 0; - public boolean mIsAccessibilityButtonAvailable; public boolean mIsNavBarMagnificationAssignedToAccessibilityButton; public ComponentName mServiceAssignedToAccessibilityButton; @@ -4954,9 +5003,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub { mIsNavBarMagnificationAssignedToAccessibilityButton = false; mIsAutoclickEnabled = false; mSoftKeyboardShowMode = 0; - - // Clear state tracked from system UI - mIsAccessibilityButtonAvailable = false; } public void destroyUiAutomationService() { diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 073d7b20ed64..3ae05115217a 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -255,7 +255,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final ViewNode node = nodes[i]; if (node == null) { - Slog.w(TAG, "fillStructureWithAllowedValues(): no node for " + viewState.id); + if (sVerbose) { + Slog.v(TAG, "fillStructureWithAllowedValues(): no node for " + viewState.id); + } continue; } @@ -862,11 +864,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final int numContexts = mContexts.size(); for (int i = 0; i < numContexts; i++) { final FillContext context = mContexts.get(i); - // TODO: create a function that gets just one node so it doesn't create an array - // unnecessarily - final ViewNode[] nodes = context.findViewNodesByAutofillIds(id); - if (nodes != null) { - AutofillValue candidate = nodes[0].getAutofillValue(); + final ViewNode node = context.findViewNodeByAutofillId(id); + if (node != null) { + final AutofillValue candidate = node.getAutofillValue(); if (sDebug) { Slog.d(TAG, "getValueFromContexts(" + id + ") at " + i + ": " + candidate); } diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index 73f1705e35df..4810f4fe8c82 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -47,6 +47,7 @@ import android.os.IBinder; import android.os.IDeviceIdleController; import android.os.IInterface; import android.os.Parcel; +import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; @@ -345,7 +346,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind } private static boolean isCallerSystem() { - return getCallingUserId() == UserHandle.USER_SYSTEM; + return Binder.getCallingUid() == Process.SYSTEM_UID; } private ServiceConnection createServiceConnection( diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index c41748424b10..756e2748b8cb 100644 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -81,6 +81,7 @@ import android.os.RemoteException; import android.os.SystemClock; import android.os.UserHandle; import android.util.EventLog; +import android.util.PrintWriterPrinter; import android.util.Slog; import android.util.SparseArray; import android.util.TimeUtils; @@ -175,6 +176,9 @@ public final class ActiveServices { long mStartVisibleTime; long mEndTime; int mNumActive; + + // Temp output of foregroundAppShownEnoughLocked + long mHideTime; } /** @@ -622,19 +626,19 @@ public final class ActiveServices { != ActivityManager.APP_START_MODE_NORMAL) { if (stopping == null) { stopping = new ArrayList<>(); - String compName = service.name.flattenToShortString(); - EventLogTags.writeAmStopIdleService(service.appInfo.uid, compName); - StringBuilder sb = new StringBuilder(64); - sb.append("Stopping service due to app idle: "); - UserHandle.formatUid(sb, service.appInfo.uid); - sb.append(" "); - TimeUtils.formatDuration(service.createTime - - SystemClock.elapsedRealtime(), sb); - sb.append(" "); - sb.append(compName); - Slog.w(TAG, sb.toString()); - stopping.add(service); } + String compName = service.name.flattenToShortString(); + EventLogTags.writeAmStopIdleService(service.appInfo.uid, compName); + StringBuilder sb = new StringBuilder(64); + sb.append("Stopping service due to app idle: "); + UserHandle.formatUid(sb, service.appInfo.uid); + sb.append(" "); + TimeUtils.formatDuration(service.createTime + - SystemClock.elapsedRealtime(), sb); + sb.append(" "); + sb.append(compName); + Slog.w(TAG, sb.toString()); + stopping.add(service); } } } @@ -736,50 +740,90 @@ public final class ActiveServices { } } + boolean foregroundAppShownEnoughLocked(ActiveForegroundApp aa, long nowElapsed) { + if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "Shown enough: pkg=" + aa.mPackageName + ", uid=" + + aa.mUid); + boolean canRemove = false; + aa.mHideTime = Long.MAX_VALUE; + if (aa.mShownWhileTop) { + // If the app was ever at the top of the screen while the foreground + // service was running, then we can always just immediately remove it. + canRemove = true; + if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "YES - shown while on top"); + } else if (mScreenOn || aa.mShownWhileScreenOn) { + final long minTime = aa.mStartVisibleTime + + (aa.mStartTime != aa.mStartVisibleTime + ? mAm.mConstants.FGSERVICE_SCREEN_ON_AFTER_TIME + : mAm.mConstants.FGSERVICE_MIN_SHOWN_TIME); + if (nowElapsed >= minTime) { + // If shown while the screen is on, and it has been shown for + // at least the minimum show time, then we can now remove it. + if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "YES - shown long enough with screen on"); + canRemove = true; + } else { + // This is when we will be okay to stop telling the user. + long reportTime = nowElapsed + mAm.mConstants.FGSERVICE_MIN_REPORT_TIME; + aa.mHideTime = reportTime > minTime ? reportTime : minTime; + if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "NO -- wait " + (aa.mHideTime-nowElapsed) + + " with screen on"); + } + } else { + final long minTime = aa.mEndTime + + mAm.mConstants.FGSERVICE_SCREEN_ON_BEFORE_TIME; + if (nowElapsed >= minTime) { + // If the foreground service has only run while the screen is + // off, but it has been gone now for long enough that we won't + // care to tell the user about it when the screen comes back on, + // then we can remove it now. + if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "YES - gone long enough with screen off"); + canRemove = true; + } else { + // This is when we won't care about this old fg service. + aa.mHideTime = minTime; + if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "NO -- wait " + (aa.mHideTime-nowElapsed) + + " with screen off"); + } + } + return canRemove; + } + void updateForegroundApps(ServiceMap smap) { // This is called from the handler without the lock held. ArrayList<ActiveForegroundApp> active = null; synchronized (mAm) { final long now = SystemClock.elapsedRealtime(); - final long nowPlusMin = now + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME; long nextUpdateTime = Long.MAX_VALUE; if (smap != null) { + if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "Updating foreground apps for user " + + smap.mUserId); for (int i = smap.mActiveForegroundApps.size()-1; i >= 0; i--) { ActiveForegroundApp aa = smap.mActiveForegroundApps.valueAt(i); - if (aa.mEndTime != 0 && (mScreenOn || aa.mShownWhileScreenOn)) { - if (!aa.mShownWhileTop && aa.mEndTime < (aa.mStartVisibleTime - + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME)) { - // Check to see if this should still be displayed... we continue - // until it has been shown for at least the timeout duration. - if (nowPlusMin >= aa.mStartVisibleTime) { - // All over! - smap.mActiveForegroundApps.removeAt(i); - smap.mActiveForegroundAppsChanged = true; - continue; - } else { - long hideTime = aa.mStartVisibleTime - + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME; - if (hideTime < nextUpdateTime) { - nextUpdateTime = hideTime; - } - } - } else { + if (aa.mEndTime != 0) { + boolean canRemove = foregroundAppShownEnoughLocked(aa, now); + if (canRemove) { // This was up for longer than the timeout, so just remove immediately. smap.mActiveForegroundApps.removeAt(i); smap.mActiveForegroundAppsChanged = true; continue; } + if (aa.mHideTime < nextUpdateTime) { + nextUpdateTime = aa.mHideTime; + } } if (!aa.mAppOnTop) { if (active == null) { active = new ArrayList<>(); } + if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "Adding active: pkg=" + + aa.mPackageName + ", uid=" + aa.mUid); active.add(aa); } } smap.removeMessages(ServiceMap.MSG_UPDATE_FOREGROUND_APPS); if (nextUpdateTime < Long.MAX_VALUE) { - Message msg = smap.obtainMessage(); + if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "Next update time in: " + + (nextUpdateTime-now)); + Message msg = smap.obtainMessage(ServiceMap.MSG_UPDATE_FOREGROUND_APPS); smap.sendMessageAtTime(msg, nextUpdateTime + SystemClock.uptimeMillis() - SystemClock.elapsedRealtime()); } @@ -882,15 +926,14 @@ public final class ActiveServices { active.mNumActive--; if (active.mNumActive <= 0) { active.mEndTime = SystemClock.elapsedRealtime(); - if (active.mShownWhileTop || active.mEndTime >= (active.mStartVisibleTime - + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME)) { + if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "Ended running of service"); + if (foregroundAppShownEnoughLocked(active, active.mEndTime)) { // Have been active for long enough that we will remove it immediately. smap.mActiveForegroundApps.remove(r.packageName); smap.mActiveForegroundAppsChanged = true; requestUpdateActiveForegroundAppsLocked(smap, 0); - } else { - requestUpdateActiveForegroundAppsLocked(smap, active.mStartVisibleTime - + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME); + } else if (active.mHideTime < Long.MAX_VALUE){ + requestUpdateActiveForegroundAppsLocked(smap, active.mHideTime); } } } @@ -904,26 +947,44 @@ public final class ActiveServices { // services that were started while the screen was off. if (screenOn) { final long nowElapsed = SystemClock.elapsedRealtime(); + if (DEBUG_FOREGROUND_SERVICE) Slog.d(TAG, "Screen turned on"); for (int i = mServiceMap.size()-1; i >= 0; i--) { ServiceMap smap = mServiceMap.valueAt(i); + long nextUpdateTime = Long.MAX_VALUE; boolean changed = false; for (int j = smap.mActiveForegroundApps.size()-1; j >= 0; j--) { ActiveForegroundApp active = smap.mActiveForegroundApps.valueAt(j); - if (!active.mShownWhileScreenOn) { - changed = true; - active.mShownWhileScreenOn = mScreenOn; - active.mStartVisibleTime = nowElapsed; - if (active.mEndTime != 0) { - active.mEndTime = nowElapsed; + if (active.mEndTime == 0) { + if (!active.mShownWhileScreenOn) { + active.mShownWhileScreenOn = true; + active.mStartVisibleTime = nowElapsed; + } + } else { + if (!active.mShownWhileScreenOn + && active.mStartVisibleTime == active.mStartTime) { + // If this was never shown while the screen was on, then we will + // count the time it started being visible as now, to tell the user + // about it now that they have a screen to look at. + active.mEndTime = active.mStartVisibleTime = nowElapsed; + } + if (foregroundAppShownEnoughLocked(active, nowElapsed)) { + // Have been active for long enough that we will remove it + // immediately. + smap.mActiveForegroundApps.remove(active.mPackageName); + smap.mActiveForegroundAppsChanged = true; + changed = true; + } else { + if (active.mHideTime < nextUpdateTime) { + nextUpdateTime = active.mHideTime; + } } } } if (changed) { - requestUpdateActiveForegroundAppsLocked(smap, - nowElapsed + mAm.mConstants.FOREGROUND_SERVICE_UI_MIN_TIME); - } else if (smap.mActiveForegroundApps.size() > 0) { - // Just being paranoid. + // Need to immediately update. requestUpdateActiveForegroundAppsLocked(smap, 0); + } else if (nextUpdateTime < Long.MAX_VALUE) { + requestUpdateActiveForegroundAppsLocked(smap, nextUpdateTime); } } } @@ -2318,7 +2379,7 @@ public final class ActiveServices { return true; } - // Is someone still bound to us keepign us running? + // Is someone still bound to us keeping us running? if (!knowConn) { hasConn = r.hasAutoCreateConnections(); } @@ -3741,6 +3802,17 @@ public final class ActiveServices { pw.println(); } } + if (smap.hasMessagesOrCallbacks()) { + if (needSep) { + pw.println(); + } + printedAnything = true; + needSep = true; + pw.print(" Handler - user "); + pw.print(user); + pw.println(":"); + smap.dumpMine(new PrintWriterPrinter(pw), " "); + } } } diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 5749f31ddb2e..6c3fe915f386 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -35,8 +35,14 @@ final class ActivityManagerConstants extends ContentObserver { // Key names stored in the settings value. private static final String KEY_MAX_CACHED_PROCESSES = "max_cached_processes"; private static final String KEY_BACKGROUND_SETTLE_TIME = "background_settle_time"; - private static final String KEY_FOREGROUND_SERVICE_UI_MIN_TIME - = "foreground_service_ui_min_time"; + private static final String KEY_FGSERVICE_MIN_SHOWN_TIME + = "fgservice_min_shown_time"; + private static final String KEY_FGSERVICE_MIN_REPORT_TIME + = "fgservice_min_report_time"; + private static final String KEY_FGSERVICE_SCREEN_ON_BEFORE_TIME + = "fgservice_screen_on_before_time"; + private static final String KEY_FGSERVICE_SCREEN_ON_AFTER_TIME + = "fgservice_screen_on_after_time"; private static final String KEY_CONTENT_PROVIDER_RETAIN_TIME = "content_provider_retain_time"; private static final String KEY_GC_TIMEOUT = "gc_timeout"; private static final String KEY_GC_MIN_INTERVAL = "gc_min_interval"; @@ -58,7 +64,10 @@ final class ActivityManagerConstants extends ContentObserver { private static final int DEFAULT_MAX_CACHED_PROCESSES = 32; private static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60*1000; - private static final long DEFAULT_FOREGROUND_SERVICE_UI_MIN_TIME = 30*1000; + private static final long DEFAULT_FGSERVICE_MIN_SHOWN_TIME = 2*1000; + private static final long DEFAULT_FGSERVICE_MIN_REPORT_TIME = 3*1000; + private static final long DEFAULT_FGSERVICE_SCREEN_ON_BEFORE_TIME = 1*1000; + private static final long DEFAULT_FGSERVICE_SCREEN_ON_AFTER_TIME = 5*1000; private static final long DEFAULT_CONTENT_PROVIDER_RETAIN_TIME = 20*1000; private static final long DEFAULT_GC_TIMEOUT = 5*1000; private static final long DEFAULT_GC_MIN_INTERVAL = 60*1000; @@ -85,8 +94,26 @@ final class ActivityManagerConstants extends ContentObserver { // before we start restricting what it can do. public long BACKGROUND_SETTLE_TIME = DEFAULT_BACKGROUND_SETTLE_TIME; - // The minimum time a foreground service will be shown as running in the notification UI. - public long FOREGROUND_SERVICE_UI_MIN_TIME = DEFAULT_FOREGROUND_SERVICE_UI_MIN_TIME; + // The minimum time we allow a foreground service to run with a notification and the + // screen on without otherwise telling the user about it. (If it runs for less than this, + // it will still be reported to the user as a running app for at least this amount of time.) + public long FGSERVICE_MIN_SHOWN_TIME = DEFAULT_FGSERVICE_MIN_SHOWN_TIME; + + // If a foreground service is shown for less than FGSERVICE_MIN_SHOWN_TIME, we will display + // the background app running notification about it for at least this amount of time (if it + // is larger than the remaining shown time). + public long FGSERVICE_MIN_REPORT_TIME = DEFAULT_FGSERVICE_MIN_REPORT_TIME; + + // The minimum amount of time the foreground service needs to have remain being shown + // before the screen goes on for us to consider it not worth showing to the user. That is + // if an app has a foreground service that stops itself this amount of time or more before + // the user turns on the screen, we will just let it go without the user being told about it. + public long FGSERVICE_SCREEN_ON_BEFORE_TIME = DEFAULT_FGSERVICE_SCREEN_ON_BEFORE_TIME; + + // The minimum amount of time a foreground service should remain reported to the user if + // it is stopped when the screen turns on. This is the time from when the screen turns + // on until we will stop reporting it. + public long FGSERVICE_SCREEN_ON_AFTER_TIME = DEFAULT_FGSERVICE_SCREEN_ON_AFTER_TIME; // How long we will retain processes hosting content providers in the "last activity" // state before allowing them to drop down to the regular cached LRU list. This is @@ -225,8 +252,14 @@ final class ActivityManagerConstants extends ContentObserver { DEFAULT_MAX_CACHED_PROCESSES); BACKGROUND_SETTLE_TIME = mParser.getLong(KEY_BACKGROUND_SETTLE_TIME, DEFAULT_BACKGROUND_SETTLE_TIME); - FOREGROUND_SERVICE_UI_MIN_TIME = mParser.getLong(KEY_FOREGROUND_SERVICE_UI_MIN_TIME, - DEFAULT_FOREGROUND_SERVICE_UI_MIN_TIME); + FGSERVICE_MIN_SHOWN_TIME = mParser.getLong(KEY_FGSERVICE_MIN_SHOWN_TIME, + DEFAULT_FGSERVICE_MIN_SHOWN_TIME); + FGSERVICE_MIN_REPORT_TIME = mParser.getLong(KEY_FGSERVICE_MIN_REPORT_TIME, + DEFAULT_FGSERVICE_MIN_REPORT_TIME); + FGSERVICE_SCREEN_ON_BEFORE_TIME = mParser.getLong(KEY_FGSERVICE_SCREEN_ON_BEFORE_TIME, + DEFAULT_FGSERVICE_SCREEN_ON_BEFORE_TIME); + FGSERVICE_SCREEN_ON_AFTER_TIME = mParser.getLong(KEY_FGSERVICE_SCREEN_ON_AFTER_TIME, + DEFAULT_FGSERVICE_SCREEN_ON_AFTER_TIME); CONTENT_PROVIDER_RETAIN_TIME = mParser.getLong(KEY_CONTENT_PROVIDER_RETAIN_TIME, DEFAULT_CONTENT_PROVIDER_RETAIN_TIME); GC_TIMEOUT = mParser.getLong(KEY_GC_TIMEOUT, @@ -284,8 +317,14 @@ final class ActivityManagerConstants extends ContentObserver { pw.println(MAX_CACHED_PROCESSES); pw.print(" "); pw.print(KEY_BACKGROUND_SETTLE_TIME); pw.print("="); pw.println(BACKGROUND_SETTLE_TIME); - pw.print(" "); pw.print(KEY_FOREGROUND_SERVICE_UI_MIN_TIME); pw.print("="); - pw.println(FOREGROUND_SERVICE_UI_MIN_TIME); + pw.print(" "); pw.print(KEY_FGSERVICE_MIN_SHOWN_TIME); pw.print("="); + pw.println(FGSERVICE_MIN_SHOWN_TIME); + pw.print(" "); pw.print(KEY_FGSERVICE_MIN_REPORT_TIME); pw.print("="); + pw.println(FGSERVICE_MIN_REPORT_TIME); + pw.print(" "); pw.print(KEY_FGSERVICE_SCREEN_ON_BEFORE_TIME); pw.print("="); + pw.println(FGSERVICE_SCREEN_ON_BEFORE_TIME); + pw.print(" "); pw.print(KEY_FGSERVICE_SCREEN_ON_AFTER_TIME); pw.print("="); + pw.println(FGSERVICE_SCREEN_ON_AFTER_TIME); pw.print(" "); pw.print(KEY_CONTENT_PROVIDER_RETAIN_TIME); pw.print("="); pw.println(CONTENT_PROVIDER_RETAIN_TIME); pw.print(" "); pw.print(KEY_GC_TIMEOUT); pw.print("="); diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java index ff5efde27f93..f440100b7621 100644 --- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java +++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java @@ -79,6 +79,7 @@ class ActivityManagerDebugConfig { static final boolean DEBUG_SAVED_STATE = DEBUG_ALL_ACTIVITIES || false; static final boolean DEBUG_SCREENSHOTS = DEBUG_ALL_ACTIVITIES || false; static final boolean DEBUG_SERVICE = DEBUG_ALL || false; + static final boolean DEBUG_FOREGROUND_SERVICE = DEBUG_ALL || false; static final boolean DEBUG_SERVICE_EXECUTING = DEBUG_ALL || false; static final boolean DEBUG_STACK = DEBUG_ALL || false; static final boolean DEBUG_STATES = DEBUG_ALL_ACTIVITIES || false; diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index ac68a9ea6dc2..14be25b6690f 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -10254,11 +10254,11 @@ public class ActivityManagerService extends IActivityManager.Stub if (DEBUG_STACK) Slog.d(TAG_STACK, "moveTaskToFront: moving taskId=" + taskId); synchronized(this) { - moveTaskToFrontLocked(taskId, flags, bOptions); + moveTaskToFrontLocked(taskId, flags, bOptions, false /* fromRecents */); } } - void moveTaskToFrontLocked(int taskId, int flags, Bundle bOptions) { + void moveTaskToFrontLocked(int taskId, int flags, Bundle bOptions, boolean fromRecents) { ActivityOptions options = ActivityOptions.fromBundle(bOptions); if (!checkAppSwitchAllowedLocked(Binder.getCallingPid(), @@ -10291,7 +10291,7 @@ public class ActivityManagerService extends IActivityManager.Stub // We are reshowing a task, use a starting window to hide the initial draw delay // so the transition can start earlier. topActivity.showStartingWindow(null /* prev */, false /* newTask */, - true /* taskSwitch */); + true /* taskSwitch */, fromRecents); } } finally { Binder.restoreCallingIdentity(origId); @@ -23752,9 +23752,10 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void notifyAppTransitionStarting(SparseIntArray reasons) { + public void notifyAppTransitionStarting(SparseIntArray reasons, long timestamp) { synchronized (ActivityManagerService.this) { - mStackSupervisor.mActivityMetricsLogger.notifyTransitionStarting(reasons); + mStackSupervisor.mActivityMetricsLogger.notifyTransitionStarting( + reasons, timestamp); } } @@ -24014,6 +24015,13 @@ public class ActivityManagerService extends IActivityManager.Stub if (reason != null) { pw.println(" Reason: " + reason); } + pw.println(" mLastHomeActivityStartResult: " + + mActivityStarter.mLastHomeActivityStartResult); + final ActivityRecord r = mActivityStarter.mLastHomeActivityStartRecord[0]; + if (r != null) { + pw.println(" mLastHomeActivityStartRecord:"); + r.dump(pw, " "); + } pw.println(); dumpActivitiesLocked(null /* fd */, pw, null /* args */, 0 /* opti */, true /* dumpAll */, false /* dumpClient */, null /* dumpPackage */, diff --git a/services/core/java/com/android/server/am/ActivityMetricsLogger.java b/services/core/java/com/android/server/am/ActivityMetricsLogger.java index bf7b663454b8..98815d7e18c7 100644 --- a/services/core/java/com/android/server/am/ActivityMetricsLogger.java +++ b/services/core/java/com/android/server/am/ActivityMetricsLogger.java @@ -230,12 +230,12 @@ class ActivityMetricsLogger { /** * Notifies the tracker that all windows of the app have been drawn. */ - void notifyWindowsDrawn(int stackId) { + void notifyWindowsDrawn(int stackId, long timestamp) { final StackTransitionInfo info = mStackTransitionInfo.get(stackId); if (info == null || info.loggedWindowsDrawn) { return; } - info.windowsDrawnDelayMs = calculateCurrentDelay(); + info.windowsDrawnDelayMs = calculateDelay(timestamp); info.loggedWindowsDrawn = true; if (allStacksWindowsDrawn() && mLoggedTransitionStarting) { reset(false /* abort */); @@ -245,13 +245,13 @@ class ActivityMetricsLogger { /** * Notifies the tracker that the starting window was drawn. */ - void notifyStartingWindowDrawn(int stackId) { + void notifyStartingWindowDrawn(int stackId, long timestamp) { final StackTransitionInfo info = mStackTransitionInfo.get(stackId); if (info == null || info.loggedStartingWindowDrawn) { return; } info.loggedStartingWindowDrawn = true; - info.startingWindowDelayMs = calculateCurrentDelay(); + info.startingWindowDelayMs = calculateDelay(timestamp); } /** @@ -260,11 +260,11 @@ class ActivityMetricsLogger { * @param stackIdReasons A map from stack id to a reason integer, which must be on of * ActivityManagerInternal.APP_TRANSITION_* reasons. */ - void notifyTransitionStarting(SparseIntArray stackIdReasons) { + void notifyTransitionStarting(SparseIntArray stackIdReasons, long timestamp) { if (!isAnyTransitionActive() || mLoggedTransitionStarting) { return; } - mCurrentTransitionDelayMs = calculateCurrentDelay(); + mCurrentTransitionDelayMs = calculateDelay(timestamp); mLoggedTransitionStarting = true; for (int index = stackIdReasons.size() - 1; index >= 0; index--) { final int stackId = stackIdReasons.keyAt(index); @@ -344,6 +344,11 @@ class ActivityMetricsLogger { return (int) (SystemClock.uptimeMillis() - mCurrentTransitionStartTime); } + private int calculateDelay(long timestamp) { + // Shouldn't take more than 25 days to launch an app, so int is fine here. + return (int) (timestamp - mCurrentTransitionStartTime); + } + private void logAppTransitionMultiEvents() { for (int index = mStackTransitionInfo.size() - 1; index >= 0; index--) { final StackTransitionInfo info = mStackTransitionInfo.valueAt(index); diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java index ec6a4f6b7f7c..68f4d0d99f93 100644 --- a/services/core/java/com/android/server/am/ActivityRecord.java +++ b/services/core/java/com/android/server/am/ActivityRecord.java @@ -1928,18 +1928,19 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } @Override - public void onStartingWindowDrawn() { + public void onStartingWindowDrawn(long timestamp) { synchronized (service) { - mStackSupervisor.mActivityMetricsLogger.notifyStartingWindowDrawn(getStackId()); + mStackSupervisor.mActivityMetricsLogger.notifyStartingWindowDrawn( + getStackId(), timestamp); } } @Override - public void onWindowsDrawn() { + public void onWindowsDrawn(long timestamp) { synchronized (service) { - mStackSupervisor.mActivityMetricsLogger.notifyWindowsDrawn(getStackId()); + mStackSupervisor.mActivityMetricsLogger.notifyWindowsDrawn(getStackId(), timestamp); if (displayStartTime != 0) { - reportLaunchTimeLocked(SystemClock.uptimeMillis()); + reportLaunchTimeLocked(timestamp); } mStackSupervisor.sendWaitingVisibleReportLocked(this); startTime = 0; @@ -2153,6 +2154,11 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo } void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch) { + showStartingWindow(prev, newTask, taskSwitch, false /* fromRecents */); + } + + void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch, + boolean fromRecents) { if (mWindowContainerController == null) { return; } @@ -2167,7 +2173,8 @@ final class ActivityRecord extends ConfigurationContainer implements AppWindowCo compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags, prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning(), allowTaskSnapshot(), - state.ordinal() >= RESUMED.ordinal() && state.ordinal() <= STOPPED.ordinal()); + state.ordinal() >= RESUMED.ordinal() && state.ordinal() <= STOPPED.ordinal(), + fromRecents); if (shown) { mStartingWindowState = STARTING_WINDOW_SHOWN; } diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 53afe78f2ea3..7de56fa17877 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -5131,7 +5131,7 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D && task.getRootActivity() != null) { mService.mActivityStarter.sendPowerHintForLaunchStartIfNeeded(true /* forceSend */); mActivityMetricsLogger.notifyActivityLaunching(); - mService.moveTaskToFrontLocked(task.taskId, 0, bOptions); + mService.moveTaskToFrontLocked(task.taskId, 0, bOptions, true /* fromRecents */); mActivityMetricsLogger.notifyActivityLaunched(ActivityManager.START_TASK_TO_FRONT, task.getTopActivity()); diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java index 1ed2ac19ea9d..d74d1d6dc954 100644 --- a/services/core/java/com/android/server/am/ActivityStarter.java +++ b/services/core/java/com/android/server/am/ActivityStarter.java @@ -188,6 +188,11 @@ class ActivityStarter { private boolean mUsingVr2dDisplay; + // Last home activity record we attempted to start + final ActivityRecord[] mLastHomeActivityStartRecord = new ActivityRecord[1]; + // The result of the last home activity we attempted to start. + int mLastHomeActivityStartResult; + private void reset() { mStartActivity = null; mIntent = null; @@ -592,12 +597,13 @@ class ActivityStarter { void startHomeActivityLocked(Intent intent, ActivityInfo aInfo, String reason) { mSupervisor.moveHomeStackTaskToTop(reason); - startActivityLocked(null /*caller*/, intent, null /*ephemeralIntent*/, - null /*resolvedType*/, aInfo, null /*rInfo*/, null /*voiceSession*/, - null /*voiceInteractor*/, null /*resultTo*/, null /*resultWho*/, - 0 /*requestCode*/, 0 /*callingPid*/, 0 /*callingUid*/, null /*callingPackage*/, - 0 /*realCallingPid*/, 0 /*realCallingUid*/, 0 /*startFlags*/, null /*options*/, - false /*ignoreTargetSecurity*/, false /*componentSpecified*/, null /*outActivity*/, + mLastHomeActivityStartResult = startActivityLocked(null /*caller*/, intent, + null /*ephemeralIntent*/, null /*resolvedType*/, aInfo, null /*rInfo*/, + null /*voiceSession*/, null /*voiceInteractor*/, null /*resultTo*/, + null /*resultWho*/, 0 /*requestCode*/, 0 /*callingPid*/, 0 /*callingUid*/, + null /*callingPackage*/, 0 /*realCallingPid*/, 0 /*realCallingUid*/, + 0 /*startFlags*/, null /*options*/, false /*ignoreTargetSecurity*/, + false /*componentSpecified*/, mLastHomeActivityStartRecord /*outActivity*/, null /*container*/, null /*inTask*/); if (mSupervisor.inResumeTopActivity) { // If we are in resume section already, home activity will be initialized, but not diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 639b7a911998..b3a2c291760c 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -614,9 +614,10 @@ public final class BroadcastQueue { skip = true; } - if (!skip && (filter.receiverList.app == null || filter.receiverList.app.crashing)) { + if (!skip && (filter.receiverList.app == null || filter.receiverList.app.killed + || filter.receiverList.app.crashing)) { Slog.w(TAG, "Skipping deliver [" + mQueueName + "] " + r - + " to " + filter.receiverList + ": process crashing"); + + " to " + filter.receiverList + ": process gone or crashing"); skip = true; } @@ -1317,7 +1318,7 @@ public final class BroadcastQueue { } // Is this receiver's application already running? - if (app != null && app.thread != null) { + if (app != null && app.thread != null && !app.killed) { try { app.addPackage(info.activityInfo.packageName, info.activityInfo.applicationInfo.versionCode, mService.mProcessStats); diff --git a/services/core/java/com/android/server/am/UserState.java b/services/core/java/com/android/server/am/UserState.java index 9970c82e2c75..b89586dc59dc 100644 --- a/services/core/java/com/android/server/am/UserState.java +++ b/services/core/java/com/android/server/am/UserState.java @@ -69,7 +69,6 @@ public final class UserState { public boolean setState(int oldState, int newState) { if (state == oldState) { setState(newState); - EventLogTags.writeAmUserStateChanged(mHandle.getIdentifier(), newState); return true; } else { Slog.w(TAG, "Expected user " + mHandle.getIdentifier() + " in state " @@ -84,6 +83,7 @@ public final class UserState { } Slog.i(TAG, "User " + mHandle.getIdentifier() + " state changed from " + stateToString(state) + " to " + stateToString(newState)); + EventLogTags.writeAmUserStateChanged(mHandle.getIdentifier(), newState); lastState = state; state = newState; } diff --git a/services/core/java/com/android/server/display/OverlayDisplayWindow.java b/services/core/java/com/android/server/display/OverlayDisplayWindow.java index f23caf2e7a76..0fdf2daf1c8d 100644 --- a/services/core/java/com/android/server/display/OverlayDisplayWindow.java +++ b/services/core/java/com/android/server/display/OverlayDisplayWindow.java @@ -30,6 +30,7 @@ import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.ScaleGestureDetector; import android.view.TextureView; +import android.view.ThreadedRenderer; import android.view.View; import android.view.WindowManager; import android.view.TextureView.SurfaceTextureListener; @@ -95,6 +96,8 @@ final class OverlayDisplayWindow implements DumpUtils.Dump { public OverlayDisplayWindow(Context context, String name, int width, int height, int densityDpi, int gravity, boolean secure, Listener listener) { + // Workaround device freeze (b/38372997) + ThreadedRenderer.disableVsync(); mContext = context; mName = name; mGravity = gravity; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 19d5f3c76ce7..bdea24704ef5 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -307,11 +307,15 @@ public class NotificationManagerService extends SystemService { // used as a mutex for access to all active notifications & listeners final Object mNotificationLock = new Object(); + @GuardedBy("mNotificationLock") final ArrayList<NotificationRecord> mNotificationList = new ArrayList<NotificationRecord>(); + @GuardedBy("mNotificationLock") final ArrayMap<String, NotificationRecord> mNotificationsByKey = new ArrayMap<String, NotificationRecord>(); + @GuardedBy("mNotificationLock") final ArrayList<NotificationRecord> mEnqueuedNotifications = new ArrayList<>(); + @GuardedBy("mNotificationLock") final ArrayMap<Integer, ArrayMap<String, String>> mAutobundledSummaries = new ArrayMap<>(); final ArrayList<ToastRecord> mToastQueue = new ArrayList<ToastRecord>(); final ArrayMap<String, NotificationRecord> mSummaryByGroupKey = new ArrayMap<>(); @@ -607,7 +611,7 @@ public class NotificationManagerService extends SystemService { @Override public void onPanelRevealed(boolean clearEffects, int items) { MetricsLogger.visible(getContext(), MetricsEvent.NOTIFICATION_PANEL); - MetricsLogger.histogram(getContext(), "notification_load", items); + MetricsLogger.histogram(getContext(), "note_load", items); EventLogTags.writeNotificationPanelRevealed(items); if (clearEffects) { clearEffects(); @@ -2806,7 +2810,8 @@ public class NotificationManagerService extends SystemService { // Clear summary. final NotificationRecord removed = findNotificationByKeyLocked(summaries.remove(pkg)); if (removed != null) { - cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED); + boolean wasPosted = removeFromNotificationListsLocked(removed); + cancelNotificationLocked(removed, false, REASON_UNAUTOBUNDLED, wasPosted); } } } @@ -3420,7 +3425,8 @@ public class NotificationManagerService extends SystemService { .setType(MetricsEvent.TYPE_CLOSE) .addTaggedData(MetricsEvent.NOTIFICATION_SNOOZED_CRITERIA, mSnoozeCriterionId == null ? 0 : 1)); - cancelNotificationLocked(r, false, REASON_SNOOZED); + boolean wasPosted = removeFromNotificationListsLocked(r); + cancelNotificationLocked(r, false, REASON_SNOOZED, wasPosted); updateLightsLocked(); if (mSnoozeCriterionId != null) { mNotificationAssistants.notifyAssistantSnoozedLocked(r.sbn, mSnoozeCriterionId); @@ -4206,15 +4212,18 @@ public class NotificationManagerService extends SystemService { manager.sendAccessibilityEvent(event); } + /** + * Removes all NotificationsRecords with the same key as the given notification record + * from both lists. Do not call this method while iterating over either list. + */ @GuardedBy("mNotificationLock") - private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason) { - final String canceledKey = r.getKey(); - - // Remove from both lists, either list could have a separate Record for what is effectively - // the same notification. + private boolean removeFromNotificationListsLocked(NotificationRecord r) { + // Remove from both lists, either list could have a separate Record for what is + // effectively the same notification. boolean wasPosted = false; NotificationRecord recordInList = null; - if ((recordInList = findNotificationByListLocked(mNotificationList, r.getKey())) != null) { + if ((recordInList = findNotificationByListLocked(mNotificationList, r.getKey())) + != null) { mNotificationList.remove(recordInList); mNotificationsByKey.remove(recordInList.sbn.getKey()); wasPosted = true; @@ -4223,6 +4232,13 @@ public class NotificationManagerService extends SystemService { != null) { mEnqueuedNotifications.remove(recordInList); } + return wasPosted; + } + + @GuardedBy("mNotificationLock") + private void cancelNotificationLocked(NotificationRecord r, boolean sendDelete, int reason, + boolean wasPosted) { + final String canceledKey = r.getKey(); // Record caller. recordCallerLocked(r); @@ -4363,7 +4379,8 @@ public class NotificationManagerService extends SystemService { } // Cancel the notification. - cancelNotificationLocked(r, sendDelete, reason); + boolean wasPosted = removeFromNotificationListsLocked(r); + cancelNotificationLocked(r, sendDelete, reason, wasPosted); cancelGroupChildrenLocked(r, callingUid, callingPid, listenerName, sendDelete); updateLightsLocked(); @@ -4440,11 +4457,11 @@ public class NotificationManagerService extends SystemService { cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid, pkg, true /*nullPkgIndicatesUserSwitch*/, channelId, flagChecker, false /*includeCurrentProfiles*/, userId, false /*sendDelete*/, reason, - listenerName); + listenerName, true /* wasPosted */); cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid, callingPid, pkg, true /*nullPkgIndicatesUserSwitch*/, channelId, flagChecker, false /*includeCurrentProfiles*/, userId, - false /*sendDelete*/, reason, listenerName); + false /*sendDelete*/, reason, listenerName, false /* wasPosted */); mSnoozeHelper.cancel(userId, pkg); } } @@ -4460,7 +4477,7 @@ public class NotificationManagerService extends SystemService { private void cancelAllNotificationsByListLocked(ArrayList<NotificationRecord> notificationList, int callingUid, int callingPid, String pkg, boolean nullPkgIndicatesUserSwitch, String channelId, FlagChecker flagChecker, boolean includeCurrentProfiles, int userId, - boolean sendDelete, int reason, String listenerName) { + boolean sendDelete, int reason, String listenerName, boolean wasPosted) { ArrayList<NotificationRecord> canceledNotifications = null; for (int i = notificationList.size() - 1; i >= 0; --i) { NotificationRecord r = notificationList.get(i); @@ -4488,8 +4505,9 @@ public class NotificationManagerService extends SystemService { if (canceledNotifications == null) { canceledNotifications = new ArrayList<>(); } + notificationList.remove(i); canceledNotifications.add(r); - cancelNotificationLocked(r, sendDelete, reason); + cancelNotificationLocked(r, sendDelete, reason, wasPosted); } if (canceledNotifications != null) { final int M = canceledNotifications.size(); @@ -4548,11 +4566,11 @@ public class NotificationManagerService extends SystemService { cancelAllNotificationsByListLocked(mNotificationList, callingUid, callingPid, null, false /*nullPkgIndicatesUserSwitch*/, null, flagChecker, includeCurrentProfiles, userId, true /*sendDelete*/, reason, - listenerName); + listenerName, true); cancelAllNotificationsByListLocked(mEnqueuedNotifications, callingUid, callingPid, null, false /*nullPkgIndicatesUserSwitch*/, null, flagChecker, includeCurrentProfiles, userId, true /*sendDelete*/, - reason, listenerName); + reason, listenerName, false); mSnoozeHelper.cancel(userId, includeCurrentProfiles); } } @@ -4569,7 +4587,6 @@ public class NotificationManagerService extends SystemService { } String pkg = r.sbn.getPackageName(); - int userId = r.getUserId(); if (pkg == null) { if (DBG) Log.e(TAG, "No package for group summary: " + r.getKey()); @@ -4577,15 +4594,15 @@ public class NotificationManagerService extends SystemService { } cancelGroupChildrenByListLocked(mNotificationList, r, callingUid, callingPid, listenerName, - sendDelete); + sendDelete, true); cancelGroupChildrenByListLocked(mEnqueuedNotifications, r, callingUid, callingPid, - listenerName, sendDelete); + listenerName, sendDelete, false); } @GuardedBy("mNotificationLock") private void cancelGroupChildrenByListLocked(ArrayList<NotificationRecord> notificationList, NotificationRecord parentNotification, int callingUid, int callingPid, - String listenerName, boolean sendDelete) { + String listenerName, boolean sendDelete, boolean wasPosted) { final String pkg = parentNotification.sbn.getPackageName(); final int userId = parentNotification.getUserId(); final int reason = REASON_GROUP_SUMMARY_CANCELED; @@ -4597,7 +4614,8 @@ public class NotificationManagerService extends SystemService { && (childR.getFlags() & Notification.FLAG_FOREGROUND_SERVICE) == 0) { EventLogTags.writeNotificationCancel(callingUid, callingPid, pkg, childSbn.getId(), childSbn.getTag(), userId, 0, 0, reason, listenerName); - cancelNotificationLocked(childR, sendDelete, reason); + notificationList.remove(i); + cancelNotificationLocked(childR, sendDelete, reason, wasPosted); } } } diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java index 8952870b8585..6953ffddaf33 100644 --- a/services/core/java/com/android/server/notification/NotificationRecord.java +++ b/services/core/java/com/android/server/notification/NotificationRecord.java @@ -25,6 +25,7 @@ import android.app.Notification; import android.app.NotificationChannel; import android.content.Context; import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.graphics.Bitmap; @@ -170,6 +171,11 @@ public final class NotificationRecord { private Uri calculateSound() { final Notification n = sbn.getNotification(); + // No notification sounds on tv + if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)) { + return null; + } + Uri sound = mChannel.getSound(); if (mPreChannelsNotification && (getChannel().getUserLockedFields() & NotificationChannel.USER_LOCKED_SOUND) == 0) { diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java index 4eadfd168640..d7b36aaa8008 100644 --- a/services/core/java/com/android/server/notification/RankingHelper.java +++ b/services/core/java/com/android/server/notification/RankingHelper.java @@ -53,6 +53,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.concurrent.ConcurrentHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -1199,6 +1200,6 @@ public class RankingHelper implements RankingConfig { boolean showBadge = DEFAULT_SHOW_BADGE; ArrayMap<String, NotificationChannel> channels = new ArrayMap<>(); - ArrayMap<String, NotificationChannelGroup> groups = new ArrayMap<>(); + Map<String, NotificationChannelGroup> groups = new ConcurrentHashMap<>(); } } diff --git a/services/core/java/com/android/server/om/OverlayManagerService.java b/services/core/java/com/android/server/om/OverlayManagerService.java index ef3e7bcefc17..2940a6e3fc8d 100644 --- a/services/core/java/com/android/server/om/OverlayManagerService.java +++ b/services/core/java/com/android/server/om/OverlayManagerService.java @@ -714,11 +714,17 @@ public final class OverlayManagerService extends SystemService { final Map<String, List<String>> pendingChanges = new ArrayMap<>(targetPackageNames.size()); synchronized (mLock) { + final List<String> frameworkOverlays = + mImpl.getEnabledOverlayPackageNames("android", userId); final int N = targetPackageNames.size(); for (int i = 0; i < N; i++) { final String targetPackageName = targetPackageNames.get(i); - pendingChanges.put(targetPackageName, - mImpl.getEnabledOverlayPackageNames(targetPackageName, userId)); + List<String> list = new ArrayList<>(); + if (!"android".equals(targetPackageName)) { + list.addAll(frameworkOverlays); + } + list.addAll(mImpl.getEnabledOverlayPackageNames(targetPackageName, userId)); + pendingChanges.put(targetPackageName, list); } } diff --git a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java index 261bcc5838f8..db6e9749535b 100644 --- a/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java +++ b/services/core/java/com/android/server/om/OverlayManagerServiceImpl.java @@ -170,6 +170,7 @@ final class OverlayManagerServiceImpl { final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId); updateAllOverlaysForTarget(packageName, userId, targetPackage); + mListener.onOverlaysChanged(packageName, userId); } void onTargetPackageChanged(@NonNull final String packageName, final int userId) { @@ -178,7 +179,9 @@ final class OverlayManagerServiceImpl { } final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId); - updateAllOverlaysForTarget(packageName, userId, targetPackage); + if (updateAllOverlaysForTarget(packageName, userId, targetPackage)) { + mListener.onOverlaysChanged(packageName, userId); + } } void onTargetPackageUpgrading(@NonNull final String packageName, final int userId) { @@ -186,7 +189,9 @@ final class OverlayManagerServiceImpl { Slog.d(TAG, "onTargetPackageUpgrading packageName=" + packageName + " userId=" + userId); } - updateAllOverlaysForTarget(packageName, userId, null); + if (updateAllOverlaysForTarget(packageName, userId, null)) { + mListener.onOverlaysChanged(packageName, userId); + } } void onTargetPackageUpgraded(@NonNull final String packageName, final int userId) { @@ -195,7 +200,9 @@ final class OverlayManagerServiceImpl { } final PackageInfo targetPackage = mPackageManager.getPackageInfo(packageName, userId); - updateAllOverlaysForTarget(packageName, userId, targetPackage); + if (updateAllOverlaysForTarget(packageName, userId, targetPackage)) { + mListener.onOverlaysChanged(packageName, userId); + } } void onTargetPackageRemoved(@NonNull final String packageName, final int userId) { diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java index 4540d2dfd692..f111db1434c3 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerSession.java +++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java @@ -856,8 +856,15 @@ public class PackageInstallerSession extends IPackageInstallerSession.Stub { mResolvedInstructionSets.add(archSubDir.getName()); List<File> oatFiles = Arrays.asList(archSubDir.listFiles()); - if (!oatFiles.isEmpty()) { - mResolvedInheritedFiles.addAll(oatFiles); + + // Only add compiled files associated with the base. + // Once b/62269291 is resolved, we can add all compiled files again. + for (File oatFile : oatFiles) { + if (oatFile.getName().equals("base.art") + || oatFile.getName().equals("base.odex") + || oatFile.getName().equals("base.vdex")) { + mResolvedInheritedFiles.add(oatFile); + } } } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 07d548d9d62b..6c59505c8dd0 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -18105,8 +18105,10 @@ public class PackageManagerService extends IPackageManager.Stub // step during installation. Instead, we'll take extra time the first time the // instant app starts. It's preferred to do it this way to provide continuous // progress to the user instead of mysteriously blocking somewhere in the - // middle of running an instant app. - if (!instantApp) { + // middle of running an instant app. The default behaviour can be overridden + // via gservices. + if (!instantApp || Global.getInt( + mContext.getContentResolver(), Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0) { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "dexopt"); // Do not run PackageDexOptimizer through the local performDexOpt // method because `pkg` may not be in `mPackages` yet. @@ -19309,7 +19311,7 @@ public class PackageManagerService extends IPackageManager.Stub synchronized (mPackages) { final PackageSetting ps = mSettings.mPackages.get(packageName); if (ps == null || filterAppAccessLPr(ps, Binder.getCallingUid(), userId)) { - return true; + return false; } return mSettings.getBlockUninstallLPr(userId, packageName); } @@ -21585,8 +21587,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); public static final int DUMP_FROZEN = 1 << 19; public static final int DUMP_DEXOPT = 1 << 20; public static final int DUMP_COMPILER_STATS = 1 << 21; - public static final int DUMP_ENABLED_OVERLAYS = 1 << 22; - public static final int DUMP_CHANGES = 1 << 23; + public static final int DUMP_CHANGES = 1 << 22; public static final int OPTION_SHOW_FILTERS = 1 << 0; @@ -21830,8 +21831,6 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); dumpState.setDump(DumpState.DUMP_DEXOPT); } else if ("compiler-stats".equals(cmd)) { dumpState.setDump(DumpState.DUMP_COMPILER_STATS); - } else if ("enabled-overlays".equals(cmd)) { - dumpState.setDump(DumpState.DUMP_ENABLED_OVERLAYS); } else if ("changes".equals(cmd)) { dumpState.setDump(DumpState.DUMP_CHANGES); } else if ("write".equals(cmd)) { @@ -24608,12 +24607,7 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); } final PackageSetting ps = mSettings.mPackages.get(targetPackageName); - String[] frameworkOverlayPaths = null; - if (!"android".equals(targetPackageName)) { - frameworkOverlayPaths = - mSettings.mPackages.get("android").getOverlayPaths(userId); - } - ps.setOverlayPaths(overlayPaths, frameworkOverlayPaths, userId); + ps.setOverlayPaths(overlayPaths, userId); return true; } } diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index d17267f38648..f68512758456 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -330,21 +330,9 @@ abstract class PackageSettingBase extends SettingBase { modifyUserState(userId).installReason = installReason; } - void setOverlayPaths(List<String> overlayPaths, String[] frameworkOverlayPaths, int userId) { - if (overlayPaths == null && frameworkOverlayPaths == null) { - modifyUserState(userId).overlayPaths = null; - return; - } - final List<String> paths; - if (frameworkOverlayPaths == null) { - paths = overlayPaths; - } else { - paths = Lists.newArrayList(frameworkOverlayPaths); - if (overlayPaths != null) { - paths.addAll(overlayPaths); - } - } - modifyUserState(userId).overlayPaths = paths.toArray(new String[paths.size()]); + void setOverlayPaths(List<String> overlayPaths, int userId) { + modifyUserState(userId).overlayPaths = overlayPaths == null ? null : + overlayPaths.toArray(new String[overlayPaths.size()]); } String[] getOverlayPaths(int userId) { diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index b006c2d991e6..45d0c585627b 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -4861,9 +4861,9 @@ final class Settings { String[] overlayPaths = ps.getOverlayPaths(user.id); if (overlayPaths != null && overlayPaths.length > 0) { - pw.println("Overlay paths:"); + pw.print(prefix); pw.println(" overlay paths:"); for (String path : overlayPaths) { - pw.println(path); + pw.print(prefix); pw.print(" "); pw.println(path); } } diff --git a/services/core/java/com/android/server/policy/BarController.java b/services/core/java/com/android/server/policy/BarController.java index 7a2808146f62..b1792358be53 100644 --- a/services/core/java/com/android/server/policy/BarController.java +++ b/services/core/java/com/android/server/policy/BarController.java @@ -166,8 +166,14 @@ public class BarController { return change || stateChanged; } - void setOnBarVisibilityChangedListener(OnBarVisibilityChangedListener listener) { + void setOnBarVisibilityChangedListener(OnBarVisibilityChangedListener listener, + boolean invokeWithState) { mVisibilityChangeListener = listener; + if (invokeWithState) { + // Optionally report the initial window state for initialization purposes + mHandler.obtainMessage(MSG_NAV_BAR_VISIBILITY_CHANGED, + (mState == StatusBarManager.WINDOW_STATE_SHOWING) ? 1 : 0, 0).sendToTarget(); + } } protected boolean skipAnimation() { diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 01eabd8f1403..8b28df1a3058 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -1045,7 +1045,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { new BarController.OnBarVisibilityChangedListener() { @Override public void onBarVisibilityChanged(boolean visible) { - mAccessibilityManager.notifyAccessibilityButtonAvailabilityChanged(visible); + mAccessibilityManager.notifyAccessibilityButtonVisibilityChanged(visible); } }; @@ -3037,7 +3037,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mNavigationBar = win; mNavigationBarController.setWindow(win); mNavigationBarController.setOnBarVisibilityChangedListener( - mNavBarVisibilityListener); + mNavBarVisibilityListener, true); if (DEBUG_LAYOUT) Slog.i(TAG, "NAVIGATION BAR: " + mNavigationBar); break; case TYPE_NAVIGATION_BAR_PANEL: diff --git a/services/core/java/com/android/server/wm/AppTransition.java b/services/core/java/com/android/server/wm/AppTransition.java index c1c72cad0c6b..b1ed35867dcf 100644 --- a/services/core/java/com/android/server/wm/AppTransition.java +++ b/services/core/java/com/android/server/wm/AppTransition.java @@ -2044,7 +2044,10 @@ public class AppTransition implements Dump { if (forceOverride || isKeyguardTransit(transit) || !isTransitionSet() || mNextAppTransition == TRANSIT_NONE) { setAppTransition(transit, flags); - } else if (!alwaysKeepCurrent) { + } + // We never want to change from a Keyguard transit to a non-Keyguard transit, as our logic + // relies on the fact that we always execute a Keyguard transition after preparing one. + else if (!alwaysKeepCurrent && !isKeyguardTransit(transit)) { if (transit == TRANSIT_TASK_OPEN && isTransitionEqual(TRANSIT_TASK_CLOSE)) { // Opening a new task always supersedes a close for the anim. setAppTransition(transit, flags); diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java index 8cfbf68e3b5d..fe7494728ac3 100644 --- a/services/core/java/com/android/server/wm/AppWindowContainerController.java +++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java @@ -38,8 +38,11 @@ import android.graphics.Rect; import android.os.Debug; import android.os.Handler; import android.os.IBinder; +import android.os.Looper; +import android.os.Message; import android.os.Trace; import android.util.Slog; +import android.view.DisplayInfo; import android.view.IApplicationToken; import android.view.WindowManagerPolicy.StartingSurface; @@ -61,23 +64,38 @@ public class AppWindowContainerController private final IApplicationToken mToken; private final Handler mHandler; - private final Runnable mOnStartingWindowDrawn = () -> { - if (mListener == null) { - return; + private final class H extends Handler { + public static final int NOTIFY_WINDOWS_DRAWN = 1; + public static final int NOTIFY_STARTING_WINDOW_DRAWN = 2; + + public H(Looper looper) { + super(looper); } - if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting drawn in " - + AppWindowContainerController.this.mToken); - mListener.onStartingWindowDrawn(); - }; - private final Runnable mOnWindowsDrawn = () -> { - if (mListener == null) { - return; + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case NOTIFY_WINDOWS_DRAWN: + if (mListener == null) { + return; + } + if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting drawn in " + + AppWindowContainerController.this.mToken); + mListener.onWindowsDrawn(msg.getWhen()); + break; + case NOTIFY_STARTING_WINDOW_DRAWN: + if (mListener == null) { + return; + } + if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting drawn in " + + AppWindowContainerController.this.mToken); + mListener.onStartingWindowDrawn(msg.getWhen()); + break; + default: + break; + } } - if (DEBUG_VISIBILITY) Slog.v(TAG_WM, "Reporting drawn in " - + AppWindowContainerController.this.mToken); - mListener.onWindowsDrawn(); - }; + } private final Runnable mOnWindowsVisible = () -> { if (mListener == null) { @@ -212,7 +230,7 @@ public class AppWindowContainerController int targetSdkVersion, int rotationAnimationHint, long inputDispatchingTimeoutNanos, WindowManagerService service, Configuration overrideConfig, Rect bounds) { super(listener, service); - mHandler = new Handler(service.mH.getLooper()); + mHandler = new H(service.mH.getLooper()); mToken = token; synchronized(mWindowMap) { AppWindowToken atoken = mRoot.getAppWindowToken(mToken.asBinder()); @@ -481,7 +499,7 @@ public class AppWindowContainerController public boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags, IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning, - boolean allowTaskSnapshot, boolean activityCreated) { + boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents) { synchronized(mWindowMap) { if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "setAppStartingWindow: token=" + mToken + " pkg=" + pkg + " transferFrom=" + transferFrom + " newTask=" + newTask @@ -510,11 +528,14 @@ public class AppWindowContainerController return false; } + final TaskSnapshot snapshot = mService.mTaskSnapshotController.getSnapshot( + mContainer.getTask().mTaskId, mContainer.getTask().mUserId, + false /* restoreFromDisk */, false /* reducedResolution */); final int type = getStartingWindowType(newTask, taskSwitch, processRunning, - allowTaskSnapshot, activityCreated); + allowTaskSnapshot, activityCreated, fromRecents, snapshot); if (type == STARTING_WINDOW_TYPE_SNAPSHOT) { - return createSnapshot(); + return createSnapshot(snapshot); } // If this is a translucent window, then don't show a starting window -- the current @@ -582,7 +603,8 @@ public class AppWindowContainerController } private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning, - boolean allowTaskSnapshot, boolean activityCreated) { + boolean allowTaskSnapshot, boolean activityCreated, boolean fromRecents, + TaskSnapshot snapshot) { if (mService.mAppTransition.getAppTransition() == TRANSIT_DOCK_TASK_FROM_RECENTS) { // TODO(b/34099271): Remove this statement to add back the starting window and figure // out why it causes flickering, the starting window appears over the thumbnail while @@ -591,7 +613,9 @@ public class AppWindowContainerController } else if (newTask || !processRunning || (taskSwitch && !activityCreated)) { return STARTING_WINDOW_TYPE_SPLASH_SCREEN; } else if (taskSwitch && allowTaskSnapshot) { - return STARTING_WINDOW_TYPE_SNAPSHOT; + return snapshot == null ? STARTING_WINDOW_TYPE_NONE + : snapshotFillsWidth(snapshot) || fromRecents ? STARTING_WINDOW_TYPE_SNAPSHOT + : STARTING_WINDOW_TYPE_SPLASH_SCREEN; } else { return STARTING_WINDOW_TYPE_NONE; } @@ -605,11 +629,7 @@ public class AppWindowContainerController mService.mAnimationHandler.postAtFrontOfQueue(mAddStartingWindow); } - private boolean createSnapshot() { - final TaskSnapshot snapshot = mService.mTaskSnapshotController.getSnapshot( - mContainer.getTask().mTaskId, mContainer.getTask().mUserId, - false /* restoreFromDisk */, false /* reducedResolution */); - + private boolean createSnapshot(TaskSnapshot snapshot) { if (snapshot == null) { return false; } @@ -620,6 +640,24 @@ public class AppWindowContainerController return true; } + private boolean snapshotFillsWidth(TaskSnapshot snapshot) { + if (snapshot == null) { + return false; + } + final Rect rect = new Rect(0, 0, snapshot.getSnapshot().getWidth(), + snapshot.getSnapshot().getHeight()); + rect.inset(snapshot.getContentInsets()); + final Rect taskBoundsWithoutInsets = new Rect(); + mContainer.getTask().getBounds(taskBoundsWithoutInsets); + final DisplayInfo di = mContainer.getDisplayContent().getDisplayInfo(); + final Rect displayBounds = new Rect(0, 0, di.logicalWidth, di.logicalHeight); + final Rect stableInsets = new Rect(); + mService.mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight, + stableInsets); + displayBounds.inset(stableInsets); + return rect.width() >= displayBounds.width(); + } + public void removeStartingWindow() { synchronized (mWindowMap) { if (mHandler.hasCallbacks(mRemoveStartingWindow)) { @@ -740,11 +778,11 @@ public class AppWindowContainerController } void reportStartingWindowDrawn() { - mHandler.post(mOnStartingWindowDrawn); + mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_STARTING_WINDOW_DRAWN)); } void reportWindowsDrawn() { - mHandler.post(mOnWindowsDrawn); + mHandler.sendMessage(mHandler.obtainMessage(H.NOTIFY_WINDOWS_DRAWN)); } void reportWindowsVisible() { diff --git a/services/core/java/com/android/server/wm/AppWindowContainerListener.java b/services/core/java/com/android/server/wm/AppWindowContainerListener.java index 26537f27bce8..8a39a7408058 100644 --- a/services/core/java/com/android/server/wm/AppWindowContainerListener.java +++ b/services/core/java/com/android/server/wm/AppWindowContainerListener.java @@ -19,7 +19,7 @@ package com.android.server.wm; /** Interface used by the creator of the controller to listen to changes with the container. */ public interface AppWindowContainerListener extends WindowContainerListener { /** Called when the windows associated app window container are drawn. */ - void onWindowsDrawn(); + void onWindowsDrawn(long timestamp); /** Called when the windows associated app window container are visible. */ void onWindowsVisible(); /** Called when the windows associated app window container are no longer visible. */ @@ -28,7 +28,7 @@ public interface AppWindowContainerListener extends WindowContainerListener { /** * Called when the starting window for this container is drawn. */ - void onStartingWindowDrawn(); + void onStartingWindowDrawn(long timestamp); /** * Called when the key dispatching to a window associated with the app window container diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java index 71ecaf61da48..bd379344e18b 100644 --- a/services/core/java/com/android/server/wm/AppWindowToken.java +++ b/services/core/java/com/android/server/wm/AppWindowToken.java @@ -394,6 +394,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree startingWindow.mPolicyVisibility = false; startingWindow.mPolicyVisibilityAfterAnim = false; } + + // We are becoming visible, so better freeze the screen with the windows that are + // getting visible so we also wait for them. + forAllWindows(mService::makeWindowFreezingScreenIfNeededLocked, true); } if (DEBUG_APP_TRANSITIONS) Slog.v(TAG_WM, "setVisibility: " + this @@ -891,8 +895,24 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree return mPendingRelaunchCount > 0; } + boolean shouldFreezeBounds() { + final Task task = getTask(); + + // For freeform windows, we can't freeze the bounds at the moment because this would make + // the resizing unresponsive. + if (task == null || task.inFreeformWorkspace()) { + return false; + } + + // We freeze the bounds while drag resizing to deal with the time between + // the divider/drag handle being released, and the handling it's new + // configuration. If we are relaunched outside of the drag resizing state, + // we need to be careful not to do this. + return getTask().isDragResizing(); + } + void startRelaunching() { - if (canFreezeBounds()) { + if (shouldFreezeBounds()) { freezeBounds(); } @@ -909,9 +929,8 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } void finishRelaunching() { - if (canFreezeBounds()) { - unfreezeBounds(); - } + unfreezeBounds(); + if (mPendingRelaunchCount > 0) { mPendingRelaunchCount--; } else { @@ -926,9 +945,7 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree if (mPendingRelaunchCount == 0) { return; } - if (canFreezeBounds()) { - unfreezeBounds(); - } + unfreezeBounds(); mPendingRelaunchCount = 0; } @@ -1032,14 +1049,6 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree } } - private boolean canFreezeBounds() { - final Task task = getTask(); - - // For freeform windows, we can't freeze the bounds at the moment because this would make - // the resizing unresponsive. - return task != null && !task.inFreeformWorkspace(); - } - /** * Freezes the task bounds. The size of this task reported the app will be fixed to the bounds * freezed by {@link Task#prepareFreezingBounds} until {@link #unfreezeBounds} gets called, even @@ -1064,9 +1073,10 @@ class AppWindowToken extends WindowToken implements WindowManagerService.AppFree * Unfreezes the previously frozen bounds. See {@link #freezeBounds}. */ private void unfreezeBounds() { - if (!mFrozenBounds.isEmpty()) { - mFrozenBounds.remove(); + if (mFrozenBounds.isEmpty()) { + return; } + mFrozenBounds.remove(); if (!mFrozenMergedConfig.isEmpty()) { mFrozenMergedConfig.remove(); } diff --git a/services/core/java/com/android/server/wm/PinnedStackController.java b/services/core/java/com/android/server/wm/PinnedStackController.java index 9a9e29a7eb88..6d33ce2941bc 100644 --- a/services/core/java/com/android/server/wm/PinnedStackController.java +++ b/services/core/java/com/android/server/wm/PinnedStackController.java @@ -155,6 +155,10 @@ class PinnedStackController { mSnapAlgorithm = new PipSnapAlgorithm(service.mContext); mDisplayInfo.copyFrom(mDisplayContent.getDisplayInfo()); reloadResources(); + // Initialize the aspect ratio to the default aspect ratio. Don't do this in reload + // resources as it would clobber mAspectRatio when entering PiP from fullscreen which + // triggers a configuration change and the resources to be reloaded. + mAspectRatio = mDefaultAspectRatio; } void onConfigurationChanged() { @@ -171,7 +175,6 @@ class PinnedStackController { mCurrentMinSize = mDefaultMinSize; mDefaultAspectRatio = res.getFloat( com.android.internal.R.dimen.config_pictureInPictureDefaultAspectRatio); - mAspectRatio = mDefaultAspectRatio; final String screenEdgeInsetsDpString = res.getString( com.android.internal.R.string.config_defaultPictureInPictureScreenEdgeInsets); final Size screenEdgeInsetsDp = !screenEdgeInsetsDpString.isEmpty() diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java index 551e3bf13339..a96d22412918 100644 --- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java +++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java @@ -125,6 +125,7 @@ class TaskSnapshotSurface implements StartingSurface { private final Paint mBackgroundPaint = new Paint(); private final int mStatusBarColor; @VisibleForTesting final SystemBarBackgroundPainter mSystemBarBackgroundPainter; + private final int mOrientationOnCreation; static TaskSnapshotSurface create(WindowManagerService service, AppWindowToken token, TaskSnapshot snapshot) { @@ -146,6 +147,7 @@ class TaskSnapshotSurface implements StartingSurface { final int sysUiVis; final int windowFlags; final int windowPrivateFlags; + final int currentOrientation; synchronized (service.mWindowMap) { final WindowState mainWindow = token.findMainWindow(); if (mainWindow == null) { @@ -183,6 +185,7 @@ class TaskSnapshotSurface implements StartingSurface { } else { taskBounds = null; } + currentOrientation = mainWindow.getConfiguration().orientation; } try { final int res = session.addToDisplay(window, window.mSeq, layoutParams, @@ -197,7 +200,8 @@ class TaskSnapshotSurface implements StartingSurface { } final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window, surface, snapshot, layoutParams.getTitle(), backgroundColor, statusBarColor, - navigationBarColor, sysUiVis, windowFlags, windowPrivateFlags, taskBounds); + navigationBarColor, sysUiVis, windowFlags, windowPrivateFlags, taskBounds, + currentOrientation); window.setOuter(snapshotSurface); try { session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, tmpFrame, @@ -215,7 +219,7 @@ class TaskSnapshotSurface implements StartingSurface { TaskSnapshotSurface(WindowManagerService service, Window window, Surface surface, TaskSnapshot snapshot, CharSequence title, int backgroundColor, int statusBarColor, int navigationBarColor, int sysUiVis, int windowFlags, int windowPrivateFlags, - Rect taskBounds) { + Rect taskBounds, int currentOrientation) { mService = service; mHandler = new Handler(mService.mH.getLooper()); mSession = WindowManagerGlobal.getWindowSession(); @@ -228,6 +232,7 @@ class TaskSnapshotSurface implements StartingSurface { mSystemBarBackgroundPainter = new SystemBarBackgroundPainter(windowFlags, windowPrivateFlags, sysUiVis, statusBarColor, navigationBarColor); mStatusBarColor = statusBarColor; + mOrientationOnCreation = currentOrientation; } @Override @@ -394,6 +399,7 @@ class TaskSnapshotSurface implements StartingSurface { static class Window extends BaseIWindow { private TaskSnapshotSurface mOuter; + public void setOuter(TaskSnapshotSurface outer) { mOuter = outer; } @@ -403,6 +409,15 @@ class TaskSnapshotSurface implements StartingSurface { Rect stableInsets, Rect outsets, boolean reportDraw, MergedConfiguration mergedConfiguration, Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar, int displayId) { + if (mergedConfiguration != null && mOuter != null + && mOuter.mOrientationOnCreation + != mergedConfiguration.getMergedConfiguration().orientation) { + + // The orientation of the screen is changing. We better remove the snapshot ASAP as + // we are going to wait on the new window in any case to unfreeze the screen, and + // the starting window is not needed anymore. + sHandler.post(mOuter::remove); + } if (reportDraw) { sHandler.obtainMessage(MSG_REPORT_DRAW, mOuter).sendToTarget(); } diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index aabf2bed1dd4..fe5b7f23d5e0 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -228,7 +228,10 @@ public class WindowAnimator { Slog.wtf(TAG, "Unhandled exception in Window Manager", e); } finally { if (transactionOpen) { - mService.closeSurfaceTransaction(); + + // Do not hold window manager lock while closing the transaction, as this might be + // blocking until the next frame, which can lead to total lock starvation. + mService.closeSurfaceTransaction(false /* withLockHeld */); if (SHOW_TRANSACTIONS) Slog.i(TAG, "<<< CLOSE TRANSACTION animate"); } } diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index 9e3edef768d7..ec7ab23f251b 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -893,10 +893,26 @@ public class WindowManagerService extends IWindowManager.Stub } void closeSurfaceTransaction() { + closeSurfaceTransaction(true /* withLockHeld */); + } + + /** + * Closes a surface transaction. + * + * @param withLockHeld Whether to acquire the window manager while doing so. In some cases + * holding the lock my lead to starvation in WM in case closeTransaction + * blocks and we call it repeatedly, like we do for animations. + */ + void closeSurfaceTransaction(boolean withLockHeld) { synchronized (mWindowMap) { if (mRoot.mSurfaceTraceEnabled) { mRoot.mRemoteEventTrace.closeSurfaceTransaction(); } + if (withLockHeld) { + SurfaceControl.closeTransaction(); + } + } + if (!withLockHeld) { SurfaceControl.closeTransaction(); } } @@ -1270,14 +1286,6 @@ public class WindowManagerService extends IWindowManager.Stub + token + ". Aborting."); return WindowManagerGlobal.ADD_APP_EXITING; } - if (rootType == TYPE_APPLICATION_STARTING - && (attrs.privateFlags & PRIVATE_FLAG_TASK_SNAPSHOT) == 0 - && atoken.firstWindowDrawn) { - // No need for this guy! - if (DEBUG_STARTING_WINDOW || localLOGV) Slog.v( - TAG_WM, "**** NO NEED TO START: " + attrs.getTitle()); - return WindowManagerGlobal.ADD_STARTING_NOT_NEEDED; - } } else if (rootType == TYPE_INPUT_METHOD) { if (token.windowType != TYPE_INPUT_METHOD) { Slog.w(TAG_WM, "Attempted to add input method window with bad token " @@ -5150,7 +5158,8 @@ public class WindowManagerService extends IWindowManager.Stub } break; case NOTIFY_APP_TRANSITION_STARTING: { - mAmInternal.notifyAppTransitionStarting((SparseIntArray) msg.obj); + mAmInternal.notifyAppTransitionStarting((SparseIntArray) msg.obj, + msg.getWhen()); } break; case NOTIFY_APP_TRANSITION_CANCELLED: { diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 73f8d27bc7a6..33cb9081325b 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -1524,7 +1524,10 @@ class WindowStateAnimator { void prepareSurfaceLocked(final boolean recoveringMemory) { final WindowState w = mWin; if (!hasSurface()) { - if (w.mOrientationChanging) { + + // There is no need to wait for an animation change if our window is gone for layout + // already as we'll never be visible. + if (w.mOrientationChanging && w.isGoneForLayoutLw()) { if (DEBUG_ORIENTATION) { Slog.v(TAG, "Orientation change skips hidden " + w); } @@ -1557,13 +1560,11 @@ class WindowStateAnimator { hide("prepareSurfaceLocked"); mWallpaperControllerLocked.hideWallpapers(w); - // If we are waiting for this window to handle an - // orientation change, well, it is hidden, so - // doesn't really matter. Note that this does - // introduce a potential glitch if the window - // becomes unhidden before it has drawn for the - // new orientation. - if (w.mOrientationChanging) { + // If we are waiting for this window to handle an orientation change. If this window is + // really hidden (gone for layout), there is no point in still waiting for it. + // Note that this does introduce a potential glitch if the window becomes unhidden + // before it has drawn for the new orientation. + if (w.mOrientationChanging && w.isGoneForLayoutLw()) { w.mOrientationChanging = false; if (DEBUG_ORIENTATION) Slog.v(TAG, "Orientation change skips hidden " + w); @@ -1630,18 +1631,19 @@ class WindowStateAnimator { displayed = true; } - if (displayed) { - if (w.mOrientationChanging) { - if (!w.isDrawnLw()) { - mAnimator.mBulkUpdateParams &= ~SET_ORIENTATION_CHANGE_COMPLETE; - mAnimator.mLastWindowFreezeSource = w; - if (DEBUG_ORIENTATION) Slog.v(TAG, - "Orientation continue waiting for draw in " + w); - } else { - w.mOrientationChanging = false; - if (DEBUG_ORIENTATION) Slog.v(TAG, "Orientation change complete in " + w); - } + if (w.mOrientationChanging) { + if (!w.isDrawnLw()) { + mAnimator.mBulkUpdateParams &= ~SET_ORIENTATION_CHANGE_COMPLETE; + mAnimator.mLastWindowFreezeSource = w; + if (DEBUG_ORIENTATION) Slog.v(TAG, + "Orientation continue waiting for draw in " + w); + } else { + w.mOrientationChanging = false; + if (DEBUG_ORIENTATION) Slog.v(TAG, "Orientation change complete in " + w); } + } + + if (displayed) { w.mToken.hasVisible = true; } } diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java index 4442bb87ec33..82c862f6a6a8 100644 --- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java +++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java @@ -25,6 +25,7 @@ import static com.android.server.wm.AppTransition.TRANSIT_WALLPAPER_CLOSE; import static com.android.server.wm.AppTransition.TRANSIT_WALLPAPER_INTRA_CLOSE; import static com.android.server.wm.AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN; import static com.android.server.wm.AppTransition.TRANSIT_WALLPAPER_OPEN; +import static com.android.server.wm.AppTransition.isKeyguardGoingAwayTransit; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS; import static com.android.server.wm.WindowManagerDebugConfig.SHOW_LIGHT_TRANSACTIONS; @@ -239,7 +240,7 @@ class WindowSurfacePlacer { if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "**** GOOD TO GO"); int transit = mService.mAppTransition.getAppTransition(); - if (mService.mSkipAppTransitionAnimation) { + if (mService.mSkipAppTransitionAnimation && !isKeyguardGoingAwayTransit(transit)) { transit = AppTransition.TRANSIT_UNSET; } mService.mSkipAppTransitionAnimation = false; @@ -598,42 +599,47 @@ class WindowSurfacePlacer { + ", openingApps=" + openingApps + ", closingApps=" + closingApps); mService.mAnimateWallpaperWithTarget = false; - if (closingAppHasWallpaper && openingAppHasWallpaper) { - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Wallpaper animation!"); - switch (transit) { - case TRANSIT_ACTIVITY_OPEN: - case TRANSIT_TASK_OPEN: - case TRANSIT_TASK_TO_FRONT: - transit = TRANSIT_WALLPAPER_INTRA_OPEN; - break; - case TRANSIT_ACTIVITY_CLOSE: - case TRANSIT_TASK_CLOSE: - case TRANSIT_TASK_TO_BACK: - transit = TRANSIT_WALLPAPER_INTRA_CLOSE; - break; - } - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, - "New transit: " + AppTransition.appTransitionToString(transit)); - } else if (openingCanBeWallpaperTarget && transit == TRANSIT_KEYGUARD_GOING_AWAY) { + if (openingCanBeWallpaperTarget && transit == TRANSIT_KEYGUARD_GOING_AWAY) { transit = TRANSIT_KEYGUARD_GOING_AWAY_ON_WALLPAPER; if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit: " + AppTransition.appTransitionToString(transit)); - } else if (oldWallpaper != null && !mService.mOpeningApps.isEmpty() - && !openingApps.contains(oldWallpaper.mAppToken) - && closingApps.contains(oldWallpaper.mAppToken)) { - // We are transitioning from an activity with a wallpaper to one without. - transit = TRANSIT_WALLPAPER_CLOSE; - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit away from wallpaper: " - + AppTransition.appTransitionToString(transit)); - } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw() && - openingApps.contains(wallpaperTarget.mAppToken)) { - // We are transitioning from an activity without - // a wallpaper to now showing the wallpaper - transit = TRANSIT_WALLPAPER_OPEN; - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit into wallpaper: " - + AppTransition.appTransitionToString(transit)); - } else { - mService.mAnimateWallpaperWithTarget = true; + } + // We never want to change from a Keyguard transit to a non-Keyguard transit, as our logic + // relies on the fact that we always execute a Keyguard transition after preparing one. + else if (!isKeyguardGoingAwayTransit(transit)) { + if (closingAppHasWallpaper && openingAppHasWallpaper) { + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "Wallpaper animation!"); + switch (transit) { + case TRANSIT_ACTIVITY_OPEN: + case TRANSIT_TASK_OPEN: + case TRANSIT_TASK_TO_FRONT: + transit = TRANSIT_WALLPAPER_INTRA_OPEN; + break; + case TRANSIT_ACTIVITY_CLOSE: + case TRANSIT_TASK_CLOSE: + case TRANSIT_TASK_TO_BACK: + transit = TRANSIT_WALLPAPER_INTRA_CLOSE; + break; + } + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, + "New transit: " + AppTransition.appTransitionToString(transit)); + } else if (oldWallpaper != null && !mService.mOpeningApps.isEmpty() + && !openingApps.contains(oldWallpaper.mAppToken) + && closingApps.contains(oldWallpaper.mAppToken)) { + // We are transitioning from an activity with a wallpaper to one without. + transit = TRANSIT_WALLPAPER_CLOSE; + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit away from wallpaper: " + + AppTransition.appTransitionToString(transit)); + } else if (wallpaperTarget != null && wallpaperTarget.isVisibleLw() && + openingApps.contains(wallpaperTarget.mAppToken)) { + // We are transitioning from an activity without + // a wallpaper to now showing the wallpaper + transit = TRANSIT_WALLPAPER_OPEN; + if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "New transit into wallpaper: " + + AppTransition.appTransitionToString(transit)); + } else { + mService.mAnimateWallpaperWithTarget = true; + } } return transit; } diff --git a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java index ec0887489fba..ae9827428179 100644 --- a/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java +++ b/services/tests/notification/src/com/android/server/notification/BuzzBeepBlinkTest.java @@ -20,21 +20,29 @@ import static android.app.Notification.GROUP_ALERT_CHILDREN; import static android.app.Notification.GROUP_ALERT_SUMMARY; import static android.app.NotificationManager.IMPORTANCE_HIGH; -import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertTrue; -import com.android.server.lights.Light; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; +import static org.mockito.Matchers.anyBoolean; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Matchers.anyString; +import static org.mockito.Matchers.argThat; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.app.Notification; import android.app.Notification.Builder; -import android.app.NotificationManager; import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.content.Context; +import android.content.pm.PackageManager; import android.graphics.Color; import android.media.AudioAttributes; import android.media.AudioManager; @@ -42,30 +50,23 @@ import android.net.Uri; import android.os.Handler; import android.os.RemoteException; import android.os.UserHandle; -import android.os.Vibrator; import android.os.VibrationEffect; +import android.os.Vibrator; import android.provider.Settings; import android.service.notification.StatusBarNotification; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; +import com.android.server.lights.Light; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; import org.mockito.ArgumentMatcher; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; -import static org.mockito.Matchers.anyBoolean; -import static org.mockito.Matchers.anyInt; -import static org.mockito.Matchers.anyObject; -import static org.mockito.Matchers.anyString; -import static org.mockito.Matchers.argThat; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.timeout; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - @SmallTest @RunWith(AndroidJUnit4.class) public class BuzzBeepBlinkTest extends NotificationTestCase { @@ -163,6 +164,11 @@ public class BuzzBeepBlinkTest extends NotificationTestCase { true /* noisy */, false /* buzzy*/, false /* lights */); } + private NotificationRecord getInsistentBeepyLeanbackNotification() { + return getLeanbackNotificationRecord(mId, true /* insistent */, false /* once */, + true /* noisy */, false /* buzzy*/, false /* lights */); + } + private NotificationRecord getBuzzyNotification() { return getNotificationRecord(mId, false /* insistent */, false /* once */, false /* noisy */, true /* buzzy*/, false /* lights */); @@ -192,23 +198,30 @@ public class BuzzBeepBlinkTest extends NotificationTestCase { return getNotificationRecord(mId, false /* insistent */, true /* once */, false /* noisy */, true /* buzzy*/, true /* lights */, true /* defaultVibration */, true /* defaultSound */, false /* defaultLights */, - null, Notification.GROUP_ALERT_ALL); + null, Notification.GROUP_ALERT_ALL, false); } private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once, boolean noisy, boolean buzzy, boolean lights) { return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, true, true, true, - null, Notification.GROUP_ALERT_ALL); + null, Notification.GROUP_ALERT_ALL, false); + } + + private NotificationRecord getLeanbackNotificationRecord(int id, boolean insistent, boolean once, + boolean noisy, boolean buzzy, boolean lights) { + return getNotificationRecord(id, insistent, once, noisy, buzzy, lights, true, true, true, + null, Notification.GROUP_ALERT_ALL, true); } private NotificationRecord getBeepyNotificationRecord(String groupKey, int groupAlertBehavior) { return getNotificationRecord(mId, false, false, true, false, false, true, true, true, - groupKey, groupAlertBehavior); + groupKey, groupAlertBehavior, false); } private NotificationRecord getNotificationRecord(int id, boolean insistent, boolean once, boolean noisy, boolean buzzy, boolean lights, boolean defaultVibration, - boolean defaultSound, boolean defaultLights, String groupKey, int groupAlertBehavior) { + boolean defaultSound, boolean defaultLights, String groupKey, int groupAlertBehavior, + boolean isLeanback) { NotificationChannel channel = new NotificationChannel("test", "test", IMPORTANCE_HIGH); final Builder builder = new Builder(getContext()) @@ -257,9 +270,15 @@ public class BuzzBeepBlinkTest extends NotificationTestCase { n.flags |= Notification.FLAG_INSISTENT; } + Context context = spy(getContext()); + PackageManager packageManager = spy(context.getPackageManager()); + when(context.getPackageManager()).thenReturn(packageManager); + when(packageManager.hasSystemFeature(PackageManager.FEATURE_LEANBACK)) + .thenReturn(isLeanback); + StatusBarNotification sbn = new StatusBarNotification(mPkg, mPkg, id, mTag, mUid, mPid, n, mUser, null, System.currentTimeMillis()); - NotificationRecord r = new NotificationRecord(getContext(), sbn, channel); + NotificationRecord r = new NotificationRecord(context, sbn, channel); mService.addNotification(r); return r; } @@ -367,6 +386,15 @@ public class BuzzBeepBlinkTest extends NotificationTestCase { } @Test + public void testNoLeanbackBeep() throws Exception { + NotificationRecord r = getInsistentBeepyLeanbackNotification(); + + mService.buzzBeepBlinkLocked(r); + + verifyNeverBeep(); + } + + @Test public void testNoInterruptionForMin() throws Exception { NotificationRecord r = getBeepyNotification(); r.setImportance(NotificationManager.IMPORTANCE_MIN, "foo"); diff --git a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java index bd6e379cb913..46c536c76d46 100644 --- a/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/notification/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -357,6 +357,43 @@ public class NotificationManagerServiceTest extends NotificationTestCase { } @Test + public void testCancelAllNotificationsMultipleEnqueuedDoesNotCrash() throws Exception { + final StatusBarNotification sbn = generateNotificationRecord(null).sbn; + for (int i = 0; i < 10; i++) { + mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", + sbn.getId(), sbn.getNotification(), sbn.getUserId()); + } + mBinderService.cancelAllNotifications(PKG, sbn.getUserId()); + waitForIdle(); + } + + @Test + public void testCancelGroupSummaryMultipleEnqueuedChildrenDoesNotCrash() throws Exception { + final NotificationRecord parent = generateNotificationRecord( + mTestNotificationChannel, 1, "group1", true); + final NotificationRecord parentAsChild = generateNotificationRecord( + mTestNotificationChannel, 1, "group1", false); + final NotificationRecord child = generateNotificationRecord( + mTestNotificationChannel, 2, "group1", false); + + // fully post parent notification + mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", + parent.sbn.getId(), parent.sbn.getNotification(), parent.sbn.getUserId()); + waitForIdle(); + + // enqueue the child several times + for (int i = 0; i < 10; i++) { + mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", + child.sbn.getId(), child.sbn.getNotification(), child.sbn.getUserId()); + } + // make the parent a child, which will cancel the child notification + mBinderService.enqueueNotificationWithTag(PKG, "opPkg", "tag", + parentAsChild.sbn.getId(), parentAsChild.sbn.getNotification(), + parentAsChild.sbn.getUserId()); + waitForIdle(); + } + + @Test public void testCancelAllNotifications_IgnoreForegroundService() throws Exception { final StatusBarNotification sbn = generateNotificationRecord(null).sbn; sbn.getNotification().flags |= Notification.FLAG_FOREGROUND_SERVICE; diff --git a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java index 65a56327bb24..606088128fc4 100644 --- a/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/wm/AppWindowContainerControllerTests.java @@ -107,7 +107,7 @@ public class AppWindowContainerControllerTests extends WindowTestsBase { createAppWindowController(); controller.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(), android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, - false); + false, false); waitUntilHandlersIdle(); final AppWindowToken atoken = controller.getAppWindowToken(mDisplayContent); assertHasStartingWindow(atoken); @@ -125,7 +125,7 @@ public class AppWindowContainerControllerTests extends WindowTestsBase { createAppWindowController(); controller.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(), android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, - false); + false, false); controller.removeStartingWindow(); waitUntilHandlersIdle(); assertNoStartingWindow(controller.getAppWindowToken(mDisplayContent)); @@ -140,11 +140,11 @@ public class AppWindowContainerControllerTests extends WindowTestsBase { createAppWindowController(); controller1.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(), android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, - false); + false, false); waitUntilHandlersIdle(); controller2.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(), android.R.style.Theme, null, "Test", 0, 0, 0, 0, controller1.mToken.asBinder(), - true, true, false, true, false); + true, true, false, true, false, false); waitUntilHandlersIdle(); assertNoStartingWindow(controller1.getAppWindowToken(mDisplayContent)); assertHasStartingWindow(controller2.getAppWindowToken(mDisplayContent)); @@ -161,11 +161,11 @@ public class AppWindowContainerControllerTests extends WindowTestsBase { // Surprise, ...! Transfer window in the middle of the creation flow. controller2.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(), android.R.style.Theme, null, "Test", 0, 0, 0, 0, controller1.mToken.asBinder(), - true, true, false, true, false); + true, true, false, true, false, false); }); controller1.addStartingWindow(InstrumentationRegistry.getContext().getPackageName(), android.R.style.Theme, null, "Test", 0, 0, 0, 0, null, true, true, false, true, - false); + false, false); waitUntilHandlersIdle(); assertNoStartingWindow(controller1.getAppWindowToken(mDisplayContent)); assertHasStartingWindow(controller2.getAppWindowToken(mDisplayContent)); diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java index e2868d7056b0..4288eac0faa9 100644 --- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java +++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotSurfaceTest.java @@ -62,7 +62,8 @@ public class TaskSnapshotSurfaceTest extends WindowTestsBase { final TaskSnapshot snapshot = new TaskSnapshot(buffer, ORIENTATION_PORTRAIT, contentInsets, false, 1.0f); mSurface = new TaskSnapshotSurface(sWm, new Window(), new Surface(), snapshot, "Test", - Color.WHITE, Color.RED, Color.BLUE, sysuiVis, windowFlags, 0, taskBounds); + Color.WHITE, Color.RED, Color.BLUE, sysuiVis, windowFlags, 0, taskBounds, + ORIENTATION_PORTRAIT); } private void setupSurface(int width, int height) { diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java index 272653f7fc66..4d2f97f2a5fc 100644 --- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java +++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java @@ -664,7 +664,8 @@ public class AppLaunch extends InstrumentationTestCase { if (lineCount == 2 && line.contains(SUCCESS_MESSAGE)) { launchSuccess = true; } - if (launchSuccess && lineCount == 4) { + // Parse TotalTime which is the launch time + if (launchSuccess && lineCount == 5) { String launchSplit[] = line.split(":"); launchTime = launchSplit[1].trim(); } |