diff options
45 files changed, 773 insertions, 326 deletions
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index 24436ea5180b..f40f244e69e5 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -488,7 +488,7 @@ public class AppStandbyController implements AppStandbyInternal { mSystemServicesReady = true; // Offload to handler thread to avoid boot time impact. - mHandler.post(mInjector::updatePowerWhitelistCache); + mHandler.post(AppStandbyController.this::updatePowerWhitelistCache); boolean userFileExists; synchronized (mAppIdleLock) { @@ -1716,6 +1716,14 @@ public class AppStandbyController implements AppStandbyInternal { } } + private void updatePowerWhitelistCache() { + if (mInjector.getBootPhase() < PHASE_SYSTEM_SERVICES_READY) { + return; + } + mInjector.updatePowerWhitelistCache(); + postCheckIdleStates(UserHandle.USER_ALL); + } + private class PackageReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { @@ -2029,10 +2037,7 @@ public class AppStandbyController implements AppStandbyInternal { } } - private void updatePowerWhitelistCache() { - if (mBootPhase < PHASE_SYSTEM_SERVICES_READY) { - return; - } + void updatePowerWhitelistCache() { try { // Don't call out to DeviceIdleController with the lock held. final String[] whitelistedPkgs = @@ -2232,7 +2237,7 @@ public class AppStandbyController implements AppStandbyInternal { break; case PowerManager.ACTION_POWER_SAVE_WHITELIST_CHANGED: if (mSystemServicesReady) { - mHandler.post(mInjector::updatePowerWhitelistCache); + mHandler.post(AppStandbyController.this::updatePowerWhitelistCache); } break; } diff --git a/apex/statsd/aidl/Android.bp b/apex/statsd/aidl/Android.bp index f66cf7c9e23c..04339e67d799 100644 --- a/apex/statsd/aidl/Android.bp +++ b/apex/statsd/aidl/Android.bp @@ -30,7 +30,6 @@ aidl_interface { "android/os/StatsDimensionsValueParcel.aidl", "android/util/StatsEventParcel.aidl", ], - host_supported: true, backend: { java: { enabled: false, // framework-statsd and service-statsd use framework-statsd-aidl-sources diff --git a/cmds/statsd/Android.bp b/cmds/statsd/Android.bp index 1579715727ac..124f815f51f0 100644 --- a/cmds/statsd/Android.bp +++ b/cmds/statsd/Android.bp @@ -171,8 +171,7 @@ cc_library_static { export_generated_headers: ["statslog_statsdtest.h"], shared_libs: [ "libstatssocket", - "libstatspull", - ], + ] } cc_library_static { @@ -186,11 +185,7 @@ cc_library_static { ], shared_libs: [ "libstatssocket", - "libstatspull", - ], - export_shared_lib_headers: [ - "libstatspull", - ], + ] } // ========= diff --git a/core/java/android/app/QueuedWork.java b/core/java/android/app/QueuedWork.java index 82cc2c4daa0b..a1fcf53a2c37 100644 --- a/core/java/android/app/QueuedWork.java +++ b/core/java/android/app/QueuedWork.java @@ -80,7 +80,7 @@ public class QueuedWork { /** Work queued via {@link #queue} */ @GuardedBy("sLock") - private static final LinkedList<Runnable> sWork = new LinkedList<>(); + private static LinkedList<Runnable> sWork = new LinkedList<>(); /** If new work can be delayed or not */ @GuardedBy("sLock") @@ -252,8 +252,8 @@ public class QueuedWork { LinkedList<Runnable> work; synchronized (sLock) { - work = (LinkedList<Runnable>) sWork.clone(); - sWork.clear(); + work = sWork; + sWork = new LinkedList<>(); // Remove all msg-s as all work will be processed now getHandler().removeMessages(QueuedWorkHandler.MSG_RUN); diff --git a/core/java/android/view/ImeFocusController.java b/core/java/android/view/ImeFocusController.java index ad43f9556d8d..92772c1d7a44 100644 --- a/core/java/android/view/ImeFocusController.java +++ b/core/java/android/view/ImeFocusController.java @@ -125,10 +125,10 @@ public final class ImeFocusController { final View viewForWindowFocus = focusedView != null ? focusedView : mViewRootImpl.mView; onViewFocusChanged(viewForWindowFocus, true); - // Starting new input when the next focused view is same as served view but the - // editor is not aligned with the same editor or editor is inactive. - final boolean nextFocusIsServedView = mServedView != null && mServedView == focusedView; - if (nextFocusIsServedView && !immDelegate.isSameEditorAndAcceptingText(focusedView)) { + // Starting new input when the next focused view is same as served view but the currently + // active connection (if any) is not associated with it. + final boolean nextFocusIsServedView = mServedView == viewForWindowFocus; + if (nextFocusIsServedView && !immDelegate.hasActiveConnection(viewForWindowFocus)) { forceFocus = true; } @@ -254,7 +254,7 @@ public final class ImeFocusController { void setCurrentRootView(ViewRootImpl rootView); boolean isCurrentRootView(ViewRootImpl rootView); boolean isRestartOnNextWindowFocus(boolean reset); - boolean isSameEditorAndAcceptingText(View view); + boolean hasActiveConnection(View view); } public View getServedView() { diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 599a7c24e8aa..793d94097862 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -19,8 +19,8 @@ package android.view.inputmethod; import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; import static android.Manifest.permission.WRITE_SECURE_SETTINGS; -import static com.android.internal.inputmethod.StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR; -import static com.android.internal.inputmethod.StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR; +import static com.android.internal.inputmethod.StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION; +import static com.android.internal.inputmethod.StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION; import android.annotation.DrawableRes; import android.annotation.NonNull; @@ -89,6 +89,7 @@ import com.android.internal.view.InputBindResult; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.lang.ref.WeakReference; import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.Collections; @@ -610,14 +611,13 @@ public final class InputMethodManager { @Override public void startInputAsyncOnWindowFocusGain(View focusedView, @SoftInputModeFlags int softInputMode, int windowFlags, boolean forceNewFocus) { - final boolean forceNewFocus1 = forceNewFocus; final int startInputFlags = getStartInputFlags(focusedView, 0); final ImeFocusController controller = getFocusController(); if (controller == null) { return; } - if (controller.checkFocus(forceNewFocus1, false)) { + if (controller.checkFocus(forceNewFocus, false)) { // We need to restart input on the current focus view. This // should be done in conjunction with telling the system service // about the window gaining focus, to help make the transition @@ -633,15 +633,15 @@ public final class InputMethodManager { // we'll just do a window focus gain and call it a day. try { View servedView = controller.getServedView(); - boolean nextFocusSameEditor = servedView != null && servedView == focusedView - && isSameEditorAndAcceptingText(focusedView); + boolean nextFocusHasConnection = servedView != null && servedView == focusedView + && hasActiveConnection(focusedView); if (DEBUG) { Log.v(TAG, "Reporting focus gain, without startInput" - + ", nextFocusIsServedView=" + nextFocusSameEditor); + + ", nextFocusIsServedView=" + nextFocusHasConnection); } final int startInputReason = - nextFocusSameEditor ? WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR - : WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR; + nextFocusHasConnection ? WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION + : WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION; mService.startInputOrWindowGainedFocus( startInputReason, mClient, focusedView.getWindowToken(), startInputFlags, softInputMode, @@ -701,33 +701,24 @@ public final class InputMethodManager { } /** - * For {@link ImeFocusController} to check if the given focused view aligns with the same - * editor and the editor is active to accept the text input. + * Checks whether the active input connection (if any) is for the given view. * - * TODO(b/160968797): Remove this method and move mCurrentTextBoxAttritube to + * TODO(b/160968797): Remove this method and move mServedInputConnectionWrapper to * ImeFocusController. - * In the long-term, we should make mCurrentTextBoxAtrtribue as per-window base instance, - * so that we we can directly check if the current focused view aligned with the same editor - * in the window without using this checking. * - * Note that this method is only use for fixing start new input may ignored issue + * Note that this method is only intended for restarting input after focus gain * (e.g. b/160391516), DO NOT leverage this method to do another check. */ - public boolean isSameEditorAndAcceptingText(View view) { + @Override + public boolean hasActiveConnection(View view) { synchronized (mH) { - if (!hasServedByInputMethodLocked(view) || mCurrentTextBoxAttribute == null) { + if (!hasServedByInputMethodLocked(view)) { return false; } - final EditorInfo ic = mCurrentTextBoxAttribute; - // This sameEditor checking is based on using object hash comparison to check if - // some fields of the current EditorInfo (e.g. autoFillId, OpPackageName) the - // hash code is same as the given focused view. - final boolean sameEditor = view.onCheckIsTextEditor() && view.getId() == ic.fieldId - && view.getAutofillId() == ic.autofillId - && view.getContext().getOpPackageName() == ic.packageName; - return sameEditor && mServedInputConnectionWrapper != null - && mServedInputConnectionWrapper.isActive(); + return mServedInputConnectionWrapper != null + && mServedInputConnectionWrapper.isActive() + && mServedInputConnectionWrapper.mServedView.get() == view; } } } @@ -980,11 +971,13 @@ public final class InputMethodManager { private static class ControlledInputConnectionWrapper extends IInputConnectionWrapper { private final InputMethodManager mParentInputMethodManager; + private final WeakReference<View> mServedView; - public ControlledInputConnectionWrapper(final Looper mainLooper, final InputConnection conn, - final InputMethodManager inputMethodManager) { + ControlledInputConnectionWrapper(Looper mainLooper, InputConnection conn, + InputMethodManager inputMethodManager, View servedView) { super(mainLooper, conn); mParentInputMethodManager = inputMethodManager; + mServedView = new WeakReference<>(servedView); } @Override @@ -1007,6 +1000,7 @@ public final class InputMethodManager { + "connection=" + getInputConnection() + " finished=" + isFinished() + " mParentInputMethodManager.mActive=" + mParentInputMethodManager.mActive + + " mServedView=" + mServedView.get() + "}"; } } @@ -1187,7 +1181,8 @@ public final class InputMethodManager { mMainLooper = looper; mH = new H(looper); mDisplayId = displayId; - mIInputContext = new ControlledInputConnectionWrapper(looper, mDummyInputConnection, this); + mIInputContext = new ControlledInputConnectionWrapper(looper, mDummyInputConnection, this, + null); } /** @@ -1970,7 +1965,7 @@ public final class InputMethodManager { icHandler = ic.getHandler(); } servedContext = new ControlledInputConnectionWrapper( - icHandler != null ? icHandler.getLooper() : vh.getLooper(), ic, this); + icHandler != null ? icHandler.getLooper() : vh.getLooper(), ic, this, view); } else { servedContext = null; missingMethodFlags = 0; diff --git a/core/java/android/window/DisplayAreaOrganizer.java b/core/java/android/window/DisplayAreaOrganizer.java index 3daf6d371450..f035d36a0f71 100644 --- a/core/java/android/window/DisplayAreaOrganizer.java +++ b/core/java/android/window/DisplayAreaOrganizer.java @@ -68,6 +68,14 @@ public class DisplayAreaOrganizer extends WindowOrganizer { public static final int FEATURE_WINDOWED_MAGNIFICATION = FEATURE_SYSTEM_FIRST + 4; /** + * Display area that can be magnified in + * {@link Settings.Secure.ACCESSIBILITY_MAGNIFICATION_MODE_FULLSCREEN}. This is different from + * {@link #FEATURE_WINDOWED_MAGNIFICATION} that the whole display will be magnified. + * @hide + */ + public static final int FEATURE_FULLSCREEN_MAGNIFICATION = FEATURE_SYSTEM_FIRST + 5; + + /** * The last boundary of display area for system features */ public static final int FEATURE_SYSTEM_LAST = 10_000; diff --git a/core/java/com/android/internal/inputmethod/InputMethodDebug.java b/core/java/com/android/internal/inputmethod/InputMethodDebug.java index 085cdfcf9462..37f68233db53 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodDebug.java +++ b/core/java/com/android/internal/inputmethod/InputMethodDebug.java @@ -46,10 +46,10 @@ public final class InputMethodDebug { return "UNSPECIFIED"; case StartInputReason.WINDOW_FOCUS_GAIN: return "WINDOW_FOCUS_GAIN"; - case StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR: - return "WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR"; - case StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR: - return "WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR"; + case StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION: + return "WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION"; + case StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION: + return "WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION"; case StartInputReason.APP_CALLED_RESTART_INPUT_API: return "APP_CALLED_RESTART_INPUT_API"; case StartInputReason.CHECK_FOCUS: diff --git a/core/java/com/android/internal/inputmethod/StartInputReason.java b/core/java/com/android/internal/inputmethod/StartInputReason.java index 946ce858c12d..2ba708dd9312 100644 --- a/core/java/com/android/internal/inputmethod/StartInputReason.java +++ b/core/java/com/android/internal/inputmethod/StartInputReason.java @@ -30,8 +30,8 @@ import java.lang.annotation.Retention; @IntDef(value = { StartInputReason.UNSPECIFIED, StartInputReason.WINDOW_FOCUS_GAIN, - StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR, - StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR, + StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION, + StartInputReason.WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION, StartInputReason.APP_CALLED_RESTART_INPUT_API, StartInputReason.CHECK_FOCUS, StartInputReason.BOUND_TO_IMMS, @@ -54,13 +54,13 @@ public @interface StartInputReason { * view and its input connection remains. {@link android.view.inputmethod.InputMethodManager} * just reports this window focus change event to sync IME input target for system. */ - int WINDOW_FOCUS_GAIN_REPORT_WITH_SAME_EDITOR = 2; + int WINDOW_FOCUS_GAIN_REPORT_WITH_CONNECTION = 2; /** * {@link android.view.Window} gained focus but there is no {@link android.view.View} that is * eligible to have IME focus. {@link android.view.inputmethod.InputMethodManager} just reports * this window focus change event for logging. */ - int WINDOW_FOCUS_GAIN_REPORT_WITHOUT_EDITOR = 3; + int WINDOW_FOCUS_GAIN_REPORT_WITHOUT_CONNECTION = 3; /** * {@link android.view.inputmethod.InputMethodManager#restartInput(android.view.View)} is * either explicitly called by the application or indirectly called by some Framework class diff --git a/libs/WindowManager/Shell/res/layout/pip_menu_activity.xml b/libs/WindowManager/Shell/res/layout/pip_menu_activity.xml index 18064b547d4d..2e0a5e09e34f 100644 --- a/libs/WindowManager/Shell/res/layout/pip_menu_activity.xml +++ b/libs/WindowManager/Shell/res/layout/pip_menu_activity.xml @@ -62,25 +62,30 @@ </FrameLayout> </FrameLayout> - <ImageButton - android:id="@+id/settings" - android:layout_width="@dimen/pip_action_size" - android:layout_height="@dimen/pip_action_size" - android:layout_gravity="top|start" - android:padding="@dimen/pip_action_padding" - android:contentDescription="@string/pip_phone_settings" - android:src="@drawable/pip_ic_settings" - android:background="?android:selectableItemBackgroundBorderless" /> - - <ImageButton - android:id="@+id/dismiss" - android:layout_width="@dimen/pip_action_size" - android:layout_height="@dimen/pip_action_size" + <LinearLayout + android:id="@+id/top_end_container" android:layout_gravity="top|end" - android:padding="@dimen/pip_action_padding" - android:contentDescription="@string/pip_phone_close" - android:src="@drawable/pip_ic_close_white" - android:background="?android:selectableItemBackgroundBorderless" /> + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal"> + <ImageButton + android:id="@+id/settings" + android:layout_width="@dimen/pip_action_size" + android:layout_height="@dimen/pip_action_size" + android:padding="@dimen/pip_action_padding" + android:contentDescription="@string/pip_phone_settings" + android:src="@drawable/pip_ic_settings" + android:background="?android:selectableItemBackgroundBorderless" /> + + <ImageButton + android:id="@+id/dismiss" + android:layout_width="@dimen/pip_action_size" + android:layout_height="@dimen/pip_action_size" + android:padding="@dimen/pip_action_padding" + android:contentDescription="@string/pip_phone_close" + android:src="@drawable/pip_ic_close_white" + android:background="?android:selectableItemBackgroundBorderless" /> + </LinearLayout> <!--TODO (b/156917828): Add content description for a11y purposes?--> <ImageButton @@ -89,6 +94,7 @@ android:layout_height="@dimen/pip_resize_handle_size" android:layout_gravity="top|start" android:layout_margin="@dimen/pip_resize_handle_margin" + android:padding="@dimen/pip_resize_handle_padding" android:src="@drawable/pip_resize_handle" android:background="?android:selectableItemBackgroundBorderless" /> </FrameLayout> diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index 48c3cad596f1..1c1217681b9f 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -52,7 +52,8 @@ <!-- The touchable/draggable edge size for PIP resize. --> <dimen name="pip_resize_edge_size">48dp</dimen> - <!-- PIP Resize handle size and margin. --> + <!-- PIP Resize handle size, margin and padding. --> <dimen name="pip_resize_handle_size">12dp</dimen> <dimen name="pip_resize_handle_margin">4dp</dimen> + <dimen name="pip_resize_handle_padding">0dp</dimen> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java index c6d128631930..87489262a420 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java @@ -712,22 +712,6 @@ class Bubble implements BubbleViewProvider { return Objects.hash(mKey); } - @Override - public void logUIEvent(int bubbleCount, int action, float normalX, float normalY, int index) { - SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED, - mPackageName, - mChannelId, - mNotificationId, - index, - bubbleCount, - action, - normalX, - normalY, - showInShade(), - false /* isOngoing (unused) */, - false /* isAppForeground (unused) */); - } - @Nullable private static String getTitle(@NonNull final NotificationEntry e) { final CharSequence titleCharSeq = e.getSbn().getNotification().extras.getCharSequence( diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java index d2dc506c8e5c..10f4385ed443 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java @@ -34,6 +34,7 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.systemui.R; import com.android.systemui.bubbles.BubbleController.DismissReason; +import com.android.systemui.shared.system.SysUiStatsLog; import com.android.systemui.statusbar.notification.NotificationEntryManager; import com.android.systemui.statusbar.notification.collection.NotificationEntry; @@ -59,7 +60,7 @@ import javax.inject.Singleton; @Singleton public class BubbleData { - private BubbleLogger mLogger = new BubbleLoggerImpl(); + private BubbleLoggerImpl mLogger = new BubbleLoggerImpl(); private static final String TAG = TAG_WITH_CLASS_NAME ? "BubbleData" : TAG_BUBBLES; @@ -611,6 +612,30 @@ public class BubbleData { } /** + * Logs the bubble UI event. + * + * @param provider The bubble view provider that is being interacted on. Null value indicates + * that the user interaction is not specific to one bubble. + * @param action The user interaction enum + * @param packageName SystemUI package + * @param bubbleCount Number of bubbles in the stack + * @param bubbleIndex Index of bubble in the stack + * @param normalX Normalized x position of the stack + * @param normalY Normalized y position of the stack + */ + void logBubbleEvent(@Nullable BubbleViewProvider provider, int action, String packageName, + int bubbleCount, int bubbleIndex, float normalX, float normalY) { + if (provider == null) { + mLogger.logStackUiChanged(packageName, action, bubbleCount, normalX, normalY); + } else if (provider.getKey().equals(BubbleOverflow.KEY)) { + mLogger.logShowOverflow(packageName, action, bubbleCount, normalX, normalY); + } else { + mLogger.logBubbleUiChanged((Bubble) provider, packageName, action, bubbleCount, normalX, + normalY, bubbleIndex); + } + } + + /** * Requests a change to the expanded state. * * @param shouldExpand the new requested state diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java index c1dd8c36ff6f..d702cc4e0062 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleLoggerImpl.java @@ -17,6 +17,7 @@ package com.android.systemui.bubbles; import com.android.internal.logging.UiEventLoggerImpl; +import com.android.systemui.shared.system.SysUiStatsLog; /** * Implementation of UiEventLogger for logging bubble UI events. @@ -64,4 +65,52 @@ public class BubbleLoggerImpl extends UiEventLoggerImpl implements BubbleLogger log(b, Event.BUBBLE_OVERFLOW_ADD_USER_GESTURE); } } + + void logStackUiChanged(String packageName, int action, int bubbleCount, float normalX, + float normalY) { + SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED, + packageName, + null /* notification channel */, + 0 /* notification ID */, + 0 /* bubble position */, + bubbleCount, + action, + normalX, + normalY, + false /* unread bubble */, + false /* on-going bubble */, + false /* isAppForeground (unused) */); + } + + void logShowOverflow(String packageName, int action, int bubbleCount, float normalX, + float normalY) { + SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED, + packageName, + BubbleOverflow.KEY /* notification channel */, + 0 /* notification ID */, + 0 /* bubble position */, + bubbleCount, + action, + normalX, + normalY, + false /* unread bubble */, + false /* on-going bubble */, + false /* isAppForeground (unused) */); + } + + void logBubbleUiChanged(Bubble bubble, String packageName, int action, int bubbleCount, + float normalX, float normalY, int index) { + SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED, + packageName, + bubble.getChannelId() /* notification channel */, + bubble.getNotificationId() /* notification ID */, + index, + bubbleCount, + action, + normalX, + normalY, + bubble.showInShade() /* isUnread */, + false /* isOngoing (unused) */, + false /* isAppForeground (unused) */); + } }
\ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java index dadcb3a3a7c4..bb9d1095a37a 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java @@ -163,12 +163,6 @@ public class BubbleOverflow implements BubbleViewProvider { } @Override - public void logUIEvent(int bubbleCount, int action, float normalX, float normalY, - int index) { - // TODO(b/149133814) Log overflow UI events. - } - - @Override public View getIconView() { return mOverflowBtn; } diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java index b328f62e067e..f02945ef843a 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java @@ -2891,23 +2891,13 @@ public class BubbleStackView extends FrameLayout * @param action the user interaction enum. */ private void logBubbleEvent(@Nullable BubbleViewProvider provider, int action) { - if (provider == null || provider.getKey().equals(BubbleOverflow.KEY)) { - SysUiStatsLog.write(SysUiStatsLog.BUBBLE_UI_CHANGED, - mContext.getApplicationInfo().packageName, - provider == null ? null : BubbleOverflow.KEY /* notification channel */, - 0 /* notification ID */, - 0 /* bubble position */, - getBubbleCount(), - action, - getNormalizedXPosition(), - getNormalizedYPosition(), - false /* unread bubble */, - false /* on-going bubble */, - false /* isAppForeground (unused) */); - return; - } - provider.logUIEvent(getBubbleCount(), action, getNormalizedXPosition(), - getNormalizedYPosition(), getBubbleIndex(provider)); + mBubbleData.logBubbleEvent(provider, + action, + mContext.getApplicationInfo().packageName, + getBubbleCount(), + getBubbleIndex(provider), + getNormalizedXPosition(), + getNormalizedYPosition()); } /** diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java index ca3e2e27fa9a..f1a01be48397 100644 --- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java +++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java @@ -32,8 +32,6 @@ interface BubbleViewProvider { @Nullable View getIconView(); - void logUIEvent(int bubbleCount, int action, float normalX, float normalY, int index); - String getKey(); Bitmap getBadgedImage(); diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java index 6b8fcd562e6f..e5cc1384efa4 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIRootComponent.java @@ -29,6 +29,7 @@ import com.android.systemui.dump.DumpManager; import com.android.systemui.fragments.FragmentService; import com.android.systemui.keyguard.KeyguardSliceProvider; import com.android.systemui.onehanded.dagger.OneHandedModule; +import com.android.systemui.pip.phone.PipMenuActivity; import com.android.systemui.pip.phone.dagger.PipModule; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.util.InjectionInflationController; @@ -121,4 +122,9 @@ public interface SystemUIRootComponent { * Member injection into the supplied argument. */ void inject(KeyguardSliceProvider keyguardSliceProvider); + + /** + * Member injection into the supplied argument. + */ + void inject(PipMenuActivity pipMenuActivity); } diff --git a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java index 0d6b522046f7..35e56ee87967 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java +++ b/packages/SystemUI/src/com/android/systemui/pip/PipTaskOrganizer.java @@ -178,6 +178,9 @@ public class PipTaskOrganizer extends TaskOrganizer implements Rect startBounds = (Rect) args.arg2; Rect toBounds = (Rect) args.arg3; userResizePip(startBounds, toBounds); + if (updateBoundsCallback != null) { + updateBoundsCallback.accept(toBounds); + } break; } } 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 ad5a13e78cb4..d6f3e163ad70 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivity.java @@ -76,12 +76,15 @@ import android.widget.ImageButton; import android.widget.LinearLayout; import com.android.systemui.Interpolators; +import com.android.systemui.SystemUIFactory; import com.android.wm.shell.R; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import javax.inject.Inject; + /** * Translucent activity that gets started on top of a task in PIP to allow the user to control it. */ @@ -100,6 +103,7 @@ public class PipMenuActivity extends Activity { public static final int MESSAGE_POINTER_EVENT = 7; public static final int MESSAGE_MENU_EXPANDED = 8; public static final int MESSAGE_FADE_OUT_MENU = 9; + public static final int MESSAGE_UPDATE_MENU_LAYOUT = 10; private static final int INITIAL_DISMISS_DELAY = 3500; private static final int POST_INTERACTION_DISMISS_DELAY = 2000; @@ -129,8 +133,12 @@ public class PipMenuActivity extends Activity { private View mSettingsButton; private View mDismissButton; private View mResizeHandle; + private View mTopEndContainer; private int mBetweenActionPaddingLand; + @Inject + PipMenuIconsAlgorithm mPipMenuIconsAlgorithm; + private AnimatorSet mMenuContainerAnimator; private ValueAnimator.AnimatorUpdateListener mMenuBgUpdateListener = @@ -193,6 +201,11 @@ public class PipMenuActivity extends Activity { fadeOutMenu(); break; } + case MESSAGE_UPDATE_MENU_LAYOUT: { + final Rect bounds = (Rect) msg.obj; + mPipMenuIconsAlgorithm.onBoundsChanged(bounds); + break; + } } } }; @@ -208,6 +221,9 @@ public class PipMenuActivity extends Activity { getWindow().addFlags(LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH); super.onCreate(savedInstanceState); + + SystemUIFactory.getInstance().getRootComponent().inject(this); + setContentView(R.layout.pip_menu_activity); mAccessibilityManager = getSystemService(AccessibilityManager.class); @@ -217,6 +233,7 @@ public class PipMenuActivity extends Activity { mViewRoot.setBackground(mBackgroundDrawable); mMenuContainer = findViewById(R.id.menu_container); mMenuContainer.setAlpha(0); + mTopEndContainer = findViewById(R.id.top_end_container); mSettingsButton = findViewById(R.id.settings); mSettingsButton.setAlpha(0); mSettingsButton.setOnClickListener((v) -> { @@ -238,6 +255,8 @@ public class PipMenuActivity extends Activity { mBetweenActionPaddingLand = getResources().getDimensionPixelSize( R.dimen.pip_between_action_padding_land); + mPipMenuIconsAlgorithm.bindViews((ViewGroup) mViewRoot, (ViewGroup) mTopEndContainer, + mResizeHandle, mSettingsButton, mDismissButton); updateFromIntent(getIntent()); setTitle(R.string.pip_menu_title); setDisablePreviewScreenshots(true); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java index 87e66fdc375c..267c5eacd139 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuActivityController.java @@ -574,6 +574,22 @@ public class PipMenuActivityController { } } + /** + * Tell the PIP Menu to recalculate its layout given its current position on the display. + */ + public void updateMenuLayout(Rect bounds) { + if (mToActivityMessenger != null) { + Message m = Message.obtain(); + m.what = PipMenuActivity.MESSAGE_UPDATE_MENU_LAYOUT; + m.obj = bounds; + try { + mToActivityMessenger.send(m); + } catch (RemoteException e) { + Log.e(TAG, "Could not dispatch touch event", e); + } + } + } + public void dump(PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.println(prefix + TAG); diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuIconsAlgorithm.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuIconsAlgorithm.java new file mode 100644 index 000000000000..69a04d8d3e22 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMenuIconsAlgorithm.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.pip.phone; + +import android.content.Context; +import android.graphics.Rect; +import android.util.Log; +import android.view.Gravity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import javax.inject.Inject; + +/** + * Helper class to calculate and place the menu icons on the PIP Menu. + */ +public class PipMenuIconsAlgorithm { + + private static final String TAG = "PipMenuIconsAlgorithm"; + + private boolean mFinishedLayout = false; + protected ViewGroup mViewRoot; + protected ViewGroup mTopEndContainer; + protected View mDragHandle; + protected View mSettingsButton; + protected View mDismissButton; + + @Inject + public PipMenuIconsAlgorithm(Context context) { + } + + /** + * Bind the necessary views. + */ + public void bindViews(ViewGroup viewRoot, ViewGroup topEndContainer, View dragHandle, + View settingsButton, View dismissButton) { + mViewRoot = viewRoot; + mTopEndContainer = topEndContainer; + mDragHandle = dragHandle; + mSettingsButton = settingsButton; + mDismissButton = dismissButton; + } + + + /** + * Updates the position of the drag handle based on where the PIP window is on the screen. + */ + public void onBoundsChanged(Rect bounds) { + if (mViewRoot == null || mTopEndContainer == null || mDragHandle == null + || mSettingsButton == null || mDismissButton == null) { + Log.e(TAG, "One if the required views is null."); + } + + //We only need to calculate the layout once since it does not change. + if (!mFinishedLayout) { + mTopEndContainer.removeView(mSettingsButton); + mViewRoot.addView(mSettingsButton); + + setLayoutGravity(mDragHandle, Gravity.START | Gravity.TOP); + setLayoutGravity(mSettingsButton, Gravity.START | Gravity.TOP); + mFinishedLayout = true; + } + } + + /** + * Set the gravity on the given view. + */ + protected static void setLayoutGravity(View v, int gravity) { + if (v.getLayoutParams() instanceof FrameLayout.LayoutParams) { + FrameLayout.LayoutParams params = (FrameLayout.LayoutParams) v.getLayoutParams(); + params.gravity = gravity; + v.setLayoutParams(params); + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java index 9f0b1de21b52..ca3ef2465498 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java @@ -127,7 +127,10 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, new PhysicsAnimator.SpringConfig( SpringForce.STIFFNESS_LOW, SpringForce.DAMPING_RATIO_LOW_BOUNCY); - private final Consumer<Rect> mUpdateBoundsCallback = mBounds::set; + private final Consumer<Rect> mUpdateBoundsCallback = (Rect newBounds) -> { + mMenuController.updateMenuLayout(newBounds); + mBounds.set(newBounds); + }; /** * Whether we're springing to the touch event location (vs. moving it to that position @@ -248,7 +251,10 @@ public class PipMotionHelper implements PipAppOpsListener.Callback, mBounds.set(toBounds); } else { mTemporaryBounds.set(toBounds); - mPipTaskOrganizer.scheduleUserResizePip(mBounds, mTemporaryBounds, null); + mPipTaskOrganizer.scheduleUserResizePip(mBounds, mTemporaryBounds, + (Rect newBounds) -> { + mMenuController.updateMenuLayout(newBounds); + }); } } else { // If PIP is 'catching up' after being stuck in the dismiss target, update the animation diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java index f4553fd3d716..bd9ddc57ca19 100644 --- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java +++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java @@ -348,7 +348,7 @@ public class PipTouchHandler { } private boolean shouldShowResizeHandle() { - return !mPipBoundsHandler.hasSaveReentryBounds(); + return false; } public void setTouchGesture(PipTouchGesture gesture) { diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index ea047888caff..d6557f6410ec 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -5288,6 +5288,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @Override public void onTempPowerSaveWhitelistChange(int appId, boolean added) { synchronized (mUidRulesFirstLock) { + if (!mSystemReady) { + return; + } mLogger.tempPowerSaveWlChanged(appId, added); if (added) { mPowerSaveTempWhitelistAppIds.put(appId, true); diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java index 6e48cbbb3b5b..651eafd77fe7 100644 --- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java +++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java @@ -399,14 +399,6 @@ public interface WindowManagerPolicy extends WindowManagerPolicyConstants { return false; } - /** - * Returns true if the window has a letterbox and any part of that letterbox overlaps with - * the given {@code rect}. - */ - default boolean isLetterboxedOverlappingWith(Rect rect) { - return false; - } - /** @return the current windowing mode of this window. */ int getWindowingMode(); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index bec0ce944e3f..a3acc1d90862 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -658,6 +658,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // TODO: Have a WindowContainer state for tracking exiting/deferred removal. boolean mIsExiting; + // Force an app transition to be ran in the case the visibility of the app did not change. + // We use this for the case of moving a Root Task to the back with multiple activities, and the + // top activity enters PIP; the bottom activity's visibility stays the same, but we need to + // run the transition. + boolean mRequestForceTransition; boolean mEnteringAnimation; @@ -1394,6 +1399,13 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } /** + * @see Letterbox#notIntersectsOrFullyContains(Rect) + */ + boolean letterboxNotIntersectsOrFullyContains(Rect rect) { + return mLetterbox == null || mLetterbox.notIntersectsOrFullyContains(rect); + } + + /** * @return {@code true} if there is a letterbox and any part of that letterbox overlaps with * the given {@code rect}. */ @@ -4199,6 +4211,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (mUseTransferredAnimation) { return false; } + // If it was set to true, reset the last request to force the transition. + mRequestForceTransition = false; return super.applyAnimation(lp, transit, enter, isVoiceInteraction, sources); } @@ -4342,7 +4356,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A // transition animation // * or this is an opening app and windows are being replaced (e.g. freeform window to // normal window). - return isVisible() != visible || (!isVisible() && mIsExiting) + return isVisible() != visible || mRequestForceTransition || (!isVisible() && mIsExiting) || (visible && forAllWindows(WindowState::waitingForReplacement, true)); } diff --git a/services/core/java/com/android/server/wm/ActivityStack.java b/services/core/java/com/android/server/wm/ActivityStack.java index 686e016f1d35..9f0bf052ecad 100644 --- a/services/core/java/com/android/server/wm/ActivityStack.java +++ b/services/core/java/com/android/server/wm/ActivityStack.java @@ -2489,15 +2489,13 @@ class ActivityStack extends Task { return true; } + mRootWindowContainer.ensureVisibilityAndConfig(null /* starting */, + getDisplay().mDisplayId, false /* markFrozenIfConfigChanged */, + false /* deferResume */); + ActivityRecord topActivity = getDisplayArea().topRunningActivity(); ActivityStack topStack = topActivity.getRootTask(); if (topStack != null && topStack != this && topActivity.isState(RESUMED)) { - // The new top activity is already resumed, so there's a good chance that nothing will - // get resumed below. So, update visibility now in case the transition is closed - // prematurely. - mRootWindowContainer.ensureVisibilityAndConfig(null /* starting */, - getDisplay().mDisplayId, false /* markFrozenIfConfigChanged */, - false /* deferResume */); // Usually resuming a top activity triggers the next app transition, but nothing's got // resumed in this case, so we need to execute it explicitly. getDisplay().mDisplayContent.executeAppTransition(); diff --git a/services/core/java/com/android/server/wm/BarController.java b/services/core/java/com/android/server/wm/BarController.java index 123fb6c9d8e3..c1447553ba31 100644 --- a/services/core/java/com/android/server/wm/BarController.java +++ b/services/core/java/com/android/server/wm/BarController.java @@ -19,6 +19,7 @@ package com.android.server.wm; import static com.android.server.wm.BarControllerProto.STATE; import static com.android.server.wm.BarControllerProto.TRANSIENT_STATE; +import android.annotation.NonNull; import android.app.StatusBarManager; import android.graphics.Rect; import android.os.Handler; @@ -169,13 +170,23 @@ public class BarController { return vis; } + private Rect getContentFrame(@NonNull WindowState win) { + final Rect rotatedContentFrame = win.mToken.getFixedRotationBarContentFrame(mWindowType); + return rotatedContentFrame != null ? rotatedContentFrame : mContentFrame; + } + + boolean isLightAppearanceAllowed(WindowState win) { + if (win == null) { + return true; + } + return !win.isLetterboxedOverlappingWith(getContentFrame(win)); + } + boolean isTransparentAllowed(WindowState win) { if (win == null) { return true; } - final Rect rotatedContentFrame = win.mToken.getFixedRotationBarContentFrame(mWindowType); - final Rect contentFrame = rotatedContentFrame != null ? rotatedContentFrame : mContentFrame; - return !win.isLetterboxedOverlappingWith(contentFrame); + return win.letterboxNotIntersectsOrFullyContains(getContentFrame(win)); } boolean setBarShowingLw(final boolean show) { diff --git a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java index 59c32045000b..33eb3ce57373 100644 --- a/services/core/java/com/android/server/wm/DisplayAreaPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayAreaPolicy.java @@ -17,12 +17,19 @@ package com.android.server.wm; import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; +import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; +import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL; import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; +import static android.window.DisplayAreaOrganizer.FEATURE_FULLSCREEN_MAGNIFICATION; import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED; import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION; +import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature; +import static com.android.server.wm.DisplayAreaPolicyBuilder.HierarchyBuilder; + import android.content.res.Resources; import android.text.TextUtils; @@ -81,21 +88,27 @@ public abstract class DisplayAreaPolicy { // Define the features that will be supported under the root of the whole logical // display. The policy will build the DisplayArea hierarchy based on this. - DisplayAreaPolicyBuilder.HierarchyBuilder rootHierarchy = - new DisplayAreaPolicyBuilder.HierarchyBuilder(root) - .addFeature(new DisplayAreaPolicyBuilder.Feature.Builder(wmService.mPolicy, - "WindowedMagnification", FEATURE_WINDOWED_MAGNIFICATION) + HierarchyBuilder rootHierarchy = new HierarchyBuilder(root) + .addFeature(new Feature.Builder(wmService.mPolicy, "WindowedMagnification", + FEATURE_WINDOWED_MAGNIFICATION) .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY) .except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY) // Make the DA dimmable so that the magnify window also mirrors the dim // layer .setNewDisplayAreaSupplier(DisplayArea.Dimmable::new) .build()) - .addFeature(new DisplayAreaPolicyBuilder.Feature.Builder(wmService.mPolicy, - "OneHanded", FEATURE_ONE_HANDED) + .addFeature(new Feature.Builder(wmService.mPolicy, "OneHanded", + FEATURE_ONE_HANDED) .all() .except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL) .build()) + .addFeature(new Feature.Builder(wmService.mPolicy, "FullscreenMagnification", + FEATURE_FULLSCREEN_MAGNIFICATION) + .all() + .except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, TYPE_INPUT_METHOD, + TYPE_INPUT_METHOD_DIALOG, TYPE_MAGNIFICATION_OVERLAY, + TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL) + .build()) .setImeContainer(imeContainer) .setTaskDisplayAreas(tdaList); diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java index 82277511ef79..53f16a7241fc 100644 --- a/services/core/java/com/android/server/wm/DisplayPolicy.java +++ b/services/core/java/com/android/server/wm/DisplayPolicy.java @@ -3428,7 +3428,7 @@ public class DisplayPolicy { // keys, we let it keep controlling the visibility. final boolean lastFocusCanReceiveKeys = (mLastFocusedWindow != null && mLastFocusedWindow.canReceiveKeys()); - winCandidate = isKeyguardShowing() ? mNotificationShade + winCandidate = isKeyguardShowing() && !isKeyguardOccluded() ? mNotificationShade : lastFocusCanReceiveKeys ? mLastFocusedWindow : mTopFullscreenOpaqueWindowState; if (winCandidate == null) { @@ -3573,17 +3573,22 @@ public class DisplayPolicy { WindowState opaqueOrDimming) { final boolean onKeyguard = isKeyguardShowing() && !isKeyguardOccluded(); final WindowState statusColorWin = onKeyguard ? mNotificationShade : opaqueOrDimming; - if (statusColorWin != null && (statusColorWin == opaque || onKeyguard)) { - // If the top fullscreen-or-dimming window is also the top fullscreen, respect - // its light flag. - appearance &= ~APPEARANCE_LIGHT_STATUS_BARS; - final int legacyAppearance = InsetsFlags.getAppearance( - PolicyControl.getSystemUiVisibility(statusColorWin, null)); - appearance |= (statusColorWin.mAttrs.insetsFlags.appearance | legacyAppearance) - & APPEARANCE_LIGHT_STATUS_BARS; - } else if (statusColorWin != null && statusColorWin.isDimming()) { - // Otherwise if it's dimming, clear the light flag. - appearance &= ~APPEARANCE_LIGHT_STATUS_BARS; + if (statusColorWin != null) { + if (statusColorWin == opaque || onKeyguard) { + // If the top fullscreen-or-dimming window is also the top fullscreen, respect + // its light flag. + appearance &= ~APPEARANCE_LIGHT_STATUS_BARS; + final int legacyAppearance = InsetsFlags.getAppearance( + PolicyControl.getSystemUiVisibility(statusColorWin, null)); + appearance |= (statusColorWin.mAttrs.insetsFlags.appearance | legacyAppearance) + & APPEARANCE_LIGHT_STATUS_BARS; + } else if (statusColorWin.isDimming()) { + // Otherwise if it's dimming, clear the light flag. + appearance &= ~APPEARANCE_LIGHT_STATUS_BARS; + } + if (!mStatusBarController.isLightAppearanceAllowed(statusColorWin)) { + appearance &= ~APPEARANCE_LIGHT_STATUS_BARS; + } } return appearance; } @@ -3648,8 +3653,7 @@ public class DisplayPolicy { return vis; } - @VisibleForTesting - static int updateLightNavigationBarAppearanceLw(int appearance, WindowState opaque, + private int updateLightNavigationBarAppearanceLw(int appearance, WindowState opaque, WindowState opaqueOrDimming, WindowState imeWindow, WindowState navColorWin) { if (navColorWin != null) { @@ -3662,6 +3666,9 @@ public class DisplayPolicy { // Clear the light flag for dimming window. appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS; } + if (!mNavigationBarController.isLightAppearanceAllowed(navColorWin)) { + appearance &= ~APPEARANCE_LIGHT_NAVIGATION_BARS; + } } return appearance; } diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java index a685886da032..28dcbcdf3cc7 100644 --- a/services/core/java/com/android/server/wm/Letterbox.java +++ b/services/core/java/com/android/server/wm/Letterbox.java @@ -77,10 +77,10 @@ public class Letterbox { mOuter.set(outer); mInner.set(inner); - mTop.layout(outer.left, outer.top, inner.right, inner.top, surfaceOrigin); - mLeft.layout(outer.left, inner.top, inner.left, outer.bottom, surfaceOrigin); - mBottom.layout(inner.left, inner.bottom, outer.right, outer.bottom, surfaceOrigin); - mRight.layout(inner.right, outer.top, outer.right, inner.bottom, surfaceOrigin); + mTop.layout(outer.left, outer.top, outer.right, inner.top, surfaceOrigin); + mLeft.layout(outer.left, outer.top, inner.left, outer.bottom, surfaceOrigin); + mBottom.layout(outer.left, inner.bottom, outer.right, outer.bottom, surfaceOrigin); + mRight.layout(inner.right, outer.top, outer.right, outer.bottom, surfaceOrigin); } @@ -101,6 +101,31 @@ public class Letterbox { } /** + * Returns {@code true} if the letterbox does not overlap with the bar, or the letterbox can + * fully cover the window frame. + * + * @param rect The area of the window frame. + */ + boolean notIntersectsOrFullyContains(Rect rect) { + int emptyCount = 0; + int noOverlappingCount = 0; + for (LetterboxSurface surface : mSurfaces) { + final Rect surfaceRect = surface.mLayoutFrameGlobal; + if (surfaceRect.isEmpty()) { + // empty letterbox + emptyCount++; + } else if (!Rect.intersects(surfaceRect, rect)) { + // no overlapping + noOverlappingCount++; + } else if (surfaceRect.contains(rect)) { + // overlapping and covered + return true; + } + } + return (emptyCount + noOverlappingCount) == mSurfaces.length; + } + + /** * Returns true if any part of the letterbox overlaps with the given {@code rect}. */ public boolean isOverlappingWith(Rect rect) { diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java index f3e23169fee0..50ae4ea2f627 100644 --- a/services/core/java/com/android/server/wm/RootWindowContainer.java +++ b/services/core/java/com/android/server/wm/RootWindowContainer.java @@ -39,6 +39,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG; import static android.view.WindowManager.TRANSIT_CRASHING_ACTIVITY_CLOSE; import static android.view.WindowManager.TRANSIT_NONE; import static android.view.WindowManager.TRANSIT_SHOW_SINGLE_TASK_DISPLAY; +import static android.view.WindowManager.TRANSIT_TASK_TO_BACK; import static com.android.server.policy.PhoneWindowManager.SYSTEM_DIALOG_REASON_ASSIST; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_LAYOUT; @@ -2156,6 +2157,20 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // On the other hand, ActivityRecord#onParentChanged takes care of setting the // up-to-dated pinned stack information on this newly created stack. r.reparent(stack, MAX_VALUE, reason); + + // In the case of this activity entering PIP due to it being moved to the back, + // the old activity would have a TRANSIT_TASK_TO_BACK transition that needs to be + // ran. But, since its visibility did not change (note how it was STOPPED/not + // visible, and with it now at the back stack, it remains not visible), the logic to + // add the transition is automatically skipped. We then add this activity manually + // to the list of apps being closed, and request its transition to be ran. + final ActivityRecord oldTopActivity = task.getTopMostActivity(); + if (oldTopActivity != null && oldTopActivity.isState(STOPPED) + && task.getDisplayContent().mAppTransition.getAppTransition() + == TRANSIT_TASK_TO_BACK) { + task.getDisplayContent().mClosingApps.add(oldTopActivity); + oldTopActivity.mRequestForceTransition = true; + } } // The intermediate windowing mode to be set on the ActivityRecord later. // This needs to happen before the re-parenting, otherwise we will always set the diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index 7afed3ccc6ca..fd4fdfccdea8 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -3822,7 +3822,14 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return mActivityRecord.getBounds().equals(mTmpRect); } - @Override + /** + * @see Letterbox#notIntersectsOrFullyContains(Rect) + */ + boolean letterboxNotIntersectsOrFullyContains(Rect rect) { + return mActivityRecord == null + || mActivityRecord.letterboxNotIntersectsOrFullyContains(rect); + } + public boolean isLetterboxedOverlappingWith(Rect rect) { return mActivityRecord != null && mActivityRecord.isLetterboxOverlappingWith(rect); } diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java index 508d2d477067..da45300ed318 100644 --- a/services/core/java/com/android/server/wm/WindowStateAnimator.java +++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java @@ -1592,12 +1592,6 @@ class WindowStateAnimator { if (mSurfaceController != null) { mSurfaceController.detachChildren(); } - // If the children are detached, it means the app is exiting. We don't want to tear the - // content down too early, otherwise we could end up with a flicker. By preserving the - // current surface, we ensure the content remains on screen until the window is completely - // removed. It also ensures that the old surface is cleaned up when started again since it - // forces mSurfaceController to be set to null. - preserveSurfaceLocked(); } void setOffsetPositionForStackResize(boolean offsetPositionForStackResize) { diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java index 23381ffd4eaa..8db09b4f156c 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationExitInfoTest.java @@ -192,6 +192,7 @@ public class ApplicationExitInfoTest { final int app1Uid = 10123; final int app1Pid1 = 12345; final int app1Pid2 = 12346; + final int app1sPid1 = 13456; final int app1DefiningUid = 23456; final int app1ConnectiongGroup = 10; final int app1UidUser2 = 1010123; @@ -202,8 +203,12 @@ public class ApplicationExitInfoTest { final long app1Rss2 = 45679; final long app1Pss3 = 34569; final long app1Rss3 = 45680; + final long app1sPss1 = 56789; + final long app1sRss1 = 67890; final String app1ProcessName = "com.android.test.stub1:process"; final String app1PackageName = "com.android.test.stub1"; + final String app1sProcessName = "com.android.test.stub_shared:process"; + final String app1sPackageName = "com.android.test.stub_shared"; final byte[] app1Cookie1 = {(byte) 0x01, (byte) 0x02, (byte) 0x03, (byte) 0x04, (byte) 0x05, (byte) 0x06, (byte) 0x07, (byte) 0x08}; final byte[] app1Cookie2 = {(byte) 0x08, (byte) 0x07, (byte) 0x06, (byte) 0x05, @@ -262,6 +267,29 @@ public class ApplicationExitInfoTest { app1Cookie1.length)); assertEquals(info.getTraceInputStream(), null); + // Now create a process record from a different package but shared UID. + sleep(1); + final long now1s = System.currentTimeMillis(); + app = makeProcessRecord( + app1sPid1, // pid + app1Uid, // uid + app1Uid, // packageUid + null, // definingUid + 0, // connectionGroup + PROCESS_STATE_BOUND_TOP, // procstate + app1sPss1, // pss + app1sRss1, // rss + app1sProcessName, // processName + app1sPackageName); // packageName + doReturn(new Pair<Long, Object>(now1s, Integer.valueOf(0))) + .when(mAppExitInfoTracker.mAppExitInfoSourceZygote) + .remove(anyInt(), anyInt()); + doReturn(null) + .when(mAppExitInfoTracker.mAppExitInfoSourceLmkd) + .remove(anyInt(), anyInt()); + noteAppKill(app, ApplicationExitInfo.REASON_USER_REQUESTED, + ApplicationExitInfo.SUBREASON_UNKNOWN, null); + // Case 2: create another app1 process record with a different pid sleep(1); final long now2 = System.currentTimeMillis(); @@ -290,8 +318,8 @@ public class ApplicationExitInfoTest { list.clear(); // Get all the records for app1Uid - mAppExitInfoTracker.getExitInfo(app1PackageName, app1Uid, 0, 0, list); - assertEquals(2, list.size()); + mAppExitInfoTracker.getExitInfo(null, app1Uid, 0, 0, list); + assertEquals(3, list.size()); info = list.get(0); @@ -315,7 +343,26 @@ public class ApplicationExitInfoTest { assertTrue(ArrayUtils.equals(info.getProcessStateSummary(), app1Cookie2, app1Cookie2.length)); + info = list.get(1); + verifyApplicationExitInfo( + info, // info + now1s, // timestamp + app1sPid1, // pid + app1Uid, // uid + app1Uid, // packageUid + null, // definingUid + app1sProcessName, // processName + 0, // connectionGroup + ApplicationExitInfo.REASON_USER_REQUESTED, // reason + null, // subReason + null, // status + app1sPss1, // pss + app1sRss1, // rss + IMPORTANCE_FOREGROUND, // importance + null); // description + + info = list.get(2); assertTrue(ArrayUtils.equals(info.getProcessStateSummary(), app1Cookie1, app1Cookie1.length)); @@ -808,7 +855,7 @@ public class ApplicationExitInfoTest { list.clear(); mAppExitInfoTracker.getExitInfo(null, app1Uid, 0, 0, list); - assertEquals(2, list.size()); + assertEquals(3, list.size()); info = list.get(0); @@ -831,6 +878,24 @@ public class ApplicationExitInfoTest { null); // description info = list.get(1); + verifyApplicationExitInfo( + info, // info + now1s, // timestamp + app1sPid1, // pid + app1Uid, // uid + app1Uid, // packageUid + null, // definingUid + app1sProcessName, // processName + 0, // connectionGroup + ApplicationExitInfo.REASON_USER_REQUESTED, // reason + null, // subReason + null, // status + app1sPss1, // pss + app1sRss1, // rss + IMPORTANCE_FOREGROUND, // importance + null); // description + + info = list.get(2); exitCode = 5; verifyApplicationExitInfo( info, // info diff --git a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java index 1a04d2ff8c29..4dec7a1a0ab9 100644 --- a/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java +++ b/services/tests/servicestests/src/com/android/server/usage/AppStandbyControllerTests.java @@ -218,6 +218,10 @@ public class AppStandbyControllerTests { } @Override + void updatePowerWhitelistCache() { + } + + @Override boolean isRestrictedBucketEnabled() { return mIsRestrictedBucketEnabled; } diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java index 6b613cad4b82..f2a553906095 100644 --- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java @@ -199,6 +199,40 @@ public class AppTransitionControllerTest extends WindowTestsBase { } @Test + public void testGetAnimationTargets_visibilityAlreadyUpdated_butForcedTransitionRequested() { + // [DisplayContent] -+- [TaskStack1] - [Task1] - [ActivityRecord1] (closing, invisible) + // +- [TaskStack2] - [Task2] - [ActivityRecord2] (opening, visible) + final ActivityStack stack1 = createTaskStackOnDisplay(mDisplayContent); + final ActivityRecord activity1 = WindowTestUtils.createTestActivityRecord(stack1); + activity1.setVisible(true); + activity1.mVisibleRequested = true; + activity1.mRequestForceTransition = true; + + final ActivityStack stack2 = createTaskStackOnDisplay(mDisplayContent); + final ActivityRecord activity2 = WindowTestUtils.createTestActivityRecord(stack2); + activity2.setVisible(false); + activity2.mVisibleRequested = false; + activity2.mRequestForceTransition = true; + + final ArraySet<ActivityRecord> opening = new ArraySet<>(); + opening.add(activity1); + final ArraySet<ActivityRecord> closing = new ArraySet<>(); + closing.add(activity2); + + // The visibility are already updated, but since forced transition is requested, it will + // be included. + WindowManagerService.sHierarchicalAnimations = false; + assertEquals( + new ArraySet<>(new WindowContainer[]{activity1}), + AppTransitionController.getAnimationTargets( + opening, closing, true /* visible */)); + assertEquals( + new ArraySet<>(new WindowContainer[]{activity2}), + AppTransitionController.getAnimationTargets( + opening, closing, false /* visible */)); + } + + @Test public void testGetAnimationTargets_exitingBeforeTransition() { // Create another non-empty task so the animation target won't promote to task display area. WindowTestUtils.createTestActivityRecord( diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java index a8fc66da9bd0..ce0aa79f2d89 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayAreaPolicyBuilderTest.java @@ -26,9 +26,11 @@ import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER; import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; +import static android.window.DisplayAreaOrganizer.FEATURE_FULLSCREEN_MAGNIFICATION; import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED; import static android.window.DisplayAreaOrganizer.FEATURE_ROOT; import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; +import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION; import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS; import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature; @@ -178,6 +180,39 @@ public class DisplayAreaPolicyBuilderTest { } @Test + public void testBuilder_defaultPolicy_hasWindowedMagnificationFeature() { + final DisplayAreaPolicy.Provider defaultProvider = DisplayAreaPolicy.Provider.fromResources( + resourcesWithProvider("")); + final DisplayAreaPolicyBuilder.Result defaultPolicy = + (DisplayAreaPolicyBuilder.Result) defaultProvider.instantiate(mWms, mDisplayContent, + mRoot, mImeContainer); + final List<Feature> features = defaultPolicy.getFeatures(); + boolean hasWindowedMagnificationFeature = false; + for (Feature feature : features) { + hasWindowedMagnificationFeature |= feature.getId() == FEATURE_WINDOWED_MAGNIFICATION; + } + + assertThat(hasWindowedMagnificationFeature).isTrue(); + } + + @Test + public void testBuilder_defaultPolicy_hasFullscreenMagnificationFeature() { + final DisplayAreaPolicy.Provider defaultProvider = DisplayAreaPolicy.Provider.fromResources( + resourcesWithProvider("")); + final DisplayAreaPolicyBuilder.Result defaultPolicy = + (DisplayAreaPolicyBuilder.Result) defaultProvider.instantiate(mWms, mDisplayContent, + mRoot, mImeContainer); + final List<Feature> features = defaultPolicy.getFeatures(); + boolean hasFullscreenMagnificationFeature = false; + for (Feature feature : features) { + hasFullscreenMagnificationFeature |= + feature.getId() == FEATURE_FULLSCREEN_MAGNIFICATION; + } + + assertThat(hasFullscreenMagnificationFeature).isTrue(); + } + + @Test public void testBuilder_createCustomizedDisplayAreaForFeature() { final Feature dimmable; final Feature other; diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java index bf84aecdb6a0..2f3004bf6832 100644 --- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java +++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java @@ -16,8 +16,8 @@ package com.android.server.wm; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; @@ -60,6 +60,103 @@ public class LetterboxTest { assertTrue(mLetterbox.isOverlappingWith(new Rect(0, 0, 1, 1))); } + private static final int TOP_BAR = 0x1; + private static final int BOTTOM_BAR = 0x2; + private static final int LEFT_BAR = 0x4; + private static final int RIGHT_BAR = 0x8; + @Test + public void testNotIntersectsOrFullyContains_usesGlobalCoordinates() { + final Rect outer = new Rect(0, 0, 10, 50); + final Point surfaceOrig = new Point(1000, 2000); + + final Rect topBar = new Rect(0, 0, 10, 2); + final Rect bottomBar = new Rect(0, 45, 10, 50); + final Rect leftBar = new Rect(0, 0, 2, 50); + final Rect rightBar = new Rect(8, 0, 10, 50); + + final LetterboxLayoutVerifier verifier = + new LetterboxLayoutVerifier(outer, surfaceOrig, mLetterbox); + verifier.setBarRect(topBar, bottomBar, leftBar, rightBar); + + // top + verifier.setInner(0, 2, 10, 50).verifyPositions(TOP_BAR | BOTTOM_BAR, BOTTOM_BAR); + // bottom + verifier.setInner(0, 0, 10, 45).verifyPositions(TOP_BAR | BOTTOM_BAR, TOP_BAR); + // left + verifier.setInner(2, 0, 10, 50).verifyPositions(LEFT_BAR | RIGHT_BAR, RIGHT_BAR); + // right + verifier.setInner(0, 0, 8, 50).verifyPositions(LEFT_BAR | RIGHT_BAR, LEFT_BAR); + // top + bottom + verifier.setInner(0, 2, 10, 45).verifyPositions(TOP_BAR | BOTTOM_BAR, 0); + // left + right + verifier.setInner(2, 0, 8, 50).verifyPositions(LEFT_BAR | RIGHT_BAR, 0); + // top + left + verifier.setInner(2, 2, 10, 50).verifyPositions(TOP_BAR | LEFT_BAR, 0); + // top + left + right + verifier.setInner(2, 2, 8, 50).verifyPositions(TOP_BAR | LEFT_BAR | RIGHT_BAR, 0); + // left + right + bottom + verifier.setInner(2, 0, 8, 45).verifyPositions(LEFT_BAR | RIGHT_BAR | BOTTOM_BAR, 0); + // all + verifier.setInner(2, 2, 8, 45) + .verifyPositions(TOP_BAR | BOTTOM_BAR | LEFT_BAR | RIGHT_BAR, 0); + } + + private static class LetterboxLayoutVerifier { + final Rect mOuter; + final Rect mInner = new Rect(); + final Point mSurfaceOrig; + final Letterbox mLetterbox; + final Rect mTempRect = new Rect(); + + final Rect mTop = new Rect(); + final Rect mBottom = new Rect(); + final Rect mLeft = new Rect(); + final Rect mRight = new Rect(); + + LetterboxLayoutVerifier(Rect outer, Point surfaceOrig, Letterbox letterbox) { + mOuter = new Rect(outer); + mSurfaceOrig = new Point(surfaceOrig); + mLetterbox = letterbox; + } + + LetterboxLayoutVerifier setInner(int left, int top, int right, int bottom) { + mInner.set(left, top, right, bottom); + mLetterbox.layout(mOuter, mInner, mSurfaceOrig); + return this; + } + + void setBarRect(Rect top, Rect bottom, Rect left, Rect right) { + mTop.set(top); + mBottom.set(bottom); + mLeft.set(left); + mRight.set(right); + } + + void verifyPositions(int allowedPos, int noOverlapPos) { + assertEquals(mLetterbox.notIntersectsOrFullyContains(mTop), + (allowedPos & TOP_BAR) != 0); + assertEquals(mLetterbox.notIntersectsOrFullyContains(mBottom), + (allowedPos & BOTTOM_BAR) != 0); + assertEquals(mLetterbox.notIntersectsOrFullyContains(mLeft), + (allowedPos & LEFT_BAR) != 0); + assertEquals(mLetterbox.notIntersectsOrFullyContains(mRight), + (allowedPos & RIGHT_BAR) != 0); + + mTempRect.set(mTop.left, mTop.top, mTop.right, mTop.bottom + 1); + assertEquals(mLetterbox.notIntersectsOrFullyContains(mTempRect), + (noOverlapPos & TOP_BAR) != 0); + mTempRect.set(mLeft.left, mLeft.top, mLeft.right + 1, mLeft.bottom); + assertEquals(mLetterbox.notIntersectsOrFullyContains(mTempRect), + (noOverlapPos & LEFT_BAR) != 0); + mTempRect.set(mRight.left - 1, mRight.top, mRight.right, mRight.bottom); + assertEquals(mLetterbox.notIntersectsOrFullyContains(mTempRect), + (noOverlapPos & RIGHT_BAR) != 0); + mTempRect.set(mBottom.left, mBottom.top - 1, mBottom.right, mBottom.bottom); + assertEquals(mLetterbox.notIntersectsOrFullyContains(mTempRect), + (noOverlapPos & BOTTOM_BAR) != 0); + } + } + @Test public void testSurfaceOrigin_applied() { mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000)); diff --git a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java index 4aac47cf006a..d53c89628d93 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootActivityContainerTests.java @@ -37,6 +37,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spy; import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.server.wm.ActivityStack.ActivityState.STOPPED; import static com.android.server.wm.ActivityStackSupervisor.ON_TOP; import static com.android.server.wm.RootWindowContainer.MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE; @@ -148,6 +149,29 @@ public class RootActivityContainerTests extends ActivityTestsBase { ensureStackPlacement(mFullscreenStack, firstActivity); } + @Test + public void testMovingBottomMostStackActivityToPinnedStack() { + final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true) + .setStack(mFullscreenStack).build(); + final Task task = firstActivity.getTask(); + + final ActivityRecord secondActivity = new ActivityBuilder(mService).setTask(task) + .setStack(mFullscreenStack).build(); + + mFullscreenStack.moveTaskToBack(task); + + // Ensure full screen stack has both tasks. + ensureStackPlacement(mFullscreenStack, firstActivity, secondActivity); + assertEquals(task.getTopMostActivity(), secondActivity); + firstActivity.setState(STOPPED, "testMovingBottomMostStackActivityToPinnedStack"); + + + // Move first activity to pinned stack. + mRootWindowContainer.moveActivityToPinnedStack(secondActivity, "initialMove"); + + assertTrue(firstActivity.mRequestForceTransition); + } + private static void ensureStackPlacement(ActivityStack stack, ActivityRecord... activities) { final Task task = stack.getBottomMostTask(); final ArrayList<ActivityRecord> stackActivities = new ArrayList<>(); diff --git a/tools/stats_log_api_gen/Android.bp b/tools/stats_log_api_gen/Android.bp index 43387fc054b5..e3b6db08c503 100644 --- a/tools/stats_log_api_gen/Android.bp +++ b/tools/stats_log_api_gen/Android.bp @@ -121,26 +121,10 @@ cc_library { ], target: { android: { - shared_libs: [ - "libstatssocket", - "libstatspull", - ], - export_shared_lib_headers: [ - "libstatssocket", - "libstatspull", - ], + shared_libs: ["libstatssocket"], }, host: { - static_libs: [ - "libstatssocket", - "libstatspull", - "statsd-aidl-ndk_platform", - ], - shared_libs: ["libbinder_ndk"], - export_static_lib_headers: [ - "libstatssocket", - "libstatspull", - ], + static_libs: ["libstatssocket"], }, }, } diff --git a/tools/stats_log_api_gen/native_writer.cpp b/tools/stats_log_api_gen/native_writer.cpp index 21e88b360dcd..0c6c0099e459 100644 --- a/tools/stats_log_api_gen/native_writer.cpp +++ b/tools/stats_log_api_gen/native_writer.cpp @@ -82,77 +82,21 @@ static void write_annotations(FILE* out, int argIndex, } } -static int write_native_method_body(FILE* out, vector<java_type_t>& signature, - const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet, - const AtomDecl& attributionDecl) { - int argIndex = 1; - fprintf(out, " AStatsEvent_setAtomId(event, code);\n"); - write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet, "AStatsEvent_", - "event, "); - for (vector<java_type_t>::const_iterator arg = signature.begin(); - arg != signature.end(); arg++) { - switch (*arg) { - case JAVA_TYPE_ATTRIBUTION_CHAIN: { - const char* uidName = attributionDecl.fields.front().name.c_str(); - const char* tagName = attributionDecl.fields.back().name.c_str(); - fprintf(out, - " AStatsEvent_writeAttributionChain(event, " - "reinterpret_cast<const uint32_t*>(%s), %s.data(), " - "static_cast<uint8_t>(%s_length));\n", - uidName, tagName, uidName); - break; - } - case JAVA_TYPE_BYTE_ARRAY: - fprintf(out, - " AStatsEvent_writeByteArray(event, " - "reinterpret_cast<const uint8_t*>(arg%d.arg), " - "arg%d.arg_length);\n", - argIndex, argIndex); - break; - case JAVA_TYPE_BOOLEAN: - fprintf(out, " AStatsEvent_writeBool(event, arg%d);\n", argIndex); - break; - case JAVA_TYPE_INT: // Fall through. - case JAVA_TYPE_ENUM: - fprintf(out, " AStatsEvent_writeInt32(event, arg%d);\n", argIndex); - break; - case JAVA_TYPE_FLOAT: - fprintf(out, " AStatsEvent_writeFloat(event, arg%d);\n", argIndex); - break; - case JAVA_TYPE_LONG: - fprintf(out, " AStatsEvent_writeInt64(event, arg%d);\n", argIndex); - break; - case JAVA_TYPE_STRING: - fprintf(out, " AStatsEvent_writeString(event, arg%d);\n", argIndex); - break; - default: - // Unsupported types: OBJECT, DOUBLE, KEY_VALUE_PAIRS - fprintf(stderr, "Encountered unsupported type."); - return 1; - } - write_annotations(out, argIndex, fieldNumberToAtomDeclSet, "AStatsEvent_", - "event, "); - argIndex++; - } - return 0; -} - -static int write_native_stats_write_methods(FILE* out, const SignatureInfoMap& signatureInfoMap, +static int write_native_stats_write_methods(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl, const bool supportQ) { fprintf(out, "\n"); - for (auto signatureInfoMapIt = signatureInfoMap.begin(); - signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) { + for (auto signatureInfoMapIt = atoms.signatureInfoMap.begin(); + signatureInfoMapIt != atoms.signatureInfoMap.end(); signatureInfoMapIt++) { vector<java_type_t> signature = signatureInfoMapIt->first; const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = signatureInfoMapIt->second; // Key value pairs not supported in native. if (find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) != signature.end()) { continue; } - write_native_method_signature(out, "int stats_write(", signature, attributionDecl, " {"); + write_native_method_signature(out, "int stats_write", signature, attributionDecl, " {"); - // Write method body. + int argIndex = 1; if (supportQ) { - int argIndex = 1; fprintf(out, " StatsEventCompat event;\n"); fprintf(out, " event.setAtomId(code);\n"); write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet, "event.", ""); @@ -194,36 +138,78 @@ static int write_native_stats_write_methods(FILE* out, const SignatureInfoMap& s write_annotations(out, argIndex, fieldNumberToAtomDeclSet, "event.", ""); argIndex++; } - fprintf(out, " return event.writeToSocket();\n"); // end method body. + fprintf(out, " return event.writeToSocket();\n"); } else { fprintf(out, " AStatsEvent* event = AStatsEvent_obtain();\n"); - int ret = write_native_method_body(out, signature, fieldNumberToAtomDeclSet, - attributionDecl); - if (ret != 0) { - return ret; + fprintf(out, " AStatsEvent_setAtomId(event, code);\n"); + write_annotations(out, ATOM_ID_FIELD_NUMBER, fieldNumberToAtomDeclSet, "AStatsEvent_", + "event, "); + for (vector<java_type_t>::const_iterator arg = signature.begin(); + arg != signature.end(); arg++) { + switch (*arg) { + case JAVA_TYPE_ATTRIBUTION_CHAIN: { + const char* uidName = attributionDecl.fields.front().name.c_str(); + const char* tagName = attributionDecl.fields.back().name.c_str(); + fprintf(out, + " AStatsEvent_writeAttributionChain(event, " + "reinterpret_cast<const uint32_t*>(%s), %s.data(), " + "static_cast<uint8_t>(%s_length));\n", + uidName, tagName, uidName); + break; + } + case JAVA_TYPE_BYTE_ARRAY: + fprintf(out, + " AStatsEvent_writeByteArray(event, " + "reinterpret_cast<const uint8_t*>(arg%d.arg), " + "arg%d.arg_length);\n", + argIndex, argIndex); + break; + case JAVA_TYPE_BOOLEAN: + fprintf(out, " AStatsEvent_writeBool(event, arg%d);\n", argIndex); + break; + case JAVA_TYPE_INT: // Fall through. + case JAVA_TYPE_ENUM: + fprintf(out, " AStatsEvent_writeInt32(event, arg%d);\n", argIndex); + break; + case JAVA_TYPE_FLOAT: + fprintf(out, " AStatsEvent_writeFloat(event, arg%d);\n", argIndex); + break; + case JAVA_TYPE_LONG: + fprintf(out, " AStatsEvent_writeInt64(event, arg%d);\n", argIndex); + break; + case JAVA_TYPE_STRING: + fprintf(out, " AStatsEvent_writeString(event, arg%d);\n", argIndex); + break; + default: + // Unsupported types: OBJECT, DOUBLE, KEY_VALUE_PAIRS + fprintf(stderr, "Encountered unsupported type."); + return 1; + } + write_annotations(out, argIndex, fieldNumberToAtomDeclSet, "AStatsEvent_", + "event, "); + argIndex++; } fprintf(out, " const int ret = AStatsEvent_write(event);\n"); fprintf(out, " AStatsEvent_release(event);\n"); - fprintf(out, " return ret;\n"); // end method body. + fprintf(out, " return ret;\n"); } - fprintf(out, "}\n\n"); // end method. + fprintf(out, "}\n\n"); } return 0; } -static void write_native_stats_write_non_chained_methods(FILE* out, - const SignatureInfoMap& signatureInfoMap, +static void write_native_stats_write_non_chained_methods(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl) { fprintf(out, "\n"); - for (auto signature_it = signatureInfoMap.begin(); - signature_it != signatureInfoMap.end(); signature_it++) { + for (auto signature_it = atoms.nonChainedSignatureInfoMap.begin(); + signature_it != atoms.nonChainedSignatureInfoMap.end(); signature_it++) { vector<java_type_t> signature = signature_it->first; // Key value pairs not supported in native. if (find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) != signature.end()) { continue; } - write_native_method_signature(out, "int stats_write_non_chained(", signature, + write_native_method_signature(out, "int stats_write_non_chained", signature, attributionDecl, " {"); vector<java_type_t> newSignature; @@ -249,34 +235,6 @@ static void write_native_stats_write_non_chained_methods(FILE* out, } } -static int write_native_build_stats_event_methods(FILE* out, - const SignatureInfoMap& signatureInfoMap, - const AtomDecl& attributionDecl) { - fprintf(out, "\n"); - for (auto signatureInfoMapIt = signatureInfoMap.begin(); - signatureInfoMapIt != signatureInfoMap.end(); signatureInfoMapIt++) { - vector<java_type_t> signature = signatureInfoMapIt->first; - const FieldNumberToAtomDeclSet& fieldNumberToAtomDeclSet = signatureInfoMapIt->second; - // Key value pairs not supported in native. - if (find(signature.begin(), signature.end(), JAVA_TYPE_KEY_VALUE_PAIR) != signature.end()) { - continue; - } - write_native_method_signature(out, "void addAStatsEvent(AStatsEventList* pulled_data, ", - signature, attributionDecl, " {"); - - fprintf(out, " AStatsEvent* event = AStatsEventList_addStatsEvent(pulled_data);\n"); - int ret = write_native_method_body(out, signature, fieldNumberToAtomDeclSet, - attributionDecl); - if (ret != 0) { - return ret; - } - fprintf(out, " AStatsEvent_build(event);\n"); // end method body. - - fprintf(out, "}\n\n"); // end method. - } - return 0; -} - static void write_native_method_header(FILE* out, const string& methodName, const SignatureInfoMap& signatureInfoMap, const AtomDecl& attributionDecl) { @@ -304,22 +262,13 @@ int write_stats_log_cpp(FILE* out, const Atoms& atoms, const AtomDecl& attributi fprintf(out, "#include <StatsEventCompat.h>\n"); } else { fprintf(out, "#include <stats_event.h>\n"); - - if (!atoms.pulledAtomsSignatureInfoMap.empty()) { - fprintf(out, "#include <stats_pull_atom_callback.h>\n"); - } } - - fprintf(out, "\n"); write_namespace(out, cppNamespace); - write_native_stats_write_methods(out, atoms.signatureInfoMap, attributionDecl, supportQ); - write_native_stats_write_non_chained_methods(out, atoms.nonChainedSignatureInfoMap, - attributionDecl); - write_native_build_stats_event_methods(out, atoms.pulledAtomsSignatureInfoMap, - attributionDecl); + write_native_stats_write_methods(out, atoms, attributionDecl, supportQ); + write_native_stats_write_non_chained_methods(out, atoms, attributionDecl); // Print footer fprintf(out, "\n"); @@ -339,9 +288,6 @@ int write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl& attrib fprintf(out, "#include <vector>\n"); fprintf(out, "#include <map>\n"); fprintf(out, "#include <set>\n"); - if (!atoms.pulledAtomsSignatureInfoMap.empty()) { - fprintf(out, "#include <stats_pull_atom_callback.h>\n"); - } fprintf(out, "\n"); write_namespace(out, cppNamespace); @@ -391,22 +337,12 @@ int write_stats_log_header(FILE* out, const Atoms& atoms, const AtomDecl& attrib fprintf(out, "//\n"); fprintf(out, "// Write methods\n"); fprintf(out, "//\n"); - write_native_method_header(out, "int stats_write(", atoms.signatureInfoMap, attributionDecl); - fprintf(out, "\n"); + write_native_method_header(out, "int stats_write", atoms.signatureInfoMap, attributionDecl); fprintf(out, "//\n"); fprintf(out, "// Write flattened methods\n"); fprintf(out, "//\n"); - write_native_method_header(out, "int stats_write_non_chained(", atoms.nonChainedSignatureInfoMap, - attributionDecl); - fprintf(out, "\n"); - - // Print pulled atoms methods. - fprintf(out, "//\n"); - fprintf(out, "// Add AStatsEvent methods\n"); - fprintf(out, "//\n"); - write_native_method_header(out, "void addAStatsEvent(AStatsEventList* pulled_data, ", - atoms.pulledAtomsSignatureInfoMap, + write_native_method_header(out, "int stats_write_non_chained", atoms.nonChainedSignatureInfoMap, attributionDecl); fprintf(out, "\n"); diff --git a/tools/stats_log_api_gen/utils.cpp b/tools/stats_log_api_gen/utils.cpp index 4b3734053421..abb89133e58e 100644 --- a/tools/stats_log_api_gen/utils.cpp +++ b/tools/stats_log_api_gen/utils.cpp @@ -182,10 +182,10 @@ void write_native_atom_constants(FILE* out, const Atoms& atoms, const AtomDecl& fprintf(out, "\n"); } -void write_native_method_signature(FILE* out, const string& signaturePrefix, +void write_native_method_signature(FILE* out, const string& methodName, const vector<java_type_t>& signature, const AtomDecl& attributionDecl, const string& closer) { - fprintf(out, "%sint32_t code", signaturePrefix.c_str()); + fprintf(out, "%s(int32_t code", methodName.c_str()); int argIndex = 1; for (vector<java_type_t>::const_iterator arg = signature.begin(); arg != signature.end(); arg++) { diff --git a/tools/stats_log_api_gen/utils.h b/tools/stats_log_api_gen/utils.h index 42dc90eb79dc..73e0cb838227 100644 --- a/tools/stats_log_api_gen/utils.h +++ b/tools/stats_log_api_gen/utils.h @@ -59,7 +59,7 @@ void write_closing_namespace(FILE* out, const string& cppNamespaces); void write_native_atom_constants(FILE* out, const Atoms& atoms, const AtomDecl& attributionDecl); -void write_native_method_signature(FILE* out, const string& signaturePrefix, +void write_native_method_signature(FILE* out, const string& methodName, const vector<java_type_t>& signature, const AtomDecl& attributionDecl, const string& closer); |